package pktline // Utility functions for working with the Git pkt-line format. See // https://github.com/git/git/blob/master/Documentation/technical/protocol-common.txt import ( "bufio" "bytes" "fmt" "io" "regexp" "strconv" ) const ( maxPktSize = 0xffff pktDelim = "0001" ) var branchRemovalPktRegexp = regexp.MustCompile(`\A[a-f0-9]{4}[a-f0-9]{40} 0{40} `) // NewScanner returns a bufio.Scanner that splits on Git pktline boundaries func NewScanner(r io.Reader) *bufio.Scanner { scanner := bufio.NewScanner(r) scanner.Buffer(make([]byte, maxPktSize), maxPktSize) scanner.Split(pktLineSplitter) return scanner } func IsRefRemoval(pkt []byte) bool { return branchRemovalPktRegexp.Match(pkt) } // IsFlush detects the special flush packet '0000' func IsFlush(pkt []byte) bool { return bytes.Equal(pkt, []byte("0000")) } // IsDone detects the special done packet '0009done\n' func IsDone(pkt []byte) bool { return bytes.Equal(pkt, PktDone()) } // PktDone returns the bytes for a "done" packet. func PktDone() []byte { return []byte("0009done\n") } func pktLineSplitter(data []byte, atEOF bool) (advance int, token []byte, err error) { if len(data) < 4 { if atEOF && len(data) > 0 { return 0, nil, fmt.Errorf("pktLineSplitter: incomplete length prefix on %q", data) } return 0, nil, nil // want more data } // We have at least 4 bytes available so we can decode the 4-hex digit // length prefix of the packet line. pktLength64, err := strconv.ParseInt(string(data[:4]), 16, 0) if err != nil { return 0, nil, fmt.Errorf("pktLineSplitter: decode length: %v", err) } // Cast is safe because we requested an int-size number from strconv.ParseInt pktLength := int(pktLength64) if pktLength < 0 { return 0, nil, fmt.Errorf("pktLineSplitter: invalid length: %d", pktLength) } if pktLength < 4 { // Special case: magic empty packet 0000, 0001, 0002 or 0003. return 4, data[:4], nil } if len(data) < pktLength { // data contains incomplete packet if atEOF { return 0, nil, fmt.Errorf("pktLineSplitter: less than %d bytes in input %q", pktLength, data) } return 0, nil, nil // want more data } return pktLength, data[:pktLength], nil }