gojay

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

commit a110d70fc686bf2213d98657436ae63bb916a8a2
parent b9b0d4d1716630576e74d7083666eee62ae89eae
Author: francoispqt <francois@parquet.ninja>
Date:   Mon, 21 May 2018 23:50:08 +0800

add some tests for float exponents

Diffstat:
Mdecode_number_float.go | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
Mdecode_number_float_test.go | 82+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mdecode_number_int.go | 8++++----
Mdecode_object_test.go | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 220 insertions(+), 16 deletions(-)

diff --git a/decode_number_float.go b/decode_number_float.go @@ -15,7 +15,7 @@ func (dec *Decoder) decodeFloat64(v *float64) error { case ' ', '\n', '\t', '\r', ',': continue case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getFloat(c) + val, err := dec.getFloat() if err != nil { return err } @@ -23,7 +23,7 @@ func (dec *Decoder) decodeFloat64(v *float64) error { return nil case '-': dec.cursor = dec.cursor + 1 - val, err := dec.getFloat(c) + val, err := dec.getFloatNegative() if err != nil { return err } @@ -48,7 +48,20 @@ func (dec *Decoder) decodeFloat64(v *float64) error { return dec.raiseInvalidJSONErr(dec.cursor) } -func (dec *Decoder) getFloat(b byte) (float64, error) { +func (dec *Decoder) getFloatNegative() (float64, error) { + // look for following numbers + for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { + switch dec.data[dec.cursor] { + case '1', '2', '3', '4', '5', '6', '7', '8', '9': + return dec.getFloat() + default: + return 0, dec.raiseInvalidJSONErr(dec.cursor) + } + } + return 0, dec.raiseInvalidJSONErr(dec.cursor) +} + +func (dec *Decoder) getFloat() (float64, error) { var end = dec.cursor var start = dec.cursor // look for following numbers @@ -73,9 +86,16 @@ func (dec *Decoder) getFloat(b byte) (float64, error) { } else if c == 'e' || c == 'E' { afterDecimal := dec.atoi64(start, end) dec.cursor = i + 1 - pow := pow10uint64[end-start+2] + expI := end - start + 2 + if expI >= len(pow10uint64) || expI < 0 { + return 0, dec.raiseInvalidJSONErr(dec.cursor) + } + pow := pow10uint64[expI] floatVal := float64(beforeDecimal+afterDecimal) / float64(pow) exp := dec.getExponent() + if +exp+1 >= int64(len(pow10uint64)) { + return 0, dec.raiseInvalidJSONErr(dec.cursor) + } // if exponent is negative if exp < 0 { return float64(floatVal) * (1 / float64(pow10uint64[exp*-1+1])), nil @@ -88,14 +108,21 @@ func (dec *Decoder) getFloat(b byte) (float64, error) { // then we add both integers // then we divide the number by the power found afterDecimal := dec.atoi64(start, end) - pow := pow10uint64[end-start+2] + expI := end - start + 2 + if expI >= len(pow10uint64) || expI < 0 { + return 0, dec.raiseInvalidJSONErr(dec.cursor) + } + pow := pow10uint64[expI] return float64(beforeDecimal+afterDecimal) / float64(pow), nil case 'e', 'E': - dec.cursor = dec.cursor + 2 + dec.cursor = j + 1 // we get part before decimal as integer beforeDecimal := uint64(dec.atoi64(start, end)) // get exponent exp := dec.getExponent() + if +exp+1 >= int64(len(pow10uint64)) { + return 0, dec.raiseInvalidJSONErr(dec.cursor) + } // if exponent is negative if exp < 0 { return float64(beforeDecimal) * (1 / float64(pow10uint64[exp*-1+1])), nil @@ -126,7 +153,7 @@ func (dec *Decoder) decodeFloat32(v *float32) error { case ' ', '\n', '\t', '\r', ',': continue case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': - val, err := dec.getFloat32(c) + val, err := dec.getFloat32() if err != nil { return err } @@ -134,7 +161,7 @@ func (dec *Decoder) decodeFloat32(v *float32) error { return nil case '-': dec.cursor = dec.cursor + 1 - val, err := dec.getFloat32(c) + val, err := dec.getFloat32Negative() if err != nil { return err } @@ -159,7 +186,20 @@ func (dec *Decoder) decodeFloat32(v *float32) error { return dec.raiseInvalidJSONErr(dec.cursor) } -func (dec *Decoder) getFloat32(b byte) (float32, error) { +func (dec *Decoder) getFloat32Negative() (float32, error) { + // look for following numbers + for ; dec.cursor < dec.length || dec.read(); dec.cursor++ { + switch dec.data[dec.cursor] { + case '1', '2', '3', '4', '5', '6', '7', '8', '9': + return dec.getFloat32() + default: + return 0, dec.raiseInvalidJSONErr(dec.cursor) + } + } + return 0, dec.raiseInvalidJSONErr(dec.cursor) +} + +func (dec *Decoder) getFloat32() (float32, error) { var end = dec.cursor var start = dec.cursor // look for following numbers @@ -184,9 +224,16 @@ func (dec *Decoder) getFloat32(b byte) (float32, error) { } else if c == 'e' || c == 'E' { afterDecimal := dec.atoi32(start, end) dec.cursor = i + 1 - pow := pow10uint64[end-start+2] + expI := end - start + 2 + if expI >= len(pow10uint64) || expI < 0 { + return 0, dec.raiseInvalidJSONErr(dec.cursor) + } + pow := pow10uint64[expI] floatVal := float32(beforeDecimal+afterDecimal) / float32(pow) exp := dec.getExponent() + if +exp+1 >= int64(len(pow10uint64)) { + return 0, dec.raiseInvalidJSONErr(dec.cursor) + } // if exponent is negative if exp < 0 { return float32(floatVal) * (1 / float32(pow10uint64[exp*-1+1])), nil @@ -199,14 +246,21 @@ func (dec *Decoder) getFloat32(b byte) (float32, error) { // then we add both integers // then we divide the number by the power found afterDecimal := dec.atoi32(start, end) - pow := pow10uint64[end-start+2] + expI := end - start + 2 + if expI >= len(pow10uint64) || expI < 0 { + return 0, dec.raiseInvalidJSONErr(dec.cursor) + } + pow := pow10uint64[expI] return float32(beforeDecimal+afterDecimal) / float32(pow), nil case 'e', 'E': - dec.cursor = dec.cursor + 2 + dec.cursor = j + 1 // we get part before decimal as integer beforeDecimal := uint32(dec.atoi32(start, end)) // get exponent exp := dec.getExponent() + if +exp+1 >= int64(len(pow10uint64)) { + return 0, dec.raiseInvalidJSONErr(dec.cursor) + } // if exponent is negative if exp < 0 { return float32(beforeDecimal) * (1 / float32(pow10uint64[exp*-1+1])), nil diff --git a/decode_number_float_test.go b/decode_number_float_test.go @@ -46,6 +46,20 @@ func TestDecoderFloat64(t *testing.T) { 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-null-err", json: "trua", expectedResult: 0, @@ -133,6 +147,30 @@ func TestDecoderFloat64(t *testing.T) { expectedResult: -788.76, }, { + name: "basic-exp-too-big", + json: "1e10000000000 ", + 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: "error", json: "83zez4", expectedResult: 0, @@ -256,6 +294,20 @@ func TestDecoderFloat32(t *testing.T) { 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-exponent-positive-positive-exp4", json: "8e+005", expectedResult: 800000, @@ -306,6 +358,36 @@ func TestDecoderFloat32(t *testing.T) { 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, diff --git a/decode_number_int.go b/decode_number_int.go @@ -168,7 +168,7 @@ func (dec *Decoder) getInt16() (int16, error) { floatVal := float64(beforeDecimal+afterDecimal) / float64(pow) // we have the floating value, now multiply by the exponent exp := dec.getExponent() - if exp+1 >= int64(len(pow10uint64)) || exp < 0 { + if +exp+1 >= int64(len(pow10uint64)) { return 0, dec.raiseInvalidJSONErr(dec.cursor) } val := floatVal * float64(pow10uint64[exp+1]) @@ -347,7 +347,7 @@ func (dec *Decoder) getInt8() (int8, error) { floatVal := float64(beforeDecimal+afterDecimal) / float64(pow) // we have the floating value, now multiply by the exponent exp := dec.getExponent() - if exp+1 >= int64(len(pow10uint64)) || exp < 0 { + if +exp+1 >= int64(len(pow10uint64)) { return 0, dec.raiseInvalidJSONErr(dec.cursor) } val := floatVal * float64(pow10uint64[exp+1]) @@ -525,7 +525,7 @@ func (dec *Decoder) getInt32() (int32, error) { floatVal := float64(beforeDecimal+afterDecimal) / float64(pow) // we have the floating value, now multiply by the exponent exp := dec.getExponent() - if exp+1 >= int64(len(pow10uint64)) || exp < 0 { + if +exp+1 >= int64(len(pow10uint64)) { return 0, dec.raiseInvalidJSONErr(dec.cursor) } val := floatVal * float64(pow10uint64[exp+1]) @@ -707,7 +707,7 @@ func (dec *Decoder) getInt64() (int64, error) { floatVal := float64(beforeDecimal+afterDecimal) / float64(pow) // we have the floating value, now multiply by the exponent exp := dec.getExponent() - if exp+1 >= int64(len(pow10uint64)) || exp < 0 { + if +exp+1 >= int64(len(pow10uint64)) { return 0, dec.raiseInvalidJSONErr(dec.cursor) } val := floatVal * float64(pow10uint64[exp+1]) diff --git a/decode_object_test.go b/decode_object_test.go @@ -53,6 +53,74 @@ func TestDecodeObjectBasic(t *testing.T) { err: false, }, { + name: "basic-with-exponent", + json: `{ + "testStr": "hello world!", + "testInt": 3e3, + "testBool": true, + "testFloat32": 2.345, + "testFloat64": 123.677, + "testInt8": 23, + "testInt16": 1245, + "testInt32": 456778, + "testInt64": 1446685358, + "testUint8": 255, + "testUint16": 3455, + "testUint32": 343443, + "testUint64": 545665757 + }`, + expectedResult: testObject{ + testStr: "hello world!", + testInt: 3000, + testBool: true, + testFloat32: 2.345, + testFloat64: 123.677, + testInt8: 23, + testInt16: 1245, + testInt32: 456778, + testInt64: 1446685358, + testUint8: 255, + testUint16: 3455, + testUint32: 343443, + testUint64: 545665757, + }, + err: false, + }, + { + name: "basic-with-exponent3", + json: `{ + "testStr": "hello world!", + "testInt": 3e-3, + "testBool": true, + "testFloat32": 2.345, + "testFloat64": 12e-3, + "testInt8": 23, + "testInt16": 1245, + "testInt32": 456778, + "testInt64": 1446685358, + "testUint8": 255, + "testUint16": 3455, + "testUint32": 343443, + "testUint64": 545665757 + }`, + expectedResult: testObject{ + testStr: "hello world!", + testInt: 0, + testBool: true, + testFloat32: 2.345, + testFloat64: 0.012, + testInt8: 23, + testInt16: 1245, + testInt32: 456778, + testInt64: 1446685358, + testUint8: 255, + testUint16: 3455, + testUint32: 343443, + testUint64: 545665757, + }, + err: false, + }, + { name: "basic-err-invalid-type", json: `1`, expectedResult: testObject{},