aboutsummaryrefslogtreecommitdiff
path: root/objectstore
diff options
context:
space:
mode:
Diffstat (limited to 'objectstore')
-rw-r--r--objectstore/chain/chain.go18
-rw-r--r--objectstore/loose/read_size.go9
-rw-r--r--objectstore/objectstore.go5
-rw-r--r--objectstore/packed/read_size.go39
-rw-r--r--objectstore/packed/read_test.go17
5 files changed, 88 insertions, 0 deletions
diff --git a/objectstore/chain/chain.go b/objectstore/chain/chain.go
index 3d683c0d..53b9ac89 100644
--- a/objectstore/chain/chain.go
+++ b/objectstore/chain/chain.go
@@ -96,6 +96,24 @@ func (chain *Chain) ReadReaderContent(id objectid.ObjectID) (objecttype.Type, in
return objecttype.TypeInvalid, 0, nil, objectstore.ErrObjectNotFound
}
+// ReadSize reads object content length from the first backend that has it.
+func (chain *Chain) ReadSize(id objectid.ObjectID) (int64, error) {
+ for i, backend := range chain.backends {
+ if backend == nil {
+ continue
+ }
+ size, err := backend.ReadSize(id)
+ if err == nil {
+ return size, nil
+ }
+ if errors.Is(err, objectstore.ErrObjectNotFound) {
+ continue
+ }
+ return 0, fmt.Errorf("objectstore: backend %d read size: %w", i, err)
+ }
+ return 0, objectstore.ErrObjectNotFound
+}
+
// ReadHeader reads object header data from the first backend that has it.
func (chain *Chain) ReadHeader(id objectid.ObjectID) (objecttype.Type, int64, error) {
for i, backend := range chain.backends {
diff --git a/objectstore/loose/read_size.go b/objectstore/loose/read_size.go
new file mode 100644
index 00000000..45f1f0fe
--- /dev/null
+++ b/objectstore/loose/read_size.go
@@ -0,0 +1,9 @@
+package loose
+
+import "codeberg.org/lindenii/furgit/objectid"
+
+// ReadSize reads an object's declared content length.
+func (store *Store) ReadSize(id objectid.ObjectID) (int64, error) {
+ _, size, err := store.ReadHeader(id)
+ return size, err
+}
diff --git a/objectstore/objectstore.go b/objectstore/objectstore.go
index 053013ed..d52f88ed 100644
--- a/objectstore/objectstore.go
+++ b/objectstore/objectstore.go
@@ -30,6 +30,11 @@ type Store interface {
// and content stream.
// Caller must close the returned reader.
ReadReaderContent(id objectid.ObjectID) (objecttype.Type, int64, io.ReadCloser, error)
+ // ReadSize reads an object's declared content length.
+ //
+ // This is equivalent to ReadHeader(...).size and may be cheaper than
+ // ReadHeader when callers do not need object type.
+ ReadSize(id objectid.ObjectID) (int64, error)
// ReadHeader reads an object's type and declared content length.
ReadHeader(id objectid.ObjectID) (objecttype.Type, int64, error)
// Close releases resources associated with the backend.
diff --git a/objectstore/packed/read_size.go b/objectstore/packed/read_size.go
new file mode 100644
index 00000000..e162586a
--- /dev/null
+++ b/objectstore/packed/read_size.go
@@ -0,0 +1,39 @@
+package packed
+
+import (
+ "fmt"
+
+ packfmt "codeberg.org/lindenii/furgit/format/pack"
+ "codeberg.org/lindenii/furgit/objectid"
+ "codeberg.org/lindenii/furgit/objecttype"
+)
+
+// ReadSize reads an object's declared content size.
+func (store *Store) ReadSize(id objectid.ObjectID) (int64, error) {
+ loc, err := store.lookup(id)
+ if err != nil {
+ return 0, err
+ }
+ return store.resolveSizeAt(loc)
+}
+
+// resolveSizeAt resolves one object's declared content size from location.
+func (store *Store) resolveSizeAt(start location) (int64, error) {
+ pack, meta, err := store.entryMetaAt(start)
+ if err != nil {
+ return 0, err
+ }
+ if packfmt.IsBaseObjectType(meta.ty) {
+ return meta.size, nil
+ }
+ switch meta.ty {
+ case objecttype.TypeRefDelta, objecttype.TypeOfsDelta:
+ return deltaDeclaredSizeAt(pack, meta.dataOffset)
+ case objecttype.TypeInvalid, objecttype.TypeFuture:
+ return 0, fmt.Errorf("objectstore/packed: unsupported pack type %d", meta.ty)
+ case objecttype.TypeCommit, objecttype.TypeTree, objecttype.TypeBlob, objecttype.TypeTag:
+ return 0, fmt.Errorf("objectstore/packed: internal invariant violation for base type %d", meta.ty)
+ default:
+ return 0, fmt.Errorf("objectstore/packed: unsupported pack type %d", meta.ty)
+ }
+}
diff --git a/objectstore/packed/read_test.go b/objectstore/packed/read_test.go
index 8cee3b62..9bfa6610 100644
--- a/objectstore/packed/read_test.go
+++ b/objectstore/packed/read_test.go
@@ -36,6 +36,13 @@ func TestPackedStoreReadAgainstGit(t *testing.T) {
if gotHeaderSize != int64(len(wantBody)) {
t.Fatalf("ReadHeader size = %d, want %d", gotHeaderSize, len(wantBody))
}
+ gotSize, err := store.ReadSize(id)
+ if err != nil {
+ t.Fatalf("ReadSize: %v", err)
+ }
+ if gotSize != int64(len(wantBody)) {
+ t.Fatalf("ReadSize = %d, want %d", gotSize, len(wantBody))
+ }
gotRaw, err := store.ReadBytesFull(id)
if err != nil {
@@ -108,6 +115,9 @@ func TestPackedStoreErrors(t *testing.T) {
if _, _, err := store.ReadHeader(notFoundID); !errors.Is(err, objectstore.ErrObjectNotFound) {
t.Fatalf("ReadHeader not-found error = %v", err)
}
+ if _, err := store.ReadSize(notFoundID); !errors.Is(err, objectstore.ErrObjectNotFound) {
+ t.Fatalf("ReadSize not-found error = %v", err)
+ }
var otherAlgo objectid.Algorithm
for _, candidate := range objectid.SupportedAlgorithms() {
@@ -182,6 +192,13 @@ func TestPackedStoreReadHeaderUsesResolvedObjectSizeForDelta(t *testing.T) {
if gotSize != wantResolvedSize {
t.Fatalf("ReadHeader(%s) size = %d, want resolved size %d", deltaID, gotSize, wantResolvedSize)
}
+ gotReadSize, err := store.ReadSize(deltaID)
+ if err != nil {
+ t.Fatalf("ReadSize(%s): %v", deltaID, err)
+ }
+ if gotReadSize != wantResolvedSize {
+ t.Fatalf("ReadSize(%s) = %d, want resolved size %d", deltaID, gotReadSize, wantResolvedSize)
+ }
})
}