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
    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
    readonly count: integer
    drop: nosuspend function(takes self: Array<T>): nil
    close: nosuspend function(takes self: Array<T>): nil
    read: function(borrows self: Array<T>): span.Span<T> borrows (self)
    write: function(exclusive self: Array<T>): span.Writable<T> borrows (self)
end

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

Type parameters

NameDescription
T

Methods

drop#
drop: nosuspend function(takes self: Array<T>): nil

Releases the allocation. Scope exit invokes this automatically.

Arguments
NameTypeDescription
takes selfArray<T>
Returns
TypeDescription
nil
close#
close: nosuspend function(takes self: Array<T>): nil

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

Arguments
NameTypeDescription
takes selfArray<T>
Returns
TypeDescription
nil
read#
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)
write#
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)

Fields

count#
count: integer

How many elements the allocation holds.

ArrayTokeninterface#

sealed interface heap.ArrayToken
    close: nosuspend function(takes self: ArrayToken): nil
end

Methods

close#
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 one element

countinteger

how many elements to allocate

Returns

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

the array, owned by the caller

Raises

  • when count is negative, the byte size overflows a Lua integer, or malloc 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 drop reaches it.

Type parameters

NameDescription
T

Arguments

NameTypeDescription
takes selfT

the array, spent by this call

Returns

TypeDescription
nil