aboutsummaryrefslogtreecommitdiff
path: root/objectstore
diff options
context:
space:
mode:
authorGravatar Runxi Yu2026-03-04 12:53:05 +0800
committerGravatar Runxi Yu2026-03-04 12:53:05 +0800
commit4668c6c07b32a3c185fdd7bfc585fb978f535a4a (patch)
treefe5f845106c98e79f51494a878d4b94ced217766 /objectstore
parentobjectstore/loose: Split (diff)
signatureNo signature
objectstore/packed: Split
Diffstat (limited to 'objectstore')
-rw-r--r--objectstore/packed/idx_candidates_mru.go73
-rw-r--r--objectstore/packed/idx_lookup.go91
-rw-r--r--objectstore/packed/idx_lookup_candidates.go72
-rw-r--r--objectstore/packed/idx_parse.go85
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
-}