diff options
| author | 2026-02-21 19:44:12 +0800 | |
|---|---|---|
| committer | 2026-02-21 19:44:12 +0800 | |
| commit | 0a4686c132052d9b01ac5d438c6a46e7b4fe22e5 (patch) | |
| tree | 8d344297d337c09d84923895899d8616f509d23d /objectstore/packed/idx_open.go | |
| parent | objectstore/packed: Lazily parse idx metadata (diff) | |
| signature | No signature | |
objectstore/packed: Separate idx candidate lookup vs actually opening it
Diffstat (limited to 'objectstore/packed/idx_open.go')
| -rw-r--r-- | objectstore/packed/idx_open.go | 131 |
1 files changed, 131 insertions, 0 deletions
diff --git a/objectstore/packed/idx_open.go b/objectstore/packed/idx_open.go new file mode 100644 index 00000000..45f0f83d --- /dev/null +++ b/objectstore/packed/idx_open.go @@ -0,0 +1,131 @@ +package packed + +import ( + "fmt" + "os" + "syscall" + + "codeberg.org/lindenii/furgit/internal/intconv" + "codeberg.org/lindenii/furgit/objectid" +) + +// idxFile stores one mapped and validated idx v2 file. +type idxFile struct { + // idxName is the basename of this .idx file. + idxName string + // packName is the matching .pack basename. + packName string + // algo is the hash algorithm encoded by the index. + algo objectid.Algorithm + + // file is the opened index file descriptor. + file *os.File + // data is the mapped index bytes. + data []byte + + // fanout stores fanout table values. + fanout [256]uint32 + // numObjects equals fanout[255]. + numObjects int + + // namesOffset starts the sorted object-id table. + namesOffset int + // offset32Offset starts the 32-bit offset table. + offset32Offset int + // offset64Offset starts the 64-bit offset table. + offset64Offset int + // offset64Count is the number of 64-bit offset entries. + offset64Count int +} + +// candidateForPack returns one discovered candidate for a pack basename. +func (store *Store) candidateForPack(packName string) (packCandidate, bool) { + store.stateMu.RLock() + candidate, ok := store.candidateByPack[packName] + store.stateMu.RUnlock() + return candidate, ok +} + +// openIndex returns one opened and parsed index, caching it by pack basename. +func (store *Store) openIndex(candidate packCandidate) (*idxFile, error) { + store.stateMu.RLock() + if index, ok := store.idxByPack[candidate.packName]; ok { + store.stateMu.RUnlock() + return index, nil + } + store.stateMu.RUnlock() + + index, err := openIdxFile(store.root, candidate.idxName, candidate.packName, store.algo) + if err != nil { + return nil, err + } + + store.stateMu.Lock() + if existing, ok := store.idxByPack[candidate.packName]; ok { + store.stateMu.Unlock() + _ = index.close() + return existing, nil + } + store.idxByPack[candidate.packName] = index + store.stateMu.Unlock() + return index, nil +} + +// openIdxFile maps and validates one idx v2 file. +func openIdxFile(root *os.Root, idxName, packName string, algo objectid.Algorithm) (*idxFile, error) { + file, err := root.Open(idxName) + if err != nil { + return nil, err + } + info, err := file.Stat() + if err != nil { + _ = file.Close() + return nil, err + } + size := info.Size() + if size < 0 || size > int64(int(^uint(0)>>1)) { + _ = file.Close() + return nil, fmt.Errorf("objectstore/packed: idx %q has unsupported size", idxName) + } + fd, err := intconv.UintptrToInt(file.Fd()) + if err != nil { + _ = file.Close() + return nil, err + } + data, err := syscall.Mmap(fd, 0, int(size), syscall.PROT_READ, syscall.MAP_PRIVATE) + if err != nil { + _ = file.Close() + return nil, err + } + + index := &idxFile{ + idxName: idxName, + packName: packName, + algo: algo, + file: file, + data: data, + } + if err := index.parse(); err != nil { + _ = index.close() + return nil, err + } + return index, nil +} + +// close unmaps and closes one idx handle. +func (index *idxFile) close() error { + var closeErr error + if index.data != nil { + if err := syscall.Munmap(index.data); err != nil && closeErr == nil { + closeErr = err + } + index.data = nil + } + if index.file != nil { + if err := index.file.Close(); err != nil && closeErr == nil { + closeErr = err + } + index.file = nil + } + return closeErr +} |
