2020-12-21 11:50:46 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
using System ;
using System.Collections.Generic ;
2021-04-27 22:41:48 -04:00
using System.Diagnostics.CodeAnalysis ;
2020-12-21 11:50:46 -04:00
using System.IO ;
using System.Text ;
2020-12-21 23:07:37 -04:00
namespace EpicGames.Core
2020-12-21 11:50:46 -04:00
{
/// <summary>
/// Reads data from a binary output stream. Similar to the NET Framework BinaryReader class, but supports fast serialization of object graphs and container types, and supports nullable objects.
/// Significantly faster than BinaryReader due to the expectation that the whole stream is in memory before deserialization.
/// </summary>
public class BinaryArchiveReader : IDisposable
{
/// <summary>
/// The input stream.
/// </summary>
2022-03-24 16:35:00 -04:00
Stream ? _stream ;
2020-12-21 11:50:46 -04:00
/// <summary>
/// The input buffer
/// </summary>
2022-03-24 16:35:00 -04:00
byte [ ] ? _buffer ;
2020-12-21 11:50:46 -04:00
/// <summary>
/// Current position within the buffer
/// </summary>
2022-03-24 16:35:00 -04:00
int _bufferPos ;
2021-08-04 16:50:01 -04:00
2020-12-21 11:50:46 -04:00
/// <summary>
/// List of previously serialized objects
/// </summary>
2022-03-24 16:35:00 -04:00
readonly List < object? > _objects = new List < object? > ( ) ;
2020-12-21 11:50:46 -04:00
/// <summary>
/// Constructor
/// </summary>
2022-03-24 16:35:00 -04:00
/// <param name="buffer">The buffer to read from</param>
public BinaryArchiveReader ( byte [ ] buffer )
2020-12-21 11:50:46 -04:00
{
2022-03-24 16:35:00 -04:00
_buffer = buffer ;
2020-12-21 11:50:46 -04:00
}
/// <summary>
/// Constructor
/// </summary>
2022-03-24 16:35:00 -04:00
/// <param name="fileName">File to read from</param>
public BinaryArchiveReader ( FileReference fileName )
2020-12-21 11:50:46 -04:00
{
2022-03-24 16:35:00 -04:00
_buffer = FileReference . ReadAllBytes ( fileName ) ;
2020-12-21 11:50:46 -04:00
}
/// <summary>
/// Constructor
/// </summary>
2022-03-24 16:35:00 -04:00
/// <param name="stream">Stream to read from</param>
public BinaryArchiveReader ( Stream stream )
2020-12-21 11:50:46 -04:00
{
2022-03-24 16:35:00 -04:00
_buffer = new byte [ stream . Length ] ;
stream . Read ( _buffer , 0 , _buffer . Length ) ;
2020-12-21 11:50:46 -04:00
}
/// <summary>
/// Dispose of the stream owned by this reader
/// </summary>
public void Dispose ( )
{
2022-03-24 16:35:00 -04:00
if ( _stream ! = null )
2020-12-21 11:50:46 -04:00
{
2022-03-24 16:35:00 -04:00
_stream . Dispose ( ) ;
_stream = null ;
2020-12-21 11:50:46 -04:00
}
2022-03-24 16:35:00 -04:00
_buffer = null ;
2020-12-21 11:50:46 -04:00
}
/// <summary>
/// Reads a bool from the stream
/// </summary>
/// <returns>The value that was read</returns>
public bool ReadBool ( )
{
return ReadByte ( ) ! = 0 ;
}
/// <summary>
/// Reads a single byte from the stream
/// </summary>
/// <returns>The value that was read</returns>
public byte ReadByte ( )
{
2022-03-24 16:35:00 -04:00
byte value = _buffer ! [ _bufferPos ] ;
_bufferPos + + ;
return value ;
2020-12-21 11:50:46 -04:00
}
/// <summary>
/// Reads a single signed byte from the stream
/// </summary>
/// <returns>The value that was read</returns>
public sbyte ReadSignedByte ( )
{
return ( sbyte ) ReadByte ( ) ;
}
/// <summary>
/// Reads a single short from the stream
/// </summary>
/// <returns>The value that was read</returns>
public short ReadShort ( )
{
return ( short ) ReadUnsignedShort ( ) ;
}
/// <summary>
/// Reads a single unsigned short from the stream
/// </summary>
/// <returns>The value that was read</returns>
public ushort ReadUnsignedShort ( )
{
2022-03-24 16:35:00 -04:00
ushort value = ( ushort ) ( _buffer ! [ _bufferPos + 0 ] | ( _buffer [ _bufferPos + 1 ] < < 8 ) ) ;
_bufferPos + = 2 ;
return value ;
2020-12-21 11:50:46 -04:00
}
/// <summary>
/// Reads a single int from the stream
/// </summary>
/// <returns>The value that was read</returns>
public int ReadInt ( )
{
return ( int ) ReadUnsignedInt ( ) ;
}
/// <summary>
/// Reads a single unsigned int from the stream
/// </summary>
/// <returns>The value that was read</returns>
public uint ReadUnsignedInt ( )
{
2022-03-24 16:35:00 -04:00
uint value = ( uint ) ( _buffer ! [ _bufferPos + 0 ] | ( _buffer [ _bufferPos + 1 ] < < 8 ) | ( _buffer [ _bufferPos + 2 ] < < 16 ) | ( _buffer [ _bufferPos + 3 ] < < 24 ) ) ;
_bufferPos + = 4 ;
return value ;
2020-12-21 11:50:46 -04:00
}
/// <summary>
/// Reads a single long from the stream
/// </summary>
/// <returns>The value that was read</returns>
public long ReadLong ( )
{
return ( long ) ReadUnsignedLong ( ) ;
}
/// <summary>
/// Reads a single unsigned long from the stream
/// </summary>
/// <returns>The value that was read</returns>
public ulong ReadUnsignedLong ( )
{
2022-03-24 16:35:00 -04:00
ulong value = ( ulong ) ReadUnsignedInt ( ) ;
value | = ( ulong ) ReadUnsignedInt ( ) < < 32 ;
return value ;
2020-12-21 11:50:46 -04:00
}
2021-09-16 17:15:33 -04:00
/// <summary>
/// Reads a double (64 bit floating point value) from the stream
/// </summary>
/// <returns>The value that was read</returns>
public double ReadDouble ( )
{
return BitConverter . Int64BitsToDouble ( ReadLong ( ) ) ;
}
2020-12-21 11:50:46 -04:00
/// <summary>
/// Reads a string from the stream
/// </summary>
/// <returns>The value that was read</returns>
2021-11-18 14:37:34 -05:00
public string? ReadString ( )
2020-12-21 11:50:46 -04:00
{
2022-04-27 14:16:14 -04:00
// ReadPrimitiveArray has been inlined here to avoid the transient byte array allocation
int length = ReadInt ( ) ;
if ( length < 0 )
2020-12-21 11:50:46 -04:00
{
return null ;
}
else
{
2022-04-27 14:16:14 -04:00
int offset = _bufferPos ;
_bufferPos + = length ;
return Encoding . UTF8 . GetString ( _buffer ! , offset , length ) ;
2020-12-21 11:50:46 -04:00
}
}
/// <summary>
/// Reads a byte array from the stream
/// </summary>
/// <returns>The data that was read</returns>
2021-11-18 14:37:34 -05:00
public byte [ ] ? ReadByteArray ( )
2020-12-21 11:50:46 -04:00
{
return ReadPrimitiveArray < byte > ( sizeof ( byte ) ) ;
}
/// <summary>
/// Reads a short array from the stream
/// </summary>
/// <returns>The data that was read</returns>
2021-11-18 14:37:34 -05:00
public short [ ] ? ReadShortArray ( )
2020-12-21 11:50:46 -04:00
{
return ReadPrimitiveArray < short > ( sizeof ( short ) ) ;
}
/// <summary>
/// Reads an int array from the stream
/// </summary>
/// <returns>The data that was read</returns>
2021-11-18 14:37:34 -05:00
public int [ ] ? ReadIntArray ( )
2020-12-21 11:50:46 -04:00
{
return ReadPrimitiveArray < int > ( sizeof ( int ) ) ;
}
/// <summary>
/// Reads an array of primitive types from the stream
/// </summary>
2022-03-24 16:35:00 -04:00
/// <param name="elementSize">Size of a single element</param>
2020-12-21 11:50:46 -04:00
/// <returns>The data that was read</returns>
2022-03-24 16:35:00 -04:00
private T [ ] ? ReadPrimitiveArray < T > ( int elementSize ) where T : struct
2020-12-21 11:50:46 -04:00
{
2022-03-24 16:35:00 -04:00
int length = ReadInt ( ) ;
if ( length < 0 )
2020-12-21 11:50:46 -04:00
{
return null ;
}
else
{
2022-03-24 16:35:00 -04:00
T [ ] result = new T [ length ] ;
ReadBulkData ( result , length * elementSize ) ;
return result ;
2020-12-21 11:50:46 -04:00
}
}
/// <summary>
/// Reads a byte array from the stream
/// </summary>
2022-03-24 16:35:00 -04:00
/// <param name="length">Length of the array to read</param>
2020-12-21 11:50:46 -04:00
/// <returns>The data that was read</returns>
2022-03-24 16:35:00 -04:00
public byte [ ] ReadFixedSizeByteArray ( int length )
2020-12-21 11:50:46 -04:00
{
2022-03-24 16:35:00 -04:00
return ReadFixedSizePrimitiveArray < byte > ( sizeof ( byte ) , length ) ;
2020-12-21 11:50:46 -04:00
}
/// <summary>
/// Reads a short array from the stream
/// </summary>
2022-03-24 16:35:00 -04:00
/// <param name="length">Length of the array to read</param>
2020-12-21 11:50:46 -04:00
/// <returns>The data that was read</returns>
2022-03-24 16:35:00 -04:00
public short [ ] ReadFixedSizeShortArray ( int length )
2020-12-21 11:50:46 -04:00
{
2022-03-24 16:35:00 -04:00
return ReadFixedSizePrimitiveArray < short > ( sizeof ( short ) , length ) ;
2020-12-21 11:50:46 -04:00
}
/// <summary>
/// Reads an int array from the stream
/// </summary>
2022-03-24 16:35:00 -04:00
/// <param name="length">Length of the array to read</param>
2020-12-21 11:50:46 -04:00
/// <returns>The data that was read</returns>
2022-03-24 16:35:00 -04:00
public int [ ] ReadFixedSizeIntArray ( int length )
2020-12-21 11:50:46 -04:00
{
2022-03-24 16:35:00 -04:00
return ReadFixedSizePrimitiveArray < int > ( sizeof ( int ) , length ) ;
2020-12-21 11:50:46 -04:00
}
/// <summary>
/// Reads an array of primitive types from the stream
/// </summary>
2022-03-24 16:35:00 -04:00
/// <param name="elementSize">Size of a single element</param>
/// <param name="elementCount">Number of elements to read</param>
2020-12-21 11:50:46 -04:00
/// <returns>The data that was read</returns>
2022-03-24 16:35:00 -04:00
private T [ ] ReadFixedSizePrimitiveArray < T > ( int elementSize , int elementCount ) where T : struct
2020-12-21 11:50:46 -04:00
{
2022-03-24 16:35:00 -04:00
T [ ] result = new T [ elementCount ] ;
ReadBulkData ( result , elementSize * elementCount ) ;
return result ;
2020-12-21 11:50:46 -04:00
}
/// <summary>
/// Reads bulk data from the stream into the given buffer
/// </summary>
2022-03-24 16:35:00 -04:00
/// <param name="data">Array which receives the data that was read</param>
/// <param name="size">Size of data to read</param>
private void ReadBulkData ( Array data , int size )
2020-12-21 11:50:46 -04:00
{
2022-03-24 16:35:00 -04:00
System . Buffer . BlockCopy ( _buffer ! , _bufferPos , data , 0 , size ) ;
_bufferPos + = size ;
2020-12-21 11:50:46 -04:00
}
/// <summary>
/// Reads an array of items
/// </summary>
/// <returns>New array</returns>
2022-03-24 16:35:00 -04:00
public T [ ] ? ReadArray < T > ( Func < T > readElement )
2020-12-21 11:50:46 -04:00
{
2022-03-24 16:35:00 -04:00
int count = ReadInt ( ) ;
if ( count < 0 )
2020-12-21 11:50:46 -04:00
{
return null ;
}
else
{
2022-03-24 16:35:00 -04:00
T [ ] result = new T [ count ] ;
for ( int idx = 0 ; idx < count ; idx + + )
2020-12-21 11:50:46 -04:00
{
2022-03-24 16:35:00 -04:00
result [ idx ] = readElement ( ) ;
2020-12-21 11:50:46 -04:00
}
2022-03-24 16:35:00 -04:00
return result ;
2020-12-21 11:50:46 -04:00
}
}
/// <summary>
/// Reads a list of items
/// </summary>
/// <typeparam name="T">The element type for the list</typeparam>
2022-03-24 16:35:00 -04:00
/// <param name="readElement">Delegate used to read a single element</param>
2020-12-21 11:50:46 -04:00
/// <returns>List of items</returns>
2022-03-24 16:35:00 -04:00
public List < T > ? ReadList < T > ( Func < T > readElement )
2020-12-21 11:50:46 -04:00
{
2022-03-24 16:35:00 -04:00
int count = ReadInt ( ) ;
if ( count < 0 )
2020-12-21 11:50:46 -04:00
{
return null ;
}
else
{
2022-03-24 16:35:00 -04:00
List < T > result = new List < T > ( count ) ;
for ( int idx = 0 ; idx < count ; idx + + )
2020-12-21 11:50:46 -04:00
{
2022-03-24 16:35:00 -04:00
result . Add ( readElement ( ) ) ;
2020-12-21 11:50:46 -04:00
}
2022-03-24 16:35:00 -04:00
return result ;
2020-12-21 11:50:46 -04:00
}
}
/// <summary>
/// Reads a hashset of items
/// </summary>
/// <typeparam name="T">The element type for the set</typeparam>
2022-03-24 16:35:00 -04:00
/// <param name="readElement">Delegate used to read a single element</param>
2020-12-21 11:50:46 -04:00
/// <returns>Set of items</returns>
2022-03-24 16:35:00 -04:00
public HashSet < T > ? ReadHashSet < T > ( Func < T > readElement )
2020-12-21 11:50:46 -04:00
{
2022-03-24 16:35:00 -04:00
int count = ReadInt ( ) ;
if ( count < 0 )
2020-12-21 11:50:46 -04:00
{
return null ;
}
else
{
2022-03-24 16:35:00 -04:00
HashSet < T > result = new HashSet < T > ( ) ;
for ( int idx = 0 ; idx < count ; idx + + )
2020-12-21 11:50:46 -04:00
{
2022-03-24 16:35:00 -04:00
result . Add ( readElement ( ) ) ;
2020-12-21 11:50:46 -04:00
}
2022-03-24 16:35:00 -04:00
return result ;
2020-12-21 11:50:46 -04:00
}
}
/// <summary>
/// Reads a hashset of items
/// </summary>
/// <typeparam name="T">The element type for the set</typeparam>
2022-03-24 16:35:00 -04:00
/// <param name="readElement">Delegate used to read a single element</param>
/// <param name="comparer">Comparison function for the set</param>
2020-12-21 11:50:46 -04:00
/// <returns>Set of items</returns>
2022-03-24 16:35:00 -04:00
public HashSet < T > ? ReadHashSet < T > ( Func < T > readElement , IEqualityComparer < T > comparer )
2020-12-21 11:50:46 -04:00
{
2022-03-24 16:35:00 -04:00
int count = ReadInt ( ) ;
if ( count < 0 )
2020-12-21 11:50:46 -04:00
{
return null ;
}
else
{
2022-03-24 16:35:00 -04:00
HashSet < T > result = new HashSet < T > ( comparer ) ;
for ( int idx = 0 ; idx < count ; idx + + )
2020-12-21 11:50:46 -04:00
{
2022-03-24 16:35:00 -04:00
result . Add ( readElement ( ) ) ;
2020-12-21 11:50:46 -04:00
}
2022-03-24 16:35:00 -04:00
return result ;
2020-12-21 11:50:46 -04:00
}
}
/// <summary>
/// Reads a dictionary of items
/// </summary>
2022-03-24 16:35:00 -04:00
/// <typeparam name="TK">Type of the dictionary key</typeparam>
/// <typeparam name="TV">Type of the dictionary value</typeparam>
/// <param name="readKey">Delegate used to read a single key</param>
/// <param name="readValue">Delegate used to read a single value</param>
2020-12-21 11:50:46 -04:00
/// <returns>New dictionary instance</returns>
2022-03-24 16:35:00 -04:00
public Dictionary < TK , TV > ? ReadDictionary < TK , TV > ( Func < TK > readKey , Func < TV > readValue ) where TK : notnull
2020-12-21 11:50:46 -04:00
{
2022-03-24 16:35:00 -04:00
int count = ReadInt ( ) ;
if ( count < 0 )
2020-12-21 11:50:46 -04:00
{
return null ;
}
else
{
2022-03-24 16:35:00 -04:00
Dictionary < TK , TV > result = new Dictionary < TK , TV > ( count ) ;
for ( int idx = 0 ; idx < count ; idx + + )
2020-12-21 11:50:46 -04:00
{
2022-03-24 16:35:00 -04:00
result . Add ( readKey ( ) , readValue ( ) ) ;
2020-12-21 11:50:46 -04:00
}
2022-03-24 16:35:00 -04:00
return result ;
2020-12-21 11:50:46 -04:00
}
}
/// <summary>
/// Reads a dictionary of items
/// </summary>
2022-03-24 16:35:00 -04:00
/// <typeparam name="TK">Type of the dictionary key</typeparam>
/// <typeparam name="TV">Type of the dictionary value</typeparam>
/// <param name="readKey">Delegate used to read a single key</param>
/// <param name="readValue">Delegate used to read a single value</param>
/// <param name="comparer">Comparison function for keys in the dictionary</param>
2020-12-21 11:50:46 -04:00
/// <returns>New dictionary instance</returns>
2022-03-24 16:35:00 -04:00
public Dictionary < TK , TV > ? ReadDictionary < TK , TV > ( Func < TK > readKey , Func < TV > readValue , IEqualityComparer < TK > comparer ) where TK : notnull
2020-12-21 11:50:46 -04:00
{
2022-03-24 16:35:00 -04:00
int count = ReadInt ( ) ;
if ( count < 0 )
2020-12-21 11:50:46 -04:00
{
return null ;
}
else
{
2022-03-24 16:35:00 -04:00
Dictionary < TK , TV > result = new Dictionary < TK , TV > ( count , comparer ) ;
for ( int idx = 0 ; idx < count ; idx + + )
2020-12-21 11:50:46 -04:00
{
2022-03-24 16:35:00 -04:00
result . Add ( readKey ( ) , readValue ( ) ) ;
2020-12-21 11:50:46 -04:00
}
2022-03-24 16:35:00 -04:00
return result ;
2020-12-21 11:50:46 -04:00
}
}
/// <summary>
/// Reads a nullable object from the archive
/// </summary>
/// <typeparam name="T">The nullable type</typeparam>
2022-03-24 16:35:00 -04:00
/// <param name="readValue">Delegate used to read a value</param>
public Nullable < T > ReadNullable < T > ( Func < T > readValue ) where T : struct
2020-12-21 11:50:46 -04:00
{
2021-08-04 16:50:01 -04:00
if ( ReadBool ( ) )
2020-12-21 11:50:46 -04:00
{
2022-03-24 16:35:00 -04:00
return new Nullable < T > ( readValue ( ) ) ;
2020-12-21 11:50:46 -04:00
}
else
{
return null ;
}
}
/// <summary>
/// Reads an object, which may be null, from the archive. Does not handle de-duplicating object references.
/// </summary>
/// <typeparam name="T">Type of the object to read</typeparam>
2022-03-24 16:35:00 -04:00
/// <param name="read">Delegate used to read the object</param>
2020-12-21 11:50:46 -04:00
/// <returns>The object instance</returns>
2022-03-24 16:35:00 -04:00
public T ? ReadOptionalObject < T > ( Func < T > read ) where T : class
2020-12-21 11:50:46 -04:00
{
2021-08-04 16:50:01 -04:00
if ( ReadBool ( ) )
2020-12-21 11:50:46 -04:00
{
2022-03-24 16:35:00 -04:00
return read ( ) ;
2020-12-21 11:50:46 -04:00
}
else
{
return null ;
}
}
/// <summary>
/// Reads an object reference from the stream. Each referenced object will only be serialized once using the supplied delegates. Reading an object instance is
/// done in two phases; first the object is created and its reference stored in the unique object list, then the object contents are read. This allows the object to
/// serialize a reference to itself.
/// </summary>
/// <typeparam name="T">Type of the object to read.</typeparam>
2022-03-24 16:35:00 -04:00
/// <param name="createObject">Delegate used to create an object instance</param>
/// <param name="readObject">Delegate used to read an object instance</param>
2020-12-21 11:50:46 -04:00
/// <returns>Object instance</returns>
2022-03-24 16:35:00 -04:00
public T ? ReadObjectReference < T > ( Func < T > createObject , Action < T > readObject ) where T : class
2020-12-21 11:50:46 -04:00
{
2022-03-24 16:35:00 -04:00
int index = ReadInt ( ) ;
if ( index < 0 )
2020-12-21 11:50:46 -04:00
{
return null ;
}
else
{
2022-03-24 16:35:00 -04:00
if ( index = = _objects . Count )
2020-12-21 11:50:46 -04:00
{
2022-03-24 16:35:00 -04:00
T obj = createObject ( ) ;
_objects . Add ( obj ) ;
readObject ( obj ) ;
2020-12-21 11:50:46 -04:00
}
2022-03-24 16:35:00 -04:00
return ( T ? ) _objects [ index ] ;
2020-12-21 11:50:46 -04:00
}
}
/// <summary>
/// Reads an object reference from the stream. Each object will only be serialized once using the supplied delegate; subsequent reads reference the original.
/// Since the reader only receives the object reference when the CreateObject delegate returns, it is not possible for the object to serialize a reference to itself.
/// </summary>
/// <typeparam name="T">Type of the object to read.</typeparam>
2022-03-24 16:35:00 -04:00
/// <param name="readObject">Delegate used to create an object instance. The object may not reference itself recursively.</param>
2020-12-21 11:50:46 -04:00
/// <returns>Object instance</returns>
2022-03-24 16:35:00 -04:00
public object? ReadUntypedObjectReference ( Func < object? > readObject )
2020-12-21 11:50:46 -04:00
{
2022-03-24 16:35:00 -04:00
int index = ReadInt ( ) ;
if ( index < 0 )
2020-12-21 11:50:46 -04:00
{
return null ;
}
else
{
// Temporarily add the reader to the object list, so we can detect invalid recursive references.
2022-03-24 16:35:00 -04:00
if ( index = = _objects . Count )
2020-12-21 11:50:46 -04:00
{
2022-03-24 16:35:00 -04:00
_objects . Add ( null ) ;
_objects [ index ] = readObject ( ) ;
2020-12-21 11:50:46 -04:00
}
2022-03-24 16:35:00 -04:00
if ( _objects [ index ] = = null )
2020-12-21 11:50:46 -04:00
{
throw new InvalidOperationException ( "Attempt to serialize reference to object recursively." ) ;
}
2022-03-24 16:35:00 -04:00
return _objects [ index ] ;
2020-12-21 11:50:46 -04:00
}
}
2021-04-27 22:41:48 -04:00
2022-04-27 14:16:14 -04:00
/// <summary>
/// Reads an object reference from the stream. Each object will only be serialized once using the supplied delegate; subsequent reads reference the original.
/// Since the reader only receives the object reference when the CreateObject delegate returns, it is not possible for the object to serialize a reference to itself.
/// </summary>
/// <typeparam name="T">Type of the object to read.</typeparam>
/// <param name="readObject">Delegate used to create an object instance. The object may not reference itself recursively.</param>
/// <returns>Object instance</returns>
public object? ReadUntypedObjectReference ( Func < BinaryArchiveReader , object? > readObject )
{
int index = ReadInt ( ) ;
if ( index < 0 )
{
return null ;
}
else
{
// Temporarily add the reader to the object list, so we can detect invalid recursive references.
if ( index = = _objects . Count )
{
_objects . Add ( null ) ;
_objects [ index ] = readObject ( this ) ;
}
if ( _objects [ index ] = = null )
{
throw new InvalidOperationException ( "Attempt to serialize reference to object recursively." ) ;
}
return _objects [ index ] ;
}
}
2021-11-18 14:37:34 -05:00
/// <summary>
/// Reads an object reference from the stream. Each object will only be serialized once using the supplied delegate; subsequent reads reference the original.
/// Since the reader only receives the object reference when the CreateObject delegate returns, it is not possible for the object to serialize a reference to itself.
/// </summary>
/// <typeparam name="T">Type of the object to read.</typeparam>
2022-03-24 16:35:00 -04:00
/// <param name="readObject">Delegate used to create an object instance. The object may not reference itself recursively.</param>
2021-11-18 14:37:34 -05:00
/// <returns>Object instance</returns>
2022-03-24 16:35:00 -04:00
public T ? ReadObjectReference < T > ( Func < T > readObject ) where T : class = > ( T ? ) ReadUntypedObjectReference ( readObject ) ;
2022-04-27 14:16:14 -04:00
/// <summary>
/// Reads an object reference from the stream. Each object will only be serialized once using the supplied delegate; subsequent reads reference the original.
/// Since the reader only receives the object reference when the CreateObject delegate returns, it is not possible for the object to serialize a reference to itself.
/// </summary>
/// <typeparam name="T">Type of the object to read.</typeparam>
/// <param name="readObject">Delegate used to create an object instance. The object may not reference itself recursively.</param>
/// <returns>Object instance</returns>
public T ? ReadObjectReference < T > ( Func < BinaryArchiveReader , T > readObject ) where T : class = > ( T ? ) ReadUntypedObjectReference ( readObject ) ;
2021-11-18 14:37:34 -05:00
2021-04-27 22:41:48 -04:00
/// <summary>
/// Helper method for validating that deserialized objects are not null
/// </summary>
/// <typeparam name="T">Type of the deserialized object</typeparam>
2022-03-24 16:35:00 -04:00
/// <param name="param">The object instance</param>
2021-04-27 22:41:48 -04:00
/// <returns>The object instance</returns>
[return: NotNull]
2022-03-24 16:35:00 -04:00
public static T NotNull < T > ( T ? param ) where T : class
2021-04-27 22:41:48 -04:00
{
2022-03-24 16:35:00 -04:00
if ( param = = null )
2021-04-27 22:41:48 -04:00
{
throw new InvalidDataException ( "Object stored in archive is not allowed to be null." ) ;
}
2022-03-24 16:35:00 -04:00
return param ;
2021-04-27 22:41:48 -04:00
}
/// <summary>
/// Helper method for validating that deserialized objects are not null
/// </summary>
/// <typeparam name="T">Type of the deserialized object</typeparam>
2022-03-24 16:35:00 -04:00
/// <param name="param">The object instance</param>
2021-04-27 22:41:48 -04:00
/// <returns>The object instance</returns>
2022-03-24 16:35:00 -04:00
public static T NotNullStruct < T > ( T ? param ) where T : struct
2021-04-27 22:41:48 -04:00
{
2022-03-24 16:35:00 -04:00
if ( param = = null )
2021-04-27 22:41:48 -04:00
{
throw new InvalidDataException ( "Object stored in archive is not allowed to be null." ) ;
}
2022-03-24 16:35:00 -04:00
return param . Value ;
2021-04-27 22:41:48 -04:00
}
2020-12-21 11:50:46 -04:00
}
}