ring_buffer.pony

class RingBuffer[A]
  """
  A ring buffer.
  """
  embed _array: Array[A]
  let _mod: USize
  var _write: USize = 0

  new create(len: USize) =>
    """
    Create a ring buffer with a fixed size. The size will be rounded up to the
    next power of 2.
    """
    let n = len.max(2).next_pow2()
    _mod = n - 1
    _array = Array[A](n)

  fun head(): USize ? =>
    """
    The first read that will succeed. If nothing has been written to the ring,
    this will raise an error.
    """
    if _write > 0 then
      if _write > space() then
        _write - space()
      else
        0
      end
    else
      error
    end

  fun size(): USize =>
    """
    The number of elements that have been added to the ring.
    """
    _write

  fun space(): USize =>
    """
    The available space in the ring.
    """
    _mod + 1

  fun apply(i: USize): this->A ? =>
    """
    Get the i-th element from the ring. If the i-th element has not yet been
    added or is no longer available, this will raise an error.
    """
    if (i >= _write) or ((_write - i) > space()) then
      error
    end

    _array(i and _mod)?

  fun ref push(value: A): Bool =>
    """
    Add an element to the ring. If the ring is full, this will drop the oldest
    element in the ring. Returns true if an element was dropped.
    """
    var full = false

    if _write < space() then
      _array.push(consume value)
    else
      try _array(_write and _mod)? = consume value end
      full = true
    end

    _write = _write + 1
    full

  fun ref clear() =>
    """
    Clear the queue.
    """
    _array.clear()
    _write = 0