nupp.mem.heap

Owned, malloc-backed C arrays.

The returned array is affine and is released with free at scope exit. Its logical element count moves with the allocation and its pointer is available only through checked span views.

local heap = nupp.mem.heap

do
    local values = heap.allocate(ffi.typeof<int32>(), 4)
    local writable = values:write()
    writable[1] = 42 as int32
    nupp.drop(writable)
    local readable = values:read()
    print(readable[1], #readable)
end -- free runs here

An allocation here sits outside LuaJIT's GC allocation limit, which is the reason to reach for one over a carray. Reading and writing are the operations nupp.mem.span defines, and a live reader and a live writer over one array report NUPP2607.

See c-interop.md for how this sits beside carray, and ownership.md for the borrow rules the views are written against.

Module contents

Types

TypeKindDescription
ArrayrecordAn owned contiguous native allocation whose logical count cannot be separated from its pointer in checked code.
ArrayTokeninterface

Functions

FunctionKindDescription
allocatefunctionAllocates count contiguous values of element outside LuaJIT's GC allocation limit.
destroyArrayfunctionFrees an allocation, which is what every heap array's contract names.

Types#

Arrayrecord#

record heap.Array<T> is heap.ArrayToken ...

An owned contiguous native allocation whose logical count cannot be separated from its pointer in checked code.

Type parameters

NameDescription
T

Members

NameKindDescription
pointerfield
countfieldHow many elements the allocation holds.
closemethodFrees the allocation.
readmethodBorrows the whole allocation as a shared checked span.
writemethodBorrows the whole allocation as an affine checked write span.

pointerfield#

pointer: T[?]
@private

countfield#

count: integer
@readonly

How many elements the allocation holds.

closemethod#

close: @nosuspend function(takes self: Array<T>): nil

Frees the allocation. nupp.drop and scope exit both reach this.

Arguments
NameTypeDescription
takes selfArray<T>
Returns
TypeDescription
nil

readmethod#

read: function(borrows self: Array<T>): span.Span<T> borrows (self)

Borrows the whole allocation as a shared checked span.

The borrow lasts as long as the span does, so a writer cannot be taken out while one is live.

Arguments
NameTypeDescription
borrows selfArray<T>
Returns
TypeDescription
span.Span<T> borrows (self)

writemethod#

write: function(exclusive self: Array<T>): span.Writable<T> borrows (self)

Borrows the whole allocation as an affine checked write span.

Exclusive for as long as the writer is live, so read reports NUPP2607 until the writer is dropped.

Arguments
NameTypeDescription
exclusive selfArray<T>
Returns
TypeDescription
span.Writable<T> borrows (self)

ArrayTokeninterface#

sealed interface heap.ArrayToken ...

Members

NameKindDescription
closemethod

closemethod#

close: @nosuspend function(takes self: ArrayToken): nil
Arguments
NameTypeDescription
takes selfArrayToken
Returns
TypeDescription
nil

Functions#

heap.allocatefunction#

function heap.allocate<T>(element: ctype<T>, count: integer): affine(heap.Array<T>, heap.destroyArray)

Allocates count contiguous values of element outside LuaJIT's GC allocation limit.

The result owns the allocation and frees it at its lexical boundary. The bytes are not zeroed, so a program that reads before it writes reads whatever was there.

do
    local values = heap.allocate(ffi.typeof<int32>(), 1000000)
    print(values.count)
end

Type parameters

NameDescription
T

Arguments

NameTypeDescription
elementctype<T>

the ctype of an element

countinteger

how many elements to allocate

Returns

TypeDescription
affine(heap.Array<T>, heap.destroyArray)

the array, owned by the caller

Raises

TypeCondition
string

when count is invalid, the byte size overflows, or allocation fails

heap.destroyArrayfunction#

function heap.destroyArray<T is heap.ArrayToken>(takes self: T): nil

Frees an allocation, which is what every heap array's contract names.

Nothing calls this by hand. It is the terminal consumer affine carries, so a scope boundary or an explicit nupp.drop reaches it.

Type parameters

NameDescription
T

Arguments

NameTypeDescription
takes selfT

the array, spent by this call

Returns

TypeDescription
nil