# memory_region.bump

> A module of memory-region's API.

A cursor that only moves forward.

`BumpAllocator` holds one number — how far into the region it has got — and
allocating is "give me where the cursor is, then move it on by `n`". No free
list, no search, no per-allocation header: two instructions. The price is that
an individual piece can never be freed, which is the usual arena trade and
suits data that is born together and dies together.

## Offsets, not addresses

`claim` returns an **offset from the start of the region**, and anything
stored inside a region refers to the rest of it by offset. A shared mapping
lands at a different address in every process that maps it, so an address
written into it means something only to the process that wrote it; a reader
adds its own `base()` and everything resolves.

`span` is how to read and write what has been claimed: a typed view that
borrows the allocator, so the compiler knows when it stops being valid.
`unsafe_address` is the way back to a raw address, for the moment one has to
be written into a C structure whose consumer is this process.

## Structs

### `BumpAllocator`

```mojo
struct BumpAllocator[R: Region & Deinitable]
```

Hands out 8-byte-aligned offsets into a region, front to back.

Arrow wants every buffer 8-byte aligned — consumers cast them in place —
so alignment is the allocator's job rather than each caller's.

Implements: `Movable`

Fields:

- `region: R`
- `used: Int`: How far the cursor has moved: the region's occupied prefix.

#### `__init__`

```mojo
def __init__(out self, var region: R)
```

#### `claim`

```mojo
def claim(mut self, n: Int) -> Int
```

Reserve `n` bytes and return their **offset** from the base.

Raises rather than growing: a region is a fixed range, and nothing
that has already handed out an offset could survive being moved.

#### `append`

```mojo
def append[dtype: DType, //](mut self, data: Span[Scalar[dtype]]) -> Int
```

Claim room for `data`, copy it in, and return where it went.

Any scalar element type, so a buffer of `Int32` offsets goes in as
itself rather than as an address and a byte count.

#### `span`

```mojo
def span[dtype: DType](ref self, offset: Int, count: Int) -> Span[Scalar[dtype], origin_of(self)]
```

`count` values of `dtype` at `offset`, as a view of this allocator.

The view borrows the allocator: writable when the allocator is
reached through `mut`, read-only otherwise, and unusable once it is
closed, moved or dropped — all checked by the compiler. Claiming more
while a view is alive is fine, because `claim` never moves anything.

The offset is one this allocator handed out, so a bad one is a bug
rather than bad input: it is a `debug_assert` against what has been
claimed, not a raise.

#### `unsafe_ptr`

```mojo
def unsafe_ptr(self, offset: Int) -> Pointer[UInt8, MutUntrackedOrigin]
```

A writable pointer to `offset`, valid in this process only.

#### `unsafe_address`

```mojo
def unsafe_address(self, offset: Int) -> Int
```

`offset` as an address in this process.

The only place an address belongs: writing one into a C structure
whose consumer is this process. Anything a peer will read stores the
offset instead.

#### `close`

```mojo
def close(deinit self)
```

Give the region back now; it would go at end of scope anyway.

#### `into_raw`

```mojo
def into_raw(deinit self) -> Int
```

Give up ownership of the region; returns its base address.

What an export does when the bytes become the consumer's — the Arrow
C Data Interface release callback frees them later.
