using System.Diagnostics; using System.Text; namespace B24SiemensPlugin.TCP { /// /// 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[int 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 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 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 text in UTF-8 encoding /// /// Text to append /// Count of append bytes public long Append(string text) { Reserve(_size + Encoding.UTF8.GetMaxByteCount(text.Length)); long result = Encoding.UTF8.GetBytes(text, 0, text.Length, _data, (int)_size); _size += result; return result; } #endregion } }