From fe0e5f3f4150c3a747299c187a47e10d09dea7de Mon Sep 17 00:00:00 2001 From: Chris Hines Date: Fri, 13 May 2016 21:43:48 -0400 Subject: [PATCH 01/12] Add TravisCI builds. --- .travis.yml | 11 +++++++++++ README.md | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..05a926b --- /dev/null +++ b/.travis.yml @@ -0,0 +1,11 @@ +language: go + +go: + - 1.0 + - 1.1 + - 1.2 + - 1.3 + - 1.4 + - 1.5 + - release + - tip diff --git a/README.md b/README.md index 0289eea..a8cfc08 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![GoDoc](https://godoc.org/github.com/go-logfmt/logfmt?status.svg)](https://godoc.org/github.com/go-logfmt/logfmt) [![Go Report Card](https://goreportcard.com/badge/go-logfmt/logfmt)](https://goreportcard.com/report/go-logfmt/logfmt) +[![GoDoc](https://godoc.org/github.com/go-logfmt/logfmt?status.svg)](https://godoc.org/github.com/go-logfmt/logfmt) [![Go Report Card](https://goreportcard.com/badge/go-logfmt/logfmt)](https://goreportcard.com/report/go-logfmt/logfmt) [![TravisCI](https://travis-ci.org/go-logfmt/logfmt.svg?branch=master)](https://travis-ci.org/go-logfmt/logfmt) # logfmt From 10c314f1e1531dcf5f5c045eeb6fbfc1dbbdb2b0 Mon Sep 17 00:00:00 2001 From: Chris Hines Date: Fri, 13 May 2016 22:36:35 -0400 Subject: [PATCH 02/12] Collect test coverage from TravisCI. --- .travis.yml | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 05a926b..967bd0e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,11 +1,16 @@ language: go go: - - 1.0 - - 1.1 - - 1.2 - - 1.3 - - 1.4 - - 1.5 - - release - - tip + - 1.2 + - 1.3 + - 1.4 + - 1.5 + - release + - tip + +before_install: + - go get github.com/mattn/goveralls + - go get golang.org/x/tools/cmd/cover + +script: + - $HOME/gopath/bin/goveralls -service=travis-ci From 66fa38bc44ab8318248d8dd0841af9c0096390cd Mon Sep 17 00:00:00 2001 From: Chris Hines Date: Fri, 13 May 2016 22:49:14 -0400 Subject: [PATCH 03/12] Disable sudo and get test dependencies. --- .travis.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 967bd0e..8c50d75 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,5 @@ language: go - +sudo: false go: - 1.2 - 1.3 @@ -12,5 +12,8 @@ before_install: - go get github.com/mattn/goveralls - go get golang.org/x/tools/cmd/cover +before_script: + - go get -v -t ./... + script: - - $HOME/gopath/bin/goveralls -service=travis-ci + - goveralls -service=travis-ci From caee5a07e3a8be5d068336ff58bc930f24961fed Mon Sep 17 00:00:00 2001 From: Chris Hines Date: Fri, 13 May 2016 22:54:55 -0400 Subject: [PATCH 04/12] Remove redundant build step. --- .travis.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8c50d75..7eab35f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,8 +12,5 @@ before_install: - go get github.com/mattn/goveralls - go get golang.org/x/tools/cmd/cover -before_script: - - go get -v -t ./... - script: - goveralls -service=travis-ci From c9a61625f544a6719331dc9e7e887748879a4741 Mon Sep 17 00:00:00 2001 From: Chris Hines Date: Fri, 13 May 2016 23:04:30 -0400 Subject: [PATCH 05/12] Use explicit go version in .travis.yml. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 7eab35f..d5e5dd5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ go: - 1.3 - 1.4 - 1.5 - - release + - 1.6 - tip before_install: From 538518f3a61c040f3d15f05decfffef6bd0302bc Mon Sep 17 00:00:00 2001 From: Chris Hines Date: Fri, 13 May 2016 23:16:40 -0400 Subject: [PATCH 06/12] Add coveralls badge. --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a8cfc08..3a8f10b 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,7 @@ -[![GoDoc](https://godoc.org/github.com/go-logfmt/logfmt?status.svg)](https://godoc.org/github.com/go-logfmt/logfmt) [![Go Report Card](https://goreportcard.com/badge/go-logfmt/logfmt)](https://goreportcard.com/report/go-logfmt/logfmt) [![TravisCI](https://travis-ci.org/go-logfmt/logfmt.svg?branch=master)](https://travis-ci.org/go-logfmt/logfmt) +[![GoDoc](https://godoc.org/github.com/go-logfmt/logfmt?status.svg)](https://godoc.org/github.com/go-logfmt/logfmt) +[![Go Report Card](https://goreportcard.com/badge/go-logfmt/logfmt)](https://goreportcard.com/report/go-logfmt/logfmt) +[![TravisCI](https://travis-ci.org/go-logfmt/logfmt.svg?branch=master)](https://travis-ci.org/go-logfmt/logfmt) +[![Coverage Status](https://coveralls.io/repos/github/go-logfmt/logfmt/badge.svg?branch=master)](https://coveralls.io/github/go-logfmt/logfmt?branch=master) # logfmt From 389ef9af36d2128e97a8ac721518ae3946e6d592 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Justin=20Nu=C3=9F?= Date: Wed, 1 Jun 2016 13:44:05 +0200 Subject: [PATCH 07/12] Pool buffers for quoted strings and byte slices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reduces allocations when writing multiple quoted strings or byte slices, by reusing bytes.Buffers, giving a nice little speed up. name old time/op new time/op delta EncodeKeyval-8 635ns ± 2% 593ns ± 0% -6.52% (p=0.008 n=5+5) name old alloc/op new alloc/op delta EncodeKeyval-8 176B ± 0% 64B ± 0% -63.64% (p=0.008 n=5+5) name old allocs/op new allocs/op delta EncodeKeyval-8 5.00 ± 0% 4.00 ± 0% -20.00% (p=0.008 n=5+5) --- jsonstring.go | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/jsonstring.go b/jsonstring.go index 6c75dc4..53b6532 100644 --- a/jsonstring.go +++ b/jsonstring.go @@ -4,6 +4,7 @@ import ( "bytes" "io" "strconv" + "sync" "unicode" "unicode/utf16" "unicode/utf8" @@ -17,9 +18,24 @@ import ( 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 := &bytes.Buffer{} + buf := getBuffer() buf.WriteByte('"') start := 0 for i := 0; i < len(s); { @@ -70,12 +86,14 @@ func writeQuotedString(w io.Writer, s string) (int, error) { buf.WriteString(s[start:]) } buf.WriteByte('"') - return w.Write(buf.Bytes()) + 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 := &bytes.Buffer{} + buf := getBuffer() buf.WriteByte('"') start := 0 for i := 0; i < len(s); { @@ -126,7 +144,9 @@ func writeQuotedBytes(w io.Writer, s []byte) (int, error) { buf.Write(s[start:]) } buf.WriteByte('"') - return w.Write(buf.Bytes()) + n, err := w.Write(buf.Bytes()) + poolBuffer(buf) + return n, err } // getu4 decodes \uXXXX from the beginning of s, returning the hex value, From 8e1123714b49f347536dfccd47b3663e67e897e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Justin=20Nu=C3=9F?= Date: Wed, 1 Jun 2016 14:50:22 +0200 Subject: [PATCH 08/12] Remove Go 1.2 from .travis.yml --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d5e5dd5..b599f65 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,6 @@ language: go sudo: false go: - - 1.2 - 1.3 - 1.4 - 1.5 From 9b7959fc8e0fafe2de3125b414d3dbc9118e359c Mon Sep 17 00:00:00 2001 From: Jud White Date: Mon, 14 Nov 2016 00:55:42 -0600 Subject: [PATCH 09/12] golint cleanup --- encode_test.go | 8 ++++---- fuzz.go | 6 ++++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/encode_test.go b/encode_test.go index c7aa1d5..094cb80 100644 --- a/encode_test.go +++ b/encode_test.go @@ -125,8 +125,8 @@ func TestMarshalKeyvals(t *testing.T) { if err != d.err { t.Errorf("%#v: got error: %v, want error: %v", d.in, err, d.err) } - if got, want := got, d.want; !reflect.DeepEqual(got, want) { - t.Errorf("%#v: got '%s', want '%s'", d.in, got, want) + if !reflect.DeepEqual(got, d.want) { + t.Errorf("%#v: got '%s', want '%s'", d.in, got, d.want) } } } @@ -181,12 +181,12 @@ func (t marshalerStringer) String() string { return fmt.Sprint(t.a + t.b) } -var marshalError = errors.New("marshal error") +var errMarshal = errors.New("marshal error") type errorMarshaler struct{} func (errorMarshaler) MarshalText() ([]byte, error) { - return nil, marshalError + return nil, errMarshal } func BenchmarkEncodeKeyval(b *testing.B) { diff --git a/fuzz.go b/fuzz.go index ab916a9..46a590c 100644 --- a/fuzz.go +++ b/fuzz.go @@ -12,13 +12,14 @@ import ( 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 { + if err = write(parsed, &w1); err != nil { panic(err) } parsed, err = parse(data) @@ -26,7 +27,7 @@ func Fuzz(data []byte) int { panic(err) } var w2 bytes.Buffer - if err := write(parsed, &w2); err != nil { + if err = write(parsed, &w2); err != nil { panic(err) } if !bytes.Equal(w1.Bytes(), w2.Bytes()) { @@ -35,6 +36,7 @@ func Fuzz(data []byte) int { return 1 } +// FuzzVsKR checks go-logfmt/logfmt against kr/logfmt func FuzzVsKR(data []byte) int { parsed, err := parse(data) parsedKR, errKR := parseKR(data) From 282c134fb5ac3ba3ec471570b8f7e97f0e0d108c Mon Sep 17 00:00:00 2001 From: Jud White Date: Mon, 14 Nov 2016 00:04:24 -0600 Subject: [PATCH 10/12] add failing tests --- decode_test.go | 2 ++ encode_test.go | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/decode_test.go b/decode_test.go index d261880..8cdc384 100644 --- a/decode_test.go +++ b/decode_test.go @@ -118,6 +118,8 @@ func TestDecoder_errors(t *testing.T) { {"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}}, } for _, test := range tests { diff --git a/encode_test.go b/encode_test.go index 094cb80..877e6e4 100644 --- a/encode_test.go +++ b/encode_test.go @@ -53,6 +53,11 @@ func TestEncodeKeyval(t *testing.T) { {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 { @@ -82,6 +87,8 @@ func TestMarshalKeyvals(t *testing.T) { {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=")}, From 52e29b14dbd438cd277b869ebeccccf8c8912951 Mon Sep 17 00:00:00 2001 From: Jud White Date: Mon, 14 Nov 2016 00:34:34 -0600 Subject: [PATCH 11/12] fuzz: - for parse/write/parse check, change second parse to parse the write of the parsed value instead of the original input MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit encode: - invalidKeyRune: treat utf8.RuneError as invalid rune - add invalidKey, invalidKeyString funcs - checks len(key) == 0, invalidKeyRune, and utf8.Valid - needsQuotedValueRune: if value contains utf8.RuneError quote the value. a literal k=\ufffd encodes to k=\"\\ufffd\" - writeStringValue, writeBytesValue: quote any invalid utf8 string - the above two changes fix the "reserialized data does not match" error found during fuzz testing decode: - reject invalidKey as parse error jsonstring: - remove "&& size == 1" when checking for rune decode error fuzz testing output: - .quoted: "0=\"\xbd\x00\"" - .output: panic: reserialized data does not match: "0=\"\\ufffd\\u0000\"\n" "0=\"�\\u0000\"\n" --- decode.go | 14 ++++++++++++++ encode.go | 21 +++++++++++++++------ fuzz.go | 2 +- jsonstring.go | 6 +++--- 4 files changed, 33 insertions(+), 10 deletions(-) diff --git a/decode.go b/decode.go index a04981f..d8d3991 100644 --- a/decode.go +++ b/decode.go @@ -68,6 +68,8 @@ func (dec *Decoder) ScanKeyval() bool { return false key: + const invalidKeyError = "invalid key" + start := dec.pos for p, c := range line[dec.pos:] { switch { @@ -75,6 +77,10 @@ key: dec.pos += p if dec.pos > start { dec.key = line[start:dec.pos] + if invalidKey(dec.key) { + dec.syntaxError(invalidKeyError) + return false + } } if dec.key == nil { dec.unexpectedByte(c) @@ -89,6 +95,10 @@ key: dec.pos += p if dec.pos > start { dec.key = line[start:dec.pos] + if invalidKey(dec.key) { + dec.syntaxError(invalidKeyError) + return false + } } return true } @@ -96,6 +106,10 @@ key: dec.pos = len(line) if dec.pos > start { dec.key = line[start:dec.pos] + if invalidKey(dec.key) { + dec.syntaxError(invalidKeyError) + return false + } } return true diff --git a/encode.go b/encode.go index 4d0fa23..aa45ee0 100644 --- a/encode.go +++ b/encode.go @@ -8,6 +8,7 @@ import ( "io" "reflect" "strings" + "unicode/utf8" ) // MarshalKeyvals returns the logfmt encoding of keyvals, a variadic sequence @@ -165,11 +166,19 @@ func writeKey(w io.Writer, key interface{}) error { } func invalidKeyRune(r rune) bool { - return r <= ' ' || r == '=' || r == '"' + return r <= ' ' || r == '=' || r == '"' || r == utf8.RuneError +} + +func invalidKeyString(key string) bool { + return len(key) == 0 || strings.IndexFunc(key, invalidKeyRune) != -1 || !utf8.ValidString(key) +} + +func invalidKey(key []byte) bool { + return len(key) == 0 || bytes.IndexFunc(key, invalidKeyRune) != -1 || !utf8.Valid(key) } func writeStringKey(w io.Writer, key string) error { - if len(key) == 0 || strings.IndexFunc(key, invalidKeyRune) != -1 { + if invalidKeyString(key) { return ErrInvalidKey } _, err := io.WriteString(w, key) @@ -177,7 +186,7 @@ func writeStringKey(w io.Writer, key string) error { } func writeBytesKey(w io.Writer, key []byte) error { - if len(key) == 0 || bytes.IndexFunc(key, invalidKeyRune) != -1 { + if invalidKey(key) { return ErrInvalidKey } _, err := w.Write(key) @@ -223,14 +232,14 @@ func writeValue(w io.Writer, value interface{}) error { } func needsQuotedValueRune(r rune) bool { - return r <= ' ' || r == '=' || r == '"' + 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 { + } else if strings.IndexFunc(value, needsQuotedValueRune) != -1 || !utf8.ValidString(value) { _, err = writeQuotedString(w, value) } else { _, err = io.WriteString(w, value) @@ -240,7 +249,7 @@ func writeStringValue(w io.Writer, value string, ok bool) error { func writeBytesValue(w io.Writer, value []byte) error { var err error - if bytes.IndexFunc(value, needsQuotedValueRune) >= 0 { + if bytes.IndexFunc(value, needsQuotedValueRune) >= 0 || !utf8.Valid(value) { _, err = writeQuotedBytes(w, value) } else { _, err = w.Write(value) diff --git a/fuzz.go b/fuzz.go index 46a590c..5e93bf1 100644 --- a/fuzz.go +++ b/fuzz.go @@ -22,7 +22,7 @@ func Fuzz(data []byte) int { if err = write(parsed, &w1); err != nil { panic(err) } - parsed, err = parse(data) + parsed, err = parse(w1.Bytes()) if err != nil { panic(err) } diff --git a/jsonstring.go b/jsonstring.go index 53b6532..030ac85 100644 --- a/jsonstring.go +++ b/jsonstring.go @@ -71,7 +71,7 @@ func writeQuotedString(w io.Writer, s string) (int, error) { continue } c, size := utf8.DecodeRuneInString(s[i:]) - if c == utf8.RuneError && size == 1 { + if c == utf8.RuneError { if start < i { buf.WriteString(s[start:i]) } @@ -129,7 +129,7 @@ func writeQuotedBytes(w io.Writer, s []byte) (int, error) { continue } c, size := utf8.DecodeRune(s[i:]) - if c == utf8.RuneError && size == 1 { + if c == utf8.RuneError { if start < i { buf.Write(s[start:i]) } @@ -182,7 +182,7 @@ func unquoteBytes(s []byte) (t []byte, ok bool) { continue } rr, size := utf8.DecodeRune(s[r:]) - if rr == utf8.RuneError && size == 1 { + if rr == utf8.RuneError { break } r += size From a9a71ce80e1282dc9f85b690e1ede891b96716da Mon Sep 17 00:00:00 2001 From: Jud White Date: Tue, 15 Nov 2016 04:34:42 -0600 Subject: [PATCH 12/12] apply review feedback --- decode.go | 15 ++++++++------- decode_test.go | 1 + encode.go | 8 ++++---- encode_test.go | 10 +++++----- fuzz.go | 1 - 5 files changed, 18 insertions(+), 17 deletions(-) diff --git a/decode.go b/decode.go index d8d3991..04e0eff 100644 --- a/decode.go +++ b/decode.go @@ -2,8 +2,10 @@ package logfmt import ( "bufio" + "bytes" "fmt" "io" + "unicode/utf8" ) // A Decoder reads and decodes logfmt records from an input stream. @@ -70,14 +72,14 @@ func (dec *Decoder) ScanKeyval() bool { key: const invalidKeyError = "invalid key" - start := dec.pos + 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 invalidKey(dec.key) { + if multibyte && bytes.IndexRune(dec.key, utf8.RuneError) != -1 { dec.syntaxError(invalidKeyError) return false } @@ -95,18 +97,20 @@ key: dec.pos += p if dec.pos > start { dec.key = line[start:dec.pos] - if invalidKey(dec.key) { + 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 invalidKey(dec.key) { + if multibyte && bytes.IndexRune(dec.key, utf8.RuneError) != -1 { dec.syntaxError(invalidKeyError) return false } @@ -200,9 +204,6 @@ func (dec *Decoder) Value() []byte { return dec.value } -// func (dec *Decoder) DecodeValue() ([]byte, error) { -// } - // Err returns the first non-EOF error that was encountered by the Scanner. func (dec *Decoder) Err() error { return dec.err diff --git a/decode_test.go b/decode_test.go index 8cdc384..363152d 100644 --- a/decode_test.go +++ b/decode_test.go @@ -120,6 +120,7 @@ func TestDecoder_errors(t *testing.T) { {"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 { diff --git a/encode.go b/encode.go index aa45ee0..55f1603 100644 --- a/encode.go +++ b/encode.go @@ -170,11 +170,11 @@ func invalidKeyRune(r rune) bool { } func invalidKeyString(key string) bool { - return len(key) == 0 || strings.IndexFunc(key, invalidKeyRune) != -1 || !utf8.ValidString(key) + return len(key) == 0 || strings.IndexFunc(key, invalidKeyRune) != -1 } func invalidKey(key []byte) bool { - return len(key) == 0 || bytes.IndexFunc(key, invalidKeyRune) != -1 || !utf8.Valid(key) + return len(key) == 0 || bytes.IndexFunc(key, invalidKeyRune) != -1 } func writeStringKey(w io.Writer, key string) error { @@ -239,7 +239,7 @@ 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 || !utf8.ValidString(value) { + } else if strings.IndexFunc(value, needsQuotedValueRune) != -1 { _, err = writeQuotedString(w, value) } else { _, err = io.WriteString(w, value) @@ -249,7 +249,7 @@ func writeStringValue(w io.Writer, value string, ok bool) error { func writeBytesValue(w io.Writer, value []byte) error { var err error - if bytes.IndexFunc(value, needsQuotedValueRune) >= 0 || !utf8.Valid(value) { + if bytes.IndexFunc(value, needsQuotedValueRune) != -1 { _, err = writeQuotedBytes(w, value) } else { _, err = w.Write(value) diff --git a/encode_test.go b/encode_test.go index 877e6e4..ebebaae 100644 --- a/encode_test.go +++ b/encode_test.go @@ -53,11 +53,11 @@ func TestEncodeKeyval(t *testing.T) { {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\""}, + {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 { diff --git a/fuzz.go b/fuzz.go index 5e93bf1..6553b35 100644 --- a/fuzz.go +++ b/fuzz.go @@ -73,7 +73,6 @@ func parse(data []byte) ([][]kv, error) { kvs = append(kvs, kv{dec.Key(), dec.Value()}) } got = append(got, kvs) - kvs = nil } return got, dec.Err() }