package packfile import ( "io" "gopkg.in/src-d/go-git.v4/core" ) // Stream implements ReadRecaller for the io.Reader of a packfile. This // implementation keeps all remembered objects referenced in maps for // quick access. type Stream struct { io.Reader count int64 offsetToObject map[int64]core.Object hashToObject map[core.Hash]core.Object } // NewStream returns a new Stream that reads form r. func NewStream(r io.Reader) *Stream { return &Stream{ Reader: r, count: 0, hashToObject: make(map[core.Hash]core.Object, 0), offsetToObject: make(map[int64]core.Object, 0), } } // Read reads up to len(p) bytes into p. func (r *Stream) Read(p []byte) (n int, err error) { n, err = r.Reader.Read(p) r.count += int64(n) return } // ReadByte reads a byte. func (r *Stream) ReadByte() (byte, error) { var p [1]byte _, err := r.Reader.Read(p[:]) r.count++ return p[0], err } // Offset returns the number of bytes read. func (r *Stream) Offset() (int64, error) { return r.count, nil } // Remember stores references to the passed object to be used later by // RecalByHash and RecallByOffset. It receives the object and the offset // of its object entry in the packfile. func (r *Stream) Remember(o int64, obj core.Object) error { h := obj.Hash() if _, ok := r.hashToObject[h]; ok { return ErrDuplicatedObject.AddDetails("with hash %s", h) } r.hashToObject[h] = obj if _, ok := r.offsetToObject[o]; ok { return ErrDuplicatedObject.AddDetails("with offset %d", o) } r.offsetToObject[o] = obj return nil } // ForgetAll forgets all previously remembered objects. func (r *Stream) ForgetAll() { r.hashToObject = make(map[core.Hash]core.Object) r.offsetToObject = make(map[int64]core.Object) } // RecallByHash returns an object that has been previously Remember-ed by // its hash. func (r *Stream) RecallByHash(h core.Hash) (core.Object, error) { obj, ok := r.hashToObject[h] if !ok { return nil, ErrCannotRecall.AddDetails("by hash %s", h) } return obj, nil } // RecallByOffset returns an object that has been previously Remember-ed by // the offset of its object entry in the packfile. func (r *Stream) RecallByOffset(o int64) (core.Object, error) { obj, ok := r.offsetToObject[o] if !ok { return nil, ErrCannotRecall.AddDetails("no object found at offset %d", o) } return obj, nil }