Download Game! Currently 83 players and visitors. Last logged in:LouenDeepwoodGralgarrAndthi

Blitzer's Blog >> 71847

Back to blogs index
Posted: 29 Aug 2026 22:34 [ permalink ]
total 20
drwxr-sr-x  2 user user 4096 Aug 29 21:29 .
-rw-r--r--  1 user user 1710 Aug 29 21:39 mvp.js
-rw-r--r--  1 user user 4520 Aug 29 21:40 LPCVirtualMachine.js
drwxr-sr-x 18 user user 4096 Aug 29 22:16 ..
user@elli:~/GD$
We need to write the JS allocation and deallocation logic for the 24-byte
intrusive linked list in a flat Int32Array. This is Milestone 1.2 from the
architecture roadmap: "Build the Global Handle Ledger (GHL). Implement the
24-byte Intrusive Linked List on top of a single Int32Array (Active/Free head
pointers, REF_INC, REF_DEC)."

We have the specification: The GHL tracks dynamically allocated strings,
arrays, mappings. It is a pre-allocated array of 24-byte nodes operating as an
intrusive doubly-linked list. Each node: ref_count, type_tag, prev_handle,
next_handle, payload_size, data_offset. All 32-bit integers. So each node is 6
uint32's = 6 * 4 = 24 bytes. We'll represent it as a flat Int32Array.

We need to implement functions to allocate a new handle (node), deallocate
(free) a node, increment and decrement reference counts (and free when
ref_count reaches 0). Also maintain free list.

Let's design:

We have a global Int32Array as the ledger. The ledger size is fixed? Or can be
resized? For MVP, we can pre-allocate a fixed size, say 1024 nodes. But we
might want dynamic growth. For now, just use a fixed size array.

We need to keep track of the head of the free list and the head of the active
list. Since it's an intrusive list, we store these as indices (handles) in
variables. We'll keep them as properties of the GHL object.

Each node:

index 0: ref_count

index 1: type_tag

index 2: prev_handle

index 3: next_handle

index 4: payload_size

index 5: data_offset

We'll use a handle ID that is the node index (starting from 0). But handle IDs
should be the index of the node in the array. We'll allocate nodes from the
free list.

We need functions:

init(numNodes): allocates the Int32Array of size numNodes * 6, initializes all
nodes to zero, links them into free list (prev and next). We'll set free_head
to 0, free_tail to numNodes-1, and link them sequentially.

allocate(): pops a node from the free list, initializes ref_count=1, type_tag,
payload_size, data_offset (maybe default 0), sets prev/next to 0 (or -1 to
indicate none), and returns the handle (index).

deallocate(handle): removes the node from active list (if it's in active list)
and adds to free list. But deallocate is typically called when ref_count hits
0. So we need a function that decrements ref_count and if zero, frees.

ref_inc(handle): increments ref_count.

ref_dec(handle): decrements ref_count, if zero, call deallocate.

But we also need to manage active list for iteration? The spec says it's
intrusive linked list for active nodes and free nodes. We can maintain
separate heads: active_head, free_head. When a node is allocated, we remove it
from free list and insert it into active list (maybe at head). When freed,
remove from active list and add to free list.

We'll also need to handle the case where the ledger is full.

We should also provide a method to get node data (read/write) but for now just
allocation/deallocation.

Let's write code in a Node.js module, maybe a class GHL.

Implementation details:

Use Int32Array for storage.

We'll use handle = index / 6? Actually handle can be the index in the
Int32Array divided by 6? But handle should be the node index (i.e., which
node). Since each node occupies 6 slots, handle = node_index. Then the offset
in the array is handle * 6.

We'll keep free_head and active_head as integer indices (or -1 if empty). Also
free_tail if we want to add to tail for efficiency, but we can add to head.

For free list: we can initialize all nodes linked together: node i's next =
i+1, prev = i-1. free_head = 0, free_tail = numNodes-1.

When allocating: pop from free_head. We need to remove from free list: set new
free_head = node.next, and if new free_head != -1, set its prev = -1. Also set
node's prev and next to -1 (or 0). Then insert into active list: set node.next
= active_head; if active_head != -1, active_head.prev = node; active_head =
node; node.prev = -1.

When deallocating: remove from active list: if node.prev != -1, node.prev.next
= node.next; else active_head = node.next; if node.next != -1, node.next.prev
= node.prev; then insert into free list: node.next = free_head; if free_head
!= -1, free_head.prev = node; free_head = node; node.prev = -1.

We need to keep active_head and free_head as properties.

Now, ref_inc and ref_dec: ref_inc just increments node.ref_count. ref_dec
decrements, and if zero, deallocate.

We'll also need to ensure that when we allocate, we set ref_count=1, and
type_tag, etc. For now, we can pass type_tag and maybe payload_size.

Let's write the code. Also, we need to handle the possibility that we might
want to access the raw data for debugging.

We'll produce a module that exports a class GHL.

Edge cases: when free_head is -1, allocation fails.

We'll write comments and use good practices.

Now, the user asked: "Let's write the JS allocation and deallocation logic."
So we'll provide code. They also gave the current files, but we are focusing
on this milestone.

We'll present the code with explanation.

We'll assume the Int32Array is created with a certain capacity. We'll expose
methods: init(capacity), allocate(typeTag, payloadSize), refInc(handle),
refDec(handle), getNode(handle). Also maybe a method to get stats.

We'll also consider that the handle might be an index, but we could also use
the handle as the index in the array (i.e., the node index). We'll make it
clear.

Let's write the code.