Train ππππ
A doubly-linked, payload-carrying chunk data structure built on Octopus chunks. The train is a train of carriages β each carriage π carries one payload chunk, and carriages couple to their neighbors at LEFT β¬ οΈ / RIGHT β‘οΈ.
Shape
A train occupies its own 64-bit train_domain. The structure has two kinds of chunks:
- Sentinel locomotive π β the chunk at
(train_domain, P0). Anchors both ends of the train. ItsLEFTslot points at the leftmost carriage, itsRIGHTslot at the rightmost. ItsPAYLOADslot is identity (no payload). - Carriage π β a chunk at
(train_domain, carriage_pod)for some non-zero pod. Each carriage is a specialized Octopus with four slots:PAYLOADπ¨ β points at the actual payload chunk in item memory (Sparkle(payload_domain, payload_pod)). Always non-identity.LEFTβ¬ οΈ β id-Sparkle of the previous carriage, or identity if leftmost.RIGHTβ‘οΈ β id-Sparkle of the next carriage, or identity if rightmost.CHILDRENπ¨βπ©βπ§βπ¦ β optional pointer at a child structure (typically the sentinel of a child train). Identity when no children are attached.
A 3-carriage train (payloads A, B, C in left-to-right order):
π π π π
ββββββββββββ ββββββββββββ ββββββββββββ ββββββββββββ
β sentinel β <--> β A β <--> β B β <--> β C β
β pod=P0 β β pod=p_A β β pod=p_B β β pod=p_C β
ββββββββββββ ββββββββββββ ββββββββββββ ββββββββββββ
Each box is one Octopus chunk. Each <--> between adjacent boxes represents one bidirectional LEFT/RIGHT pointer pair. The slot values inside each chunk are:
| Chunk | PAYLOAD | LEFT | RIGHT | CHILDREN |
|---|---|---|---|---|
sentinel π (pod=P0) | identity β₯ | leftmost (A) | rightmost (C) | identity β₯ (or a child structure) |
| carriage A | Sparkle(A*) | identity β₯ | carriage B | identity β₯ (or a child structure) |
| carriage B | Sparkle(B*) | carriage A | carriage C | identity β₯ (or a child structure) |
| carriage C | Sparkle(C*) | carriage B | identity β₯ | identity β₯ (or a child structure) |
Where A*, B*, C* are the actual payload chunks living elsewhere in item memory.
The sentinelβs LEFT/RIGHT are anchor pointers (jumps directly to the leftmost / rightmost), not neighbor pointers. The carriagesβ LEFT/RIGHT are conventional neighbor pointers.
Pushing
storage.mem_set(
memory.train_appender(
train_domain, memory.with_sparkle(payload_domain, "A")))
storage.mem_set(
memory.train_prepender(
train_domain, memory.with_sparkle(payload_domain, "Z")))
Each push rewrites up to three chunks atomically through one mutable view:
- The new carriage at
(train_domain, fresh_pod). - The previous tail/head carriage, with its tail-side neighbor link updated to the new carriage. (Skipped on the first push, when the train is empty.)
- The sentinel, with its tail-side anchor pointer updated to the new carriage. The head-side pointer is updated only when the train transitions from empty to one element.
The sentinel is auto-created on the first push β no separate βinit trainβ step is needed.
β οΈ Single-writer per train. Concurrent pushes from independent mutable views on the same train WILL race on the sentinel and on the previous tail/head β last-write wins, with dropped pushes possible. Callers expecting concurrent appenders must synchronize externally (e.g., serialize through a single goroutine/task, or take an external lock keyed on
train_domain). Reads through aVieware always snapshot-consistent per chunk.
Mid-train insertion: TrainInsertAfter / TrainInsertBefore
TrainAppender and TrainPrepender are thin wrappers over the more general TrainInsertAfter / TrainInsertBefore, which take a reference carriage selector instead of a domain. The new carriage is wedged on the chosen side of the reference. The train domain is taken from the resolved reference carriage.
| Reference carriage | TrainInsertAfter | TrainInsertBefore |
|---|---|---|
sentinel π (TrainCarriage(domain)) | append (new rightmost) | prepend (new leftmost) |
| member carriage X | wedge between X and X.right | wedge between X.left and X |
Sentinel-as-reference is the wraparound bookend case: since the sentinel sits on both ends of the train, βafter the sentinelβ means βright of the rightmost,β and βbefore the sentinelβ means βleft of the leftmost.β On an empty train, either becomes the first and only member.
Each insert rewrites:
- The new carriage.
- The carriage on each side whose neighbor link points at the new one (one rewrite for end-inserts, two for mid-train inserts).
- The sentinel, only when its anchor pointer changes (i.e., the new carriage becomes the leftmost or rightmost).
# Mid-train: wedge M between B and C.
storage.mem_set(memory.train_insert_after(
memory.train_carriage(train_domain, b_pod),
memory.with_sparkle(payload_domain, "M"),
))
# Sentinel-as-ref: equivalent to train_appender.
storage.mem_set(memory.train_insert_after(
memory.train_carriage(train_domain),
memory.with_sparkle(payload_domain, "tail"),
))
Empty check: TrainIsEmpty
A direct query that reads the sentinel and reports whether both LEFT and RIGHT anchors are identity. A train whose sentinel hasnβt been written yet (never touched) is also reported empty β no auto-init.
with storage.new_view() as view:
if memory.train_is_empty(view, train_domain):
print("nothing to process")
Iterating β cursor-straddle semantics
TrainForward / TrainBackward walk the train, yielding each carriageβs payload chunk. The starting point is itself a Selector β pass TrainCarriage(domain) (with no pod) to start from the sentinel for a full-train iteration, or TrainCarriage(domain, pod) to start from a specific carriage.
The cursor straddles between elements, similar to Javaβs ListIterator or C++βs std::list::iterator:
startis the cursor; iteration is exclusive ofstartβs own payload.- When
startresolves to the sentinel, iteration covers the whole train (sentinel acts as both before-leftmost and after-rightmost). - When
startresolves to a member carriage, iteration yields the elements strictly after it in the chosen direction; the start carriageβs own payload is NOT yielded.
start | TrainForward yields | TrainBackward yields |
|---|---|---|
| sentinel | leftmost, β¦, rightmost | rightmost, β¦, leftmost |
| carriage X | X.right, X.right.right, β¦ | X.left, X.left.left, β¦ |
# Whole-train iteration from the sentinel.
for chunk in storage.mem_get(
memory.train_forward(memory.train_carriage(train_domain))):
print(chunk.note)
# From a specific carriage onward (exclusive of that carriage).
for chunk in storage.mem_get(
memory.train_backward(memory.train_carriage(train_domain, carriage_pod))):
print(chunk.note)
Single-step shorthands: TrainNext / TrainPrev
TrainNext(start) is shorthand for Range(0, 1, TrainForward(start)) β the next single payload from the cursorβs position. TrainPrev(start) is the backward mirror. They follow the same exclusive-start rule.
| Cursor | TrainNext yields | TrainPrev yields |
|---|---|---|
| sentinel | leftmost carriageβs payload | rightmost carriageβs payload |
| carriage X | Xβs right neighborβs payload (or empty if X is rightmost) | Xβs left neighborβs payload (or empty if X is leftmost) |
# Peek at the front / back of the train.
front = next(iter(storage.mem_get(
memory.train_next(memory.train_carriage(train_domain))))).note
back = next(iter(storage.mem_get(
memory.train_prev(memory.train_carriage(train_domain))))).note
Iteration rule (implementation)
Each step:
- Integrity β sentinel is identified by
pod == P0. The sentinel MUST have an identityPAYLOAD; a carriage MUST have a non-identityPAYLOAD. Either violation returnsFailedPrecondition(the train is malformed). - Advance β on a carriage, follow the matching-direction key (
RIGHTfor forward,LEFTfor backward) to the next neighbor. On the sentinel, follow the opposite-direction key β sentinelβsLEFT/RIGHTare anchor pointers, so the opposite key takes you to the appropriate end. - Yield β yield the chunk you advanced to. Stop when the next pointer is identity.
The advance-then-yield order is what makes the iteration exclusive of start.
Hierarchy: the CHILDREN slot
Each carriageβs CHILDREN π¨βπ©βπ§βπ¦ slot can point at the sentinel of another train (or any other chunk). Attach one at push time via the children argument; query it via the TrainChildren(parent) selector.
# Push a parent carriage whose CHILDREN points at a child train's sentinel.
storage.mem_set(memory.train_appender(
parent_domain,
memory.with_sparkle(payload_domain, "B"),
children=memory.train_carriage(child_domain),
))
# Walk the child train from the parent.
for chunk in storage.mem_get(
memory.train_forward(
memory.train_children(
memory.train_carriage(parent_domain, parent_carriage_pod)))):
print(chunk.note)
TrainChildren(parent) yields zero chunks (no error) when the parent has identity CHILDREN or is missing entirely. Hierarchical traversal is the callerβs job β walk a parent train, recurse into TrainChildren on each carriage.