Files
conservatorio-tomadini/plugins/system/nrframework/NRFramework/Parser/RingBuffer.php
2024-12-31 11:07:09 +01:00

228 lines
6.0 KiB
PHP

<?php
/**
* @author Tassos.gr <info@tassos.gr>
* @link https://www.tassos.gr
* @copyright Copyright © 2024 Tassos All Rights Reserved
* @license GNU GPLv3 <http://www.gnu.org/licenses/gpl.html> or later
*/
namespace NRFramework\Parser;
defined('_JEXEC') or die;
/**
* RingBuffer
*
* A circular buffer of fixed length.
* This class essentially implements a fixed-size FIFO stack but with
* "convenient" accessor methods compared to manually handling a vanilla PHP array.
*
* Used by NRFramework\Parser\Parser and NRFramework\Parser\Lexer.
*/
class RingBuffer implements \Countable, \ArrayAccess, \Iterator
{
/**
* Iterator position
* @var int
*/
protected $iterator_position = 0;
/**
* Position of the next element
* @var integer
*/
protected $position = 0;
/**
* Contents buffer
* @var \SplFixedArray
*/
protected $buffer;
/**
* Size of the ring buffer
* @var int
*/
protected $size;
/**
* RingBuffer constructor
*
* Handles arguments through 'func_get_args' (gotta love PHP)
*
* @param int $size Size of the ring buffer
* @param array $val Initial values
*/
public function __construct()
{
//argument checks
$argv = func_get_args();
$argc = count($argv);
switch($argc)
{
case 1:
// array
if (is_array($argv[0]))
{
$this ->size = count($argv[0]);
$this->buffer = \SplFixedArray::fromArray($argv[0]);
}
// size
else if (is_numeric($argv[0]))
{
if ($argv[0] < 1)
{
throw new \InvalidArgumentException('RingBuffer ctor: size must be greater than zero');
}
$size = (integer)$argv[0];
$this->buffer = \SplFixedArray::fromArray(array_fill(0, $size, null));
$this->size = $size;
}
else
{
throw new \InvalidArgumentException("RingBuffer ctor: arguments must be an array ,a numeric size or both");
}
break;
case 2:
if(is_array($argv[0]) && is_numeric($argv[1]))
{
if ($argv[1] < 1)
{
throw new \InvalidArgumentException('RingBuffer ctor: size must be greater than zero');
}
$arr_size = count($argv[0]);
$size = (integer)$argv[1];
if ($arr_size == $size)
{
$this->buffer = \SplFixedArray::fromArray($argv[0]);
$this->size = $size;
}
else if ($arr_size > $size)
{
$this->buffer = \SplFixedArray::fromArray(array_slice($argv[0], 0, $size));
$this->size = $size ;
}
else // $arr_size < $size
{
$this->buffer = \SplFixedArray::fromArray(array_merge($argv[0], array_fill(0, $size - $arr_size, null)));
$this->size = $size ;
$this->position = $arr_size ;
}
}
else
{
throw new \InvalidArgumentException("RingBuffer ctor: arguments must be an array ,a numeric size or both");
}
break;
default:
throw new \InvalidArgumentException('RingBuffer ctor: no arguments given');
}
}
/**
* Returns the internal buffer as an array
*
* @return \SplFixedArray
*/
public function buffer()
{
return $this->buffer;
}
/**
* 'Countable' interface methods
*/
/**
* Returns the size of the buffer
*
* @return int
*/
public function count() : int
{
return $this->size;
}
/**
* 'ArrayAccess' interface methods
*/
protected function offsetOf($offset)
{
return ($this->position + $offset) % $this->size;
}
public function offsetExists($offset) : bool
{
return ($offset >= 0) && ($offset < $this->size);
}
#[\ReturnTypeWillChange]
public function offsetGet($offset)
{
// if (!$this->offsetExists($offset))
if (($offset < 1) && ($offset >= $this->size))
{
throw new \OutOfBoundsException("RingBuffer: invalid offset $offset.");
}
return $this->buffer[($this->position + $offset) % $this->size];
}
public function offsetUnset($offset) : void
{
if (!$this->offsetExists($offset))
{
throw new \OutOfBoundsException("RingBuffer: invalid offset $offset.");
}
$this->buffer[$this->offsetOf($offset)] = null;
}
public function offsetSet($offset, $value) : void
{
if ($offset === null)
{
$this->buffer[$this->position] = $value;
$this->position = ($this->position + 1)%$this->size;
}
else if ($this->offsetExists($offset))
{
$this->buffer[$this->offsetOf($offset)] = $value;
}
else
{
throw new \OutOfBoundsException("RingBuffer: invalid offset $offset.");
}
}
/**
* 'Iterator' interface methods
*/
public function rewind() : void
{
$this->iterator_position = 0;
}
public function current() : mixed
{
return $this->buffer[$this->offsetOf($this->iterator_position)];
}
public function key() : mixed
{
return $this->iterator_position;
}
public function next() : void
{
$this->iterator_position++;
}
public function valid() : bool
{
return ($this->iterator_position >= 0) && ($this->iterator_position < $this->size);
}
}