gojay

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

commit 8a6a58d6ae768be5203aef1e639c27c4ddd3b943
parent 1f3d8b54c1fd186494c8f1fe59f24e1f26a9cf18
Author: francoispqt <francois@parquet.ninja>
Date:   Sat, 25 Aug 2018 22:12:03 +0800

add null decoding for primitive types

Diffstat:
Mdecode.go | 341+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdecode_bool.go | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdecode_bool_test.go | 232+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdecode_number_float.go | 90+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdecode_number_float_test.go | 667+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Mdecode_number_int.go | 235+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdecode_number_int_test.go | 2165+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
Mdecode_number_uint.go | 176+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdecode_number_uint_test.go | 695++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Mdecode_string.go | 43++++++++++++++++++++++++++++++++++++++++++-
Mdecode_string_test.go | 316+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
11 files changed, 4621 insertions(+), 396 deletions(-)

diff --git a/decode.go b/decode.go @@ -78,66 +78,131 @@ func Unmarshal(data []byte, v interface{}) error { dec.length = len(data) dec.data = data err = dec.decodeString(vt) + case **string: + dec = borrowDecoder(nil, 0) + dec.length = len(data) + dec.data = data + err = dec.decodeStringNull(vt) case *int: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeInt(vt) + case **int: + dec = borrowDecoder(nil, 0) + dec.length = len(data) + dec.data = data + err = dec.decodeIntNull(vt) case *int8: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeInt8(vt) + case **int8: + dec = borrowDecoder(nil, 0) + dec.length = len(data) + dec.data = data + err = dec.decodeInt8Null(vt) case *int16: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeInt16(vt) + case **int16: + dec = borrowDecoder(nil, 0) + dec.length = len(data) + dec.data = data + err = dec.decodeInt16Null(vt) case *int32: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeInt32(vt) + case **int32: + dec = borrowDecoder(nil, 0) + dec.length = len(data) + dec.data = data + err = dec.decodeInt32Null(vt) case *int64: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeInt64(vt) + case **int64: + dec = borrowDecoder(nil, 0) + dec.length = len(data) + dec.data = data + err = dec.decodeInt64Null(vt) case *uint8: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeUint8(vt) + case **uint8: + dec = borrowDecoder(nil, 0) + dec.length = len(data) + dec.data = data + err = dec.decodeUint8Null(vt) case *uint16: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeUint16(vt) + case **uint16: + dec = borrowDecoder(nil, 0) + dec.length = len(data) + dec.data = data + err = dec.decodeUint16Null(vt) case *uint32: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeUint32(vt) + case **uint32: + dec = borrowDecoder(nil, 0) + dec.length = len(data) + dec.data = data + err = dec.decodeUint32Null(vt) case *uint64: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeUint64(vt) + case **uint64: + dec = borrowDecoder(nil, 0) + dec.length = len(data) + dec.data = data + err = dec.decodeUint64Null(vt) case *float64: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeFloat64(vt) + case **float64: + dec = borrowDecoder(nil, 0) + dec.length = len(data) + dec.data = data + err = dec.decodeFloat64Null(vt) case *float32: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeFloat32(vt) + case **float32: + dec = borrowDecoder(nil, 0) + dec.length = len(data) + dec.data = data + err = dec.decodeFloat32Null(vt) case *bool: dec = borrowDecoder(nil, 0) dec.length = len(data) dec.data = data err = dec.decodeBool(vt) + case **bool: + dec = borrowDecoder(nil, 0) + dec.length = len(data) + dec.data = data + err = dec.decodeBoolNull(vt) case UnmarshalerJSONObject: dec = borrowDecoder(nil, 0) dec.length = len(data) @@ -197,30 +262,56 @@ func (dec *Decoder) Decode(v interface{}) error { switch vt := v.(type) { case *string: err = dec.decodeString(vt) + case **string: + err = dec.decodeStringNull(vt) case *int: err = dec.decodeInt(vt) + case **int: + err = dec.decodeIntNull(vt) case *int8: err = dec.decodeInt8(vt) + case **int8: + err = dec.decodeInt8Null(vt) case *int16: err = dec.decodeInt16(vt) + case **int16: + err = dec.decodeInt16Null(vt) case *int32: err = dec.decodeInt32(vt) + case **int32: + err = dec.decodeInt32Null(vt) case *int64: err = dec.decodeInt64(vt) + case **int64: + err = dec.decodeInt64Null(vt) case *uint8: err = dec.decodeUint8(vt) + case **uint8: + err = dec.decodeUint8Null(vt) case *uint16: err = dec.decodeUint16(vt) + case **uint16: + err = dec.decodeUint16Null(vt) case *uint32: err = dec.decodeUint32(vt) + case **uint32: + err = dec.decodeUint32Null(vt) case *uint64: err = dec.decodeUint64(vt) + case **uint64: + err = dec.decodeUint64Null(vt) case *float64: err = dec.decodeFloat64(vt) + case **float64: + err = dec.decodeFloat64Null(vt) case *float32: err = dec.decodeFloat32(vt) + case **float32: + err = dec.decodeFloat32Null(vt) case *bool: err = dec.decodeBool(vt) + case **bool: + err = dec.decodeBoolNull(vt) case UnmarshalerJSONObject: _, err = dec.decodeObject(vt) case UnmarshalerJSONArray: @@ -244,72 +335,156 @@ func (dec *Decoder) AddInt(v *int) error { return dec.Int(v) } +// AddIntNull decodes the next key to an *int. +// If next key value overflows int, an InvalidUnmarshalError error will be returned. +// If a `null` is encountered, gojay does not change the value of the pointer. +func (dec *Decoder) AddIntNull(v **int) error { + return dec.IntNull(v) +} + // AddInt8 decodes the next key to an *int. // If next key value overflows int8, an InvalidUnmarshalError error will be returned. func (dec *Decoder) AddInt8(v *int8) error { return dec.Int8(v) } +// AddInt8Null decodes the next key to an *int. +// If next key value overflows int8, an InvalidUnmarshalError error will be returned. +// If a `null` is encountered, gojay does not change the value of the pointer. +func (dec *Decoder) AddInt8Null(v **int8) error { + return dec.Int8Null(v) +} + // AddInt16 decodes the next key to an *int. // If next key value overflows int16, an InvalidUnmarshalError error will be returned. func (dec *Decoder) AddInt16(v *int16) error { return dec.Int16(v) } +// AddInt16Null decodes the next key to an *int. +// If next key value overflows int16, an InvalidUnmarshalError error will be returned. +// If a `null` is encountered, gojay does not change the value of the pointer. +func (dec *Decoder) AddInt16Null(v **int16) error { + return dec.Int16Null(v) +} + // AddInt32 decodes the next key to an *int. // If next key value overflows int32, an InvalidUnmarshalError error will be returned. func (dec *Decoder) AddInt32(v *int32) error { return dec.Int32(v) } +// AddInt32Null decodes the next key to an *int. +// If next key value overflows int32, an InvalidUnmarshalError error will be returned. +// If a `null` is encountered, gojay does not change the value of the pointer. +func (dec *Decoder) AddInt32Null(v **int32) error { + return dec.Int32Null(v) +} + // AddInt64 decodes the next key to an *int. // If next key value overflows int64, an InvalidUnmarshalError error will be returned. func (dec *Decoder) AddInt64(v *int64) error { return dec.Int64(v) } +// AddInt64Null decodes the next key to an *int. +// If next key value overflows int64, an InvalidUnmarshalError error will be returned. +// If a `null` is encountered, gojay does not change the value of the pointer. +func (dec *Decoder) AddInt64Null(v **int64) error { + return dec.Int64Null(v) +} + // AddUint8 decodes the next key to an *int. // If next key value overflows uint8, an InvalidUnmarshalError error will be returned. func (dec *Decoder) AddUint8(v *uint8) error { return dec.Uint8(v) } +// AddUint8Null decodes the next key to an *int. +// If next key value overflows uint8, an InvalidUnmarshalError error will be returned. +// If a `null` is encountered, gojay does not change the value of the pointer. +func (dec *Decoder) AddUint8Null(v **uint8) error { + return dec.Uint8Null(v) +} + // AddUint16 decodes the next key to an *int. // If next key value overflows uint16, an InvalidUnmarshalError error will be returned. func (dec *Decoder) AddUint16(v *uint16) error { return dec.Uint16(v) } +// AddUint16Null decodes the next key to an *int. +// If next key value overflows uint16, an InvalidUnmarshalError error will be returned. +// If a `null` is encountered, gojay does not change the value of the pointer. +func (dec *Decoder) AddUint16Null(v **uint16) error { + return dec.Uint16Null(v) +} + // AddUint32 decodes the next key to an *int. // If next key value overflows uint32, an InvalidUnmarshalError error will be returned. func (dec *Decoder) AddUint32(v *uint32) error { return dec.Uint32(v) } +// AddUint32Null decodes the next key to an *int. +// If next key value overflows uint32, an InvalidUnmarshalError error will be returned. +// If a `null` is encountered, gojay does not change the value of the pointer. +func (dec *Decoder) AddUint32Null(v **uint32) error { + return dec.Uint32Null(v) +} + // AddUint64 decodes the next key to an *int. // If next key value overflows uint64, an InvalidUnmarshalError error will be returned. func (dec *Decoder) AddUint64(v *uint64) error { return dec.Uint64(v) } +// AddUint64Null decodes the next key to an *int. +// If next key value overflows uint64, an InvalidUnmarshalError error will be returned. +// If a `null` is encountered, gojay does not change the value of the pointer. +func (dec *Decoder) AddUint64Null(v **uint64) error { + return dec.Uint64Null(v) +} + // AddFloat decodes the next key to a *float64. // If next key value overflows float64, an InvalidUnmarshalError error will be returned. func (dec *Decoder) AddFloat(v *float64) error { return dec.Float64(v) } +// AddFloatNull decodes the next key to a *float64. +// If next key value overflows float64, an InvalidUnmarshalError error will be returned. +// If a `null` is encountered, gojay does not change the value of the pointer. +func (dec *Decoder) AddFloatNull(v **float64) error { + return dec.Float64Null(v) +} + // AddFloat64 decodes the next key to a *float64. // If next key value overflows float64, an InvalidUnmarshalError error will be returned. func (dec *Decoder) AddFloat64(v *float64) error { return dec.Float64(v) } +// AddFloat64Null decodes the next key to a *float64. +// If next key value overflows float64, an InvalidUnmarshalError error will be returned. +// If a `null` is encountered, gojay does not change the value of the pointer. +func (dec *Decoder) AddFloat64Null(v **float64) error { + return dec.Float64Null(v) +} + // AddFloat32 decodes the next key to a *float64. // If next key value overflows float64, an InvalidUnmarshalError error will be returned. func (dec *Decoder) AddFloat32(v *float32) error { return dec.Float32(v) } +// AddFloat32Null decodes the next key to a *float64. +// If next key value overflows float64, an InvalidUnmarshalError error will be returned. +// If a `null` is encountered, gojay does not change the value of the pointer. +func (dec *Decoder) AddFloat32Null(v **float32) error { + return dec.Float32Null(v) +} + // AddBool decodes the next key to a *bool. // If next key is neither null nor a JSON boolean, an InvalidUnmarshalError will be returned. // If next key is null, bool will be false. @@ -317,12 +492,27 @@ func (dec *Decoder) AddBool(v *bool) error { return dec.Bool(v) } +// AddBoolNull decodes the next key to a *bool. +// If next key is neither null nor a JSON boolean, an InvalidUnmarshalError will be returned. +// If next key is null, bool will be false. +// If a `null` is encountered, gojay does not change the value of the pointer. +func (dec *Decoder) AddBoolNull(v **bool) error { + return dec.BoolNull(v) +} + // AddString decodes the next key to a *string. // If next key is not a JSON string nor null, InvalidUnmarshalError will be returned. func (dec *Decoder) AddString(v *string) error { return dec.String(v) } +// AddStringNull decodes the next key to a *string. +// If next key is not a JSON string nor null, InvalidUnmarshalError will be returned. +// If a `null` is encountered, gojay does not change the value of the pointer. +func (dec *Decoder) AddStringNull(v **string) error { + return dec.StringNull(v) +} + // AddObject decodes the next key to a UnmarshalerJSONObject. func (dec *Decoder) AddObject(v UnmarshalerJSONObject) error { return dec.Object(v) @@ -344,6 +534,17 @@ func (dec *Decoder) Int(v *int) error { return nil } +// IntNull decodes the next key to an *int. +// If next key value overflows int, an InvalidUnmarshalError error will be returned. +func (dec *Decoder) IntNull(v **int) error { + err := dec.decodeIntNull(v) + if err != nil { + return err + } + dec.called |= 1 + return nil +} + // Int8 decodes the next key to an *int. // If next key value overflows int8, an InvalidUnmarshalError error will be returned. func (dec *Decoder) Int8(v *int8) error { @@ -355,6 +556,17 @@ func (dec *Decoder) Int8(v *int8) error { return nil } +// Int8Null decodes the next key to an *int. +// If next key value overflows int8, an InvalidUnmarshalError error will be returned. +func (dec *Decoder) Int8Null(v **int8) error { + err := dec.decodeInt8Null(v) + if err != nil { + return err + } + dec.called |= 1 + return nil +} + // Int16 decodes the next key to an *int. // If next key value overflows int16, an InvalidUnmarshalError error will be returned. func (dec *Decoder) Int16(v *int16) error { @@ -366,6 +578,17 @@ func (dec *Decoder) Int16(v *int16) error { return nil } +// Int16Null decodes the next key to an *int. +// If next key value overflows int16, an InvalidUnmarshalError error will be returned. +func (dec *Decoder) Int16Null(v **int16) error { + err := dec.decodeInt16Null(v) + if err != nil { + return err + } + dec.called |= 1 + return nil +} + // Int32 decodes the next key to an *int. // If next key value overflows int32, an InvalidUnmarshalError error will be returned. func (dec *Decoder) Int32(v *int32) error { @@ -377,6 +600,17 @@ func (dec *Decoder) Int32(v *int32) error { return nil } +// Int32Null decodes the next key to an *int. +// If next key value overflows int32, an InvalidUnmarshalError error will be returned. +func (dec *Decoder) Int32Null(v **int32) error { + err := dec.decodeInt32Null(v) + if err != nil { + return err + } + dec.called |= 1 + return nil +} + // Int64 decodes the next key to an *int. // If next key value overflows int64, an InvalidUnmarshalError error will be returned. func (dec *Decoder) Int64(v *int64) error { @@ -388,6 +622,17 @@ func (dec *Decoder) Int64(v *int64) error { return nil } +// Int64Null decodes the next key to an *int. +// If next key value overflows int64, an InvalidUnmarshalError error will be returned. +func (dec *Decoder) Int64Null(v **int64) error { + err := dec.decodeInt64Null(v) + if err != nil { + return err + } + dec.called |= 1 + return nil +} + // Uint8 decodes the next key to an *int. // If next key value overflows uint8, an InvalidUnmarshalError error will be returned. func (dec *Decoder) Uint8(v *uint8) error { @@ -399,6 +644,17 @@ func (dec *Decoder) Uint8(v *uint8) error { return nil } +// Uint8Null decodes the next key to an *int. +// If next key value overflows uint8, an InvalidUnmarshalError error will be returned. +func (dec *Decoder) Uint8Null(v **uint8) error { + err := dec.decodeUint8Null(v) + if err != nil { + return err + } + dec.called |= 1 + return nil +} + // Uint16 decodes the next key to an *int. // If next key value overflows uint16, an InvalidUnmarshalError error will be returned. func (dec *Decoder) Uint16(v *uint16) error { @@ -410,6 +666,17 @@ func (dec *Decoder) Uint16(v *uint16) error { return nil } +// Uint16Null decodes the next key to an *int. +// If next key value overflows uint16, an InvalidUnmarshalError error will be returned. +func (dec *Decoder) Uint16Null(v **uint16) error { + err := dec.decodeUint16Null(v) + if err != nil { + return err + } + dec.called |= 1 + return nil +} + // Uint32 decodes the next key to an *int. // If next key value overflows uint32, an InvalidUnmarshalError error will be returned. func (dec *Decoder) Uint32(v *uint32) error { @@ -421,6 +688,17 @@ func (dec *Decoder) Uint32(v *uint32) error { return nil } +// Uint32Null decodes the next key to an *int. +// If next key value overflows uint32, an InvalidUnmarshalError error will be returned. +func (dec *Decoder) Uint32Null(v **uint32) error { + err := dec.decodeUint32Null(v) + if err != nil { + return err + } + dec.called |= 1 + return nil +} + // Uint64 decodes the next key to an *int. // If next key value overflows uint64, an InvalidUnmarshalError error will be returned. func (dec *Decoder) Uint64(v *uint64) error { @@ -432,12 +710,29 @@ func (dec *Decoder) Uint64(v *uint64) error { return nil } +// Uint64Null decodes the next key to an *int. +// If next key value overflows uint64, an InvalidUnmarshalError error will be returned. +func (dec *Decoder) Uint64Null(v **uint64) error { + err := dec.decodeUint64Null(v) + if err != nil { + return err + } + dec.called |= 1 + return nil +} + // Float decodes the next key to a *float64. // If next key value overflows float64, an InvalidUnmarshalError error will be returned. func (dec *Decoder) Float(v *float64) error { return dec.Float64(v) } +// FloatNull decodes the next key to a *float64. +// If next key value overflows float64, an InvalidUnmarshalError error will be returned. +func (dec *Decoder) FloatNull(v **float64) error { + return dec.Float64Null(v) +} + // Float64 decodes the next key to a *float64. // If next key value overflows float64, an InvalidUnmarshalError error will be returned. func (dec *Decoder) Float64(v *float64) error { @@ -449,6 +744,17 @@ func (dec *Decoder) Float64(v *float64) error { return nil } +// Float64Null decodes the next key to a *float64. +// If next key value overflows float64, an InvalidUnmarshalError error will be returned. +func (dec *Decoder) Float64Null(v **float64) error { + err := dec.decodeFloat64Null(v) + if err != nil { + return err + } + dec.called |= 1 + return nil +} + // Float32 decodes the next key to a *float64. // If next key value overflows float64, an InvalidUnmarshalError error will be returned. func (dec *Decoder) Float32(v *float32) error { @@ -460,6 +766,17 @@ func (dec *Decoder) Float32(v *float32) error { return nil } +// Float32Null decodes the next key to a *float64. +// If next key value overflows float64, an InvalidUnmarshalError error will be returned. +func (dec *Decoder) Float32Null(v **float32) error { + err := dec.decodeFloat32Null(v) + if err != nil { + return err + } + dec.called |= 1 + return nil +} + // Bool decodes the next key to a *bool. // If next key is neither null nor a JSON boolean, an InvalidUnmarshalError will be returned. // If next key is null, bool will be false. @@ -472,6 +789,18 @@ func (dec *Decoder) Bool(v *bool) error { return nil } +// BoolNull decodes the next key to a *bool. +// If next key is neither null nor a JSON boolean, an InvalidUnmarshalError will be returned. +// If next key is null, bool will be false. +func (dec *Decoder) BoolNull(v **bool) error { + err := dec.decodeBoolNull(v) + if err != nil { + return err + } + dec.called |= 1 + return nil +} + // String decodes the next key to a *string. // If next key is not a JSON string nor null, InvalidUnmarshalError will be returned. func (dec *Decoder) String(v *string) error { @@ -483,6 +812,18 @@ func (dec *Decoder) String(v *string) error { return nil } +// StringNull decodes the next key to a **string. +// If next key is not a JSON string nor null, InvalidUnmarshalError will be returned. +// If a `null` is encountered, gojay does not change the value of the pointer. +func (dec *Decoder) StringNull(v **string) error { + err := dec.decodeStringNull(v) + if err != nil { + return err + } + dec.called |= 1 + return nil +} + // AddTime decodes the next key to a *time.Time with the given format func (dec *Decoder) AddTime(v *time.Time, format string) error { return dec.Time(v, format) diff --git a/decode_bool.go b/decode_bool.go @@ -9,6 +9,15 @@ func (dec *Decoder) DecodeBool(v *bool) error { } return dec.decodeBool(v) } + +// DecodeBoolNull is like DecodeBool but it takes a **bool +// if the JSON is `true` or `false` and the pointer is nil, gojay assigns a new pointer to a bool +func (dec *Decoder) DecodeBoolNull(v **bool) error { + if dec.isPooled == 1 { + panic(InvalidUsagePooledDecoderError("Invalid usage of pooled decoder")) + } + return dec.decodeBoolNull(v) +} func (dec *Decoder) decodeBool(v *bool) error { for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { switch dec.data[dec.cursor] { @@ -52,6 +61,54 @@ func (dec *Decoder) decodeBool(v *bool) error { } return nil } +func (dec *Decoder) decodeBoolNull(v **bool) error { + for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { + switch dec.data[dec.cursor] { + case ' ', '\n', '\t', '\r', ',': + continue + case 't': + dec.cursor++ + err := dec.assertTrue() + if err != nil { + return err + } + if *v == nil { + *v = new(bool) + } + **v = true + dec.cursor++ + return nil + case 'f': + dec.cursor++ + err := dec.assertFalse() + if err != nil { + return err + } + if *v == nil { + *v = new(bool) + } + **v = false + dec.cursor++ + return nil + case 'n': + dec.cursor++ + err := dec.assertNull() + if err != nil { + return err + } + dec.cursor++ + return nil + default: + dec.err = dec.makeInvalidUnmarshalErr(v) + err := dec.skipData() + if err != nil { + return err + } + return nil + } + } + return nil +} func (dec *Decoder) assertTrue() error { i := 0 diff --git a/decode_bool_test.go b/decode_bool_test.go @@ -238,6 +238,238 @@ func TestDecoderBool(t *testing.T) { } } +func TestDecoderBoolNull(t *testing.T) { + testCases := []struct { + name string + json string + expectedResult bool + expectations func(t *testing.T, v *bool, err error) + }{ + { + name: "true-basic", + json: "true", + expectations: func(t *testing.T, v *bool, err error) { + assert.Nil(t, err, "err should be nil") + assert.True(t, *v, "result should be true") + }, + }, + { + name: "false-basic", + json: "false", + expectations: func(t *testing.T, v *bool, err error) { + assert.Nil(t, err, "err should be nil") + assert.False(t, *v, "result should be false") + }, + }, + { + name: "null-basic", + json: "null", + expectations: func(t *testing.T, v *bool, err error) { + assert.Nil(t, err, "err should be nil") + assert.Nil(t, v, "result should be nil") + }, + }, + { + name: "true-error", + json: "taue", + expectations: func(t *testing.T, v *bool, err error) { + assert.NotNil(t, err, "err should be nil") + assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") + assert.Nil(t, v, "result should be false") + }, + }, + { + name: "true-error2", + json: "trae", + expectations: func(t *testing.T, v *bool, err error) { + assert.NotNil(t, err, "err should be nil") + assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") + assert.Nil(t, v, "result should be nil") + }, + }, + { + name: "true-error3", + json: "trua", + expectations: func(t *testing.T, v *bool, err error) { + assert.NotNil(t, err, "err should be nil") + assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") + assert.Nil(t, v, "result should be nil") + }, + }, + { + name: "true-error4", + json: "truea", + expectations: func(t *testing.T, v *bool, err error) { + assert.NotNil(t, err, "err should be nil") + assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") + assert.Nil(t, v, "result should be nil") + }, + }, + { + name: "true-error5", + json: "t", + expectations: func(t *testing.T, v *bool, err error) { + assert.NotNil(t, err, "err should be nil") + assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") + assert.Nil(t, v, "result should be nil") + }, + }, + { + name: "true-error6", + json: "a", + expectations: func(t *testing.T, v *bool, err error) { + assert.NotNil(t, err, "err should be nil") + assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") + assert.Nil(t, v, "result should be nil") + }, + }, + { + name: "false-error", + json: "fulse", + expectations: func(t *testing.T, v *bool, err error) { + assert.NotNil(t, err, "err should be nil") + assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") + assert.Nil(t, v, "result should be nil") + }, + }, + { + name: "false-error2", + json: "fause", + expectations: func(t *testing.T, v *bool, err error) { + assert.NotNil(t, err, "err should be nil") + assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") + assert.Nil(t, v, "result should be nil") + }, + }, + { + name: "false-error3", + json: "falze", + expectations: func(t *testing.T, v *bool, err error) { + assert.NotNil(t, err, "err should be nil") + assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") + assert.Nil(t, v, "result should be nil") + }, + }, + { + name: "false-error4", + json: "falso", + expectations: func(t *testing.T, v *bool, err error) { + assert.NotNil(t, err, "err should be nil") + assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") + assert.Nil(t, v, "result should be nil") + }, + }, + { + name: "false-error5", + json: "falsea", + expectations: func(t *testing.T, v *bool, err error) { + assert.NotNil(t, err, "err should be nil") + assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") + assert.Nil(t, v, "result should be nil") + }, + }, + { + name: "false-error6", + json: "f", + expectations: func(t *testing.T, v *bool, err error) { + assert.NotNil(t, err, "err should be nil") + assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") + assert.Nil(t, v, "result should be nil") + }, + }, + { + name: "false-error7", + json: "a", + expectations: func(t *testing.T, v *bool, err error) { + assert.NotNil(t, err, "err should be nil") + assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") + assert.Nil(t, v, "result should be nil") + }, + }, + { + name: "null-error", + json: "nall", + expectations: func(t *testing.T, v *bool, err error) { + assert.NotNil(t, err, "err should be nil") + assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") + assert.Nil(t, v, "result should be nil") + }, + }, + { + name: "null-error2", + json: "nual", + expectations: func(t *testing.T, v *bool, err error) { + assert.NotNil(t, err, "err should be nil") + assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") + assert.Nil(t, v, "result should be nil") + }, + }, + { + name: "null-error3", + json: "nula", + expectations: func(t *testing.T, v *bool, err error) { + assert.NotNil(t, err, "err should be nil") + assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") + assert.Nil(t, v, "result should be nil") + }, + }, + { + name: "null-error4", + json: "nulle", + expectations: func(t *testing.T, v *bool, err error) { + assert.NotNil(t, err, "err should be nil") + assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") + assert.Nil(t, v, "result should be nil") + }, + }, + { + name: "null-error5", + json: "n", + expectations: func(t *testing.T, v *bool, err error) { + assert.NotNil(t, err, "err should be nil") + assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") + assert.Nil(t, v, "result should be nil") + }, + }, + { + name: "null-error6", + json: "a", + expectations: func(t *testing.T, v *bool, err error) { + assert.NotNil(t, err, "err should be nil") + assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") + assert.Nil(t, v, "result should be nil") + }, + }, + { + name: "null-skip", + json: "{}", + expectations: func(t *testing.T, v *bool, err error) { + assert.NotNil(t, err, "err should not be nil") + assert.IsType(t, InvalidUnmarshalError(""), err, "err should be of type InvalidUnmarshalError") + assert.Nil(t, v, "result should be nil") + }, + }, + { + name: "null-skip", + json: "", + expectations: func(t *testing.T, v *bool, err error) { + assert.Nil(t, err, "err should not be nil") + assert.Nil(t, v, "result should be nil") + }, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + var v = struct { + b *bool + }{} + err := Unmarshal([]byte(testCase.json), &v.b) + testCase.expectations(t, v.b, err) + }) + } +} + func TestDecoderBoolDecoderAPI(t *testing.T) { var v bool dec := BorrowDecoder(strings.NewReader("true")) diff --git a/decode_number_float.go b/decode_number_float.go @@ -48,6 +48,51 @@ func (dec *Decoder) decodeFloat64(v *float64) error { } return dec.raiseInvalidJSONErr(dec.cursor) } +func (dec *Decoder) decodeFloat64Null(v **float64) error { + for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { + switch c := dec.data[dec.cursor]; c { + case ' ', '\n', '\t', '\r', ',': + continue + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + val, err := dec.getFloat() + if err != nil { + return err + } + if *v == nil { + *v = new(float64) + } + **v = val + return nil + case '-': + dec.cursor = dec.cursor + 1 + val, err := dec.getFloatNegative() + if err != nil { + return err + } + if *v == nil { + *v = new(float64) + } + **v = -val + return nil + case 'n': + dec.cursor++ + err := dec.assertNull() + if err != nil { + return err + } + dec.cursor++ + return nil + default: + dec.err = dec.makeInvalidUnmarshalErr(v) + err := dec.skipData() + if err != nil { + return err + } + return nil + } + } + return dec.raiseInvalidJSONErr(dec.cursor) +} func (dec *Decoder) getFloatNegative() (float64, error) { // look for following numbers @@ -198,6 +243,51 @@ func (dec *Decoder) decodeFloat32(v *float32) error { } return dec.raiseInvalidJSONErr(dec.cursor) } +func (dec *Decoder) decodeFloat32Null(v **float32) error { + for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { + switch c := dec.data[dec.cursor]; c { + case ' ', '\n', '\t', '\r', ',': + continue + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + val, err := dec.getFloat32() + if err != nil { + return err + } + if *v == nil { + *v = new(float32) + } + **v = val + return nil + case '-': + dec.cursor = dec.cursor + 1 + val, err := dec.getFloat32Negative() + if err != nil { + return err + } + if *v == nil { + *v = new(float32) + } + **v = -val + return nil + case 'n': + dec.cursor++ + err := dec.assertNull() + if err != nil { + return err + } + dec.cursor++ + return nil + default: + dec.err = dec.makeInvalidUnmarshalErr(v) + err := dec.skipData() + if err != nil { + return err + } + return nil + } + } + return dec.raiseInvalidJSONErr(dec.cursor) +} func (dec *Decoder) getFloat32Negative() (float32, error) { // look for following numbers diff --git a/decode_number_float_test.go b/decode_number_float_test.go @@ -324,12 +324,627 @@ func TestDecoderFloat64(t *testing.T) { }) } +func TestDecoderFloat64Null(t *testing.T) { + testCases := []struct { + name string + json string + expectedResult float64 + resultIsNil bool + err bool + errType interface{} + }{ + { + name: "basic-float", + json: "1.1", + expectedResult: 1.1, + }, + { + name: "basic-exponent-positive-positive-exp", + json: " 1e2", + expectedResult: 100, + }, + { + name: "basic-exponent-positive-positive-exp2", + json: "5e+06", + expectedResult: 5000000, + }, + { + name: "basic-exponent-positive-positive-exp3", + json: "3e+3", + expectedResult: 3000, + }, + { + name: "basic-null", + json: "null", + expectedResult: 0, + resultIsNil: true, + }, + { + name: "basic-null-err", + json: "nxll", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + resultIsNil: true, + }, + { + name: "basic-negative-err", + json: "-", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + resultIsNil: true, + }, + { + name: "basic-negative-err", + json: "-q", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + resultIsNil: true, + }, + { + name: "basic-null-err", + json: "trua", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + resultIsNil: true, + }, + { + name: "basic-err1", + json: "0.", + expectedResult: 0, + err: true, + resultIsNil: true, + }, + { + name: "basic-err2", + json: "-1.", + expectedResult: 0, + err: true, + resultIsNil: true, + }, + { + name: "exponent-err-", + json: "0.1e", + expectedResult: 0, + err: true, + resultIsNil: true, + }, + { + name: "exp-err", + json: "0e-20", + expectedResult: 0, + err: true, + resultIsNil: true, + }, + { + name: "exp-err3", + json: "-9e-60", + expectedResult: 0, + err: true, + resultIsNil: true, + }, + { + name: "exp-err4", + json: "0.e-2", + err: true, + resultIsNil: true, + }, + { + name: "exp-err5", + json: "-5.E-2", + err: true, + resultIsNil: true, + }, + { + name: "basic-exponent-positive-positive-exp4", + json: "8e+005", + expectedResult: 800000, + }, + { + name: "basic-exponent-positive-negative-exp", + json: "1e-2", + expectedResult: 0.01, + }, + { + name: "basic-exponent-positive-negative-exp2", + json: "5e-6", + expectedResult: 0.000005, + }, + { + name: "basic-exponent-positive-negative-exp3", + json: "3e-3", + expectedResult: 0.003, + }, + { + name: "basic-exponent-positive-negative-exp4", + json: "8e-005", + expectedResult: 0.00008, + }, + { + name: "basic-exponent-negative-positive-exp", + json: "-1e2", + expectedResult: -100, + }, + { + name: "basic-exponent-negative-positive-exp2", + json: "-5e+06", + expectedResult: -5000000, + }, + { + name: "basic-exponent-negative-positive-exp3", + json: "-3e03", + expectedResult: -3000, + }, + { + name: "basic-float2", + json: "877 ", + expectedResult: 877, + }, + { + name: "basic-exponent-negative-positive-exp4", + json: "-8e+005", + expectedResult: -800000, + }, + { + name: "basic-exponent-negative-positive-exp4", + json: "-8.2e-005", + expectedResult: -0.000082, + }, + { + name: "basic-float", + json: "2.4595", + expectedResult: 2.4595, + }, + { + name: "basic-float2", + json: "877", + expectedResult: 877, + }, + { + name: "basic-float2", + json: "-7.8876", + expectedResult: -7.8876, + }, + { + name: "basic-float", + json: "2.4595e1", + expectedResult: 24.595, + }, + { + name: "basic-float2", + json: "-7.8876e002", + expectedResult: -788.76, + }, + { + name: "basic-float3", + json: "-0.1234", + expectedResult: -0.1234, + }, + { + name: "basic-exp-too-big", + json: "1e10000000000 ", + expectedResult: 0, + err: true, + resultIsNil: true, + }, + { + name: "basic-exp-too-big", + json: "1.002e10000000000 ", + expectedResult: 0, + err: true, + resultIsNil: true, + }, + { + name: "basic-exp-too-big", + json: "0e9223372036000000000 ", + expectedResult: 0, + err: true, + resultIsNil: true, + }, + { + name: "basic-exp-too-big", + json: "1.00232492420002423545849009", + expectedResult: 0, + err: true, + resultIsNil: true, + }, + { + name: "basic-exp-too-big", + json: "1.00232492420002423545849009e10000000000 ", + expectedResult: 0, + err: true, + resultIsNil: true, + }, + { + name: "error", + json: "83zez4", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + resultIsNil: true, + }, + { + name: "exponent-err", + json: "0.1e ", + expectedResult: 0, + err: true, + resultIsNil: true, + }, + { + name: "exponent-err", + json: "0e", + expectedResult: 0, + err: true, + resultIsNil: true, + }, + { + name: "error", + json: "-83zez4", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + resultIsNil: true, + }, + { + name: "invalid-type", + json: `"string"`, + expectedResult: 0, + err: true, + errType: InvalidUnmarshalError(""), + resultIsNil: true, + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + json := []byte(testCase.json) + var v = (*float64)(nil) + err := Unmarshal(json, &v) + if testCase.err { + assert.NotNil(t, err, "Err must not be nil") + if testCase.errType != nil { + assert.IsType( + t, + testCase.errType, + err, + fmt.Sprintf("err should be of type %s", reflect.TypeOf(err).String()), + ) + } + } else { + assert.Nil(t, err, "Err must be nil") + } + if testCase.resultIsNil { + assert.Nil(t, v) + } else { + assert.Equal(t, testCase.expectedResult*1000000, math.Round(*v*1000000), fmt.Sprintf("v must be equal to %f", testCase.expectedResult)) + } + }) + } +} + func TestDecoderFloat32(t *testing.T) { testCases := []struct { name string json string expectedResult float32 - skipResult bool + skipResult bool + err bool + errType interface{} + }{ + { + name: "basic-float", + json: "1.1", + expectedResult: 1.1, + }, + { + name: "basic-exponent-positive-positive-exp", + json: "1e2", + expectedResult: 100, + }, + { + name: "basic-exponent-positive-positive-exp2", + json: "5e+06", + expectedResult: 5000000, + }, + { + name: "basic-exponent-positive-positive-exp3", + json: "3e+3", + expectedResult: 3000, + }, + { + name: "basic-null", + json: "null", + expectedResult: 0, + }, + { + name: "basic-err1", + json: "0.", + expectedResult: 0, + err: true, + }, + { + name: "basic-err2", + json: "-1.", + expectedResult: 0, + err: true, + }, + { + name: "exp-err", + json: "0e-20", + expectedResult: 0, + err: true, + }, + { + name: "exp-err3", + json: "-9e-60", + expectedResult: 0, + err: true, + }, + { + name: "exp-err4", + json: "0.e-2", + err: true, + }, + { + name: "exp-err5", + json: "-5.E-2", + err: true, + }, + { + name: "basic-null", + json: "null", + expectedResult: 0, + }, + { + name: "basic-null-err", + json: "trua", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-null-err", + json: "nxll", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-negative-err", + json: "-", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "exponent-err-", + json: "0.1e", + expectedResult: 0, + err: true, + }, + { + name: "basic-negative-err", + json: "-q", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-exponent-positive-positive-exp4", + json: "8e+005", + expectedResult: 800000, + }, + { + name: "basic-exponent-positive-negative-exp", + json: "1e-2", + expectedResult: 0.01, + }, + { + name: "basic-exponent-positive-negative-exp2", + json: "5e-6", + expectedResult: 0.000005, + }, + { + name: "basic-exponent-positive-negative-exp3", + json: "3e-3", + expectedResult: 0.003, + }, + { + name: "basic-exponent-positive-negative-exp4", + json: "8e-005", + expectedResult: 0.00008, + }, + { + name: "basic-exponent-negative-positive-exp", + json: "-1e2", + expectedResult: -100, + }, + { + name: "basic-exponent-negative-positive-exp2", + json: "-5e+06", + expectedResult: -5000000, + }, + { + name: "basic-exponent-negative-positive-exp3", + json: "-3e03", + expectedResult: -3000, + }, + { + name: "basic-exponent-negative-positive-exp4", + json: "-8e+005", + expectedResult: -800000, + }, + { + name: "basic-exponent-negative-positive-exp4", + json: "-8.2e-005", + expectedResult: -0.000082, + }, + { + name: "basic-exp-too-big", + json: "1e10000000000 ", + expectedResult: 0, + err: true, + }, + { + name: "basic-exp-too-big", + json: "1.0023249242000242e10000000000 ", + expectedResult: 0, + err: true, + }, + { + name: "basic-exp-too-big", + json: "1.002e10000000000 ", + expectedResult: 0, + err: true, + }, + { + name: "basic-exp-too-big", + json: "1.00232492420002423545849009", + expectedResult: 0, + err: true, + }, + { + name: "basic-exp-too-big", + json: "1.00232492420002423545849009e10000000000 ", + expectedResult: 0, + err: true, + }, + { + name: "basic-float", + json: "2.4595", + expectedResult: 2.4595, + }, + { + name: "basic-float2", + json: "877", + expectedResult: 877, + }, + { + name: "basic-float2", + json: "877 ", + expectedResult: 877, + }, + { + name: "basic-float2", + json: "-7.8876", + expectedResult: -7.8876, + }, + { + name: "basic-float", + json: "2.459e1", + expectedResult: 24.59, + }, + { + name: "basic-float2", + json: "-7.8876e002", + expectedResult: -788.76, + }, + { + name: "basic-float3", + json: "-0.1234", + expectedResult: -0.1234, + }, + { + name: "error", + json: "83zez4", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "error", + json: "-83zez4", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "exponent-err", + json: "0e", + expectedResult: 0, + err: true, + }, + { + name: "invalid-type", + json: `"string"`, + expectedResult: 0, + err: true, + errType: InvalidUnmarshalError(""), + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + json := []byte(testCase.json) + var v float32 + err := Unmarshal(json, &v) + if testCase.err { + assert.NotNil(t, err, "Err must not be nil") + if testCase.errType != nil { + assert.IsType( + t, + testCase.errType, + err, + fmt.Sprintf("err should be of type %s", reflect.TypeOf(err).String()), + ) + } + } else { + assert.Nil(t, err, "Err must be nil") + } + if !testCase.skipResult { + assert.Equal( + t, + float64(testCase.expectedResult*1000000), math.Round(float64(v*1000000)), + fmt.Sprintf("v must be equal to %f", testCase.expectedResult), + ) + } + }) + } + t.Run("pool-error", func(t *testing.T) { + result := float32(1) + dec := NewDecoder(nil) + dec.Release() + defer func() { + err := recover() + assert.NotNil(t, err, "err shouldnt be nil") + assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") + }() + _ = dec.DecodeFloat32(&result) + assert.True(t, false, "should not be called as decoder should have panicked") + }) + t.Run("decoder-api", func(t *testing.T) { + var v float32 + dec := NewDecoder(strings.NewReader(`1.25`)) + defer dec.Release() + err := dec.DecodeFloat32(&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-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(``)) + defer dec.Release() + err := dec.DecodeFloat32(&v) + assert.NotNil(t, err, "Err must not be nil") + assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") + }) +} + +func TestDecoderFloat32Null(t *testing.T) { + testCases := []struct { + name string + json string + expectedResult float32 + resultIsNil bool err bool errType interface{} }{ @@ -345,59 +960,65 @@ func TestDecoderFloat32(t *testing.T) { }, { name: "basic-exponent-positive-positive-exp2", - json: "5e+06", + json: "5e+06 ", expectedResult: 5000000, }, { name: "basic-exponent-positive-positive-exp3", - json: "3e+3", + json: " 3e+3", expectedResult: 3000, }, { name: "basic-null", json: "null", expectedResult: 0, + resultIsNil: true, }, { name: "basic-err1", json: "0.", expectedResult: 0, err: true, + resultIsNil: true, }, { name: "basic-err2", json: "-1.", expectedResult: 0, err: true, + resultIsNil: true, }, { name: "exp-err", json: "0e-20", expectedResult: 0, err: true, + resultIsNil: true, }, { name: "exp-err3", json: "-9e-60", expectedResult: 0, err: true, + resultIsNil: true, }, { - name: "exp-err4", - json: "0.e-2", - skipResult: true, - err: true, + name: "exp-err4", + json: "0.e-2", + err: true, + resultIsNil: true, }, { - name: "exp-err5", - json: "-5.E-2", - skipResult: true, - err: true, + name: "exp-err5", + json: "-5.E-2", + err: true, + resultIsNil: true, }, { name: "basic-null", json: "null", expectedResult: 0, + resultIsNil: true, }, { name: "basic-null-err", @@ -405,6 +1026,7 @@ func TestDecoderFloat32(t *testing.T) { expectedResult: 0, err: true, errType: InvalidJSONError(""), + resultIsNil: true, }, { name: "basic-null-err", @@ -412,6 +1034,7 @@ func TestDecoderFloat32(t *testing.T) { expectedResult: 0, err: true, errType: InvalidJSONError(""), + resultIsNil: true, }, { name: "basic-negative-err", @@ -419,12 +1042,14 @@ func TestDecoderFloat32(t *testing.T) { expectedResult: 0, err: true, errType: InvalidJSONError(""), + resultIsNil: true, }, { name: "exponent-err-", json: "0.1e", expectedResult: 0, err: true, + resultIsNil: true, }, { name: "basic-negative-err", @@ -432,6 +1057,7 @@ func TestDecoderFloat32(t *testing.T) { expectedResult: 0, err: true, errType: InvalidJSONError(""), + resultIsNil: true, }, { name: "basic-exponent-positive-positive-exp4", @@ -440,7 +1066,7 @@ func TestDecoderFloat32(t *testing.T) { }, { name: "basic-exponent-positive-negative-exp", - json: "1e-2", + json: " 1e-2", expectedResult: 0.01, }, { @@ -488,30 +1114,35 @@ func TestDecoderFloat32(t *testing.T) { json: "1e10000000000 ", expectedResult: 0, err: true, + resultIsNil: true, }, { name: "basic-exp-too-big", json: "1.0023249242000242e10000000000 ", expectedResult: 0, err: true, + resultIsNil: true, }, { name: "basic-exp-too-big", json: "1.002e10000000000 ", expectedResult: 0, err: true, + resultIsNil: true, }, { name: "basic-exp-too-big", json: "1.00232492420002423545849009", expectedResult: 0, err: true, + resultIsNil: true, }, { name: "basic-exp-too-big", json: "1.00232492420002423545849009e10000000000 ", expectedResult: 0, err: true, + resultIsNil: true, }, { name: "basic-float", @@ -554,6 +1185,7 @@ func TestDecoderFloat32(t *testing.T) { expectedResult: 0, err: true, errType: InvalidJSONError(""), + resultIsNil: true, }, { name: "error", @@ -561,12 +1193,14 @@ func TestDecoderFloat32(t *testing.T) { expectedResult: 0, err: true, errType: InvalidJSONError(""), + resultIsNil: true, }, { name: "exponent-err", json: "0e", expectedResult: 0, err: true, + resultIsNil: true, }, { name: "invalid-type", @@ -574,12 +1208,13 @@ func TestDecoderFloat32(t *testing.T) { expectedResult: 0, err: true, errType: InvalidUnmarshalError(""), + resultIsNil: true, }, } for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { json := []byte(testCase.json) - var v float32 + var v = (*float32)(nil) err := Unmarshal(json, &v) if testCase.err { assert.NotNil(t, err, "Err must not be nil") @@ -594,10 +1229,12 @@ func TestDecoderFloat32(t *testing.T) { } else { assert.Nil(t, err, "Err must be nil") } - if !testCase.skipResult { + if testCase.resultIsNil { + assert.Nil(t, v) + } else { assert.Equal( t, - float64(testCase.expectedResult*1000000), math.Round(float64(v*1000000)), + float64(testCase.expectedResult*1000000), math.Round(float64(*v*1000000)), fmt.Sprintf("v must be equal to %f", testCase.expectedResult), ) } diff --git a/decode_number_int.go b/decode_number_int.go @@ -61,6 +61,59 @@ func (dec *Decoder) decodeInt(v *int) error { return dec.raiseInvalidJSONErr(dec.cursor) } +func (dec *Decoder) decodeIntNull(v **int) error { + for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { + switch c := dec.data[dec.cursor]; c { + case ' ', '\n', '\t', '\r', ',': + continue + // we don't look for 0 as leading zeros are invalid per RFC + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + val, err := dec.getInt64() + if err != nil { + return err + } + if *v == nil { + *v = new(int) + } + **v = int(val) + return nil + case '-': + dec.cursor = dec.cursor + 1 + val, err := dec.getInt64Negative() + if err != nil { + return err + } + if *v == nil { + *v = new(int) + } + **v = -int(val) + return nil + case 'n': + dec.cursor++ + err := dec.assertNull() + if err != nil { + return err + } + 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, + ), + ) + err := dec.skipData() + if err != nil { + return err + } + return nil + } + } + return dec.raiseInvalidJSONErr(dec.cursor) +} + // DecodeInt16 reads the next JSON-encoded value from its input and stores it in the int16 pointed to by v. // // See the documentation for Unmarshal for details about the conversion of JSON into a Go value. @@ -110,6 +163,52 @@ func (dec *Decoder) decodeInt16(v *int16) error { } return dec.raiseInvalidJSONErr(dec.cursor) } +func (dec *Decoder) decodeInt16Null(v **int16) error { + for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { + switch c := dec.data[dec.cursor]; c { + case ' ', '\n', '\t', '\r', ',': + continue + // we don't look for 0 as leading zeros are invalid per RFC + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + val, err := dec.getInt16() + if err != nil { + return err + } + if *v == nil { + *v = new(int16) + } + **v = val + return nil + case '-': + dec.cursor = dec.cursor + 1 + val, err := dec.getInt16Negative() + if err != nil { + return err + } + if *v == nil { + *v = new(int16) + } + **v = -val + return nil + case 'n': + dec.cursor++ + err := dec.assertNull() + if err != nil { + return err + } + dec.cursor++ + return nil + default: + dec.err = dec.makeInvalidUnmarshalErr(v) + err := dec.skipData() + if err != nil { + return err + } + return nil + } + } + return dec.raiseInvalidJSONErr(dec.cursor) +} func (dec *Decoder) getInt16Negative() (int16, error) { // look for following numbers @@ -298,6 +397,52 @@ func (dec *Decoder) decodeInt8(v *int8) error { } return dec.raiseInvalidJSONErr(dec.cursor) } +func (dec *Decoder) decodeInt8Null(v **int8) error { + for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { + switch c := dec.data[dec.cursor]; c { + case ' ', '\n', '\t', '\r', ',': + continue + // we don't look for 0 as leading zeros are invalid per RFC + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + val, err := dec.getInt8() + if err != nil { + return err + } + if *v == nil { + *v = new(int8) + } + **v = val + return nil + case '-': + dec.cursor = dec.cursor + 1 + val, err := dec.getInt8Negative() + if err != nil { + return err + } + if *v == nil { + *v = new(int8) + } + **v = -val + return nil + case 'n': + dec.cursor++ + err := dec.assertNull() + if err != nil { + return err + } + dec.cursor++ + return nil + default: + dec.err = dec.makeInvalidUnmarshalErr(v) + err := dec.skipData() + if err != nil { + return err + } + return nil + } + } + return dec.raiseInvalidJSONErr(dec.cursor) +} func (dec *Decoder) getInt8Negative() (int8, error) { // look for following numbers @@ -484,6 +629,51 @@ func (dec *Decoder) decodeInt32(v *int32) error { } return dec.raiseInvalidJSONErr(dec.cursor) } +func (dec *Decoder) decodeInt32Null(v **int32) error { + for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { + switch c := dec.data[dec.cursor]; c { + case ' ', '\n', '\t', '\r', ',': + continue + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + val, err := dec.getInt32() + if err != nil { + return err + } + if *v == nil { + *v = new(int32) + } + **v = val + return nil + case '-': + dec.cursor = dec.cursor + 1 + val, err := dec.getInt32Negative() + if err != nil { + return err + } + if *v == nil { + *v = new(int32) + } + **v = -val + return nil + case 'n': + dec.cursor++ + err := dec.assertNull() + if err != nil { + return err + } + dec.cursor++ + return nil + default: + dec.err = dec.makeInvalidUnmarshalErr(v) + err := dec.skipData() + if err != nil { + return err + } + return nil + } + } + return dec.raiseInvalidJSONErr(dec.cursor) +} func (dec *Decoder) getInt32Negative() (int32, error) { // look for following numbers @@ -672,6 +862,51 @@ func (dec *Decoder) decodeInt64(v *int64) error { } return dec.raiseInvalidJSONErr(dec.cursor) } +func (dec *Decoder) decodeInt64Null(v **int64) error { + for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { + switch c := dec.data[dec.cursor]; c { + case ' ', '\n', '\t', '\r', ',': + continue + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + val, err := dec.getInt64() + if err != nil { + return err + } + if *v == nil { + *v = new(int64) + } + **v = val + return nil + case '-': + dec.cursor = dec.cursor + 1 + val, err := dec.getInt64Negative() + if err != nil { + return err + } + if *v == nil { + *v = new(int64) + } + **v = -val + return nil + case 'n': + dec.cursor++ + err := dec.assertNull() + if err != nil { + return err + } + dec.cursor++ + return nil + default: + dec.err = dec.makeInvalidUnmarshalErr(v) + err := dec.skipData() + if err != nil { + return err + } + return nil + } + } + return dec.raiseInvalidJSONErr(dec.cursor) +} func (dec *Decoder) getInt64Negative() (int64, error) { // look for following numbers diff --git a/decode_number_int_test.go b/decode_number_int_test.go @@ -309,14 +309,14 @@ func TestDecoderInt(t *testing.T) { assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") }) } - -func TestDecoderInt64(t *testing.T) { +func TestDecoderIntNull(t *testing.T) { testCases := []struct { name string json string - expectedResult int64 + expectedResult int err bool errType interface{} + resultIsNil bool }{ { name: "basic-positive", @@ -325,7 +325,7 @@ func TestDecoderInt64(t *testing.T) { }, { name: "basic-positive2", - json: " 1039405", + json: "1039405", expectedResult: 1039405, }, { @@ -337,24 +337,25 @@ func TestDecoderInt64(t *testing.T) { name: "basic-null", json: "null", expectedResult: 0, + resultIsNil: true, }, { - name: "basic-null-err", - json: "nxll", + name: "basic-negative-err", + json: "-", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-negative-err", - json: "-", + json: "-q", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { - name: "basic-negative-err", - json: "-q", + name: "basic-null-err", + json: "nxll", expectedResult: 0, err: true, errType: InvalidJSONError(""), @@ -373,27 +374,24 @@ func TestDecoderInt64(t *testing.T) { }, { name: "basic-big-overflow", - json: " 9223372036854775808", - expectedResult: 0, - err: true, - }, - { - name: "basic-big-overflow", - json: " 9223372036854775827", + json: "9223372036854775808", expectedResult: 0, err: true, + errType: InvalidUnmarshalError(""), }, { name: "basic-big-overflow2", json: "92233720368547758089", expectedResult: 0, err: true, + errType: InvalidUnmarshalError(""), }, { name: "basic-big-overflow3", json: "92233720368547758089 ", expectedResult: 0, err: true, + errType: InvalidUnmarshalError(""), }, { name: "basic-negative2", @@ -401,6 +399,12 @@ func TestDecoderInt64(t *testing.T) { expectedResult: -2349557, }, { + name: "exponent-err-too-big", + json: "0e10000000000000000000", + expectedResult: 0, + err: true, + }, + { name: "basic-float", json: "2.4595", expectedResult: 2, @@ -412,6 +416,11 @@ func TestDecoderInt64(t *testing.T) { }, { name: "basic-float2", + json: "-7.8876 ", + expectedResult: -7, + }, + { + name: "basic-float2", json: "-7.8876a", expectedResult: 0, err: true, @@ -423,10 +432,15 @@ func TestDecoderInt64(t *testing.T) { }, { name: "basic-exponent-positive-positive-exp2", - json: "5e+06 ", + json: "5e+06", expectedResult: 5000000, }, { + name: "basic-exponent-positive-positive-exp2", + json: "5.01e+10", + expectedResult: 50100000000, + }, + { name: "basic-exponent-positive-positive-exp3", json: "3e+3", expectedResult: 3000, @@ -438,7 +452,7 @@ func TestDecoderInt64(t *testing.T) { }, { name: "basic-exponent-positive-negative-exp", - json: "1e-2 ", + json: "1e-2", expectedResult: 0, }, { @@ -452,13 +466,6 @@ func TestDecoderInt64(t *testing.T) { expectedResult: 0, }, { - name: "error3", - json: "0E40", - expectedResult: 0, - err: true, - errType: InvalidJSONError(""), - }, - { name: "basic-exponent-positive-negative-exp4", json: "8e-005", expectedResult: 0, @@ -474,11 +481,6 @@ func TestDecoderInt64(t *testing.T) { expectedResult: -5000000, }, { - name: "basic-exponent-negative-positive-exp2", - json: "-5.4e+06", - expectedResult: -5400000, - }, - { name: "basic-exponent-negative-positive-exp3", json: "-3e03", expectedResult: -3000, @@ -489,47 +491,24 @@ func TestDecoderInt64(t *testing.T) { expectedResult: -800000, }, { - name: "exponent-err-too-big", - json: "0e10000000000000000000", - expectedResult: 0, - err: true, - }, - { - name: "exponent-err-too-big", - json: "0e1000000000000000000000000 ", - expectedResult: 0, - err: true, - }, - { - name: "exponent-err-too-big", - json: "0.1e1000000000", - expectedResult: 0, - err: true, - }, - { - name: "exponent-err-too-big", - json: "0.1932242242424244244e1000000000000000000000000", + name: "error1", + json: "132zz4", expectedResult: 0, err: true, }, { - name: "basic-exponent-negative-positive-exp4", - json: "8ea+00a5", + name: "negative-error2", + json: " -1213xdde2323 ", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { - name: "basic-exponent-err", - json: "3e", - expectedResult: 0, - err: true, - }, - { - name: "error1", - json: "132zz4", + name: "error3", + json: "-8e+00$aa5", expectedResult: 0, err: true, + errType: InvalidJSONError(""), }, { name: "error4", @@ -539,17 +518,17 @@ func TestDecoderInt64(t *testing.T) { errType: InvalidJSONError(""), }, { - name: "error5", - json: "0E40", + name: "exponent-err-", + json: "0.1e", expectedResult: 0, err: true, - errType: InvalidJSONError(""), }, { - name: "exponent-err-", - json: "0.1e", + name: "error5", + json: "0E40", expectedResult: 0, err: true, + errType: InvalidJSONError(""), }, { name: "error6", @@ -558,6 +537,7 @@ func TestDecoderInt64(t *testing.T) { err: true, errType: InvalidJSONError(""), }, + { name: "error7", json: "-5.e-2", @@ -576,11 +556,11 @@ func TestDecoderInt64(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { json := []byte(testCase.json) - var v int64 + var v = (*int)(nil) err := Unmarshal(json, &v) if testCase.err { assert.NotNil(t, err, "Err must not be nil") - if testCase.errType != nil { + if testCase.errType != nil && err != nil { assert.IsType( t, testCase.errType, @@ -588,55 +568,23 @@ func TestDecoderInt64(t *testing.T) { fmt.Sprintf("err should be of type %s", reflect.TypeOf(err).String()), ) } + return + } + assert.Nil(t, err, "Err must be nil") + if testCase.resultIsNil { + assert.Nil(t, v) } else { - assert.Nil(t, err, "Err must be nil") + assert.Equal(t, testCase.expectedResult, *v, fmt.Sprintf("v must be equal to %d", testCase.expectedResult)) } - assert.Equal(t, testCase.expectedResult, v, fmt.Sprintf("v must be equal to %d", testCase.expectedResult)) }) } - t.Run("pool-error", func(t *testing.T) { - result := int64(1) - dec := NewDecoder(nil) - dec.Release() - defer func() { - err := recover() - assert.NotNil(t, err, "err shouldnt be nil") - assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") - }() - _ = dec.DecodeInt64(&result) - assert.True(t, false, "should not be called as decoder should have panicked") - }) - t.Run("decoder-api", func(t *testing.T) { - var v int64 - dec := NewDecoder(strings.NewReader(`33`)) - defer dec.Release() - err := dec.DecodeInt64(&v) - 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(``)) - defer dec.Release() - err := dec.DecodeInt64(&v) - assert.NotNil(t, err, "Err must not be nil") - assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") - }) } -func TestDecoderInt32(t *testing.T) { +func TestDecoderInt64(t *testing.T) { testCases := []struct { name string json string - expectedResult int32 + expectedResult int64 err bool errType interface{} }{ @@ -689,34 +637,40 @@ func TestDecoderInt32(t *testing.T) { errType: InvalidJSONError(""), }, { - name: "basic-negative2", - json: "-2349557", - expectedResult: -2349557, - }, - { name: "basic-big", - json: " 2147483647", - expectedResult: 2147483647, + json: "9223372036854775807", + expectedResult: 9223372036854775807, }, { name: "basic-big-overflow", - json: " 2147483648", + json: " 9223372036854775808", expectedResult: 0, err: true, }, { name: "basic-big-overflow", - json: " 2147483657", + json: " 9223372036854775827", expectedResult: 0, err: true, }, { name: "basic-big-overflow2", - json: "21474836483", + json: "92233720368547758089", expectedResult: 0, err: true, }, { + name: "basic-big-overflow3", + json: "92233720368547758089 ", + expectedResult: 0, + err: true, + }, + { + name: "basic-negative2", + json: "-2349557", + expectedResult: -2349557, + }, + { name: "basic-float", json: "2.4595", expectedResult: 2, @@ -734,34 +688,12 @@ func TestDecoderInt32(t *testing.T) { }, { name: "basic-exponent-positive-positive-exp", - json: "1.2E2", - expectedResult: 120, - }, - { - name: "exponent-err-too-big", - json: "0e10000000000000000000", - expectedResult: 0, - err: true, - }, - { - name: "exponent-err-too-big", - json: "0.1932242242424244244e1000000000000000000000000", - expectedResult: 0, - err: true, - }, - { - name: "basic-exponent-positive-positive-exp1", - json: "3.5e+005 ", - expectedResult: 350000, - }, - { - name: "basic-exponent-positive-positive-exp1", - json: "3.5e+005", - expectedResult: 350000, + json: "1e2", + expectedResult: 100, }, { name: "basic-exponent-positive-positive-exp2", - json: "5e+06", + json: "5e+06 ", expectedResult: 5000000, }, { @@ -771,7 +703,7 @@ func TestDecoderInt32(t *testing.T) { }, { name: "basic-exponent-positive-positive-exp4", - json: "8e+005 ", + json: "8e+005", expectedResult: 800000, }, { @@ -781,7 +713,7 @@ func TestDecoderInt32(t *testing.T) { }, { name: "basic-exponent-positive-negative-exp2", - json: "5E-6", + json: "5e-6", expectedResult: 0, }, { @@ -790,6 +722,13 @@ func TestDecoderInt32(t *testing.T) { expectedResult: 0, }, { + name: "error3", + json: "0E40", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { name: "basic-exponent-positive-negative-exp4", json: "8e-005", expectedResult: 0, @@ -805,6 +744,11 @@ func TestDecoderInt32(t *testing.T) { expectedResult: -5000000, }, { + name: "basic-exponent-negative-positive-exp2", + json: "-5.4e+06", + expectedResult: -5400000, + }, + { name: "basic-exponent-negative-positive-exp3", json: "-3e03", expectedResult: -3000, @@ -815,14 +759,14 @@ func TestDecoderInt32(t *testing.T) { expectedResult: -800000, }, { - name: "exponent-err-", - json: "0.1e", + name: "exponent-err-too-big", + json: "0e10000000000000000000", expectedResult: 0, err: true, }, { name: "exponent-err-too-big", - json: "0.1e10000000000000000000", + json: "0e1000000000000000000000000 ", expectedResult: 0, err: true, }, @@ -834,25 +778,1676 @@ func TestDecoderInt32(t *testing.T) { }, { name: "exponent-err-too-big", - json: "0.1e1000000000 ", + json: "0.1932242242424244244e1000000000000000000000000", expectedResult: 0, err: true, }, { - name: "exponent-err-too-big", - json: "0e100000000000", + name: "basic-exponent-negative-positive-exp4", + json: "8ea+00a5", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-exponent-err", + json: "3e", + expectedResult: 0, + err: true, + }, + { + name: "error1", + json: "132zz4", expectedResult: 0, err: true, }, { - name: "exponent-err-too-big", - json: "0e100000000000 ", + name: "error4", + json: "0.E----", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "error5", + json: "0E40", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "exponent-err-", + json: "0.1e", + expectedResult: 0, + err: true, + }, + { + name: "error6", + json: "0.e-9", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "error7", + json: "-5.e-2", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "invalid-type", + json: `"string"`, + expectedResult: 0, + err: true, + errType: InvalidUnmarshalError(""), + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + json := []byte(testCase.json) + var v int64 + err := Unmarshal(json, &v) + if testCase.err { + assert.NotNil(t, err, "Err must not be nil") + if testCase.errType != nil { + assert.IsType( + t, + testCase.errType, + err, + fmt.Sprintf("err should be of type %s", reflect.TypeOf(err).String()), + ) + } + } else { + assert.Nil(t, err, "Err must be nil") + } + assert.Equal(t, testCase.expectedResult, v, fmt.Sprintf("v must be equal to %d", testCase.expectedResult)) + }) + } + t.Run("pool-error", func(t *testing.T) { + result := int64(1) + dec := NewDecoder(nil) + dec.Release() + defer func() { + err := recover() + assert.NotNil(t, err, "err shouldnt be nil") + assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") + }() + _ = dec.DecodeInt64(&result) + assert.True(t, false, "should not be called as decoder should have panicked") + }) + t.Run("decoder-api", func(t *testing.T) { + var v int64 + dec := NewDecoder(strings.NewReader(`33`)) + defer dec.Release() + err := dec.DecodeInt64(&v) + 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(``)) + defer dec.Release() + err := dec.DecodeInt64(&v) + assert.NotNil(t, err, "Err must not be nil") + assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") + }) +} +func TestDecoderInt64Null(t *testing.T) { + testCases := []struct { + name string + json string + expectedResult int64 + err bool + errType interface{} + resultIsNil bool + }{ + { + name: "basic-positive", + json: "100", + expectedResult: 100, + }, + { + name: "basic-positive2", + json: " 1039405", + expectedResult: 1039405, + }, + { + name: "basic-negative", + json: "-2", + expectedResult: -2, + }, + { + name: "basic-null", + json: "null", + expectedResult: 0, + resultIsNil: true, + }, + { + name: "basic-null-err", + json: "nxll", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-negative-err", + json: "-", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-negative-err", + json: "-q", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-skip-data-err", + json: "trua", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-big", + json: "9223372036854775807", + expectedResult: 9223372036854775807, + }, + { + name: "basic-big-overflow", + json: " 9223372036854775808", + expectedResult: 0, + err: true, + }, + { + name: "basic-big-overflow", + json: " 9223372036854775827", + expectedResult: 0, + err: true, + }, + { + name: "basic-big-overflow2", + json: "92233720368547758089", + expectedResult: 0, + err: true, + }, + { + name: "basic-big-overflow3", + json: "92233720368547758089 ", + expectedResult: 0, + err: true, + }, + { + name: "basic-negative2", + json: "-2349557", + expectedResult: -2349557, + }, + { + name: "basic-float", + json: "2.4595", + expectedResult: 2, + }, + { + name: "basic-float2", + json: "-7.8876", + expectedResult: -7, + }, + { + name: "basic-float2", + json: "-7.8876a", + expectedResult: 0, + err: true, + }, + { + name: "basic-exponent-positive-positive-exp", + json: "1e2", + expectedResult: 100, + }, + { + name: "basic-exponent-positive-positive-exp2", + json: "5e+06 ", + expectedResult: 5000000, + }, + { + name: "basic-exponent-positive-positive-exp3", + json: "3e+3", + expectedResult: 3000, + }, + { + name: "basic-exponent-positive-positive-exp4", + json: "8e+005", + expectedResult: 800000, + }, + { + name: "basic-exponent-positive-negative-exp", + json: "1e-2 ", + expectedResult: 0, + }, + { + name: "basic-exponent-positive-negative-exp2", + json: "5e-6", + expectedResult: 0, + }, + { + name: "basic-exponent-positive-negative-exp3", + json: "3e-3", + expectedResult: 0, + }, + { + name: "error3", + json: "0E40", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-exponent-positive-negative-exp4", + json: "8e-005", + expectedResult: 0, + }, + { + name: "basic-exponent-negative-positive-exp", + json: "-1e2", + expectedResult: -100, + }, + { + name: "basic-exponent-negative-positive-exp2", + json: "-5e+06", + expectedResult: -5000000, + }, + { + name: "basic-exponent-negative-positive-exp2", + json: "-5.4e+06", + expectedResult: -5400000, + }, + { + name: "basic-exponent-negative-positive-exp3", + json: "-3e03", + expectedResult: -3000, + }, + { + name: "basic-exponent-negative-positive-exp4", + json: "-8e+005", + expectedResult: -800000, + }, + { + name: "exponent-err-too-big", + json: "0e10000000000000000000", + expectedResult: 0, + err: true, + }, + { + name: "exponent-err-too-big", + json: "0e1000000000000000000000000 ", + expectedResult: 0, + err: true, + }, + { + name: "exponent-err-too-big", + json: "0.1e1000000000", + expectedResult: 0, + err: true, + }, + { + name: "exponent-err-too-big", + json: "0.1932242242424244244e1000000000000000000000000", + expectedResult: 0, + err: true, + }, + { + name: "basic-exponent-negative-positive-exp4", + json: "8ea+00a5", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-exponent-err", + json: "3e", + expectedResult: 0, + err: true, + }, + { + name: "error1", + json: "132zz4", + expectedResult: 0, + err: true, + }, + { + name: "error4", + json: "0.E----", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "error5", + json: "0E40", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "exponent-err-", + json: "0.1e", + expectedResult: 0, + err: true, + }, + { + name: "error6", + json: "0.e-9", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "error7", + json: "-5.e-2", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "invalid-type", + json: `"string"`, + expectedResult: 0, + err: true, + errType: InvalidUnmarshalError(""), + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + json := []byte(testCase.json) + var v = (*int64)(nil) + err := Unmarshal(json, &v) + if testCase.err { + assert.NotNil(t, err, "Err must not be nil") + if testCase.errType != nil { + assert.IsType( + t, + testCase.errType, + err, + fmt.Sprintf("err should be of type %s", reflect.TypeOf(err).String()), + ) + } + return + } + assert.Nil(t, err, "Err must be nil") + if testCase.resultIsNil { + assert.Nil(t, v) + } else { + assert.Equal(t, testCase.expectedResult, *v, fmt.Sprintf("v must be equal to %d", testCase.expectedResult)) + } + }) + } +} + +func TestDecoderInt32(t *testing.T) { + testCases := []struct { + name string + json string + expectedResult int32 + err bool + errType interface{} + }{ + { + name: "basic-positive", + json: "100", + expectedResult: 100, + }, + { + name: "basic-positive2", + json: " 1039405", + expectedResult: 1039405, + }, + { + name: "basic-negative", + json: "-2", + expectedResult: -2, + }, + { + name: "basic-null", + json: "null", + expectedResult: 0, + }, + { + name: "basic-null-err", + json: "nxll", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-negative-err", + json: "-", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-negative-err", + json: "-q", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-skip-data-err", + json: "trua", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-negative2", + json: "-2349557", + expectedResult: -2349557, + }, + { + name: "basic-big", + json: " 2147483647", + expectedResult: 2147483647, + }, + { + name: "basic-big-overflow", + json: " 2147483648", + expectedResult: 0, + err: true, + }, + { + name: "basic-big-overflow", + json: " 2147483657", + expectedResult: 0, + err: true, + }, + { + name: "basic-big-overflow2", + json: "21474836483", + expectedResult: 0, + err: true, + }, + { + name: "basic-float", + json: "2.4595", + expectedResult: 2, + }, + { + name: "basic-float2", + json: "-7.8876", + expectedResult: -7, + }, + { + name: "basic-float2", + json: "-7.8876a", + expectedResult: 0, + err: true, + }, + { + name: "basic-exponent-positive-positive-exp", + json: "1.2E2", + expectedResult: 120, + }, + { + name: "exponent-err-too-big", + json: "0e10000000000000000000", + expectedResult: 0, + err: true, + }, + { + name: "exponent-err-too-big", + json: "0.1932242242424244244e1000000000000000000000000", + expectedResult: 0, + err: true, + }, + { + name: "basic-exponent-positive-positive-exp1", + json: "3.5e+005 ", + expectedResult: 350000, + }, + { + name: "basic-exponent-positive-positive-exp1", + json: "3.5e+005", + expectedResult: 350000, + }, + { + name: "basic-exponent-positive-positive-exp2", + json: "5e+06", + expectedResult: 5000000, + }, + { + name: "basic-exponent-positive-positive-exp3", + json: "3e+3", + expectedResult: 3000, + }, + { + name: "basic-exponent-positive-positive-exp4", + json: "8e+005 ", + expectedResult: 800000, + }, + { + name: "basic-exponent-positive-negative-exp", + json: "1e-2 ", + expectedResult: 0, + }, + { + name: "basic-exponent-positive-negative-exp2", + json: "5E-6", + expectedResult: 0, + }, + { + name: "basic-exponent-positive-negative-exp3", + json: "3e-3", + expectedResult: 0, + }, + { + name: "basic-exponent-positive-negative-exp4", + json: "8e-005", + expectedResult: 0, + }, + { + name: "basic-exponent-negative-positive-exp", + json: "-1e2", + expectedResult: -100, + }, + { + name: "basic-exponent-negative-positive-exp2", + json: "-5e+06", + expectedResult: -5000000, + }, + { + name: "basic-exponent-negative-positive-exp3", + json: "-3e03", + expectedResult: -3000, + }, + { + name: "basic-exponent-negative-positive-exp4", + json: "-8e+005", + expectedResult: -800000, + }, + { + name: "exponent-err-", + json: "0.1e", + expectedResult: 0, + err: true, + }, + { + name: "exponent-err-too-big", + json: "0.1e10000000000000000000", + expectedResult: 0, + err: true, + }, + { + name: "exponent-err-too-big", + json: "0.1e1000000000", + expectedResult: 0, + err: true, + }, + { + name: "exponent-err-too-big", + json: "0.1e1000000000 ", + expectedResult: 0, + err: true, + }, + { + name: "exponent-err-too-big", + json: "0e100000000000", + expectedResult: 0, + err: true, + }, + { + name: "exponent-err-too-big", + json: "0e100000000000 ", + expectedResult: 0, + err: true, + }, + { + name: "basic-exponent-err", + json: "3e", + expectedResult: 0, + err: true, + }, + { + name: "error3", + json: "0E40", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "error4", + json: "0.E----", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "error5", + json: "0E40", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "error6", + json: "0.e-9", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "error7", + json: "-5.e-2", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-float", + json: "8.32 ", + expectedResult: 8, + }, + { + name: "error", + json: "83zez4", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "error", + json: "8ea00$aa5", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "error2", + json: "-8e+00$aa5", + expectedResult: 0, + err: true, + }, + { + name: "invalid-type", + json: `"string"`, + expectedResult: 0, + err: true, + errType: InvalidUnmarshalError(""), + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + json := []byte(testCase.json) + var v int32 + err := Unmarshal(json, &v) + if testCase.err { + assert.NotNil(t, err, "Err must not be nil") + if testCase.errType != nil { + assert.IsType( + t, + testCase.errType, + err, + fmt.Sprintf("err should be of type %s", reflect.TypeOf(err).String()), + ) + } + } else { + assert.Nil(t, err, "Err must be nil") + } + assert.Equal(t, testCase.expectedResult, v, fmt.Sprintf("v must be equal to %d", testCase.expectedResult)) + }) + } + t.Run("pool-error", func(t *testing.T) { + result := int32(1) + dec := NewDecoder(nil) + dec.Release() + defer func() { + err := recover() + assert.NotNil(t, err, "err shouldnt be nil") + assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") + }() + _ = dec.DecodeInt32(&result) + assert.True(t, false, "should not be called as decoder should have panicked") + + }) + t.Run("decoder-api", func(t *testing.T) { + var v int32 + dec := NewDecoder(strings.NewReader(`33`)) + defer dec.Release() + err := dec.DecodeInt32(&v) + 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(``)) + defer dec.Release() + err := dec.DecodeInt32(&v) + assert.NotNil(t, err, "Err must not be nil") + assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") + }) +} +func TestDecoderInt32Null(t *testing.T) { + testCases := []struct { + name string + json string + expectedResult int32 + err bool + errType interface{} + resultIsNil bool + }{ + { + name: "basic-positive", + json: "100", + expectedResult: 100, + }, + { + name: "basic-positive2", + json: " 1039405", + expectedResult: 1039405, + }, + { + name: "basic-negative", + json: "-2", + expectedResult: -2, + }, + { + name: "basic-null", + json: "null", + expectedResult: 0, + resultIsNil: true, + }, + { + name: "basic-null-err", + json: "nxll", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-negative-err", + json: "-", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-negative-err", + json: "-q", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-skip-data-err", + json: "trua", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-negative2", + json: "-2349557", + expectedResult: -2349557, + }, + { + name: "basic-big", + json: " 2147483647", + expectedResult: 2147483647, + }, + { + name: "basic-big-overflow", + json: " 2147483648", + expectedResult: 0, + err: true, + }, + { + name: "basic-big-overflow", + json: " 2147483657", + expectedResult: 0, + err: true, + }, + { + name: "basic-big-overflow2", + json: "21474836483", + expectedResult: 0, + err: true, + }, + { + name: "basic-float", + json: "2.4595", + expectedResult: 2, + }, + { + name: "basic-float2", + json: "-7.8876", + expectedResult: -7, + }, + { + name: "basic-float2", + json: "-7.8876a", + expectedResult: 0, + err: true, + }, + { + name: "basic-exponent-positive-positive-exp", + json: "1.2E2", + expectedResult: 120, + }, + { + name: "exponent-err-too-big", + json: "0e10000000000000000000", + expectedResult: 0, + err: true, + }, + { + name: "exponent-err-too-big", + json: "0.1932242242424244244e1000000000000000000000000", + expectedResult: 0, + err: true, + }, + { + name: "basic-exponent-positive-positive-exp1", + json: "3.5e+005 ", + expectedResult: 350000, + }, + { + name: "basic-exponent-positive-positive-exp1", + json: "3.5e+005", + expectedResult: 350000, + }, + { + name: "basic-exponent-positive-positive-exp2", + json: "5e+06", + expectedResult: 5000000, + }, + { + name: "basic-exponent-positive-positive-exp3", + json: "3e+3", + expectedResult: 3000, + }, + { + name: "basic-exponent-positive-positive-exp4", + json: "8e+005 ", + expectedResult: 800000, + }, + { + name: "basic-exponent-positive-negative-exp", + json: "1e-2 ", + expectedResult: 0, + }, + { + name: "basic-exponent-positive-negative-exp2", + json: "5E-6", + expectedResult: 0, + }, + { + name: "basic-exponent-positive-negative-exp3", + json: "3e-3", + expectedResult: 0, + }, + { + name: "basic-exponent-positive-negative-exp4", + json: "8e-005", + expectedResult: 0, + }, + { + name: "basic-exponent-negative-positive-exp", + json: "-1e2", + expectedResult: -100, + }, + { + name: "basic-exponent-negative-positive-exp2", + json: "-5e+06", + expectedResult: -5000000, + }, + { + name: "basic-exponent-negative-positive-exp3", + json: "-3e03", + expectedResult: -3000, + }, + { + name: "basic-exponent-negative-positive-exp4", + json: "-8e+005", + expectedResult: -800000, + }, + { + name: "exponent-err-", + json: "0.1e", + expectedResult: 0, + err: true, + }, + { + name: "exponent-err-too-big", + json: "0.1e10000000000000000000", + expectedResult: 0, + err: true, + }, + { + name: "exponent-err-too-big", + json: "0.1e1000000000", + expectedResult: 0, + err: true, + }, + { + name: "exponent-err-too-big", + json: "0.1e1000000000 ", + expectedResult: 0, + err: true, + }, + { + name: "exponent-err-too-big", + json: "0e100000000000", + expectedResult: 0, + err: true, + }, + { + name: "exponent-err-too-big", + json: "0e100000000000 ", + expectedResult: 0, + err: true, + }, + { + name: "basic-exponent-err", + json: "3e", + expectedResult: 0, + err: true, + }, + { + name: "error3", + json: "0E40", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "error4", + json: "0.E----", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "error5", + json: "0E40", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "error6", + json: "0.e-9", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "error7", + json: "-5.e-2", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-float", + json: "8.32 ", + expectedResult: 8, + }, + { + name: "error", + json: "83zez4", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "error", + json: "8ea00$aa5", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "error2", + json: "-8e+00$aa5", + expectedResult: 0, + err: true, + }, + { + name: "invalid-type", + json: `"string"`, + expectedResult: 0, + err: true, + errType: InvalidUnmarshalError(""), + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + json := []byte(testCase.json) + var v = (*int32)(nil) + err := Unmarshal(json, &v) + if testCase.err { + assert.NotNil(t, err, "Err must not be nil") + if testCase.errType != nil { + assert.IsType( + t, + testCase.errType, + err, + fmt.Sprintf("err should be of type %s", reflect.TypeOf(err).String()), + ) + } + return + } + + assert.Nil(t, err, "Err must be nil") + if testCase.resultIsNil { + assert.Nil(t, v) + } else { + assert.Equal(t, testCase.expectedResult, *v, fmt.Sprintf("v must be equal to %d", testCase.expectedResult)) + } + }) + } +} + +func TestDecoderInt16(t *testing.T) { + testCases := []struct { + name string + json string + expectedResult int16 + err bool + errType interface{} + }{ + { + name: "basic-positive", + json: "100", + expectedResult: 100, + }, + { + name: "basic-positive2", + json: " 5321", + expectedResult: 5321, + }, + { + name: "basic-negative", + json: "-2", + expectedResult: -2, + }, + { + name: "basic-null", + json: "null", + expectedResult: 0, + }, + { + name: "basic-null-err", + json: "nxll", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-negative-err", + json: "-", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-negative-err", + json: "-q", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-skip-data-err", + json: "trua", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-negative2", + json: "-2456", + expectedResult: -2456, + }, + { + name: "basic-big", + json: " 24566", + expectedResult: 24566, + }, + { + 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, + }, + { + name: "basic-big-overflow2", + json: "21474836483", + expectedResult: 0, + err: true, + }, + { + name: "basic-float", + json: "2.4595", + expectedResult: 2, + }, + { + name: "basic-float2", + json: "-7.8876", + expectedResult: -7, + }, + { + name: "basic-float2", + json: "-7.8876a", + expectedResult: 0, + err: true, + }, + { + name: "basic-exponent-positive-positive-exp", + json: "1.2E2", + expectedResult: 120, + }, + { + name: "basic-exponent-positive-positive-exp1", + json: "3.5e+001 ", + expectedResult: 35, + }, + { + name: "basic-exponent-positive-positive-exp1", + json: "3.5e+002", + expectedResult: 350, + }, + { + name: "basic-exponent-positive-positive-exp2", + json: "5e+03", + expectedResult: 5000, + }, + { + name: "basic-exponent-positive-positive-exp3", + json: "3e+3", + expectedResult: 3000, + }, + { + name: "basic-exponent-positive-positive-exp4", + json: "8e+02 ", + expectedResult: 800, + }, + { + name: "basic-exponent-positive-negative-exp", + json: "1e-2 ", + expectedResult: 0, + }, + { + name: "basic-exponent-positive-negative-exp2", + json: "5E-6", + expectedResult: 0, + }, + { + name: "basic-exponent-positive-negative-exp3", + json: "3e-3", + expectedResult: 0, + }, + { + name: "basic-exponent-positive-negative-exp4", + json: "8e-005", + expectedResult: 0, + }, + { + name: "basic-exponent-negative-positive-exp", + json: "-1e2", + expectedResult: -100, + }, + { + name: "exponent-err-", + json: "0.1e", + expectedResult: 0, + err: true, + }, + { + name: "exponent-err-too-big", + json: "0.1e10000000000000000000", + expectedResult: 0, + err: true, + }, + { + name: "exponent-err-too-big", + json: "0.1e10000000000 ", + expectedResult: 0, + err: true, + }, + { + name: "exponent-err-too-big", + json: "0e10000000000 ", + expectedResult: 0, + err: true, + }, + { + name: "exponent-err-too-big", + json: "0.1932242242424244244e1000000000000000000000000", + expectedResult: 0, + err: true, + }, + { + name: "basic-exponent-negative-positive-exp2", + json: "-5e+03", + expectedResult: -5000, + }, + { + name: "basic-exponent-negative-positive-exp3", + json: "-3e03", + expectedResult: -3000, + }, + { + name: "basic-exponent-negative-positive-exp4", + json: "-8e+003", + expectedResult: -8000, + }, + { + name: "basic-exponent-err", + json: "3e", + expectedResult: 0, + err: true, + }, + { + name: "basic-float", + json: "8.32 ", + expectedResult: 8, + }, + { + name: "error", + json: "83zez4", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "error", + json: "8ea00$aa5", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "error2", + json: "-8e+00$aa5", + expectedResult: 0, + err: true, + }, + { + name: "error3", + json: "0E40", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "error4", + json: "0.E----", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "error5", + json: "0E40", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "error6", + json: "0.e-9", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "error7", + json: "0.e", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "error8", + json: "-5.e-2", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "invalid-type", + json: `"string"`, + expectedResult: 0, + err: true, + errType: InvalidUnmarshalError(""), + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + json := []byte(testCase.json) + var v int16 + err := Unmarshal(json, &v) + if testCase.err { + assert.NotNil(t, err, "Err must not be nil") + if testCase.errType != nil { + assert.IsType( + t, + testCase.errType, + err, + fmt.Sprintf("err should be of type %s", reflect.TypeOf(err).String()), + ) + } + } else { + assert.Nil(t, err, "Err must be nil") + } + assert.Equal(t, testCase.expectedResult, v, fmt.Sprintf("v must be equal to %d", testCase.expectedResult)) + }) + } + t.Run("pool-error", func(t *testing.T) { + result := int16(1) + dec := NewDecoder(nil) + dec.Release() + defer func() { + err := recover() + assert.NotNil(t, err, "err shouldnt be nil") + assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") + }() + _ = dec.DecodeInt16(&result) + assert.True(t, false, "should not be called as decoder should have panicked") + + }) + t.Run("decoder-api", func(t *testing.T) { + var v int16 + dec := NewDecoder(strings.NewReader(`33`)) + defer dec.Release() + err := dec.DecodeInt16(&v) + 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(``)) + defer dec.Release() + err := dec.DecodeInt16(&v) + assert.NotNil(t, err, "Err must not be nil") + assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") + }) +} +func TestDecoderInt16Null(t *testing.T) { + testCases := []struct { + name string + json string + expectedResult int16 + err bool + errType interface{} + resultIsNil bool + }{ + { + name: "basic-positive", + json: "100", + expectedResult: 100, + }, + { + name: "basic-positive2", + json: " 5321", + expectedResult: 5321, + }, + { + name: "basic-negative", + json: "-2", + expectedResult: -2, + }, + { + name: "basic-null", + json: "null", + expectedResult: 0, + resultIsNil: true, + }, + { + name: "basic-null-err", + json: "nxll", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-negative-err", + json: "-", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-negative-err", + json: "-q", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-skip-data-err", + json: "trua", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-negative2", + json: "-2456", + expectedResult: -2456, + }, + { + name: "basic-big", + json: " 24566", + expectedResult: 24566, + }, + { + 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, + }, + { + name: "basic-big-overflow2", + json: "21474836483", + expectedResult: 0, + err: true, + }, + { + name: "basic-float", + json: "2.4595", + expectedResult: 2, + }, + { + name: "basic-float2", + json: "-7.8876", + expectedResult: -7, + }, + { + name: "basic-float2", + json: "-7.8876a", + expectedResult: 0, + err: true, + }, + { + name: "basic-exponent-positive-positive-exp", + json: "1.2E2", + expectedResult: 120, + }, + { + name: "basic-exponent-positive-positive-exp1", + json: "3.5e+001 ", + expectedResult: 35, + }, + { + name: "basic-exponent-positive-positive-exp1", + json: "3.5e+002", + expectedResult: 350, + }, + { + name: "basic-exponent-positive-positive-exp2", + json: "5e+03", + expectedResult: 5000, + }, + { + name: "basic-exponent-positive-positive-exp3", + json: "3e+3", + expectedResult: 3000, + }, + { + name: "basic-exponent-positive-positive-exp4", + json: "8e+02 ", + expectedResult: 800, + }, + { + name: "basic-exponent-positive-negative-exp", + json: "1e-2 ", + expectedResult: 0, + }, + { + name: "basic-exponent-positive-negative-exp2", + json: "5E-6", + expectedResult: 0, + }, + { + name: "basic-exponent-positive-negative-exp3", + json: "3e-3", + expectedResult: 0, + }, + { + name: "basic-exponent-positive-negative-exp4", + json: "8e-005", + expectedResult: 0, + }, + { + name: "basic-exponent-negative-positive-exp", + json: "-1e2", + expectedResult: -100, + }, + { + name: "exponent-err-", + json: "0.1e", + expectedResult: 0, + err: true, + }, + { + name: "exponent-err-too-big", + json: "0.1e10000000000000000000", + expectedResult: 0, + err: true, + }, + { + name: "exponent-err-too-big", + json: "0.1e10000000000 ", + expectedResult: 0, + err: true, + }, + { + name: "exponent-err-too-big", + json: "0e10000000000 ", + expectedResult: 0, + err: true, + }, + { + name: "exponent-err-too-big", + json: "0.1932242242424244244e1000000000000000000000000", + expectedResult: 0, + err: true, + }, + { + name: "basic-exponent-negative-positive-exp2", + json: "-5e+03", + expectedResult: -5000, + }, + { + name: "basic-exponent-negative-positive-exp3", + json: "-3e03", + expectedResult: -3000, + }, + { + name: "basic-exponent-negative-positive-exp4", + json: "-8e+003", + expectedResult: -8000, + }, + { + name: "basic-exponent-err", + json: "3e", + expectedResult: 0, + err: true, + }, + { + name: "basic-float", + json: "8.32 ", + expectedResult: 8, + }, + { + name: "error", + json: "83zez4", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "error", + json: "8ea00$aa5", expectedResult: 0, err: true, + errType: InvalidJSONError(""), }, { - name: "basic-exponent-err", - json: "3e", + name: "error2", + json: "-8e+00$aa5", expectedResult: 0, err: true, }, @@ -886,37 +2481,19 @@ func TestDecoderInt32(t *testing.T) { }, { name: "error7", - json: "-5.e-2", - expectedResult: 0, - err: true, - errType: InvalidJSONError(""), - }, - { - name: "basic-float", - json: "8.32 ", - expectedResult: 8, - }, - { - name: "error", - json: "83zez4", + json: "0.e", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { - name: "error", - json: "8ea00$aa5", + name: "error8", + json: "-5.e-2", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { - name: "error2", - json: "-8e+00$aa5", - expectedResult: 0, - err: true, - }, - { name: "invalid-type", json: `"string"`, expectedResult: 0, @@ -927,7 +2504,7 @@ func TestDecoderInt32(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { json := []byte(testCase.json) - var v int32 + var v = (*int16)(nil) err := Unmarshal(json, &v) if testCase.err { assert.NotNil(t, err, "Err must not be nil") @@ -939,56 +2516,23 @@ func TestDecoderInt32(t *testing.T) { fmt.Sprintf("err should be of type %s", reflect.TypeOf(err).String()), ) } + return + } + assert.Nil(t, err, "Err must be nil") + if testCase.resultIsNil { + assert.Nil(t, v) } else { - assert.Nil(t, err, "Err must be nil") + assert.Equal(t, testCase.expectedResult, *v, fmt.Sprintf("v must be equal to %d", testCase.expectedResult)) } - assert.Equal(t, testCase.expectedResult, v, fmt.Sprintf("v must be equal to %d", testCase.expectedResult)) }) } - t.Run("pool-error", func(t *testing.T) { - result := int32(1) - dec := NewDecoder(nil) - dec.Release() - defer func() { - err := recover() - assert.NotNil(t, err, "err shouldnt be nil") - assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") - }() - _ = dec.DecodeInt32(&result) - assert.True(t, false, "should not be called as decoder should have panicked") - - }) - t.Run("decoder-api", func(t *testing.T) { - var v int32 - dec := NewDecoder(strings.NewReader(`33`)) - defer dec.Release() - err := dec.DecodeInt32(&v) - 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(``)) - defer dec.Release() - err := dec.DecodeInt32(&v) - assert.NotNil(t, err, "Err must not be nil") - assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") - }) } -func TestDecoderInt16(t *testing.T) { +func TestDecoderInt8(t *testing.T) { testCases := []struct { name string json string - expectedResult int16 + expectedResult int8 err bool errType interface{} }{ @@ -999,8 +2543,8 @@ func TestDecoderInt16(t *testing.T) { }, { name: "basic-positive2", - json: " 5321", - expectedResult: 5321, + json: " 127", + expectedResult: 127, }, { name: "basic-negative", @@ -1013,22 +2557,22 @@ func TestDecoderInt16(t *testing.T) { expectedResult: 0, }, { - name: "basic-null-err", - json: "nxll", + name: "basic-negative-err", + json: "-", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { name: "basic-negative-err", - json: "-", + json: "-q", expectedResult: 0, err: true, errType: InvalidJSONError(""), }, { - name: "basic-negative-err", - json: "-q", + name: "basic-null-err", + json: "nxll", expectedResult: 0, err: true, errType: InvalidJSONError(""), @@ -1042,29 +2586,29 @@ func TestDecoderInt16(t *testing.T) { }, { name: "basic-negative2", - json: "-2456", - expectedResult: -2456, + json: "-123", + expectedResult: -123, }, { name: "basic-big", - json: " 24566", - expectedResult: 24566, + json: " 43", + expectedResult: 43, }, { name: "basic-big-overflow", - json: "66535", + json: " 2147483648", expectedResult: 0, err: true, }, { name: "basic-big-overflow", - json: "32768", + json: "137", expectedResult: 0, err: true, }, { name: "basic-big-overflow", - json: " 2147483648", + json: "128", expectedResult: 0, err: true, }, @@ -1102,23 +2646,13 @@ func TestDecoderInt16(t *testing.T) { }, { name: "basic-exponent-positive-positive-exp1", - json: "3.5e+002", - expectedResult: 350, + json: "3.5e+001", + expectedResult: 35, }, { name: "basic-exponent-positive-positive-exp2", - json: "5e+03", - expectedResult: 5000, - }, - { - name: "basic-exponent-positive-positive-exp3", - json: "3e+3", - expectedResult: 3000, - }, - { - name: "basic-exponent-positive-positive-exp4", - json: "8e+02 ", - expectedResult: 800, + json: "5e+01", + expectedResult: 50, }, { name: "basic-exponent-positive-negative-exp", @@ -1137,7 +2671,17 @@ func TestDecoderInt16(t *testing.T) { }, { name: "basic-exponent-positive-negative-exp4", - json: "8e-005", + json: "8e-1 ", + expectedResult: 0, + }, + { + name: "basic-exponent-positive-negative-exp4", + json: "8e1 ", + expectedResult: 80, + }, + { + name: "basic-exponent-positive-negative-exp4", + json: "8e-1", expectedResult: 0, }, { @@ -1146,26 +2690,31 @@ func TestDecoderInt16(t *testing.T) { expectedResult: -100, }, { - name: "exponent-err-", - json: "0.1e", - expectedResult: 0, - err: true, + name: "basic-exponent-negative-positive-exp2", + json: "-5e+01", + expectedResult: -50, }, { - name: "exponent-err-too-big", - json: "0.1e10000000000000000000", + name: "basic-exponent-negative-positive-exp3", + json: "-3e01", + expectedResult: -30, + }, + { + name: "error3", + json: "0E40", expectedResult: 0, err: true, + errType: InvalidJSONError(""), }, { - name: "exponent-err-too-big", - json: "0.1e10000000000 ", + name: "exponent-err-", + json: "0.1e", expectedResult: 0, err: true, }, { name: "exponent-err-too-big", - json: "0e10000000000 ", + json: "0.1e10000000000000000000", expectedResult: 0, err: true, }, @@ -1176,19 +2725,21 @@ func TestDecoderInt16(t *testing.T) { err: true, }, { - name: "basic-exponent-negative-positive-exp2", - json: "-5e+03", - expectedResult: -5000, + name: "basic-exponent-negative-positive-exp4", + json: "-8e+001", + expectedResult: -80, }, { - name: "basic-exponent-negative-positive-exp3", - json: "-3e03", - expectedResult: -3000, + name: "exponent-err-too-big2", + json: "0e100 ", + expectedResult: 0, + err: true, }, { - name: "basic-exponent-negative-positive-exp4", - json: "-8e+003", - expectedResult: -8000, + name: "exponent-err-too-big2", + json: "0.1e100 ", + expectedResult: 0, + err: true, }, { name: "basic-exponent-err", @@ -1222,13 +2773,6 @@ func TestDecoderInt16(t *testing.T) { err: true, }, { - name: "error3", - json: "0E40", - expectedResult: 0, - err: true, - errType: InvalidJSONError(""), - }, - { name: "error4", json: "0.E----", expectedResult: 0, @@ -1264,6 +2808,13 @@ func TestDecoderInt16(t *testing.T) { errType: InvalidJSONError(""), }, { + name: "error8", + json: "-5.01e", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { name: "invalid-type", json: `"string"`, expectedResult: 0, @@ -1274,7 +2825,7 @@ func TestDecoderInt16(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { json := []byte(testCase.json) - var v int16 + var v int8 err := Unmarshal(json, &v) if testCase.err { assert.NotNil(t, err, "Err must not be nil") @@ -1293,7 +2844,7 @@ func TestDecoderInt16(t *testing.T) { }) } t.Run("pool-error", func(t *testing.T) { - result := int16(1) + result := int8(1) dec := NewDecoder(nil) dec.Release() defer func() { @@ -1301,43 +2852,43 @@ func TestDecoderInt16(t *testing.T) { assert.NotNil(t, err, "err shouldnt be nil") assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") }() - _ = dec.DecodeInt16(&result) + _ = dec.DecodeInt8(&result) assert.True(t, false, "should not be called as decoder should have panicked") }) t.Run("decoder-api", func(t *testing.T) { - var v int16 + var v int8 dec := NewDecoder(strings.NewReader(`33`)) defer dec.Release() - err := dec.DecodeInt16(&v) + err := dec.DecodeInt8(&v) assert.Nil(t, err, "Err must be nil") - assert.Equal(t, int16(33), v, "v must be equal to 33") + assert.Equal(t, int8(33), v, "v must be equal to 33") }) t.Run("decoder-api2", func(t *testing.T) { - var v int16 + 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, int16(33), v, "v must be equal to 33") + assert.Equal(t, int8(33), v, "v must be equal to 33") }) t.Run("decoder-api-invalid-json", func(t *testing.T) { - var v int16 + var v int8 dec := NewDecoder(strings.NewReader(``)) defer dec.Release() - err := dec.DecodeInt16(&v) + err := dec.DecodeInt8(&v) assert.NotNil(t, err, "Err must not be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") }) } - -func TestDecoderInt8(t *testing.T) { +func TestDecoderInt8Null(t *testing.T) { testCases := []struct { name string json string expectedResult int8 err bool errType interface{} + resultIsNil bool }{ { name: "basic-positive", @@ -1358,6 +2909,7 @@ func TestDecoderInt8(t *testing.T) { name: "basic-null", json: "null", expectedResult: 0, + resultIsNil: true, }, { name: "basic-negative-err", @@ -1628,7 +3180,7 @@ func TestDecoderInt8(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { json := []byte(testCase.json) - var v int8 + var v = (*int8)(nil) err := Unmarshal(json, &v) if testCase.err { assert.NotNil(t, err, "Err must not be nil") @@ -1640,47 +3192,14 @@ func TestDecoderInt8(t *testing.T) { fmt.Sprintf("err should be of type %s", reflect.TypeOf(err).String()), ) } + return + } + assert.Nil(t, err, "Err must be nil") + if testCase.resultIsNil { + assert.Nil(t, v) } else { - assert.Nil(t, err, "Err must be nil") + assert.Equal(t, testCase.expectedResult, *v, fmt.Sprintf("v must be equal to %d", testCase.expectedResult)) } - assert.Equal(t, testCase.expectedResult, v, fmt.Sprintf("v must be equal to %d", testCase.expectedResult)) }) } - t.Run("pool-error", func(t *testing.T) { - result := int8(1) - dec := NewDecoder(nil) - dec.Release() - defer func() { - err := recover() - assert.NotNil(t, err, "err shouldnt be nil") - assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") - }() - _ = dec.DecodeInt8(&result) - assert.True(t, false, "should not be called as decoder should have panicked") - - }) - t.Run("decoder-api", func(t *testing.T) { - var v int8 - dec := NewDecoder(strings.NewReader(`33`)) - defer dec.Release() - err := dec.DecodeInt8(&v) - 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(``)) - defer dec.Release() - err := dec.DecodeInt8(&v) - assert.NotNil(t, err, "Err must not be nil") - assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") - }) } diff --git a/decode_number_uint.go b/decode_number_uint.go @@ -52,6 +52,50 @@ func (dec *Decoder) decodeUint8(v *uint8) error { } return dec.raiseInvalidJSONErr(dec.cursor) } +func (dec *Decoder) decodeUint8Null(v **uint8) error { + for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { + switch c := dec.data[dec.cursor]; c { + case ' ', '\n', '\t', '\r', ',': + continue + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + val, err := dec.getUint8() + if err != nil { + return err + } + if *v == nil { + *v = new(uint8) + } + **v = val + return nil + case '-': // if negative, we just set it to 0 and set error + dec.err = dec.makeInvalidUnmarshalErr(v) + err := dec.skipData() + if err != nil { + return err + } + if *v == nil { + *v = new(uint8) + } + return nil + case 'n': + dec.cursor++ + err := dec.assertNull() + if err != nil { + return err + } + dec.cursor++ + return nil + default: + dec.err = dec.makeInvalidUnmarshalErr(v) + err := dec.skipData() + if err != nil { + return err + } + return nil + } + } + return dec.raiseInvalidJSONErr(dec.cursor) +} func (dec *Decoder) getUint8() (uint8, error) { var end = dec.cursor @@ -122,6 +166,50 @@ func (dec *Decoder) decodeUint16(v *uint16) error { } return dec.raiseInvalidJSONErr(dec.cursor) } +func (dec *Decoder) decodeUint16Null(v **uint16) error { + for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { + switch c := dec.data[dec.cursor]; c { + case ' ', '\n', '\t', '\r', ',': + continue + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + val, err := dec.getUint16() + if err != nil { + return err + } + if *v == nil { + *v = new(uint16) + } + **v = val + return nil + case '-': + dec.err = dec.makeInvalidUnmarshalErr(v) + err := dec.skipData() + if err != nil { + return err + } + if *v == nil { + *v = new(uint16) + } + return nil + case 'n': + dec.cursor++ + err := dec.assertNull() + if err != nil { + return err + } + dec.cursor++ + return nil + default: + dec.err = dec.makeInvalidUnmarshalErr(v) + err := dec.skipData() + if err != nil { + return err + } + return nil + } + } + return dec.raiseInvalidJSONErr(dec.cursor) +} func (dec *Decoder) getUint16() (uint16, error) { var end = dec.cursor @@ -192,6 +280,50 @@ func (dec *Decoder) decodeUint32(v *uint32) error { } return dec.raiseInvalidJSONErr(dec.cursor) } +func (dec *Decoder) decodeUint32Null(v **uint32) error { + for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { + switch c := dec.data[dec.cursor]; c { + case ' ', '\n', '\t', '\r', ',': + continue + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + val, err := dec.getUint32() + if err != nil { + return err + } + if *v == nil { + *v = new(uint32) + } + **v = val + return nil + case '-': + dec.err = dec.makeInvalidUnmarshalErr(v) + err := dec.skipData() + if err != nil { + return err + } + if *v == nil { + *v = new(uint32) + } + return nil + case 'n': + dec.cursor++ + err := dec.assertNull() + if err != nil { + return err + } + dec.cursor++ + return nil + default: + dec.err = dec.makeInvalidUnmarshalErr(v) + err := dec.skipData() + if err != nil { + return err + } + return nil + } + } + return dec.raiseInvalidJSONErr(dec.cursor) +} func (dec *Decoder) getUint32() (uint32, error) { var end = dec.cursor @@ -261,6 +393,50 @@ func (dec *Decoder) decodeUint64(v *uint64) error { } return dec.raiseInvalidJSONErr(dec.cursor) } +func (dec *Decoder) decodeUint64Null(v **uint64) error { + for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { + switch c := dec.data[dec.cursor]; c { + case ' ', '\n', '\t', '\r', ',': + continue + case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': + val, err := dec.getUint64() + if err != nil { + return err + } + if *v == nil { + *v = new(uint64) + } + **v = val + return nil + case '-': + dec.err = dec.makeInvalidUnmarshalErr(v) + err := dec.skipData() + if err != nil { + return err + } + if *v == nil { + *v = new(uint64) + } + return nil + case 'n': + dec.cursor++ + err := dec.assertNull() + if err != nil { + return err + } + dec.cursor++ + return nil + default: + dec.err = dec.makeInvalidUnmarshalErr(v) + err := dec.skipData() + if err != nil { + return err + } + return nil + } + } + return dec.raiseInvalidJSONErr(dec.cursor) +} func (dec *Decoder) getUint64() (uint64, error) { var end = dec.cursor diff --git a/decode_number_uint_test.go b/decode_number_uint_test.go @@ -163,12 +163,447 @@ func TestDecoderUint64(t *testing.T) { assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") }) } +func TestDecoderUint64Null(t *testing.T) { + testCases := []struct { + name string + json string + expectedResult uint64 + err bool + errType interface{} + resultIsNil bool + }{ + { + name: "basic-positive", + json: "100", + expectedResult: 100, + }, + { + name: "basic-positive2", + json: " 1039405", + expectedResult: 1039405, + }, + { + name: "basic-negative", + json: "-2", + expectedResult: 0, + err: true, + }, + { + name: "basic-null", + json: "null", + expectedResult: 0, + resultIsNil: true, + }, + { + name: "basic-null-err", + json: "nxll", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-skip-data-err", + json: "trua", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-big", + json: "18446744073709551615", + expectedResult: 18446744073709551615, + }, + { + name: "basic-big-overflow", + json: "18446744073709551616", + expectedResult: 0, + err: true, + }, + { + name: "basic-big-overflow", + json: "18446744073709551625", + expectedResult: 0, + err: true, + }, + { + name: "basic-big-overflow2", + json: "184467440737095516161", + expectedResult: 0, + err: true, + }, + { + name: "basic-negative2", + json: "-2349557", + expectedResult: 0, + err: true, + }, + { + name: "basic-float", + json: "2.4595", + expectedResult: 2, + }, + { + name: "basic-float2", + json: "-7.8876", + expectedResult: 0, + err: true, + }, + { + name: "error1", + json: "132zz4", + expectedResult: 0, + err: true, + }, + { + name: "error", + json: "-83zez4", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "invalid-type", + json: `"string"`, + expectedResult: 0, + err: true, + errType: InvalidUnmarshalError(""), + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + json := []byte(testCase.json) + var v = (*uint64)(nil) + err := Unmarshal(json, &v) + if testCase.err { + assert.NotNil(t, err, "Err must not be nil") + if testCase.errType != nil { + assert.IsType( + t, + testCase.errType, + err, + fmt.Sprintf("err should be of type %s", reflect.TypeOf(err).String()), + ) + } + return + } + assert.Nil(t, err, "Err must be nil") + if testCase.resultIsNil { + assert.Nil(t, v) + } else { + assert.Equal(t, testCase.expectedResult, *v, fmt.Sprintf("v must be equal to %d", testCase.expectedResult)) + } + }) + } +} + +func TestDecoderUint32(t *testing.T) { + testCases := []struct { + name string + json string + expectedResult uint32 + err bool + errType interface{} + }{ + { + name: "basic-positive", + json: "100", + expectedResult: 100, + }, + { + name: "basic-positive2", + json: " 1039405 ", + expectedResult: 1039405, + }, + { + name: "basic-negative", + json: "-2", + expectedResult: 0, + err: true, + }, + { + name: "basic-null", + json: "null", + expectedResult: 0, + }, + { + name: "basic-null-err", + json: "nxll", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-skip-data-err", + json: "trua", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-negative2", + json: "-2349557", + expectedResult: 0, + err: true, + }, + { + name: "basic-big", + json: "4294967295", + expectedResult: 4294967295, + }, + { + name: "basic-big-overflow", + json: " 4294967298", + expectedResult: 0, + err: true, + }, + { + name: "basic-big-overflow", + json: "4294967395", + expectedResult: 0, + err: true, + }, + { + name: "basic-big-overflow2", + json: "42949672983", + expectedResult: 0, + err: true, + }, + { + name: "basic-float", + json: "2.4595", + expectedResult: 2, + }, + { + name: "basic-float2", + json: "-7.8876", + expectedResult: 0, + err: true, + }, + { + name: "error", + json: "83zez4", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "error", + json: "-83zez4", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "invalid-type", + json: `"string"`, + expectedResult: 0, + err: true, + errType: InvalidUnmarshalError(""), + }, + { + name: "invalid-json", + json: `123invalid`, + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + json := []byte(testCase.json) + var v uint32 + err := Unmarshal(json, &v) + if testCase.err { + assert.NotNil(t, err, "Err must not be nil") + if testCase.errType != nil { + assert.IsType( + t, + testCase.errType, + err, + fmt.Sprintf("err should be of type %s", reflect.TypeOf(err).String()), + ) + } + } else { + assert.Nil(t, err, "Err must be nil") + } + assert.Equal(t, testCase.expectedResult, v, fmt.Sprintf("v must be equal to %d", testCase.expectedResult)) + }) + } + t.Run("pool-error", func(t *testing.T) { + result := uint32(1) + dec := NewDecoder(nil) + dec.Release() + defer func() { + err := recover() + assert.NotNil(t, err, "err shouldnt be nil") + assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") + }() + _ = dec.DecodeUint32(&result) + assert.True(t, false, "should not be called as decoder should have panicked") + }) + t.Run("decoder-api", func(t *testing.T) { + var v uint32 + dec := NewDecoder(strings.NewReader(`33`)) + defer dec.Release() + err := dec.DecodeUint32(&v) + assert.Nil(t, err, "Err must be nil") + assert.Equal(t, uint32(33), v, "v must be equal to 33") + }) + t.Run("decoder-api-json-error", func(t *testing.T) { + var v uint32 + dec := NewDecoder(strings.NewReader(``)) + defer dec.Release() + err := dec.DecodeUint32(&v) + assert.NotNil(t, err, "Err must not be nil") + assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") + }) +} +func TestDecoderUint32Null(t *testing.T) { + testCases := []struct { + name string + json string + expectedResult uint32 + err bool + errType interface{} + resultIsNil bool + }{ + { + name: "basic-positive", + json: "100", + expectedResult: 100, + }, + { + name: "basic-positive2", + json: " 1039405 ", + expectedResult: 1039405, + }, + { + name: "basic-negative", + json: "-2", + expectedResult: 0, + err: true, + }, + { + name: "basic-null", + json: "null", + expectedResult: 0, + resultIsNil: true, + }, + { + name: "basic-null-err", + json: "nxll", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-skip-data-err", + json: "trua", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-negative2", + json: "-2349557", + expectedResult: 0, + err: true, + }, + { + name: "basic-big", + json: "4294967295", + expectedResult: 4294967295, + }, + { + name: "basic-big-overflow", + json: " 4294967298", + expectedResult: 0, + err: true, + }, + { + name: "basic-big-overflow", + json: "4294967395", + expectedResult: 0, + err: true, + }, + { + name: "basic-big-overflow2", + json: "42949672983", + expectedResult: 0, + err: true, + }, + { + name: "basic-float", + json: "2.4595", + expectedResult: 2, + }, + { + name: "basic-float2", + json: "-7.8876", + expectedResult: 0, + err: true, + }, + { + name: "error", + json: "83zez4", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "error", + json: "-83zez4", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "invalid-type", + json: `"string"`, + expectedResult: 0, + err: true, + errType: InvalidUnmarshalError(""), + }, + { + name: "invalid-json", + json: `123invalid`, + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + json := []byte(testCase.json) + var v = (*uint32)(nil) + err := Unmarshal(json, &v) + if testCase.err { + assert.NotNil(t, err, "Err must not be nil") + if testCase.errType != nil { + assert.IsType( + t, + testCase.errType, + err, + fmt.Sprintf("err should be of type %s", reflect.TypeOf(err).String()), + ) + } + return + } + assert.Nil(t, err, "Err must be nil") + if testCase.resultIsNil { + assert.Nil(t, v) + } else { + assert.Equal(t, testCase.expectedResult, *v, fmt.Sprintf("v must be equal to %d", testCase.expectedResult)) + } + }) + } +} -func TestDecoderUint32(t *testing.T) { +func TestDecoderUint16(t *testing.T) { testCases := []struct { name string json string - expectedResult uint32 + expectedResult uint16 err bool errType interface{} }{ @@ -179,8 +614,8 @@ func TestDecoderUint32(t *testing.T) { }, { name: "basic-positive2", - json: " 1039405 ", - expectedResult: 1039405, + json: " 3224 ", + expectedResult: 3224, }, { name: "basic-negative", @@ -208,15 +643,29 @@ func TestDecoderUint32(t *testing.T) { errType: InvalidJSONError(""), }, { + name: "basic-skip-data-err", + json: "trua", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-overflow", + json: "335346564", + expectedResult: 0, + err: true, + errType: InvalidUnmarshalError(""), + }, + { name: "basic-negative2", - json: "-2349557", + json: "-24467", expectedResult: 0, err: true, }, { name: "basic-big", - json: "4294967295", - expectedResult: 4294967295, + json: "54546", + expectedResult: 54546, }, { name: "basic-big-overflow", @@ -226,7 +675,13 @@ func TestDecoderUint32(t *testing.T) { }, { name: "basic-big-overflow", - json: "4294967395", + json: " 65537", + expectedResult: 0, + err: true, + }, + { + name: "basic-big-overflow", + json: " 66537", expectedResult: 0, err: true, }, @@ -279,7 +734,7 @@ func TestDecoderUint32(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { json := []byte(testCase.json) - var v uint32 + var v uint16 err := Unmarshal(json, &v) if testCase.err { assert.NotNil(t, err, "Err must not be nil") @@ -298,7 +753,7 @@ func TestDecoderUint32(t *testing.T) { }) } t.Run("pool-error", func(t *testing.T) { - result := uint32(1) + result := uint16(1) dec := NewDecoder(nil) dec.Release() defer func() { @@ -306,34 +761,42 @@ func TestDecoderUint32(t *testing.T) { assert.NotNil(t, err, "err shouldnt be nil") assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") }() - _ = dec.DecodeUint32(&result) + _ = dec.DecodeUint16(&result) assert.True(t, false, "should not be called as decoder should have panicked") }) t.Run("decoder-api", func(t *testing.T) { - var v uint32 + var v uint16 dec := NewDecoder(strings.NewReader(`33`)) defer dec.Release() - err := dec.DecodeUint32(&v) + err := dec.DecodeUint16(&v) assert.Nil(t, err, "Err must be nil") - assert.Equal(t, uint32(33), v, "v must be equal to 33") + 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 uint32 + var v uint16 dec := NewDecoder(strings.NewReader(``)) defer dec.Release() - err := dec.DecodeUint32(&v) + err := dec.DecodeUint16(&v) assert.NotNil(t, err, "Err must not be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") }) } - -func TestDecoderUint16(t *testing.T) { +func TestDecoderUint16Null(t *testing.T) { testCases := []struct { name string json string expectedResult uint16 err bool errType interface{} + resultIsNil bool }{ { name: "basic-positive", @@ -355,6 +818,7 @@ func TestDecoderUint16(t *testing.T) { name: "basic-null", json: "null", expectedResult: 0, + resultIsNil: true, }, { name: "basic-null-err", @@ -462,7 +926,7 @@ func TestDecoderUint16(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { json := []byte(testCase.json) - var v uint16 + var v = (*uint16)(nil) err := Unmarshal(json, &v) if testCase.err { assert.NotNil(t, err, "Err must not be nil") @@ -474,48 +938,16 @@ func TestDecoderUint16(t *testing.T) { fmt.Sprintf("err should be of type %s", reflect.TypeOf(err).String()), ) } + return + } + assert.Nil(t, err, "Err must be nil") + if testCase.resultIsNil { + assert.Nil(t, v) } else { - assert.Nil(t, err, "Err must be nil") + assert.Equal(t, testCase.expectedResult, *v, fmt.Sprintf("v must be equal to %d", testCase.expectedResult)) } - assert.Equal(t, testCase.expectedResult, v, fmt.Sprintf("v must be equal to %d", testCase.expectedResult)) }) } - t.Run("pool-error", func(t *testing.T) { - result := uint16(1) - dec := NewDecoder(nil) - dec.Release() - defer func() { - err := recover() - assert.NotNil(t, err, "err shouldnt be nil") - assert.IsType(t, InvalidUsagePooledDecoderError(""), err, "err should be of type InvalidUsagePooledDecoderError") - }() - _ = dec.DecodeUint16(&result) - assert.True(t, false, "should not be called as decoder should have panicked") - }) - t.Run("decoder-api", func(t *testing.T) { - var v uint16 - dec := NewDecoder(strings.NewReader(`33`)) - defer dec.Release() - err := dec.DecodeUint16(&v) - 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(``)) - defer dec.Release() - err := dec.DecodeUint16(&v) - assert.NotNil(t, err, "Err must not be nil") - assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") - }) } func TestDecoderUint8(t *testing.T) { @@ -696,3 +1128,152 @@ func TestDecoderUint8(t *testing.T) { assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") }) } + +func TestDecoderUint8Null(t *testing.T) { + testCases := []struct { + name string + json string + expectedResult uint8 + err bool + errType interface{} + resultIsNil bool + }{ + { + name: "basic-positive", + json: "100", + expectedResult: 100, + }, + { + name: "basic-positive2", + json: " 255 ", + expectedResult: 255, + }, + { + name: "basic-negative", + json: "-2", + expectedResult: 0, + err: true, + }, + { + name: "basic-null", + json: "null", + expectedResult: 0, + resultIsNil: true, + }, + { + name: "basic-null-err", + json: "nxll", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-skip-data-err", + json: "trua", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "basic-negative2", + json: "-234", + expectedResult: 0, + err: true, + }, + { + name: "basic-big", + json: "200", + expectedResult: 200, + }, + { + name: "basic-overflow", + json: "256", + expectedResult: 0, + err: true, + errType: InvalidUnmarshalError(""), + }, + { + name: "basic-overflow", + json: "274", + expectedResult: 0, + err: true, + errType: InvalidUnmarshalError(""), + }, + { + name: "basic-big-overflow", + json: " 4294967298", + expectedResult: 0, + err: true, + }, + { + name: "basic-big-overflow2", + json: "42949672983", + expectedResult: 0, + err: true, + }, + { + name: "basic-float", + json: "2.4595", + expectedResult: 2, + }, + { + name: "basic-float2", + json: "-7.8876", + expectedResult: 0, + err: true, + }, + { + name: "error", + json: "83zez4", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "error", + json: "-83zez4", + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "invalid-type", + json: `"string"`, + expectedResult: 0, + err: true, + errType: InvalidUnmarshalError(""), + }, + { + name: "invalid-json", + json: `123invalid`, + expectedResult: 0, + err: true, + errType: InvalidJSONError(""), + }, + } + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + json := []byte(testCase.json) + var v = (*uint8)(nil) + err := Unmarshal(json, &v) + if testCase.err { + assert.NotNil(t, err, "Err must not be nil") + if testCase.errType != nil { + assert.IsType( + t, + testCase.errType, + err, + fmt.Sprintf("err should be of type %s", reflect.TypeOf(err).String()), + ) + } + return + } + assert.Nil(t, err, "Err must be nil") + if testCase.resultIsNil { + assert.Nil(t, v) + } else { + assert.Equal(t, testCase.expectedResult, *v, fmt.Sprintf("v must be equal to %d", testCase.expectedResult)) + } + }) + } +} diff --git a/decode_string.go b/decode_string.go @@ -20,7 +20,7 @@ func (dec *Decoder) decodeString(v *string) error { // is string continue case '"': - dec.cursor = dec.cursor + 1 + dec.cursor++ start, end, err := dec.getString() if err != nil { return err @@ -51,6 +51,47 @@ func (dec *Decoder) decodeString(v *string) error { return nil } +func (dec *Decoder) decodeStringNull(v **string) error { + for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { + switch dec.data[dec.cursor] { + case ' ', '\n', '\t', '\r', ',': + // is string + continue + case '"': + dec.cursor++ + start, end, err := dec.getString() + if err != nil { + return err + } + if *v == nil { + *v = new(string) + } + // we do minus one to remove the last quote + d := dec.data[start : end-1] + **v = *(*string)(unsafe.Pointer(&d)) + dec.cursor = end + return nil + // is nil + case 'n': + dec.cursor++ + err := dec.assertNull() + if err != nil { + return err + } + dec.cursor++ + return nil + default: + dec.err = dec.makeInvalidUnmarshalErr(v) + err := dec.skipData() + if err != nil { + return err + } + return nil + } + } + return nil +} + func (dec *Decoder) parseEscapedString() error { if dec.cursor >= dec.length && !dec.read() { return dec.raiseInvalidJSONErr(dec.cursor) diff --git a/decode_string_test.go b/decode_string_test.go @@ -319,6 +319,322 @@ func TestDecoderString(t *testing.T) { }) } } +func TestDecoderStringNull(t *testing.T) { + testCases := []struct { + name string + json string + expectedResult string + err bool + errType interface{} + resultIsNil bool + }{ + { + name: "basic-string", + json: `"string"`, + expectedResult: "string", + err: false, + }, + { + name: "string-solidus", + json: `"\/"`, + expectedResult: "/", + err: false, + }, + { + name: "basic-string", + json: ``, + expectedResult: "", + err: false, + resultIsNil: true, + }, + { + name: "basic-string", + json: `""`, + expectedResult: "", + err: false, + }, + { + name: "basic-string2", + json: `"hello world!"`, + expectedResult: "hello world!", + err: false, + }, + { + name: "escape-control-char", + json: `"\n"`, + expectedResult: "\n", + err: false, + }, + { + name: "escape-control-char", + json: `"\\n"`, + expectedResult: `\n`, + err: false, + }, + { + name: "escape-control-char", + json: `"\t"`, + expectedResult: "\t", + err: false, + }, + { + name: "escape-control-char", + json: `"\\t"`, + expectedResult: `\t`, + err: false, + }, + { + name: "escape-control-char", + json: `"\b"`, + expectedResult: "\b", + err: false, + }, + { + name: "escape-control-char", + json: `"\\b"`, + expectedResult: `\b`, + err: false, + }, + { + name: "escape-control-char", + json: `"\f"`, + expectedResult: "\f", + err: false, + }, + { + name: "escape-control-char", + json: `"\\f"`, + expectedResult: `\f`, + err: false, + }, + { + name: "escape-control-char", + json: `"\r"`, + expectedResult: "\r", + err: false, + }, + { + name: "escape-control-char", + json: `"\`, + expectedResult: "", + err: true, + }, + { + name: "escape-control-char-solidus", + json: `"\/"`, + expectedResult: "/", + err: false, + }, + { + name: "escape-control-char-solidus", + json: `"/"`, + expectedResult: "/", + err: false, + }, + { + name: "escape-control-char-solidus-escape-char", + json: `"\\/"`, + expectedResult: `\/`, + err: false, + }, + { + name: "escape-control-char", + json: `"\\r"`, + expectedResult: `\r`, + err: false, + }, + { + name: "utf8", + json: `"𠜎 𠜱 𠝹 𠱓 𠱸 𠲖 𠳏 𠳕 𠴕 𠵼 𠵿"`, + expectedResult: "𠜎 𠜱 𠝹 𠱓 𠱸 𠲖 𠳏 𠳕 𠴕 𠵼 𠵿", + err: false, + }, + { + name: "utf8-code-point", + json: `"\u06fc"`, + expectedResult: `ۼ`, + err: false, + }, + { + name: "utf8-code-point-escaped", + json: `"\\u2070"`, + expectedResult: `\u2070`, + err: false, + }, + { + name: "utf8-code-point-err", + json: `"\u2Z70"`, + expectedResult: ``, + err: true, + }, + { + name: "utf16-surrogate", + json: `"\uD834\uDD1E"`, + expectedResult: `𝄞`, + err: false, + }, + { + name: "utf16-surrogate", + json: `"\uD834\\"`, + expectedResult: `�\`, + err: false, + }, + { + name: "utf16-surrogate", + json: `"\uD834\uD834"`, + expectedResult: "�\x00\x00\x00", + err: false, + }, + { + name: "utf16-surrogate", + json: `"\uD834"`, + expectedResult: `�`, + err: false, + }, + { + name: "utf16-surrogate-err", + json: `"\uD834\`, + expectedResult: ``, + err: true, + }, + { + name: "utf16-surrogate-err2", + json: `"\uD834\uDZ1E`, + expectedResult: ``, + err: true, + }, + { + name: "utf16-surrogate-err3", + json: `"\uD834`, + expectedResult: ``, + err: true, + }, + { + name: "utf16-surrogate-followed-by-control-char", + json: `"\uD834\t"`, + expectedResult: "�\t", + err: false, + }, + { + name: "utf16-surrogate-followed-by-control-char", + json: `"\uD834\n"`, + expectedResult: "�\n", + err: false, + }, + { + name: "utf16-surrogate-followed-by-control-char", + json: `"\uD834\f"`, + expectedResult: "�\f", + err: false, + }, + { + name: "utf16-surrogate-followed-by-control-char", + json: `"\uD834\b"`, + expectedResult: "�\b", + err: false, + }, + { + name: "utf16-surrogate-followed-by-control-char", + json: `"\uD834\r"`, + expectedResult: "�\r", + err: false, + }, + { + name: "utf16-surrogate-followed-by-control-char", + json: `"\uD834\h"`, + expectedResult: "", + err: true, + }, + { + name: "null", + json: `null`, + expectedResult: "", + resultIsNil: true, + }, + { + name: "null-err", + json: `nall`, + expectedResult: "", + err: true, + }, + { + name: "escape quote err", + json: `"test string \" escaped"`, + expectedResult: `test string " escaped`, + err: false, + }, + { + name: "escape quote err2", + json: `"test string \t escaped"`, + expectedResult: "test string \t escaped", + err: false, + }, + { + name: "escape quote err2", + json: `"test string \r escaped"`, + expectedResult: "test string \r escaped", + err: false, + }, + { + name: "escape quote err2", + json: `"test string \b escaped"`, + expectedResult: "test string \b escaped", + err: false, + }, + { + name: "escape quote err", + json: `"test string \n escaped"`, + expectedResult: "test string \n escaped", + err: false, + }, + { + name: "escape quote err", + json: `"test string \\\" escaped`, + expectedResult: ``, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "escape quote err", + json: `"test string \\\l escaped"`, + expectedResult: ``, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "invalid-json", + json: `invalid`, + expectedResult: ``, + err: true, + errType: InvalidJSONError(""), + }, + { + name: "string-complex", + json: ` "string with spaces and \"escape\"d \"quotes\" and escaped line returns \n and escaped \\\\ escaped char"`, + expectedResult: "string with spaces and \"escape\"d \"quotes\" and escaped line returns \n and escaped \\\\ escaped char", + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + str := (*string)(nil) + err := Unmarshal([]byte(testCase.json), &str) + if testCase.err { + assert.NotNil(t, err, "err should not be nil") + if testCase.errType != nil { + assert.IsType(t, testCase.errType, err, "err should of the given type") + } + return + } + assert.Nil(t, err, "Err must be nil") + if testCase.resultIsNil { + assert.Nil(t, str) + } else { + assert.Equal(t, testCase.expectedResult, *str, fmt.Sprintf("v must be equal to %s", testCase.expectedResult)) + } + }) + } +} func TestDecoderStringInvalidType(t *testing.T) { json := []byte(`1`) var v string