aboutsummaryrefslogtreecommitdiff
path: root/object/store/packed/read_reader.go
diff options
context:
space:
mode:
authorGravatar Runxi Yu2026-03-28 04:19:44 +0000
committerGravatar Runxi Yu2026-03-28 04:20:29 +0000
commit402ef2733813d128631ca4aea18c2908c74340d5 (patch)
treee03a90b6f41411bd62e7339390802c5c50082850 /object/store/packed/read_reader.go
parentobject/store: Rename from object/storer (diff)
signatureNo signature
object/store: Rename back from storer; rename Store to ReadingStore v0.1.118
Diffstat (limited to 'object/store/packed/read_reader.go')
-rw-r--r--object/store/packed/read_reader.go103
1 files changed, 103 insertions, 0 deletions
diff --git a/object/store/packed/read_reader.go b/object/store/packed/read_reader.go
new file mode 100644
index 00000000..0608e390
--- /dev/null
+++ b/object/store/packed/read_reader.go
@@ -0,0 +1,103 @@
+package packed
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+
+ packfmt "codeberg.org/lindenii/furgit/format/packfile"
+ "codeberg.org/lindenii/furgit/internal/iolimit"
+ objectheader "codeberg.org/lindenii/furgit/object/header"
+ objectid "codeberg.org/lindenii/furgit/object/id"
+ objecttype "codeberg.org/lindenii/furgit/object/type"
+)
+
+// ReadReaderContent reads an object's type, declared content size, and content
+// stream.
+//
+// The caller must close the returned reader.
+//
+// For base pack entries, the returned reader borrows store-owned mapped pack
+// data and is only valid until the store is closed.
+//
+// Close releases reader-local resources only. It does not drain unread data for
+// additional validation. In particular, malformed trailing compressed data,
+// trailing bytes past the declared object size, and the zlib Adler-32 trailer
+// may go unverified unless the caller reads to io.EOF.
+func (store *Store) ReadReaderContent(id objectid.ObjectID) (objecttype.Type, int64, io.ReadCloser, error) {
+ loc, err := store.lookup(id)
+ if err != nil {
+ return objecttype.TypeInvalid, 0, nil, err
+ }
+
+ pack, meta, err := store.entryMetaAt(loc)
+ if err != nil {
+ return objecttype.TypeInvalid, 0, nil, err
+ }
+
+ if packfmt.IsBaseObjectType(meta.ty) {
+ zr, err := zlibReaderAt(pack, meta.dataOffset)
+ if err != nil {
+ return objecttype.TypeInvalid, 0, nil, err
+ }
+
+ return meta.ty, meta.size, &readCloser{
+ reader: iolimit.ExpectLengthReader(zr, meta.size),
+ closer: zr,
+ }, nil
+ }
+
+ ty, content, err := store.deltaResolveContent(loc)
+ if err != nil {
+ return objecttype.TypeInvalid, 0, nil, err
+ }
+
+ return ty, int64(len(content)), io.NopCloser(bytes.NewReader(content)), nil
+}
+
+// ReadReaderFull reads a full serialized object stream as "type size\0content".
+//
+// The caller must close the returned reader.
+//
+// For base pack entries, the returned reader borrows store-owned mapped pack
+// data and is only valid until the store is closed.
+//
+// Close releases reader-local resources only. It does not drain unread data for
+// additional validation. In particular, malformed trailing compressed data,
+// trailing bytes past the declared object size, and the zlib Adler-32 trailer
+// may go unverified unless the caller reads to io.EOF.
+func (store *Store) ReadReaderFull(id objectid.ObjectID) (io.ReadCloser, error) {
+ loc, err := store.lookup(id)
+ if err != nil {
+ return nil, err
+ }
+
+ pack, meta, err := store.entryMetaAt(loc)
+ if err != nil {
+ return nil, err
+ }
+
+ if packfmt.IsBaseObjectType(meta.ty) {
+ header, ok := objectheader.Encode(meta.ty, meta.size)
+ if !ok {
+ return nil, fmt.Errorf("objectstore/packed: failed to encode object header for type %d", meta.ty)
+ }
+
+ zr, err := zlibReaderAt(pack, meta.dataOffset)
+ if err != nil {
+ return nil, err
+ }
+
+ return &readCloser{
+ reader: io.MultiReader(bytes.NewReader(header), iolimit.ExpectLengthReader(zr, meta.size)),
+ closer: zr,
+ }, nil
+ }
+
+ raw, err := store.ReadBytesFull(id)
+ if err != nil {
+ return nil, err
+ }
+
+ return io.NopCloser(bytes.NewReader(raw)), nil
+}