using System.Buffers.Binary; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; namespace VersionedSerialization.Impl; file static class EndianReader { [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Span Cast(Span src) { Debug.Assert(Unsafe.SizeOf() == Unsafe.SizeOf()); return Unsafe.BitCast, Span>(src); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ReadOnlySpan Cast(ReadOnlySpan src) { Debug.Assert(Unsafe.SizeOf() == Unsafe.SizeOf()); return Unsafe.BitCast, ReadOnlySpan>(src); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void ReverseEndianness(ReadOnlySpan input, Span output) where T : unmanaged { if (RuntimeHelpers.IsReferenceOrContainsReferences()) throw new InvalidOperationException(); if (Unsafe.SizeOf() == sizeof(short)) { var src = Cast(input); var dst = Cast(output); BinaryPrimitives.ReverseEndianness(src, dst); } else if (Unsafe.SizeOf() == sizeof(int)) { var src = Cast(input); var dst = Cast(output); BinaryPrimitives.ReverseEndianness(src, dst); } else if (Unsafe.SizeOf() == sizeof(long)) { var src = Cast(input); var dst = Cast(output); BinaryPrimitives.ReverseEndianness(src, dst); } else if (Unsafe.SizeOf() == Unsafe.SizeOf()) { var src = Cast(input); var dst = Cast(output); BinaryPrimitives.ReverseEndianness(src, dst); } Debug.Assert(false, "Failed to reverse endianness for type"); } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void ReadLittleEndian(ref TReader reader, scoped Span dest) where TReader : IReader, allows ref struct where TType : unmanaged { if (BitConverter.IsLittleEndian || Unsafe.SizeOf() == sizeof(byte)) { reader.ReadPrimitive(dest); } else { var data = reader.ReadBytes(Unsafe.SizeOf() * dest.Length); var src = MemoryMarshal.Cast(data); ReverseEndianness(src, dest); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void ReadBigEndian(ref TReader reader, scoped Span dest) where TReader : IReader, allows ref struct where TType : unmanaged { if (!BitConverter.IsLittleEndian || Unsafe.SizeOf() == sizeof(byte)) { reader.ReadPrimitive(dest); } else { var data = reader.ReadBytes(Unsafe.SizeOf() * dest.Length); var src = MemoryMarshal.Cast(data); ReverseEndianness(src, dest); } } } public ref struct LittleEndianReader(TReader impl) : IReader where TReader : IReader, allows ref struct { private TReader _impl = impl; public string ReadString(int length = -1, Encoding? encoding = null) => _impl.ReadString(length, encoding); public ReadOnlySpan ReadBytes(long length) => _impl.ReadBytes(length); public void Read(scoped Span dest) where T : unmanaged => _impl.Read(dest); public void ReadPrimitive(scoped Span dest) where T : unmanaged => EndianReader.ReadLittleEndian(ref _impl, dest); } public ref struct LittleEndianSeekableReader(TReader impl) : ISeekableReader where TReader : ISeekableReader, allows ref struct { private TReader _impl = impl; public int Offset { get => _impl.Offset; set => _impl.Offset = value; } public int Length => _impl.Length; public string ReadString(int length = -1, Encoding? encoding = null) => _impl.ReadString(length, encoding); public ReadOnlySpan ReadBytes(long length) => _impl.ReadBytes(length); public void Read(scoped Span dest) where T : unmanaged => _impl.Read(dest); public void ReadPrimitive(scoped Span dest) where T : unmanaged => EndianReader.ReadLittleEndian(ref _impl, dest); } public ref struct BigEndianReader(TReader impl) : IReader where TReader : IReader, allows ref struct { private TReader _impl = impl; public string ReadString(int length = -1, Encoding? encoding = null) => _impl.ReadString(length, encoding); public ReadOnlySpan ReadBytes(long length) => _impl.ReadBytes(length); public void Read(scoped Span dest) where T : unmanaged => _impl.Read(dest); public void ReadPrimitive(scoped Span dest) where T : unmanaged => EndianReader.ReadBigEndian(ref _impl, dest); } public ref struct BigEndianSeekableReader(TReader impl) : ISeekableReader where TReader : ISeekableReader, allows ref struct { private TReader _impl = impl; public int Offset { get => _impl.Offset; set => _impl.Offset = value; } public int Length => _impl.Length; public string ReadString(int length = -1, Encoding? encoding = null) => _impl.ReadString(length, encoding); public ReadOnlySpan ReadBytes(long length) => _impl.ReadBytes(length); public void Read(scoped Span dest) where T : unmanaged => _impl.Read(dest); public void ReadPrimitive(scoped Span dest) where T : unmanaged => EndianReader.ReadBigEndian(ref _impl, dest); }