Livt packages

Livt.Collections

Version 1.4.2

Fixed-capacity FIFOs, queues, stacks, circular buffers, lists, and bit sets for Livt hardware.

Published files (62)

src/circular/CircularBuffer.lvt

namespace Livt.Collections.Circular

/**
 * Overwrite-on-full ring storage selected by element type and compile-time size.
 * Empty reads return the element type's zero value.
 */
component CircularBuffer<T, CAPACITY: int = 64> : ICircularBuffer<T>
{
	static assert CAPACITY > 0, "Circular-buffer capacity must be positive"

	// Capacity-derived ring-address and logical occupancy widths.
	const ADDRESS_BITS: int = Bits.RequiredFor(CAPACITY - 1)
	const COUNT_BITS: int = Bits.RequiredFor(CAPACITY)

	// Backing cells retain inactive values across Clear; size defines validity.
	@UninitializedStorage
	storage: T[CAPACITY]

	// Ring positions, logical occupancy, and the value returned by empty reads.
	head: logic[ADDRESS_BITS]
	tail: logic[ADDRESS_BITS]
	size: logic[COUNT_BITS]
	zero: T

	/**
	 * Appends a value, overwriting the oldest value when full.
	 */
	override fn Write(value: T)
	{
		if ((this.size as uint) == (CAPACITY as uint)) {
			if ((this.head as uint) == ((CAPACITY - 1) as uint)) {
				this.head = 0
			} else {
				this.head = ((this.head as uint) + 1) as logic[ADDRESS_BITS]
			}
		}

		if ((this.size as uint) < (CAPACITY as uint)) {
			this.size = ((this.size as uint) + 1) as logic[COUNT_BITS]
		}

		this.storage[this.tail as uint] = value

		if ((this.tail as uint) == ((CAPACITY - 1) as uint)) {
			this.tail = 0
		} else {
			this.tail = ((this.tail as uint) + 1) as logic[ADDRESS_BITS]
		}
	}

	/**
	 * Removes and returns the oldest value, or zero when empty.
	 */
	override fn Read() T
	{
		if ((this.size as uint) > 0) {
			var value = this.storage[this.head as uint]

			if ((this.head as uint) == ((CAPACITY - 1) as uint)) {
				this.head = 0
			} else {
				this.head = ((this.head as uint) + 1) as logic[ADDRESS_BITS]
			}

			this.size = ((this.size as uint) - 1) as logic[COUNT_BITS]
			return value
		}

		return this.zero
	}

	/**
	 * Returns the oldest value without removing it, or zero when empty.
	 */
	override fn Peek() T
	{
		if ((this.size as uint) > 0) {
			return this.storage[this.head as uint]
		}

		return this.zero
	}

	/**
	 * Returns true when the buffer contains no values.
	 */
	override fn IsEmpty() bool
	{
		return (this.size as uint) == 0
	}

	/**
	 * Returns true when the buffer has reached its configured capacity.
	 */
	override fn IsFull() bool
	{
		return (this.size as uint) == (CAPACITY as uint)
	}

	/**
	 * Returns the current number of buffered values.
	 */
	override fn GetSize() int
	{
		return (this.size as uint) as int
	}

	/**
	 * Resets ring positions and occupancy without erasing inactive storage cells.
	 */
	override fn Clear()
	{
		this.head = 0
		this.tail = 0
		this.size = 0
	}
}