ref: 561c3b2cf5893430d4ff63883dab818d8d6f5c3f
dir: /format/pack/entry.go/
package pack
import (
"fmt"
"codeberg.org/lindenii/furgit/objecttype"
)
// EntryHeader is one parsed pack entry header prefix.
type EntryHeader struct {
// Type is the entry type tag from the first header byte.
Type objecttype.Type
// Size is the declared resulting object size.
Size int64
// HeaderSize is the number of bytes consumed by the type/size header.
HeaderSize int
}
// ParseEntryHeader parses one pack entry type/size header from data.
func ParseEntryHeader(data []byte) (EntryHeader, error) {
var zero EntryHeader
if len(data) == 0 {
return zero, fmt.Errorf("format/pack: truncated entry header")
}
first := data[0]
header := EntryHeader{
Type: objecttype.Type((first >> 4) & 0x07),
Size: int64(first & 0x0f),
HeaderSize: 1,
}
shift := uint(4)
b := first
for b&0x80 != 0 {
if header.HeaderSize >= len(data) {
return zero, fmt.Errorf("format/pack: truncated entry header")
}
b = data[header.HeaderSize]
header.HeaderSize++
header.Size |= int64(b&0x7f) << shift
shift += 7
}
if header.Size < 0 {
return zero, fmt.Errorf("format/pack: negative entry size")
}
return header, nil
}
// Entry is one parsed pack entry prefix, including any delta base reference
// data that appears before the compressed payload.
type Entry struct {
// Type is the pack entry type.
Type objecttype.Type
// Size is the declared resulting object size.
Size int64
// DataOffset is the byte offset from the start of the entry to the zlib
// payload bytes.
DataOffset int
// RefBaseID is the referenced base object ID bytes for ref-delta entries.
RefBaseID []byte
// OfsBaseDistance is the backward distance for ofs-delta entries.
OfsBaseDistance uint64
}
// ParseEntry parses one full pack entry prefix from data.
//
// hashSize must match the hash algorithm size used by the pack/index.
func ParseEntry(data []byte, hashSize int) (Entry, error) {
var zero Entry
header, err := ParseEntryHeader(data)
if err != nil {
return zero, err
}
entry := Entry{
Type: header.Type,
Size: header.Size,
DataOffset: header.HeaderSize,
}
switch entry.Type {
case objecttype.TypeCommit, objecttype.TypeTree, objecttype.TypeBlob, objecttype.TypeTag:
// Base object entries have no extra prefix fields.
case objecttype.TypeRefDelta:
if hashSize <= 0 {
return zero, fmt.Errorf("format/pack: invalid hash size %d", hashSize)
}
end := entry.DataOffset + hashSize
if end > len(data) {
return zero, fmt.Errorf("format/pack: truncated ref-delta base id")
}
entry.RefBaseID = data[entry.DataOffset:end]
entry.DataOffset = end
case objecttype.TypeOfsDelta:
dist, consumed, err := ParseOfsDeltaDistance(data[entry.DataOffset:])
if err != nil {
return zero, err
}
entry.OfsBaseDistance = dist
entry.DataOffset += consumed
case objecttype.TypeInvalid, objecttype.TypeFuture:
return zero, fmt.Errorf("format/pack: unsupported object type %d", entry.Type)
default:
return zero, fmt.Errorf("format/pack: unsupported object type %d", entry.Type)
}
if entry.DataOffset > len(data) {
return zero, fmt.Errorf("format/pack: entry data offset out of bounds")
}
return entry, nil
}