diff options
| -rw-r--r-- | objectstore/packed/idx_candidates_mru.go | 73 | ||||
| -rw-r--r-- | objectstore/packed/idx_lookup.go | 91 | ||||
| -rw-r--r-- | objectstore/packed/idx_lookup_candidates.go | 72 | ||||
| -rw-r--r-- | objectstore/packed/idx_parse.go | 85 |
4 files changed, 164 insertions, 157 deletions
diff --git a/objectstore/packed/idx_candidates_mru.go b/objectstore/packed/idx_candidates_mru.go new file mode 100644 index 00000000..593e84f4 --- /dev/null +++ b/objectstore/packed/idx_candidates_mru.go @@ -0,0 +1,73 @@ +package packed + +// packCandidateNode is one node in the candidate MRU order list. +type packCandidateNode struct { + candidate packCandidate + prev *packCandidateNode + next *packCandidateNode +} + +// touchCandidate moves one candidate to the front of the lookup order. +// This is done on a best-effort basis. +func (store *Store) touchCandidate(packName string) { + if !store.candidatesMu.TryLock() { + return + } + defer store.candidatesMu.Unlock() + + node := store.candidateNodeByPack[packName] + if node == nil || node == store.candidateHead { + return + } + + if node.prev != nil { + node.prev.next = node.next + } + + if node.next != nil { + node.next.prev = node.prev + } + + if store.candidateTail == node { + store.candidateTail = node.prev + } + + node.prev = nil + + node.next = store.candidateHead + if store.candidateHead != nil { + store.candidateHead.prev = node + } + + store.candidateHead = node + if store.candidateTail == nil { + store.candidateTail = node + } +} + +// firstCandidatePackName returns the current head pack name, or "" when none +// are available. +func (store *Store) firstCandidatePackName() string { + store.candidatesMu.RLock() + defer store.candidatesMu.RUnlock() + + if store.candidateHead == nil { + return "" + } + + return store.candidateHead.candidate.packName +} + +// nextCandidatePackName returns the pack name after currentPack in current MRU +// order, or "" at end / when currentPack is not present. +func (store *Store) nextCandidatePackName(currentPack string) string { + store.candidatesMu.RLock() + defer store.candidatesMu.RUnlock() + + node := store.candidateNodeByPack[currentPack] + if node == nil || node.next == nil { + return "" + } + + return node.next.candidate.packName +} diff --git a/objectstore/packed/idx_lookup.go b/objectstore/packed/idx_lookup.go new file mode 100644 index 00000000..a97c5e79 --- /dev/null +++ b/objectstore/packed/idx_lookup.go @@ -0,0 +1,91 @@ +package packed + +import ( + "bytes" + "encoding/binary" + "fmt" + + "codeberg.org/lindenii/furgit/objectid" +) + +// lookup resolves one object ID to its pack offset within this index. +func (index *idxFile) lookup(id objectid.ObjectID) (uint64, bool, error) { + if id.Algorithm() != index.algo { + return 0, false, fmt.Errorf("objectstore/packed: object id algorithm mismatch") + } + + idBytes := (&id).RawBytes() + + hashSize := len(idBytes) + if hashSize != index.algo.Size() { + return 0, false, fmt.Errorf("objectstore/packed: unexpected object id length") + } + + first := int(idBytes[0]) + + lo := 0 + if first > 0 { + lo = int(index.fanout[first-1]) + } + + hi := int(index.fanout[first]) + if lo < 0 || hi < 0 || lo > hi || hi > index.numObjects { + return 0, false, fmt.Errorf("objectstore/packed: idx %q has invalid fanout bounds", index.idxName) + } + + for lo < hi { + mid := lo + (hi-lo)/2 + + nameOffset := index.namesOffset + mid*hashSize + if nameOffset < 0 || nameOffset+hashSize > len(index.data) { + return 0, false, fmt.Errorf("objectstore/packed: idx %q truncated name table", index.idxName) + } + + cmp := bytes.Compare(index.data[nameOffset:nameOffset+hashSize], idBytes) + if cmp == 0 { + offset, err := index.offsetAt(mid) + if err != nil { + return 0, false, err + } + + return offset, true, nil + } + + if cmp < 0 { + lo = mid + 1 + } else { + hi = mid + } + } + + return 0, false, nil +} + +// offsetAt resolves the pack offset for one object index entry. +func (index *idxFile) offsetAt(objectIndex int) (uint64, error) { + if objectIndex < 0 || objectIndex >= index.numObjects { + return 0, fmt.Errorf("objectstore/packed: idx %q offset index out of bounds", index.idxName) + } + + wordOffset := index.offset32Offset + objectIndex*4 + if wordOffset < 0 || wordOffset+4 > len(index.data) { + return 0, fmt.Errorf("objectstore/packed: idx %q truncated 32-bit offset table", index.idxName) + } + + word := binary.BigEndian.Uint32(index.data[wordOffset : wordOffset+4]) + if word&0x80000000 == 0 { + return uint64(word), nil + } + + pos := int(word & 0x7fffffff) + if pos < 0 || pos >= index.offset64Count { + return 0, fmt.Errorf("objectstore/packed: idx %q invalid 64-bit offset position", index.idxName) + } + + offOffset := index.offset64Offset + pos*8 + if offOffset < 0 || offOffset+8 > len(index.data)-2*index.algo.Size() { + return 0, fmt.Errorf("objectstore/packed: idx %q truncated 64-bit offset table", index.idxName) + } + + return binary.BigEndian.Uint64(index.data[offOffset : offOffset+8]), nil +} diff --git a/objectstore/packed/idx_lookup_candidates.go b/objectstore/packed/idx_lookup_candidates.go index 72121b25..508da2ef 100644 --- a/objectstore/packed/idx_lookup_candidates.go +++ b/objectstore/packed/idx_lookup_candidates.go @@ -23,13 +23,6 @@ type packCandidate struct { mtime int64 } -// packCandidateNode is one node in the candidate MRU order list. -type packCandidateNode struct { - candidate packCandidate - prev *packCandidateNode - next *packCandidateNode -} - // ensureCandidates discovers pack/index pairs once. func (store *Store) ensureCandidates() error { store.discoverOnce.Do(func() { @@ -134,68 +127,3 @@ func (store *Store) discoverCandidates() ([]packCandidate, error) { return candidates, nil } - -// touchCandidate moves one candidate to the front of the lookup order. -// This is done on a best-effort basis. -func (store *Store) touchCandidate(packName string) { - if !store.candidatesMu.TryLock() { - return - } - defer store.candidatesMu.Unlock() - - node := store.candidateNodeByPack[packName] - if node == nil || node == store.candidateHead { - return - } - - if node.prev != nil { - node.prev.next = node.next - } - - if node.next != nil { - node.next.prev = node.prev - } - - if store.candidateTail == node { - store.candidateTail = node.prev - } - - node.prev = nil - - node.next = store.candidateHead - if store.candidateHead != nil { - store.candidateHead.prev = node - } - - store.candidateHead = node - if store.candidateTail == nil { - store.candidateTail = node - } -} - -// firstCandidatePackName returns the current head pack name, or "" when none -// are available. -func (store *Store) firstCandidatePackName() string { - store.candidatesMu.RLock() - defer store.candidatesMu.RUnlock() - - if store.candidateHead == nil { - return "" - } - - return store.candidateHead.candidate.packName -} - -// nextCandidatePackName returns the pack name after currentPack in current MRU -// order, or "" at end / when currentPack is not present. -func (store *Store) nextCandidatePackName(currentPack string) string { - store.candidatesMu.RLock() - defer store.candidatesMu.RUnlock() - - node := store.candidateNodeByPack[currentPack] - if node == nil || node.next == nil { - return "" - } - - return node.next.candidate.packName -} diff --git a/objectstore/packed/idx_parse.go b/objectstore/packed/idx_parse.go index 870ffdae..4da3bf42 100644 --- a/objectstore/packed/idx_parse.go +++ b/objectstore/packed/idx_parse.go @@ -1,11 +1,8 @@ package packed import ( - "bytes" "encoding/binary" "fmt" - - "codeberg.org/lindenii/furgit/objectid" ) const ( @@ -79,85 +76,3 @@ func (index *idxFile) parse() error { return nil } - -// lookup resolves one object ID to its pack offset within this index. -func (index *idxFile) lookup(id objectid.ObjectID) (uint64, bool, error) { - if id.Algorithm() != index.algo { - return 0, false, fmt.Errorf("objectstore/packed: object id algorithm mismatch") - } - - idBytes := (&id).RawBytes() - - hashSize := len(idBytes) - if hashSize != index.algo.Size() { - return 0, false, fmt.Errorf("objectstore/packed: unexpected object id length") - } - - first := int(idBytes[0]) - - lo := 0 - if first > 0 { - lo = int(index.fanout[first-1]) - } - - hi := int(index.fanout[first]) - if lo < 0 || hi < 0 || lo > hi || hi > index.numObjects { - return 0, false, fmt.Errorf("objectstore/packed: idx %q has invalid fanout bounds", index.idxName) - } - - for lo < hi { - mid := lo + (hi-lo)/2 - - nameOffset := index.namesOffset + mid*hashSize - if nameOffset < 0 || nameOffset+hashSize > len(index.data) { - return 0, false, fmt.Errorf("objectstore/packed: idx %q truncated name table", index.idxName) - } - - cmp := bytes.Compare(index.data[nameOffset:nameOffset+hashSize], idBytes) - if cmp == 0 { - offset, err := index.offsetAt(mid) - if err != nil { - return 0, false, err - } - - return offset, true, nil - } - - if cmp < 0 { - lo = mid + 1 - } else { - hi = mid - } - } - - return 0, false, nil -} - -// offsetAt resolves the pack offset for one object index entry. -func (index *idxFile) offsetAt(objectIndex int) (uint64, error) { - if objectIndex < 0 || objectIndex >= index.numObjects { - return 0, fmt.Errorf("objectstore/packed: idx %q offset index out of bounds", index.idxName) - } - - wordOffset := index.offset32Offset + objectIndex*4 - if wordOffset < 0 || wordOffset+4 > len(index.data) { - return 0, fmt.Errorf("objectstore/packed: idx %q truncated 32-bit offset table", index.idxName) - } - - word := binary.BigEndian.Uint32(index.data[wordOffset : wordOffset+4]) - if word&0x80000000 == 0 { - return uint64(word), nil - } - - pos := int(word & 0x7fffffff) - if pos < 0 || pos >= index.offset64Count { - return 0, fmt.Errorf("objectstore/packed: idx %q invalid 64-bit offset position", index.idxName) - } - - offOffset := index.offset64Offset + pos*8 - if offOffset < 0 || offOffset+8 > len(index.data)-2*index.algo.Size() { - return 0, fmt.Errorf("objectstore/packed: idx %q truncated 64-bit offset table", index.idxName) - } - - return binary.BigEndian.Uint64(index.data[offOffset : offOffset+8]), nil -} |
