--- /dev/null
+using System;
+
+namespace Hazel
+{
+ /// <summary>
+ /// This is a minimal implementation of `System.Span` in .NET 5.0
+ /// </summary>
+ public struct ByteSpan
+ {
+ private readonly byte[] array_;
+
+ /// <summary>
+ /// Createa a new span object containing an entire array
+ /// </summary>
+ public ByteSpan(byte[] array)
+ {
+ if (array == null)
+ {
+ this.array_ = null;
+ this.Offset = 0;
+ this.Length = 0;
+ }
+ else
+ {
+ this.array_ = array;
+ this.Offset = 0;
+ this.Length = array.Length;
+ }
+ }
+
+ /// <summary>
+ /// Creates a new span object containing a subset of an array
+ /// </summary>
+ public ByteSpan(byte[] array, int offset, int length)
+ {
+ if (array == null)
+ {
+ if (offset != 0)
+ {
+ throw new ArgumentException("Invalid offset", nameof(offset));
+ }
+ if (length != 0)
+ {
+ throw new ArgumentException("Invalid length", nameof(offset));
+ }
+
+ this.array_ = null;
+ this.Offset = 0;
+ this.Length = 0;
+ }
+ else
+ {
+ if (offset < 0 || offset > array.Length)
+ {
+ throw new ArgumentException("Invalid offset", nameof(offset));
+ }
+ if (length < 0 || (offset + length) > array.Length)
+ {
+ throw new ArgumentException("Invalid length", nameof(length));
+ }
+
+ this.array_ = array;
+ this.Offset = offset;
+ this.Length = length;
+ }
+ }
+
+ /// <summary>
+ /// Returns the underlying array.
+ ///
+ /// WARNING: This does not return the span, but the entire underlying storage block
+ /// </summary>
+ public byte[] GetUnderlyingArray()
+ {
+ return this.array_;
+ }
+
+ /// <summary>
+ /// Returns the offset into the underlying array
+ /// </summary>
+ public int Offset { get; }
+
+ /// <summary>
+ /// Returns the length of the current span
+ /// </summary>
+ public int Length { get; }
+
+ /// <summary>
+ /// Gets the span element at the specified index
+ /// </summary>
+ public byte this[int index]
+ {
+ get
+ {
+ if (index < 0 || index >= this.Length)
+ {
+ throw new IndexOutOfRangeException();
+ }
+
+ return this.array_[this.Offset + index];
+ }
+ set
+ {
+ if (index < 0 || index >= this.Length)
+ {
+ throw new IndexOutOfRangeException();
+ }
+
+ this.array_[this.Offset + index] = value;
+ }
+ }
+
+ /// <summary>
+ /// Create a new span that is a subset of this span [offset, this.Length-offset)
+ /// </summary>
+ public ByteSpan Slice(int offset)
+ {
+ return Slice(offset, this.Length - offset);
+ }
+
+ /// <summary>
+ /// Create a new span that is a subset of this span [offset, length)
+ /// </summary>
+ public ByteSpan Slice(int offset, int length)
+ {
+ return new ByteSpan(this.array_, this.Offset + offset, length);
+ }
+
+ /// <summary>
+ /// Copies the contents of the span to an array
+ /// </summary>
+ public void CopyTo(byte[] array, int offset)
+ {
+ CopyTo(new ByteSpan(array, offset, array.Length - offset));
+ }
+
+ /// <summary>
+ /// Copies the contents of the span to another span
+ /// </summary>
+ public void CopyTo(ByteSpan destination)
+ {
+ if (destination.Length < this.Length)
+ {
+ throw new ArgumentException("Destination span is shorter than source", nameof(destination));
+ }
+
+ if (Length > 0)
+ {
+ Buffer.BlockCopy(this.array_, this.Offset, destination.array_, destination.Offset, this.Length);
+ }
+ }
+
+ /// <summary>
+ /// Create a new array with the contents of this span
+ /// </summary>
+ public byte[] ToArray()
+ {
+ byte[] result = new byte[Length];
+ CopyTo(result);
+ return result;
+ }
+
+ /// <summary>
+ /// Implicit conversion from byte[] -> ByteSpan
+ /// </summary>
+ public static implicit operator ByteSpan(byte[] array)
+ {
+ return new ByteSpan(array);
+ }
+
+ /// <summary>
+ /// Retuns an empty span object
+ /// </summary>
+ public static ByteSpan Empty
+ {
+ get
+ {
+ return new ByteSpan(null);
+ }
+ }
+ }
+}
--- /dev/null
+namespace Hazel
+{
+ /// <summary>
+ /// Extension functions for (en/de)coding integer values
+ /// </summary>
+ public static class ByteSpanBigEndianExtensions
+ {
+ // Write a 16-bit integer in big-endian format to output[0..2)
+ public static void WriteBigEndian16(this ByteSpan output, ushort value, int offset = 0)
+ {
+ output[offset + 0] = (byte)(value >> 8);
+ output[offset + 1] = (byte)(value >> 0);
+ }
+
+ // Write a 24-bit integer in big-endian format to output[0..3)
+ public static void WriteBigEndian24(this ByteSpan output, uint value, int offset = 0)
+ {
+ output[offset + 0] = (byte)(value >> 16);
+ output[offset + 1] = (byte)(value >> 8);
+ output[offset + 2] = (byte)(value >> 0);
+ }
+
+ // Write a 32-bit integer in big-endian format to output[0..4)
+ public static void WriteBigEndian32(this ByteSpan output, uint value, int offset)
+ {
+ output[offset + 0] = (byte)(value >> 24);
+ output[offset + 1] = (byte)(value >> 16);
+ output[offset + 2] = (byte)(value >> 8);
+ output[offset + 3] = (byte)(value >> 0);
+ }
+
+ // Write a 48-bit integer in big-endian format to output[0..6)
+ public static void WriteBigEndian48(this ByteSpan output, ulong value, int offset = 0)
+ {
+ output[offset + 0] = (byte)(value >> 40);
+ output[offset + 1] = (byte)(value >> 32);
+ output[offset + 2] = (byte)(value >> 24);
+ output[offset + 3] = (byte)(value >> 16);
+ output[offset + 4] = (byte)(value >> 8);
+ output[offset + 5] = (byte)(value >> 0);
+ }
+
+ // Write a 64-bit integer in big-endian format to output[0..8)
+ public static void WriteBigEndian64(this ByteSpan output, ulong value, int offset = 0)
+ {
+ output[offset + 0] = (byte)(value >> 56);
+ output[offset + 1] = (byte)(value >> 48);
+ output[offset + 2] = (byte)(value >> 40);
+ output[offset + 3] = (byte)(value >> 32);
+ output[offset + 4] = (byte)(value >> 24);
+ output[offset + 5] = (byte)(value >> 16);
+ output[offset + 6] = (byte)(value >> 8);
+ output[offset + 7] = (byte)(value >> 0);
+ }
+
+ // Read a 16-bit integer in big-endian format from input[0..2)
+ public static ushort ReadBigEndian16(this ByteSpan input, int offset = 0)
+ {
+ ushort value = 0;
+ value |= (ushort)(input[offset + 0] << 8);
+ value |= (ushort)(input[offset + 1] << 0);
+ return value;
+ }
+
+ // Read a 24-bit integer in big-endian format from input[0..3)
+ public static uint ReadBigEndian24(this ByteSpan input, int offset = 0)
+ {
+ uint value = 0;
+ value |= (uint)input[offset + 0] << 16;
+ value |= (uint)input[offset + 1] << 8;
+ value |= (uint)input[offset + 2] << 0;
+ return value;
+ }
+
+ // Read a 48-bit integer in big-endian format from input[0..3)
+ public static ulong ReadBigEndian48(this ByteSpan input, int offset = 0)
+ {
+ ulong value = 0;
+ value |= (ulong)input[offset + 0] << 40;
+ value |= (ulong)input[offset + 1] << 32;
+ value |= (ulong)input[offset + 2] << 24;
+ value |= (ulong)input[offset + 3] << 16;
+ value |= (ulong)input[offset + 4] << 8;
+ value |= (ulong)input[offset + 5] << 0;
+ return value;
+ }
+ }
+
+ public static class ByteSpanLittleEndianExtensions
+ {
+ // Read a 24-bit integer in little-endian format from input[0..3)
+ public static uint ReadLittleEndian24(this ByteSpan input, int offset = 0)
+ {
+ uint value = 0;
+ value |= (uint)input[offset + 0];
+ value |= (uint)input[offset + 1] << 8;
+ value |= (uint)input[offset + 2] << 16;
+ return value;
+ }
+
+ // Read a 24-bit integer in little-endian format from input[0..4)
+ public static uint ReadLittleEndian32(this ByteSpan input, int offset = 0)
+ {
+ uint value = 0;
+ value |= (uint)input[offset + 0];
+ value |= (uint)input[offset + 1] << 8;
+ value |= (uint)input[offset + 2] << 16;
+ value |= (uint)input[offset + 3] << 24;
+ return value;
+ }
+ }
+}