std/core/buffer

std/core/src/buffer.trb

Buffer, the heap storage primitive: a growable sequence whose writes happen in place while the storage has one owner.

It lives in std/core next to Array for the same reason: a collection is a data structure written on top of a storage primitive, and Buffer is that primitive for everything that grows. It implements only what std/core owns

Related

type Buffer

native type Buffer<Item> with Iterate<Item>, Length, MutableIndex<Int, Item>, MutableSlice, From<Iterate<Item>>

A growable sequence on the heap, with an in-place promise and an O(1) Buffer.swapRemove.

The in-place promise. buffer[index] = value, Buffer.set, Buffer.append, Buffer.insert, Buffer.removeAt, Buffer.swapRemove, Buffer.replace, Buffer.reverse and Buffer.clear change the storage in place whenever it has one owner. They copy it once (copy on write) only when the Buffer value has been copied - const other = buffer, passed or captured by value - and is then written. Nothing stronger is promised: a copy that is alive makes the next write O(n), so keep a big buffer in one var owner and pass it as a var parameter.

It is a value like every other: a copy never sees a later write to the original, and the other way round.

Examples

var column: Buffer<Int> = Buffer.withCapacity 4
column.append 10
column.append 20
column.append 30
column.append 40
column[0] = 11
print column.swapRemove(1)
print column

Pitfalls

Buffer.slice copies. A slice of an ArrayList shares its storage, which turns the next write to either side into a copy; a Buffer is the type for code that cannot afford that, so its slice owns storage of its own.

Related

  • Array - the inline storage primitive.
  • List.slice - the shared slice a Buffer does not have.

fn from

static fn from(items: Iterate<Item>): Self

The buffer holding items in the order they are iterated.

Ordinary TorbScript for the reason ArrayList.from gives: items is a trait-typed value of the program, which a function of the runtime cannot walk.

Examples

const buffer: Buffer<Int> = Buffer.from([1, 2, 3])
print buffer

fn withCapacity

native static fn withCapacity(capacity: Int): Self

An empty buffer, with room for capacity items before it has to grow again. The capacity is a hint.

fn holdsClose

native static fn holdsClose(): Bool

Whether an item of this buffer may hold a Close value. It is a constant of the instantiated item type, so a branch on it for an item that cannot hold one is removed from the program.

fn length

native fn length(): Int

How many items there are.

fn get

native fn get(index: Int): Item?

The item at index, or None if it is out of bounds, a negative index included. buffer[index] panics instead; see Buffer.at.

fn at

fn at(index: Int): Item

The item at index. This is buffer[index].

Panics

When index is out of bounds, with index <index> is out of bounds for a length of <count> - the message of ArrayList.at. Buffer.get is the same question with an Option for an answer.

fn set

native var fn set(index: Int, value: Item)

Changes the item at index, in place while the storage has one owner. buffer[index] = value goes through this; see MutableIndex.set.

Panics

When index is out of bounds, with the message of Buffer.at.

fn update

var fn update(index: Int, change: (var element: Item) => Void)

Runs change on the item at index, in the buffer's own storage: no copy of the item is made and none is held while change runs.

Panics

When index is out of bounds, with the message of Buffer.at, before change runs.

fn append

native var fn append(value: Item)

Appends an item at the end, in place while the storage has one owner.

fn insert

native var fn insert(index: Int, value: Item)

Inserts value at index and shifts everything at or after it one position on. index may be the length, which appends.

Panics

When index is negative or after the end, with the message of Buffer.at.

fn removeAt

native var fn removeAt(index: Int): Item?

Removes the item at index, shifts everything after it one position back and answers it, or None if index is out of range. Buffer.swapRemove is the O(1) twin that does not keep the order.

fn swapRemove

var fn swapRemove(index: Int): Item?

Removes the item at index and answers it, by moving the last item into its slot: O(1), the order is not kept, and nothing ever shifts. Reach for it where the order of the items does not matter - an ECS column, a pool - and for Buffer.removeAt where it does.

It answers None, and changes nothing, where index is out of range. On the last index it is removeAt(length() - 1).

Examples

var numbers = Buffer.from([10, 20, 30, 40])
print numbers.swapRemove(1)
print numbers
print numbers.swapRemove(3)
print numbers

Panics

Never: the index is checked first, so the set it ends in is always in range.

fn slice

fn slice(range: Bounds<Int>): Self

The items the range covers, as a buffer of their own: buffer[1..3]. The result is a copy, so writing the original afterwards does not touch the slice and the other way round, and neither write is a second copy.

Examples

var numbers = Buffer.from([1, 2, 3, 4, 5])
const middle = numbers[1..4]
numbers[2] = 0
print middle
print numbers

Panics

When the range reaches outside the buffer, or starts after it ends, with the messages of List.slice.

fn replace

var fn replace(range: Bounds<Int>, values: Self)

Replaces the items of range with values, in place while the storage has one owner; the two need not be the same length. What buffer[from..to] = values goes through; see MutableSlice.replace.

Panics

When the range reaches outside the buffer, or starts after it ends, with the messages of List.slice.

fn reverse

native var fn reverse()

Reverses the order, in place while the storage has one owner.

fn clear

native var fn clear()

Removes every item, in place while the storage has one owner.

fn iterate

fn iterate(): Iterator<Item>

A cursor over the items, front to back: see BufferIterator.

fn keys

fn keys(): Iterate<Int>

The indices of the items, 0..length(): what for var item in items visits to change every item in place.