gojay

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

commit 491f8af345159c2f188f718fd05bef53aa3ef734
parent 1f3d8b54c1fd186494c8f1fe59f24e1f26a9cf18
Author: Francois Parquet <francois.parquet@gmail.com>
Date:   Sun, 26 Aug 2018 22:29:17 +0800

Merge pull request #66 from francoispqt/feature/add-decode-null

Feature/add decode null
Diffstat:
Mdecode.go | 341+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdecode_bool.go | 48++++++++++++++++++++++++++++++++++++++++++++++++
Mdecode_bool_test.go | 239+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdecode_number_float.go | 90+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdecode_number_float_test.go | 728+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Mdecode_number_int.go | 235+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdecode_number_int_test.go | 2206++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
Mdecode_number_uint.go | 176+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdecode_number_uint_test.go | 739+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Mdecode_object_test.go | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
Mdecode_string.go | 43++++++++++++++++++++++++++++++++++++++++++-
Mdecode_string_test.go | 323+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdecode_test.go | 401++++++++++++++++++++++++++++++++++++-------------------------------------------
Mencode_interface_test.go | 6+++++-
Mencode_object_test.go | 12++++++++++--
Mgojay_test.go | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
16 files changed, 5061 insertions(+), 665 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 @@ -52,6 +52,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,245 @@ 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) + }) + } + t.Run("decoder-api-invalid-json2", func(t *testing.T) { + var v = new(bool) + var dec = NewDecoder(strings.NewReader(`folse`)) + err := dec.BoolNull(&v) + assert.NotNil(t, err, "Err must not be nil") + assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") + }) +} + 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,647 @@ 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)) + } + }) + } + t.Run("decoder-api-invalid-json", func(t *testing.T) { + var v = new(float64) + err := Unmarshal([]byte(``), &v) + assert.NotNil(t, err, "Err must not be nil") + assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") + }) + t.Run("decoder-api-invalid-json2", func(t *testing.T) { + var v = new(float64) + var dec = NewDecoder(strings.NewReader(``)) + err := dec.FloatNull(&v) + assert.NotNil(t, err, "Err must not be nil") + assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") + }) + t.Run("decoder-api-invalid-json2", func(t *testing.T) { + var v = new(float64) + var dec = NewDecoder(strings.NewReader(``)) + err := dec.AddFloat64Null(&v) + assert.NotNil(t, err, "Err must not be nil") + assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") + }) +} + 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 +980,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 +1046,7 @@ func TestDecoderFloat32(t *testing.T) { expectedResult: 0, err: true, errType: InvalidJSONError(""), + resultIsNil: true, }, { name: "basic-null-err", @@ -412,6 +1054,7 @@ func TestDecoderFloat32(t *testing.T) { expectedResult: 0, err: true, errType: InvalidJSONError(""), + resultIsNil: true, }, { name: "basic-negative-err", @@ -419,12 +1062,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 +1077,7 @@ func TestDecoderFloat32(t *testing.T) { expectedResult: 0, err: true, errType: InvalidJSONError(""), + resultIsNil: true, }, { name: "basic-exponent-positive-positive-exp4", @@ -440,7 +1086,7 @@ func TestDecoderFloat32(t *testing.T) { }, { name: "basic-exponent-positive-negative-exp", - json: "1e-2", + json: " 1e-2", expectedResult: 0.01, }, { @@ -488,30 +1134,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 +1205,7 @@ func TestDecoderFloat32(t *testing.T) { expectedResult: 0, err: true, errType: InvalidJSONError(""), + resultIsNil: true, }, { name: "error", @@ -561,12 +1213,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 +1228,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,48 +1249,27 @@ 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), ) } }) } - 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-invalid-json", func(t *testing.T) { + var v = new(float32) + err := Unmarshal([]byte(``), &v) + assert.NotNil(t, err, "Err must not be nil") + assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") }) - t.Run("decoder-api-json-error", func(t *testing.T) { - var v float32 - dec := NewDecoder(strings.NewReader(``)) - defer dec.Release() - err := dec.DecodeFloat32(&v) + t.Run("decoder-api-invalid-json2", func(t *testing.T) { + var v = new(float32) + var dec = NewDecoder(strings.NewReader(``)) + err := dec.Float32Null(&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_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,36 @@ 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) + var v = new(int) + err := Unmarshal([]byte(``), &v) + assert.NotNil(t, err, "Err must not be nil") + assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") + }) + t.Run("decoder-api-invalid-json2", func(t *testing.T) { + var v = new(int) + var dec = NewDecoder(strings.NewReader(``)) + err := dec.IntNull(&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 +650,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 +701,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 +716,7 @@ func TestDecoderInt32(t *testing.T) { }, { name: "basic-exponent-positive-positive-exp4", - json: "8e+005 ", + json: "8e+005", expectedResult: 800000, }, { @@ -781,7 +726,7 @@ func TestDecoderInt32(t *testing.T) { }, { name: "basic-exponent-positive-negative-exp2", - json: "5E-6", + json: "5e-6", expectedResult: 0, }, { @@ -790,6 +735,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 +757,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 +772,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 +791,1702 @@ 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: "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)) + } + }) + } + t.Run("decoder-api-invalid-json", func(t *testing.T) { + var v = new(int64) + err := Unmarshal([]byte(``), &v) + assert.NotNil(t, err, "Err must not be nil") + assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") + }) + t.Run("decoder-api-invalid-json2", func(t *testing.T) { + var v = new(int64) + var dec = NewDecoder(strings.NewReader(``)) + err := dec.Int64Null(&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) { + 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)) + } + }) + } + t.Run("decoder-api-invalid-json", func(t *testing.T) { + var v = new(int32) + err := Unmarshal([]byte(``), &v) + assert.NotNil(t, err, "Err must not be nil") + assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") + }) + t.Run("decoder-api-invalid-json2", func(t *testing.T) { + var v = new(int32) + var dec = NewDecoder(strings.NewReader(``)) + err := dec.Int32Null(&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) { + 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: "exponent-err-too-big", - json: "0e100000000000 ", + 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 +2520,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 +2543,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 +2555,36 @@ 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) + var v = new(int16) + err := Unmarshal([]byte(``), &v) + assert.NotNil(t, err, "Err must not be nil") + assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") + }) + t.Run("decoder-api-invalid-json2", func(t *testing.T) { + var v = new(int16) + var dec = NewDecoder(strings.NewReader(``)) + err := dec.Int16Null(&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 +2595,8 @@ func TestDecoderInt16(t *testing.T) { }, { name: "basic-positive2", - json: " 5321", - expectedResult: 5321, + json: " 127", + expectedResult: 127, }, { name: "basic-negative", @@ -1013,22 +2609,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 +2638,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 +2698,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 +2723,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 +2742,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 +2777,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 +2825,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 +2860,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 +2877,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 +2896,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 +2904,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 +2961,7 @@ func TestDecoderInt8(t *testing.T) { name: "basic-null", json: "null", expectedResult: 0, + resultIsNil: true, }, { name: "basic-negative-err", @@ -1628,7 +3232,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,46 +3244,26 @@ 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) + var v = new(int8) + err := Unmarshal([]byte(``), &v) + assert.NotNil(t, err, "Err must not be nil") + assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") + }) + t.Run("decoder-api-invalid-json2", func(t *testing.T) { + var v = new(int8) + var dec = NewDecoder(strings.NewReader(``)) + err := dec.Int8Null(&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,473 @@ 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)) + } + }) + } + t.Run("decoder-api-invalid-json", func(t *testing.T) { + var v = new(uint64) + err := Unmarshal([]byte(``), &v) + assert.NotNil(t, err, "Err must not be nil") + assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") + }) + t.Run("decoder-api-invalid-json2", func(t *testing.T) { + var v = new(uint64) + var dec = NewDecoder(strings.NewReader(``)) + err := dec.Uint64Null(&v) + assert.NotNil(t, err, "Err must not be nil") + assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") + }) +} + +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)) + } + }) + } + t.Run("decoder-api-invalid-json", func(t *testing.T) { + var v = new(uint32) + err := Unmarshal([]byte(``), &v) + assert.NotNil(t, err, "Err must not be nil") + assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") + }) + t.Run("decoder-api-invalid-json2", func(t *testing.T) { + var v = new(uint32) + var dec = NewDecoder(strings.NewReader(``)) + err := dec.Uint32Null(&v) + assert.NotNil(t, err, "Err must not be nil") + assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") + }) +} -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 +640,8 @@ func TestDecoderUint32(t *testing.T) { }, { name: "basic-positive2", - json: " 1039405 ", - expectedResult: 1039405, + json: " 3224 ", + expectedResult: 3224, }, { name: "basic-negative", @@ -208,15 +669,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 +701,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 +760,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 +779,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 +787,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 +844,7 @@ func TestDecoderUint16(t *testing.T) { name: "basic-null", json: "null", expectedResult: 0, + resultIsNil: true, }, { name: "basic-null-err", @@ -462,7 +952,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,45 +964,26 @@ 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-invalid-json", func(t *testing.T) { + var v = new(uint16) + err := Unmarshal([]byte(``), &v) + assert.NotNil(t, err, "Err must not be nil") + assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") }) - t.Run("decoder-api-json-error", func(t *testing.T) { - var v uint16 - dec := NewDecoder(strings.NewReader(``)) - defer dec.Release() - err := dec.DecodeUint16(&v) + t.Run("decoder-api-invalid-json2", func(t *testing.T) { + var v = new(uint16) + var dec = NewDecoder(strings.NewReader(``)) + err := dec.Uint16Null(&v) assert.NotNil(t, err, "Err must not be nil") assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") }) @@ -696,3 +1167,165 @@ 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)) + } + }) + } + t.Run("decoder-api-invalid-json", func(t *testing.T) { + var v = new(uint8) + err := Unmarshal([]byte(``), &v) + assert.NotNil(t, err, "Err must not be nil") + assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") + }) + t.Run("decoder-api-invalid-json2", func(t *testing.T) { + var v = new(uint8) + var dec = NewDecoder(strings.NewReader(``)) + err := dec.Uint8Null(&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_object_test.go b/decode_object_test.go @@ -3,12 +3,19 @@ package gojay import ( "fmt" "io" + "reflect" "strings" "testing" "github.com/stretchr/testify/assert" ) +func makePointer(v interface{}) interface{} { + var ptr = reflect.New(reflect.TypeOf(v)) + ptr.Elem().Set(reflect.ValueOf(v)) + return ptr.Interface() +} + func TestDecodeObjectBasic(t *testing.T) { testCases := []struct { name string @@ -22,33 +29,62 @@ func TestDecodeObjectBasic(t *testing.T) { name: "basic", json: `{ "testStr": "hello world!", + "testStrNull": "hello world!", "testInt": 4535, + "testIntNull": 4535, "testBool": true, + "testBoolNull": true, "testFloat32": 2.345, + "testFloat32Null": 2.345, "testFloat64": 123.677, + "testFloat64Null": 123.677, "testInt8": 23, + "testInt8Null": 23, "testInt16": 1245, + "testInt16Null": 1245, "testInt32": 456778, + "testInt32Null": 456778, "testInt64": 1446685358, + "testInt64Null": 1446685358, "testUint8": 255, + "testUint8Null": 255, "testUint16": 3455, + "testUint16Null": 3455, "testUint32": 343443, - "testUint64": 545665757 + "testUint32Null": 343443, + "testUint64": 545665757, + "testUint64Null": 545665757, + "testSubObjectNull": { + "testStr": "1" + } }`, expectedResult: testObject{ - testStr: "hello world!", - testInt: 4535, - testBool: true, - testFloat32: 2.345, - testFloat64: 123.677, - testInt8: 23, - testInt16: 1245, - testInt32: 456778, - testInt64: 1446685358, - testUint8: 255, - testUint16: 3455, - testUint32: 343443, - testUint64: 545665757, + testStr: "hello world!", + testStrNull: makePointer("hello world!").(*string), + testInt: 4535, + testIntNull: makePointer(4535).(*int), + testBool: true, + testBoolNull: makePointer(true).(*bool), + testFloat32: 2.345, + testFloat32Null: makePointer(float32(2.345)).(*float32), + testFloat64: 123.677, + testFloat64Null: makePointer(float64(123.677)).(*float64), + testInt8: 23, + testInt8Null: makePointer(int8(23)).(*int8), + testInt16: 1245, + testInt16Null: makePointer(int16(1245)).(*int16), + testInt32: 456778, + testInt32Null: makePointer(int32(456778)).(*int32), + testInt64: 1446685358, + testInt64Null: makePointer(int64(1446685358)).(*int64), + testUint8: 255, + testUint8Null: makePointer(uint8(255)).(*uint8), + testUint16: 3455, + testUint16Null: makePointer(uint16(3455)).(*uint16), + testUint32: 343443, + testUint32Null: makePointer(uint32(343443)).(*uint32), + testUint64: 545665757, + testUint64Null: makePointer(uint64(545665757)).(*uint64), }, err: false, }, @@ -1377,6 +1413,10 @@ func TestSkipObject(t *testing.T) { json: `"key":"value\\\\\\\" hello"}`, }, { + name: "basic-escaped", + json: `"key":"value\\\\\\\\"}`, + }, + { name: "basic-err", json: ``, err: true, 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,329 @@ 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)) + } + }) + } + t.Run("decoder-api-invalid-json2", func(t *testing.T) { + var v = new(string) + var dec = NewDecoder(strings.NewReader(`a`)) + err := dec.StringNull(&v) + assert.NotNil(t, err, "Err must not be nil") + assert.IsType(t, InvalidJSONError(""), err, "err should be of type InvalidJSONError") + }) +} func TestDecoderStringInvalidType(t *testing.T) { json := []byte(`1`) var v string diff --git a/decode_test.go b/decode_test.go @@ -1,8 +1,8 @@ package gojay import ( + "bytes" "fmt" - "io" "reflect" "testing" @@ -35,14 +35,15 @@ func (t *testDecodeSlice) UnmarshalJSONArray(dec *Decoder) error { return nil } -// Unmarshal tests -func TestUnmarshalAllTypes(t *testing.T) { - testCases := []struct { - name string - v interface{} - d []byte - expectations func(err error, v interface{}, t *testing.T) - }{ +type allTypeDecodeTestCase struct { + name string + v interface{} + d []byte + expectations func(err error, v interface{}, t *testing.T) +} + +func allTypesTestCases() []allTypeDecodeTestCase { + return []allTypeDecodeTestCase{ { v: new(string), d: []byte(`"test string"`), @@ -54,346 +55,281 @@ func TestUnmarshalAllTypes(t *testing.T) { }, }, { - v: new(string), - d: []byte(`null`), - name: "test decode string null", + v: new(*string), + d: []byte(`"test string"`), + name: "test decode string", expectations: func(err error, v interface{}, t *testing.T) { - vt := v.(*string) + vt := v.(**string) assert.Nil(t, err, "err must be nil") - assert.Equal(t, "", *vt, "v must be equal to 1") + assert.Equal(t, "test string", **vt, "v must be equal to 1") }, }, { - v: new(int), - d: []byte(`1`), - name: "test decode int", + v: new(string), + d: []byte(`null`), + name: "test decode string null", expectations: func(err error, v interface{}, t *testing.T) { - vt := v.(*int) + vt := v.(*string) assert.Nil(t, err, "err must be nil") - assert.Equal(t, 1, *vt, "v must be equal to 1") + assert.Equal(t, "", *vt, "v must be equal to 1") }, }, { - v: new(int64), - d: []byte(`1`), - name: "test decode int64", + v: new(*string), + d: []byte(`null`), + name: "test decode string null", expectations: func(err error, v interface{}, t *testing.T) { - vt := v.(*int64) + vt := v.(**string) assert.Nil(t, err, "err must be nil") - assert.Equal(t, int64(1), *vt, "v must be equal to 1") + assert.Nil(t, *vt, "v must be nil") }, }, { - v: new(uint64), + v: new(*string), d: []byte(`1`), - name: "test decode uint64", - expectations: func(err error, v interface{}, t *testing.T) { - vt := v.(*uint64) - assert.Nil(t, err, "err must be nil") - assert.Equal(t, uint64(1), *vt, "v must be equal to 1") - }, - }, - { - v: new(uint64), - d: []byte(`-1`), - name: "test decode uint64 negative", + name: "test decode string null", expectations: func(err error, v interface{}, t *testing.T) { - vt := v.(*uint64) - assert.NotNil(t, err, "err must not be nil") - assert.Equal(t, uint64(0), *vt, "v must be equal to 1") + vt := v.(**string) + assert.NotNil(t, err, "err must be nil") + assert.Nil(t, *vt, "v must be nil") }, }, { - v: new(int32), + + v: new(int), d: []byte(`1`), - name: "test decode int32", + name: "test decode int", expectations: func(err error, v interface{}, t *testing.T) { - vt := v.(*int32) + vt := v.(*int) assert.Nil(t, err, "err must be nil") - assert.Equal(t, int32(1), *vt, "v must be equal to 1") + assert.Equal(t, 1, *vt, "v must be equal to 1") }, }, { - v: new(uint32), + v: new(*int), d: []byte(`1`), - name: "test decode uint32", + name: "test decode int", expectations: func(err error, v interface{}, t *testing.T) { - vt := v.(*uint32) + vt := v.(**int) assert.Nil(t, err, "err must be nil") - assert.Equal(t, uint32(1), *vt, "v must be equal to 1") - }, - }, - { - v: new(uint32), - d: []byte(`-1`), - name: "test decode uint32 negative", - expectations: func(err error, v interface{}, t *testing.T) { - vt := v.(*uint32) - assert.NotNil(t, err, "err must not be nil") - assert.Equal(t, uint32(0), *vt, "v must be equal to 1") + assert.Equal(t, 1, **vt, "v must be equal to 1") }, }, { - v: new(float64), - d: []byte(`1.15`), - name: "test decode float64", + v: new(*int), + d: []byte(`""`), + name: "test decode int", expectations: func(err error, v interface{}, t *testing.T) { - vt := v.(*float64) - assert.Nil(t, err, "err must be nil") - assert.Equal(t, float64(1.15), *vt, "v must be equal to 1") + assert.NotNil(t, err, "err must be nil") }, }, { - v: new(float64), - d: []byte(`null`), - name: "test decode float64 null", + v: new(*int8), + d: []byte(`1`), + name: "test decode int", expectations: func(err error, v interface{}, t *testing.T) { - vt := v.(*float64) + vt := v.(**int8) assert.Nil(t, err, "err must be nil") - assert.Equal(t, float64(0), *vt, "v must be equal to 1") + assert.Equal(t, int8(1), **vt, "v must be equal to 1") }, }, { - v: new(bool), - d: []byte(`true`), - name: "test decode bool true", + v: new(*int8), + d: []byte(`""`), + name: "test decode int", expectations: func(err error, v interface{}, t *testing.T) { - vt := v.(*bool) - assert.Nil(t, err, "err must be nil") - assert.Equal(t, true, *vt, "v must be equal to 1") + assert.NotNil(t, err, "err must be nil") }, }, { - v: new(bool), - d: []byte(`false`), - name: "test decode bool false", + v: new(*int16), + d: []byte(`1`), + name: "test decode int", expectations: func(err error, v interface{}, t *testing.T) { - vt := v.(*bool) + vt := v.(**int16) assert.Nil(t, err, "err must be nil") - assert.Equal(t, false, *vt, "v must be equal to 1") + assert.Equal(t, int16(1), **vt, "v must be equal to 1") }, }, { - v: new(bool), - d: []byte(`null`), - name: "test decode bool null", + v: new(*int16), + d: []byte(`""`), + name: "test decode int", expectations: func(err error, v interface{}, t *testing.T) { - vt := v.(*bool) - assert.Nil(t, err, "err must be nil") - assert.Equal(t, false, *vt, "v must be equal to 1") + assert.NotNil(t, err, "err must be nil") }, }, { - v: new(testDecodeObj), - d: []byte(`{"test":"test"}`), - name: "test decode object", + v: new(int64), + d: []byte(`1`), + name: "test decode int64", expectations: func(err error, v interface{}, t *testing.T) { - vt := v.(*testDecodeObj) + vt := v.(*int64) assert.Nil(t, err, "err must be nil") - assert.Equal(t, "test", vt.test, "v.test must be equal to 'test'") + assert.Equal(t, int64(1), *vt, "v must be equal to 1") }, }, { - v: new(testDecodeObj), - d: []byte(`{"test":null}`), - name: "test decode object null key", + v: new(*int64), + d: []byte(`1`), + name: "test decode int64", expectations: func(err error, v interface{}, t *testing.T) { - vt := v.(*testDecodeObj) + vt := v.(**int64) assert.Nil(t, err, "err must be nil") - assert.Equal(t, "", vt.test, "v.test must be equal to 'test'") + assert.Equal(t, int64(1), **vt, "v must be equal to 1") }, }, { - v: new(testDecodeObj), - d: []byte(`null`), - name: "test decode object null", + v: new(*int64), + d: []byte(`""`), + name: "test decode int64", expectations: func(err error, v interface{}, t *testing.T) { - vt := v.(*testDecodeObj) - assert.Nil(t, err, "err must be nil") - assert.Equal(t, "", vt.test, "v.test must be equal to 'test'") + assert.NotNil(t, err, "err must be nil") }, }, { - v: new(testDecodeSlice), - d: []byte(`[{"test":"test"}]`), - name: "test decode slice", + v: new(uint64), + d: []byte(`1`), + name: "test decode uint64", expectations: func(err error, v interface{}, t *testing.T) { - vtPtr := v.(*testDecodeSlice) - vt := *vtPtr + vt := v.(*uint64) assert.Nil(t, err, "err must be nil") - assert.Len(t, vt, 1, "len of vt must be 1") - assert.Equal(t, "test", vt[0].test, "vt[0].test must be equal to 'test'") + assert.Equal(t, uint64(1), *vt, "v must be equal to 1") }, }, { - v: new(testDecodeSlice), - d: []byte(`[{"test":"test"},{"test":"test2"}]`), - name: "test decode slice", + v: new(*uint64), + d: []byte(`1`), + name: "test decode uint64", expectations: func(err error, v interface{}, t *testing.T) { - vtPtr := v.(*testDecodeSlice) - vt := *vtPtr + vt := v.(**uint64) assert.Nil(t, err, "err must be nil") - assert.Len(t, vt, 2, "len of vt must be 2") - assert.Equal(t, "test", vt[0].test, "vt[0].test must be equal to 'test'") - assert.Equal(t, "test2", vt[1].test, "vt[1].test must be equal to 'test2'") + assert.Equal(t, uint64(1), **vt, "v must be equal to 1") }, }, { - v: new(struct{}), - d: []byte(`{"test":"test"}`), - name: "test decode invalid type", + v: new(uint64), + d: []byte(`-1`), + name: "test decode uint64 negative", expectations: func(err error, v interface{}, t *testing.T) { + vt := v.(*uint64) assert.NotNil(t, err, "err must not be nil") - assert.IsType(t, InvalidUnmarshalError(""), err, "err must be of type InvalidUnmarshalError") - assert.Equal(t, fmt.Sprintf(invalidUnmarshalErrorMsg, reflect.TypeOf(v).String()), err.Error(), "err message should be equal to invalidUnmarshalErrorMsg") + assert.Equal(t, uint64(0), *vt, "v must be equal to 1") }, }, - } - for _, testCase := range testCases { - testCase := testCase - t.Run(testCase.name, func(*testing.T) { - err := Unmarshal(testCase.d, testCase.v) - testCase.expectations(err, testCase.v, t) - }) - } -} - -// Decode tests - -type TestReader struct { - data string - done bool -} - -func (r *TestReader) Read(b []byte) (int, error) { - if !r.done { - n := copy(b, r.data) - r.done = true - return n, nil - } - return 0, io.EOF -} - -func TestDecodeAllTypes(t *testing.T) { - testCases := []struct { - name string - v interface{} - r TestReader - expectations func(err error, v interface{}, t *testing.T) - }{ { - v: new(string), - r: TestReader{`"test string"`, false}, - name: "test decode string", + v: new(int32), + d: []byte(`1`), + name: "test decode int32", expectations: func(err error, v interface{}, t *testing.T) { - vt := v.(*string) + vt := v.(*int32) assert.Nil(t, err, "err must be nil") - assert.Equal(t, "test string", *vt, "v must be equal to 1") + assert.Equal(t, int32(1), *vt, "v must be equal to 1") }, }, { - v: new(string), - r: TestReader{`null`, false}, - name: "test decode string null", + v: new(*int32), + d: []byte(`1`), + name: "test decode int32", expectations: func(err error, v interface{}, t *testing.T) { - vt := v.(*string) + vt := v.(**int32) assert.Nil(t, err, "err must be nil") - assert.Equal(t, "", *vt, "v must be equal to 1") + assert.Equal(t, int32(1), **vt, "v must be equal to 1") }, }, { - v: new(int), - r: TestReader{`1`, false}, - name: "test decode int", + v: new(uint32), + d: []byte(`1`), + name: "test decode uint32", expectations: func(err error, v interface{}, t *testing.T) { - vt := v.(*int) + vt := v.(*uint32) assert.Nil(t, err, "err must be nil") - assert.Equal(t, 1, *vt, "v must be equal to 1") + assert.Equal(t, uint32(1), *vt, "v must be equal to 1") }, }, { - v: new(int64), - r: TestReader{`1`, false}, - name: "test decode int64", + v: new(*uint32), + d: []byte(`1`), + name: "test decode uint32", expectations: func(err error, v interface{}, t *testing.T) { - vt := v.(*int64) + vt := v.(**uint32) assert.Nil(t, err, "err must be nil") - assert.Equal(t, int64(1), *vt, "v must be equal to 1") + assert.Equal(t, uint32(1), **vt, "v must be equal to 1") }, }, { - v: new(uint64), - r: TestReader{`1`, false}, - name: "test decode uint64", + v: new(uint32), + d: []byte(`-1`), + name: "test decode uint32 negative", expectations: func(err error, v interface{}, t *testing.T) { - vt := v.(*uint64) - assert.Nil(t, err, "err must be nil") - assert.Equal(t, uint64(1), *vt, "v must be equal to 1") + vt := v.(*uint32) + assert.NotNil(t, err, "err must not be nil") + assert.Equal(t, uint32(0), *vt, "v must be equal to 1") }, }, { - v: new(uint64), - r: TestReader{`-1`, false}, - name: "test decode uint64 negative", + v: new(*uint16), + d: []byte(`1`), + name: "test decode uint16", expectations: func(err error, v interface{}, t *testing.T) { - vt := v.(*uint64) - assert.NotNil(t, err, "err must not be nil") - assert.Equal(t, uint64(0), *vt, "v must be equal to 1") + vt := v.(**uint16) + assert.Nil(t, err, "err must be nil") + assert.Equal(t, uint16(1), **vt, "v must be equal to 1") }, }, { - v: new(int32), - r: TestReader{`1`, false}, - name: "test decode int32", + v: new(*uint8), + d: []byte(`1`), + name: "test decode uint8", expectations: func(err error, v interface{}, t *testing.T) { - vt := v.(*int32) + vt := v.(**uint8) assert.Nil(t, err, "err must be nil") - assert.Equal(t, int32(1), *vt, "v must be equal to 1") + assert.Equal(t, uint8(1), **vt, "v must be equal to 1") }, }, { - v: new(uint32), - r: TestReader{`1`, false}, - name: "test decode uint32", + v: new(float64), + d: []byte(`1.15`), + name: "test decode float64", expectations: func(err error, v interface{}, t *testing.T) { - vt := v.(*uint32) + vt := v.(*float64) assert.Nil(t, err, "err must be nil") - assert.Equal(t, uint32(1), *vt, "v must be equal to 1") + assert.Equal(t, float64(1.15), *vt, "v must be equal to 1") }, }, { - v: new(uint32), - r: TestReader{`-1`, false}, - name: "test decode uint32 negative", + v: new(*float64), + d: []byte(`1.15`), + name: "test decode float64", expectations: func(err error, v interface{}, t *testing.T) { - vt := v.(*uint32) - assert.NotNil(t, err, "err must not be nil") - assert.Equal(t, uint32(0), *vt, "v must be equal to 1") + vt := v.(**float64) + assert.Nil(t, err, "err must be nil") + assert.Equal(t, float64(1.15), **vt, "v must be equal to 1") }, }, { v: new(float64), - r: TestReader{`1.15`, false}, - name: "test decode float64", + d: []byte(`null`), + name: "test decode float64 null", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*float64) assert.Nil(t, err, "err must be nil") - assert.Equal(t, float64(1.15), *vt, "v must be equal to 1") + assert.Equal(t, float64(0), *vt, "v must be equal to 1") }, }, { - v: new(float64), - r: TestReader{`null`, false}, + v: new(*float32), + d: []byte(`1.15`), name: "test decode float64 null", expectations: func(err error, v interface{}, t *testing.T) { - vt := v.(*float64) + vt := v.(**float32) assert.Nil(t, err, "err must be nil") - assert.Equal(t, float64(0), *vt, "v must be equal to 1") + assert.Equal(t, float32(1.15), **vt, "v must be equal to 1") }, }, { v: new(bool), - r: TestReader{`true`, false}, + d: []byte(`true`), name: "test decode bool true", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*bool) @@ -402,8 +338,18 @@ func TestDecodeAllTypes(t *testing.T) { }, }, { + v: new(*bool), + d: []byte(`true`), + name: "test decode bool true", + expectations: func(err error, v interface{}, t *testing.T) { + vt := v.(**bool) + assert.Nil(t, err, "err must be nil") + assert.Equal(t, true, **vt, "v must be equal to 1") + }, + }, + { v: new(bool), - r: TestReader{`false`, false}, + d: []byte(`false`), name: "test decode bool false", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*bool) @@ -413,7 +359,7 @@ func TestDecodeAllTypes(t *testing.T) { }, { v: new(bool), - r: TestReader{`null`, false}, + d: []byte(`null`), name: "test decode bool null", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*bool) @@ -423,7 +369,7 @@ func TestDecodeAllTypes(t *testing.T) { }, { v: new(testDecodeObj), - r: TestReader{`{"test":"test"}`, false}, + d: []byte(`{"test":"test"}`), name: "test decode object", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*testDecodeObj) @@ -433,7 +379,7 @@ func TestDecodeAllTypes(t *testing.T) { }, { v: new(testDecodeObj), - r: TestReader{`{"test":null}`, false}, + d: []byte(`{"test":null}`), name: "test decode object null key", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*testDecodeObj) @@ -443,7 +389,7 @@ func TestDecodeAllTypes(t *testing.T) { }, { v: new(testDecodeObj), - r: TestReader{`null`, false}, + d: []byte(`null`), name: "test decode object null", expectations: func(err error, v interface{}, t *testing.T) { vt := v.(*testDecodeObj) @@ -453,7 +399,7 @@ func TestDecodeAllTypes(t *testing.T) { }, { v: new(testDecodeSlice), - r: TestReader{`[{"test":"test"}]`, false}, + d: []byte(`[{"test":"test"}]`), name: "test decode slice", expectations: func(err error, v interface{}, t *testing.T) { vtPtr := v.(*testDecodeSlice) @@ -465,7 +411,7 @@ func TestDecodeAllTypes(t *testing.T) { }, { v: new(testDecodeSlice), - r: TestReader{`[{"test":"test"},{"test":"test2"}]`, false}, + d: []byte(`[{"test":"test"},{"test":"test2"}]`), name: "test decode slice", expectations: func(err error, v interface{}, t *testing.T) { vtPtr := v.(*testDecodeSlice) @@ -478,7 +424,7 @@ func TestDecodeAllTypes(t *testing.T) { }, { v: new(struct{}), - r: TestReader{`{"test":"test"}`, false}, + d: []byte(`{"test":"test"}`), name: "test decode invalid type", expectations: func(err error, v interface{}, t *testing.T) { assert.NotNil(t, err, "err must not be nil") @@ -487,10 +433,25 @@ func TestDecodeAllTypes(t *testing.T) { }, }, } - for _, testCase := range testCases { +} + +// Unmarshal tests +func TestUnmarshalAllTypes(t *testing.T) { + for _, testCase := range allTypesTestCases() { + testCase := testCase + t.Run(testCase.name, func(*testing.T) { + err := Unmarshal(testCase.d, testCase.v) + testCase.expectations(err, testCase.v, t) + }) + } +} + +// Decode tests +func TestDecodeAllTypes(t *testing.T) { + for _, testCase := range allTypesTestCases() { testCase := testCase t.Run(testCase.name, func(*testing.T) { - dec := NewDecoder(&testCase.r) + dec := NewDecoder(bytes.NewReader(testCase.d)) err := dec.Decode(testCase.v) testCase.expectations(err, testCase.v, t) }) diff --git a/encode_interface_test.go b/encode_interface_test.go @@ -112,7 +112,11 @@ var encoderTestCases = []struct { }, }, { - v: &testObject{"漢字", 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.1, 1.1, true, &testObject{}, testSliceInts{}}, + v: &testObject{ + "漢字", nil, 1, nil, 1, nil, 1, nil, 1, nil, 1, nil, + 1, nil, 1, nil, 1, nil, 1, nil, 1.1, nil, 1.1, nil, true, nil, + &testObject{}, testSliceInts{}, + }, expectations: func(t *testing.T, b string, err error) { assert.Nil(t, err, "err should be nil") assert.Equal(t, `{"testStr":"漢字","testInt":1,"testInt64":1,"testInt32":1,"testInt16":1,"testInt8":1,"testUint64":1,"testUint32":1,"testUint16":1,"testUint8":1,"testFloat64":1.1,"testFloat32":1.1,"testBool":true}`, string(b), `string(b) should equal {"testStr":"漢字","testInt":1,"testInt64":1,"testInt32":1,"testInt16":1,"testInt8":1,"testUint64":1,"testUint32":1,"testUint16":1,"testUint8":1,"testFloat64":1.1,"testFloat32":1.1,"testBool":true}`) diff --git a/encode_object_test.go b/encode_object_test.go @@ -83,7 +83,11 @@ func TestEncoderObjectEncodeAPI(t *testing.T) { t.Run("encode-basic", func(t *testing.T) { builder := &strings.Builder{} enc := NewEncoder(builder) - err := enc.EncodeObject(&testObject{"漢字", 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.1, 1.1, true, &testObject{}, testSliceInts{}}) + err := enc.EncodeObject(&testObject{ + "漢字", nil, 1, nil, 1, nil, 1, nil, 1, nil, 1, nil, 1, nil, 1, nil, + 1, nil, 1, nil, 1.1, nil, 1.1, nil, true, nil, + &testObject{}, testSliceInts{}, + }) assert.Nil(t, err, "Error should be nil") assert.Equal( t, @@ -96,7 +100,11 @@ func TestEncoderObjectEncodeAPI(t *testing.T) { func TestEncoderObjectMarshalAPI(t *testing.T) { t.Run("marshal-basic", func(t *testing.T) { - r, err := Marshal(&testObject{"漢字", 1, 1, 1, 1, 1, 1, 1, 1, 1, 1.1, 1.1, true, &testObject{}, testSliceInts{}}) + r, err := Marshal(&testObject{ + "漢字", nil, 1, nil, 1, nil, 1, nil, 1, nil, 1, nil, 1, nil, 1, + nil, 1, nil, 1, nil, 1.1, nil, 1.1, nil, true, nil, + &testObject{}, testSliceInts{}, + }) assert.Nil(t, err, "Error should be nil") assert.Equal( t, diff --git a/gojay_test.go b/gojay_test.go @@ -1,21 +1,34 @@ package gojay type testObject struct { - testStr string - testInt int - testInt64 int64 - testInt32 int32 - testInt16 int16 - testInt8 int8 - testUint64 uint64 - testUint32 uint32 - testUint16 uint16 - testUint8 uint8 - testFloat64 float64 - testFloat32 float32 - testBool bool - testSubObject *testObject - testSubArray testSliceInts + testStr string + testStrNull *string + testInt int + testIntNull *int + testInt64 int64 + testInt64Null *int64 + testInt32 int32 + testInt32Null *int32 + testInt16 int16 + testInt16Null *int16 + testInt8 int8 + testInt8Null *int8 + testUint64 uint64 + testUint64Null *uint64 + testUint32 uint32 + testUint32Null *uint32 + testUint16 uint16 + testUint16Null *uint16 + testUint8 uint8 + testUint8Null *uint8 + testFloat64 float64 + testFloat64Null *float64 + testFloat32 float32 + testFloat32Null *float32 + testBool bool + testBoolNull *bool + testSubObject *testObject + testSubArray testSliceInts } // make sure it implements interfaces @@ -46,36 +59,62 @@ func (t *testObject) UnmarshalJSONObject(dec *Decoder, k string) error { switch k { case "testStr": return dec.AddString(&t.testStr) + case "testStrNull": + return dec.AddStringNull(&t.testStrNull) case "testInt": return dec.AddInt(&t.testInt) + case "testIntNull": + return dec.AddIntNull(&t.testIntNull) case "testInt64": return dec.AddInt64(&t.testInt64) + case "testInt64Null": + return dec.AddInt64Null(&t.testInt64Null) case "testInt32": return dec.AddInt32(&t.testInt32) + case "testInt32Null": + return dec.AddInt32Null(&t.testInt32Null) case "testInt16": return dec.AddInt16(&t.testInt16) + case "testInt16Null": + return dec.AddInt16Null(&t.testInt16Null) case "testInt8": return dec.AddInt8(&t.testInt8) + case "testInt8Null": + return dec.AddInt8Null(&t.testInt8Null) case "testUint64": return dec.AddUint64(&t.testUint64) + case "testUint64Null": + return dec.AddUint64Null(&t.testUint64Null) case "testUint32": return dec.AddUint32(&t.testUint32) + case "testUint32Null": + return dec.AddUint32Null(&t.testUint32Null) case "testUint16": return dec.AddUint16(&t.testUint16) + case "testUint16Null": + return dec.AddUint16Null(&t.testUint16Null) case "testUint8": return dec.AddUint8(&t.testUint8) + case "testUint8Null": + return dec.AddUint8Null(&t.testUint8Null) case "testFloat64": return dec.AddFloat(&t.testFloat64) + case "testFloat64Null": + return dec.AddFloatNull(&t.testFloat64Null) case "testFloat32": return dec.AddFloat32(&t.testFloat32) + case "testFloat32Null": + return dec.AddFloat32Null(&t.testFloat32Null) case "testBool": return dec.AddBool(&t.testBool) + case "testBoolNull": + return dec.AddBoolNull(&t.testBoolNull) } return nil } func (t *testObject) NKeys() int { - return 13 + return 28 } type testObject0Keys struct {