/************************************************************************* * ModernUO * * Copyright 2019-2022 - ModernUO Development Team * * Email: hi@modernuo.com * * File: CircularBuffer.cs * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * *************************************************************************/ namespace System.Buffers; public readonly ref struct CircularBuffer { private readonly Span _first; private readonly Span _second; public int Length { get; } public CircularBuffer(ArraySegment[] buffers) : this(buffers[0], buffers[1]) { } public CircularBuffer(Span first, Span second) { _first = first; _second = second; Length = first.Length + second.Length; } public T this[int index] { get { if (index < 0 || index > Length) { throw new ArgumentOutOfRangeException(nameof(index)); } return index < _first.Length ? _first[index] : _second[index - _first.Length]; } set { if (index < 0 || index > Length) { throw new ArgumentOutOfRangeException(nameof(index)); } if (index < _first.Length) { _first[index] = value; } else { _second[index - _first.Length] = value; } } } public void CopyFrom(ReadOnlySpan bytes) { var remaining = bytes.Length; var offset = 0; if (remaining == 0) { return; } for (int i = 0; i < 2; i++) { var buffer = i == 0 ? _first : _second; if (buffer.Length == 0) { continue; } var sz = Math.Min(remaining, buffer.Length); bytes.Slice(offset, sz).CopyTo(buffer); remaining -= sz; offset += sz; if (remaining == 0) { return; } } throw new OutOfMemoryException(); } public void CopyTo(Span bytes) { if (bytes.Length < Length) { throw new ArgumentOutOfRangeException(nameof(bytes)); } if (_first.Length > 0) { _first.CopyTo(bytes); } if (_second.Length > 0) { _second.CopyTo(bytes[_first.Length..]); } } public CircularBuffer Slice(int offset, int count) { var firstCount = Math.Min(count, _first.Length - offset); var first = offset < _first.Length ? _first.Slice(offset, firstCount) : Span.Empty; var secondCount = offset > _first.Length ? count : count - firstCount; var second = secondCount > 0 ? _second.Slice(Math.Max(0, offset - _first.Length), secondCount) : Span.Empty; return new CircularBuffer(first, second); } public Span GetSpan(int index) { if (index is < 0 or > 1) { throw new ArgumentOutOfRangeException(nameof(index)); } return index == 0 ? _first : _second; } }