func (d *Decoder) DecodeObjectAt(offset int64) (plumbing.EncodedObject, error) {
	if !d.s.IsSeekable {
		return nil, ErrNonSeekable
	}
	beforeJump, err := d.s.SeekFromStart(offset)
	if err != nil {
		return nil, err
	}
	defer func() {
		_, seekErr := d.s.SeekFromStart(beforeJump)
		if err == nil {
			err = seekErr
		}
	}()
	return d.doDecodeObject(plumbing.AnyObject)
}
DecodeObjectAt reads an object at the given location. Every EncodedObject
returned is added into a internal index. This is intended to be able to regenerate
objects from deltas (offset deltas or reference deltas) without an package index
(.idx file). If Decode wasn't called previously objects offset should provided
using the SetOffsets method. It decodes the object regardless of the Decoder
type.
package packfile
import (
	"bytes"
	"gopkg.in/src-d/go-git.v4/plumbing"
	"gopkg.in/src-d/go-git.v4/plumbing/cache"
	"gopkg.in/src-d/go-git.v4/plumbing/storer"
)
type Format int
const (
	UnknownFormat Format = iota
	OFSDeltaFormat
	REFDeltaFormat
)
var (
	ErrMaxObjectsLimitReached = NewError("max. objects limit reached")
	ErrInvalidObject = NewError("invalid git object")
	ErrPackEntryNotFound = NewError("can't find a pack entry")
	ErrZLib = NewError("zlib reading error")
	ErrCannotRecall = NewError("cannot recall object")
	ErrResolveDeltasNotSupported = NewError("resolve delta is not supported")
	ErrNonSeekable = NewError("non-seekable scanner")
	ErrRollback = NewError("rollback error, during set error")
	ErrAlreadyDecoded = NewError("packfile was already decoded")
)
type Decoder struct {
	DeltaBaseCache cache.Object
	s  *Scanner
	o  storer.EncodedObjectStorer
	tx storer.Transaction
	isDecoded bool
	hasBuiltIndex bool
	idx           *Index
	offsetToType map[int64]plumbing.ObjectType
	decoderType  plumbing.ObjectType
}
func NewDecoder(s *Scanner, o storer.EncodedObjectStorer) (*Decoder, error) {
	return NewDecoderForType(s, o, plumbing.AnyObject)
}
func NewDecoderForType(s *Scanner, o storer.EncodedObjectStorer,
	t plumbing.ObjectType) (*Decoder, error) {
	if t == plumbing.OFSDeltaObject ||
		t == plumbing.REFDeltaObject ||
		t == plumbing.InvalidObject {
		return nil, plumbing.ErrInvalidType
	}
	if !canResolveDeltas(s, o) {
		return nil, ErrResolveDeltasNotSupported
	}
	return &Decoder{
		s: s,
		o: o,
		idx:          NewIndex(0),
		offsetToType: make(map[int64]plumbing.ObjectType),
		decoderType:  t,
	}, nil
}
func canResolveDeltas(s *Scanner, o storer.EncodedObjectStorer) bool {
	return s.IsSeekable || o != nil
}
func (d *Decoder) Decode() (checksum plumbing.Hash, err error) {
	defer func() { d.isDecoded = true }()
	if d.isDecoded {
		return plumbing.ZeroHash, ErrAlreadyDecoded
	}
	if err := d.doDecode(); err != nil {
		return plumbing.ZeroHash, err
	}
	return d.s.Checksum()
}
func (d *Decoder) doDecode() error {
	_, count, err := d.s.Header()
	if err != nil {
		return err
	}
	if !d.hasBuiltIndex {
		d.idx = NewIndex(int(count))
	}
	defer func() { d.hasBuiltIndex = true }()
	_, isTxStorer := d.o.(storer.Transactioner)
	switch {
	case d.o == nil:
		return d.decodeObjects(int(count))
	case isTxStorer:
		return d.decodeObjectsWithObjectStorerTx(int(count))
	default:
		return d.decodeObjectsWithObjectStorer(int(count))
	}
}
func (d *Decoder) decodeObjects(count int) error {
	for i := 0; i < count; i++ {
		if _, err := d.DecodeObject(); err != nil {
			return err
		}
	}
	return nil
}
func (d *Decoder) decodeObjectsWithObjectStorer(count int) error {
	for i := 0; i < count; i++ {
		obj, err := d.DecodeObject()
		if err != nil {
			return err
		}
		if _, err := d.o.SetEncodedObject(obj); err != nil {
			return err
		}
	}
	return nil
}
func (d *Decoder) decodeObjectsWithObjectStorerTx(count int) error {
	d.tx = d.o.(storer.Transactioner).Begin()
	for i := 0; i < count; i++ {
		obj, err := d.DecodeObject()
		if err != nil {
			return err
		}
		if _, err := d.tx.SetEncodedObject(obj); err != nil {
			if rerr := d.tx.Rollback(); rerr != nil {
				return ErrRollback.AddDetails(
					"error: %s, during tx.Set error: %s", rerr, err,
				)
			}
			return err
		}
	}
	return d.tx.Commit()
}
func (d *Decoder) DecodeObject() (plumbing.EncodedObject, error) {
	return d.doDecodeObject(d.decoderType)
}
func (d *Decoder) doDecodeObject(t plumbing.ObjectType) (plumbing.EncodedObject, error) {
	h, err := d.s.NextObjectHeader()
	if err != nil {
		return nil, err
	}
	if t == plumbing.AnyObject {
		return d.decodeByHeader(h)
	}
	return d.decodeIfSpecificType(h)
}
func (d *Decoder) decodeIfSpecificType(h *ObjectHeader) (plumbing.EncodedObject, error) {
	var (
		obj      plumbing.EncodedObject
		realType plumbing.ObjectType
		err      error
	)
	switch h.Type {
	case plumbing.OFSDeltaObject:
		realType, err = d.ofsDeltaType(h.OffsetReference)
	case plumbing.REFDeltaObject:
		realType, err = d.refDeltaType(h.Reference)
		if err == plumbing.ErrObjectNotFound {
			obj, err = d.decodeByHeader(h)
			if err != nil {
				realType = obj.Type()
			}
		}
	default:
		realType = h.Type
	}
	if err != nil {
		return nil, err
	}
	d.offsetToType[h.Offset] = realType
	if d.decoderType == realType {
		if obj != nil {
			return obj, nil
		}
		return d.decodeByHeader(h)
	}
	return nil, nil
}
func (d *Decoder) ofsDeltaType(offset int64) (plumbing.ObjectType, error) {
	t, ok := d.offsetToType[offset]
	if !ok {
		return plumbing.InvalidObject, plumbing.ErrObjectNotFound
	}
	return t, nil
}
func (d *Decoder) refDeltaType(ref plumbing.Hash) (plumbing.ObjectType, error) {
	e, ok := d.idx.LookupHash(ref)
	if !ok {
		return plumbing.InvalidObject, plumbing.ErrObjectNotFound
	}
	return d.ofsDeltaType(int64(e.Offset))
}
func (d *Decoder) decodeByHeader(h *ObjectHeader) (plumbing.EncodedObject, error) {
	obj := d.newObject()
	obj.SetSize(h.Length)
	obj.SetType(h.Type)
	var crc uint32
	var err error
	switch h.Type {
	case plumbing.CommitObject, plumbing.TreeObject, plumbing.BlobObject, plumbing.TagObject:
		crc, err = d.fillRegularObjectContent(obj)
	case plumbing.REFDeltaObject:
		crc, err = d.fillREFDeltaObjectContent(obj, h.Reference)
	case plumbing.OFSDeltaObject:
		crc, err = d.fillOFSDeltaObjectContent(obj, h.OffsetReference)
	default:
		err = ErrInvalidObject.AddDetails("type %q", h.Type)
	}
	if err != nil {
		return obj, err
	}
	if !d.hasBuiltIndex {
		d.idx.Add(obj.Hash(), uint64(h.Offset), crc)
	}
	return obj, nil
}
func (d *Decoder) newObject() plumbing.EncodedObject {
	if d.o == nil {
		return &plumbing.MemoryObject{}
	}
	return d.o.NewEncodedObject()
}