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 MemoryPack.Internal; using System.Buffers; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace MemoryPack { public static partial class MemoryPackSerializer { [ThreadStatic] static MemoryPackReaderOptionalState? threadStaticReaderOptionalState; public static T? Deserialize(ReadOnlySpan buffer, MemoryPackSerializerOptions? options = default) { T? value = default; Deserialize(buffer, ref value, options); return value; } public static int Deserialize(ReadOnlySpan buffer, ref T? value, MemoryPackSerializerOptions? options = default) { if (!RuntimeHelpers.IsReferenceOrContainsReferences()) { if (buffer.Length < Unsafe.SizeOf()) { MemoryPackSerializationException.ThrowInvalidRange(Unsafe.SizeOf(), buffer.Length); } value = Unsafe.ReadUnaligned(ref MemoryMarshal.GetReference(buffer)); return Unsafe.SizeOf(); } var state = threadStaticReaderOptionalState; if (state == null) { state = threadStaticReaderOptionalState = new MemoryPackReaderOptionalState(); } state.Init(options); var reader = new MemoryPackReader(buffer, state); try { reader.ReadValue(ref value); return reader.Consumed; } finally { reader.Dispose(); state.Reset(); } } public static T? Deserialize(in ReadOnlySequence buffer, MemoryPackSerializerOptions? options = default) { T? value = default; Deserialize(buffer, ref value, options); return value; } public static int Deserialize(in ReadOnlySequence buffer, ref T? value, MemoryPackSerializerOptions? options = default) { var state = threadStaticReaderOptionalState; if (state == null) { state = threadStaticReaderOptionalState = new MemoryPackReaderOptionalState(); } state.Init(options); var reader = new MemoryPackReader(buffer, state); try { reader.ReadValue(ref value); return reader.Consumed; } finally { reader.Dispose(); state.Reset(); } } public static async ValueTask DeserializeAsync(Stream stream, MemoryPackSerializerOptions? options = default, CancellationToken cancellationToken = default) { if (stream is MemoryStream ms && ms.TryGetBuffer(out ArraySegment streamBuffer)) { cancellationToken.ThrowIfCancellationRequested(); T? value = default; var bytesRead = Deserialize(streamBuffer.AsSpan(checked((int)ms.Position)), ref value, options); // Emulate that we had actually "read" from the stream. ms.Seek(bytesRead, SeekOrigin.Current); return value; } var builder = ReusableReadOnlySequenceBuilderPool.Rent(); try { var buffer = ArrayPool.Shared.Rent(65536); // initial 64K var offset = 0; do { if (offset == buffer.Length) { builder.Add(buffer, returnToPool: true); buffer = ArrayPool.Shared.Rent(MathEx.NewArrayCapacity(buffer.Length)); offset = 0; } int read = 0; try { read = await stream.ReadAsync(buffer.AsMemory(offset, buffer.Length - offset), cancellationToken).ConfigureAwait(false); } catch { // buffer is not added in builder, so return here. ArrayPool.Shared.Return(buffer); throw; } offset += read; if (read == 0) { builder.Add(buffer.AsMemory(0, offset), returnToPool: true); break; } } while (true); // If single buffer, we can avoid ReadOnlySequence build cost. if (builder.TryGetSingleMemory(out var memory)) { return Deserialize(memory.Span, options); } else { var seq = builder.Build(); var result = Deserialize(seq, options); return result; } } finally { builder.Reset(); } } } }