···127127- serialized into runs of CAR-format blocks,
128128- any other transformation
129129130130-Once the entire tree has been walked and frozen, the highest-layer MST node can finally be considered frozen to produce the root node CID, which match the CID in a STAR-lite file's header.
130130+Once the entire tree has been walked and frozen, the highest-layer MST node can finally be considered frozen to produce the root node CID, which must match the CID in a STAR-lite file's header.
131131132132133133### Archive verification
···148148# link_record(key, cid) appends an entry with a key and value link
149149# link_subtree(cid) inserts a node link as the "left" child (empty node),
150150# or as the right-most entry's "right"
151151-# to_cbor() => bytes bytes: canonical DAG-CBOR encoding of the MST node
151151+# to_cbor() => bytes canonical DAG-CBOR encoding of the MST node
152152153153def reconstruct_root_cid(key_record_pairs):
154154 """Compute the MST root CID from repo contents
···158158 stack: list[MstNode] = []
159159 prev_layer = -1
160160161161- # the actual walk. everything left of the stack is finalized.
161161+ # the actual walk. everything to the left of the stack is finalized.
162162 # anything remaining in the stack gets rolled up at the end.
163163 for (key, record_cbor) in key_record_pairs:
164164 key_layer = compute_mst_layer(key)
···168168 stack.append(MstNode())
169169170170 # finalize lower levels if this key is at a higher level than last.
171171- # higher key means everything lower in the stack is to-our-left now.
171171+ # higher key means everything lower in the stack is left-of-us now.
172172 if key_layer > prev_layer:
173173 for node, parent in zip(stack[:key_layer], stack[1:]):
174174 if node.is_empty():
···177177 node.reset_to_empty()
178178179179 # add a node entry for the current record
180180- stack[key_layer].link_value(key, compute_cid(record_cbor))
180180+ stack[key_layer].link_record(key, compute_cid(record_cbor))
181181182182 prev_layer = key_layer
183183···204204205205Since our depth-first walk finalizes children before parents, and the final parent finalizes last, we must unfortunately buffer all serialized CAR frames while the tree is walked. The good news is that a disk-spill-friendly byte log works well for this buffering.
206206207207+#### pseudo-code
207208208208-#### some old intuition-y words that might go somewhere but not here now
209209+```python
210210+# MstNode interface changes:
211211+# entries list of (key, cid, log position, right link)
212212+# left, entries[].right optional subtree link + stashed emit plan
213213+# link_record(key, cid, log_pos) stash the carv1 frame's byte log position
214214+# link_subtree(cid, emit_plan) stash an emit plan with the link
209215210210-Stream-ordered CARs (in "preorder traversal" block order) are a depth-first walk over the Merkle Search Tree, and keys encountered during a depth-first MST walk are in strict lexicographic order.
216216+def car_frame(data_bytes: bytes) -> tuple[Cid, bytes]:
217217+ """CARv1 block framing: [ varint | CID | data ]"""
218218+ cid = compute_cid(data_bytes)
219219+ data = cid.to_bytes() + data_bytes # wire-encoded CID bytes, not the digest
220220+ return cid, varint_bytes(len(data)) + data
211221212212-There is a a useful symmetry here:
222222+def frame_at(byte_log: bytes, position: int) -> bytes:
223223+ """Get a logged CARv1 frame from `position` using its own varint length"""
224224+ varint_len, payload_len = varint_read(byte_log, position)
225225+ frame_end = position + varint_len + payload_len
226226+ return byte_log[position:frame_end]
213227214214-- every subtree of an MST occupies a contiguous region of the stream-order serialized CAR
215215-- every subtree of an MST spans a contiguous range lexicographically-ordered keys
216228217217-So, any subtree-spanning range of keys (and records) can be materialized directly into its stream-ordered sequence of CAR blocks, independent of the rest of the archive.
229229+def build_subtree_emit_plan(node: MstNode, node_frame_position):
230230+ """assemble the stream-ordered emit plan for finalized subtree
218231232232+ this is the core of how we drive the CAR preorder traversal output!
219233220220-#### pseudo-code
234234+ node_frame_position: offset in the byte log of this node's own CARv1 frame
221235222222-```python
223223-# wip!
236236+ returns: ordered list of value-log indexes to serialized CARv1 frames
237237+ """
238238+ plan = []
224239225225-def to_stream_ordered_car(key_record_pairs):
226226- stack = []
227227- byte_log = [] # disk spilling omitted from this example
228228- prev_key_layer = 0
240240+ # first: the (CBOR-encoded) parent node itself
241241+ plan.append(node_frame_position)
229242230230- for (key, record) in key_record_pairs:
231231- record_cid = compute_cid(record)
243243+ # next, the left sub-subtree, if present
244244+ if node.left:
245245+ plan.extend(node.left.subtree_emit_plan)
232246233233- record_run = byte_log.append_car_frame(record_cid, record)
247247+ # finally, each value and entire value-right-subtree, in order:
248248+ for entry in node.entries:
249249+ # value first (always present in an MST entry)
250250+ plan.append(entry.frame_position)
251251+ # then after-value right sub-subtree (if present)
252252+ if entry.right:
253253+ plan.extend(entry.right.subtree_emit_plan)
234254235235- key_layer = layer_of(key)
255255+ return plan
236256237237- extend stack with empty slots until len(stack) >= key_layer + 1
238257239239- # every layer below key_layer that has content gets frozen. Its
240240- # node frame is appended to the byte log, and the resulting
241241- # subtree's emit plan is propagated up to layer L+1.
242242- for lower_layer in range(0, key_layer):
243243- if node := stack.get(lower_layer):
244244- (node_cid, node_bytes) = encode_mst_node(node)
245245- node_run = byte_log.append_car_frame(node_cid, node_bytes)
246246- subtree_emit_plan = build_emit_plan(node, node_run)
247247- push_subtree_with_plan(stack[lower_layer + 1], node_cid, subtree_emit_plan)
248248- stack[lower_layer] = None
258258+def to_stream_ordered_car_body(key_record_pairs):
259259+ """Get a stream-ordered atproto CAR body from repository contents
249260250250- # bleh, None handling kind of sucks. we should actually check nodes for .empty() and push/extend where needed
261261+ returns (root_cid, output_bytes) -- does not write a CAR header or the
262262+ commit object's block, which must come first in the body for stream-order.
251263252252- if stack.get(key_layer) is None:
253253- stack[key_layer] = make_empty_node() # blehhh
264264+ key_record_pairs must be in lexicographic key order (= depth-first mst walk)
265265+ """
266266+ stack: list[MstNode] = []
267267+ byte_log = bytearray()
268268+ prev_layer = -1
254269255255- stack[key_layer].entries.append(WhatIsThis(
256256- key=key,
257257- cid=record_cid,
258258- car_run=record_run,
259259- right=None,
260260- right_emit_plan=None,
261261- ))
270270+ # the actual walk. everything to the left of the stack is finalized.
271271+ # anything remaining in the stack gets rolled up at the end.
272272+ # serialized CARv1 frames appended into byte_log as we go.
273273+ for (key, record_cbor) in key_record_pairs:
274274+ key_layer = compute_mst_layer(key)
262275263263- # End of input: fold remaining stack bottom-up the same way.
264264- node_cid, node_emit_plan = None, None
265265- for node in stack:
266266- if node_cid is not None:
267267- push_subtree_with_plan(node, node_cid, node_emit_plan)
268268- node_cid, node_emit_plan = None, None
269269- if node is not empty:
270270- (node_cid, node_bytes) = encode_mst_node(node)
271271- node_run = byte_log.append_car_frame(node_cid, node_bytes)
272272- node_emit_plan = build_emit_plan(node, node_run)
273273- node_cid = node_cid
276276+ # grow the stack if needed, init with empty nodes.
277277+ while len(stack) <= key_layer:
278278+ stack.append(MstNode())
274279275275- # Empty repo: emit the canonical empty MST node into the byte log.
276276- if node_cid is None:
277277- (node_cid, node_bytes) = encode_mst_node(empty stack-slot)
278278- node_run = byte_log.append_car_frame(node_cid, node_bytes)
279279- node_emit_plan = [node_run]
280280+ # finalize lower levels if this key is at a higher level than last.
281281+ # higher key means everything lower in the stack is left-of-us now.
282282+ if key_layer > prev_layer:
283283+ for node, parent in zip(stack[:key_layer], stack[1:]):
284284+ if node.is_empty():
285285+ continue # skip possible empty bottom-most nodes
280286281281- output = []
282282- for run in node_emit_plan:
283283- output.extend(byte_log[run.what:run.whattt])
287287+ # put finalized (+serialized, CAR-framed) node into the byte log
288288+ frame_position = len(byte_log)
289289+ cid, framed = car_frame(node.to_cbor())
290290+ byte_log.extend(framed)
284291285285- return node_cid, output
292292+ # link it from the parent node now it's finalized with a CID
293293+ node_emit_plan = build_subtree_emit_plan(node, frame_position)
294294+ parent.link_subtree(cid, node_emit_plan)
295295+ node.reset_to_empty()
296296+297297+ # put the current record into the byte log
298298+ frame_position = len(byte_log)
299299+ record_cid, framed = car_frame(record_cbor)
300300+ byte_log.extend(framed)
301301+302302+ # and link it from the MST node's entries at this layer
303303+ stack[key_layer].link_record(key, record_cid, frame_position)
304304+305305+ prev_layer = key_layer
306306+307307+ # finalize remaining stack
308308+ for node, parent in zip(stack[:-1], stack[1:]):
309309+ if node.is_empty():
310310+ continue
311311+312312+ frame_position = len(byte_log)
313313+ cid, framed = car_frame(node.to_cbor())
314314+ byte_log.extend(framed)
315315+316316+ node_emit_plan = build_subtree_emit_plan(node, frame_position)
317317+ parent.link_subtree(cid, node_emit_plan)
318318+ node.reset_to_empty()
319319+320320+ # get the finished root node, finally.
321321+ if len(stack) > 0:
322322+ root = stack[-1]
323323+ else:
324324+ root = MstNode() # empty repo: atproto CAR writes one single empty node
325325+326326+ # frame the root and get it in the logggggggg
327327+ root_frame_position = len(byte_log)
328328+ root_cid, framed = car_frame(root.to_cbor())
329329+ byte_log.extend(framed)
330330+331331+ # and pull together the final emit plan
332332+ root_emit_plan = build_subtree_emit_plan(root, root_frame_position)
333333+334334+ # walk the plan into the final output!!!
335335+ output = bytearray()
336336+ for position in root_emit_plan:
337337+ output.extend(frame_at(byte_log, position))
338338+339339+ return root_cid, output
286340```
341341+342342+343343+#### some old intuition-y words that might go somewhere but not here now
344344+345345+Stream-ordered CARs (in "preorder traversal" block order) are a depth-first walk over the Merkle Search Tree, and keys encountered during a depth-first MST walk are in strict lexicographic order.
346346+347347+There is a a useful symmetry here:
348348+349349+- every subtree of an MST occupies a contiguous region of the stream-order serialized CAR
350350+- every subtree of an MST spans a contiguous range lexicographically-ordered keys
351351+352352+So, any subtree-spanning range of keys (and records) can be materialized directly into its stream-ordered sequence of CAR blocks, independent of the rest of the archive.
287353288354289355#### Empty repos