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