]> git.deb.at Git - rhonda/impostor.hazel.git/commitdiff
Add ByteSpan utility
authorMatthew Endsley <mendsley@gmail.com>
Thu, 17 Dec 2020 23:55:07 +0000 (15:55 -0800)
committerMatthew Endsley <mendsley@gmail.com>
Tue, 2 Feb 2021 16:53:29 +0000 (08:53 -0800)
Hazel/ByteSpan.cs [new file with mode: 0644]
Hazel/ByteSpanExtensions.cs [new file with mode: 0644]
Hazel/Hazel.csproj

diff --git a/Hazel/ByteSpan.cs b/Hazel/ByteSpan.cs
new file mode 100644 (file)
index 0000000..9d33193
--- /dev/null
@@ -0,0 +1,182 @@
+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);
+            }
+        }
+    }
+}
diff --git a/Hazel/ByteSpanExtensions.cs b/Hazel/ByteSpanExtensions.cs
new file mode 100644 (file)
index 0000000..2749822
--- /dev/null
@@ -0,0 +1,112 @@
+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;
+        }
+    }
+}
index f6b10e6b4d382eab36b17ef69289883b4fd56732..79ad6e82a3cbc97efc59a092585f2709806b4021 100644 (file)
@@ -68,6 +68,8 @@
     <Reference Include="System.Xml" />
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="ByteSpan.cs" />
+    <Compile Include="ByteSpanExtensions.cs" />
     <Compile Include="Connection.cs" />
     <Compile Include="ConnectionListener.cs" />
     <Compile Include="ConnectionState.cs" />