diff options
| author | 2026-01-29 14:38:18 +0100 | |
|---|---|---|
| committer | 2026-01-29 14:51:10 +0100 | |
| commit | 33de7fd28ce870d0b98016fcb42aa9ae5c0ca78a (patch) | |
| tree | 06e2ef213cb07396bf59e979096df910ed477ede /pack_idx_read.go | |
| parent | pack: Harden pack writing test with 1000 1kb files (diff) | |
| signature | No signature | |
packed: More uniform file naming scheme
Diffstat (limited to 'pack_idx_read.go')
| -rw-r--r-- | pack_idx_read.go | 290 |
1 files changed, 0 insertions, 290 deletions
diff --git a/pack_idx_read.go b/pack_idx_read.go deleted file mode 100644 index 0dbb9bcf..00000000 --- a/pack_idx_read.go +++ /dev/null @@ -1,290 +0,0 @@ -package furgit - -import ( - "bytes" - "errors" - "fmt" - "os" - "path/filepath" - "strings" - "sync" - "syscall" -) - -const ( - idxMagic = 0xff744f63 - idxVersion2 = 2 -) - -type packIndex struct { - repo *Repository - idxRel string - packPath string - - loadOnce sync.Once - loadErr error - - numObjects int - fanout []byte - names []byte - crcs []byte - offset32 []byte - offset64 []byte - data []byte - - closeOnce sync.Once -} - -func (pi *packIndex) Close() error { - if pi == nil { - return nil - } - var closeErr error - pi.closeOnce.Do(func() { - if len(pi.data) > 0 { - if err := syscall.Munmap(pi.data); closeErr == nil { - closeErr = err - } - pi.data = nil - pi.fanout = nil - pi.names = nil - pi.crcs = nil - pi.offset32 = nil - pi.offset64 = nil - pi.numObjects = 0 - } - }) - return closeErr -} - -func (pi *packIndex) ensureLoaded() error { - pi.loadOnce.Do(func() { - pi.loadErr = pi.load() - }) - return pi.loadErr -} - -func (pi *packIndex) load() error { - if pi.repo == nil { - return ErrInvalidObject - } - f, err := os.Open(pi.repo.repoPath(pi.idxRel)) - if err != nil { - return err - } - stat, err := f.Stat() - if err != nil { - _ = f.Close() - return err - } - if stat.Size() < 8+256*4 { - _ = f.Close() - return ErrInvalidObject - } - region, err := syscall.Mmap( - int(f.Fd()), - 0, - int(stat.Size()), - syscall.PROT_READ, - syscall.MAP_PRIVATE, - ) - if err != nil { - _ = f.Close() - return err - } - err = f.Close() - if err != nil { - _ = syscall.Munmap(region) - return err - } - err = pi.parse(region) - if err != nil { - _ = syscall.Munmap(region) - return err - } - pi.data = region - return nil -} - -func (repo *Repository) packIndexes() ([]*packIndex, error) { - repo.packIdxOnce.Do(func() { - repo.packIdx, repo.packIdxErr = repo.loadPackIndexes() - }) - return repo.packIdx, repo.packIdxErr -} - -func (repo *Repository) loadPackIndexes() ([]*packIndex, error) { - dir := filepath.Join(repo.rootPath, "objects", "pack") - entries, err := os.ReadDir(dir) - if err != nil { - if os.IsNotExist(err) { - return nil, ErrNotFound - } - return nil, err - } - - idxs := make([]*packIndex, 0, len(entries)) - for _, entry := range entries { - if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".idx") { - continue - } - rel := filepath.Join("objects", "pack", entry.Name()) - packRel := strings.TrimSuffix(rel, ".idx") + ".pack" - idxs = append(idxs, &packIndex{ - repo: repo, - idxRel: rel, - packPath: packRel, - }) - } - if len(idxs) == 0 { - return nil, ErrNotFound - } - return idxs, nil -} - -func (pi *packIndex) parse(buf []byte) error { - if len(buf) < 8+256*4 { - return ErrInvalidObject - } - if readBE32(buf[0:4]) != idxMagic { - return ErrInvalidObject - } - if readBE32(buf[4:8]) != idxVersion2 { - return ErrInvalidObject - } - - const fanoutBytes = 256 * 4 - fanoutStart := 8 - fanoutEnd := fanoutStart + fanoutBytes - if fanoutEnd > len(buf) { - return ErrInvalidObject - } - pi.fanout = buf[fanoutStart:fanoutEnd] - nobj := int(readBE32(pi.fanout[len(pi.fanout)-4:])) - - namesStart := fanoutEnd - namesEnd := namesStart + nobj*pi.repo.hashAlgo.Size() - if namesEnd > len(buf) { - return ErrInvalidObject - } - - crcStart := namesEnd - crcEnd := crcStart + nobj*4 - if crcEnd > len(buf) { - return ErrInvalidObject - } - - off32Start := crcEnd - off32End := off32Start + nobj*4 - if off32End > len(buf) { - return ErrInvalidObject - } - - pi.offset32 = buf[off32Start:off32End] - - off64Start := off32End - trailerStart := len(buf) - 2*pi.repo.hashAlgo.Size() - if trailerStart < off64Start { - return ErrInvalidObject - } - if (trailerStart-off64Start)%8 != 0 { - return ErrInvalidObject - } - off64End := trailerStart - pi.offset64 = buf[off64Start:off64End] - - pi.numObjects = nobj - pi.names = buf[namesStart:namesEnd] - pi.crcs = buf[crcStart:crcEnd] - return nil -} - -func readBE32(b []byte) uint32 { - _ = b[3] - return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3]) -} - -func readBE64(b []byte) uint64 { - _ = b[7] - return (uint64(b[0]) << 56) | (uint64(b[1]) << 48) | - (uint64(b[2]) << 40) | (uint64(b[3]) << 32) | - (uint64(b[4]) << 24) | (uint64(b[5]) << 16) | - (uint64(b[6]) << 8) | uint64(b[7]) -} - -func (pi *packIndex) fanoutEntry(i int) uint32 { - if len(pi.fanout) == 0 { - return 0 - } - entries := len(pi.fanout) / 4 - if i < 0 || i >= entries { - return 0 - } - start := i * 4 - return readBE32(pi.fanout[start : start+4]) -} - -func (pi *packIndex) offset(idx int) (uint64, error) { - start := idx * 4 - word := readBE32(pi.offset32[start : start+4]) - if word&0x80000000 == 0 { - return uint64(word), nil - } - pos := int(word & 0x7fffffff) - entries := len(pi.offset64) / 8 - if pos < 0 || pos >= entries { - return 0, errors.New("furgit: pack: corrupt 64-bit offset table") - } - base := pos * 8 - return readBE64(pi.offset64[base : base+8]), nil -} - -func (pi *packIndex) lookup(id Hash) (packlocation, error) { - err := pi.ensureLoaded() - if err != nil { - return packlocation{}, err - } - if id.algo != pi.repo.hashAlgo { - return packlocation{}, fmt.Errorf("furgit: hash algorithm mismatch: got %s, expected %s", id.algo.String(), pi.repo.hashAlgo.String()) - } - first := int(id.data[0]) - var lo int - if first > 0 { - lo = int(pi.fanoutEntry(first - 1)) - } - hi := int(pi.fanoutEntry(first)) - idx, found := bsearchHash(pi.names, pi.repo.hashAlgo.Size(), lo, hi, id) - if !found { - return packlocation{}, ErrNotFound - } - ofs, err := pi.offset(idx) - if err != nil { - return packlocation{}, err - } - return packlocation{ - PackPath: pi.packPath, - Offset: ofs, - }, nil -} - -func bsearchHash(names []byte, stride, lo, hi int, want Hash) (int, bool) { - for lo < hi { - mid := lo + (hi-lo)/2 - cmp := compareHash(names, stride, mid, want.data[:stride]) - if cmp == 0 { - return mid, true - } - if cmp > 0 { - hi = mid - } else { - lo = mid + 1 - } - } - return lo, false -} - -func compareHash(names []byte, stride, idx int, want []byte) int { - base := idx * stride - end := base + stride - return bytes.Compare(names[base:end], want) -} |
