// Package pktline implements reading payloads form pkt-lines and creating pkt-lines from payloads. package pktline import ( "bytes" "errors" "io" "strings" ) const ( // MaxPayloadSize is the maximum payload size of a pkt-line in bytes. MaxPayloadSize = 65516 ) var ( flush = []byte{'0', '0', '0', '0'} ) // PktLines values represent a succession of pkt-lines. Values from // this type are not zero-value safe, use the New function instead. type PktLines struct { r io.Reader } 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") ) // New returns an empty PktLines (with no payloads) ready to be used. func New() *PktLines { return &PktLines{ r: bytes.NewReader(nil), } } // AddFlush adds a flush-pkt to p. func (p *PktLines) AddFlush() { p.r = io.MultiReader(p.r, bytes.NewReader(flush)) } // Add adds the slices in pp as the payloads of a // corresponding number of pktlines. func (p *PktLines) Add(pp ...[]byte) error { tmp := []io.Reader{p.r} for _, p := range pp { if err := add(&tmp, p); err != nil { return err } } p.r = io.MultiReader(tmp...) return nil } func add(dst *[]io.Reader, e []byte) error { if err := checkPayloadLength(len(e)); err != nil { return err } n := len(e) + 4 *dst = append(*dst, bytes.NewReader(asciiHex16(n))) *dst = append(*dst, bytes.NewReader(e)) return nil } func checkPayloadLength(n int) error { switch { case n < 0: panic("unexpected negative payload length") case n == 0: return ErrEmptyPayload case n > MaxPayloadSize: return ErrPayloadTooLong default: return nil } } // Returns the hexadecimal ascii representation of the 16 less // significant bits of n. The length of the returned slice will always // be 4. Example: if n is 1234 (0x4d2), the return value will be // []byte{'0', '4', 'd', '2'}. func asciiHex16(n int) []byte { var ret [4]byte ret[0] = byteToASCIIHex(byte(n & 0xf000 >> 12)) ret[1] = byteToASCIIHex(byte(n & 0x0f00 >> 8)) ret[2] = byteToASCIIHex(byte(n & 0x00f0 >> 4)) ret[3] = byteToASCIIHex(byte(n & 0x000f)) return ret[:] } // turns a byte into its hexadecimal ascii representation. Example: // from 11 (0xb) to 'b'. func byteToASCIIHex(n byte) byte { if n < 10 { return '0' + n } return 'a' - 10 + n } // AddString adds the strings in pp as payloads of a // corresponding number of pktlines. func (p *PktLines) AddString(pp ...string) error { tmp := []io.Reader{p.r} for _, p := range pp { if err := addString(&tmp, p); err != nil { return err } } p.r = io.MultiReader(tmp...) return nil } func addString(dst *[]io.Reader, s string) error { if err := checkPayloadLength(len(s)); err != nil { return err } n := len(s) + 4 *dst = append(*dst, bytes.NewReader(asciiHex16(n))) *dst = append(*dst, strings.NewReader(s)) return nil } // Read reads the pktlines for the payloads added so far. func (p *PktLines) Read(b []byte) (n int, err error) { return p.r.Read(b) }