package objfile
import (
"errors"
"io"
"strconv"
"gopkg.in/src-d/go-git.v4/core"
)
var (
// ErrClosed is returned when the objfile Reader or Writer is already closed.
ErrClosed = errors.New("objfile: already closed")
// ErrHeader is returned when the objfile has an invalid header.
ErrHeader = errors.New("objfile: invalid header")
// ErrNegativeSize is returned when a negative object size is declared.
ErrNegativeSize = errors.New("objfile: negative object size")
)
type header struct {
t core.ObjectType
size int64
}
func (h *header) Read(r io.Reader) error {
t, err := h.readSlice(r, ' ')
if err != nil {
return err
}
h.t, err = core.ParseObjectType(string(t))
if err != nil {
return err
}
size, err := h.readSlice(r, 0)
if err != nil {
return err
}
h.size, err = strconv.ParseInt(string(size), 10, 64)
if err != nil {
return ErrHeader
}
if h.size < 0 {
return ErrNegativeSize
}
return nil
}
func (h *header) Write(w io.Writer) error {
b := h.t.Bytes()
b = append(b, ' ')
b = append(b, []byte(strconv.FormatInt(h.size, 10))...)
b = append(b, 0)
_, err := w.Write(b)
return err
}
// readSlice reads one byte at a time from r until it encounters delim or an
// error.
func (h *header) readSlice(r io.Reader, delim byte) ([]byte, error) {
var buf [1]byte
value := make([]byte, 0, 16)
for {
if n, err := r.Read(buf[:]); err != nil && (err != io.EOF || n == 0) {
if err == io.EOF {
return nil, ErrHeader
}
return nil, err
}
if buf[0] == delim {
return value, nil
}
value = append(value, buf[0])
}
}