Moved to google.golang.org/genproto/googleapis/api/annotations
Fixes #52
This commit is contained in:
4
vendor/github.com/go-logfmt/logfmt/.gitignore
generated
vendored
Normal file
4
vendor/github.com/go-logfmt/logfmt/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
_testdata/
|
||||
_testdata2/
|
||||
logfmt-fuzz.zip
|
||||
logfmt.test.exe
|
15
vendor/github.com/go-logfmt/logfmt/.travis.yml
generated
vendored
Normal file
15
vendor/github.com/go-logfmt/logfmt/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
language: go
|
||||
sudo: false
|
||||
go:
|
||||
- 1.3
|
||||
- 1.4
|
||||
- 1.5
|
||||
- 1.6
|
||||
- tip
|
||||
|
||||
before_install:
|
||||
- go get github.com/mattn/goveralls
|
||||
- go get golang.org/x/tools/cmd/cover
|
||||
|
||||
script:
|
||||
- goveralls -service=travis-ci
|
22
vendor/github.com/go-logfmt/logfmt/LICENSE
generated
vendored
Normal file
22
vendor/github.com/go-logfmt/logfmt/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 go-logfmt
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
33
vendor/github.com/go-logfmt/logfmt/README.md
generated
vendored
Normal file
33
vendor/github.com/go-logfmt/logfmt/README.md
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
[](https://godoc.org/github.com/go-logfmt/logfmt)
|
||||
[](https://goreportcard.com/report/go-logfmt/logfmt)
|
||||
[](https://travis-ci.org/go-logfmt/logfmt)
|
||||
[](https://coveralls.io/github/go-logfmt/logfmt?branch=master)
|
||||
|
||||
# logfmt
|
||||
|
||||
Package logfmt implements utilities to marshal and unmarshal data in the [logfmt
|
||||
format](https://brandur.org/logfmt). It provides an API similar to
|
||||
[encoding/json](http://golang.org/pkg/encoding/json/) and
|
||||
[encoding/xml](http://golang.org/pkg/encoding/xml/).
|
||||
|
||||
The logfmt format was first documented by Brandur Leach in [this
|
||||
article](https://brandur.org/logfmt). The format has not been formally
|
||||
standardized. The most authoritative public specification to date has been the
|
||||
documentation of a Go Language [package](http://godoc.org/github.com/kr/logfmt)
|
||||
written by Blake Mizerany and Keith Rarick.
|
||||
|
||||
## Goals
|
||||
|
||||
This project attempts to conform as closely as possible to the prior art, while
|
||||
also removing ambiguity where necessary to provide well behaved encoder and
|
||||
decoder implementations.
|
||||
|
||||
## Non-goals
|
||||
|
||||
This project does not attempt to formally standardize the logfmt format. In the
|
||||
event that logfmt is standardized this project would take conforming to the
|
||||
standard as a goal.
|
||||
|
||||
## Versioning
|
||||
|
||||
Package logfmt publishes releases via [semver](http://semver.org/) compatible Git tags prefixed with a single 'v'.
|
75
vendor/github.com/go-logfmt/logfmt/decode-bench_test.go
generated
vendored
Normal file
75
vendor/github.com/go-logfmt/logfmt/decode-bench_test.go
generated
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
package logfmt
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
kr "github.com/kr/logfmt"
|
||||
)
|
||||
|
||||
func BenchmarkDecodeKeyval(b *testing.B) {
|
||||
const rows = 10000
|
||||
data := []byte{}
|
||||
for i := 0; i < rows; i++ {
|
||||
data = append(data, "a=1 b=\"bar\" ƒ=2h3s r=\"esc\\tmore stuff\" d x=sf \n"...)
|
||||
}
|
||||
|
||||
b.SetBytes(int64(len(data)))
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
var (
|
||||
dec = NewDecoder(bytes.NewReader(data))
|
||||
j = 0
|
||||
)
|
||||
for dec.ScanRecord() {
|
||||
for dec.ScanKeyval() {
|
||||
}
|
||||
j++
|
||||
}
|
||||
if err := dec.Err(); err != nil {
|
||||
b.Errorf("got %v, want %v", err, nil)
|
||||
}
|
||||
if j != rows {
|
||||
b.Errorf("got %v, want %v", j, rows)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkKRDecode(b *testing.B) {
|
||||
const rows = 10000
|
||||
data := []byte{}
|
||||
for i := 0; i < rows; i++ {
|
||||
data = append(data, "a=1 b=\"bar\" ƒ=2h3s r=\"esc\\tmore stuff\" d x=sf \n"...)
|
||||
}
|
||||
|
||||
b.SetBytes(int64(len(data)))
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
var (
|
||||
s = bufio.NewScanner(bytes.NewReader(data))
|
||||
err error
|
||||
j = 0
|
||||
dh discardHandler
|
||||
)
|
||||
for err == nil && s.Scan() {
|
||||
err = kr.Unmarshal(s.Bytes(), &dh)
|
||||
j++
|
||||
}
|
||||
if err == nil {
|
||||
err = s.Err()
|
||||
}
|
||||
if err != nil {
|
||||
b.Errorf("got %v, want %v", err, nil)
|
||||
}
|
||||
if j != rows {
|
||||
b.Errorf("got %v, want %v", j, rows)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type discardHandler struct{}
|
||||
|
||||
func (discardHandler) HandleLogfmt(key, val []byte) error {
|
||||
return nil
|
||||
}
|
237
vendor/github.com/go-logfmt/logfmt/decode.go
generated
vendored
Normal file
237
vendor/github.com/go-logfmt/logfmt/decode.go
generated
vendored
Normal file
@@ -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)
|
||||
}
|
184
vendor/github.com/go-logfmt/logfmt/decode_test.go
generated
vendored
Normal file
184
vendor/github.com/go-logfmt/logfmt/decode_test.go
generated
vendored
Normal file
@@ -0,0 +1,184 @@
|
||||
package logfmt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type kv struct {
|
||||
k, v []byte
|
||||
}
|
||||
|
||||
func (s kv) String() string {
|
||||
return fmt.Sprintf("{k:%q v:%q}", s.k, s.v)
|
||||
}
|
||||
|
||||
func TestDecoder_scan(t *testing.T) {
|
||||
tests := []struct {
|
||||
data string
|
||||
want [][]kv
|
||||
}{
|
||||
{"", nil},
|
||||
{"\n\n", [][]kv{nil, nil}},
|
||||
{`x= `, [][]kv{{{[]byte("x"), nil}}}},
|
||||
{`y=`, [][]kv{{{[]byte("y"), nil}}}},
|
||||
{`y`, [][]kv{{{[]byte("y"), nil}}}},
|
||||
{`y=f`, [][]kv{{{[]byte("y"), []byte("f")}}}},
|
||||
{"y=\"\\tf\"", [][]kv{{{[]byte("y"), []byte("\tf")}}}},
|
||||
{"a=1\n", [][]kv{{{[]byte("a"), []byte("1")}}}},
|
||||
{
|
||||
`a=1 b="bar" ƒ=2h3s r="esc\t" d x=sf `,
|
||||
[][]kv{{
|
||||
{[]byte("a"), []byte("1")},
|
||||
{[]byte("b"), []byte("bar")},
|
||||
{[]byte("ƒ"), []byte("2h3s")},
|
||||
{[]byte("r"), []byte("esc\t")},
|
||||
{[]byte("d"), nil},
|
||||
{[]byte("x"), []byte("sf")},
|
||||
}},
|
||||
},
|
||||
{
|
||||
"y=f\ny=g",
|
||||
[][]kv{
|
||||
{{[]byte("y"), []byte("f")}},
|
||||
{{[]byte("y"), []byte("g")}},
|
||||
},
|
||||
},
|
||||
{
|
||||
"y=f \n\x1e y=g",
|
||||
[][]kv{
|
||||
{{[]byte("y"), []byte("f")}},
|
||||
{{[]byte("y"), []byte("g")}},
|
||||
},
|
||||
},
|
||||
{
|
||||
"y= d y=g",
|
||||
[][]kv{{
|
||||
{[]byte("y"), nil},
|
||||
{[]byte("d"), nil},
|
||||
{[]byte("y"), []byte("g")},
|
||||
}},
|
||||
},
|
||||
{
|
||||
"y=\"f\"\ny=g",
|
||||
[][]kv{
|
||||
{{[]byte("y"), []byte("f")}},
|
||||
{{[]byte("y"), []byte("g")}},
|
||||
},
|
||||
},
|
||||
{
|
||||
"y=\"f\\n\"y=g",
|
||||
[][]kv{{
|
||||
{[]byte("y"), []byte("f\n")},
|
||||
{[]byte("y"), []byte("g")},
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
var got [][]kv
|
||||
dec := NewDecoder(strings.NewReader(test.data))
|
||||
|
||||
for dec.ScanRecord() {
|
||||
var kvs []kv
|
||||
for dec.ScanKeyval() {
|
||||
k := dec.Key()
|
||||
v := dec.Value()
|
||||
if k != nil {
|
||||
kvs = append(kvs, kv{k, v})
|
||||
}
|
||||
}
|
||||
got = append(got, kvs)
|
||||
}
|
||||
if err := dec.Err(); err != nil {
|
||||
t.Errorf("got err: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(got, test.want) {
|
||||
t.Errorf("\n in: %q\n got: %+v\nwant: %+v", test.data, got, test.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecoder_errors(t *testing.T) {
|
||||
tests := []struct {
|
||||
data string
|
||||
want error
|
||||
}{
|
||||
{"a=1\n=bar", &SyntaxError{Msg: "unexpected '='", Line: 2, Pos: 1}},
|
||||
{"a=1\n\"k\"=bar", &SyntaxError{Msg: "unexpected '\"'", Line: 2, Pos: 1}},
|
||||
{"a=1\nk\"ey=bar", &SyntaxError{Msg: "unexpected '\"'", Line: 2, Pos: 2}},
|
||||
{"a=1\nk=b\"ar", &SyntaxError{Msg: "unexpected '\"'", Line: 2, Pos: 4}},
|
||||
{"a=1\nk=b =ar", &SyntaxError{Msg: "unexpected '='", Line: 2, Pos: 5}},
|
||||
{"a==", &SyntaxError{Msg: "unexpected '='", Line: 1, Pos: 3}},
|
||||
{"a=1\nk=b=ar", &SyntaxError{Msg: "unexpected '='", Line: 2, Pos: 4}},
|
||||
{"a=\"1", &SyntaxError{Msg: "unterminated quoted value", Line: 1, Pos: 5}},
|
||||
{"a=\"1\\", &SyntaxError{Msg: "unterminated quoted value", Line: 1, Pos: 6}},
|
||||
{"a=\"\\t1", &SyntaxError{Msg: "unterminated quoted value", Line: 1, Pos: 7}},
|
||||
{"a=\"\\u1\"", &SyntaxError{Msg: "invalid quoted value", Line: 1, Pos: 8}},
|
||||
{"a\ufffd=bar", &SyntaxError{Msg: "invalid key", Line: 1, Pos: 5}},
|
||||
{"\x80=bar", &SyntaxError{Msg: "invalid key", Line: 1, Pos: 2}},
|
||||
{"\x80", &SyntaxError{Msg: "invalid key", Line: 1, Pos: 2}},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
dec := NewDecoder(strings.NewReader(test.data))
|
||||
|
||||
for dec.ScanRecord() {
|
||||
for dec.ScanKeyval() {
|
||||
}
|
||||
}
|
||||
if got, want := dec.Err(), test.want; !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("got: %v, want: %v", got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecoder_decode_encode(t *testing.T) {
|
||||
tests := []struct {
|
||||
in, out string
|
||||
}{
|
||||
{"", ""},
|
||||
{"\n", "\n"},
|
||||
{"\n \n", "\n\n"},
|
||||
{
|
||||
"a=1\nb=2\n",
|
||||
"a=1\nb=2\n",
|
||||
},
|
||||
{
|
||||
"a=1 b=\"bar\" ƒ=2h3s r=\"esc\\t\" d x=sf ",
|
||||
"a=1 b=bar ƒ=2h3s r=\"esc\\t\" d= x=sf\n",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
dec := NewDecoder(strings.NewReader(test.in))
|
||||
buf := bytes.Buffer{}
|
||||
enc := NewEncoder(&buf)
|
||||
|
||||
var err error
|
||||
loop:
|
||||
for dec.ScanRecord() && err == nil {
|
||||
for dec.ScanKeyval() {
|
||||
if dec.Key() == nil {
|
||||
continue
|
||||
}
|
||||
if err = enc.EncodeKeyval(dec.Key(), dec.Value()); err != nil {
|
||||
break loop
|
||||
}
|
||||
}
|
||||
enc.EndRecord()
|
||||
}
|
||||
if err == nil {
|
||||
err = dec.Err()
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("got err: %v", err)
|
||||
}
|
||||
if got, want := buf.String(), test.out; got != want {
|
||||
t.Errorf("\n got: %q\nwant: %q", got, want)
|
||||
}
|
||||
}
|
||||
}
|
6
vendor/github.com/go-logfmt/logfmt/doc.go
generated
vendored
Normal file
6
vendor/github.com/go-logfmt/logfmt/doc.go
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
// Package logfmt implements utilities to marshal and unmarshal data in the
|
||||
// logfmt format. The logfmt format records key/value pairs in a way that
|
||||
// balances readability for humans and simplicity of computer parsing. It is
|
||||
// most commonly used as a more human friendly alternative to JSON for
|
||||
// structured logging.
|
||||
package logfmt
|
321
vendor/github.com/go-logfmt/logfmt/encode.go
generated
vendored
Normal file
321
vendor/github.com/go-logfmt/logfmt/encode.go
generated
vendored
Normal file
@@ -0,0 +1,321 @@
|
||||
package logfmt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// MarshalKeyvals returns the logfmt encoding of keyvals, a variadic sequence
|
||||
// of alternating keys and values.
|
||||
func MarshalKeyvals(keyvals ...interface{}) ([]byte, error) {
|
||||
buf := &bytes.Buffer{}
|
||||
if err := NewEncoder(buf).EncodeKeyvals(keyvals...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// An Encoder writes logfmt data to an output stream.
|
||||
type Encoder struct {
|
||||
w io.Writer
|
||||
scratch bytes.Buffer
|
||||
needSep bool
|
||||
}
|
||||
|
||||
// NewEncoder returns a new encoder that writes to w.
|
||||
func NewEncoder(w io.Writer) *Encoder {
|
||||
return &Encoder{
|
||||
w: w,
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
space = []byte(" ")
|
||||
equals = []byte("=")
|
||||
newline = []byte("\n")
|
||||
null = []byte("null")
|
||||
)
|
||||
|
||||
// EncodeKeyval writes the logfmt encoding of key and value to the stream. A
|
||||
// single space is written before the second and subsequent keys in a record.
|
||||
// Nothing is written if a non-nil error is returned.
|
||||
func (enc *Encoder) EncodeKeyval(key, value interface{}) error {
|
||||
enc.scratch.Reset()
|
||||
if enc.needSep {
|
||||
if _, err := enc.scratch.Write(space); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := writeKey(&enc.scratch, key); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := enc.scratch.Write(equals); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := writeValue(&enc.scratch, value); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := enc.w.Write(enc.scratch.Bytes())
|
||||
enc.needSep = true
|
||||
return err
|
||||
}
|
||||
|
||||
// EncodeKeyvals writes the logfmt encoding of keyvals to the stream. Keyvals
|
||||
// is a variadic sequence of alternating keys and values. Keys of unsupported
|
||||
// type are skipped along with their corresponding value. Values of
|
||||
// unsupported type or that cause a MarshalerError are replaced by their error
|
||||
// but do not cause EncodeKeyvals to return an error. If a non-nil error is
|
||||
// returned some key/value pairs may not have be written.
|
||||
func (enc *Encoder) EncodeKeyvals(keyvals ...interface{}) error {
|
||||
if len(keyvals) == 0 {
|
||||
return nil
|
||||
}
|
||||
if len(keyvals)%2 == 1 {
|
||||
keyvals = append(keyvals, nil)
|
||||
}
|
||||
for i := 0; i < len(keyvals); i += 2 {
|
||||
k, v := keyvals[i], keyvals[i+1]
|
||||
err := enc.EncodeKeyval(k, v)
|
||||
if err == ErrUnsupportedKeyType {
|
||||
continue
|
||||
}
|
||||
if _, ok := err.(*MarshalerError); ok || err == ErrUnsupportedValueType {
|
||||
v = err
|
||||
err = enc.EncodeKeyval(k, v)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalerError represents an error encountered while marshaling a value.
|
||||
type MarshalerError struct {
|
||||
Type reflect.Type
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *MarshalerError) Error() string {
|
||||
return "error marshaling value of type " + e.Type.String() + ": " + e.Err.Error()
|
||||
}
|
||||
|
||||
// ErrNilKey is returned by Marshal functions and Encoder methods if a key is
|
||||
// a nil interface or pointer value.
|
||||
var ErrNilKey = errors.New("nil key")
|
||||
|
||||
// ErrInvalidKey is returned by Marshal functions and Encoder methods if a key
|
||||
// contains an invalid character.
|
||||
var ErrInvalidKey = errors.New("invalid key")
|
||||
|
||||
// ErrUnsupportedKeyType is returned by Encoder methods if a key has an
|
||||
// unsupported type.
|
||||
var ErrUnsupportedKeyType = errors.New("unsupported key type")
|
||||
|
||||
// ErrUnsupportedValueType is returned by Encoder methods if a value has an
|
||||
// unsupported type.
|
||||
var ErrUnsupportedValueType = errors.New("unsupported value type")
|
||||
|
||||
func writeKey(w io.Writer, key interface{}) error {
|
||||
if key == nil {
|
||||
return ErrNilKey
|
||||
}
|
||||
|
||||
switch k := key.(type) {
|
||||
case string:
|
||||
return writeStringKey(w, k)
|
||||
case []byte:
|
||||
if k == nil {
|
||||
return ErrNilKey
|
||||
}
|
||||
return writeBytesKey(w, k)
|
||||
case encoding.TextMarshaler:
|
||||
kb, err := safeMarshal(k)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if kb == nil {
|
||||
return ErrNilKey
|
||||
}
|
||||
return writeBytesKey(w, kb)
|
||||
case fmt.Stringer:
|
||||
ks, ok := safeString(k)
|
||||
if !ok {
|
||||
return ErrNilKey
|
||||
}
|
||||
return writeStringKey(w, ks)
|
||||
default:
|
||||
rkey := reflect.ValueOf(key)
|
||||
switch rkey.Kind() {
|
||||
case reflect.Array, reflect.Chan, reflect.Func, reflect.Map, reflect.Slice, reflect.Struct:
|
||||
return ErrUnsupportedKeyType
|
||||
case reflect.Ptr:
|
||||
if rkey.IsNil() {
|
||||
return ErrNilKey
|
||||
}
|
||||
return writeKey(w, rkey.Elem().Interface())
|
||||
}
|
||||
return writeStringKey(w, fmt.Sprint(k))
|
||||
}
|
||||
}
|
||||
|
||||
func invalidKeyRune(r rune) bool {
|
||||
return r <= ' ' || r == '=' || r == '"' || r == utf8.RuneError
|
||||
}
|
||||
|
||||
func invalidKeyString(key string) bool {
|
||||
return len(key) == 0 || strings.IndexFunc(key, invalidKeyRune) != -1
|
||||
}
|
||||
|
||||
func invalidKey(key []byte) bool {
|
||||
return len(key) == 0 || bytes.IndexFunc(key, invalidKeyRune) != -1
|
||||
}
|
||||
|
||||
func writeStringKey(w io.Writer, key string) error {
|
||||
if invalidKeyString(key) {
|
||||
return ErrInvalidKey
|
||||
}
|
||||
_, err := io.WriteString(w, key)
|
||||
return err
|
||||
}
|
||||
|
||||
func writeBytesKey(w io.Writer, key []byte) error {
|
||||
if invalidKey(key) {
|
||||
return ErrInvalidKey
|
||||
}
|
||||
_, err := w.Write(key)
|
||||
return err
|
||||
}
|
||||
|
||||
func writeValue(w io.Writer, value interface{}) error {
|
||||
switch v := value.(type) {
|
||||
case nil:
|
||||
return writeBytesValue(w, null)
|
||||
case string:
|
||||
return writeStringValue(w, v, true)
|
||||
case []byte:
|
||||
return writeBytesValue(w, v)
|
||||
case encoding.TextMarshaler:
|
||||
vb, err := safeMarshal(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if vb == nil {
|
||||
vb = null
|
||||
}
|
||||
return writeBytesValue(w, vb)
|
||||
case error:
|
||||
se, ok := safeError(v)
|
||||
return writeStringValue(w, se, ok)
|
||||
case fmt.Stringer:
|
||||
ss, ok := safeString(v)
|
||||
return writeStringValue(w, ss, ok)
|
||||
default:
|
||||
rvalue := reflect.ValueOf(value)
|
||||
switch rvalue.Kind() {
|
||||
case reflect.Array, reflect.Chan, reflect.Func, reflect.Map, reflect.Slice, reflect.Struct:
|
||||
return ErrUnsupportedValueType
|
||||
case reflect.Ptr:
|
||||
if rvalue.IsNil() {
|
||||
return writeBytesValue(w, null)
|
||||
}
|
||||
return writeValue(w, rvalue.Elem().Interface())
|
||||
}
|
||||
return writeStringValue(w, fmt.Sprint(v), true)
|
||||
}
|
||||
}
|
||||
|
||||
func needsQuotedValueRune(r rune) bool {
|
||||
return r <= ' ' || r == '=' || r == '"' || r == utf8.RuneError
|
||||
}
|
||||
|
||||
func writeStringValue(w io.Writer, value string, ok bool) error {
|
||||
var err error
|
||||
if ok && value == "null" {
|
||||
_, err = io.WriteString(w, `"null"`)
|
||||
} else if strings.IndexFunc(value, needsQuotedValueRune) != -1 {
|
||||
_, err = writeQuotedString(w, value)
|
||||
} else {
|
||||
_, err = io.WriteString(w, value)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func writeBytesValue(w io.Writer, value []byte) error {
|
||||
var err error
|
||||
if bytes.IndexFunc(value, needsQuotedValueRune) != -1 {
|
||||
_, err = writeQuotedBytes(w, value)
|
||||
} else {
|
||||
_, err = w.Write(value)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// EndRecord writes a newline character to the stream and resets the encoder
|
||||
// to the beginning of a new record.
|
||||
func (enc *Encoder) EndRecord() error {
|
||||
_, err := enc.w.Write(newline)
|
||||
if err == nil {
|
||||
enc.needSep = false
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Reset resets the encoder to the beginning of a new record.
|
||||
func (enc *Encoder) Reset() {
|
||||
enc.needSep = false
|
||||
}
|
||||
|
||||
func safeError(err error) (s string, ok bool) {
|
||||
defer func() {
|
||||
if panicVal := recover(); panicVal != nil {
|
||||
if v := reflect.ValueOf(err); v.Kind() == reflect.Ptr && v.IsNil() {
|
||||
s, ok = "null", false
|
||||
} else {
|
||||
panic(panicVal)
|
||||
}
|
||||
}
|
||||
}()
|
||||
s, ok = err.Error(), true
|
||||
return
|
||||
}
|
||||
|
||||
func safeString(str fmt.Stringer) (s string, ok bool) {
|
||||
defer func() {
|
||||
if panicVal := recover(); panicVal != nil {
|
||||
if v := reflect.ValueOf(str); v.Kind() == reflect.Ptr && v.IsNil() {
|
||||
s, ok = "null", false
|
||||
} else {
|
||||
panic(panicVal)
|
||||
}
|
||||
}
|
||||
}()
|
||||
s, ok = str.String(), true
|
||||
return
|
||||
}
|
||||
|
||||
func safeMarshal(tm encoding.TextMarshaler) (b []byte, err error) {
|
||||
defer func() {
|
||||
if panicVal := recover(); panicVal != nil {
|
||||
if v := reflect.ValueOf(tm); v.Kind() == reflect.Ptr && v.IsNil() {
|
||||
b, err = nil, nil
|
||||
} else {
|
||||
panic(panicVal)
|
||||
}
|
||||
}
|
||||
}()
|
||||
b, err = tm.MarshalText()
|
||||
if err != nil {
|
||||
return nil, &MarshalerError{
|
||||
Type: reflect.TypeOf(tm),
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
233
vendor/github.com/go-logfmt/logfmt/encode_internal_test.go
generated
vendored
Normal file
233
vendor/github.com/go-logfmt/logfmt/encode_internal_test.go
generated
vendored
Normal file
@@ -0,0 +1,233 @@
|
||||
package logfmt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSafeString(t *testing.T) {
|
||||
_, ok := safeString((*stringStringer)(nil))
|
||||
if got, want := ok, false; got != want {
|
||||
t.Errorf(" got %v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSafeMarshal(t *testing.T) {
|
||||
kb, err := safeMarshal((*stringMarshaler)(nil))
|
||||
if got := kb; got != nil {
|
||||
t.Errorf(" got %v, want nil", got)
|
||||
}
|
||||
if got, want := err, error(nil); got != want {
|
||||
t.Errorf(" got %v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteKeyStrings(t *testing.T) {
|
||||
keygen := []func(string) interface{}{
|
||||
func(s string) interface{} { return s },
|
||||
func(s string) interface{} { return stringData(s) },
|
||||
func(s string) interface{} { return stringStringer(s) },
|
||||
func(s string) interface{} { return stringMarshaler(s) },
|
||||
}
|
||||
|
||||
data := []struct {
|
||||
key string
|
||||
want string
|
||||
err error
|
||||
}{
|
||||
{key: "k", want: "k"},
|
||||
{key: `\`, want: `\`},
|
||||
{key: "\n", err: ErrInvalidKey},
|
||||
{key: "\x00", err: ErrInvalidKey},
|
||||
{key: "\x10", err: ErrInvalidKey},
|
||||
{key: "\x1F", err: ErrInvalidKey},
|
||||
{key: "", err: ErrInvalidKey},
|
||||
{key: " ", err: ErrInvalidKey},
|
||||
{key: "=", err: ErrInvalidKey},
|
||||
{key: `"`, err: ErrInvalidKey},
|
||||
}
|
||||
|
||||
for _, g := range keygen {
|
||||
for _, d := range data {
|
||||
w := &bytes.Buffer{}
|
||||
key := g(d.key)
|
||||
err := writeKey(w, key)
|
||||
if err != d.err {
|
||||
t.Errorf("%#v (%[1]T): got error: %v, want error: %v", key, err, d.err)
|
||||
}
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if got, want := w.String(), d.want; got != want {
|
||||
t.Errorf("%#v (%[1]T): got '%s', want '%s'", key, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteKey(t *testing.T) {
|
||||
var (
|
||||
nilPtr *int
|
||||
one = 1
|
||||
ptr = &one
|
||||
)
|
||||
|
||||
data := []struct {
|
||||
key interface{}
|
||||
want string
|
||||
err error
|
||||
}{
|
||||
{key: nil, err: ErrNilKey},
|
||||
{key: nilPtr, err: ErrNilKey},
|
||||
{key: (*stringStringer)(nil), err: ErrNilKey},
|
||||
{key: (*stringMarshaler)(nil), err: ErrNilKey},
|
||||
{key: (*stringerMarshaler)(nil), err: ErrNilKey},
|
||||
{key: ptr, want: "1"},
|
||||
|
||||
{key: errorMarshaler{}, err: &MarshalerError{Type: reflect.TypeOf(errorMarshaler{}), Err: errMarshaling}},
|
||||
{key: make(chan int), err: ErrUnsupportedKeyType},
|
||||
{key: []int{}, err: ErrUnsupportedKeyType},
|
||||
{key: map[int]int{}, err: ErrUnsupportedKeyType},
|
||||
{key: [2]int{}, err: ErrUnsupportedKeyType},
|
||||
{key: struct{}{}, err: ErrUnsupportedKeyType},
|
||||
{key: fmt.Sprint, err: ErrUnsupportedKeyType},
|
||||
}
|
||||
|
||||
for _, d := range data {
|
||||
w := &bytes.Buffer{}
|
||||
err := writeKey(w, d.key)
|
||||
if !reflect.DeepEqual(err, d.err) {
|
||||
t.Errorf("%#v: got error: %v, want error: %v", d.key, err, d.err)
|
||||
}
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if got, want := w.String(), d.want; got != want {
|
||||
t.Errorf("%#v: got '%s', want '%s'", d.key, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteValueStrings(t *testing.T) {
|
||||
keygen := []func(string) interface{}{
|
||||
func(s string) interface{} { return s },
|
||||
func(s string) interface{} { return errors.New(s) },
|
||||
func(s string) interface{} { return stringData(s) },
|
||||
func(s string) interface{} { return stringStringer(s) },
|
||||
func(s string) interface{} { return stringMarshaler(s) },
|
||||
}
|
||||
|
||||
data := []struct {
|
||||
value string
|
||||
want string
|
||||
err error
|
||||
}{
|
||||
{value: "", want: ""},
|
||||
{value: "v", want: "v"},
|
||||
{value: " ", want: `" "`},
|
||||
{value: "=", want: `"="`},
|
||||
{value: `\`, want: `\`},
|
||||
{value: `"`, want: `"\""`},
|
||||
{value: `\"`, want: `"\\\""`},
|
||||
{value: "\n", want: `"\n"`},
|
||||
{value: "\x00", want: `"\u0000"`},
|
||||
{value: "\x10", want: `"\u0010"`},
|
||||
{value: "\x1F", want: `"\u001f"`},
|
||||
{value: "µ", want: `µ`},
|
||||
}
|
||||
|
||||
for _, g := range keygen {
|
||||
for _, d := range data {
|
||||
w := &bytes.Buffer{}
|
||||
value := g(d.value)
|
||||
err := writeValue(w, value)
|
||||
if err != d.err {
|
||||
t.Errorf("%#v (%[1]T): got error: %v, want error: %v", value, err, d.err)
|
||||
}
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if got, want := w.String(), d.want; got != want {
|
||||
t.Errorf("%#v (%[1]T): got '%s', want '%s'", value, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteValue(t *testing.T) {
|
||||
var (
|
||||
nilPtr *int
|
||||
one = 1
|
||||
ptr = &one
|
||||
)
|
||||
|
||||
data := []struct {
|
||||
value interface{}
|
||||
want string
|
||||
err error
|
||||
}{
|
||||
{value: nil, want: "null"},
|
||||
{value: nilPtr, want: "null"},
|
||||
{value: (*stringStringer)(nil), want: "null"},
|
||||
{value: (*stringMarshaler)(nil), want: "null"},
|
||||
{value: (*stringerMarshaler)(nil), want: "null"},
|
||||
{value: ptr, want: "1"},
|
||||
|
||||
{value: errorMarshaler{}, err: &MarshalerError{Type: reflect.TypeOf(errorMarshaler{}), Err: errMarshaling}},
|
||||
{value: make(chan int), err: ErrUnsupportedValueType},
|
||||
{value: []int{}, err: ErrUnsupportedValueType},
|
||||
{value: map[int]int{}, err: ErrUnsupportedValueType},
|
||||
{value: [2]int{}, err: ErrUnsupportedValueType},
|
||||
{value: struct{}{}, err: ErrUnsupportedValueType},
|
||||
{value: fmt.Sprint, err: ErrUnsupportedValueType},
|
||||
}
|
||||
|
||||
for _, d := range data {
|
||||
w := &bytes.Buffer{}
|
||||
err := writeValue(w, d.value)
|
||||
if !reflect.DeepEqual(err, d.err) {
|
||||
t.Errorf("%#v: got error: %v, want error: %v", d.value, err, d.err)
|
||||
}
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if got, want := w.String(), d.want; got != want {
|
||||
t.Errorf("%#v: got '%s', want '%s'", d.value, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type stringData string
|
||||
|
||||
type stringStringer string
|
||||
|
||||
func (s stringStringer) String() string {
|
||||
return string(s)
|
||||
}
|
||||
|
||||
type stringMarshaler string
|
||||
|
||||
func (s stringMarshaler) MarshalText() ([]byte, error) {
|
||||
return []byte(s), nil
|
||||
}
|
||||
|
||||
type stringerMarshaler string
|
||||
|
||||
func (s stringerMarshaler) String() string {
|
||||
return "String() called"
|
||||
}
|
||||
|
||||
func (s stringerMarshaler) MarshalText() ([]byte, error) {
|
||||
return []byte(s), nil
|
||||
}
|
||||
|
||||
var errMarshaling = errors.New("marshal error")
|
||||
|
||||
type errorMarshaler struct{}
|
||||
|
||||
func (errorMarshaler) MarshalText() ([]byte, error) {
|
||||
return nil, errMarshaling
|
||||
}
|
206
vendor/github.com/go-logfmt/logfmt/encode_test.go
generated
vendored
Normal file
206
vendor/github.com/go-logfmt/logfmt/encode_test.go
generated
vendored
Normal file
@@ -0,0 +1,206 @@
|
||||
package logfmt_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-logfmt/logfmt"
|
||||
)
|
||||
|
||||
func TestEncodeKeyval(t *testing.T) {
|
||||
data := []struct {
|
||||
key, value interface{}
|
||||
want string
|
||||
err error
|
||||
}{
|
||||
{key: "k", value: "v", want: "k=v"},
|
||||
{key: "k", value: nil, want: "k=null"},
|
||||
{key: `\`, value: "v", want: `\=v`},
|
||||
{key: "k", value: "", want: "k="},
|
||||
{key: "k", value: "null", want: `k="null"`},
|
||||
{key: "k", value: "<nil>", want: `k=<nil>`},
|
||||
{key: "k", value: true, want: "k=true"},
|
||||
{key: "k", value: 1, want: "k=1"},
|
||||
{key: "k", value: 1.025, want: "k=1.025"},
|
||||
{key: "k", value: 1e-3, want: "k=0.001"},
|
||||
{key: "k", value: 3.5 + 2i, want: "k=(3.5+2i)"},
|
||||
{key: "k", value: "v v", want: `k="v v"`},
|
||||
{key: "k", value: " ", want: `k=" "`},
|
||||
{key: "k", value: `"`, want: `k="\""`},
|
||||
{key: "k", value: `=`, want: `k="="`},
|
||||
{key: "k", value: `\`, want: `k=\`},
|
||||
{key: "k", value: `=\`, want: `k="=\\"`},
|
||||
{key: "k", value: `\"`, want: `k="\\\""`},
|
||||
{key: "k", value: [2]int{2, 19}, err: logfmt.ErrUnsupportedValueType},
|
||||
{key: "k", value: []string{"e1", "e 2"}, err: logfmt.ErrUnsupportedValueType},
|
||||
{key: "k", value: structData{"a a", 9}, err: logfmt.ErrUnsupportedValueType},
|
||||
{key: "k", value: decimalMarshaler{5, 9}, want: "k=5.9"},
|
||||
{key: "k", value: (*decimalMarshaler)(nil), want: "k=null"},
|
||||
{key: "k", value: decimalStringer{5, 9}, want: "k=5.9"},
|
||||
{key: "k", value: (*decimalStringer)(nil), want: "k=null"},
|
||||
{key: "k", value: marshalerStringer{5, 9}, want: "k=5.9"},
|
||||
{key: "k", value: (*marshalerStringer)(nil), want: "k=null"},
|
||||
{key: "k", value: new(nilMarshaler), want: "k=notnilmarshaler"},
|
||||
{key: "k", value: (*nilMarshaler)(nil), want: "k=nilmarshaler"},
|
||||
{key: (*marshalerStringer)(nil), value: "v", err: logfmt.ErrNilKey},
|
||||
{key: decimalMarshaler{5, 9}, value: "v", want: "5.9=v"},
|
||||
{key: (*decimalMarshaler)(nil), value: "v", err: logfmt.ErrNilKey},
|
||||
{key: decimalStringer{5, 9}, value: "v", want: "5.9=v"},
|
||||
{key: (*decimalStringer)(nil), value: "v", err: logfmt.ErrNilKey},
|
||||
{key: marshalerStringer{5, 9}, value: "v", want: "5.9=v"},
|
||||
{key: "k", value: "\xbd", want: `k="\ufffd"`},
|
||||
{key: "k", value: "\ufffd\x00", want: `k="\ufffd\u0000"`},
|
||||
{key: "k", value: "\ufffd", want: `k="\ufffd"`},
|
||||
{key: "k", value: []byte("\ufffd\x00"), want: `k="\ufffd\u0000"`},
|
||||
{key: "k", value: []byte("\ufffd"), want: `k="\ufffd"`},
|
||||
}
|
||||
|
||||
for _, d := range data {
|
||||
w := &bytes.Buffer{}
|
||||
enc := logfmt.NewEncoder(w)
|
||||
err := enc.EncodeKeyval(d.key, d.value)
|
||||
if err != d.err {
|
||||
t.Errorf("%#v, %#v: got error: %v, want error: %v", d.key, d.value, err, d.err)
|
||||
}
|
||||
if got, want := w.String(), d.want; got != want {
|
||||
t.Errorf("%#v, %#v: got '%s', want '%s'", d.key, d.value, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalKeyvals(t *testing.T) {
|
||||
one := 1
|
||||
ptr := &one
|
||||
nilPtr := (*int)(nil)
|
||||
|
||||
data := []struct {
|
||||
in []interface{}
|
||||
want []byte
|
||||
err error
|
||||
}{
|
||||
{in: nil, want: nil},
|
||||
{in: kv(), want: nil},
|
||||
{in: kv(nil, "v"), err: logfmt.ErrNilKey},
|
||||
{in: kv(nilPtr, "v"), err: logfmt.ErrNilKey},
|
||||
{in: kv("\ufffd"), err: logfmt.ErrInvalidKey},
|
||||
{in: kv("\xbd"), err: logfmt.ErrInvalidKey},
|
||||
{in: kv("k"), want: []byte("k=null")},
|
||||
{in: kv("k", nil), want: []byte("k=null")},
|
||||
{in: kv("k", ""), want: []byte("k=")},
|
||||
{in: kv("k", "null"), want: []byte(`k="null"`)},
|
||||
{in: kv("k", "v"), want: []byte("k=v")},
|
||||
{in: kv("k", true), want: []byte("k=true")},
|
||||
{in: kv("k", 1), want: []byte("k=1")},
|
||||
{in: kv("k", ptr), want: []byte("k=1")},
|
||||
{in: kv("k", nilPtr), want: []byte("k=null")},
|
||||
{in: kv("k", 1.025), want: []byte("k=1.025")},
|
||||
{in: kv("k", 1e-3), want: []byte("k=0.001")},
|
||||
{in: kv("k", "v v"), want: []byte(`k="v v"`)},
|
||||
{in: kv("k", `"`), want: []byte(`k="\""`)},
|
||||
{in: kv("k", `=`), want: []byte(`k="="`)},
|
||||
{in: kv("k", `\`), want: []byte(`k=\`)},
|
||||
{in: kv("k", `=\`), want: []byte(`k="=\\"`)},
|
||||
{in: kv("k", `\"`), want: []byte(`k="\\\""`)},
|
||||
{in: kv("k1", "v1", "k2", "v2"), want: []byte("k1=v1 k2=v2")},
|
||||
{in: kv("k1", "v1", "k2", [2]int{}), want: []byte("k1=v1 k2=\"unsupported value type\"")},
|
||||
{in: kv([2]int{}, "v1", "k2", "v2"), want: []byte("k2=v2")},
|
||||
{in: kv("k", time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)), want: []byte("k=2009-11-10T23:00:00Z")},
|
||||
{in: kv("k", errorMarshaler{}), want: []byte("k=\"error marshaling value of type logfmt_test.errorMarshaler: marshal error\"")},
|
||||
{in: kv("k", decimalMarshaler{5, 9}), want: []byte("k=5.9")},
|
||||
{in: kv("k", (*decimalMarshaler)(nil)), want: []byte("k=null")},
|
||||
{in: kv("k", decimalStringer{5, 9}), want: []byte("k=5.9")},
|
||||
{in: kv("k", (*decimalStringer)(nil)), want: []byte("k=null")},
|
||||
{in: kv("k", marshalerStringer{5, 9}), want: []byte("k=5.9")},
|
||||
{in: kv("k", (*marshalerStringer)(nil)), want: []byte("k=null")},
|
||||
{in: kv(one, "v"), want: []byte("1=v")},
|
||||
{in: kv(ptr, "v"), want: []byte("1=v")},
|
||||
{in: kv((*marshalerStringer)(nil), "v"), err: logfmt.ErrNilKey},
|
||||
{in: kv(decimalMarshaler{5, 9}, "v"), want: []byte("5.9=v")},
|
||||
{in: kv((*decimalMarshaler)(nil), "v"), err: logfmt.ErrNilKey},
|
||||
{in: kv(decimalStringer{5, 9}, "v"), want: []byte("5.9=v")},
|
||||
{in: kv((*decimalStringer)(nil), "v"), err: logfmt.ErrNilKey},
|
||||
{in: kv(marshalerStringer{5, 9}, "v"), want: []byte("5.9=v")},
|
||||
}
|
||||
|
||||
for _, d := range data {
|
||||
got, err := logfmt.MarshalKeyvals(d.in...)
|
||||
if err != d.err {
|
||||
t.Errorf("%#v: got error: %v, want error: %v", d.in, err, d.err)
|
||||
}
|
||||
if !reflect.DeepEqual(got, d.want) {
|
||||
t.Errorf("%#v: got '%s', want '%s'", d.in, got, d.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func kv(keyvals ...interface{}) []interface{} {
|
||||
return keyvals
|
||||
}
|
||||
|
||||
type structData struct {
|
||||
A string `logfmt:"fieldA"`
|
||||
B int
|
||||
}
|
||||
|
||||
type nilMarshaler int
|
||||
|
||||
func (m *nilMarshaler) MarshalText() ([]byte, error) {
|
||||
if m == nil {
|
||||
return []byte("nilmarshaler"), nil
|
||||
}
|
||||
return []byte("notnilmarshaler"), nil
|
||||
}
|
||||
|
||||
type decimalMarshaler struct {
|
||||
a, b int
|
||||
}
|
||||
|
||||
func (t decimalMarshaler) MarshalText() ([]byte, error) {
|
||||
buf := &bytes.Buffer{}
|
||||
fmt.Fprintf(buf, "%d.%d", t.a, t.b)
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
type decimalStringer struct {
|
||||
a, b int
|
||||
}
|
||||
|
||||
func (s decimalStringer) String() string {
|
||||
return fmt.Sprintf("%d.%d", s.a, s.b)
|
||||
}
|
||||
|
||||
type marshalerStringer struct {
|
||||
a, b int
|
||||
}
|
||||
|
||||
func (t marshalerStringer) MarshalText() ([]byte, error) {
|
||||
buf := &bytes.Buffer{}
|
||||
fmt.Fprintf(buf, "%d.%d", t.a, t.b)
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (t marshalerStringer) String() string {
|
||||
return fmt.Sprint(t.a + t.b)
|
||||
}
|
||||
|
||||
var errMarshal = errors.New("marshal error")
|
||||
|
||||
type errorMarshaler struct{}
|
||||
|
||||
func (errorMarshaler) MarshalText() ([]byte, error) {
|
||||
return nil, errMarshal
|
||||
}
|
||||
|
||||
func BenchmarkEncodeKeyval(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
enc := logfmt.NewEncoder(ioutil.Discard)
|
||||
for i := 0; i < b.N; i++ {
|
||||
enc.EncodeKeyval("sk", "10")
|
||||
enc.EncodeKeyval("some-key", "a rather long string with spaces")
|
||||
}
|
||||
}
|
60
vendor/github.com/go-logfmt/logfmt/example_test.go
generated
vendored
Normal file
60
vendor/github.com/go-logfmt/logfmt/example_test.go
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
package logfmt_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-logfmt/logfmt"
|
||||
)
|
||||
|
||||
func ExampleEncoder() {
|
||||
check := func(err error) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
e := logfmt.NewEncoder(os.Stdout)
|
||||
|
||||
check(e.EncodeKeyval("id", 1))
|
||||
check(e.EncodeKeyval("dur", time.Second+time.Millisecond))
|
||||
check(e.EndRecord())
|
||||
|
||||
check(e.EncodeKeyval("id", 1))
|
||||
check(e.EncodeKeyval("path", "/path/to/file"))
|
||||
check(e.EncodeKeyval("err", errors.New("file not found")))
|
||||
check(e.EndRecord())
|
||||
|
||||
// Output:
|
||||
// id=1 dur=1.001s
|
||||
// id=1 path=/path/to/file err="file not found"
|
||||
}
|
||||
|
||||
func ExampleDecoder() {
|
||||
in := `
|
||||
id=1 dur=1.001s
|
||||
id=1 path=/path/to/file err="file not found"
|
||||
`
|
||||
|
||||
d := logfmt.NewDecoder(strings.NewReader(in))
|
||||
for d.ScanRecord() {
|
||||
for d.ScanKeyval() {
|
||||
fmt.Printf("k: %s v: %s\n", d.Key(), d.Value())
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
if d.Err() != nil {
|
||||
panic(d.Err())
|
||||
}
|
||||
|
||||
// Output:
|
||||
// k: id v: 1
|
||||
// k: dur v: 1.001s
|
||||
//
|
||||
// k: id v: 1
|
||||
// k: path v: /path/to/file
|
||||
// k: err v: file not found
|
||||
}
|
126
vendor/github.com/go-logfmt/logfmt/fuzz.go
generated
vendored
Normal file
126
vendor/github.com/go-logfmt/logfmt/fuzz.go
generated
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
// +build gofuzz
|
||||
|
||||
package logfmt
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
|
||||
kr "github.com/kr/logfmt"
|
||||
)
|
||||
|
||||
// Fuzz checks reserialized data matches
|
||||
func Fuzz(data []byte) int {
|
||||
parsed, err := parse(data)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
var w1 bytes.Buffer
|
||||
if err = write(parsed, &w1); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
parsed, err = parse(w1.Bytes())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
var w2 bytes.Buffer
|
||||
if err = write(parsed, &w2); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if !bytes.Equal(w1.Bytes(), w2.Bytes()) {
|
||||
panic(fmt.Sprintf("reserialized data does not match:\n%q\n%q\n", w1.Bytes(), w2.Bytes()))
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
// FuzzVsKR checks go-logfmt/logfmt against kr/logfmt
|
||||
func FuzzVsKR(data []byte) int {
|
||||
parsed, err := parse(data)
|
||||
parsedKR, errKR := parseKR(data)
|
||||
|
||||
// github.com/go-logfmt/logfmt is a stricter parser. It returns errors for
|
||||
// more inputs than github.com/kr/logfmt. Ignore any inputs that have a
|
||||
// stict error.
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
|
||||
// Fail if the more forgiving parser finds an error not found by the
|
||||
// stricter parser.
|
||||
if errKR != nil {
|
||||
panic(fmt.Sprintf("unmatched error: %v", errKR))
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(parsed, parsedKR) {
|
||||
panic(fmt.Sprintf("parsers disagree:\n%+v\n%+v\n", parsed, parsedKR))
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
type kv struct {
|
||||
k, v []byte
|
||||
}
|
||||
|
||||
func parse(data []byte) ([][]kv, error) {
|
||||
var got [][]kv
|
||||
dec := NewDecoder(bytes.NewReader(data))
|
||||
for dec.ScanRecord() {
|
||||
var kvs []kv
|
||||
for dec.ScanKeyval() {
|
||||
kvs = append(kvs, kv{dec.Key(), dec.Value()})
|
||||
}
|
||||
got = append(got, kvs)
|
||||
}
|
||||
return got, dec.Err()
|
||||
}
|
||||
|
||||
func parseKR(data []byte) ([][]kv, error) {
|
||||
var (
|
||||
s = bufio.NewScanner(bytes.NewReader(data))
|
||||
err error
|
||||
h saveHandler
|
||||
got [][]kv
|
||||
)
|
||||
for err == nil && s.Scan() {
|
||||
h.kvs = nil
|
||||
err = kr.Unmarshal(s.Bytes(), &h)
|
||||
got = append(got, h.kvs)
|
||||
}
|
||||
if err == nil {
|
||||
err = s.Err()
|
||||
}
|
||||
return got, err
|
||||
}
|
||||
|
||||
type saveHandler struct {
|
||||
kvs []kv
|
||||
}
|
||||
|
||||
func (h *saveHandler) HandleLogfmt(key, val []byte) error {
|
||||
if len(key) == 0 {
|
||||
key = nil
|
||||
}
|
||||
if len(val) == 0 {
|
||||
val = nil
|
||||
}
|
||||
h.kvs = append(h.kvs, kv{key, val})
|
||||
return nil
|
||||
}
|
||||
|
||||
func write(recs [][]kv, w io.Writer) error {
|
||||
enc := NewEncoder(w)
|
||||
for _, rec := range recs {
|
||||
for _, f := range rec {
|
||||
if err := enc.EncodeKeyval(f.k, f.v); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := enc.EndRecord(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
277
vendor/github.com/go-logfmt/logfmt/jsonstring.go
generated
vendored
Normal file
277
vendor/github.com/go-logfmt/logfmt/jsonstring.go
generated
vendored
Normal file
@@ -0,0 +1,277 @@
|
||||
package logfmt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"strconv"
|
||||
"sync"
|
||||
"unicode"
|
||||
"unicode/utf16"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// Taken from Go's encoding/json and modified for use here.
|
||||
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
var hex = "0123456789abcdef"
|
||||
|
||||
var bufferPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &bytes.Buffer{}
|
||||
},
|
||||
}
|
||||
|
||||
func getBuffer() *bytes.Buffer {
|
||||
return bufferPool.Get().(*bytes.Buffer)
|
||||
}
|
||||
|
||||
func poolBuffer(buf *bytes.Buffer) {
|
||||
buf.Reset()
|
||||
bufferPool.Put(buf)
|
||||
}
|
||||
|
||||
// NOTE: keep in sync with writeQuotedBytes below.
|
||||
func writeQuotedString(w io.Writer, s string) (int, error) {
|
||||
buf := getBuffer()
|
||||
buf.WriteByte('"')
|
||||
start := 0
|
||||
for i := 0; i < len(s); {
|
||||
if b := s[i]; b < utf8.RuneSelf {
|
||||
if 0x20 <= b && b != '\\' && b != '"' {
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if start < i {
|
||||
buf.WriteString(s[start:i])
|
||||
}
|
||||
switch b {
|
||||
case '\\', '"':
|
||||
buf.WriteByte('\\')
|
||||
buf.WriteByte(b)
|
||||
case '\n':
|
||||
buf.WriteByte('\\')
|
||||
buf.WriteByte('n')
|
||||
case '\r':
|
||||
buf.WriteByte('\\')
|
||||
buf.WriteByte('r')
|
||||
case '\t':
|
||||
buf.WriteByte('\\')
|
||||
buf.WriteByte('t')
|
||||
default:
|
||||
// This encodes bytes < 0x20 except for \n, \r, and \t.
|
||||
buf.WriteString(`\u00`)
|
||||
buf.WriteByte(hex[b>>4])
|
||||
buf.WriteByte(hex[b&0xF])
|
||||
}
|
||||
i++
|
||||
start = i
|
||||
continue
|
||||
}
|
||||
c, size := utf8.DecodeRuneInString(s[i:])
|
||||
if c == utf8.RuneError {
|
||||
if start < i {
|
||||
buf.WriteString(s[start:i])
|
||||
}
|
||||
buf.WriteString(`\ufffd`)
|
||||
i += size
|
||||
start = i
|
||||
continue
|
||||
}
|
||||
i += size
|
||||
}
|
||||
if start < len(s) {
|
||||
buf.WriteString(s[start:])
|
||||
}
|
||||
buf.WriteByte('"')
|
||||
n, err := w.Write(buf.Bytes())
|
||||
poolBuffer(buf)
|
||||
return n, err
|
||||
}
|
||||
|
||||
// NOTE: keep in sync with writeQuoteString above.
|
||||
func writeQuotedBytes(w io.Writer, s []byte) (int, error) {
|
||||
buf := getBuffer()
|
||||
buf.WriteByte('"')
|
||||
start := 0
|
||||
for i := 0; i < len(s); {
|
||||
if b := s[i]; b < utf8.RuneSelf {
|
||||
if 0x20 <= b && b != '\\' && b != '"' {
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if start < i {
|
||||
buf.Write(s[start:i])
|
||||
}
|
||||
switch b {
|
||||
case '\\', '"':
|
||||
buf.WriteByte('\\')
|
||||
buf.WriteByte(b)
|
||||
case '\n':
|
||||
buf.WriteByte('\\')
|
||||
buf.WriteByte('n')
|
||||
case '\r':
|
||||
buf.WriteByte('\\')
|
||||
buf.WriteByte('r')
|
||||
case '\t':
|
||||
buf.WriteByte('\\')
|
||||
buf.WriteByte('t')
|
||||
default:
|
||||
// This encodes bytes < 0x20 except for \n, \r, and \t.
|
||||
buf.WriteString(`\u00`)
|
||||
buf.WriteByte(hex[b>>4])
|
||||
buf.WriteByte(hex[b&0xF])
|
||||
}
|
||||
i++
|
||||
start = i
|
||||
continue
|
||||
}
|
||||
c, size := utf8.DecodeRune(s[i:])
|
||||
if c == utf8.RuneError {
|
||||
if start < i {
|
||||
buf.Write(s[start:i])
|
||||
}
|
||||
buf.WriteString(`\ufffd`)
|
||||
i += size
|
||||
start = i
|
||||
continue
|
||||
}
|
||||
i += size
|
||||
}
|
||||
if start < len(s) {
|
||||
buf.Write(s[start:])
|
||||
}
|
||||
buf.WriteByte('"')
|
||||
n, err := w.Write(buf.Bytes())
|
||||
poolBuffer(buf)
|
||||
return n, err
|
||||
}
|
||||
|
||||
// getu4 decodes \uXXXX from the beginning of s, returning the hex value,
|
||||
// or it returns -1.
|
||||
func getu4(s []byte) rune {
|
||||
if len(s) < 6 || s[0] != '\\' || s[1] != 'u' {
|
||||
return -1
|
||||
}
|
||||
r, err := strconv.ParseUint(string(s[2:6]), 16, 64)
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
return rune(r)
|
||||
}
|
||||
|
||||
func unquoteBytes(s []byte) (t []byte, ok bool) {
|
||||
if len(s) < 2 || s[0] != '"' || s[len(s)-1] != '"' {
|
||||
return
|
||||
}
|
||||
s = s[1 : len(s)-1]
|
||||
|
||||
// Check for unusual characters. If there are none,
|
||||
// then no unquoting is needed, so return a slice of the
|
||||
// original bytes.
|
||||
r := 0
|
||||
for r < len(s) {
|
||||
c := s[r]
|
||||
if c == '\\' || c == '"' || c < ' ' {
|
||||
break
|
||||
}
|
||||
if c < utf8.RuneSelf {
|
||||
r++
|
||||
continue
|
||||
}
|
||||
rr, size := utf8.DecodeRune(s[r:])
|
||||
if rr == utf8.RuneError {
|
||||
break
|
||||
}
|
||||
r += size
|
||||
}
|
||||
if r == len(s) {
|
||||
return s, true
|
||||
}
|
||||
|
||||
b := make([]byte, len(s)+2*utf8.UTFMax)
|
||||
w := copy(b, s[0:r])
|
||||
for r < len(s) {
|
||||
// Out of room? Can only happen if s is full of
|
||||
// malformed UTF-8 and we're replacing each
|
||||
// byte with RuneError.
|
||||
if w >= len(b)-2*utf8.UTFMax {
|
||||
nb := make([]byte, (len(b)+utf8.UTFMax)*2)
|
||||
copy(nb, b[0:w])
|
||||
b = nb
|
||||
}
|
||||
switch c := s[r]; {
|
||||
case c == '\\':
|
||||
r++
|
||||
if r >= len(s) {
|
||||
return
|
||||
}
|
||||
switch s[r] {
|
||||
default:
|
||||
return
|
||||
case '"', '\\', '/', '\'':
|
||||
b[w] = s[r]
|
||||
r++
|
||||
w++
|
||||
case 'b':
|
||||
b[w] = '\b'
|
||||
r++
|
||||
w++
|
||||
case 'f':
|
||||
b[w] = '\f'
|
||||
r++
|
||||
w++
|
||||
case 'n':
|
||||
b[w] = '\n'
|
||||
r++
|
||||
w++
|
||||
case 'r':
|
||||
b[w] = '\r'
|
||||
r++
|
||||
w++
|
||||
case 't':
|
||||
b[w] = '\t'
|
||||
r++
|
||||
w++
|
||||
case 'u':
|
||||
r--
|
||||
rr := getu4(s[r:])
|
||||
if rr < 0 {
|
||||
return
|
||||
}
|
||||
r += 6
|
||||
if utf16.IsSurrogate(rr) {
|
||||
rr1 := getu4(s[r:])
|
||||
if dec := utf16.DecodeRune(rr, rr1); dec != unicode.ReplacementChar {
|
||||
// A valid pair; consume.
|
||||
r += 6
|
||||
w += utf8.EncodeRune(b[w:], dec)
|
||||
break
|
||||
}
|
||||
// Invalid surrogate; fall back to replacement rune.
|
||||
rr = unicode.ReplacementChar
|
||||
}
|
||||
w += utf8.EncodeRune(b[w:], rr)
|
||||
}
|
||||
|
||||
// Quote, control characters are invalid.
|
||||
case c == '"', c < ' ':
|
||||
return
|
||||
|
||||
// ASCII
|
||||
case c < utf8.RuneSelf:
|
||||
b[w] = c
|
||||
r++
|
||||
w++
|
||||
|
||||
// Coerce to well-formed UTF-8.
|
||||
default:
|
||||
rr, size := utf8.DecodeRune(s[r:])
|
||||
r += size
|
||||
w += utf8.EncodeRune(b[w:], rr)
|
||||
}
|
||||
}
|
||||
return b[0:w], true
|
||||
}
|
Reference in New Issue
Block a user