pktline

package
v4.0.0-rc1+incompatible Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 19, 2016 License: MIT Imports: 4 Imported by: 0

Documentation

Overview

Package pktline implements reading payloads form pkt-lines and creating pkt-lines from payloads.

Index

Examples

Constants

View Source
const (
	// MaxPayloadSize is the maximum payload size of a pkt-line in bytes.
	MaxPayloadSize = 65516
)

Variables

View Source
var (
	// ErrPayloadTooLong is returned by the Add methods when any of the
	// provided payloads is bigger than MaxPayloadSize.
	ErrPayloadTooLong = errors.New("payload is too long")
	// ErrEmptyPayload is returned by the Add methods when an empty
	// payload is provided.
	ErrEmptyPayload = errors.New("cannot add empty payloads")
)
View Source
var ErrInvalidPktLen = errors.New("invalid pkt-len found")

ErrInvalidPktLen is returned by Err() when an invalid pkt-len is found.

Functions

This section is empty.

Types

type PktLines

type PktLines struct {
	// contains filtered or unexported fields
}

PktLines values represent a succession of pkt-lines. Values from this type are not zero-value safe, use the New function instead.

Example
// Create an empty collection of pktlines.
p := pktline.New()

// Add two strings as payloads ("foo\n" and "bar\n"), they will
// end up as two consecutive pktlines.
p.AddString("foo\n", "bar\n") // error checks removed for brevity

// You can also add byte slices as payloads...
p.Add([]byte("hello\n"), []byte("world!\n"))

// Add a flush-pkt.
p.AddFlush()

// PktLines are Readers, so you can directly read the full sequence.
io.Copy(os.Stdout, p)
Output:

0008foo
0008bar
000ahello
000bworld!
0000

func New

func New() *PktLines

New returns an empty PktLines (with no payloads) ready to be used.

func (*PktLines) Add

func (p *PktLines) Add(pp ...[]byte) error

Add adds the slices in pp as the payloads of a corresponding number of pktlines.

func (*PktLines) AddFlush

func (p *PktLines) AddFlush()

AddFlush adds a flush-pkt to p.

func (*PktLines) AddString

func (p *PktLines) AddString(pp ...string) error

AddString adds the strings in pp as payloads of a corresponding number of pktlines.

func (*PktLines) Read

func (p *PktLines) Read(b []byte) (n int, err error)

Read reads the pktlines for the payloads added so far.

type Scanner

type Scanner struct {
	// contains filtered or unexported fields
}

Scanner provides a convenient interface for reading the payloads of a series of pkt-lines. It takes an io.Reader providing the source, which then can be tokenized through repeated calls to the Scan method.

After each Scan call, the Bytes method will return the payload of the corresponding pkt-line on a shared buffer, which will be 65516 bytes or smaller. Flush pkt-lines are represented by empty byte slices.

Scanning stops at EOF or the first I/O error.

Example
package main

import (
	"fmt"
	"io"
	"strings"

	"gopkg.in/src-d/go-git.v4/formats/packp/pktline"

	. "gopkg.in/check.v1"
)

type SuiteScanner struct{}

var _ = Suite(&SuiteScanner{})

func (s *SuiteScanner) TestInvalid(c *C) {
	for _, test := range [...]string{
		"0001", "0002", "0003", "0004",
		"0001asdfsadf", "0004foo",
		"fff1", "fff2",
		"gorka",
		"0", "003",
		"   5a", "5   a", "5   \n",
		"-001", "-000",
	} {
		r := strings.NewReader(test)
		sc := pktline.NewScanner(r)
		_ = sc.Scan()
		c.Assert(sc.Err(), ErrorMatches, pktline.ErrInvalidPktLen.Error(),
			Commentf("data = %q", test))
	}
}

func (s *SuiteScanner) TestEmptyReader(c *C) {
	r := strings.NewReader("")
	sc := pktline.NewScanner(r)
	hasPayload := sc.Scan()
	c.Assert(hasPayload, Equals, false)
	c.Assert(sc.Err(), Equals, nil)
}

func (s *SuiteScanner) TestFlush(c *C) {
	p := pktline.New()
	p.AddFlush()
	sc := pktline.NewScanner(p)

	c.Assert(sc.Scan(), Equals, true)
	payload := sc.Bytes()
	c.Assert(len(payload), Equals, 0)
}

func (s *SuiteScanner) TestPktLineTooShort(c *C) {
	r := strings.NewReader("010cfoobar")

	sc := pktline.NewScanner(r)

	c.Assert(sc.Scan(), Equals, false)
	c.Assert(sc.Err(), ErrorMatches, "unexpected EOF")
}

func (s *SuiteScanner) TestScanAndPayload(c *C) {
	for _, test := range [...]string{
		"a",
		"a\n",
		strings.Repeat("a", 100),
		strings.Repeat("a", 100) + "\n",
		strings.Repeat("\x00", 100),
		strings.Repeat("\x00", 100) + "\n",
		strings.Repeat("a", pktline.MaxPayloadSize),
		strings.Repeat("a", pktline.MaxPayloadSize-1) + "\n",
	} {
		p := pktline.New()
		err := p.AddString(test)
		c.Assert(err, IsNil,
			Commentf("input len=%x, contents=%.10q\n", len(test), test))
		sc := pktline.NewScanner(p)

		c.Assert(sc.Scan(), Equals, true,
			Commentf("test = %.20q...", test))
		obtained := sc.Bytes()
		c.Assert(obtained, DeepEquals, []byte(test),
			Commentf("in = %.20q out = %.20q", test, string(obtained)))
	}
}

func (s *SuiteScanner) TestSkip(c *C) {
	for _, test := range [...]struct {
		input    []string
		n        int
		expected []byte
	}{
		{
			input: []string{
				"first",
				"second",
				"third"},
			n:        1,
			expected: []byte("second"),
		},
		{
			input: []string{
				"first",
				"second",
				"third"},
			n:        2,
			expected: []byte("third"),
		},
	} {
		p := pktline.New()
		err := p.AddString(test.input...)
		c.Assert(err, IsNil)
		sc := pktline.NewScanner(p)
		for i := 0; i < test.n; i++ {
			c.Assert(sc.Scan(), Equals, true,
				Commentf("scan error = %s", sc.Err()))
		}
		c.Assert(sc.Scan(), Equals, true,
			Commentf("scan error = %s", sc.Err()))
		obtained := sc.Bytes()
		c.Assert(obtained, DeepEquals, test.expected,
			Commentf("\nin = %.20q\nout = %.20q\nexp = %.20q",
				test.input, obtained, test.expected))
	}
}

func (s *SuiteScanner) TestEOF(c *C) {
	p := pktline.New()
	err := p.AddString("first", "second")
	c.Assert(err, IsNil)
	sc := pktline.NewScanner(p)
	for sc.Scan() {
	}
	c.Assert(sc.Err(), IsNil)
}

// A section are several non flush-pkt lines followed by a flush-pkt, which
// how the git protocol sends long messages.
func (s *SuiteScanner) TestReadSomeSections(c *C) {
	nSections := 2
	nLines := 4
	data := sectionsExample(c, nSections, nLines)
	sc := pktline.NewScanner(data)

	sectionCounter := 0
	lineCounter := 0
	for sc.Scan() {
		if len(sc.Bytes()) == 0 {
			sectionCounter++
		}
		lineCounter++
	}
	c.Assert(sc.Err(), IsNil)
	c.Assert(sectionCounter, Equals, nSections)
	c.Assert(lineCounter, Equals, (1+nLines)*nSections)
}

// returns nSection sections, each of them with nLines pkt-lines (not
// counting the flush-pkt:
//
// 0009 0.0\n
// 0009 0.1\n
// ...
// 0000
// and so on
func sectionsExample(c *C, nSections, nLines int) io.Reader {
	p := pktline.New()
	for section := 0; section < nSections; section++ {
		ss := []string{}
		for line := 0; line < nLines; line++ {
			line := fmt.Sprintf(" %d.%d\n", section, line)
			ss = append(ss, line)
		}
		err := p.AddString(ss...)
		c.Assert(err, IsNil)
		p.AddFlush()
	}

	return p
}

func main() {
	// A reader is needed as input.
	input := strings.NewReader("000ahello\n" +
		"000bworld!\n" +
		"0000",
	)

	// Create the scanner...
	s := pktline.NewScanner(input)

	// and scan every pkt-line found in the input.
	for s.Scan() {
		payload := s.Bytes()
		if len(payload) == 0 { // zero sized payloads correspond to flush-pkts.
			fmt.Println("FLUSH-PKT DETECTED\n")
		} else { // otherwise, you will be able to access the full payload.
			fmt.Printf("PAYLOAD = %q\n", string(payload))
		}
	}

	// this will catch any error when reading from the input, if any.
	if s.Err() != nil {
		fmt.Println(s.Err())
	}

}
Output:

PAYLOAD = "hello\n"
PAYLOAD = "world!\n"
FLUSH-PKT DETECTED

func NewScanner

func NewScanner(r io.Reader) *Scanner

NewScanner returns a new Scanner to read from r.

func (*Scanner) Bytes

func (s *Scanner) Bytes() []byte

Bytes returns the most recent payload generated by a call to Scan. The underlying array may point to data that will be overwritten by a subsequent call to Scan. It does no allocation.

func (*Scanner) Err

func (s *Scanner) Err() error

Err returns the first error encountered by the Scanner.

func (*Scanner) Scan

func (s *Scanner) Scan() bool

Scan advances the Scanner to the next pkt-line, whose payload will then be available through the Bytes method. Scanning stops at EOF or the first I/O error. After Scan returns false, the Err method will return any error that occurred during scanning, except that if it was io.EOF, Err will return nil.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL