using System; using Best.HTTP.Shared.PlatformSupport.Text; namespace Best.HTTP.Shared.PlatformSupport.Memory { /// /// Represents a segment (a continuous section) of a byte array, providing functionalities to /// work with a portion of the data without copying. /// [Best.HTTP.Shared.PlatformSupport.IL2CPP.Il2CppEagerStaticClassConstructionAttribute] public readonly struct BufferSegment { internal const int ToStringMaxDumpLength = 128; /// /// Represents an empty buffer segment. /// public static readonly BufferSegment Empty = new BufferSegment(null, 0, 0); /// /// The underlying data of the buffer segment. /// public readonly byte[] Data; /// /// The starting offset of the segment within the data. /// public readonly int Offset; /// /// The number of bytes in the segment that contain valid data. /// public readonly int Count; /// /// Initializes a new instance of the BufferSegment struct. /// /// The data for the buffer segment. /// The starting offset of the segment. /// The number of bytes in the segment. public BufferSegment(byte[] data, int offset, int count) { this.Data = data; this.Offset = offset; this.Count = count; } /// /// Converts the buffer segment to an AutoReleaseBuffer to use it in a local using statement. /// /// A new AutoReleaseBuffer instance containing the data of the buffer segment. public AutoReleaseBuffer AsAutoRelease() => new AutoReleaseBuffer(this); /// /// Creates a new segment starting from the specified offset. /// /// The new segment will reference the same underlying byte[] as the original, without creating a copy of the data. /// The starting offset of the new segment. /// A new buffer segment that references the same underlying data. public BufferSegment Slice(int newOffset) => new BufferSegment(this.Data, newOffset, this.Count - (newOffset - this.Offset)); /// /// Creates a new segment with the specified offset and count. /// /// The new segment will reference the same underlying byte[] as the original, without creating a copy of the data. /// The starting offset of the new segment. /// The number of bytes in the new segment. /// A new buffer segment that references the same underlying data. public BufferSegment Slice(int offset, int count) => new BufferSegment(this.Data, offset, count); /// /// Copyies the buffer's content to the received array. /// /// The array the data will be copied into. public void CopyTo(byte[] to) => Array.Copy(this.Data, this.Offset, to, 0, this.Count); public override bool Equals(object obj) { if (obj == null || !(obj is BufferSegment)) return false; return Equals((BufferSegment)obj); } public bool Equals(BufferSegment other) => this.Data == other.Data && this.Offset == other.Offset && this.Count == other.Count; public bool Equals(AutoReleaseBuffer other) => this.Data == other.Data && this.Offset == other.Offset && this.Count == other.Count; public override int GetHashCode() => (this.Data != null ? this.Data.GetHashCode() : 0) * 21 + this.Offset + this.Count; public static bool operator ==(BufferSegment left, BufferSegment right) => left.Equals(right); public static bool operator !=(BufferSegment left, BufferSegment right) => !left.Equals(right); public static bool operator ==(BufferSegment left, AutoReleaseBuffer right) => left.Equals(right); public static bool operator !=(BufferSegment left, AutoReleaseBuffer right) => !left.Equals(right); public static implicit operator byte[](BufferSegment left) => left.Data; public override string ToString() { var sb = StringBuilderPool.Get(this.Count + 5); sb.Append("[BufferSegment "); if (this.Count > 0) { sb.AppendFormat("Offset: {0:N0} ", this.Offset); sb.AppendFormat("Count: {0:N0} ", this.Count); sb.Append("Data: ["); if (this.Count > 0) { int dumpCount = Math.Min(this.Count, ToStringMaxDumpLength); sb.AppendFormat("{0:X2}", this.Data[this.Offset]); for (int i = 1; i < dumpCount; ++i) sb.AppendFormat(", {0:X2}", this.Data[this.Offset + i]); if (this.Count > dumpCount) sb.Append(", ..."); } sb.Append("]]"); } else sb.Append(']'); return StringBuilderPool.ReleaseAndGrab(sb); } } }