aboutsummaryrefslogtreecommitdiffstats
path: root/packfile
diff options
context:
space:
mode:
authorMáximo Cuadros <mcuadros@gmail.com>2015-10-23 17:45:01 +0200
committerMáximo Cuadros <mcuadros@gmail.com>2015-10-23 17:45:01 +0200
commitd82f291cde9987322c8a0c81a325e1ba6159684c (patch)
treed423447ee374fbfa802f7ff354651fd34afe0fb2 /packfile
parent6c629843a1750a27c9af01ed2985f362f619c47a (diff)
parent27aa8cdd2431068606741a589383c02c149ea625 (diff)
downloadgo-git-d82f291cde9987322c8a0c81a325e1ba6159684c.tar.gz
Merge pull request #2 from mcuadros/v2.0.0
formats/packfile: cleanup and hash type
Diffstat (limited to 'packfile')
-rw-r--r--packfile/delta.go93
-rw-r--r--packfile/doc.go168
-rw-r--r--packfile/objects.go200
-rw-r--r--packfile/objects_test.go50
-rw-r--r--packfile/packfile.go83
-rw-r--r--packfile/reader.go402
-rw-r--r--packfile/reader_test.go35
7 files changed, 0 insertions, 1031 deletions
diff --git a/packfile/delta.go b/packfile/delta.go
deleted file mode 100644
index 86b556f..0000000
--- a/packfile/delta.go
+++ /dev/null
@@ -1,93 +0,0 @@
-package packfile
-
-const delta_size_min = 4
-
-func deltaHeaderSize(b []byte) (uint, []byte) {
- var size, j uint
- var cmd byte
- for {
- cmd = b[j]
- size |= (uint(cmd) & 0x7f) << (j * 7)
- j++
- if uint(cmd)&0xb80 == 0 || j == uint(len(b)) {
- break
- }
- }
- return size, b[j:]
-}
-
-func PatchDelta(src, delta []byte) []byte {
- if len(delta) < delta_size_min {
- return nil
- }
- size, delta := deltaHeaderSize(delta)
- if size != uint(len(src)) {
- return nil
- }
- size, delta = deltaHeaderSize(delta)
- origSize := size
-
- dest := make([]byte, 0)
-
- // var offset uint
- var cmd byte
- for {
- cmd = delta[0]
- delta = delta[1:]
- if (cmd & 0x80) != 0 {
- var cp_off, cp_size uint
- if (cmd & 0x01) != 0 {
- cp_off = uint(delta[0])
- delta = delta[1:]
- }
- if (cmd & 0x02) != 0 {
- cp_off |= uint(delta[0]) << 8
- delta = delta[1:]
- }
- if (cmd & 0x04) != 0 {
- cp_off |= uint(delta[0]) << 16
- delta = delta[1:]
- }
- if (cmd & 0x08) != 0 {
- cp_off |= uint(delta[0]) << 24
- delta = delta[1:]
- }
-
- if (cmd & 0x10) != 0 {
- cp_size = uint(delta[0])
- delta = delta[1:]
- }
- if (cmd & 0x20) != 0 {
- cp_size |= uint(delta[0]) << 8
- delta = delta[1:]
- }
- if (cmd & 0x40) != 0 {
- cp_size |= uint(delta[0]) << 16
- delta = delta[1:]
- }
- if cp_size == 0 {
- cp_size = 0x10000
- }
- if cp_off+cp_size < cp_off ||
- cp_off+cp_size > uint(len(src)) ||
- cp_size > origSize {
- break
- }
- dest = append(dest, src[cp_off:cp_off+cp_size]...)
- size -= cp_size
- } else if cmd != 0 {
- if uint(cmd) > origSize {
- break
- }
- dest = append(dest, delta[0:uint(cmd)]...)
- size -= uint(cmd)
- delta = delta[uint(cmd):]
- } else {
- return nil
- }
- if size <= 0 {
- break
- }
- }
- return dest
-}
diff --git a/packfile/doc.go b/packfile/doc.go
deleted file mode 100644
index 1fc28da..0000000
--- a/packfile/doc.go
+++ /dev/null
@@ -1,168 +0,0 @@
-package packfile
-
-// Code from:
-// https://github.com/gitchain/gitchain/tree/master/git @ 4c2fabdf9
-//
-// GIT pack format
-// ===============
-//
-// == pack-*.pack files have the following format:
-//
-// - A header appears at the beginning and consists of the following:
-//
-// 4-byte signature:
-// The signature is: {'P', 'A', 'C', 'K'}
-//
-// 4-byte version number (network byte order):
-// GIT currently accepts version number 2 or 3 but
-// generates version 2 only.
-//
-// 4-byte number of objects contained in the pack (network byte order)
-//
-// Observation: we cannot have more than 4G versions ;-) and
-// more than 4G objects in a pack.
-//
-// - The header is followed by number of object entries, each of
-// which looks like this:
-//
-// (undeltified representation)
-// n-byte type and length (3-bit type, (n-1)*7+4-bit length)
-// compressed data
-//
-// (deltified representation)
-// n-byte type and length (3-bit type, (n-1)*7+4-bit length)
-// 20-byte base object name
-// compressed delta data
-//
-// Observation: length of each object is encoded in a variable
-// length format and is not constrained to 32-bit or anything.
-//
-// - The trailer records 20-byte SHA1 checksum of all of the above.
-//
-// == Original (version 1) pack-*.idx files have the following format:
-//
-// - The header consists of 256 4-byte network byte order
-// integers. N-th entry of this table records the number of
-// objects in the corresponding pack, the first byte of whose
-// object name is less than or equal to N. This is called the
-// 'first-level fan-out' table.
-//
-// - The header is followed by sorted 24-byte entries, one entry
-// per object in the pack. Each entry is:
-//
-// 4-byte network byte order integer, recording where the
-// object is stored in the packfile as the offset from the
-// beginning.
-//
-// 20-byte object name.
-//
-// - The file is concluded with a trailer:
-//
-// A copy of the 20-byte SHA1 checksum at the end of
-// corresponding packfile.
-//
-// 20-byte SHA1-checksum of all of the above.
-//
-// Pack Idx file:
-//
-// -- +--------------------------------+
-// fanout | fanout[0] = 2 (for example) |-.
-// table +--------------------------------+ |
-// | fanout[1] | |
-// +--------------------------------+ |
-// | fanout[2] | |
-// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
-// | fanout[255] = total objects |---.
-// -- +--------------------------------+ | |
-// main | offset | | |
-// index | object name 00XXXXXXXXXXXXXXXX | | |
-// table +--------------------------------+ | |
-// | offset | | |
-// | object name 00XXXXXXXXXXXXXXXX | | |
-// +--------------------------------+<+ |
-// .-| offset | |
-// | | object name 01XXXXXXXXXXXXXXXX | |
-// | +--------------------------------+ |
-// | | offset | |
-// | | object name 01XXXXXXXXXXXXXXXX | |
-// | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
-// | | offset | |
-// | | object name FFXXXXXXXXXXXXXXXX | |
-// --| +--------------------------------+<--+
-// trailer | | packfile checksum |
-// | +--------------------------------+
-// | | idxfile checksum |
-// | +--------------------------------+
-// .-------.
-// |
-// Pack file entry: <+
-//
-// packed object header:
-// 1-byte size extension bit (MSB)
-// type (next 3 bit)
-// size0 (lower 4-bit)
-// n-byte sizeN (as long as MSB is set, each 7-bit)
-// size0..sizeN form 4+7+7+..+7 bit integer, size0
-// is the least significant part, and sizeN is the
-// most significant part.
-// packed object data:
-// If it is not DELTA, then deflated bytes (the size above
-// is the size before compression).
-// If it is REF_DELTA, then
-// 20-byte base object name SHA1 (the size above is the
-// size of the delta data that follows).
-// delta data, deflated.
-// If it is OFS_DELTA, then
-// n-byte offset (see below) interpreted as a negative
-// offset from the type-byte of the header of the
-// ofs-delta entry (the size above is the size of
-// the delta data that follows).
-// delta data, deflated.
-//
-// offset encoding:
-// n bytes with MSB set in all but the last one.
-// The offset is then the number constructed by
-// concatenating the lower 7 bit of each byte, and
-// for n >= 2 adding 2^7 + 2^14 + ... + 2^(7*(n-1))
-// to the result.
-//
-//
-//
-// == Version 2 pack-*.idx files support packs larger than 4 GiB, and
-// have some other reorganizations. They have the format:
-//
-// - A 4-byte magic number '\377tOc' which is an unreasonable
-// fanout[0] value.
-//
-// - A 4-byte version number (= 2)
-//
-// - A 256-entry fan-out table just like v1.
-//
-// - A table of sorted 20-byte SHA1 object names. These are
-// packed together without offset values to reduce the cache
-// footprint of the binary search for a specific object name.
-//
-// - A table of 4-byte CRC32 values of the packed object data.
-// This is new in v2 so compressed data can be copied directly
-// from pack to pack during repacking without undetected
-// data corruption.
-//
-// - A table of 4-byte offset values (in network byte order).
-// These are usually 31-bit pack file offsets, but large
-// offsets are encoded as an index into the next table with
-// the msbit set.
-//
-// - A table of 8-byte offset entries (empty for pack files less
-// than 2 GiB). Pack files are organized with heavily used
-// objects toward the front, so most object references should
-// not need to refer to this table.
-//
-// - The same trailer as a v1 pack file:
-//
-// A copy of the 20-byte SHA1 checksum at the end of
-// corresponding packfile.
-//
-// 20-byte SHA1-checksum of all of the above.
-//
-// From:
-// https://www.kernel.org/pub/software/scm/git/docs/v1.7.5/technical/pack-protocol.txt
diff --git a/packfile/objects.go b/packfile/objects.go
deleted file mode 100644
index 6449808..0000000
--- a/packfile/objects.go
+++ /dev/null
@@ -1,200 +0,0 @@
-package packfile
-
-import (
- "bytes"
- "encoding/hex"
- "fmt"
- "strconv"
- "time"
-
- "gopkg.in/src-d/go-git.v2/commons"
-)
-
-type Object interface {
- Type() string
- Hash() string
-}
-
-type Hash []byte
-
-func (h Hash) String() string {
- return hex.EncodeToString(h)
-}
-
-type Commit struct {
- Tree Hash
- Parents []Hash
- Author Signature
- Committer Signature
- Message string
- hash string
-}
-
-func NewCommit(b []byte) (*Commit, error) {
- o := &Commit{hash: commons.GitHash("commit", b)}
-
- lines := bytes.Split(b, []byte{'\n'})
- for i := range lines {
- if len(lines[i]) > 0 {
- var err error
-
- split := bytes.SplitN(lines[i], []byte{' '}, 2)
- switch string(split[0]) {
- case "tree":
- o.Tree = make([]byte, 20)
- _, err = hex.Decode(o.Tree, split[1])
- case "parent":
- h := make([]byte, 20)
- _, err = hex.Decode(h, split[1])
- if err == nil {
- o.Parents = append(o.Parents, h)
- }
- case "author":
- o.Author = NewSignature(split[1])
- case "committer":
- o.Committer = NewSignature(split[1])
- }
-
- if err != nil {
- return nil, err
- }
- } else {
- o.Message = string(bytes.Join(append(lines[i+1:]), []byte{'\n'}))
- break
- }
- }
-
- return o, nil
-}
-
-func (o *Commit) Type() string {
- return "commit"
-}
-
-func (o *Commit) Hash() string {
- return o.hash
-}
-
-type Signature struct {
- Name string
- Email string
- When time.Time
-}
-
-func NewSignature(signature []byte) Signature {
- ret := Signature{}
- if len(signature) == 0 {
- return ret
- }
-
- from := 0
- state := 'n' // n: name, e: email, t: timestamp, z: timezone
- for i := 0; ; i++ {
- var c byte
- var end bool
- if i < len(signature) {
- c = signature[i]
- } else {
- end = true
- }
-
- switch state {
- case 'n':
- if c == '<' || end {
- if i == 0 {
- break
- }
- ret.Name = string(signature[from : i-1])
- state = 'e'
- from = i + 1
- }
- case 'e':
- if c == '>' || end {
- ret.Email = string(signature[from:i])
- i++
- state = 't'
- from = i + 1
- }
- case 't':
- if c == ' ' || end {
- t, err := strconv.ParseInt(string(signature[from:i]), 10, 64)
- if err == nil {
- ret.When = time.Unix(t, 0)
- }
- end = true
- }
- }
-
- if end {
- break
- }
- }
-
- return ret
-}
-
-func (s *Signature) String() string {
- return fmt.Sprintf("%q <%s> @ %s", s.Name, s.Email, s.When)
-}
-
-type Tree struct {
- Entries []TreeEntry
- hash string
-}
-
-type TreeEntry struct {
- Name string
- Hash string
-}
-
-func NewTree(body []byte) (*Tree, error) {
- o := &Tree{hash: commons.GitHash("tree", body)}
-
- if len(body) == 0 {
- return o, nil
- }
-
- for {
- split := bytes.SplitN(body, []byte{0}, 2)
- split1 := bytes.SplitN(split[0], []byte{' '}, 2)
-
- o.Entries = append(o.Entries, TreeEntry{
- Name: string(split1[1]),
- Hash: fmt.Sprintf("%x", split[1][0:20]),
- })
-
- body = split[1][20:]
- if len(split[1]) == 20 {
- break
- }
- }
-
- return o, nil
-}
-
-func (o *Tree) Type() string {
- return "tree"
-}
-
-func (o *Tree) Hash() string {
- return o.hash
-}
-
-type Blob struct {
- Len int
- hash string
-}
-
-func NewBlob(b []byte) (*Blob, error) {
- return &Blob{Len: len(b), hash: commons.GitHash("blob", b)}, nil
-}
-
-func (o *Blob) Type() string {
- return "blob"
-}
-
-func (o *Blob) Hash() string {
- return o.hash
-}
-
-type ContentCallback func(hash string, content []byte)
diff --git a/packfile/objects_test.go b/packfile/objects_test.go
deleted file mode 100644
index 93c348b..0000000
--- a/packfile/objects_test.go
+++ /dev/null
@@ -1,50 +0,0 @@
-package packfile
-
-import (
- "testing"
- "time"
-
- "github.com/stretchr/testify/assert"
-)
-
-func TestSignature(t *testing.T) {
- cases := map[string]Signature{
- `Foo Bar <foo@bar.com> 1257894000 +0100`: {
- Name: "Foo Bar",
- Email: "foo@bar.com",
- When: time.Unix(1257894000, 0),
- },
- `Foo Bar <> 1257894000 +0100`: {
- Name: "Foo Bar",
- Email: "",
- When: time.Unix(1257894000, 0),
- },
- ` <> 1257894000`: {
- Name: "",
- Email: "",
- When: time.Unix(1257894000, 0),
- },
- `Foo Bar <foo@bar.com>`: {
- Name: "Foo Bar",
- Email: "foo@bar.com",
- When: time.Time{},
- },
- ``: {
- Name: "",
- Email: "",
- When: time.Time{},
- },
- `<`: {
- Name: "",
- Email: "",
- When: time.Time{},
- },
- }
-
- for raw, exp := range cases {
- got := NewSignature([]byte(raw))
- assert.Equal(t, exp.Name, got.Name)
- assert.Equal(t, exp.Email, got.Email)
- assert.Equal(t, exp.When.Unix(), got.When.Unix())
- }
-}
diff --git a/packfile/packfile.go b/packfile/packfile.go
deleted file mode 100644
index 11ef969..0000000
--- a/packfile/packfile.go
+++ /dev/null
@@ -1,83 +0,0 @@
-package packfile
-
-import "fmt"
-
-type Packfile struct {
- Version uint32
- Size int64
- ObjectCount int
- Checksum []byte
- Commits map[string]*Commit
- Trees map[string]*Tree
- Blobs map[string]*Blob
-}
-
-func NewPackfile() *Packfile {
- return &Packfile{
- Commits: make(map[string]*Commit, 0),
- Trees: make(map[string]*Tree, 0),
- Blobs: make(map[string]*Blob, 0),
- }
-}
-
-type BlobEntry struct {
- path string
- *Blob
-}
-
-type SubtreeEntry struct {
- path string
- *Tree
- TreeCh
-}
-
-type treeEntry interface {
- isTreeEntry()
- Path() string
-}
-
-func (b BlobEntry) isTreeEntry() {}
-func (b BlobEntry) Path() string { return b.path }
-func (b SubtreeEntry) isTreeEntry() {}
-func (b SubtreeEntry) Path() string { return b.path }
-
-type TreeCh <-chan treeEntry
-
-func (p *Packfile) WalkCommit(commitHash string) (TreeCh, error) {
- commit, ok := p.Commits[commitHash]
- if !ok {
- return nil, fmt.Errorf("Unable to find %q commit", commitHash)
- }
-
- treeHash := fmt.Sprintf("%x", string(commit.Tree))
- return p.WalkTree(p.Trees[treeHash]), nil
-}
-
-func (p *Packfile) WalkTree(tree *Tree) TreeCh {
- return p.walkTree(tree, "")
-}
-
-func (p *Packfile) walkTree(tree *Tree, pathPrefix string) TreeCh {
- ch := make(chan treeEntry)
-
- if tree == nil {
- close(ch)
- return ch
- }
-
- go func() {
- defer func() {
- close(ch)
- }()
- for _, e := range tree.Entries {
- path := pathPrefix + e.Name
- if blob, ok := p.Blobs[e.Hash]; ok {
- ch <- BlobEntry{path, blob}
- } else if subtree, ok := p.Trees[e.Hash]; ok {
- ch <- SubtreeEntry{path, subtree, p.walkTree(subtree, path+"/")}
- }
- }
- }()
-
- return ch
-}
diff --git a/packfile/reader.go b/packfile/reader.go
deleted file mode 100644
index e761654..0000000
--- a/packfile/reader.go
+++ /dev/null
@@ -1,402 +0,0 @@
-package packfile
-
-import (
- "bytes"
- "encoding/binary"
- "fmt"
- "io"
-
- "github.com/klauspost/compress/zlib"
-)
-
-const MaxObjectsLimit = 1000000
-
-var ErrMaxSize = fmt.Errorf("Max size exceeded for in-memory client")
-
-type TrackingByteReader struct {
- r io.Reader
- n, l int
-}
-
-func (t *TrackingByteReader) Pos() int { return t.n }
-
-func (t *TrackingByteReader) Read(p []byte) (n int, err error) {
- n, err = t.r.Read(p)
- if err != nil {
- return 0, err
- }
- t.n += n
- if t.n >= t.l {
- return n, ErrMaxSize
- }
- return n, err
-}
-
-func (t *TrackingByteReader) ReadByte() (c byte, err error) {
- var p [1]byte
- n, err := t.r.Read(p[:])
- if err != nil {
- return 0, err
- }
- if n > 1 {
- return 0, fmt.Errorf("read %d bytes, should have read just 1", n)
- }
- t.n += n // n is 1
- return p[0], nil
-}
-
-type PackfileReader struct {
- r *TrackingByteReader
-
- objects map[string]packfileObject
- offsets map[int]string
- deltas []packfileDelta
-
- contentCallback ContentCallback
-}
-
-type packfileObject struct {
- bytes []byte
- typ int8
-}
-
-type packfileDelta struct {
- hash string
- delta []byte
-}
-
-func NewPackfileReader(r io.Reader, l int, fn ContentCallback) (*PackfileReader, error) {
- return &PackfileReader{
- r: &TrackingByteReader{r: r, n: 0, l: l},
- objects: make(map[string]packfileObject, 0),
- offsets: make(map[int]string, 0),
- contentCallback: fn,
- }, nil
-}
-
-func (pr *PackfileReader) Pos() int { return pr.r.Pos() }
-
-func (pr *PackfileReader) Read() (*Packfile, error) {
- packfile := NewPackfile()
-
- if err := pr.validateHeader(); err != nil {
- if err == io.EOF {
- // This is an empty repo. It's OK.
- return packfile, nil
- }
- return nil, err
- }
-
- ver, err := pr.readInt32()
- if err != nil {
- return nil, err
- }
-
- count, err := pr.readInt32()
- if err != nil {
- return nil, err
- }
-
- packfile.Version = uint32(ver)
- packfile.ObjectCount = int(count)
-
- if packfile.ObjectCount > MaxObjectsLimit {
- return nil, NewError("too many objects (%d)", packfile.ObjectCount)
- }
-
- if err := pr.readObjects(packfile); err != nil {
- return nil, err
- }
-
- packfile.Size = int64(pr.r.Pos())
-
- return packfile, nil
-}
-
-func (pr *PackfileReader) validateHeader() error {
- var header = make([]byte, 4)
- if _, err := pr.r.Read(header); err != nil {
- return err
- }
-
- if !bytes.Equal(header, []byte{'P', 'A', 'C', 'K'}) {
- return NewError("Pack file does not start with 'PACK'")
- }
-
- return nil
-}
-
-func (pr *PackfileReader) readInt32() (uint32, error) {
- var value uint32
- if err := binary.Read(pr.r, binary.BigEndian, &value); err != nil {
- return 0, err
- }
-
- return value, nil
-}
-
-func (pr *PackfileReader) readObjects(packfile *Packfile) error {
- // This code has 50-80 µs of overhead per object not counting zlib inflation.
- // Together with zlib inflation, it's 400-410 µs for small objects.
- // That's 1 sec for ~2450 objects, ~4.20 MB, or ~250 ms per MB,
- // of which 12-20 % is _not_ zlib inflation (ie. is our code).
-
- for i := 0; i < packfile.ObjectCount; i++ {
- var pos = pr.Pos()
- obj, err := pr.readObject(packfile)
- if err != nil && err != io.EOF {
- return err
- }
-
- pr.offsets[pos] = obj.hash
-
- if err == io.EOF {
- break
- }
- }
-
- return nil
-}
-
-func (pr *PackfileReader) readObject(packfile *Packfile) (*objectReader, error) {
- o, err := newObjectReader(pr, packfile)
- if err != nil {
- return nil, err
- }
-
- switch o.typ {
- case OBJ_REF_DELTA:
- err = o.readREFDelta()
- case OBJ_OFS_DELTA:
- err = o.readOFSDelta()
- case OBJ_COMMIT, OBJ_TREE, OBJ_BLOB, OBJ_TAG:
- err = o.readObject()
- default:
- err = NewError("Invalid git object tag %q", o.typ)
- }
-
- if err != nil {
- return nil, err
- }
-
- return o, err
-}
-
-const (
- OBJ_COMMIT = 1
- OBJ_TREE = 2
- OBJ_BLOB = 3
- OBJ_TAG = 4
- OBJ_OFS_DELTA = 6
- OBJ_REF_DELTA = 7
-)
-
-const SIZE_LIMIT uint64 = 1 << 32 // 4GB
-
-type objectReader struct {
- pr *PackfileReader
- pf *Packfile
- hash string
- steps int
-
- typ int8
- size uint64
-}
-
-func newObjectReader(pr *PackfileReader, pf *Packfile) (*objectReader, error) {
- o := &objectReader{pr: pr, pf: pf}
-
- var buf [1]byte
- if _, err := o.Read(buf[:]); err != nil {
- return nil, err
- }
-
- o.typ = int8((buf[0] >> 4) & 7)
- o.size = uint64(buf[0] & 15)
- o.steps++ // byte we just read to get `o.typ` and `o.size`
-
- var shift uint = 4
- for buf[0]&0x80 == 0x80 {
- if _, err := o.Read(buf[:]); err != nil {
- return nil, err
- }
-
- o.size += uint64(buf[0]&0x7f) << shift
- o.steps++ // byte we just read to update `o.size`
- shift += 7
- }
-
- return o, nil
-}
-
-func (o *objectReader) readREFDelta() error {
- var ref [20]byte
- if _, err := o.Read(ref[:]); err != nil {
- return err
- }
-
- buf, err := o.inflate()
- if err != nil {
- return err
- }
-
- refhash := fmt.Sprintf("%x", ref)
- referenced, ok := o.pr.objects[refhash]
- if !ok {
- o.pr.deltas = append(o.pr.deltas, packfileDelta{hash: refhash, delta: buf[:]})
- } else {
- patched := PatchDelta(referenced.bytes, buf[:])
- if patched == nil {
- return NewError("error while patching %x", ref)
- }
- o.typ = referenced.typ
- err = o.addObject(patched)
- if err != nil {
- return err
- }
- }
-
- return nil
-}
-
-func decodeOffset(src io.ByteReader, steps int) (int, error) {
- b, err := src.ReadByte()
- if err != nil {
- return 0, err
- }
- var offset = int(b & 0x7f)
- for (b & 0x80) != 0 {
- offset += 1 // WHY?
- b, err = src.ReadByte()
- if err != nil {
- return 0, err
- }
- offset = (offset << 7) + int(b&0x7f)
- }
- // offset needs to be aware of the bytes we read for `o.typ` and `o.size`
- offset += steps
- return -offset, nil
-}
-
-func (o *objectReader) readOFSDelta() error {
- var pos = o.pr.Pos()
-
- // read negative offset
- offset, err := decodeOffset(o.pr.r, o.steps)
- if err != nil {
- return err
- }
-
- buf, err := o.inflate()
- if err != nil {
- return err
- }
-
- refhash := o.pr.offsets[pos+offset]
- referenced, ok := o.pr.objects[refhash]
- if !ok {
- return NewError("can't find a pack entry at %d", pos+offset)
- } else {
- patched := PatchDelta(referenced.bytes, buf)
- if patched == nil {
- return NewError("error while patching %x", refhash)
- }
- o.typ = referenced.typ
- err = o.addObject(patched)
- if err != nil {
- return err
- }
- }
-
- return nil
-}
-
-func (o *objectReader) readObject() error {
- buf, err := o.inflate()
- if err != nil {
- return err
- }
-
- return o.addObject(buf)
-}
-
-func (o *objectReader) addObject(bytes []byte) error {
- var hash string
-
- switch o.typ {
- case OBJ_COMMIT:
- c, err := NewCommit(bytes)
- if err != nil {
- return err
- }
- o.pf.Commits[c.Hash()] = c
- hash = c.Hash()
- case OBJ_TREE:
- c, err := NewTree(bytes)
- if err != nil {
- return err
- }
- o.pf.Trees[c.Hash()] = c
- hash = c.Hash()
- case OBJ_BLOB:
- c, err := NewBlob(bytes)
- if err != nil {
- return err
- }
- o.pf.Blobs[c.Hash()] = c
- hash = c.Hash()
-
- if o.pr.contentCallback != nil {
- o.pr.contentCallback(hash, bytes)
- }
- }
-
- o.pr.objects[hash] = packfileObject{bytes: bytes, typ: o.typ}
- o.hash = hash
-
- return nil
-}
-
-func (o *objectReader) inflate() ([]byte, error) {
- zr, err := zlib.NewReader(o.pr.r)
- if err != nil {
- if err == zlib.ErrHeader {
- return nil, zlib.ErrHeader
- } else {
- return nil, NewError("error opening packfile's object zlib: %v", err)
- }
- }
- defer zr.Close()
-
- if o.size > SIZE_LIMIT {
- return nil, NewError("the object size exceeed the allowed limit: %d", o.size)
- }
-
- var buf bytes.Buffer
- io.Copy(&buf, zr) // also: io.CopyN(&buf, zr, int64(o.size))
-
- var bufLen = buf.Len()
- if bufLen != int(o.size) {
- return nil, NewError("inflated size mismatch, expected %d, got %d", o.size, bufLen)
- }
-
- return buf.Bytes(), nil
-}
-
-func (o *objectReader) Read(p []byte) (int, error) {
- return o.pr.r.Read(p)
-}
-
-func (o *objectReader) ReadByte() (byte, error) {
- return o.pr.r.ReadByte()
-}
-
-type ReaderError struct {
- Msg string // description of error
-}
-
-func NewError(format string, args ...interface{}) error {
- return &ReaderError{Msg: fmt.Sprintf(format, args...)}
-}
-
-func (e *ReaderError) Error() string { return e.Msg }
diff --git a/packfile/reader_test.go b/packfile/reader_test.go
deleted file mode 100644
index 04f2948..0000000
--- a/packfile/reader_test.go
+++ /dev/null
@@ -1,35 +0,0 @@
-package packfile
-
-import (
- "bytes"
- "encoding/base64"
- "testing"
-
- "github.com/stretchr/testify/assert"
-)
-
-var packFileWithEmptyObjects = "UEFDSwAAAAIAAAALnw54nKXMQWoDMQxA0b1PoX2hSLIm44FSAlmXnEG2NYlhXAfHgdLb5Cy9WAM5Qpb/Lf7oZqArUpakyYtQjCoxZ5lmWXwwyuzJbHqAuYt2+x6QoyCyhYCKIa67lGameSLWvPh5JU0hsCg7vY1z6/D1d/8ptcHhprm3Kxz7KL/wUdOz96eqZXtPrX4CCeOOPU8Eb0iI7qG1jGGvXdxaNoPs/gHeNkp8lA94nKXMQUpDMRCA4X1OMXtBZpI3L3kiRXAtPcMkmWjgxZSYQultPEsv1oJHcPl/i38OVRC0IXF0lshrJorZEcpKmTEJYbA+B3aFzEmGfk9gpqJEsmnZNutXF71i1IURU/G0bsWWwJ6NnOdXH/Bx+73U1uH9LHn0HziOWa/w2tJfv302qftz6u0AtFh0wQdmeEJCNA9tdU7938WUuivEF5CczR11ZEsNnw54nKWMUQoCIRRF/13F+w/ijY6jQkTQd7SGpz5LyAxzINpNa2ljTbSEPu/hnNsbM4TJTzqyt561GdUUmJKT6K2MeiCVgnZWoY/iRo2vHVS0URrUS+e+dkqIEp11HMhh9IaUkRM6QXM/1waH9+uRS4X9TLHVOxxbz0/YlPDbu1OhfFmHWrYwjBKVNVaNsMIBUSy05N75vxeR8oXBiw8GoErCnwt4nKXMzQkCMRBA4XuqmLsgM2M2ZkAWwbNYQ341sCEQsyB2Yy02pmAJHt93eKOnBFpMNJqtl5CFxVIMomViomQSEWP2JrN3yq3j1jqc369HqQ1Oq4u93eHSR3nCoYZfH6/VlWUbWp2BNOPO7i1OsEFCVF+tZYz030XlsiRw6gPZ0jxaqwV4nDM0MDAzMVFIZHg299HsTRevOXt3a64rj7px6ElP8ERDiGQSQ2uoXe8RrcodS5on+J4/u8HjD4NDKFQyRS8tPx+rbgDt3yiEMHicAwAAAAABPnicS0wEAa4kMOACACTjBKdkZXici7aaYAUAA3gBYKoDeJwzNDAwMzFRSGR4NvfR7E0Xrzl7d2uuK4+6cehJT/BEQ4hkEsOELYFJvS2eX47UJdVttFQrenrmzQwA13MaiDd4nEtMBAEuAApMAlGtAXicMzQwMDMxUUhkeDb30exNF685e3drriuPunHoSU/wRACvkA258N/i8hVXx9CiAZzvFXNIhCuSFmE="
-
-func TestReadPackfile(t *testing.T) {
- data, _ := base64.StdEncoding.DecodeString(packFileWithEmptyObjects)
- d := bytes.NewReader(data)
-
- r, err := NewPackfileReader(d, 8<<20, nil)
- assert.Nil(t, err)
-
- p, err := r.Read()
- assert.Nil(t, err)
-
- assert.Equal(t, 11, p.ObjectCount)
- assert.Equal(t, 4, len(p.Commits))
- assert.Equal(t, 4, len(p.Trees))
-}
-
-func TestReadPackfileInvalid(t *testing.T) {
- r, err := NewPackfileReader(bytes.NewReader([]byte("dasdsadasas")), 8<<20, nil)
- assert.Nil(t, err)
-
- _, err = r.Read()
- _, ok := err.(*ReaderError)
- assert.True(t, ok)
-}