gojay

high performance JSON encoder/decoder with stream API for Golang
git clone git://git.lair.cx/gojay
Log | Files | Refs | README | LICENSE

commit ac278250a8d7a4543bba4f971d66eef067c9c07d
parent 73e818cfa07a20afdd11e26f4abbc3795ec878e4
Author: francoispqt <francois@parquet.ninja>
Date:   Sat, 19 May 2018 18:36:04 +0800

refactor tests and error raising

Diffstat:
Mbenchmarks/decoder/decoder_large_test.go | 2--
Mdecode_array.go | 20+++++---------------
Mdecode_array_test.go | 6+-----
Mdecode_bool.go | 36++++++++++++++----------------------
Mdecode_number.go | 2+-
Mdecode_number_float.go | 26++++++--------------------
Mdecode_number_float_test.go | 16++++++++++++++++
Mdecode_number_int.go | 90++++++++++++++++++++++++++++---------------------------------------------------
Mdecode_number_int_test.go | 76++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdecode_number_uint.go | 73++++++++++++++++++++++++-------------------------------------------------
Mdecode_number_uint_test.go | 16++++++++++++++++
Mdecode_object.go | 26++++++++++++--------------
Mdecode_object_test.go | 311++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Mdecode_stream_test.go | 4++--
Mdecode_string.go | 33+++++++++++++--------------------
Mdecode_string_test.go | 8++++++--
Mdecode_unsafe.go | 33+++++++++++++++++++++++++++++----
Mdecode_unsafe_test.go | 69+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Merrors.go | 28++++++++++++++++++++++++++--
Mgojay_test.go | 2++
20 files changed, 652 insertions(+), 225 deletions(-)

diff --git a/benchmarks/decoder/decoder_large_test.go b/benchmarks/decoder/decoder_large_test.go @@ -1,7 +1,6 @@ package benchmarks import ( - "log" "testing" "github.com/francoispqt/gojay" @@ -15,7 +14,6 @@ func TestGoJayDecodeObjLarge(t *testing.T) { assert.Nil(t, err, "err should be nil") assert.Len(t, result.Users, 32, "Len of users should be 32") for _, u := range result.Users { - log.Print(u) assert.True(t, len(u.Username) > 0, "User should have username") } assert.Len(t, result.Topics.Topics, 30, "Len of topics should be 30") diff --git a/decode_array.go b/decode_array.go @@ -1,9 +1,5 @@ package gojay -import ( - "fmt" -) - // DecodeArray reads the next JSON-encoded value from its input and stores it in the value pointed to by v. // // v must implement UnmarshalerJSONArray. @@ -40,7 +36,7 @@ func (dec *Decoder) decodeArray(arr UnmarshalerJSONArray) (int, error) { } n++ } - return 0, InvalidJSONError("Invalid JSON could not find array closing bracket") + return 0, dec.raiseInvalidJSONErr(dec.cursor) case 'n': // is null dec.cursor++ @@ -53,23 +49,17 @@ func (dec *Decoder) decodeArray(arr UnmarshalerJSONArray) (int, error) { case '{', '"', 'f', 't', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': // can't unmarshall to struct // we skip array and set Error - dec.err = InvalidUnmarshalError( - fmt.Sprintf( - "Cannot unmarshall to array, wrong char '%s' found at pos %d", - string(dec.data[dec.cursor]), - dec.cursor, - ), - ) + dec.err = dec.makeInvalidUnmarshalErr(arr) err := dec.skipData() if err != nil { return 0, err } return dec.cursor, nil default: - return 0, InvalidJSONError("Invalid JSON") + return 0, dec.raiseInvalidJSONErr(dec.cursor) } } - return 0, InvalidJSONError("Invalid JSON") + return 0, dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) skipArray() (int, error) { @@ -114,5 +104,5 @@ func (dec *Decoder) skipArray() (int, error) { continue } } - return 0, InvalidJSONError("Invalid JSON") + return 0, dec.raiseInvalidJSONErr(dec.cursor) } diff --git a/decode_array_test.go b/decode_array_test.go @@ -1,7 +1,6 @@ package gojay import ( - "log" "strings" "testing" @@ -75,7 +74,6 @@ func TestSliceInts(t *testing.T) { } continue } - log.Print(s) for k, v := range testCase.expectedResult { assert.Equal(t, v, s[k], "value at given index should be the same as expected results") } @@ -211,7 +209,6 @@ func TestSliceBools(t *testing.T) { } return } - log.Print(s, testCase.name) assert.Nil(t, err, "err should be nil") for k, v := range testCase.expectedResult { assert.Equal(t, v, s[k], "value at given index should be the same as expected results") @@ -278,7 +275,6 @@ func TestSliceSlices(t *testing.T) { } return } - log.Print(s, testCase.name) assert.Nil(t, err, "err should be nil") for k, v := range testCase.expectedResult { assert.Equal(t, v, s[k], "value at given index should be the same as expected results") @@ -393,7 +389,7 @@ func TestDecodeSliceInvalidType(t *testing.T) { err := UnmarshalJSONArray([]byte(`{}`), &result) assert.NotNil(t, err, "err should not be nil") assert.IsType(t, InvalidUnmarshalError(""), err, "err should be of type InvalidUnmarshalError") - assert.Equal(t, "Cannot unmarshall to array, wrong char '{' found at pos 0", err.Error(), "err should not be nil") + assert.Equal(t, "Cannot unmarshal JSON to type '*gojay.testSliceObjects'", err.Error(), "err should not be nil") } func TestDecoderChannelOfObjectsBasic(t *testing.T) { diff --git a/decode_bool.go b/decode_bool.go @@ -1,7 +1,5 @@ package gojay -import "fmt" - // DecodeBool reads the next JSON-encoded value from its input and stores it in the boolean pointed to by v. // // See the documentation for Unmarshal for details about the conversion of JSON into a Go value. @@ -44,13 +42,7 @@ func (dec *Decoder) decodeBool(v *bool) error { dec.cursor++ return nil default: - dec.err = InvalidUnmarshalError( - fmt.Sprintf( - "Cannot unmarshall to bool, wrong char '%s' found at pos %d", - string(dec.data[dec.cursor]), - dec.cursor, - ), - ) + dec.err = dec.makeInvalidUnmarshalErr(v) err := dec.skipData() if err != nil { return err @@ -67,15 +59,15 @@ func (dec *Decoder) assertTrue() error { switch i { case 0: if dec.data[dec.cursor] != 'r' { - return InvalidJSONError(fmt.Sprintf(invalidJSONCharErrorMsg, dec.data[dec.cursor], dec.cursor)) + return dec.raiseInvalidJSONErr(dec.cursor) } case 1: if dec.data[dec.cursor] != 'u' { - return InvalidJSONError(fmt.Sprintf(invalidJSONCharErrorMsg, dec.data[dec.cursor], dec.cursor)) + return dec.raiseInvalidJSONErr(dec.cursor) } case 2: if dec.data[dec.cursor] != 'e' { - return InvalidJSONError(fmt.Sprintf(invalidJSONCharErrorMsg, dec.data[dec.cursor], dec.cursor)) + return dec.raiseInvalidJSONErr(dec.cursor) } case 3: switch dec.data[dec.cursor] { @@ -83,7 +75,7 @@ func (dec *Decoder) assertTrue() error { dec.cursor-- return nil default: - return InvalidJSONError(fmt.Sprintf(invalidJSONCharErrorMsg, dec.data[dec.cursor], dec.cursor)) + return dec.raiseInvalidJSONErr(dec.cursor) } } i++ @@ -100,15 +92,15 @@ func (dec *Decoder) assertNull() error { switch i { case 0: if dec.data[dec.cursor] != 'u' { - return InvalidJSONError(fmt.Sprintf(invalidJSONCharErrorMsg, dec.data[dec.cursor], dec.cursor)) + return dec.raiseInvalidJSONErr(dec.cursor) } case 1: if dec.data[dec.cursor] != 'l' { - return InvalidJSONError(fmt.Sprintf(invalidJSONCharErrorMsg, dec.data[dec.cursor], dec.cursor)) + return dec.raiseInvalidJSONErr(dec.cursor) } case 2: if dec.data[dec.cursor] != 'l' { - return InvalidJSONError(fmt.Sprintf(invalidJSONCharErrorMsg, dec.data[dec.cursor], dec.cursor)) + return dec.raiseInvalidJSONErr(dec.cursor) } case 3: switch dec.data[dec.cursor] { @@ -116,7 +108,7 @@ func (dec *Decoder) assertNull() error { dec.cursor-- return nil default: - return InvalidJSONError(fmt.Sprintf(invalidJSONCharErrorMsg, dec.data[dec.cursor], dec.cursor)) + return dec.raiseInvalidJSONErr(dec.cursor) } } i++ @@ -133,19 +125,19 @@ func (dec *Decoder) assertFalse() error { switch i { case 0: if dec.data[dec.cursor] != 'a' { - return InvalidJSONError(fmt.Sprintf(invalidJSONCharErrorMsg, dec.data[dec.cursor], dec.cursor)) + return dec.raiseInvalidJSONErr(dec.cursor) } case 1: if dec.data[dec.cursor] != 'l' { - return InvalidJSONError(fmt.Sprintf(invalidJSONCharErrorMsg, dec.data[dec.cursor], dec.cursor)) + return dec.raiseInvalidJSONErr(dec.cursor) } case 2: if dec.data[dec.cursor] != 's' { - return InvalidJSONError(fmt.Sprintf(invalidJSONCharErrorMsg, dec.data[dec.cursor], dec.cursor)) + return dec.raiseInvalidJSONErr(dec.cursor) } case 3: if dec.data[dec.cursor] != 'e' { - return InvalidJSONError(fmt.Sprintf(invalidJSONCharErrorMsg, dec.data[dec.cursor], dec.cursor)) + return dec.raiseInvalidJSONErr(dec.cursor) } case 4: switch dec.data[dec.cursor] { @@ -153,7 +145,7 @@ func (dec *Decoder) assertFalse() error { dec.cursor-- return nil default: - return InvalidJSONError(fmt.Sprintf(invalidJSONCharErrorMsg, dec.data[dec.cursor], dec.cursor)) + return dec.raiseInvalidJSONErr(dec.cursor) } } i++ diff --git a/decode_number.go b/decode_number.go @@ -74,7 +74,7 @@ func (dec *Decoder) skipNumber() (int, error) { continue } // invalid json we expect numbers, dot (single one), comma, or spaces - return end, InvalidJSONError("Invalid JSON while parsing number") + return end, dec.raiseInvalidJSONErr(dec.cursor) } return end, nil } diff --git a/decode_number_float.go b/decode_number_float.go @@ -1,7 +1,5 @@ package gojay -import "fmt" - // DecodeFloat64 reads the next JSON-encoded value from its input and stores it in the float64 pointed to by v. // // See the documentation for Unmarshal for details about the conversion of JSON into a Go value. @@ -39,13 +37,7 @@ func (dec *Decoder) decodeFloat64(v *float64) error { } return nil default: - dec.err = InvalidUnmarshalError( - fmt.Sprintf( - "Cannot unmarshall to float, wrong char '%s' found at pos %d", - string(dec.data[dec.cursor]), - dec.cursor, - ), - ) + dec.err = dec.makeInvalidUnmarshalErr(v) err := dec.skipData() if err != nil { return err @@ -53,7 +45,7 @@ func (dec *Decoder) decodeFloat64(v *float64) error { return nil } } - return InvalidJSONError("Invalid JSON while parsing float") + return dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) getFloat(b byte) (float64, error) { @@ -114,7 +106,7 @@ func (dec *Decoder) getFloat(b byte) (float64, error) { return float64(dec.atoi64(start, end)), nil } // invalid json we expect numbers, dot (single one), comma, or spaces - return 0, InvalidJSONError("Invalid JSON while parsing number") + return 0, dec.raiseInvalidJSONErr(dec.cursor) } return float64(dec.atoi64(start, end)), nil } @@ -156,13 +148,7 @@ func (dec *Decoder) decodeFloat32(v *float32) error { } return nil default: - dec.err = InvalidUnmarshalError( - fmt.Sprintf( - "Cannot unmarshall to float, wrong char '%s' found at pos %d", - string(dec.data[dec.cursor]), - dec.cursor, - ), - ) + dec.err = dec.makeInvalidUnmarshalErr(v) err := dec.skipData() if err != nil { return err @@ -170,7 +156,7 @@ func (dec *Decoder) decodeFloat32(v *float32) error { return nil } } - return InvalidJSONError("Invalid JSON while parsing float") + return dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) getFloat32(b byte) (float32, error) { @@ -231,7 +217,7 @@ func (dec *Decoder) getFloat32(b byte) (float32, error) { return float32(dec.atoi32(start, end)), nil } // invalid json we expect numbers, dot (single one), comma, or spaces - return 0, InvalidJSONError("Invalid JSON while parsing number") + return 0, dec.raiseInvalidJSONErr(dec.cursor) } return float32(dec.atoi32(start, end)), nil } diff --git a/decode_number_float_test.go b/decode_number_float_test.go @@ -188,6 +188,14 @@ func TestDecoderFloat64(t *testing.T) { assert.Nil(t, err, "Err must be nil") assert.Equal(t, 1.25, v, "v must be equal to 1.25") }) + t.Run("decoder-api2", func(t *testing.T) { + var v float64 + dec := NewDecoder(strings.NewReader(`1.25`)) + defer dec.Release() + err := dec.DecodeFloat64(&v) + assert.Nil(t, err, "Err must be nil") + assert.Equal(t, 1.25, v, "v must be equal to 1.25") + }) t.Run("decoder-api-json-error", func(t *testing.T) { var v float64 dec := NewDecoder(strings.NewReader(``)) @@ -376,6 +384,14 @@ func TestDecoderFloat32(t *testing.T) { assert.Nil(t, err, "Err must be nil") assert.Equal(t, float32(1.25), v, "v must be equal to 1.25") }) + t.Run("decoder-api2", func(t *testing.T) { + var v float32 + dec := NewDecoder(strings.NewReader(`1.25`)) + defer dec.Release() + err := dec.Decode(&v) + assert.Nil(t, err, "Err must be nil") + assert.Equal(t, float32(1.25), v, "v must be equal to 1.25") + }) t.Run("decoder-api-json-error", func(t *testing.T) { var v float32 dec := NewDecoder(strings.NewReader(``)) diff --git a/decode_number_int.go b/decode_number_int.go @@ -100,13 +100,7 @@ func (dec *Decoder) decodeInt16(v *int16) error { dec.cursor++ return nil default: - dec.err = InvalidUnmarshalError( - fmt.Sprintf( - "Cannot unmarshall to int, wrong char '%s' found at pos %d", - string(dec.data[dec.cursor]), - dec.cursor, - ), - ) + dec.err = dec.makeInvalidUnmarshalErr(v) err := dec.skipData() if err != nil { return err @@ -114,7 +108,7 @@ func (dec *Decoder) decodeInt16(v *int16) error { return nil } } - return InvalidJSONError("Invalid JSON while parsing int") + return dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) getInt16(b byte) (int16, error) { @@ -176,7 +170,7 @@ func (dec *Decoder) getInt16(b byte) (int16, error) { return dec.atoi16(start, end), nil } // invalid json we expect numbers, dot (single one), comma, or spaces - return 0, InvalidJSONError("Invalid JSON while parsing number") + return 0, dec.raiseInvalidJSONErr(dec.cursor) } return dec.atoi16(start, end), nil } @@ -213,11 +207,10 @@ func (dec *Decoder) getInt16WithExp(init int16, cursor int) (int16, error) { } return init * int16(pow10uint64[exp+1]), nil default: - dec.err = InvalidJSONError("Invalid JSON") - return 0, dec.err + return 0, dec.raiseInvalidJSONErr(dec.cursor) } } - return 0, InvalidJSONError("Invalid JSON") + return 0, dec.raiseInvalidJSONErr(dec.cursor) } // DecodeInt8 reads the next JSON-encoded value from its input and stores it in the int8 pointed to by v. @@ -259,13 +252,7 @@ func (dec *Decoder) decodeInt8(v *int8) error { dec.cursor++ return nil default: - dec.err = InvalidUnmarshalError( - fmt.Sprintf( - "Cannot unmarshall to int, wrong char '%s' found at pos %d", - string(dec.data[dec.cursor]), - dec.cursor, - ), - ) + dec.err = dec.makeInvalidUnmarshalErr(v) err := dec.skipData() if err != nil { return err @@ -273,7 +260,7 @@ func (dec *Decoder) decodeInt8(v *int8) error { return nil } } - return InvalidJSONError("Invalid JSON while parsing int") + return dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) getInt8(b byte) (int8, error) { @@ -335,7 +322,7 @@ func (dec *Decoder) getInt8(b byte) (int8, error) { return dec.atoi8(start, end), nil } // invalid json we expect numbers, dot (single one), comma, or spaces - return 0, InvalidJSONError("Invalid JSON while parsing number") + return 0, dec.raiseInvalidJSONErr(dec.cursor) } return dec.atoi8(start, end), nil } @@ -372,11 +359,11 @@ func (dec *Decoder) getInt8WithExp(init int8, cursor int) (int8, error) { } return init * int8(pow10uint64[exp+1]), nil default: - dec.err = InvalidJSONError("Invalid JSON") + dec.err = dec.raiseInvalidJSONErr(dec.cursor) return 0, dec.err } } - return 0, InvalidJSONError("Invalid JSON") + return 0, dec.raiseInvalidJSONErr(dec.cursor) } // DecodeInt32 reads the next JSON-encoded value from its input and stores it in the int32 pointed to by v. @@ -416,13 +403,7 @@ func (dec *Decoder) decodeInt32(v *int32) error { } return nil default: - dec.err = InvalidUnmarshalError( - fmt.Sprintf( - "Cannot unmarshall to int, wrong char '%s' found at pos %d", - string(dec.data[dec.cursor]), - dec.cursor, - ), - ) + dec.err = dec.makeInvalidUnmarshalErr(v) err := dec.skipData() if err != nil { return err @@ -430,7 +411,7 @@ func (dec *Decoder) decodeInt32(v *int32) error { return nil } } - return InvalidJSONError("Invalid JSON while parsing int") + return dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) getInt32(b byte) (int32, error) { @@ -492,7 +473,7 @@ func (dec *Decoder) getInt32(b byte) (int32, error) { return dec.atoi32(start, end), nil } // invalid json we expect numbers, dot (single one), comma, or spaces - return 0, InvalidJSONError("Invalid JSON while parsing number") + return 0, dec.raiseInvalidJSONErr(dec.cursor) } return dec.atoi32(start, end), nil } @@ -529,11 +510,11 @@ func (dec *Decoder) getInt32WithExp(init int32, cursor int) (int32, error) { } return init * int32(pow10uint64[exp+1]), nil default: - dec.err = InvalidJSONError("Invalid JSON") + dec.err = dec.raiseInvalidJSONErr(dec.cursor) return 0, dec.err } } - return 0, InvalidJSONError("Invalid JSON") + return 0, dec.raiseInvalidJSONErr(dec.cursor) } // DecodeInt64 reads the next JSON-encoded value from its input and stores it in the int64 pointed to by v. @@ -574,13 +555,7 @@ func (dec *Decoder) decodeInt64(v *int64) error { } return nil default: - dec.err = InvalidUnmarshalError( - fmt.Sprintf( - "Cannot unmarshall to int, wrong char '%s' found at pos %d", - string(dec.data[dec.cursor]), - dec.cursor, - ), - ) + dec.err = dec.makeInvalidUnmarshalErr(v) err := dec.skipData() if err != nil { return err @@ -588,7 +563,7 @@ func (dec *Decoder) decodeInt64(v *int64) error { return nil } } - return InvalidJSONError("Invalid JSON while parsing int") + return dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) getInt64(b byte) (int64, error) { @@ -650,7 +625,7 @@ func (dec *Decoder) getInt64(b byte) (int64, error) { return dec.getInt64WithExp(dec.atoi64(start, end), j+1) } // invalid json we expect numbers, dot (single one), comma, or spaces - return 0, InvalidJSONError("Invalid JSON while parsing number") + return 0, dec.raiseInvalidJSONErr(dec.cursor) } return dec.atoi64(start, end), nil } @@ -687,11 +662,10 @@ func (dec *Decoder) getInt64WithExp(init int64, cursor int) (int64, error) { } return init * int64(pow10uint64[exp+1]), nil default: - dec.err = InvalidJSONError("Invalid JSON") - return 0, dec.err + return 0, dec.raiseInvalidJSONErr(dec.cursor) } } - return 0, InvalidJSONError("Invalid JSON") + return 0, dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) atoi64(start, end int) int64 { @@ -708,18 +682,18 @@ func (dec *Decoder) atoi64(start, end int) int64 { for i := start + 1; i < end; i++ { intv := int64(digits[dec.data[i]]) if val > maxInt64toMultiply { - dec.err = InvalidUnmarshalError("Overflows int64") + dec.err = dec.makeInvalidUnmarshalErr(val) return 0 } val = (val << 3) + (val << 1) if math.MaxInt64-val < intv { - dec.err = InvalidUnmarshalError("Overflows int64") + dec.err = dec.makeInvalidUnmarshalErr(val) return 0 } val += intv } } else { - dec.err = InvalidUnmarshalError("Overflows int64") + dec.err = dec.makeInvalidUnmarshalErr(val) return 0 } return val @@ -739,18 +713,18 @@ func (dec *Decoder) atoi32(start, end int) int32 { for i := start + 1; i < end; i++ { intv := int32(digits[dec.data[i]]) if val > maxInt32toMultiply { - dec.err = InvalidUnmarshalError("Overflows int32") + dec.err = dec.makeInvalidUnmarshalErr(val) return 0 } val = (val << 3) + (val << 1) if math.MaxInt32-val < intv { - dec.err = InvalidUnmarshalError("Overflows int32") + dec.err = dec.makeInvalidUnmarshalErr(val) return 0 } val += intv } } else { - dec.err = InvalidUnmarshalError("Overflows int32") + dec.err = dec.makeInvalidUnmarshalErr(val) return 0 } return val @@ -770,18 +744,18 @@ func (dec *Decoder) atoi16(start, end int) int16 { for i := start + 1; i < end; i++ { intv := int16(digits[dec.data[i]]) if val > maxInt16toMultiply { - dec.err = InvalidUnmarshalError("Overflows int32") + dec.err = dec.makeInvalidUnmarshalErr(val) return 0 } val = (val << 3) + (val << 1) if math.MaxInt16-val < intv { - dec.err = InvalidUnmarshalError("Overflows int32") + dec.err = dec.makeInvalidUnmarshalErr(val) return 0 } val += intv } } else { - dec.err = InvalidUnmarshalError("Overflows int32") + dec.err = dec.makeInvalidUnmarshalErr(val) return 0 } return val @@ -801,18 +775,18 @@ func (dec *Decoder) atoi8(start, end int) int8 { for i := start + 1; i < end; i++ { intv := int8(digits[dec.data[i]]) if val > maxInt8toMultiply { - dec.err = InvalidUnmarshalError("Overflows int32") + dec.err = dec.makeInvalidUnmarshalErr(val) return 0 } val = (val << 3) + (val << 1) if math.MaxInt8-val < intv { - dec.err = InvalidUnmarshalError("Overflows int32") + dec.err = dec.makeInvalidUnmarshalErr(val) return 0 } val += intv } } else { - dec.err = InvalidUnmarshalError("Overflows int32") + dec.err = dec.makeInvalidUnmarshalErr(val) return 0 } return val diff --git a/decode_number_int_test.go b/decode_number_int_test.go @@ -230,6 +230,14 @@ func TestDecoderInt(t *testing.T) { assert.Nil(t, err, "Err must be nil") assert.Equal(t, int(33), v, "v must be equal to 33") }) + t.Run("decoder-api2", func(t *testing.T) { + var v int + dec := NewDecoder(strings.NewReader(`33`)) + defer dec.Release() + err := dec.Decode(&v) + assert.Nil(t, err, "Err must be nil") + assert.Equal(t, int(33), v, "v must be equal to 33") + }) t.Run("decoder-api-invalid-json", func(t *testing.T) { var v int dec := NewDecoder(strings.NewReader(``)) @@ -287,6 +295,12 @@ func TestDecoderInt64(t *testing.T) { err: true, }, { + name: "basic-big-overflow", + json: " 9223372036854775827", + expectedResult: 0, + err: true, + }, + { name: "basic-big-overflow2", json: "92233720368547758089", expectedResult: 0, @@ -446,6 +460,14 @@ func TestDecoderInt64(t *testing.T) { assert.Nil(t, err, "Err must be nil") assert.Equal(t, int64(33), v, "v must be equal to 33") }) + t.Run("decoder-api2", func(t *testing.T) { + var v int64 + dec := NewDecoder(strings.NewReader(`33`)) + defer dec.Release() + err := dec.Decode(&v) + assert.Nil(t, err, "Err must be nil") + assert.Equal(t, int64(33), v, "v must be equal to 33") + }) t.Run("decoder-api-invalid-json", func(t *testing.T) { var v int64 dec := NewDecoder(strings.NewReader(``)) @@ -508,6 +530,12 @@ func TestDecoderInt32(t *testing.T) { err: true, }, { + name: "basic-big-overflow", + json: " 2147483657", + expectedResult: 0, + err: true, + }, + { name: "basic-big-overflow2", json: "21474836483", expectedResult: 0, @@ -674,6 +702,14 @@ func TestDecoderInt32(t *testing.T) { assert.Nil(t, err, "Err must be nil") assert.Equal(t, int32(33), v, "v must be equal to 33") }) + t.Run("decoder-api2", func(t *testing.T) { + var v int32 + dec := NewDecoder(strings.NewReader(`33`)) + defer dec.Release() + err := dec.Decode(&v) + assert.Nil(t, err, "Err must be nil") + assert.Equal(t, int32(33), v, "v must be equal to 33") + }) t.Run("decoder-api-invalid-json", func(t *testing.T) { var v int32 dec := NewDecoder(strings.NewReader(``)) @@ -731,6 +767,18 @@ func TestDecoderInt16(t *testing.T) { }, { name: "basic-big-overflow", + json: "66535", + expectedResult: 0, + err: true, + }, + { + name: "basic-big-overflow", + json: "32768", + expectedResult: 0, + err: true, + }, + { + name: "basic-big-overflow", json: " 2147483648", expectedResult: 0, err: true, @@ -902,6 +950,14 @@ func TestDecoderInt16(t *testing.T) { assert.Nil(t, err, "Err must be nil") assert.Equal(t, int16(33), v, "v must be equal to 33") }) + t.Run("decoder-api2", func(t *testing.T) { + var v int16 + dec := NewDecoder(strings.NewReader(`33`)) + defer dec.Release() + err := dec.Decode(&v) + assert.Nil(t, err, "Err must be nil") + assert.Equal(t, int16(33), v, "v must be equal to 33") + }) t.Run("decoder-api-invalid-json", func(t *testing.T) { var v int16 dec := NewDecoder(strings.NewReader(``)) @@ -964,6 +1020,18 @@ func TestDecoderInt8(t *testing.T) { err: true, }, { + name: "basic-big-overflow", + json: "137", + expectedResult: 0, + err: true, + }, + { + name: "basic-big-overflow", + json: "128", + expectedResult: 0, + err: true, + }, + { name: "basic-big-overflow2", json: "21474836483", expectedResult: 0, @@ -1120,6 +1188,14 @@ func TestDecoderInt8(t *testing.T) { assert.Nil(t, err, "Err must be nil") assert.Equal(t, int8(33), v, "v must be equal to 33") }) + t.Run("decoder-api2", func(t *testing.T) { + var v int8 + dec := NewDecoder(strings.NewReader(`33`)) + defer dec.Release() + err := dec.Decode(&v) + assert.Nil(t, err, "Err must be nil") + assert.Equal(t, int8(33), v, "v must be equal to 33") + }) t.Run("decoder-api-invalid-json", func(t *testing.T) { var v int8 dec := NewDecoder(strings.NewReader(``)) diff --git a/decode_number_uint.go b/decode_number_uint.go @@ -1,7 +1,6 @@ package gojay import ( - "fmt" "math" ) @@ -44,13 +43,7 @@ func (dec *Decoder) decodeUint8(v *uint8) error { } return nil default: - dec.err = InvalidUnmarshalError( - fmt.Sprintf( - "Cannot unmarshall to int, wrong char '%s' found at pos %d", - string(dec.data[dec.cursor]), - dec.cursor, - ), - ) + dec.err = dec.makeInvalidUnmarshalErr(v) err := dec.skipData() if err != nil { return err @@ -58,7 +51,7 @@ func (dec *Decoder) decodeUint8(v *uint8) error { return nil } } - return InvalidJSONError("Invalid JSON while parsing int") + return dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) getUint8(b byte) (uint8, error) { @@ -77,7 +70,7 @@ func (dec *Decoder) getUint8(b byte) (uint8, error) { return dec.atoui8(start, end), nil } // invalid json we expect numbers, dot (single one), comma, or spaces - return 0, InvalidJSONError("Invalid JSON while parsing number") + return 0, dec.raiseInvalidJSONErr(dec.cursor) } return dec.atoui8(start, end), nil } @@ -121,13 +114,7 @@ func (dec *Decoder) decodeUint16(v *uint16) error { } return nil default: - dec.err = InvalidUnmarshalError( - fmt.Sprintf( - "Cannot unmarshall to int, wrong char '%s' found at pos %d", - string(dec.data[dec.cursor]), - dec.cursor, - ), - ) + dec.err = dec.makeInvalidUnmarshalErr(v) err := dec.skipData() if err != nil { return err @@ -135,7 +122,7 @@ func (dec *Decoder) decodeUint16(v *uint16) error { return nil } } - return InvalidJSONError("Invalid JSON while parsing int") + return dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) getUint16(b byte) (uint16, error) { @@ -154,7 +141,7 @@ func (dec *Decoder) getUint16(b byte) (uint16, error) { return dec.atoui16(start, end), nil } // invalid json we expect numbers, dot (single one), comma, or spaces - return 0, InvalidJSONError("Invalid JSON while parsing number") + return 0, dec.raiseInvalidJSONErr(dec.cursor) } return dec.atoui16(start, end), nil } @@ -198,13 +185,7 @@ func (dec *Decoder) decodeUint32(v *uint32) error { } return nil default: - dec.err = InvalidUnmarshalError( - fmt.Sprintf( - "Cannot unmarshall to int, wrong char '%s' found at pos %d", - string(dec.data[dec.cursor]), - dec.cursor, - ), - ) + dec.err = dec.makeInvalidUnmarshalErr(v) err := dec.skipData() if err != nil { return err @@ -212,7 +193,7 @@ func (dec *Decoder) decodeUint32(v *uint32) error { return nil } } - return InvalidJSONError("Invalid JSON while parsing int") + return dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) getUint32(b byte) (uint32, error) { @@ -231,7 +212,7 @@ func (dec *Decoder) getUint32(b byte) (uint32, error) { return dec.atoui32(start, end), nil } // invalid json we expect numbers, dot (single one), comma, or spaces - return 0, InvalidJSONError("Invalid JSON while parsing number") + return 0, dec.raiseInvalidJSONErr(dec.cursor) } return dec.atoui32(start, end), nil } @@ -274,13 +255,7 @@ func (dec *Decoder) decodeUint64(v *uint64) error { } return nil default: - dec.err = InvalidUnmarshalError( - fmt.Sprintf( - "Cannot unmarshall to int, wrong char '%s' found at pos %d", - string(dec.data[dec.cursor]), - dec.cursor, - ), - ) + dec.err = dec.makeInvalidUnmarshalErr(v) err := dec.skipData() if err != nil { return err @@ -288,7 +263,7 @@ func (dec *Decoder) decodeUint64(v *uint64) error { return nil } } - return InvalidJSONError("Invalid JSON while parsing int") + return dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) getUint64(b byte) (uint64, error) { @@ -305,7 +280,7 @@ func (dec *Decoder) getUint64(b byte) (uint64, error) { return dec.atoui64(start, end), nil } // invalid json we expect numbers, dot (single one), comma, or spaces - return 0, InvalidJSONError("Invalid JSON while parsing number") + return 0, dec.raiseInvalidJSONErr(dec.cursor) } return dec.atoui64(start, end), nil } @@ -323,18 +298,18 @@ func (dec *Decoder) atoui64(start, end int) uint64 { for i := start + 1; i < end; i++ { uintv := uint64(digits[dec.data[i]]) if val > maxUint64toMultiply { - dec.err = InvalidUnmarshalError("Overflows uint64") + dec.err = dec.makeInvalidUnmarshalErr(val) return 0 } val = (val << 3) + (val << 1) if math.MaxUint64-val < uintv { - dec.err = InvalidUnmarshalError("Overflows uint64") + dec.err = dec.makeInvalidUnmarshalErr(val) return 0 } val += uintv } } else { - dec.err = InvalidUnmarshalError("Overflows uint64") + dec.err = dec.makeInvalidUnmarshalErr(val) return 0 } return val @@ -354,18 +329,18 @@ func (dec *Decoder) atoui32(start, end int) uint32 { for i := start + 1; i < end; i++ { uintv := uint32(digits[dec.data[i]]) if val > maxUint32toMultiply { - dec.err = InvalidUnmarshalError("Overflows uint32") + dec.err = dec.makeInvalidUnmarshalErr(val) return 0 } val = (val << 3) + (val << 1) if math.MaxUint32-val < uintv { - dec.err = InvalidUnmarshalError("Overflows int32") + dec.err = dec.makeInvalidUnmarshalErr(val) return 0 } val += uintv } } else if ll > maxUint32Length { - dec.err = InvalidUnmarshalError("Overflows uint32") + dec.err = dec.makeInvalidUnmarshalErr(val) val = 0 } return val @@ -385,18 +360,18 @@ func (dec *Decoder) atoui16(start, end int) uint16 { for i := start + 1; i < end; i++ { uintv := uint16(digits[dec.data[i]]) if val > maxUint16toMultiply { - dec.err = InvalidUnmarshalError("Overflows uint16") + dec.err = dec.makeInvalidUnmarshalErr(val) return 0 } val = (val << 3) + (val << 1) if math.MaxUint16-val < uintv { - dec.err = InvalidUnmarshalError("Overflows uint16") + dec.err = dec.makeInvalidUnmarshalErr(val) return 0 } val += uintv } } else if ll > maxUint16Length { - dec.err = InvalidUnmarshalError("Overflows uint16") + dec.err = dec.makeInvalidUnmarshalErr(val) val = 0 } return val @@ -416,18 +391,18 @@ func (dec *Decoder) atoui8(start, end int) uint8 { for i := start + 1; i < end; i++ { uintv := uint8(digits[dec.data[i]]) if val > maxUint8toMultiply { - dec.err = InvalidUnmarshalError("Overflows uint8") + dec.err = dec.makeInvalidUnmarshalErr(val) return 0 } val = (val << 3) + (val << 1) if math.MaxUint8-val < uintv { - dec.err = InvalidUnmarshalError("Overflows uint8") + dec.err = dec.makeInvalidUnmarshalErr(val) return 0 } val += uintv } } else if ll > maxUint8Length { - dec.err = InvalidUnmarshalError("Overflows uint8") + dec.err = dec.makeInvalidUnmarshalErr(val) val = 0 } return val diff --git a/decode_number_uint_test.go b/decode_number_uint_test.go @@ -451,6 +451,14 @@ func TestDecoderUint16(t *testing.T) { assert.Nil(t, err, "Err must be nil") assert.Equal(t, uint16(33), v, "v must be equal to 33") }) + t.Run("decoder-api2", func(t *testing.T) { + var v uint16 + dec := NewDecoder(strings.NewReader(`33`)) + defer dec.Release() + err := dec.Decode(&v) + assert.Nil(t, err, "Err must be nil") + assert.Equal(t, uint16(33), v, "v must be equal to 33") + }) t.Run("decoder-api-json-error", func(t *testing.T) { var v uint16 dec := NewDecoder(strings.NewReader(``)) @@ -612,6 +620,14 @@ func TestDecoderUint8(t *testing.T) { assert.Nil(t, err, "Err must be nil") assert.Equal(t, uint8(33), v, "v must be equal to 33") }) + t.Run("decoder-api2", func(t *testing.T) { + var v uint8 + dec := NewDecoder(strings.NewReader(`33`)) + defer dec.Release() + err := dec.Decode(&v) + assert.Nil(t, err, "Err must be nil") + assert.Equal(t, uint8(33), v, "v must be equal to 33") + }) t.Run("decoder-api-json-error", func(t *testing.T) { var v uint8 dec := NewDecoder(strings.NewReader(``)) diff --git a/decode_object.go b/decode_object.go @@ -1,7 +1,6 @@ package gojay import ( - "fmt" "unsafe" ) @@ -36,6 +35,7 @@ func (dec *Decoder) decodeObject(j UnmarshalerJSONObject) (int, error) { } err = j.UnmarshalJSONObject(dec, k) if err != nil { + dec.err = err return 0, err } else if dec.called&1 == 0 { err := dec.skipData() @@ -57,6 +57,7 @@ func (dec *Decoder) decodeObject(j UnmarshalerJSONObject) (int, error) { } err = j.UnmarshalJSONObject(dec, k) if err != nil { + dec.err = err return 0, err } else if dec.called&1 == 0 { err := dec.skipData() @@ -87,14 +88,8 @@ func (dec *Decoder) decodeObject(j UnmarshalerJSONObject) (int, error) { dec.cursor++ return dec.cursor, nil default: - // can't unmarshall to struct - dec.err = InvalidUnmarshalError( - fmt.Sprintf( - "Cannot unmarshal to struct, wrong char '%s' found at pos %d", - string(dec.data[dec.cursor]), - dec.cursor, - ), - ) + // can't unmarshal to struct + dec.err = dec.makeInvalidUnmarshalErr(j) err := dec.skipData() if err != nil { return 0, err @@ -102,7 +97,7 @@ func (dec *Decoder) decodeObject(j UnmarshalerJSONObject) (int, error) { return dec.cursor, nil } } - return 0, InvalidJSONError("Invalid JSON while parsing object") + return 0, dec.raiseInvalidJSONErr(dec.length - 1) } func (dec *Decoder) skipObject() (int, error) { @@ -173,13 +168,16 @@ func (dec *Decoder) nextKey() (string, bool, error) { d := dec.data[start : end-1] return *(*string)(unsafe.Pointer(&d)), false, nil } - return "", false, InvalidJSONError("Invalid JSON while parsing object key") + return "", false, dec.raiseInvalidJSONErr(dec.cursor) case '}': dec.cursor = dec.cursor + 1 return "", true, nil + default: + // can't unmarshall to struct + return "", false, dec.raiseInvalidJSONErr(dec.cursor) } } - return "", false, InvalidJSONError("Invalid JSON while parsing object key") + return "", false, dec.raiseInvalidJSONErr(dec.length - 1) } func (dec *Decoder) skipData() error { @@ -235,9 +233,9 @@ func (dec *Decoder) skipData() error { dec.cursor = end return err } - return InvalidJSONError("Invalid JSON") + return dec.raiseInvalidJSONErr(dec.cursor) } - return InvalidJSONError("Invalid JSON") + return dec.raiseInvalidJSONErr(dec.cursor) } // DecodeObjectFunc is a custom func type implementating UnarshaleObject. diff --git a/decode_object_test.go b/decode_object_test.go @@ -2,7 +2,6 @@ package gojay import ( "io" - "log" "strings" "testing" @@ -10,16 +9,300 @@ import ( ) func TestDecodeObjectBasic(t *testing.T) { + testCases := []struct { + name string + json string + expectedResult testObject + err bool + errType interface{} + skipCheckResult bool + }{ + { + name: "basic", + json: `{ + "testStr": "hello world!", + "testInt": 4535, + "testBool": true, + "testFloat32": 2.345, + "testFloat64": 123.677, + "testInt8": 23, + "testInt16": 1245, + "testInt32": 456778, + "testInt64": 1446685358, + "testUint8": 255, + "testUint16": 3455, + "testUint32": 343443, + "testUint64": 545665757 + }`, + expectedResult: testObject{ + testStr: "hello world!", + testInt: 4535, + testBool: true, + testFloat32: 2.345, + testFloat64: 123.677, + testInt8: 23, + testInt16: 1245, + testInt32: 456778, + testInt64: 1446685358, + testUint8: 255, + testUint16: 3455, + testUint32: 343443, + testUint64: 545665757, + }, + err: false, + }, + { + name: "basic-err", + json: `{ + "testStr": "hello world!", + "testInt": 453q5, + "testBool": trae, + "testFloat32": 2q.345, + "testFloat64": 12x3.677, + "testInt8": 2s3, + "testInt16": 1245, + "testInt32": 4567q78, + "testInt64": 14466e85358, + "testUint8": 2s55, + "testUint16": 345i5, + "testUint32": 343q443, + "testUint64": 5456657z57 + }`, + expectedResult: testObject{}, + err: true, + }, + { + name: "basic-err2", + json: `{ + "testStr": "hello world!", + "testInt": 4535, + "testBool": true, + "testFloat32": 2.345, + "testFloat64": 123.677, + "testInt8": 23, + "testInt16": 1245, + "testInt32": 4567x78, + "testInt64": 1446685358, + "testUint8": 255, + "testUint16": 3455, + "testUint32": 343443, + "testUint64": 545665757 + }`, + expectedResult: testObject{}, + err: true, + }, + { + name: "basic-err-float32", + json: `{ + "testStr": "hello world!", + "testInt": 4535, + "testBool": true, + "testFloat32": 2q.345, + "testFloat64": 123.677, + "testInt8": 23, + "testInt16": 1245, + "testInt32": 456778, + "testInt64": 1446685358, + "testUint8": 255, + "testUint16": 3455, + "testUint32": 343443, + "testUint64": 545665757 + }`, + expectedResult: testObject{}, + err: true, + }, + { + name: "basic-err-float64", + json: `{ + "testStr": "hello world!", + "testInt": 4535, + "testBool": true, + "testFloat32": 2.345, + "testFloat64": 1x23.677, + "testInt8": 23, + "testInt16": 1245, + "testInt32": 456778, + "testInt64": 1446685358, + "testUint8": 255, + "testUint16": 3455, + "testUint32": 343443, + "testUint64": 545665757 + }`, + expectedResult: testObject{}, + err: true, + }, + { + name: "basic-err3", + json: `{ + "testStr": "hello world!", + "testInt": 4535, + "testBool": true, + "testFloat32": 2.345, + "testFloat64": 123.677, + "testInt8": 2q3, + "testInt16": 1245, + "testInt32": 456778, + "testInt64": 1446685358, + "testUint8": 255, + "testUint16": 3455, + "testUint32": 343443, + "testUint64": 545665757 + }`, + expectedResult: testObject{}, + err: true, + }, + { + name: "basic-err-int16", + json: `{ + "testStr": "hello world!", + "testInt": 4535, + "testBool": true, + "testFloat32": 2.345, + "testFloat64": 123.677, + "testInt8": 23, + "testInt16": 1x245, + "testInt32": 456778, + "testInt64": 1446685358, + "testUint8": 255, + "testUint16": 3455, + "testUint32": 343443, + "testUint64": 545665757 + }`, + expectedResult: testObject{}, + err: true, + }, + { + name: "basic-err-int64", + json: `{ + "testStr": "hello world!", + "testInt": 4535, + "testBool": true, + "testFloat32": 2.345, + "testFloat64": 123.677, + "testInt8": 23, + "testInt16": 1245, + "testInt32": 456778, + "testInt64": 1446q685358, + "testUint8": 255, + "testUint16": 3455, + "testUint32": 343443, + "testUint64": 545665757 + }`, + expectedResult: testObject{}, + err: true, + }, + { + name: "basic-err-uint8", + json: `{ + "testStr": "hello world!", + "testInt": 4535, + "testBool": true, + "testFloat32": 2.345, + "testFloat64": 123.677, + "testInt8": 23, + "testInt16": 1245, + "testInt32": 456778, + "testInt64": 1446685358, + "testUint8": 2x55, + "testUint16": 3455, + "testUint32": 343443, + "testUint64": 545665757 + }`, + expectedResult: testObject{}, + err: true, + }, + { + name: "basic-err-uint16", + json: `{ + "testStr": "hello world!", + "testInt": 4535, + "testBool": true, + "testFloat32": 2.345, + "testFloat64": 123.677, + "testInt8": 23, + "testInt16": 1245, + "testInt32": 456778, + "testInt64": 1446685358, + "testUint8": 255, + "testUint16": 3x455, + "testUint32": 343443, + "testUint64": 545665757 + }`, + expectedResult: testObject{}, + err: true, + }, + { + name: "basic-err-uint32", + json: `{ + "testStr": "hello world!", + "testInt": 4535, + "testBool": true, + "testFloat32": 2.345, + "testFloat64": 123.677, + "testInt8": 23, + "testInt16": 1245, + "testInt32": 456778, + "testInt64": 1446685358, + "testUint8": 255, + "testUint16": 3455, + "testUint32": 3x43443, + "testUint64": 545665757 + }`, + expectedResult: testObject{}, + err: true, + }, + { + name: "basic-err-uint64", + json: `{ + "testStr": "hello world!", + "testInt": 4535, + "testBool": true, + "testFloat32": 2.345, + "testFloat64": 123.677, + "testInt8": 23, + "testInt16": 1245, + "testInt32": 456778, + "testInt64": 1446685358, + "testUint8": 255, + "testUint16": 3455, + "testUint32": 343443, + "testUint64": 5456x65757 + }`, + expectedResult: testObject{}, + err: true, + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + s := testObject{} + dec := BorrowDecoder(strings.NewReader(testCase.json)) + defer dec.Release() + err := dec.Decode(&s) + if testCase.err { + t.Log(err) + assert.NotNil(t, err, "err should not be nil") + if testCase.errType != nil { + assert.IsType(t, testCase.errType, err, "err should be of the given type") + } + return + } + assert.Nil(t, err, "err should be nil") + if !testCase.skipCheckResult { + assert.Equal(t, testCase.expectedResult, s, "value at given index should be the same as expected results") + } + }) + } } func TestDecodeObjectComplex(t *testing.T) { testCases := []struct { - name string - json string - expectedResult testObjectComplex - err bool - errType interface{} + name string + json string + expectedResult testObjectComplex + err bool + errType interface{} + skipCheckResult bool }{ { name: "basic", @@ -47,6 +330,14 @@ func TestDecodeObjectComplex(t *testing.T) { }, err: false, }, + { + name: "complex-json-err", + json: `{"testSubObject":{"testStr":"some string,"testInt":124465,"testUint16":120, "testUint8":15,"testInt16":-135,"testInt8":-23},"testSubSliceInts":[1,2],"testStr":"some \\n string"}`, + expectedResult: testObjectComplex{ + testSubObject: &testObject{}, + }, + err: true, + }, } for _, testCase := range testCases { @@ -59,15 +350,17 @@ func TestDecodeObjectComplex(t *testing.T) { defer dec.Release() err := dec.Decode(&s) if testCase.err { + t.Log(err) assert.NotNil(t, err, "err should not be nil") if testCase.errType != nil { assert.IsType(t, testCase.errType, err, "err should be of the given type") } return } - log.Print(s, testCase.name) assert.Nil(t, err, "err should be nil") - assert.Equal(t, testCase.expectedResult, s, "value at given index should be the same as expected results") + if !testCase.skipCheckResult { + assert.Equal(t, testCase.expectedResult, s, "value at given index should be the same as expected results") + } }) } } @@ -288,7 +581,7 @@ func TestDecodeObjComplex(t *testing.T) { result := jsonObjectComplex{} err := UnmarshalJSONObject(jsonComplex, &result) assert.NotNil(t, err, "err should not be as invalid type as been encountered nil") - assert.Equal(t, `Cannot unmarshal to struct, wrong char '"' found at pos 639`, err.Error(), "err should not be as invalid type as been encountered nil") + assert.Equal(t, `Cannot unmarshal JSON to type '*gojay.jsonObjectComplex'`, err.Error(), "err should not be as invalid type as been encountered nil") assert.Equal(t, `{"test":"1","test1":2}`, result.Test, "result.Test is not expected value") assert.Equal(t, "\\\\\\\\\n", result.Test2, "result.Test2 is not expected value") assert.Equal(t, 1, result.Test3, "result.test3 is not expected value") diff --git a/decode_stream_test.go b/decode_stream_test.go @@ -168,7 +168,7 @@ func TestStreamDecodingObjectsParallel(t *testing.T) { expectations: func(err error, result []*TestObj, t *testing.T) { assert.NotNil(t, err, "err is not nil as JSON is invalid") assert.IsType(t, InvalidJSONError(""), err, "err is of type InvalidJSONError") - assert.Equal(t, "Invalid JSON", err.Error(), "err message is Invalid JSON") + assert.Equal(t, "Invalid JSON, wrong char 'i' found at position 6", err.Error(), "err message is Invalid JSON") }, }, } @@ -276,7 +276,7 @@ func TestStreamDecodingStringsParallel(t *testing.T) { assert.NotNil(t, err, "err should not be nil") assert.IsType(t, InvalidJSONError(""), err, "err is of type InvalidJSONError") - assert.Equal(t, "Invalid JSON", err.Error(), "err message is Invalid JSON") + assert.Equal(t, "Invalid JSON, wrong char 'w' found at position 6", err.Error(), "err message is Invalid JSON") }, }, } diff --git a/decode_string.go b/decode_string.go @@ -1,7 +1,6 @@ package gojay import ( - "fmt" "unsafe" ) @@ -41,13 +40,7 @@ func (dec *Decoder) decodeString(v *string) error { dec.cursor++ return nil default: - dec.err = InvalidUnmarshalError( - fmt.Sprintf( - "Cannot unmarshall to string, wrong char '%s' found at pos %d", - string(dec.data[dec.cursor]), - dec.cursor, - ), - ) + dec.err = dec.makeInvalidUnmarshalErr(v) err := dec.skipData() if err != nil { return err @@ -70,7 +63,7 @@ func (dec *Decoder) parseEscapedString() error { case '"': // nSlash must be odd if nSlash&1 != 1 { - return InvalidJSONError("Invalid JSON unescaped character") + return dec.raiseInvalidJSONErr(dec.cursor) } diff := (nSlash - 1) >> 1 dec.data = append(dec.data[:start+diff-1], dec.data[dec.cursor-1:]...) @@ -102,7 +95,7 @@ func (dec *Decoder) parseEscapedString() error { // divide nSlash - 1 by 2 and leave last one // else divide nSlash by 2 and leave the letter if nSlash&1 != 0 { - return InvalidJSONError("Invalid JSON unescaped character") + return dec.raiseInvalidJSONErr(dec.cursor) } var diff int diff = nSlash >> 1 @@ -116,7 +109,7 @@ func (dec *Decoder) parseEscapedString() error { // divide nSlash - 1 by 2 and leave last one // else divide nSlash by 2 and leave the letter if nSlash&1 != 0 { - return InvalidJSONError("Invalid JSON unescaped character") + return dec.raiseInvalidJSONErr(dec.cursor) } var diff int diff = nSlash >> 1 @@ -130,7 +123,7 @@ func (dec *Decoder) parseEscapedString() error { // divide nSlash - 1 by 2 and leave last one // else divide nSlash by 2 and leave the letter if nSlash&1 != 0 { - return InvalidJSONError("Invalid JSON unescaped character") + return dec.raiseInvalidJSONErr(dec.cursor) } var diff int diff = nSlash >> 1 @@ -144,7 +137,7 @@ func (dec *Decoder) parseEscapedString() error { // divide nSlash - 1 by 2 and leave last one // else divide nSlash by 2 and leave the letter if nSlash&1 != 0 { - return InvalidJSONError("Invalid JSON unescaped character") + return dec.raiseInvalidJSONErr(dec.cursor) } var diff int diff = nSlash >> 1 @@ -158,7 +151,7 @@ func (dec *Decoder) parseEscapedString() error { // divide nSlash - 1 by 2 and leave last one // else divide nSlash by 2 and leave the letter if nSlash&1 != 0 { - return InvalidJSONError("Invalid JSON unescaped character") + return dec.raiseInvalidJSONErr(dec.cursor) } var diff int diff = nSlash >> 1 @@ -169,7 +162,7 @@ func (dec *Decoder) parseEscapedString() error { default: // nSlash must be even if nSlash&1 == 1 { - return InvalidJSONError("Invalid JSON unescaped character") + return dec.raiseInvalidJSONErr(dec.cursor) } diff := nSlash >> 1 dec.data = append(dec.data[:start+diff-1], dec.data[dec.cursor-1:]...) @@ -204,7 +197,7 @@ func (dec *Decoder) getString() (int, int, error) { continue } } - return 0, 0, InvalidJSONError("Invalid JSON while parsing string") + return 0, 0, dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) skipEscapedString() error { @@ -218,7 +211,7 @@ func (dec *Decoder) skipEscapedString() error { case '"': // nSlash must be odd if nSlash&1 != 1 { - return InvalidJSONError("Invalid JSON unescaped character") + return dec.raiseInvalidJSONErr(dec.cursor) } return nil case 'n', 'r', 't': @@ -226,13 +219,13 @@ func (dec *Decoder) skipEscapedString() error { default: // nSlash must be even if nSlash&1 == 1 { - return InvalidJSONError("Invalid JSON unescaped character") + return dec.raiseInvalidJSONErr(dec.cursor) } return nil } } } - return InvalidJSONError("Invalid JSON") + return dec.raiseInvalidJSONErr(dec.cursor) } func (dec *Decoder) skipString() error { @@ -254,5 +247,5 @@ func (dec *Decoder) skipString() error { continue } } - return InvalidJSONError("Invalid JSON while parsing string") + return dec.raiseInvalidJSONErr(len(dec.data) - 1) } diff --git a/decode_string_test.go b/decode_string_test.go @@ -2,7 +2,6 @@ package gojay import ( "fmt" - "log" "strings" "sync" "testing" @@ -151,6 +150,12 @@ func TestDecoderString(t *testing.T) { err: true, }, { + name: "utf16-surrogate-err3", + json: `"\uD834`, + expectedResult: ``, + err: true, + }, + { name: "utf16-surrogate-followed-by-control-char", json: `"\uD834\t"`, expectedResult: "�\t", @@ -373,7 +378,6 @@ func TestSkipString(t *testing.T) { if testCase.errType != nil { assert.IsType(t, testCase.errType, err, "err should be of expected type") } - log.Print(err) } else { assert.Nil(t, err, "err should be nil") } diff --git a/decode_unsafe.go b/decode_unsafe.go @@ -57,21 +57,41 @@ func (u decUnsafe) Unmarshal(data []byte, v interface{}) error { dec.length = len(data) dec.data = data err = dec.decodeInt(vt) - case *int32: + case *int8: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data - err = dec.decodeInt32(vt) - case *uint32: + err = dec.decodeInt8(vt) + case *int16: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data - err = dec.decodeUint32(vt) + err = dec.decodeInt16(vt) + case *int32: + dec = borrowDecoder(nil, 0) + dec.length = len(data) + dec.data = data + err = dec.decodeInt32(vt) case *int64: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeInt64(vt) + case *uint8: + dec = borrowDecoder(nil, 0) + dec.length = len(data) + dec.data = data + err = dec.decodeUint8(vt) + case *uint16: + dec = borrowDecoder(nil, 0) + dec.length = len(data) + dec.data = data + err = dec.decodeUint16(vt) + case *uint32: + dec = borrowDecoder(nil, 0) + dec.length = len(data) + dec.data = data + err = dec.decodeUint32(vt) case *uint64: dec = borrowDecoder(nil, 0) dec.length = len(data) @@ -82,6 +102,11 @@ func (u decUnsafe) Unmarshal(data []byte, v interface{}) error { dec.length = len(data) dec.data = data err = dec.decodeFloat64(vt) + case *float32: + dec = borrowDecoder(nil, 0) + dec.length = len(data) + dec.data = data + err = dec.decodeFloat32(vt) case *bool: dec = borrowDecoder(nil, 0) dec.length = len(data) diff --git a/decode_unsafe_test.go b/decode_unsafe_test.go @@ -46,6 +46,36 @@ func TestUnmarshalUnsafeAllTypes(t *testing.T) { }, }, { + v: new(int8), + d: []byte(`1`), + name: "test decode int8", + expectations: func(err error, v interface{}, t *testing.T) { + vt := v.(*int8) + assert.Nil(t, err, "err must be nil") + assert.Equal(t, int8(1), *vt, "v must be equal to 1") + }, + }, + { + v: new(int16), + d: []byte(`1`), + name: "test decode int16", + expectations: func(err error, v interface{}, t *testing.T) { + vt := v.(*int16) + assert.Nil(t, err, "err must be nil") + assert.Equal(t, int16(1), *vt, "v must be equal to 1") + }, + }, + { + v: new(int32), + d: []byte(`1`), + name: "test decode int32", + expectations: func(err error, v interface{}, t *testing.T) { + vt := v.(*int32) + assert.Nil(t, err, "err must be nil") + assert.Equal(t, int32(1), *vt, "v must be equal to 1") + }, + }, + { v: new(int64), d: []byte(`1`), name: "test decode int64", @@ -106,6 +136,26 @@ func TestUnmarshalUnsafeAllTypes(t *testing.T) { }, }, { + v: new(uint8), + d: []byte(`1`), + name: "test decode int8", + expectations: func(err error, v interface{}, t *testing.T) { + vt := v.(*uint8) + assert.Nil(t, err, "err must be nil") + assert.Equal(t, uint8(1), *vt, "v must be equal to 1") + }, + }, + { + v: new(uint16), + d: []byte(`1`), + name: "test decode uint16", + expectations: func(err error, v interface{}, t *testing.T) { + vt := v.(*uint16) + assert.Nil(t, err, "err must be nil") + assert.Equal(t, uint16(1), *vt, "v must be equal to 1") + }, + }, + { v: new(float64), d: []byte(`1.15`), name: "test decode float64", @@ -126,6 +176,16 @@ func TestUnmarshalUnsafeAllTypes(t *testing.T) { }, }, { + v: new(float32), + d: []byte(`1.15`), + name: "test decode float64", + expectations: func(err error, v interface{}, t *testing.T) { + vt := v.(*float32) + assert.Nil(t, err, "err must be nil") + assert.Equal(t, float32(1.15), *vt, "v must be equal to 1") + }, + }, + { v: new(bool), d: []byte(`true`), name: "test decode bool true", @@ -220,6 +280,15 @@ func TestUnmarshalUnsafeAllTypes(t *testing.T) { assert.Equal(t, fmt.Sprintf(invalidUnmarshalErrorMsg, reflect.TypeOf(v).String()), err.Error(), "err message should be equal to invalidUnmarshalErrorMsg") }, }, + { + v: new(int), + d: []byte(`1a2`), + name: "test decode invalid json", + expectations: func(err error, v interface{}, t *testing.T) { + assert.NotNil(t, err, "err must not be nil") + assert.IsType(t, InvalidJSONError(""), err, "err must be of type InvalidJSONError") + }, + }, } for _, testCase := range testCases { testCase := testCase diff --git a/errors.go b/errors.go @@ -1,6 +1,10 @@ package gojay -const invalidJSONCharErrorMsg = "Invalid JSON character %c found at position %d" +import ( + "fmt" +) + +const invalidJSONCharErrorMsg = "Invalid JSON, wrong char '%c' found at position %d" // InvalidJSONError is a type representing an error returned when // Decoding encounters invalid JSON. @@ -10,7 +14,18 @@ func (err InvalidJSONError) Error() string { return string(err) } -const invalidUnmarshalErrorMsg = "Invalid type %s provided to Unmarshal" +func (dec *Decoder) raiseInvalidJSONErr(pos int) error { + dec.err = InvalidJSONError( + fmt.Sprintf( + invalidJSONCharErrorMsg, + dec.data[pos], + pos, + ), + ) + return dec.err +} + +const invalidUnmarshalErrorMsg = "Cannot unmarshal JSON to type '%T'" // InvalidUnmarshalError is a type representing an error returned when // Decoding cannot unmarshal JSON to the receiver type for various reasons. @@ -20,6 +35,15 @@ func (err InvalidUnmarshalError) Error() string { return string(err) } +func (dec *Decoder) makeInvalidUnmarshalErr(v interface{}) error { + return InvalidUnmarshalError( + fmt.Sprintf( + invalidUnmarshalErrorMsg, + v, + ), + ) +} + const invalidMarshalErrorMsg = "Invalid type %s provided to Marshal" // InvalidMarshalError is a type representing an error returned when diff --git a/gojay_test.go b/gojay_test.go @@ -50,6 +50,8 @@ func (t *testObject) UnmarshalJSONObject(dec *Decoder, k string) error { return dec.AddInt(&t.testInt) case "testInt64": return dec.AddInt64(&t.testInt64) + case "testInt32": + return dec.AddInt32(&t.testInt32) case "testInt16": return dec.AddInt16(&t.testInt16) case "testInt8":