aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/go-logfmt/logfmt/decode.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/go-logfmt/logfmt/decode.go')
-rw-r--r--vendor/github.com/go-logfmt/logfmt/decode.go237
1 files changed, 237 insertions, 0 deletions
diff --git a/vendor/github.com/go-logfmt/logfmt/decode.go b/vendor/github.com/go-logfmt/logfmt/decode.go
new file mode 100644
index 0000000..04e0eff
--- /dev/null
+++ b/vendor/github.com/go-logfmt/logfmt/decode.go
@@ -0,0 +1,237 @@
+package logfmt
+
+import (
+ "bufio"
+ "bytes"
+ "fmt"
+ "io"
+ "unicode/utf8"
+)
+
+// A Decoder reads and decodes logfmt records from an input stream.
+type Decoder struct {
+ pos int
+ key []byte
+ value []byte
+ lineNum int
+ s *bufio.Scanner
+ err error
+}
+
+// NewDecoder returns a new decoder that reads from r.
+//
+// The decoder introduces its own buffering and may read data from r beyond
+// the logfmt records requested.
+func NewDecoder(r io.Reader) *Decoder {
+ dec := &Decoder{
+ s: bufio.NewScanner(r),
+ }
+ return dec
+}
+
+// ScanRecord advances the Decoder to the next record, which can then be
+// parsed with the ScanKeyval method. It returns false when decoding stops,
+// either by reaching the end of the input or an error. After ScanRecord
+// returns false, the Err method will return any error that occurred during
+// decoding, except that if it was io.EOF, Err will return nil.
+func (dec *Decoder) ScanRecord() bool {
+ if dec.err != nil {
+ return false
+ }
+ if !dec.s.Scan() {
+ dec.err = dec.s.Err()
+ return false
+ }
+ dec.lineNum++
+ dec.pos = 0
+ return true
+}
+
+// ScanKeyval advances the Decoder to the next key/value pair of the current
+// record, which can then be retrieved with the Key and Value methods. It
+// returns false when decoding stops, either by reaching the end of the
+// current record or an error.
+func (dec *Decoder) ScanKeyval() bool {
+ dec.key, dec.value = nil, nil
+ if dec.err != nil {
+ return false
+ }
+
+ line := dec.s.Bytes()
+
+ // garbage
+ for p, c := range line[dec.pos:] {
+ if c > ' ' {
+ dec.pos += p
+ goto key
+ }
+ }
+ dec.pos = len(line)
+ return false
+
+key:
+ const invalidKeyError = "invalid key"
+
+ start, multibyte := dec.pos, false
+ for p, c := range line[dec.pos:] {
+ switch {
+ case c == '=':
+ dec.pos += p
+ if dec.pos > start {
+ dec.key = line[start:dec.pos]
+ if multibyte && bytes.IndexRune(dec.key, utf8.RuneError) != -1 {
+ dec.syntaxError(invalidKeyError)
+ return false
+ }
+ }
+ if dec.key == nil {
+ dec.unexpectedByte(c)
+ return false
+ }
+ goto equal
+ case c == '"':
+ dec.pos += p
+ dec.unexpectedByte(c)
+ return false
+ case c <= ' ':
+ dec.pos += p
+ if dec.pos > start {
+ dec.key = line[start:dec.pos]
+ if multibyte && bytes.IndexRune(dec.key, utf8.RuneError) != -1 {
+ dec.syntaxError(invalidKeyError)
+ return false
+ }
+ }
+ return true
+ case c >= utf8.RuneSelf:
+ multibyte = true
+ }
+ }
+ dec.pos = len(line)
+ if dec.pos > start {
+ dec.key = line[start:dec.pos]
+ if multibyte && bytes.IndexRune(dec.key, utf8.RuneError) != -1 {
+ dec.syntaxError(invalidKeyError)
+ return false
+ }
+ }
+ return true
+
+equal:
+ dec.pos++
+ if dec.pos >= len(line) {
+ return true
+ }
+ switch c := line[dec.pos]; {
+ case c <= ' ':
+ return true
+ case c == '"':
+ goto qvalue
+ }
+
+ // value
+ start = dec.pos
+ for p, c := range line[dec.pos:] {
+ switch {
+ case c == '=' || c == '"':
+ dec.pos += p
+ dec.unexpectedByte(c)
+ return false
+ case c <= ' ':
+ dec.pos += p
+ if dec.pos > start {
+ dec.value = line[start:dec.pos]
+ }
+ return true
+ }
+ }
+ dec.pos = len(line)
+ if dec.pos > start {
+ dec.value = line[start:dec.pos]
+ }
+ return true
+
+qvalue:
+ const (
+ untermQuote = "unterminated quoted value"
+ invalidQuote = "invalid quoted value"
+ )
+
+ hasEsc, esc := false, false
+ start = dec.pos
+ for p, c := range line[dec.pos+1:] {
+ switch {
+ case esc:
+ esc = false
+ case c == '\\':
+ hasEsc, esc = true, true
+ case c == '"':
+ dec.pos += p + 2
+ if hasEsc {
+ v, ok := unquoteBytes(line[start:dec.pos])
+ if !ok {
+ dec.syntaxError(invalidQuote)
+ return false
+ }
+ dec.value = v
+ } else {
+ start++
+ end := dec.pos - 1
+ if end > start {
+ dec.value = line[start:end]
+ }
+ }
+ return true
+ }
+ }
+ dec.pos = len(line)
+ dec.syntaxError(untermQuote)
+ return false
+}
+
+// Key returns the most recent key found by a call to ScanKeyval. The returned
+// slice may point to internal buffers and is only valid until the next call
+// to ScanRecord. It does no allocation.
+func (dec *Decoder) Key() []byte {
+ return dec.key
+}
+
+// Value returns the most recent value found by a call to ScanKeyval. The
+// returned slice may point to internal buffers and is only valid until the
+// next call to ScanRecord. It does no allocation when the value has no
+// escape sequences.
+func (dec *Decoder) Value() []byte {
+ return dec.value
+}
+
+// Err returns the first non-EOF error that was encountered by the Scanner.
+func (dec *Decoder) Err() error {
+ return dec.err
+}
+
+func (dec *Decoder) syntaxError(msg string) {
+ dec.err = &SyntaxError{
+ Msg: msg,
+ Line: dec.lineNum,
+ Pos: dec.pos + 1,
+ }
+}
+
+func (dec *Decoder) unexpectedByte(c byte) {
+ dec.err = &SyntaxError{
+ Msg: fmt.Sprintf("unexpected %q", c),
+ Line: dec.lineNum,
+ Pos: dec.pos + 1,
+ }
+}
+
+// A SyntaxError represents a syntax error in the logfmt input stream.
+type SyntaxError struct {
+ Msg string
+ Line int
+ Pos int
+}
+
+func (e *SyntaxError) Error() string {
+ return fmt.Sprintf("logfmt syntax error at pos %d on line %d: %s", e.Pos, e.Line, e.Msg)
+}
nihil fit ex nihilo