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
}
}