Skip to content

Instantly share code, notes, and snippets.

@thenameless314159
Last active July 6, 2023 12:04
Show Gist options
  • Save thenameless314159/a2a6487ce19c0da24d08a1cf328ddc8e to your computer and use it in GitHub Desktop.
Save thenameless314159/a2a6487ce19c0da24d08a1cf328ddc8e to your computer and use it in GitHub Desktop.
DotNext API proposal
/// <summary>
/// Represents an object that can be both restored from and converted to a span-based binary representation.
/// </summary>
/// <remarks>
/// This interface is intended to be used for objects that exposes settable members.
/// </remarks>
public interface IBufferFormattable : IBufferWritable, IBinaryReadable
{
}
/// <summary>
/// Represents an object that can be both restored from and converted to a binary representation.
/// </summary>
/// <typeparam name="TSelf">The implementing type.</typeparam>
public interface IBufferFormattable<TSelf> : IBufferWritable, IBinaryReadable<TSelf>
where TSelf : IBufferFormattable<TSelf>
{
}
/// <summary>
/// Represents an object that can be restored from a span-based binary representation.
/// </summary>
/// <remarks>
/// This interface is intended to be used for objects that exposes settable members.
/// </remarks>
public interface IBinaryReadable : ISpanReadable<byte>
{
/// <summary>
/// Restores the instance values from its binary representation.
/// </summary>
/// <param name="source">The source buffer.</param>
/// <param name="bytesRead">The number of bytes that was read from the source buffer.</param>
/// <returns>The restored object the parsing done successfully; otherwise <see langword="null"/>.</returns>
public static T? Read<T>(in ReadOnlySequence<byte> source, out int bytesRead)
where T : IBinaryReadable<T>
=> IBinaryReadable<T>.Read(in source, out bytesRead);
/// <summary>
/// Restores the instance values from its binary representation.
/// </summary>
/// <param name="source">The source buffer.</param>
/// <param name="bytesRead">The number of bytes that was read from the source buffer.</param>
/// <returns>The restored object the parsing done successfully; otherwise <see langword="null"/>.</returns>
public static T? Read<T>(ReadOnlySpan<byte> source, out int bytesRead)
where T : IBinaryReadable<T>
=> IBinaryReadable<T>.Read(source, out bytesRead);
/// <summary>
/// Attempts to restore the object from its binary representation.
/// </summary>
/// <param name="source">The source buffer.</param>
/// <param name="result">The restored object.</param>
/// <param name="bytesRead">The number of bytes that was read from the source buffer.</param>
/// <param name="rethrowOnFailure">Whether read exceptions should be thrown or discarded.</param>
/// <returns><see langword="true"/> if the parsing done successfully; otherwise, <see langword="false"/>.</returns>
public static bool TryRead<T>(in ReadOnlySequence<byte> source, [NotNullWhen(true)] out T? result, out int bytesRead, bool rethrowOnFailure = true)
where T : IBinaryReadable<T>
=> IBinaryReadable<T>.TryRead(in source, out result, out bytesRead, rethrowOnFailure);
/// <summary>
/// Attempts to restore the object from its binary representation.
/// </summary>
/// <param name="source">The source buffer.</param>
/// <param name="result">The restored object.</param>
/// <param name="bytesRead">The number of bytes that was read from the source buffer.</param>
/// <param name="rethrowOnFailure">Whether read exceptions should be thrown or discarded.</param>
/// <returns><see langword="true"/> if the parsing done successfully; otherwise, <see langword="false"/>.</returns>
public static bool TryRead<T>(ReadOnlySpan<byte> source, [NotNullWhen(true)] out T? result, out int bytesRead, bool rethrowOnFailure = true)
where T : IBinaryReadable<T>
=> IBinaryReadable<T>.TryRead(source, out result, out bytesRead, rethrowOnFailure);
}
/// <summary>
/// Represents an object that can be restored from a binary representation.
/// </summary>
/// <typeparam name="TSelf">The implementing type.</typeparam>
public interface IBinaryReadable<out TSelf> : ISpanReadable<TSelf, byte>
where TSelf : IBinaryReadable<TSelf>
{
/// <summary>
/// Restores the instance values from its binary representation.
/// </summary>
/// <param name="source">The source buffer.</param>
/// <param name="bytesRead">The number of bytes that was read from the source buffer.</param>
/// <returns>The restored object the parsing done successfully; otherwise <see langword="null"/>.</returns>
public static TSelf? Read(in ReadOnlySequence<byte> source, out int bytesRead)
{
if (source.IsSingleSegment)
return Read(source.FirstSpan, out bytesRead);
if (source.Length > int.MaxValue)
throw new ArgumentOutOfRangeException(nameof(source), default(string));
int size = (int)source.Length;
using BufferRental<byte> buffer = size <= (uint)BufferRental<byte>.StackallocThreshold
? new(stackalloc byte[size])
: new(size);
source.CopyTo(buffer.Span);
return Read(buffer.Span[..size], out bytesRead);
}
/// <summary>
/// Attempts to restore the object from its binary representation.
/// </summary>
/// <param name="source">The source buffer.</param>
/// <param name="result">The restored object.</param>
/// <param name="bytesRead">The number of bytes that was read from the source buffer.</param>
/// <param name="rethrowOnFailure">Whether read exceptions should be thrown or discarded.</param>
/// <returns><see langword="true"/> if the parsing done successfully; otherwise, <see langword="false"/>.</returns>
public static bool TryRead(in ReadOnlySequence<byte> source, [NotNullWhen(true)] out TSelf? result, out int bytesRead, bool rethrowOnFailure = true)
{
if (source.IsSingleSegment)
return TryRead(source.FirstSpan, out result, out bytesRead, rethrowOnFailure);
if (source.Length > int.MaxValue)
throw new ArgumentOutOfRangeException(nameof(source), default(string));
int size = (int)source.Length;
using BufferRental<byte> buffer = size <= (uint)BufferRental<byte>.StackallocThreshold
? new(stackalloc byte[size])
: new(size);
source.CopyTo(buffer.Span);
return TryRead(buffer.Span[..size], out result, out bytesRead, rethrowOnFailure);
}
}
/// <summary>
/// Provides extension methods for the <see cref="IBinaryReadable"/> interface.
/// </summary>
public static class BufferReadableExtensions
{
/// <summary>
/// Restores the instance values from its binary representation.
/// </summary>
/// <param name="value">The readable value.</param>
/// <param name="source">The source buffer.</param>
/// <returns>The number of bytes that was read from the source buffer.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Read(this IBinaryReadable value, in ReadOnlySequence<byte> source)
{
if (source.IsSingleSegment)
return value.Deserialize(source.FirstSpan);
if (source.Length > int.MaxValue)
throw new ArgumentOutOfRangeException(nameof(source), default(string));
int size = (int)source.Length;
using BufferRental<byte> buffer = size <= (uint)BufferRental<byte>.StackallocThreshold
? new(stackalloc byte[size])
: new(size);
source.CopyTo(buffer.Span);
return value.Deserialize(buffer.Span[..size]);
}
/// <summary>
/// Attempts to restore the object from its binary representation.
/// </summary>
/// <param name="value">The readable value.</param>
/// <param name="source">The source buffer.</param>
/// <param name="bytesRead">The number of bytes that was read from the source buffer.</param>
/// <param name="rethrowOnFailure">Whether read exceptions should be thrown or discarded.</param>
/// <returns><see langword="true"/> if the parsing done successfully; otherwise, <see langword="false"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryRead(this IBinaryReadable value, in ReadOnlySequence<byte> source, out int bytesRead, bool rethrowOnFailure = true)
{
if (source.IsSingleSegment)
return value.TryDeserialize(source.FirstSpan, out bytesRead, rethrowOnFailure);
if (source.Length > int.MaxValue)
throw new ArgumentOutOfRangeException(nameof(source), default(string));
int size = (int)source.Length;
using BufferRental<byte> buffer = size <= (uint)BufferRental<byte>.StackallocThreshold
? new(stackalloc byte[size])
: new(size);
source.CopyTo(buffer.Span);
return value.TryDeserialize(buffer.Span[..size], out bytesRead, rethrowOnFailure);
}
}
/// <summary>
/// Represents an object that can be restored from a sequence-based binary representation.
/// </summary>
/// <remarks>
/// This interface is intended to be used for objects that exposes settable members.
/// </remarks>
public interface IBufferReadable
{
/// <summary>
/// Restores the instance values from its binary representation.
/// </summary>
/// <param name="input">The input sequence reader.</param>
void Deserialize(ref SequenceReader<byte> input);
/// <summary>
/// Restores the instance values from its binary representation.
/// </summary>
/// <param name="source">The source buffer.</param>
/// <param name="bytesRead">The number of bytes that was read from the source buffer.</param>
/// <returns>The restored object the parsing done successfully; otherwise <see langword="null"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T? Read<T>(in ReadOnlySequence<byte> source, out long bytesRead)
where T : IBufferReadable<T>
=> IBufferReadable<T>.Read(in source, out bytesRead);
/// <summary>
/// Restores the instance values from its binary representation.
/// </summary>
/// <param name="source">The source buffer.</param>
/// <param name="bytesRead">The number of bytes that was read from the source buffer.</param>
/// <returns>The restored object the parsing done successfully; otherwise <see langword="null"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T? Read<T>(in ReadOnlyMemory<byte> source, out long bytesRead)
where T : IBufferReadable<T>
=> IBufferReadable<T>.Read(in source, out bytesRead);
/// <summary>
/// Attempts to restore the object from its binary representation.
/// </summary>
/// <param name="source">The source buffer.</param>
/// <param name="result">The restored object.</param>
/// <param name="bytesRead">The number of bytes that was read from the source buffer.</param>
/// <param name="rethrowOnFailure">Whether read exceptions should be thrown or discarded.</param>
/// <returns><see langword="true"/> if the parsing done successfully; otherwise, <see langword="false"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryRead<T>(in ReadOnlySequence<byte> source, [NotNullWhen(true)] out T? result, out long bytesRead, bool rethrowOnFailure = true)
where T : IBufferReadable<T>
=> IBufferReadable<T>.TryRead(in source, out result, out bytesRead, rethrowOnFailure);
/// <summary>
/// Attempts to restore the object from its binary representation.
/// </summary>
/// <param name="source">The source buffer.</param>
/// <param name="result">The restored object.</param>
/// <param name="bytesRead">The number of bytes that was read from the source buffer.</param>
/// <param name="rethrowOnFailure">Whether read exceptions should be thrown or discarded.</param>
/// <returns><see langword="true"/> if the parsing done successfully; otherwise, <see langword="false"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryRead<T>(in ReadOnlyMemory<byte> source, [NotNullWhen(true)] out T? result, out long bytesRead, bool rethrowOnFailure = true)
where T : IBufferReadable<T>
=> IBufferReadable<T>.TryRead(in source, out result, out bytesRead, rethrowOnFailure);
}
/// <summary>
/// Represents an object that can be restored from a binary representation.
/// </summary>
/// <typeparam name="TSelf">The implementing type.</typeparam>
/// <remarks>
/// This interface is intended to be used for decoding objects in networking scenarios from sequences obtained from a System.IO.PipeReader for example.
/// </remarks>
public interface IBufferReadable<out TSelf>
where TSelf : IBufferReadable<TSelf>
{
/// <summary>
/// Restores the object from its binary representation.
/// </summary>
/// <param name="input">The input sequence reader.</param>
/// <returns>The restored object the parsing done successfully; otherwise <see langword="null"/>.</returns>
public static abstract TSelf? Read(ref SequenceReader<byte> input);
/// <summary>
/// Restores the object from its span representation.
/// </summary>
/// <param name="input">The input span reader.</param>
/// <param name="bytesRead">The number of bytes that was read from the source buffer.</param>
/// <returns>The restored object the parsing done successfully; otherwise <see langword="null"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TSelf? Read(in ReadOnlySequence<byte> input, out long bytesRead)
{
SequenceReader<byte> reader = new(input);
var result = TSelf.Read(ref reader);
bytesRead = reader.Consumed;
return result;
}
/// <summary>
/// Restores the instance values from its binary representation.
/// </summary>
/// <param name="source">The source buffer.</param>
/// <param name="bytesRead">The number of bytes that was read from the source buffer.</param>
/// <returns>The restored object the parsing done successfully; otherwise <see langword="null"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TSelf? Read(in ReadOnlyMemory<byte> source, out long bytesRead)
{
ReadOnlySequence<byte> sequence = new(source);
return Read(in sequence, out bytesRead);
}
/// <summary>
/// Attempts to restore the object from its span representation.
/// </summary>
/// <param name="source">The source span.</param>
/// <param name="result">The restored object.</param>
/// <param name="bytesRead">The number of bytes that was read from the source buffer.</param>
/// <param name="rethrowOnFailure">Whether read exceptions should be thrown or discarded.</param>
/// <returns><see langword="true"/> if the parsing done successfully; otherwise, <see langword="false"/>.</returns>
public static bool TryRead(in ReadOnlySequence<byte> source, [NotNullWhen(true)] out TSelf? result, out long bytesRead, bool rethrowOnFailure = true)
{
SequenceReader<byte> reader = new(source);
try { result = TSelf.Read(ref reader); }
catch (Exception) when (rethrowOnFailure)
{
throw;
}
catch
{
result = default;
return false;
}
// The bytesRead must be assigned regardless of the operation success.
finally
{
bytesRead = reader.Consumed;
}
return result != null;
}
/// <summary>
/// Attempts to restore the object from its span representation.
/// </summary>
/// <param name="source">The source span.</param>
/// <param name="result">The restored object.</param>
/// <param name="bytesRead">The number of bytes that was read from the source buffer.</param>
/// <param name="rethrowOnFailure">Whether read exceptions should be thrown or discarded.</param>
/// <returns><see langword="true"/> if the parsing done successfully; otherwise, <see langword="false"/>.</returns>
public static bool TryRead(in ReadOnlyMemory<byte> source, [NotNullWhen(true)] out TSelf? result, out long bytesRead, bool rethrowOnFailure = true)
{
ReadOnlySequence<byte> sequence = new(source);
return TryRead(in sequence, out result, out bytesRead, rethrowOnFailure);
}
}
/// <summary>
/// Provides extension methods for the <see cref="IBufferReadable"/> interface.
/// </summary>
public static class BinaryReadableExtensions
{
/// <summary>
/// Restores the instance values from its binary representation.
/// </summary>
/// <param name="value">The readable value.</param>
/// <param name="source">The source buffer.</param>
/// <returns>The number of bytes that was read from the source.</returns>
public static long Deserialize(this IBufferReadable value, in ReadOnlySequence<byte> source)
{
SequenceReader<byte> reader = new(source);
value.Deserialize(ref reader);
return reader.Consumed;
}
/// <summary>
/// Restores the instance values from its binary representation.
/// </summary>
/// <param name="value">The readable value.</param>
/// <param name="source">The source buffer.</param>
/// <returns>The number of bytes that was read from the source.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static long Deserialize(this IBufferReadable value, in ReadOnlyMemory<byte> source)
{
ReadOnlySequence<byte> sequence = new(source);
return value.Deserialize(in sequence);
}
/// <summary>
/// Attempts to restore the object from its binary representation.
/// </summary>
/// <param name="value">The readable value.</param>
/// <param name="source">The source buffer.</param>
/// <param name="bytesRead">The number of bytes that was read from the source buffer.</param>
/// <param name="rethrowOnFailure">Whether read exceptions should be thrown or discarded.</param>
/// <returns><see langword="true"/> if the parsing done successfully; otherwise, <see langword="false"/>.</returns>
public static bool TryDeserialize(this IBufferReadable value, in ReadOnlySequence<byte> source, out long bytesRead, bool rethrowOnFailure = true)
{
SequenceReader<byte> reader = new(source);
try { value.Deserialize(ref reader); }
catch (Exception) when (rethrowOnFailure)
{
throw;
}
catch
{
return false;
}
// The readCount must be assigned regardless of the operation success.
finally
{
bytesRead = reader.Consumed;
}
return true;
}
/// <summary>
/// Attempts to restore the object from its binary representation.
/// </summary>
/// <param name="value">The readable value.</param>
/// <param name="source">The source buffer.</param>
/// <param name="bytesRead">The number of bytes that was read from the source buffer.</param>
/// <param name="rethrowOnFailure">Whether read exceptions should be thrown or discarded.</param>
/// <returns><see langword="true"/> if the parsing done successfully; otherwise, <see langword="false"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryDeserialize(this IBufferReadable value, in ReadOnlyMemory<byte> source, out long bytesRead, bool rethrowOnFailure = true)
{
ReadOnlySequence<byte> sequence = new(source);
return value.TryDeserialize(in sequence, out bytesRead, rethrowOnFailure);
}
}
/// <summary>
/// Represents an object that can be both restored from a sequence-based and converted to a span-based binary representation.
/// </summary>
/// <remarks>
/// This interface is intended to be used for objects that exposes settable members.
/// </remarks>
public interface IBufferSerializable : IBufferReadable, IBufferWritable
{
}
/// <summary>
/// Represents an object that can be both restored from and converted to a binary representation.
/// </summary>
/// <remarks>
/// This interface is intended to be used for decoding objects in networking scenarios from sequences obtained from a System.IO.PipeReader for example.
/// </remarks>
public interface IBufferSerializable<out TSelf> : IBufferReadable<TSelf>, IBufferWritable
where TSelf : IBufferSerializable<TSelf>
{
}
/// <summary>
/// Represents an object that can be converted to a binary representation.
/// </summary>
public interface IBufferWritable : ISpanWritable<byte>, ISizeComputable
{
}
/// <summary>
/// Provides extension methods for the <see cref="IBufferWritable"/> interface.
/// </summary>
public static class BufferWritableExtensions
{
/// <summary>
/// Encodes the object to the output buffer.
/// </summary>
/// <param name="value">The writable value.</param>
/// <param name="output">The output buffer writer.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void WriteTo(this IBufferWritable value, IBufferWriter<byte> output)
{
int size = value.GetSizeOf();
Span<byte> span = output.GetSpan(size);
int bytesWritten = value.Serialize(span);
Debug.Assert(bytesWritten == size);
output.Advance(bytesWritten);
}
/// <summary>
/// Gets the object as a binary representation.
/// </summary>
/// <param name="value">The writable value.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static byte[] GetBytes(this IBufferWritable value)
{
byte[] buffer = new byte[value.GetSizeOf()];
int bytesWritten = value.Serialize(buffer);
Debug.Assert(bytesWritten == buffer.Length);
return buffer;
}
/// <summary>
/// Gets the object as an owned binary representation.
/// </summary>
/// <param name="value">The writable value.</param>
/// <param name="bytesWritten">The number of bytes that was written to the</param>
/// <param name="allocator">The memory allocator.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static IMemoryOwner<byte> GetOwnedBytes(this IBufferWritable value, out int bytesWritten, Func<int, IMemoryOwner<byte>>? allocator = null)
{
int size = value.GetSizeOf();
IMemoryOwner<byte> owner =
allocator?.Invoke(size)
?? MemoryPool<byte>.Shared.Rent(size);
bytesWritten = value.Serialize(owner.Memory.Span[..size]);
Debug.Assert(bytesWritten == size);
return owner;
}
}
/// <summary>
/// Represents an object that can be deserialized from a span of <typeparamref name="T" /> elements.
/// </summary>
/// <remarks>
/// This interface is intended to be used for objects that exposes settable members.
/// </remarks>
/// <typeparam name="T">The span element type.</typeparam>
public interface ISpanReadable<T>
{
/// <summary>
/// Restores the instance values from its span representation.
/// </summary>
/// <param name="input">The input span reader.</param>
void Deserialize(ref SpanReader<T> input);
}
/// <summary>
/// Represents an object that can be restored from a span of <typeparamref name="T" /> elements.
/// </summary>
/// <typeparam name="TSelf">The implementing type.</typeparam>
/// <typeparam name="T">The span element type.</typeparam>
public interface ISpanReadable<out TSelf, T>
where TSelf : ISpanReadable<TSelf, T>
{
/// <summary>
/// Restores the object from its span representation.
/// </summary>
/// <param name="input">The input span reader.</param>
/// <returns>The restored object the parsing done successfully; otherwise <see langword="null"/>.</returns>
public static abstract TSelf? Read(ref SpanReader<T> input);
/// <summary>
/// Restores the object from its span representation.
/// </summary>
/// <param name="source">The source span.</param>
/// <param name="readCount">The number of elements that was read from the source span.</param>
/// <returns>The restored object the parsing done successfully; otherwise <see langword="null"/>.</returns>
public static TSelf? Read(ReadOnlySpan<T> source, out int readCount)
{
SpanReader<T> reader = new(source);
var result = TSelf.Read(ref reader);
readCount = reader.ConsumedCount;
return result;
}
/// <summary>
/// Attempts to restore the object from its span representation.
/// </summary>
/// <param name="source">The source span.</param>
/// <param name="result">The restored object.</param>
/// <param name="readCount">The number of elements that was read from the source span.</param>
/// <param name="rethrowOnFailure">Whether read exceptions should be thrown or discarded.</param>
/// <returns><see langword="true"/> if the parsing done successfully; otherwise, <see langword="false"/>.</returns>
public static bool TryRead(ReadOnlySpan<T> source, [NotNullWhen(true)] out TSelf? result, out int readCount, bool rethrowOnFailure = true)
{
SpanReader<T> reader = new(source);
try { result = TSelf.Read(ref reader); }
catch (Exception) when (rethrowOnFailure)
{
throw;
}
catch
{
result = default;
return false;
}
// The readCount must be assigned regardless of the operation success.
finally
{
readCount = reader.ConsumedCount;
}
return result != null;
}
}
/// <summary>
/// Provides extension methods for the <see cref="ISpanReadable{T}"/> interface.
/// </summary>
public static class SpanReadableExtensions
{
/// <summary>
/// Restores the instance values from its binary representation.
/// </summary>
/// <param name="value">The readable value.</param>
/// <param name="source">The source span.</param>
/// <returns>The number of bytes that was read from the source buffer.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Deserialize<T>(this ISpanReadable<T> value, ReadOnlySpan<T> source)
{
SpanReader<T> reader = new(source);
value.Deserialize(ref reader);
return reader.ConsumedCount;
}
/// <summary>
/// Attempts to restore the object from its binary representation.
/// </summary>
/// <param name="value">The readable value.</param>
/// <param name="source">The source span.</param>
/// <param name="readCount">The number of elements that was read from the source span.</param>
/// <param name="rethrowOnFailure">Whether read exceptions should be thrown or discarded.</param>
/// <returns><see langword="true"/> if the parsing done successfully; otherwise, <see langword="false"/>.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool TryDeserialize<T>(this ISpanReadable<T> value, ReadOnlySpan<T> source, out int readCount, bool rethrowOnFailure = true)
{
SpanReader<T> reader = new(source);
try { value.Deserialize(ref reader); }
catch (Exception) when (rethrowOnFailure)
{
throw;
}
catch
{
return false;
}
// The writtenCount must be assigned regardless of the operation success.
finally
{
readCount = reader.ConsumedCount;
}
return true;
}
}
/// <summary>
/// Represents helper methods to work with <see cref="SequenceReader{T}"/> buffer representations.
/// </summary>
public static class SequenceReaderExtensions
{
/// <summary>
/// Reads the value of blittable type from the raw bytes
/// represented by the memory block.
/// </summary>
/// <param name="reader">The memory reader.</param>
/// <param name="result">The value deserialized from bytes.</param>
/// <typeparam name="T">The blittable type.</typeparam>
/// <returns>
/// <see langword="true"/> if memory block contains enough amount of unread bytes to decode the value;
/// otherwise, <see langword="false"/>.
/// </returns>
public static unsafe bool TryRead<T>(this scoped ref SequenceReader<byte> reader, out T result)
where T : unmanaged
{
ReadOnlySpan<byte> unreadSpan = reader.UnreadSpan;
if (unreadSpan.Length < sizeof(T))
return TryReadMultiSegment(ref reader, out result);
result = ReadUnaligned<T>(ref MemoryMarshal.GetReference(unreadSpan));
reader.Advance(sizeof(T));
return true;
}
private static unsafe bool TryReadMultiSegment<T>(scoped ref SequenceReader<byte> reader, out T result)
where T : unmanaged
{
Span<byte> span = stackalloc byte[sizeof(T)];
if (!reader.TryCopyTo(span))
{
result = default;
return false;
}
result = ReadUnaligned<T>(ref MemoryMarshal.GetReference(span));
reader.Advance(sizeof(T));
return true;
}
/// <summary>
/// Reads the value of blittable type from the raw bytes
/// represented by the memory block.
/// </summary>
/// <param name="reader">The memory reader.</param>
/// <typeparam name="T">The blittable type.</typeparam>
/// <returns>The value deserialized from bytes.</returns>
/// <exception cref="InternalBufferOverflowException">The end of memory block is reached.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe T Read<T>(this ref SequenceReader<byte> reader) where T : unmanaged
{
if (!reader.TryReadExact(sizeof(T), out ReadOnlySequence<byte> sequence))
ThrowInternalBufferOverflowException();
ReadOnlySpan<byte> span = sequence.FirstSpan;
if (span.Length < sizeof(T))
return ReadMultiSegment<T>(in sequence);
return ReadUnaligned<T>(ref MemoryMarshal.GetReference(span));
}
private static unsafe T ReadMultiSegment<T>(in ReadOnlySequence<byte> input)
where T : unmanaged
{
Span<byte> span = stackalloc byte[sizeof(T)];
input.CopyTo(span);
return ReadUnaligned<T>(ref MemoryMarshal.GetReference(span));
}
/// <summary>
/// Decodes 16-bit signed integer.
/// </summary>
/// <param name="reader">The memory reader.</param>
/// <param name="isLittleEndian"><see langword="true"/> to use little-endian encoding; <see langword="false"/> to use big-endian encoding.</param>
/// <returns>The decoded value.</returns>
/// <exception cref="InternalBufferOverflowException">The end of memory block is reached.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static short ReadInt16(this ref SequenceReader<byte> reader, bool isLittleEndian = true)
=> isLittleEndian != BitConverter.IsLittleEndian
? ReverseEndianness(reader.Read<short>())
: reader.Read<short>();
// etc...
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment