diff options
| author | 2026-03-25 14:34:50 +0000 | |
|---|---|---|
| committer | 2026-03-25 14:34:50 +0000 | |
| commit | e4a7aa0742f5070299d37e8421c99d67f0af3f90 (patch) | |
| tree | 36d89781476a92e61280c5ff232a2773e4092c0e /object/storer/packed/idx_parse.go | |
| parent | *: delta -> packfile/delta (diff) | |
| signature | No signature | |
*: object/store -> object/storer v0.1.107
Diffstat (limited to 'object/storer/packed/idx_parse.go')
| -rw-r--r-- | object/storer/packed/idx_parse.go | 78 |
1 files changed, 78 insertions, 0 deletions
diff --git a/object/storer/packed/idx_parse.go b/object/storer/packed/idx_parse.go new file mode 100644 index 00000000..128f01a2 --- /dev/null +++ b/object/storer/packed/idx_parse.go @@ -0,0 +1,78 @@ +package packed + +import ( + "encoding/binary" + "fmt" +) + +const ( + idxMagicV2 = 0xff744f63 + idxVersionV2 = 2 +) + +// parse validates mapped idx v2 structure and stores table boundaries. +func (index *idxFile) parse() error { + hashSize := index.algo.Size() + if hashSize <= 0 { + return fmt.Errorf("objectstorer/packed: idx %q has invalid hash algorithm", index.idxName) + } + + minLen := 8 + 256*4 + 2*hashSize + if len(index.data) < minLen { + return fmt.Errorf("objectstorer/packed: idx %q too short", index.idxName) + } + + if binary.BigEndian.Uint32(index.data[:4]) != idxMagicV2 { + return fmt.Errorf("objectstorer/packed: idx %q invalid magic", index.idxName) + } + + if binary.BigEndian.Uint32(index.data[4:8]) != idxVersionV2 { + return fmt.Errorf("objectstorer/packed: idx %q unsupported version", index.idxName) + } + + prev := uint32(0) + + for i := range 256 { + base := 8 + i*4 + + cur := binary.BigEndian.Uint32(index.data[base : base+4]) + if cur < prev { + return fmt.Errorf("objectstorer/packed: idx %q has non-monotonic fanout table", index.idxName) + } + + index.fanout[i] = cur + prev = cur + } + + index.numObjects = int(index.fanout[255]) + if index.numObjects < 0 { + return fmt.Errorf("objectstorer/packed: idx %q has invalid object count", index.idxName) + } + + namesBytes := index.numObjects * hashSize + crcBytes := index.numObjects * 4 + offset32Bytes := index.numObjects * 4 + + minSize := 8 + 256*4 + namesBytes + crcBytes + offset32Bytes + 2*hashSize + if minSize < 0 || len(index.data) < minSize { + return fmt.Errorf("objectstorer/packed: idx %q has truncated tables", index.idxName) + } + + index.namesOffset = 8 + 256*4 + index.offset32Offset = index.namesOffset + namesBytes + crcBytes + index.offset64Offset = index.offset32Offset + offset32Bytes + + offset64Bytes := len(index.data) - index.offset64Offset - 2*hashSize + if offset64Bytes < 0 || offset64Bytes%8 != 0 { + return fmt.Errorf("objectstorer/packed: idx %q has malformed 64-bit offset table", index.idxName) + } + + index.offset64Count = offset64Bytes / 8 + + maxOffset64Count := max(index.numObjects-1, 0) + if index.offset64Count > maxOffset64Count { + return fmt.Errorf("objectstorer/packed: idx %q has oversized 64-bit offset table", index.idxName) + } + + return nil +} |
