bipbuffer

A Nim implementation of Simon Cooke's Bip Buffer

A Bi-partite buffer is similar to a circular buffer, but where data is inserted in two revolving regions. This allows reads to return contiguous blocks of memory, even if they span a region that would normally include a wrap-around in a circular buffer. It's especially useful for APIs requiring blocks of contiguous memory, eliminating the need to copy data into an interim buffer before use.

Usage

import bipbuffer

# Create buffer with capacity of 4 int items
var buffer = newBipBuffer[int](4)


block:
  # Reserve 4 slots for insert on buffer
  var reserved = buffer.reserve(4)
  
  # Assign data to buffer slots
  reserved[0] = 7
  reserved[1] = 22
  reserved[2] = 218
  reserved[3] = 56

# Commit reserved data into an available region
buffer.commit(4)

block:
  # Get stored data in a contiguous block
  var bloc = buffer.read
  assert bloc[0] == 7
  assert bloc[1] == 22
  assert bloc[2] == 218
  assert bloc[3] == 56

# Mark first two parts of the block as free
buffer.decommit(2)

# The block should now contain only the last two values
block:
  var bloc = buffer.read
  assert bloc[0] == 218
  assert bloc[1] == 56

Types

BipBuffer[T] = object
  buffer: seq[T]
  headA, headB: int
  tailA, tailB: int
  reserveStart, reserveEnd: int
ShallowSlice[T] = object
  point: ptr [T]
  size: int

Procs

proc `[]`[T](p: ShallowSlice[T]; k: int): T {...}{.inline.}
Routine to dereference pointer with assertion not to exceed slice boundaries
proc `[]=`[T](p: ShallowSlice[T]; k: int; val: T) {...}{.inline.}
Routine to assign buffer with value at specific index with assertion not to exceed slice boundaries
proc len[T](x: ShallowSlice[T]): int
Get slice length
proc isNil[T](x: ShallowSlice[T]): bool
proc len(x: BipBuffer): int

Returns number of commited elements

This approximates the size of the buffer that will be returned on read()

proc clear(x: var BipBuffer)

Clears all regions and reservations

Data in the underlying buffer is unchanged

proc free(x: var BipBuffer) {...}{.inline.}
Frees and clears the buffer to make available for reuse
proc newBipBuffer[T](length: int): BipBuffer[T] {...}{.inline.}
Create a buffer of capacity length
proc reservedLen(x: BipBuffer): int

Return number of reserved elements

This is the amount of available space for writing data to buffer

proc reserve[T](x: var BipBuffer[T]; length: int): ShallowSlice[T] {...}{.
    raises: [OverflowError], inline.}

Reserves up to length slots of storing data.

If there is less free space than needed, the buffer size will equal the free space. It will returns an OverflowError if there is no free space.

proc commit[T](x: var BipBuffer[T]; length: int) {...}{.inline.}

Commits data unto reserved memory block allowing it to be read

If length is 0 reservation will be cleared wihtout any other changes

proc read[T](x: var BipBuffer[T]): ShallowSlice[T] {...}{.inline.}

Retreives available commited data as a contiguous block

Returns nil if no data is available.

proc decommit[T](x: var BipBuffer[T]; length: int) {...}{.inline.}

Marks the first length elements of available data as seen

Next call of read() will not include these elements

proc isEmpty(x: BipBuffer): bool
Check if buffer is empty

Templates

template `+`[T](p: ptr T; off: int): ptr T
Routine to perform pointer arithmatic. Advances pointer to next position in a continguous memory block