Artifact 1f24f96e1d2e4e432d328dca198945bda23e70f9fe42a7dbd2044aa08cc13e27:
- File
internal/deck/occurrence.go
— part of check-in
[f51becc770]
at
2026-10-17 20:49:24
on branch feat/submanifest-reader
— feat(manifest): read sub-manifest trees (draft-fossil-submanifests-00)
Parse the S card and the SUBMANIFEST artifact type, and read tree manifests through one enumeration seam.
deck: the S card (exact arity, decoded filename, raw 40/64-hex hash, strictly ascending run between R and T), type Submanifest reached only by reassigning a D-less manifest, its F S Z legality row, and the 4.4 checks: no B with S, no F with S in a manifest, F xor S in a sub-manifest.
manifest: checkinFileCards, ownFileCards and lookupFileCard are the one place a check-in's file set is assembled (9.3). A tree manifest is walked depth first with an explicit stack, a depth bound of 16, the repeated-artifact check and the least-filename and ordering checks of 5.1; lookup binary-searches one sub-manifest per level (5.3); a delta may name a tree as its baseline (5.4). A tree that cannot be walked is an error, never a partial file set (5.2). mlink derivation, R-card verification and the delta-manifest builder read through the seam, and a commit on a tree parent is written flat.
Crosslink defers a check-in whose tree cannot be walked, recording the blocking sub-manifest in orphan so the phantom-fill cascade links it on arrival (7.3, 9.4). Storing an S-bearing artifact reserves phantoms for its absent references with the referrer's privacy (7.1) and sets the seen-submanifest indicator (8.1). A sub-manifest is filed as examined so the sweep never reselects it (9.2). The examined-blob table is versioned, so verdicts a parser without S cards reached are dropped. (user: [email protected], size: 3589) [annotate] [blame] [check-ins using] [more...]
package deck import ( "fmt" "strings" ) // Compare is the canonical comparator of draft-fossil-artifact-format-00 // §4.5.3: a raw-byte comparison in which octets are compared as unsigned // values, locale-independent, with no Unicode normalization and no case // folding. A shorter string sorts before any extension of itself, an // absent value (the empty string) sorts before any present value, and the // directory separator '/' (0x2F) receives no special treatment. It returns // a negative number, zero, or a positive number as a sorts before, equal // to, or after b. // // §4.5.3 defines one comparator shared by the F, J, M and T sort checks, // the delta-baseline merge walk (§8.2) and single-name search (§8.4). A // second copy anywhere is a correctness hazard, not a style issue: F // ordering is what the baseline binary search and the delta-manifest merge // walk will rely on, so two comparators that disagree resolve a delta manifest // wrongly rather than merely failing validation. Route every such // comparison through here. func Compare(a, b string) int { return strings.Compare(a, b) } // singleOccurrence marks, by letter offset from 'A', the 14 card types of // §4.5.1 whose duplicate MUST be rejected. F, J, M, T, P and Q are // repeatable and each occurrence appends, as is S // (draft-fossil-submanifests-00 §3.4). The Z card belongs to neither // class: it has no duplicate guard, because §4.5.1 derives "exactly one Z, // last" from the checksum framing rather than from a card rule, and an // earlier Z line is simply content covered by the final checksum. var singleOccurrence = func() [26]bool { var s [26]bool for _, c := range "ABCDEGHIKLNRUW" { s[c-'A'] = true } return s }() // seenCards tracks which single-occurrence card types a parse has already // consumed. The zero value is ready to use. type seenCards [26]bool // claim records one occurrence of card and reports an error if the type is // single-occurrence and has been seen before (§4.5.1). Card letters outside // A-Z are left to the parser's unknown-card path. func (s *seenCards) claim(card byte) error { if card < 'A' || card > 'Z' { return nil } i := card - 'A' if !singleOccurrence[i] { return nil } if s[i] { return fmt.Errorf("duplicate '%c' card", card) } s[i] = true return nil } // tagOrderKey returns the primary ordering key for a T card: the full name // token including its leading sign character, in decoded form (§4.7.16). // Ordering is the post-decode rule §4.5.2 spells out for F — a name reaches // the wire as "sym-my\sbranch" and orders as "sym-my branch", so comparing // decoded names rejects runs canonical fossil accepts. // // TagCard splits the sign into Type and stores Name already decoded (the // parser defossilizes it), so the key is just the sign reattached to the // name. Both the parser's ordering check and the marshaller's sort call // this, so neither can drift from the other. func tagOrderKey(t TagCard) string { return string(t.Type) + t.Name } // requireAscending enforces the single-key strictly-ascending intra-run // rule of §4.5.2 for the F, J and M card lists, and for the S card list of // draft-fossil-submanifests-00 §3.4: equal or descending // neighbours are rejected. The key is always the parser's decoded form, // which for F is the post-decode filename rather than the raw wire token. func requireAscending(card byte, previous, current string) error { if Compare(current, previous) > 0 { return nil } return fmt.Errorf("%c-card %q out of order after %q", card, current, previous) }