aboutsummaryrefslogtreecommitdiff
path: root/object/store/packed/entry.go
diff options
context:
space:
mode:
Diffstat (limited to 'object/store/packed/entry.go')
-rw-r--r--object/store/packed/entry.go73
1 files changed, 73 insertions, 0 deletions
diff --git a/object/store/packed/entry.go b/object/store/packed/entry.go
new file mode 100644
index 00000000..908afad0
--- /dev/null
+++ b/object/store/packed/entry.go
@@ -0,0 +1,73 @@
+package packed
+
+import (
+ "errors"
+ "fmt"
+ "io"
+
+ "lindenii.org/go/furgit/internal/compress/zlib"
+ "lindenii.org/go/furgit/internal/format/packfile"
+ "lindenii.org/go/furgit/object/id"
+ "lindenii.org/go/lgo/intconv"
+)
+
+var errPayloadOverlong = errors.New("entry payload longer than declared")
+
+// entryHeaderAt parses the entry header at offset,
+// returning it together with the entry's compressed payload view.
+//
+// The entry header only contains the inflated length,
+// so payload slice extends to the end of the pack;
+// the compressed data length is determined by the zlib stream end,
+// not the slice length.
+//
+// Labels: Life-Parent, Mut-No.
+func (pack *pack) entryHeaderAt(offset int, objectFormat id.ObjectFormat) (packfile.EntryHeader, []byte, error) {
+ var zero packfile.EntryHeader
+
+ pos := offset
+ if pos < 0 || pos >= len(pack.data) {
+ return zero, nil, fmt.Errorf("%w: pack %q: entry offset out of bounds", ErrMalformedPackedStore, pack.name)
+ }
+
+ header, err := packfile.ParseEntryHeader(pack.data[pos:], objectFormat.Size())
+ if err != nil {
+ return zero, nil, fmt.Errorf("%w: pack %q: %w", ErrMalformedPackedStore, pack.name, err)
+ }
+
+ return header, pack.data[pos+header.HeaderLen:], nil
+}
+
+// inflate decompresses one entry payload of expectedSize bytes,
+// rejecting payloads whose inflated size differs.
+//
+// Labels: Life-Independent.
+func inflate(payload []byte, expectedSize uint64) ([]byte, error) {
+ size, err := intconv.Uint64ToInt(expectedSize)
+ if err != nil {
+ return nil, fmt.Errorf("declared size: %w", err)
+ }
+
+ zr, err := zlib.NewReaderBytes(payload)
+ if err != nil {
+ return nil, fmt.Errorf("inflating entry payload: %w", err)
+ }
+
+ defer func() { _ = zr.Close() }()
+
+ out := make([]byte, size)
+
+ _, err = io.ReadFull(zr, out)
+ if err != nil {
+ return nil, fmt.Errorf("inflating entry payload: %w", err)
+ }
+
+ var probe [1]byte
+
+ n, err := zr.Read(probe[:])
+ if n != 0 || !errors.Is(err, io.EOF) {
+ return nil, errPayloadOverlong
+ }
+
+ return out, nil
+}