class SPCore::CircularBuffer

A circular (ring) buffer. The same array is used to store data over the life of the buffer, unless the size is changed. As data is pushed and popped indices are updated to track the oldest and newest elements.

Push behavior can be modified by setting override_when_full. If true, when the fill count reaches buffer size then new data will override the oldest data. If false, when fill count is maxed the new data will raise an exception.

Pop behavior can be modified by setting fifo to true or false. If true, pop will return the oldest data (data first pushed). If false, pop will return the newest data (data last pushed).

@author James Tunnell

Attributes

fifo[RW]
fill_count[R]
override_when_full[RW]

Public Class Methods

new(size, opts = {}) click to toggle source

A new instance of CircularBuffer. @param [Fixnum] size The buffer size (and maximum fill count). @param [Hash] opts Contain optional arguments to modify buffer push/pop behavior.

Valid keys are :override_when_full and :fifo.
# File lib/spcore/core/circular_buffer.rb, line 24
def initialize size, opts = {}
  
  opts = { :fifo => true, :override_when_full => true }.merge opts
  
  @buffer = Array.new(size)
  @oldest = 0;
  @newest = 0;
  @fill_count = 0;

  @fifo = opts[:fifo]
  @override_when_full = opts[:override_when_full]
end

Public Instance Methods

empty?() click to toggle source

Return true if the buffer is empty.

# File lib/spcore/core/circular_buffer.rb, line 43
def empty?
  return @fill_count == 0
end
full?() click to toggle source

Return true if the buffer is full (fill count == buffer size).

# File lib/spcore/core/circular_buffer.rb, line 48
def full?
  return (@fill_count == size)
end
newest(relative_index = 0) click to toggle source

access the latest data element.

# File lib/spcore/core/circular_buffer.rb, line 116
def newest relative_index = 0
  raise ArgumentError, "buffer is empty" if empty?
  raise ArgumentError, "relative_index is greater or equal to @fill_count" if relative_index >= @fill_count

  # newestIdx is actually @newest - 1
  newestIdx = @newest - 1;
  if(newestIdx < 0)
    newestIdx += @buffer.count;
  end

  absIdx = newestIdx - relative_index;
  if(absIdx < 0)
    absIdx += @buffer.count;
  end

  return @buffer[absIdx];
end
oldest(relative_index = 0) click to toggle source

access the oldest data element.

# File lib/spcore/core/circular_buffer.rb, line 135
def oldest relative_index = 0
  raise ArgumentError, "buffer is empty" if empty?
  raise ArgumentError, "relative_index is greater or equal to @fill_count" if relative_index >= @fill_count
  
  absIdx = @oldest + relative_index;
  if(absIdx >= @buffer.count)
      absIdx -= @buffer.count;
  end
  
  return @buffer[absIdx];
end
pop() click to toggle source

Pop the oldest/newest element, depending on @fifo flag. When true, pop the oldest, otherwise pop the newest. Set to true by default, can override during initialize or later using fifo=.

# File lib/spcore/core/circular_buffer.rb, line 150
def pop
  raise ArgumentError, "buffer is empty" if empty?
  
  if @fifo
    # FIFO - pop the oldest element
    element = oldest
    @oldest += 1
    if(@oldest >= @buffer.count)
      @oldest = 0
    end
    @fill_count -= 1
    return element    
  else
    # FILO - pop the newest element
    element = newest
    if(@newest > 0)
      @newest -= 1
    else
      @newest = @buffer.count - 1
    end
    @fill_count -= 1
    return element      
  end
end
push(element) click to toggle source

push a single data element.

# File lib/spcore/core/circular_buffer.rb, line 86
def push element
  if full?
    raise ArgumentError, "buffer is full, and override_when_full is false" unless @override_when_full
    
    @buffer[@newest] = element;
    @newest += 1
    @oldest += 1
  else
    @buffer[@newest] = element;
    @newest += 1
    @fill_count += 1      
  end

  if @oldest >= @buffer.count
    @oldest = 0
  end

  if @newest >= @buffer.count
    @newest = 0
  end
end
push_ary(ary) click to toggle source

push an array of data elements.

# File lib/spcore/core/circular_buffer.rb, line 109
def push_ary ary
  ary.each do |element|
    push element
  end
end
resize(size) click to toggle source

Change the buffer size, allocating a new backing array at the same time.

# File lib/spcore/core/circular_buffer.rb, line 53
def resize size
  rv = false
  if(size != @buffer.count)
    rv = true
    @buffer = Array.new(size)
    @oldest = 0
    @newest = 0
    @fill_count = 0
  end
  return rv
end
size() click to toggle source

Return the buffer size (max fill count).

# File lib/spcore/core/circular_buffer.rb, line 38
def size
  return @buffer.count
end
to_ary() click to toggle source

Return an array containing the data layed out contiguously (data normally is split somewhere along the backing array).

# File lib/spcore/core/circular_buffer.rb, line 67
def to_ary
  if empty?
    return []
  end

  # newest index is actually @newest - 1
  newest_idx = @newest - 1;
  if(newest_idx < 0)
    newest_idx += @buffer.count;
  end

  if newest_idx >= @oldest
    return @buffer[@oldest..newest_idx]
  else
    return @buffer[@oldest...@buffer.count] + @buffer[0..newest_idx]
  end
end