// Package pktline is git's framing: four hex digits of length, 0000 flush, 0001 delimiter. package pktline import ( "bufio" "errors" "fmt" "io" "strconv" "strings" ) const ( flush = "0000" delim = "0001" ) // ErrFlush is returned by Read when it reads a flush packet. var ErrFlush = errors.New("flush") // ErrDelim is returned by Read when it reads a delimiter packet. var ErrDelim = errors.New("delim") type Reader struct{ r *bufio.Reader } func NewReader(r io.Reader) *Reader { return &Reader{bufio.NewReader(r)} } // Read returns the next packet's payload, with any trailing newline removed. func (p *Reader) Read() (string, error) { var head [4]byte if _, err := io.ReadFull(p.r, head[:]); err != nil { return "", err } n, err := strconv.ParseUint(string(head[:]), 16, 32) if err != nil { return "", fmt.Errorf("malformed pkt-line length %q", head) } switch n { case 0: return "", ErrFlush case 1: return "", ErrDelim } if n < 4 { return "", fmt.Errorf("impossible pkt-line length %d", n) } body := make([]byte, n-4) if _, err := io.ReadFull(p.r, body); err != nil { return "", err } return strings.TrimSuffix(string(body), "\n"), nil } type Writer struct{ w io.Writer } func NewWriter(w io.Writer) *Writer { return &Writer{w} } // Write sends one packet with a newline, which git expects for these text protocols. func (p *Writer) Write(s string) error { if len(s)+5 > 0xffff { return errors.New("pkt-line too long") } _, err := fmt.Fprintf(p.w, "%04x%s\n", len(s)+5, s) return err } // Flush sends a flush packet, which ends a section. func (p *Writer) Flush() error { _, err := io.WriteString(p.w, flush) return err } // Delim sends a delimiter packet. func (p *Writer) Delim() error { _, err := io.WriteString(p.w, delim) return err }