using System; using System.Buffers.Binary; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace ZeroLevel.Services.HashFunctions { internal static class Utils { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ReadOnlySpan PopAll(this ref ReadOnlySpan @this) where TTo : struct { #if NETCOREAPP3_0 var totBytes = @this.Length; var toLength = (totBytes / Unsafe.SizeOf()); var sliceLength = toLength * Unsafe.SizeOf(); ref var thisRef = ref MemoryMarshal.GetReference(@this); @this = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.Add(ref thisRef, sliceLength), totBytes - sliceLength); return MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As(ref thisRef), toLength); #else return @this.PopAll(); #endif } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ReadOnlySpan PopAll(this ref ReadOnlySpan @this) where TFrom : struct where TTo : struct { var totBytes = @this.Length * Unsafe.SizeOf(); var toLength = (totBytes / Unsafe.SizeOf()); var sliceLength = toLength * Unsafe.SizeOf() / Unsafe.SizeOf(); #if NETSTANDARD2_0 var result = MemoryMarshal.Cast(@this); #else var result = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.As(ref MemoryMarshal.GetReference(@this)), toLength); #endif @this = @this.Slice(sliceLength); return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint AsLittleEndian(this uint @this) { if (BitConverter.IsLittleEndian) { return @this; } return BinaryPrimitives.ReverseEndianness(@this); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ulong AsLittleEndian(this ulong @this) { if (BitConverter.IsLittleEndian) { return @this; } return BinaryPrimitives.ReverseEndianness(@this); } public static bool TryPop(this ref ReadOnlySpan @this, int count, out ReadOnlySpan popped) where TTo : struct { var byteCount = count * Unsafe.SizeOf(); if (@this.Length >= byteCount) { popped = MemoryMarshal.Cast(@this.Slice(0, byteCount)); @this = @this.Slice(byteCount); return true; } popped = default; return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref readonly TTo First(this ReadOnlySpan @this) where TTo : struct { return ref MemoryMarshal.Cast(@this)[0]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref readonly TTo Last(this ReadOnlySpan @this) where TTo : struct { return ref MemoryMarshal.Cast(@this.Slice(@this.Length - Unsafe.SizeOf()))[0]; } public static ref readonly TTo First(this ReadOnlySpan @this) where TTo : struct where TFrom : struct { #if NETSTANDARD2_0 return ref MemoryMarshal.Cast(@this)[0]; #else //TODO: is this version actually any faster/better at all? return ref MemoryMarshal.AsRef(MemoryMarshal.AsBytes(@this)); #endif } } public static class Safeish { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref readonly TTo As(in TFrom from) where TTo : struct where TFrom : struct { if (Unsafe.SizeOf() < Unsafe.SizeOf()) { throw new InvalidCastException(); } return ref Unsafe.As(ref Unsafe.AsRef(from)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ref TTo AsMut(ref TFrom from) where TTo : struct where TFrom : struct { if (Unsafe.SizeOf() < Unsafe.SizeOf()) { throw new InvalidCastException(); } return ref Unsafe.As(ref from); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ReadOnlySpan AsSpan(in TFrom from) where TTo : struct where TFrom : struct { #if NETSTANDARD2_0 var asSpan = CreateReadOnlySpan(ref Unsafe.AsRef(from)); #else var asSpan = MemoryMarshal.CreateReadOnlySpan(ref Unsafe.AsRef(from), 1); #endif return MemoryMarshal.Cast(asSpan); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Span AsMutableSpan(ref TFrom from) where TTo : struct where TFrom : struct { #if NETSTANDARD2_0 var asSpan = CreateSpan(ref Unsafe.AsRef(from)); #else var asSpan = MemoryMarshal.CreateSpan(ref from, 1); #endif return MemoryMarshal.Cast(asSpan); } #if NETSTANDARD2_0 private static unsafe Span CreateSpan(ref T from) where T : struct { void* ptr = Unsafe.AsPointer(ref from); return new Span(ptr, 1); } private static unsafe ReadOnlySpan CreateReadOnlySpan(ref T from) where T : struct { void* ptr = Unsafe.AsPointer(ref from); return new ReadOnlySpan(ptr, 1); } #endif } }