···11+* One-time load -> immutable snapshots and such; publish only fully loaded
22+ snapshots.
33+* Reload behavior for tables.list; read tables.list, open all listed tables,
44+ and retry from the start if any listed table is missing during open.
55+* Snapshot staleness detection
66+* Resolve's block search should be restart aware. Then the linear scan is only
77+ within the chosen restart window.
88+* Cache parsed index-block metadata per table and block offset. Reuse restart
99+ offsets and decoded key boundaries across lookups to try to reduce repeated
1010+ parsing.
1111+* Pin one snapshot for each high-level read call. ResolveFully should resolve
1212+ all symbolic hops against one snapshot and avoid repeated ensure/reload
1313+ checks per hop.
1414+* Add lazy snapshot-derived caches for list-heavy operations. Build visible
1515+ merged refs and sorted names once per snapshot, and reuse for List and
1616+ Shorten.
1717+* Make format parser stricter to match Git-level behavior. Validate unaligned
1818+ multi-ref-block tables require ref index, and enforce more precise
1919+ first-block/restart invariants where applicable.
2020+* Improve parse and lookup diagnostics with contextual errors. Return
2121+ fmt.Errorf values that include table name, block offset, and record offset,
2222+ where possible.
+424
refstore/reftable/lookup.go
···11+package reftable
22+33+import (
44+ "encoding/binary"
55+ "fmt"
66+ "strings"
77+88+ "codeberg.org/lindenii/furgit/objectid"
99+)
1010+1111+// resolveRecord resolves one ref name inside a single table file.
1212+func (table *tableFile) resolveRecord(name string) (recordValue, bool, error) {
1313+ if table.refIndexPos != 0 {
1414+ pos, ok, err := table.resolveRefBlockPosFromIndex(name, int(table.refIndexPos))
1515+ if err != nil {
1616+ return recordValue{}, false, err
1717+ }
1818+ if !ok {
1919+ return recordValue{}, false, nil
2020+ }
2121+ return table.lookupInRefBlock(name, pos)
2222+ }
2323+2424+ // Without a ref index, fall back to scanning ref blocks in order.
2525+ pos := table.headerLen
2626+ for pos < table.refEnd {
2727+ for pos < table.refEnd && table.data[pos] == 0 {
2828+ pos++
2929+ }
3030+ if pos >= table.refEnd {
3131+ break
3232+ }
3333+ if table.data[pos] != blockTypeRef {
3434+ return recordValue{}, false, fmt.Errorf("refstore/reftable: table %q: unexpected block type %q in ref section", table.name, table.data[pos])
3535+ }
3636+ block, blockEnd, err := table.readBlockAt(pos)
3737+ if err != nil {
3838+ return recordValue{}, false, err
3939+ }
4040+ found, done, rec, err := lookupRecordInRefBlock(table, block, name)
4141+ if err != nil {
4242+ return recordValue{}, false, err
4343+ }
4444+ if found {
4545+ return rec, true, nil
4646+ }
4747+ if done {
4848+ return recordValue{}, false, nil
4949+ }
5050+ pos = table.nextBlockPos(blockEnd)
5151+ }
5252+ return recordValue{}, false, nil
5353+}
5454+5555+// resolveRefBlockPosFromIndex resolves a candidate ref block position via index blocks.
5656+func (table *tableFile) resolveRefBlockPosFromIndex(name string, indexPos int) (int, bool, error) {
5757+ block, _, err := table.readBlockAt(indexPos)
5858+ if err != nil {
5959+ return 0, false, err
6060+ }
6161+ if block.blockType != blockTypeIndex {
6262+ return 0, false, fmt.Errorf("refstore/reftable: table %q: ref index root is not index block", table.name)
6363+ }
6464+ childPos, ok, err := lookupChildPosInIndexBlock(block, name)
6565+ if err != nil {
6666+ return 0, false, err
6767+ }
6868+ if !ok {
6969+ return 0, false, nil
7070+ }
7171+ if childPos < 0 || childPos >= len(table.data) {
7272+ return 0, false, fmt.Errorf("refstore/reftable: table %q: index child position out of range", table.name)
7373+ }
7474+7575+ childType := table.data[childPos]
7676+ switch childType {
7777+ case blockTypeRef:
7878+ return childPos, true, nil
7979+ case blockTypeIndex:
8080+ return table.resolveRefBlockPosFromIndex(name, childPos)
8181+ default:
8282+ return 0, false, fmt.Errorf("refstore/reftable: table %q: unexpected child block type %q", table.name, childType)
8383+ }
8484+}
8585+8686+// lookupInRefBlock searches one ref block by full ref name.
8787+func (table *tableFile) lookupInRefBlock(name string, pos int) (recordValue, bool, error) {
8888+ block, _, err := table.readBlockAt(pos)
8989+ if err != nil {
9090+ return recordValue{}, false, err
9191+ }
9292+ if block.blockType != blockTypeRef {
9393+ return recordValue{}, false, fmt.Errorf("refstore/reftable: table %q: expected ref block at %d", table.name, pos)
9494+ }
9595+ found, _, rec, err := lookupRecordInRefBlock(table, block, name)
9696+ if err != nil {
9797+ return recordValue{}, false, err
9898+ }
9999+ return rec, found, nil
100100+}
101101+102102+// forEachRecord iterates all ref records in this table in lexical order.
103103+func (table *tableFile) forEachRecord(fn func(name string, rec recordValue) error) error {
104104+ pos := table.headerLen
105105+ prevLast := ""
106106+ for pos < table.refEnd {
107107+ for pos < table.refEnd && table.data[pos] == 0 {
108108+ pos++
109109+ }
110110+ if pos >= table.refEnd {
111111+ break
112112+ }
113113+ if table.data[pos] != blockTypeRef {
114114+ return fmt.Errorf("refstore/reftable: table %q: unexpected block type %q in ref section", table.name, table.data[pos])
115115+ }
116116+117117+ block, blockEnd, err := table.readBlockAt(pos)
118118+ if err != nil {
119119+ return err
120120+ }
121121+ var first, last string
122122+ err = forEachRecordInRefBlock(table, block, func(name string, rec recordValue) error {
123123+ if first == "" {
124124+ first = name
125125+ }
126126+ last = name
127127+ return fn(name, rec)
128128+ })
129129+ if err != nil {
130130+ return err
131131+ }
132132+ if prevLast != "" && first != "" && strings.Compare(first, prevLast) <= 0 {
133133+ return fmt.Errorf("refstore/reftable: table %q: ref blocks are not strictly ordered", table.name)
134134+ }
135135+ if last != "" {
136136+ prevLast = last
137137+ }
138138+ pos = table.nextBlockPos(blockEnd)
139139+ }
140140+ return nil
141141+}
142142+143143+// blockView is one decoded block boundary within the mapped table bytes.
144144+type blockView struct {
145145+ blockType byte
146146+ start int
147147+ end int
148148+ first bool
149149+ payload []byte
150150+}
151151+152152+// readBlockAt validates and returns a block view starting at pos.
153153+func (table *tableFile) readBlockAt(pos int) (blockView, int, error) {
154154+ if pos < 0 || pos+4 > len(table.data) {
155155+ return blockView{}, 0, fmt.Errorf("refstore/reftable: table %q: block header out of range", table.name)
156156+ }
157157+ blockLen := int(readUint24(table.data[pos+1 : pos+4]))
158158+ effectiveLen := blockLen
159159+ if pos == table.headerLen {
160160+ if blockLen < table.headerLen {
161161+ return blockView{}, 0, fmt.Errorf("refstore/reftable: table %q: invalid first block length", table.name)
162162+ }
163163+ effectiveLen = blockLen - table.headerLen
164164+ }
165165+ if effectiveLen < 4 {
166166+ return blockView{}, 0, fmt.Errorf("refstore/reftable: table %q: invalid block length", table.name)
167167+ }
168168+ end := pos + effectiveLen
169169+ if end > len(table.data) {
170170+ return blockView{}, 0, fmt.Errorf("refstore/reftable: table %q: block out of range", table.name)
171171+ }
172172+ view := blockView{blockType: table.data[pos], start: pos, end: end, first: pos == table.headerLen, payload: table.data[pos:end]}
173173+ return view, end, nil
174174+}
175175+176176+// nextBlockPos computes the next block start from current block end.
177177+func (table *tableFile) nextBlockPos(blockEnd int) int {
178178+ if table.blockSize > 0 {
179179+ return alignUp(blockEnd, table.blockSize)
180180+ }
181181+ return blockEnd
182182+}
183183+184184+// lookupChildPosInIndexBlock selects a child block position for key.
185185+func lookupChildPosInIndexBlock(block blockView, key string) (int, bool, error) {
186186+ off, recordsEnd, restarts, err := parseBlockLayout(block)
187187+ if err != nil {
188188+ return 0, false, err
189189+ }
190190+ if err := validateRestarts(block, restarts, off, recordsEnd, true); err != nil {
191191+ return 0, false, err
192192+ }
193193+ prev := ""
194194+ for off < recordsEnd {
195195+ name, v, nextOff, err := parseKeyedRecord(block.payload, off, recordsEnd, prev)
196196+ if err != nil {
197197+ return 0, false, err
198198+ }
199199+ if (v & 0x7) != 0 {
200200+ return 0, false, fmt.Errorf("index value_type must be 0")
201201+ }
202202+ childPos, nextOff, err := readVarint(block.payload, nextOff, recordsEnd)
203203+ if err != nil {
204204+ return 0, false, err
205205+ }
206206+ if strings.Compare(key, name) <= 0 {
207207+ if childPos > uint64(int(^uint(0)>>1)) {
208208+ return 0, false, fmt.Errorf("index child position overflows int")
209209+ }
210210+ return int(childPos), true, nil
211211+ }
212212+ prev = name
213213+ off = nextOff
214214+ }
215215+ if off != recordsEnd {
216216+ return 0, false, fmt.Errorf("malformed index block")
217217+ }
218218+ return 0, false, nil
219219+}
220220+221221+// lookupRecordInRefBlock searches one ref block and may short-circuit by sort order.
222222+func lookupRecordInRefBlock(table *tableFile, block blockView, key string) (found bool, done bool, rec recordValue, err error) {
223223+ off, recordsEnd, restarts, err := parseBlockLayout(block)
224224+ if err != nil {
225225+ return false, false, recordValue{}, err
226226+ }
227227+ if err := validateRestarts(block, restarts, off, recordsEnd, true); err != nil {
228228+ return false, false, recordValue{}, err
229229+ }
230230+ prev := ""
231231+ for off < recordsEnd {
232232+ name, v, nextOff, err := parseKeyedRecord(block.payload, off, recordsEnd, prev)
233233+ if err != nil {
234234+ return false, false, recordValue{}, err
235235+ }
236236+ typeBits := byte(v & 0x7)
237237+ _, nextOff, err = readVarint(block.payload, nextOff, recordsEnd)
238238+ if err != nil {
239239+ return false, false, recordValue{}, err
240240+ }
241241+ recVal, nextOff, err := parseRefValue(block.payload, nextOff, recordsEnd, table.algo, typeBits)
242242+ if err != nil {
243243+ return false, false, recordValue{}, err
244244+ }
245245+ cmp := strings.Compare(name, key)
246246+ if cmp == 0 {
247247+ return true, true, recVal, nil
248248+ }
249249+ if cmp > 0 {
250250+ return false, true, recordValue{}, nil
251251+ }
252252+ prev = name
253253+ off = nextOff
254254+ }
255255+ if off != recordsEnd {
256256+ return false, false, recordValue{}, fmt.Errorf("malformed ref block")
257257+ }
258258+ return false, false, recordValue{}, nil
259259+}
260260+261261+// forEachRecordInRefBlock iterates all records in one ref block.
262262+func forEachRecordInRefBlock(table *tableFile, block blockView, fn func(name string, rec recordValue) error) error {
263263+ off, recordsEnd, restarts, err := parseBlockLayout(block)
264264+ if err != nil {
265265+ return err
266266+ }
267267+ if err := validateRestarts(block, restarts, off, recordsEnd, true); err != nil {
268268+ return err
269269+ }
270270+ prev := ""
271271+ for off < recordsEnd {
272272+ name, v, nextOff, err := parseKeyedRecord(block.payload, off, recordsEnd, prev)
273273+ if err != nil {
274274+ return err
275275+ }
276276+ typeBits := byte(v & 0x7)
277277+ _, nextOff, err = readVarint(block.payload, nextOff, recordsEnd)
278278+ if err != nil {
279279+ return err
280280+ }
281281+ recVal, nextOff, err := parseRefValue(block.payload, nextOff, recordsEnd, table.algo, typeBits)
282282+ if err != nil {
283283+ return err
284284+ }
285285+ if err := fn(name, recVal); err != nil {
286286+ return err
287287+ }
288288+ prev = name
289289+ off = nextOff
290290+ }
291291+ if off != recordsEnd {
292292+ return fmt.Errorf("malformed ref block")
293293+ }
294294+ return nil
295295+}
296296+297297+// parseBlockLayout parses common record/restart regions for ref and index blocks.
298298+func parseBlockLayout(block blockView) (recordsStart int, recordsEnd int, restarts []int, err error) {
299299+ if len(block.payload) < 6 {
300300+ return 0, 0, nil, fmt.Errorf("short block")
301301+ }
302302+ restartCount := int(binary.BigEndian.Uint16(block.payload[len(block.payload)-2:]))
303303+ if restartCount <= 0 {
304304+ return 0, 0, nil, fmt.Errorf("invalid restart count")
305305+ }
306306+ restarts = make([]int, restartCount)
307307+ restartBytes := restartCount * 3
308308+ restartsStart := len(block.payload) - 2 - restartBytes
309309+ if restartsStart < 4 {
310310+ return 0, 0, nil, fmt.Errorf("invalid restart table")
311311+ }
312312+ for i := 0; i < restartCount; i++ {
313313+ off := restartsStart + i*3
314314+ rel := int(readUint24(block.payload[off : off+3]))
315315+ base := block.start
316316+ if block.first {
317317+ // In the first block, restart offsets are relative to file start.
318318+ base = 0
319319+ }
320320+ abs := base + rel
321321+ restarts[i] = abs - block.start
322322+ }
323323+ return 4, restartsStart, restarts, nil
324324+}
325325+326326+// validateRestarts validates restart monotonicity, bounds and record-prefix invariants.
327327+func validateRestarts(block blockView, restarts []int, recordsStart, recordsEnd int, requirePrefixZero bool) error {
328328+ prev := -1
329329+ for _, off := range restarts {
330330+ if off < recordsStart || off >= recordsEnd {
331331+ return fmt.Errorf("restart offset out of range")
332332+ }
333333+ if off <= prev {
334334+ return fmt.Errorf("restart offsets not strictly increasing")
335335+ }
336336+ prev = off
337337+ if requirePrefixZero {
338338+ prefix, _, err := readVarint(block.payload, off, recordsEnd)
339339+ if err != nil {
340340+ return err
341341+ }
342342+ if prefix != 0 {
343343+ return fmt.Errorf("restart record prefix length must be zero")
344344+ }
345345+ }
346346+ }
347347+ return nil
348348+}
349349+350350+// parseKeyedRecord parses one prefix-compressed key record header.
351351+func parseKeyedRecord(buf []byte, off, end int, prev string) (name string, rawType uint64, next int, err error) {
352352+ prefixLen, next, err := readVarint(buf, off, end)
353353+ if err != nil {
354354+ return "", 0, 0, err
355355+ }
356356+ suffixAndType, next, err := readVarint(buf, next, end)
357357+ if err != nil {
358358+ return "", 0, 0, err
359359+ }
360360+ suffixLen := int(suffixAndType >> 3)
361361+ if suffixLen < 0 || next+suffixLen > end {
362362+ return "", 0, 0, fmt.Errorf("invalid suffix length")
363363+ }
364364+ if int(prefixLen) > len(prev) {
365365+ return "", 0, 0, fmt.Errorf("invalid prefix length")
366366+ }
367367+ name = prev[:prefixLen] + string(buf[next:next+suffixLen])
368368+ next += suffixLen
369369+ if prev != "" && strings.Compare(name, prev) <= 0 {
370370+ return "", 0, 0, fmt.Errorf("keys not strictly increasing")
371371+ }
372372+ return name, suffixAndType, next, nil
373373+}
374374+375375+// parseRefValue parses one ref-record value payload according to value_type.
376376+func parseRefValue(buf []byte, off, end int, algo objectid.Algorithm, valueType byte) (recordValue, int, error) {
377377+ switch valueType {
378378+ case 0x0:
379379+ return recordValue{deleted: true}, off, nil
380380+ case 0x1:
381381+ id, next, err := readObjectID(buf, off, end, algo)
382382+ if err != nil {
383383+ return recordValue{}, 0, err
384384+ }
385385+ return recordValue{detachedID: id, hasDetached: true}, next, nil
386386+ case 0x2:
387387+ id, next, err := readObjectID(buf, off, end, algo)
388388+ if err != nil {
389389+ return recordValue{}, 0, err
390390+ }
391391+ peeled, next, err := readObjectID(buf, next, end, algo)
392392+ if err != nil {
393393+ return recordValue{}, 0, err
394394+ }
395395+ peeledCopy := peeled
396396+ return recordValue{detachedID: id, hasDetached: true, peeled: &peeledCopy}, next, nil
397397+ case 0x3:
398398+ targetLen, next, err := readVarint(buf, off, end)
399399+ if err != nil {
400400+ return recordValue{}, 0, err
401401+ }
402402+ if targetLen > uint64(end-next) {
403403+ return recordValue{}, 0, fmt.Errorf("invalid symref target length")
404404+ }
405405+ target := string(buf[next : next+int(targetLen)])
406406+ next += int(targetLen)
407407+ return recordValue{symbolicTarget: target}, next, nil
408408+ default:
409409+ return recordValue{}, 0, fmt.Errorf("unsupported ref value type %d", valueType)
410410+ }
411411+}
412412+413413+// readObjectID reads one object ID using the table algorithm width.
414414+func readObjectID(buf []byte, off, end int, algo objectid.Algorithm) (objectid.ObjectID, int, error) {
415415+ sz := algo.Size()
416416+ if off < 0 || sz < 0 || off+sz > end {
417417+ return objectid.ObjectID{}, 0, fmt.Errorf("truncated object id")
418418+ }
419419+ id, err := objectid.FromBytes(algo, buf[off:off+sz])
420420+ if err != nil {
421421+ return objectid.ObjectID{}, 0, err
422422+ }
423423+ return id, off + sz, nil
424424+}
+36
refstore/reftable/parse_helpers.go
···11+package reftable
22+33+import "fmt"
44+55+// readUint24 reads a 24-bit big-endian unsigned integer.
66+func readUint24(b []byte) uint32 {
77+ return uint32(b[0])<<16 | uint32(b[1])<<8 | uint32(b[2])
88+}
99+1010+// alignUp rounds pos up to the next multiple of blockSize.
1111+func alignUp(pos, blockSize int) int {
1212+ rem := pos % blockSize
1313+ if rem == 0 {
1414+ return pos
1515+ }
1616+ return pos + (blockSize - rem)
1717+}
1818+1919+// readVarint decodes one reftable/ofs-delta style varint.
2020+func readVarint(buf []byte, off, end int) (uint64, int, error) {
2121+ if off >= end {
2222+ return 0, 0, fmt.Errorf("unexpected EOF")
2323+ }
2424+ b := buf[off]
2525+ val := uint64(b & 0x7f)
2626+ off++
2727+ for b&0x80 != 0 {
2828+ if off >= end {
2929+ return 0, 0, fmt.Errorf("unexpected EOF")
3030+ }
3131+ b = buf[off]
3232+ off++
3333+ val = ((val + 1) << 7) | uint64(b&0x7f)
3434+ }
3535+ return val, off, nil
3636+}
+8
refstore/reftable/path.go
···11+package reftable
22+33+import "path"
44+55+// pathMatch applies path.Match to full ref names.
66+func pathMatch(pattern, name string) (bool, error) {
77+ return path.Match(pattern, name)
88+}