using System; using System.Diagnostics; using System.Text; namespace NetCoreServer { /// /// Dynamic byte buffer /// public class Buffer { private byte[] _data; private long _size; private long _offset; /// /// Is the buffer empty? /// public bool IsEmpty => (_data == null) || (_size == 0); /// /// Bytes memory buffer /// public byte[] Data => _data; /// /// Bytes memory buffer capacity /// public long Capacity => _data.Length; /// /// Bytes memory buffer size /// public long Size => _size; /// /// Bytes memory buffer offset /// public long Offset => _offset; /// /// Buffer indexer operator /// public byte this[long index] => _data[index]; /// /// Initialize a new expandable buffer with zero capacity /// public Buffer() { _data = new byte[0]; _size = 0; _offset = 0; } /// /// Initialize a new expandable buffer with the given capacity /// public Buffer(long capacity) { _data = new byte[capacity]; _size = 0; _offset = 0; } /// /// Initialize a new expandable buffer with the given data /// public Buffer(byte[] data) { _data = data; _size = data.Length; _offset = 0; } #region Memory buffer methods /// /// Get a span of bytes from the current buffer /// public Span AsSpan() { return new Span(_data, (int)_offset, (int)_size); } /// /// Get a string from the current buffer /// public override string ToString() { return ExtractString(0, _size); } /// /// Clear the current buffer and its offset /// public void Clear() { _size = 0; _offset = 0; } /// /// Extract the string from buffer of the given offset and size /// public string ExtractString(long offset, long size) { Debug.Assert(((offset + size) <= Size), "Invalid offset & size!"); if ((offset + size) > Size) throw new ArgumentException("Invalid offset & size!", nameof(offset)); return Encoding.UTF8.GetString(_data, (int)offset, (int)size); } /// /// Remove the buffer of the given offset and size /// public void Remove(long offset, long size) { Debug.Assert(((offset + size) <= Size), "Invalid offset & size!"); if ((offset + size) > Size) throw new ArgumentException("Invalid offset & size!", nameof(offset)); Array.Copy(_data, offset + size, _data, offset, _size - size - offset); _size -= size; if (_offset >= (offset + size)) _offset -= size; else if (_offset >= offset) { _offset -= _offset - offset; if (_offset > Size) _offset = Size; } } /// /// Reserve the buffer of the given capacity /// public void Reserve(long capacity) { Debug.Assert((capacity >= 0), "Invalid reserve capacity!"); if (capacity < 0) throw new ArgumentException("Invalid reserve capacity!", nameof(capacity)); if (capacity > Capacity) { byte[] data = new byte[Math.Max(capacity, 2 * Capacity)]; Array.Copy(_data, 0, data, 0, _size); _data = data; } } /// /// Resize the current buffer /// public void Resize(long size) { Reserve(size); _size = size; if (_offset > _size) _offset = _size; } /// /// Shift the current buffer offset /// public void Shift(long offset) { _offset += offset; } /// /// Unshift the current buffer offset /// public void Unshift(long offset) { _offset -= offset; } #endregion #region Buffer I/O methods /// /// Append the single byte /// /// Byte value to append /// Count of append bytes public long Append(byte value) { Reserve(_size + 1); _data[_size] = value; _size += 1; return 1; } /// /// Append the given buffer /// /// Buffer to append /// Count of append bytes public long Append(byte[] buffer) { Reserve(_size + buffer.Length); Array.Copy(buffer, 0, _data, _size, buffer.Length); _size += buffer.Length; return buffer.Length; } /// /// Append the given buffer fragment /// /// Buffer to append /// Buffer offset /// Buffer size /// Count of append bytes public long Append(byte[] buffer, long offset, long size) { Reserve(_size + size); Array.Copy(buffer, offset, _data, _size, size); _size += size; return size; } /// /// Append the given span of bytes /// /// Buffer to append as a span of bytes /// Count of append bytes public long Append(ReadOnlySpan buffer) { Reserve(_size + buffer.Length); buffer.CopyTo(new Span(_data, (int)_size, buffer.Length)); _size += buffer.Length; return buffer.Length; } /// /// Append the given buffer /// /// Buffer to append /// Count of append bytes public long Append(Buffer buffer) => Append(buffer.AsSpan()); /// /// Append the given text in UTF-8 encoding /// /// Text to append /// Count of append bytes public long Append(string text) { int length = Encoding.UTF8.GetMaxByteCount(text.Length); Reserve(_size + length); long result = Encoding.UTF8.GetBytes(text, 0, text.Length, _data, (int)_size); _size += result; return result; } /// /// Append the given text in UTF-8 encoding /// /// Text to append as a span of characters /// Count of append bytes public long Append(ReadOnlySpan text) { int length = Encoding.UTF8.GetMaxByteCount(text.Length); Reserve(_size + length); long result = Encoding.UTF8.GetBytes(text, new Span(_data, (int)_size, length)); _size += result; return result; } #endregion } }