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 }