mirror of
https://github.com/StarBeat/UnityDependencyAnalyzer.git
synced 2026-03-08 05:35:27 +08:00
930 lines
25 KiB
C#
930 lines
25 KiB
C#
|
|
using System;
|
||
|
|
using System.Collections.Generic;
|
||
|
|
using System.IO;
|
||
|
|
using System.Linq;
|
||
|
|
using System.Net.Http;
|
||
|
|
using System.Threading;
|
||
|
|
using System.Threading.Tasks;
|
||
|
|
|
||
|
|
#nullable enable
|
||
|
|
using System.Buffers;
|
||
|
|
using System.Runtime.CompilerServices;
|
||
|
|
using System.Runtime.InteropServices;
|
||
|
|
using System.Text;
|
||
|
|
#if NET7_0_OR_GREATER
|
||
|
|
using System.Text.Unicode;
|
||
|
|
#endif
|
||
|
|
|
||
|
|
namespace MemoryPack {
|
||
|
|
|
||
|
|
#if NET7_0_OR_GREATER
|
||
|
|
using static GC;
|
||
|
|
using static MemoryMarshal;
|
||
|
|
#else
|
||
|
|
using static MemoryPack.Internal.MemoryMarshalEx;
|
||
|
|
#endif
|
||
|
|
|
||
|
|
[StructLayout(LayoutKind.Auto)]
|
||
|
|
public ref partial struct MemoryPackReader
|
||
|
|
{
|
||
|
|
ReadOnlySequence<byte> bufferSource;
|
||
|
|
readonly long totalLength;
|
||
|
|
#if NET7_0_OR_GREATER
|
||
|
|
ref byte bufferReference;
|
||
|
|
#else
|
||
|
|
ReadOnlySpan<byte> bufferReference;
|
||
|
|
#endif
|
||
|
|
int bufferLength;
|
||
|
|
byte[]? rentBuffer;
|
||
|
|
int advancedCount;
|
||
|
|
int consumed; // total length of consumed
|
||
|
|
readonly MemoryPackReaderOptionalState optionalState;
|
||
|
|
|
||
|
|
public int Consumed => consumed;
|
||
|
|
public long Remaining => totalLength - consumed;
|
||
|
|
public MemoryPackReaderOptionalState OptionalState => optionalState;
|
||
|
|
public MemoryPackSerializerOptions Options => optionalState.Options;
|
||
|
|
|
||
|
|
public MemoryPackReader(in ReadOnlySequence<byte> sequence, MemoryPackReaderOptionalState optionalState)
|
||
|
|
{
|
||
|
|
this.bufferSource = sequence.IsSingleSegment ? ReadOnlySequence<byte>.Empty : sequence;
|
||
|
|
var span = sequence.FirstSpan;
|
||
|
|
#if NET7_0_OR_GREATER
|
||
|
|
this.bufferReference = ref MemoryMarshal.GetReference(span);
|
||
|
|
#else
|
||
|
|
this.bufferReference = span;
|
||
|
|
#endif
|
||
|
|
this.bufferLength = span.Length;
|
||
|
|
this.advancedCount = 0;
|
||
|
|
this.consumed = 0;
|
||
|
|
this.rentBuffer = null;
|
||
|
|
this.totalLength = sequence.Length;
|
||
|
|
this.optionalState = optionalState;
|
||
|
|
}
|
||
|
|
|
||
|
|
public MemoryPackReader(ReadOnlySpan<byte> buffer, MemoryPackReaderOptionalState optionalState)
|
||
|
|
{
|
||
|
|
this.bufferSource = ReadOnlySequence<byte>.Empty;
|
||
|
|
#if NET7_0_OR_GREATER
|
||
|
|
this.bufferReference = ref MemoryMarshal.GetReference(buffer);
|
||
|
|
#else
|
||
|
|
this.bufferReference = buffer;
|
||
|
|
#endif
|
||
|
|
this.bufferLength = buffer.Length;
|
||
|
|
this.advancedCount = 0;
|
||
|
|
this.consumed = 0;
|
||
|
|
this.rentBuffer = null;
|
||
|
|
this.totalLength = buffer.Length;
|
||
|
|
this.optionalState = optionalState;
|
||
|
|
}
|
||
|
|
|
||
|
|
// buffer operations
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
public ref byte GetSpanReference(int sizeHint)
|
||
|
|
{
|
||
|
|
if (sizeHint <= bufferLength)
|
||
|
|
{
|
||
|
|
#if NET7_0_OR_GREATER
|
||
|
|
return ref bufferReference;
|
||
|
|
#else
|
||
|
|
return ref MemoryMarshal.GetReference(bufferReference);
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
return ref GetNextSpan(sizeHint);
|
||
|
|
}
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||
|
|
ref byte GetNextSpan(int sizeHint)
|
||
|
|
{
|
||
|
|
if (rentBuffer != null)
|
||
|
|
{
|
||
|
|
ArrayPool<byte>.Shared.Return(rentBuffer);
|
||
|
|
rentBuffer = null;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (Remaining == 0)
|
||
|
|
{
|
||
|
|
MemoryPackSerializationException.ThrowSequenceReachedEnd();
|
||
|
|
}
|
||
|
|
|
||
|
|
try
|
||
|
|
{
|
||
|
|
bufferSource = bufferSource.Slice(advancedCount);
|
||
|
|
}
|
||
|
|
catch (ArgumentOutOfRangeException)
|
||
|
|
{
|
||
|
|
MemoryPackSerializationException.ThrowSequenceReachedEnd();
|
||
|
|
}
|
||
|
|
|
||
|
|
advancedCount = 0;
|
||
|
|
|
||
|
|
if (sizeHint <= Remaining)
|
||
|
|
{
|
||
|
|
if (sizeHint <= bufferSource.FirstSpan.Length)
|
||
|
|
{
|
||
|
|
#if NET7_0_OR_GREATER
|
||
|
|
bufferReference = ref MemoryMarshal.GetReference(bufferSource.FirstSpan);
|
||
|
|
bufferLength = bufferSource.FirstSpan.Length;
|
||
|
|
return ref bufferReference;
|
||
|
|
#else
|
||
|
|
bufferReference = bufferSource.FirstSpan;
|
||
|
|
bufferLength = bufferSource.FirstSpan.Length;
|
||
|
|
return ref MemoryMarshal.GetReference(bufferReference);
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
rentBuffer = ArrayPool<byte>.Shared.Rent(sizeHint);
|
||
|
|
bufferSource.Slice(0, sizeHint).CopyTo(rentBuffer);
|
||
|
|
var span = rentBuffer.AsSpan(0, sizeHint);
|
||
|
|
#if NET7_0_OR_GREATER
|
||
|
|
bufferReference = ref MemoryMarshal.GetReference(span);
|
||
|
|
bufferLength = span.Length;
|
||
|
|
return ref bufferReference;
|
||
|
|
#else
|
||
|
|
bufferReference = span;
|
||
|
|
bufferLength = span.Length;
|
||
|
|
return ref MemoryMarshal.GetReference(bufferReference);
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
MemoryPackSerializationException.ThrowSequenceReachedEnd();
|
||
|
|
#if NET7_0_OR_GREATER
|
||
|
|
return ref bufferReference; // dummy.
|
||
|
|
#else
|
||
|
|
return ref MemoryMarshal.GetReference(bufferReference);
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
public void Advance(int count)
|
||
|
|
{
|
||
|
|
if (count == 0) return;
|
||
|
|
|
||
|
|
var rest = bufferLength - count;
|
||
|
|
if (rest < 0)
|
||
|
|
{
|
||
|
|
if (TryAdvanceSequence(count))
|
||
|
|
{
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
bufferLength = rest;
|
||
|
|
#if NET7_0_OR_GREATER
|
||
|
|
bufferReference = ref Unsafe.Add(ref bufferReference, count);
|
||
|
|
#else
|
||
|
|
bufferReference = bufferReference.Slice(count);
|
||
|
|
#endif
|
||
|
|
advancedCount += count;
|
||
|
|
consumed += count;
|
||
|
|
}
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||
|
|
bool TryAdvanceSequence(int count)
|
||
|
|
{
|
||
|
|
var rest = bufferSource.Length - count;
|
||
|
|
if (rest < 0)
|
||
|
|
{
|
||
|
|
MemoryPackSerializationException.ThrowInvalidAdvance();
|
||
|
|
}
|
||
|
|
|
||
|
|
bufferSource = bufferSource.Slice(advancedCount + count);
|
||
|
|
#if NET7_0_OR_GREATER
|
||
|
|
bufferReference = ref MemoryMarshal.GetReference(bufferSource.FirstSpan);
|
||
|
|
#else
|
||
|
|
bufferReference = bufferSource.FirstSpan;
|
||
|
|
#endif
|
||
|
|
bufferLength = bufferSource.FirstSpan.Length;
|
||
|
|
advancedCount = 0;
|
||
|
|
consumed += count;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
public void GetRemainingSource(out ReadOnlySpan<byte> singleSource, out ReadOnlySequence<byte> remainingSource)
|
||
|
|
{
|
||
|
|
if (bufferSource.IsEmpty)
|
||
|
|
{
|
||
|
|
remainingSource = ReadOnlySequence<byte>.Empty;
|
||
|
|
#if NET7_0_OR_GREATER
|
||
|
|
singleSource = MemoryMarshal.CreateReadOnlySpan(ref bufferReference, bufferLength);
|
||
|
|
#else
|
||
|
|
singleSource = bufferReference;
|
||
|
|
#endif
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
if (bufferSource.IsSingleSegment)
|
||
|
|
{
|
||
|
|
remainingSource = ReadOnlySequence<byte>.Empty;
|
||
|
|
singleSource = bufferSource.FirstSpan.Slice(advancedCount);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
singleSource = default;
|
||
|
|
remainingSource = bufferSource.Slice(advancedCount);
|
||
|
|
if (remainingSource.IsSingleSegment)
|
||
|
|
{
|
||
|
|
singleSource = remainingSource.FirstSpan;
|
||
|
|
remainingSource = ReadOnlySequence<byte>.Empty;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
public void Dispose()
|
||
|
|
{
|
||
|
|
if (rentBuffer != null)
|
||
|
|
{
|
||
|
|
ArrayPool<byte>.Shared.Return(rentBuffer);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
public IMemoryPackFormatter GetFormatter(Type type)
|
||
|
|
{
|
||
|
|
return MemoryPackFormatterProvider.GetFormatter(type);
|
||
|
|
}
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
public IMemoryPackFormatter<T> GetFormatter<T>()
|
||
|
|
{
|
||
|
|
return MemoryPackFormatterProvider.GetFormatter<T>();
|
||
|
|
}
|
||
|
|
|
||
|
|
// read methods
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
public bool TryReadObjectHeader(out byte memberCount)
|
||
|
|
{
|
||
|
|
memberCount = GetSpanReference(1);
|
||
|
|
Advance(1);
|
||
|
|
return memberCount != MemoryPackCode.NullObject;
|
||
|
|
}
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
public bool TryReadUnionHeader(out ushort tag)
|
||
|
|
{
|
||
|
|
var firstTag = GetSpanReference(1);
|
||
|
|
Advance(1);
|
||
|
|
if (firstTag < MemoryPackCode.WideTag)
|
||
|
|
{
|
||
|
|
tag = firstTag;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
else if (firstTag == MemoryPackCode.WideTag)
|
||
|
|
{
|
||
|
|
ReadUnmanaged(out tag);
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
tag = 0;
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
public bool TryReadCollectionHeader(out int length)
|
||
|
|
{
|
||
|
|
length = Unsafe.ReadUnaligned<int>(ref GetSpanReference(4));
|
||
|
|
Advance(4);
|
||
|
|
|
||
|
|
// If collection-length is larger than buffer-length, it is invalid data.
|
||
|
|
if (Remaining < length)
|
||
|
|
{
|
||
|
|
MemoryPackSerializationException.ThrowInsufficientBufferUnless(length);
|
||
|
|
}
|
||
|
|
|
||
|
|
return length != MemoryPackCode.NullCollection;
|
||
|
|
}
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
public bool PeekIsNull()
|
||
|
|
{
|
||
|
|
var code = GetSpanReference(1);
|
||
|
|
return code == MemoryPackCode.NullObject;
|
||
|
|
}
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
public bool TryPeekObjectHeader(out byte memberCount)
|
||
|
|
{
|
||
|
|
memberCount = GetSpanReference(1);
|
||
|
|
return memberCount != MemoryPackCode.NullObject;
|
||
|
|
}
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
public bool TryPeekUnionHeader(out ushort tag)
|
||
|
|
{
|
||
|
|
var firstTag = GetSpanReference(1);
|
||
|
|
if (firstTag < MemoryPackCode.WideTag)
|
||
|
|
{
|
||
|
|
tag = firstTag;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
else if (firstTag == MemoryPackCode.WideTag)
|
||
|
|
{
|
||
|
|
ref var spanRef = ref GetSpanReference(sizeof(ushort) + 1); // skip firstTag
|
||
|
|
tag = Unsafe.ReadUnaligned<ushort>(ref Unsafe.Add(ref spanRef, 1));
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
tag = 0;
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
public bool TryPeekCollectionHeader(out int length)
|
||
|
|
{
|
||
|
|
length = Unsafe.ReadUnaligned<int>(ref GetSpanReference(4));
|
||
|
|
|
||
|
|
// If collection-length is larger than buffer-length, it is invalid data.
|
||
|
|
if (Remaining < length)
|
||
|
|
{
|
||
|
|
MemoryPackSerializationException.ThrowInsufficientBufferUnless(length);
|
||
|
|
}
|
||
|
|
|
||
|
|
return length != MemoryPackCode.NullCollection;
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>
|
||
|
|
/// no validate collection size, be careful to use.
|
||
|
|
/// </summary>
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
public bool DangerousTryReadCollectionHeader(out int length)
|
||
|
|
{
|
||
|
|
length = Unsafe.ReadUnaligned<int>(ref GetSpanReference(4));
|
||
|
|
Advance(4);
|
||
|
|
|
||
|
|
return length != MemoryPackCode.NullCollection;
|
||
|
|
}
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
public string? ReadString()
|
||
|
|
{
|
||
|
|
if (!TryReadCollectionHeader(out var length))
|
||
|
|
{
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
if (length == 0)
|
||
|
|
{
|
||
|
|
return "";
|
||
|
|
}
|
||
|
|
|
||
|
|
if (length > 0)
|
||
|
|
{
|
||
|
|
return ReadUtf16(length);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
return ReadUtf8(length);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
string ReadUtf16(int length)
|
||
|
|
{
|
||
|
|
var byteCount = checked(length * 2);
|
||
|
|
ref var src = ref GetSpanReference(byteCount);
|
||
|
|
|
||
|
|
var str = new string(MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As<byte, char>(ref src), length));
|
||
|
|
|
||
|
|
Advance(byteCount);
|
||
|
|
|
||
|
|
return str;
|
||
|
|
}
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.NoInlining)] // non default, no inline
|
||
|
|
string ReadUtf8(int utf8Length)
|
||
|
|
{
|
||
|
|
// (int ~utf8-byte-count, int utf16-length, utf8-bytes)
|
||
|
|
// already read utf8 length, but it is complement.
|
||
|
|
|
||
|
|
utf8Length = ~utf8Length;
|
||
|
|
|
||
|
|
ref var spanRef = ref GetSpanReference(utf8Length + 4); // + read utf16 length
|
||
|
|
|
||
|
|
string str;
|
||
|
|
var utf16Length = Unsafe.ReadUnaligned<int>(ref spanRef);
|
||
|
|
|
||
|
|
if (utf16Length <= 0)
|
||
|
|
{
|
||
|
|
var src = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.Add(ref spanRef, 4), utf8Length);
|
||
|
|
str = Encoding.UTF8.GetString(src);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
// check malformed utf16Length
|
||
|
|
var max = unchecked((Remaining + 1) * 3);
|
||
|
|
if (max < 0) max = int.MaxValue;
|
||
|
|
if (max < utf16Length)
|
||
|
|
{
|
||
|
|
MemoryPackSerializationException.ThrowInsufficientBufferUnless(utf8Length);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
#if NET7_0_OR_GREATER
|
||
|
|
// regular path, know decoded UTF16 length will gets faster decode result
|
||
|
|
unsafe
|
||
|
|
{
|
||
|
|
fixed (byte* p = &Unsafe.Add(ref spanRef, 4))
|
||
|
|
{
|
||
|
|
str = string.Create(utf16Length, ((IntPtr)p, utf8Length), static (dest, state) =>
|
||
|
|
{
|
||
|
|
var src = MemoryMarshal.CreateSpan(ref Unsafe.AsRef<byte>((byte*)state.Item1), state.Item2);
|
||
|
|
var status = Utf8.ToUtf16(src, dest, out var bytesRead, out var charsWritten, replaceInvalidSequences: false);
|
||
|
|
if (status != OperationStatus.Done)
|
||
|
|
{
|
||
|
|
MemoryPackSerializationException.ThrowFailedEncoding(status);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
#else
|
||
|
|
var src = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.Add(ref spanRef, 4), utf8Length);
|
||
|
|
str = Encoding.UTF8.GetString(src);
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
Advance(utf8Length + 4);
|
||
|
|
|
||
|
|
return str;
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
public T1 ReadUnmanaged<T1>()
|
||
|
|
where T1 : unmanaged
|
||
|
|
{
|
||
|
|
var size = Unsafe.SizeOf<T1>();
|
||
|
|
ref var spanRef = ref GetSpanReference(size);
|
||
|
|
var value1 = Unsafe.ReadUnaligned<T1>(ref spanRef);
|
||
|
|
Advance(size);
|
||
|
|
return value1;
|
||
|
|
}
|
||
|
|
|
||
|
|
#if NET7_0_OR_GREATER
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
public void ReadPackable<T>(ref T? value)
|
||
|
|
where T : IMemoryPackable<T>
|
||
|
|
{
|
||
|
|
T.Deserialize(ref this, ref value);
|
||
|
|
}
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
public T? ReadPackable<T>()
|
||
|
|
where T : IMemoryPackable<T>
|
||
|
|
{
|
||
|
|
T? value = default;
|
||
|
|
T.Deserialize(ref this, ref value);
|
||
|
|
return value;
|
||
|
|
}
|
||
|
|
|
||
|
|
#else
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
public void ReadPackable<T>(ref T? value)
|
||
|
|
where T : IMemoryPackable<T>
|
||
|
|
{
|
||
|
|
ReadValue(ref value);
|
||
|
|
}
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
public T? ReadPackable<T>()
|
||
|
|
where T : IMemoryPackable<T>
|
||
|
|
{
|
||
|
|
return ReadValue<T>();
|
||
|
|
}
|
||
|
|
|
||
|
|
#endif
|
||
|
|
|
||
|
|
// non packable, get formatter dynamically.
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
public void ReadValue<T>(ref T? value)
|
||
|
|
{
|
||
|
|
GetFormatter<T>().Deserialize(ref this, ref value);
|
||
|
|
}
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
public T? ReadValue<T>()
|
||
|
|
{
|
||
|
|
T? value = default;
|
||
|
|
GetFormatter<T>().Deserialize(ref this, ref value);
|
||
|
|
return value;
|
||
|
|
}
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
public void ReadValue(Type type, ref object? value)
|
||
|
|
{
|
||
|
|
GetFormatter(type).Deserialize(ref this, ref value);
|
||
|
|
}
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
public object? ReadValue(Type type)
|
||
|
|
{
|
||
|
|
object? value = default;
|
||
|
|
GetFormatter(type).Deserialize(ref this, ref value);
|
||
|
|
return value;
|
||
|
|
}
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
public void ReadValueWithFormatter<TFormatter, T>(TFormatter formatter, ref T? value)
|
||
|
|
where TFormatter : IMemoryPackFormatter<T>
|
||
|
|
{
|
||
|
|
formatter.Deserialize(ref this, ref value);
|
||
|
|
}
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
public T? ReadValueWithFormatter<TFormatter, T>(TFormatter formatter)
|
||
|
|
where TFormatter : IMemoryPackFormatter<T>
|
||
|
|
{
|
||
|
|
T? value = default;
|
||
|
|
formatter.Deserialize(ref this, ref value);
|
||
|
|
return value;
|
||
|
|
}
|
||
|
|
|
||
|
|
#region ReadArray/Span
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
public T?[]? ReadArray<T>()
|
||
|
|
{
|
||
|
|
T?[]? value = default;
|
||
|
|
ReadArray(ref value);
|
||
|
|
return value;
|
||
|
|
}
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
public void ReadArray<T>(ref T?[]? value)
|
||
|
|
{
|
||
|
|
if (!RuntimeHelpers.IsReferenceOrContainsReferences<T>())
|
||
|
|
{
|
||
|
|
DangerousReadUnmanagedArray(ref value);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!TryReadCollectionHeader(out var length))
|
||
|
|
{
|
||
|
|
value = null;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (length == 0)
|
||
|
|
{
|
||
|
|
value = Array.Empty<T>();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// T[] support overwrite
|
||
|
|
if (value == null || value.Length != length)
|
||
|
|
{
|
||
|
|
value = new T[length];
|
||
|
|
}
|
||
|
|
|
||
|
|
var formatter = GetFormatter<T>();
|
||
|
|
for (int i = 0; i < length; i++)
|
||
|
|
{
|
||
|
|
formatter.Deserialize(ref this, ref value[i]);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
public void ReadSpan<T>(ref Span<T?> value)
|
||
|
|
{
|
||
|
|
if (!RuntimeHelpers.IsReferenceOrContainsReferences<T>())
|
||
|
|
{
|
||
|
|
DangerousReadUnmanagedSpan(ref value);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!TryReadCollectionHeader(out var length))
|
||
|
|
{
|
||
|
|
value = default;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (length == 0)
|
||
|
|
{
|
||
|
|
value = Array.Empty<T>();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (value.Length != length)
|
||
|
|
{
|
||
|
|
value = new T[length];
|
||
|
|
}
|
||
|
|
|
||
|
|
var formatter = GetFormatter<T>();
|
||
|
|
for (int i = 0; i < length; i++)
|
||
|
|
{
|
||
|
|
formatter.Deserialize(ref this, ref value[i]);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
public T?[]? ReadPackableArray<T>()
|
||
|
|
where T : IMemoryPackable<T>
|
||
|
|
{
|
||
|
|
T?[]? value = default;
|
||
|
|
ReadPackableArray(ref value);
|
||
|
|
return value;
|
||
|
|
}
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
public void ReadPackableArray<T>(ref T?[]? value)
|
||
|
|
where T : IMemoryPackable<T>
|
||
|
|
{
|
||
|
|
#if !NET7_0_OR_GREATER
|
||
|
|
ReadArray(ref value);
|
||
|
|
return;
|
||
|
|
#else
|
||
|
|
if (!RuntimeHelpers.IsReferenceOrContainsReferences<T>())
|
||
|
|
{
|
||
|
|
DangerousReadUnmanagedArray(ref value);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!TryReadCollectionHeader(out var length))
|
||
|
|
{
|
||
|
|
value = null;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (length == 0)
|
||
|
|
{
|
||
|
|
value = Array.Empty<T>();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// T[] support overwrite
|
||
|
|
if (value == null || value.Length != length)
|
||
|
|
{
|
||
|
|
value = new T[length];
|
||
|
|
}
|
||
|
|
|
||
|
|
for (int i = 0; i < length; i++)
|
||
|
|
{
|
||
|
|
T.Deserialize(ref this, ref value[i]);
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
public void ReadPackableSpan<T>(ref Span<T?> value)
|
||
|
|
where T : IMemoryPackable<T>
|
||
|
|
{
|
||
|
|
#if !NET7_0_OR_GREATER
|
||
|
|
ReadSpan(ref value);
|
||
|
|
return;
|
||
|
|
#else
|
||
|
|
if (!RuntimeHelpers.IsReferenceOrContainsReferences<T>())
|
||
|
|
{
|
||
|
|
DangerousReadUnmanagedSpan(ref value);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!TryReadCollectionHeader(out var length))
|
||
|
|
{
|
||
|
|
value = default;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (length == 0)
|
||
|
|
{
|
||
|
|
value = Array.Empty<T>();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (value.Length != length)
|
||
|
|
{
|
||
|
|
value = new T[length];
|
||
|
|
}
|
||
|
|
|
||
|
|
for (int i = 0; i < length; i++)
|
||
|
|
{
|
||
|
|
T.Deserialize(ref this, ref value[i]);
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
#endregion
|
||
|
|
|
||
|
|
#region UnmanagedArray/Span
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
public T[]? ReadUnmanagedArray<T>()
|
||
|
|
where T : unmanaged
|
||
|
|
{
|
||
|
|
return DangerousReadUnmanagedArray<T>();
|
||
|
|
}
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
public void ReadUnmanagedArray<T>(ref T[]? value)
|
||
|
|
where T : unmanaged
|
||
|
|
{
|
||
|
|
DangerousReadUnmanagedArray<T>(ref value);
|
||
|
|
}
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
public void ReadUnmanagedSpan<T>(ref Span<T> value)
|
||
|
|
where T : unmanaged
|
||
|
|
{
|
||
|
|
DangerousReadUnmanagedSpan<T>(ref value);
|
||
|
|
}
|
||
|
|
|
||
|
|
// T: should be unamanged type
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
public unsafe T[]? DangerousReadUnmanagedArray<T>()
|
||
|
|
{
|
||
|
|
if (!TryReadCollectionHeader(out var length))
|
||
|
|
{
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (length == 0) return Array.Empty<T>();
|
||
|
|
|
||
|
|
var byteCount = length * Unsafe.SizeOf<T>();
|
||
|
|
ref var src = ref GetSpanReference(byteCount);
|
||
|
|
var dest = AllocateUninitializedArray<T>(length);
|
||
|
|
Unsafe.CopyBlockUnaligned(ref Unsafe.As<T, byte>(ref GetArrayDataReference(dest)), ref src, (uint)byteCount);
|
||
|
|
Advance(byteCount);
|
||
|
|
|
||
|
|
return dest;
|
||
|
|
}
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
public unsafe void DangerousReadUnmanagedArray<T>(ref T[]? value)
|
||
|
|
{
|
||
|
|
if (!TryReadCollectionHeader(out var length))
|
||
|
|
{
|
||
|
|
value = null;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (length == 0)
|
||
|
|
{
|
||
|
|
value = Array.Empty<T>();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
var byteCount = length * Unsafe.SizeOf<T>();
|
||
|
|
ref var src = ref GetSpanReference(byteCount);
|
||
|
|
|
||
|
|
if (value == null || value.Length != length)
|
||
|
|
{
|
||
|
|
value = AllocateUninitializedArray<T>(length);
|
||
|
|
}
|
||
|
|
|
||
|
|
ref var dest = ref Unsafe.As<T, byte>(ref GetArrayDataReference(value));
|
||
|
|
Unsafe.CopyBlockUnaligned(ref dest, ref src, (uint)byteCount);
|
||
|
|
|
||
|
|
Advance(byteCount);
|
||
|
|
}
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
public unsafe void DangerousReadUnmanagedSpan<T>(ref Span<T> value)
|
||
|
|
{
|
||
|
|
if (!TryReadCollectionHeader(out var length))
|
||
|
|
{
|
||
|
|
value = default;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (length == 0)
|
||
|
|
{
|
||
|
|
value = Array.Empty<T>();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
var byteCount = length * Unsafe.SizeOf<T>();
|
||
|
|
ref var src = ref GetSpanReference(byteCount);
|
||
|
|
|
||
|
|
if (value == null || value.Length != length)
|
||
|
|
{
|
||
|
|
value = AllocateUninitializedArray<T>(length);
|
||
|
|
}
|
||
|
|
|
||
|
|
ref var dest = ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(value));
|
||
|
|
Unsafe.CopyBlockUnaligned(ref dest, ref src, (uint)byteCount);
|
||
|
|
|
||
|
|
Advance(byteCount);
|
||
|
|
}
|
||
|
|
|
||
|
|
#endregion
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
public void ReadSpanWithoutReadLengthHeader<T>(int length, ref Span<T?> value)
|
||
|
|
{
|
||
|
|
if (length == 0)
|
||
|
|
{
|
||
|
|
value = Array.Empty<T>();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!RuntimeHelpers.IsReferenceOrContainsReferences<T>())
|
||
|
|
{
|
||
|
|
if (value.Length != length)
|
||
|
|
{
|
||
|
|
value = AllocateUninitializedArray<T>(length);
|
||
|
|
}
|
||
|
|
|
||
|
|
var byteCount = length * Unsafe.SizeOf<T>();
|
||
|
|
ref var src = ref GetSpanReference(byteCount);
|
||
|
|
ref var dest = ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(value)!);
|
||
|
|
Unsafe.CopyBlockUnaligned(ref dest, ref src, (uint)byteCount);
|
||
|
|
|
||
|
|
Advance(byteCount);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
if (value.Length != length)
|
||
|
|
{
|
||
|
|
value = new T[length];
|
||
|
|
}
|
||
|
|
|
||
|
|
var formatter = GetFormatter<T>();
|
||
|
|
for (int i = 0; i < length; i++)
|
||
|
|
{
|
||
|
|
formatter.Deserialize(ref this, ref value[i]);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
public void ReadPackableSpanWithoutReadLengthHeader<T>(int length, ref Span<T?> value)
|
||
|
|
where T : IMemoryPackable<T>
|
||
|
|
{
|
||
|
|
#if !NET7_0_OR_GREATER
|
||
|
|
ReadSpanWithoutReadLengthHeader(length, ref value);
|
||
|
|
return;
|
||
|
|
#else
|
||
|
|
if (length == 0)
|
||
|
|
{
|
||
|
|
value = Array.Empty<T>();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (!RuntimeHelpers.IsReferenceOrContainsReferences<T>())
|
||
|
|
{
|
||
|
|
if (value.Length != length)
|
||
|
|
{
|
||
|
|
value = AllocateUninitializedArray<T>(length);
|
||
|
|
}
|
||
|
|
|
||
|
|
var byteCount = length * Unsafe.SizeOf<T>();
|
||
|
|
ref var src = ref GetSpanReference(byteCount);
|
||
|
|
ref var dest = ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(value)!);
|
||
|
|
Unsafe.CopyBlockUnaligned(ref dest, ref src, (uint)byteCount);
|
||
|
|
|
||
|
|
Advance(byteCount);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
if (value.Length != length)
|
||
|
|
{
|
||
|
|
value = new T[length];
|
||
|
|
}
|
||
|
|
|
||
|
|
for (int i = 0; i < length; i++)
|
||
|
|
{
|
||
|
|
T.Deserialize(ref this, ref value[i]);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
#endif
|
||
|
|
}
|
||
|
|
|
||
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||
|
|
public unsafe void DangerousReadUnmanagedSpanView<T>(out bool isNull, out ReadOnlySpan<byte> view)
|
||
|
|
{
|
||
|
|
if (!TryReadCollectionHeader(out var length))
|
||
|
|
{
|
||
|
|
isNull = true;
|
||
|
|
view = default;
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
isNull = false;
|
||
|
|
|
||
|
|
if (length == 0)
|
||
|
|
{
|
||
|
|
view = Array.Empty<byte>();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
var byteCount = length * Unsafe.SizeOf<T>();
|
||
|
|
ref var src = ref GetSpanReference(byteCount);
|
||
|
|
|
||
|
|
var span = MemoryMarshal.CreateReadOnlySpan(ref src, byteCount);
|
||
|
|
|
||
|
|
Advance(byteCount);
|
||
|
|
view = span; // safe until call next GetSpanReference
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|