diff --git a/any.go b/any.go index f6b8aeab..6e8b75de 100644 --- a/any.go +++ b/any.go @@ -37,7 +37,7 @@ type Any interface { type baseAny struct{} func (any *baseAny) Get(path ...interface{}) Any { - return &invalidAny{baseAny{}, fmt.Errorf("GetIndex %v from simple value", path)} + return &invalidAny{baseAny{}, errors.New(fmt.Sprintf("GetIndex %v from simple value", path))} } func (any *baseAny) Size() int { @@ -142,7 +142,7 @@ func Wrap(val interface{}) Any { } return &falseAny{} } - return &invalidAny{baseAny{}, fmt.Errorf("unsupported type: %v", typ)} + return &invalidAny{baseAny{}, errors.New(fmt.Sprintf("unsupported type: %v", typ))} } // ReadAny read next JSON element as an Any object. It is a better json.RawMessage. diff --git a/any_invalid.go b/any_invalid.go index 1d859eac..3fb6a8c4 100644 --- a/any_invalid.go +++ b/any_invalid.go @@ -1,6 +1,9 @@ package jsoniter -import "fmt" +import ( + "fmt" + "errors" +) type invalidAny struct { baseAny @@ -8,7 +11,7 @@ type invalidAny struct { } func newInvalidAny(path []interface{}) *invalidAny { - return &invalidAny{baseAny{}, fmt.Errorf("%v not found", path)} + return &invalidAny{baseAny{}, errors.New(fmt.Sprintf("%v not found", path))} } func (any *invalidAny) LastError() error { @@ -68,9 +71,9 @@ func (any *invalidAny) WriteTo(stream *Stream) { func (any *invalidAny) Get(path ...interface{}) Any { if any.err == nil { - return &invalidAny{baseAny{}, fmt.Errorf("get %v from invalid", path)} + return &invalidAny{baseAny{}, errors.New(fmt.Sprintf("get %v from invalid", path))} } - return &invalidAny{baseAny{}, fmt.Errorf("%v, get %v from invalid", any.err, path)} + return &invalidAny{baseAny{}, errors.New(fmt.Sprintf("%v, get %v from invalid", any.err, path))} } func (any *invalidAny) Parse() *Iterator { diff --git a/any_str.go b/any_str.go index 1f12f661..1633d8de 100644 --- a/any_str.go +++ b/any_str.go @@ -3,6 +3,7 @@ package jsoniter import ( "fmt" "strconv" + "errors" ) type stringAny struct { @@ -14,7 +15,7 @@ func (any *stringAny) Get(path ...interface{}) Any { if len(path) == 0 { return any } - return &invalidAny{baseAny{}, fmt.Errorf("GetIndex %v from simple value", path)} + return &invalidAny{baseAny{}, errors.New(fmt.Sprintf("GetIndex %v from simple value", path))} } func (any *stringAny) Parse() *Iterator { diff --git a/iter.go b/iter.go index 29b31cf7..4dde4094 100644 --- a/iter.go +++ b/iter.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "io" + "errors" ) // ValueType the type for JSON element @@ -221,8 +222,8 @@ func (iter *Iterator) ReportError(operation string, msg string) { contextEnd = iter.tail } context := string(iter.buf[contextStart:contextEnd]) - iter.Error = fmt.Errorf("%s: %s, error found in #%v byte of ...|%s|..., bigger context ...|%s|...", - operation, msg, iter.head-peekStart, parsing, context) + iter.Error = errors.New(fmt.Sprintf("%s: %s, error found in #%v byte of ...|%s|..., bigger context ...|%s|...", + operation, msg, iter.head-peekStart, parsing, context)) } // CurrentBuffer gets current buffer as string for debugging purpose diff --git a/misc_tests/jsoniter_error_log_test.go b/misc_tests/jsoniter_error_log_test.go new file mode 100644 index 00000000..8f7d4c42 --- /dev/null +++ b/misc_tests/jsoniter_error_log_test.go @@ -0,0 +1,40 @@ +package misc_tests + +import ( + "errors" + "fmt" + "strconv" + "strings" + "testing" +) + +// 7793382 152 ns/op 40 B/op 3 allocs/op +// 14880938 78 ns/op 23 B/op 1 allocs/op +// 10051323 119 ns/op 24 B/op 2 allocs/op +func Benchmark_fmt_errorf(b *testing.B) { + b.ReportAllocs() + for n := 0; n < b.N; n++ { + fmt.Errorf("Error #%d", n) + } +} + +func Benchmark_errors_new_join(b *testing.B) { + for n := 0; n < b.N; n++ { + errors.New(strings.Join([]string{"Error #", strconv.Itoa(n)}, "")) + } +} + +func Benchmark_errors_new_sprintf(b *testing.B) { + for n := 0; n < b.N; n++ { + errors.New(fmt.Sprintf("Error #%d", n)) + } +} + +func TestErrorsAreEquivalent(t *testing.T) { + errorf := fmt.Errorf("Error #%d", 1).Error() + join := errors.New(strings.Join([]string{"Error #", strconv.Itoa(1)}, "")).Error() + sprintf := errors.New(fmt.Sprintf("Error #%d", 1)).Error() + if errorf != join || errorf != sprintf { + t.Fatalf("Errors are not equal. [errorf: %s] [join: %s] [sprintf: %s]", errorf, join, sprintf) + } +} diff --git a/reflect.go b/reflect.go index 74974ba7..0acc932d 100644 --- a/reflect.go +++ b/reflect.go @@ -1,8 +1,9 @@ package jsoniter import ( - "fmt" + "errors" "reflect" + "strings" "unsafe" "github.com/modern-go/reflect2" @@ -183,7 +184,9 @@ func _createDecoderOfType(ctx *ctx, typ reflect2.Type) ValDecoder { case reflect.Ptr: return decoderOfOptional(ctx, typ) default: - return &lazyErrorDecoder{err: fmt.Errorf("%s%s is unsupported type", ctx.prefix, typ.String())} + return &lazyErrorDecoder{err: errors.New(strings.Join([]string{ + ctx.prefix, typ.String(), " is unsupported type", + }, ""))} } } @@ -282,7 +285,9 @@ func _createEncoderOfType(ctx *ctx, typ reflect2.Type) ValEncoder { case reflect.Ptr: return encoderOfOptional(ctx, typ) default: - return &lazyErrorEncoder{err: fmt.Errorf("%s%s is unsupported type", ctx.prefix, typ.String())} + return &lazyErrorEncoder{err: errors.New(strings.Join([]string{ + ctx.prefix, typ.String(), " is unsupported type", + }, ""))} } } diff --git a/reflect_array.go b/reflect_array.go index 13a0b7b0..182df1b3 100644 --- a/reflect_array.go +++ b/reflect_array.go @@ -5,6 +5,7 @@ import ( "github.com/modern-go/reflect2" "io" "unsafe" + "errors" ) func decoderOfArray(ctx *ctx, typ reflect2.Type) ValDecoder { @@ -48,7 +49,7 @@ func (encoder *arrayEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { } stream.WriteArrayEnd() if stream.Error != nil && stream.Error != io.EOF { - stream.Error = fmt.Errorf("%v: %s", encoder.arrayType, stream.Error.Error()) + stream.Error = errors.New(fmt.Sprintf("%v: %s", encoder.arrayType, stream.Error.Error())) } } @@ -64,7 +65,7 @@ type arrayDecoder struct { func (decoder *arrayDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { decoder.doDecode(ptr, iter) if iter.Error != nil && iter.Error != io.EOF { - iter.Error = fmt.Errorf("%v: %s", decoder.arrayType, iter.Error.Error()) + iter.Error = errors.New(fmt.Sprintf("%v: %s", decoder.arrayType, iter.Error.Error())) } } diff --git a/reflect_map.go b/reflect_map.go index 9e2b623f..8b15b8da 100644 --- a/reflect_map.go +++ b/reflect_map.go @@ -7,6 +7,7 @@ import ( "reflect" "sort" "unsafe" + "errors" ) func decoderOfMap(ctx *ctx, typ reflect2.Type) ValDecoder { @@ -88,7 +89,7 @@ func decoderOfMapKey(ctx *ctx, typ reflect2.Type) ValDecoder { valType: typ, } } - return &lazyErrorDecoder{err: fmt.Errorf("unsupported map key type: %v", typ)} + return &lazyErrorDecoder{err: errors.New(fmt.Sprintf("unsupported map key type: %v", typ))} } } @@ -131,7 +132,7 @@ func encoderOfMapKey(ctx *ctx, typ reflect2.Type) ValEncoder { if typ.Kind() == reflect.Interface { return &dynamicMapKeyEncoder{ctx, typ} } - return &lazyErrorEncoder{err: fmt.Errorf("unsupported map key type: %v", typ)} + return &lazyErrorEncoder{err: errors.New(fmt.Sprintf("unsupported map key type: %v", typ))} } } diff --git a/reflect_slice.go b/reflect_slice.go index 9441d79d..a712b63b 100644 --- a/reflect_slice.go +++ b/reflect_slice.go @@ -5,6 +5,7 @@ import ( "github.com/modern-go/reflect2" "io" "unsafe" + "errors" ) func decoderOfSlice(ctx *ctx, typ reflect2.Type) ValDecoder { @@ -43,7 +44,7 @@ func (encoder *sliceEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { } stream.WriteArrayEnd() if stream.Error != nil && stream.Error != io.EOF { - stream.Error = fmt.Errorf("%v: %s", encoder.sliceType, stream.Error.Error()) + stream.Error = errors.New(fmt.Sprintf("%v: %s", encoder.sliceType, stream.Error.Error())) } } @@ -59,7 +60,7 @@ type sliceDecoder struct { func (decoder *sliceDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { decoder.doDecode(ptr, iter) if iter.Error != nil && iter.Error != io.EOF { - iter.Error = fmt.Errorf("%v: %s", decoder.sliceType, iter.Error.Error()) + iter.Error = errors.New(fmt.Sprintf("%v: %s", decoder.sliceType, iter.Error.Error())) } } diff --git a/reflect_struct_decoder.go b/reflect_struct_decoder.go index d7eb0eb5..898f2d32 100644 --- a/reflect_struct_decoder.go +++ b/reflect_struct_decoder.go @@ -5,6 +5,7 @@ import ( "io" "strings" "unsafe" + "errors" "github.com/modern-go/reflect2" ) @@ -508,7 +509,7 @@ func (decoder *generalStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) decoder.decodeOneField(ptr, iter) } if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { - iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) + iter.Error = errors.New(fmt.Sprintf("%v.%s", decoder.typ, iter.Error.Error())) } if c != '}' { iter.ReportError("struct Decode", `expect }, but found `+string([]byte{c})) @@ -589,7 +590,7 @@ func (decoder *oneFieldStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) } } if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { - iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) + iter.Error = errors.New(fmt.Sprintf("%v.%s", decoder.typ, iter.Error.Error())) } iter.decrementDepth() } @@ -623,7 +624,7 @@ func (decoder *twoFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator } } if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { - iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) + iter.Error = errors.New(fmt.Sprintf("%v.%s", decoder.typ, iter.Error.Error())) } iter.decrementDepth() } @@ -661,7 +662,7 @@ func (decoder *threeFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterat } } if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { - iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) + iter.Error = errors.New(fmt.Sprintf("%v.%s", decoder.typ, iter.Error.Error())) } iter.decrementDepth() } @@ -703,7 +704,7 @@ func (decoder *fourFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterato } } if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { - iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) + iter.Error = errors.New(fmt.Sprintf("%v.%s", decoder.typ, iter.Error.Error())) } iter.decrementDepth() } @@ -749,7 +750,7 @@ func (decoder *fiveFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterato } } if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { - iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) + iter.Error = errors.New(fmt.Sprintf("%v.%s", decoder.typ, iter.Error.Error())) } iter.decrementDepth() } @@ -799,7 +800,7 @@ func (decoder *sixFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator } } if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { - iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) + iter.Error = errors.New(fmt.Sprintf("%v.%s", decoder.typ, iter.Error.Error())) } iter.decrementDepth() } @@ -853,7 +854,7 @@ func (decoder *sevenFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterat } } if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { - iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) + iter.Error = errors.New(fmt.Sprintf("%v.%s", decoder.typ, iter.Error.Error())) } iter.decrementDepth() } @@ -911,7 +912,7 @@ func (decoder *eightFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterat } } if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { - iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) + iter.Error = errors.New(fmt.Sprintf("%v.%s", decoder.typ, iter.Error.Error())) } iter.decrementDepth() } @@ -973,7 +974,7 @@ func (decoder *nineFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterato } } if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { - iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) + iter.Error = errors.New(fmt.Sprintf("%v.%s", decoder.typ, iter.Error.Error())) } iter.decrementDepth() } @@ -1039,7 +1040,7 @@ func (decoder *tenFieldsStructDecoder) Decode(ptr unsafe.Pointer, iter *Iterator } } if iter.Error != nil && iter.Error != io.EOF && len(decoder.typ.Type1().Name()) != 0 { - iter.Error = fmt.Errorf("%v.%s", decoder.typ, iter.Error.Error()) + iter.Error = errors.New(fmt.Sprintf("%v.%s", decoder.typ, iter.Error.Error())) } iter.decrementDepth() } @@ -1053,7 +1054,7 @@ func (decoder *structFieldDecoder) Decode(ptr unsafe.Pointer, iter *Iterator) { fieldPtr := decoder.field.UnsafeGet(ptr) decoder.fieldDecoder.Decode(fieldPtr, iter) if iter.Error != nil && iter.Error != io.EOF { - iter.Error = fmt.Errorf("%s: %s", decoder.field.Name(), iter.Error.Error()) + iter.Error = errors.New(fmt.Sprintf("%s: %s", decoder.field.Name(), iter.Error.Error())) } } diff --git a/reflect_struct_encoder.go b/reflect_struct_encoder.go index 152e3ef5..aa2153e2 100644 --- a/reflect_struct_encoder.go +++ b/reflect_struct_encoder.go @@ -6,6 +6,8 @@ import ( "io" "reflect" "unsafe" + "errors" + "strings" ) func encoderOfStruct(ctx *ctx, typ reflect2.Type) ValEncoder { @@ -66,7 +68,7 @@ func createCheckIsEmpty(ctx *ctx, typ reflect2.Type) checkIsEmpty { case reflect.Ptr: return &OptionalEncoder{} default: - return &lazyErrorEncoder{err: fmt.Errorf("unsupported type: %v", typ)} + return &lazyErrorEncoder{err: errors.New(fmt.Sprintf("unsupported type: %v", typ))} } } @@ -109,7 +111,7 @@ func (encoder *structFieldEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { fieldPtr := encoder.field.UnsafeGet(ptr) encoder.fieldEncoder.Encode(fieldPtr, stream) if stream.Error != nil && stream.Error != io.EOF { - stream.Error = fmt.Errorf("%s: %s", encoder.field.Name(), stream.Error.Error()) + stream.Error = errors.New(strings.Join([]string{encoder.field.Name(), stream.Error.Error()}, ": ")) } } @@ -160,7 +162,7 @@ func (encoder *structEncoder) Encode(ptr unsafe.Pointer, stream *Stream) { } stream.WriteObjectEnd() if stream.Error != nil && stream.Error != io.EOF { - stream.Error = fmt.Errorf("%v.%s", encoder.typ, stream.Error.Error()) + stream.Error = errors.New(fmt.Sprintf("%v.%s", encoder.typ, stream.Error.Error())) } } diff --git a/stream_float.go b/stream_float.go index 826aa594..faeb8db1 100644 --- a/stream_float.go +++ b/stream_float.go @@ -4,6 +4,7 @@ import ( "fmt" "math" "strconv" + "errors" ) var pow10 []uint64 @@ -15,7 +16,7 @@ func init() { // WriteFloat32 write float32 to stream func (stream *Stream) WriteFloat32(val float32) { if math.IsInf(float64(val), 0) || math.IsNaN(float64(val)) { - stream.Error = fmt.Errorf("unsupported value: %f", val) + stream.Error = errors.New(fmt.Sprintf("unsupported value: %f", val)) return } abs := math.Abs(float64(val)) @@ -32,7 +33,7 @@ func (stream *Stream) WriteFloat32(val float32) { // WriteFloat32Lossy write float32 to stream with ONLY 6 digits precision although much much faster func (stream *Stream) WriteFloat32Lossy(val float32) { if math.IsInf(float64(val), 0) || math.IsNaN(float64(val)) { - stream.Error = fmt.Errorf("unsupported value: %f", val) + stream.Error = errors.New(fmt.Sprintf("unsupported value: %f", val)) return } if val < 0 { @@ -64,7 +65,7 @@ func (stream *Stream) WriteFloat32Lossy(val float32) { // WriteFloat64 write float64 to stream func (stream *Stream) WriteFloat64(val float64) { if math.IsInf(val, 0) || math.IsNaN(val) { - stream.Error = fmt.Errorf("unsupported value: %f", val) + stream.Error = errors.New(fmt.Sprintf("unsupported value: %f", val)) return } abs := math.Abs(val) @@ -81,7 +82,7 @@ func (stream *Stream) WriteFloat64(val float64) { // WriteFloat64Lossy write float64 to stream with ONLY 6 digits precision although much much faster func (stream *Stream) WriteFloat64Lossy(val float64) { if math.IsInf(val, 0) || math.IsNaN(val) { - stream.Error = fmt.Errorf("unsupported value: %f", val) + stream.Error = errors.New(fmt.Sprintf("unsupported value: %f", val)) return } if val < 0 {