Skip to content

Commit

Permalink
Merge branch 'release/v0.4.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
ChrisHines committed Nov 22, 2018
2 parents 390ab79 + 0e6ce84 commit 07c9b44
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 42 deletions.
11 changes: 6 additions & 5 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
language: go
sudo: false
go:
- 1.3
- 1.4
- 1.5
- 1.6
- tip
- "1.7.x"
- "1.8.x"
- "1.9.x"
- "1.10.x"
- "1.11.x"
- "tip"

before_install:
- go get github.com/mattn/goveralls
Expand Down
41 changes: 41 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Changelog
All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.4.0] - 2018-11-21

### Added
- Go module support by [@ChrisHines]
- CHANGELOG by [@ChrisHines]

### Changed
- Drop invalid runes from keys instead of returning ErrInvalidKey by [@ChrisHines]
- On panic while printing, attempt to print panic value by [@bboreham]

## [0.3.0] - 2016-11-15
### Added
- Pool buffers for quoted strings and byte slices by [@nussjustin]
### Fixed
- Fuzz fix, quote invalid UTF-8 values by [@judwhite]

## [0.2.0] - 2016-05-08
### Added
- Encoder.EncodeKeyvals by [@ChrisHines]

## [0.1.0] - 2016-03-28
### Added
- Encoder by [@ChrisHines]
- Decoder by [@ChrisHines]
- MarshalKeyvals by [@ChrisHines]

[0.4.0]: https://github.com/go-logfmt/logfmt/compare/v0.3.0...v0.4.0
[0.3.0]: https://github.com/go-logfmt/logfmt/compare/v0.2.0...v0.3.0
[0.2.0]: https://github.com/go-logfmt/logfmt/compare/v0.1.0...v0.2.0
[0.1.0]: https://github.com/go-logfmt/logfmt/commits/v0.1.0

[@ChrisHines]: https://github.com/ChrisHines
[@bboreham]: https://github.com/bboreham
[@judwhite]: https://github.com/judwhite
[@nussjustin]: https://github.com/nussjustin
39 changes: 20 additions & 19 deletions encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,8 @@ func (e *MarshalerError) Error() string {
// 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.
// ErrInvalidKey is returned by Marshal functions and Encoder methods if, after
// dropping invalid runes, a key is empty.
var ErrInvalidKey = errors.New("invalid key")

// ErrUnsupportedKeyType is returned by Encoder methods if a key has an
Expand Down Expand Up @@ -165,31 +165,32 @@ func writeKey(w io.Writer, key interface{}) error {
}
}

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
// keyRuneFilter returns r for all valid key runes, and -1 for all invalid key
// runes. When used as the mapping function for strings.Map and bytes.Map
// functions it causes them to remove invalid key runes from strings or byte
// slices respectively.
func keyRuneFilter(r rune) rune {
if r <= ' ' || r == '=' || r == '"' || r == utf8.RuneError {
return -1
}
return r
}

func writeStringKey(w io.Writer, key string) error {
if invalidKeyString(key) {
k := strings.Map(keyRuneFilter, key)
if k == "" {
return ErrInvalidKey
}
_, err := io.WriteString(w, key)
_, err := io.WriteString(w, k)
return err
}

func writeBytesKey(w io.Writer, key []byte) error {
if invalidKey(key) {
k := bytes.Map(keyRuneFilter, key)
if len(k) == 0 {
return ErrInvalidKey
}
_, err := w.Write(key)
_, err := w.Write(k)
return err
}

Expand Down Expand Up @@ -278,7 +279,7 @@ func safeError(err error) (s string, ok bool) {
if v := reflect.ValueOf(err); v.Kind() == reflect.Ptr && v.IsNil() {
s, ok = "null", false
} else {
panic(panicVal)
s, ok = fmt.Sprintf("PANIC:%v", panicVal), false
}
}
}()
Expand All @@ -292,7 +293,7 @@ func safeString(str fmt.Stringer) (s string, ok bool) {
if v := reflect.ValueOf(str); v.Kind() == reflect.Ptr && v.IsNil() {
s, ok = "null", false
} else {
panic(panicVal)
s, ok = fmt.Sprintf("PANIC:%v", panicVal), true
}
}
}()
Expand All @@ -306,7 +307,7 @@ func safeMarshal(tm encoding.TextMarshaler) (b []byte, err error) {
if v := reflect.ValueOf(tm); v.Kind() == reflect.Ptr && v.IsNil() {
b, err = nil, nil
} else {
panic(panicVal)
b, err = nil, fmt.Errorf("panic when marshalling: %s", panicVal)
}
}
}()
Expand Down
76 changes: 58 additions & 18 deletions encode_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"errors"
"fmt"
"io/ioutil"
"reflect"
"testing"
)
Expand All @@ -26,11 +27,26 @@ func TestSafeMarshal(t *testing.T) {
}

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) },
keygen := []struct {
name string
fn func(string) interface{}
}{
{
name: "string",
fn: func(s string) interface{} { return s },
},
{
name: "named-string",
fn: func(s string) interface{} { return stringData(s) },
},
{
name: "Stringer",
fn: func(s string) interface{} { return stringStringer(s) },
},
{
name: "TextMarshaler",
fn: func(s string) interface{} { return stringMarshaler(s) },
},
}

data := []struct {
Expand All @@ -48,23 +64,30 @@ func TestWriteKeyStrings(t *testing.T) {
{key: " ", err: ErrInvalidKey},
{key: "=", err: ErrInvalidKey},
{key: `"`, err: ErrInvalidKey},
{key: "k\n", want: "k"},
{key: "k\nk", want: "kk"},
{key: "k\tk", want: "kk"},
{key: "k=k", want: "kk"},
{key: `"kk"`, want: "kk"},
}

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)
t.Run(g.name, func(t *testing.T) {
for _, d := range data {
w := &bytes.Buffer{}
key := g.fn(d.key)
err := writeKey(w, key)
if err != d.err {
t.Errorf("%#v: 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: got '%s', want '%s'", key, got, want)
}
}
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)
}
}
})
}
}

Expand Down Expand Up @@ -231,3 +254,20 @@ type errorMarshaler struct{}
func (errorMarshaler) MarshalText() ([]byte, error) {
return nil, errMarshaling
}

func BenchmarkWriteStringKey(b *testing.B) {
keys := []string{
"k",
"caller",
"has space",
`"quoted"`,
}

for _, k := range keys {
b.Run(k, func(b *testing.B) {
for i := 0; i < b.N; i++ {
writeStringKey(ioutil.Discard, k)
}
})
}
}
18 changes: 18 additions & 0 deletions encode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@ func TestMarshalKeyvals(t *testing.T) {
{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")},
{in: kv("k", panicingStringer{0}), want: []byte("k=ok")},
{in: kv("k", panicingStringer{1}), want: []byte("k=PANIC:panic1")},
// Need extra mechanism to test panic-while-printing-panicVal
//{in: kv("k", panicingStringer{2}), want: []byte("?")},
}

for _, d := range data {
Expand Down Expand Up @@ -196,6 +200,20 @@ func (errorMarshaler) MarshalText() ([]byte, error) {
return nil, errMarshal
}

type panicingStringer struct {
a int
}

func (p panicingStringer) String() string {
switch p.a {
case 1:
panic("panic1")
case 2:
panic(panicingStringer{a: 2})
}
return "ok"
}

func BenchmarkEncodeKeyval(b *testing.B) {
b.ReportAllocs()
enc := logfmt.NewEncoder(ioutil.Discard)
Expand Down
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/go-logfmt/logfmt

require github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=

0 comments on commit 07c9b44

Please sign in to comment.