using System; namespace Hazel { /// /// This is a minimal implementation of `System.Span` in .NET 5.0 /// public struct ByteSpan { private readonly byte[] array_; /// /// Createa a new span object containing an entire array /// 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; } } /// /// Creates a new span object containing a subset of an array /// 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) { throw new ArgumentException($"Invalid length: {length}", nameof(length)); } if ((offset + length) > array.Length) { throw new ArgumentException($"Invalid length. Length: {length} Offset: {offset} Array size: {array.Length}", nameof(length)); } this.array_ = array; this.Offset = offset; this.Length = length; } } /// /// Returns the underlying array. /// /// WARNING: This does not return the span, but the entire underlying storage block /// public byte[] GetUnderlyingArray() { return this.array_; } /// /// Returns the offset into the underlying array /// public int Offset { get; } /// /// Returns the length of the current span /// public int Length { get; } /// /// Gets the span element at the specified index /// 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; } } /// /// Create a new span that is a subset of this span [offset, this.Length-offset) /// public ByteSpan Slice(int offset) { return Slice(offset, this.Length - offset); } /// /// Create a new span that is a subset of this span [offset, length) /// public ByteSpan Slice(int offset, int length) { return new ByteSpan(this.array_, this.Offset + offset, length); } /// /// Copies the contents of the span to an array /// public void CopyTo(byte[] array, int offset) { CopyTo(new ByteSpan(array, offset, array.Length - offset)); } /// /// Copies the contents of the span to another span /// 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); } } /// /// Create a new array with the contents of this span /// public byte[] ToArray() { byte[] result = new byte[Length]; CopyTo(result); return result; } public override string ToString() { return string.Join(" ", this.ToArray()); } /// /// Implicit conversion from byte[] -> ByteSpan /// public static implicit operator ByteSpan(byte[] array) { return new ByteSpan(array); } /// /// Retuns an empty span object /// public static ByteSpan Empty { get { return new ByteSpan(null); } } } }