/* Copyright (C) 2009 Matthew Geyer * * This file is part of UO Machine. * * UO Machine 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. * * UO Machine is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with UO Machine. If not, see . */ using System; namespace ClassicAssist.Misc { /* NOTE: This class forces thread safety on every access to make it more user-friendly * in scripts at the expense of a bit of performance. Obviously if you use this code * elsewhere you would want to implement the thread-safety yourself. */ /// /// Thread-safe generic circular buffer (not a queue). Read position is remembered. /// public sealed class CircularBuffer { private readonly object _syncRoot; private T[] _buffer; private int _capacity, _head, _tail, _readOffset; public CircularBuffer( int capacity ) { _capacity = capacity; Count = 0; _head = 0; _tail = 0; _readOffset = 0; _buffer = new T[capacity + 1]; _syncRoot = new object(); } /// /// Get or set buffer capacity. Buffer can only grow. /// public int Capacity { get => _capacity; set { lock ( _syncRoot ) { if ( value <= _capacity ) { return; } T[] newBuffer = new T[value + 1]; T[] oldBuffer = GetBuffer(); Array.Copy( oldBuffer, 0, newBuffer, 0, oldBuffer.Length ); _buffer = newBuffer; _capacity = value; _head = 0; _tail = Count; } } } /// /// Get number of elements actually contained in buffer. /// public int Count { get; private set; } /// /// Reset buffer. /// public void Clear() { lock ( _syncRoot ) { Count = 0; _tail = 0; _head = 0; _readOffset = 0; } } /// /// Change read position to specified number of indices from the beginning. /// /// public void Seek( int count ) { lock ( _syncRoot ) { _readOffset = Math.Min( count, Count ); } } /// /// Write item to buffer. /// public void Write( T item ) { lock ( _syncRoot ) { _buffer[_tail] = item; _tail = ++_tail % _buffer.Length; if ( _tail == _head ) { _head = ++_head % _buffer.Length; _readOffset = Math.Max( 0, _readOffset - 1 ); } Count++; Count = Math.Min( Count, _capacity ); } } /// /// Read item from buffer and increment read position. Item remains in buffer until overwritten. /// /// True on success, false if nothing new to read. public bool Read( out T item ) { lock ( _syncRoot ) { int readPos = ( _head + _readOffset ) % _buffer.Length; if ( readPos == _tail ) { item = default; return false; } _readOffset = Math.Min( _readOffset + 1, Count ); item = _buffer[readPos]; return true; } } /// /// Get array of elements contained in buffer. /// public T[] GetBuffer() { lock ( _syncRoot ) { T[] buffer = new T[Count]; for ( int x = 0; x < Count; x++ ) { buffer[x] = _buffer[( _head + x ) % _buffer.Length]; } return buffer; } } } }