Skip to content

Heap

Heap exposes three static helpers for inspecting the allocator’s state at runtime — total free bytes, the size of the biggest single free extent, and the compile-time heap capacity. All methods are static.

#import <Heap.xt>

Heap is meaningful only on heap-allocator targets — currently xl-shadow, xe-nobank, xt, xe-heap, and the multi-bank rambo* / compy* variants. On bump-allocator targets the allocator keeps no free-list metadata, so size() and largest() would return misleading values.

static u32 size(void); // current free bytes (across every reserved bank)
static u16 largest(void); // biggest single contiguous free extent
static u32 totalSize(void); // compile-time heap capacity (constant)
u32 free = Heap.size();
u16 max = Heap.largest();
u32 total = Heap.totalSize();
Stdio.printf("heap: %lu free of %lu (largest extent: %u)\n", free, total, max);
  • size() and totalSize() are u32 because a multi-bank heap can easily exceed 64 KB. For example, xe-heap reserves 6 banks × 16 KB = 96 KB; rambo576 goes higher.
  • largest() is u16 because a single free extent cannot span a bank boundary — it’s bounded by the flat region size or one 16 KB bank, both of which fit in 16 bits.

All three counts are in bytes and include the 4-byte per-block header (2-byte size + 2-byte retain count) that the allocator stores in front of every allocation. A new u8[100] therefore consumes 104 bytes from the size() total, not 100.

size() reports total free bytes summed across every free extent. First-fit allocation can’t satisfy a request larger than the largest single extent, even if size() is much bigger — fragmentation can leave you with plenty of total free and nothing usefully large.

if (Heap.largest() < (u16)needed + 4) {
Stdio.print("not enough contiguous heap; bailing\n");
return;
}
u8@ buf = new u8[needed];

The +4 accounts for the per-block header overhead; if you forget it the allocator will refuse the request even though largest() says yes.

  • size() walks the free list in every reserved bank — O(free-block count). Safe to call often, but not free.
  • largest() walks the same lists with a running max — same cost class as size().
  • totalSize() is a linker-resolved constant (heap_total_bytes) emitted at build time. Free at runtime.