commit a9541d970e672141c20ed15f636c355d4da87a27
parent e463341caf8850e339dff20d8ebab3db8962ad67
Author: francoispqt <francois@parquet.ninja>
Date: Sun, 13 May 2018 13:27:48 +0800
add exponent syntax and check of bools
Diffstat:
9 files changed, 728 insertions(+), 17 deletions(-)
diff --git a/README.md b/README.md
@@ -4,9 +4,10 @@
[![Go doc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square
)](https://godoc.org/github.com/francoispqt/gojay)
![MIT License](https://img.shields.io/badge/license-mit-blue.svg?style=flat-square)
+[![Sourcegraph](https://sourcegraph.com/github.com/francoispqt/gojay/-/badge.svg)](https://sourcegraph.com/github.com/francoispqt/gojay)
# GoJay
-**Package is currently at version 0.10.5 and still under development**
+**Package is currently at version 1.0.0 and safe to use in production**
GoJay is a performant JSON encoder/decoder for Golang (currently the most performant, [see benchmarks](#benchmark-results)).
diff --git a/benchmarks/encoder/encoder_bench_large_test.go b/benchmarks/encoder/encoder_bench_large_test.go
@@ -28,7 +28,7 @@ func BenchmarkJsonIterEncodeLargeStruct(b *testing.B) {
}
}
}
-
+b
func BenchmarkEasyJsonEncodeObjLarge(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
diff --git a/decode_bool.go b/decode_bool.go
@@ -75,13 +75,21 @@ func (dec *Decoder) assertTrue() error {
if dec.data[dec.cursor] != 'e' {
return InvalidJSONError(fmt.Sprintf(invalidJSONCharErrorMsg, dec.data[dec.cursor], dec.cursor))
}
- return nil
- default:
- return InvalidJSONError(fmt.Sprintf(invalidJSONCharErrorMsg, dec.data[dec.cursor], dec.cursor))
+ case 3:
+ switch dec.data[dec.cursor] {
+ case ' ', '\t', '\n', ',', ']', '}':
+ dec.cursor--
+ return nil
+ default:
+ return InvalidJSONError(fmt.Sprintf(invalidJSONCharErrorMsg, dec.data[dec.cursor], dec.cursor))
+ }
}
i++
}
- return InvalidJSONError("Invalid JSO")
+ if i == 3 {
+ return nil
+ }
+ return InvalidJSONError("Invalid JSON")
}
func (dec *Decoder) assertNull() error {
@@ -100,12 +108,20 @@ func (dec *Decoder) assertNull() error {
if dec.data[dec.cursor] != 'l' {
return InvalidJSONError(fmt.Sprintf(invalidJSONCharErrorMsg, dec.data[dec.cursor], dec.cursor))
}
- return nil
- default:
- return InvalidJSONError(fmt.Sprintf(invalidJSONCharErrorMsg, dec.data[dec.cursor], dec.cursor))
+ case 3:
+ switch dec.data[dec.cursor] {
+ case ' ', '\t', '\n', ',', ']', '}':
+ dec.cursor--
+ return nil
+ default:
+ return InvalidJSONError(fmt.Sprintf(invalidJSONCharErrorMsg, dec.data[dec.cursor], dec.cursor))
+ }
}
i++
}
+ if i == 3 {
+ return nil
+ }
return InvalidJSONError("Invalid JSON")
}
@@ -129,11 +145,19 @@ func (dec *Decoder) assertFalse() error {
if dec.data[dec.cursor] != 'e' {
return InvalidJSONError(fmt.Sprintf(invalidJSONCharErrorMsg, dec.data[dec.cursor], dec.cursor))
}
- return nil
- default:
- return InvalidJSONError(fmt.Sprintf(invalidJSONCharErrorMsg, dec.data[dec.cursor], dec.cursor))
+ case 4:
+ switch dec.data[dec.cursor] {
+ case ' ', '\t', '\n', ',', ']', '}':
+ dec.cursor--
+ return nil
+ default:
+ return InvalidJSONError(fmt.Sprintf(invalidJSONCharErrorMsg, dec.data[dec.cursor], dec.cursor))
+ }
}
i++
}
+ if i == 4 {
+ return nil
+ }
return InvalidJSONError("Invalid JSON")
}
diff --git a/decode_bool_test.go b/decode_bool_test.go
@@ -7,6 +7,182 @@ import (
"github.com/stretchr/testify/assert"
)
+func TestDecoderBool(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.False(t, v, "result should be false")
+ },
+ },
+ {
+ 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.False(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.False(t, v, "result should be false")
+ },
+ },
+ {
+ 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.False(t, v, "result should be false")
+ },
+ },
+ {
+ 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.False(t, v, "result should be false")
+ },
+ },
+ {
+ name: "true-error4",
+ 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.False(t, v, "result should be false")
+ },
+ },
+ {
+ 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.False(t, v, "result should be false")
+ },
+ },
+ {
+ 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.False(t, v, "result should be false")
+ },
+ },
+ {
+ 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.False(t, v, "result should be false")
+ },
+ },
+ {
+ 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.False(t, v, "result should be false")
+ },
+ },
+ {
+ 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.False(t, v, "result should be false")
+ },
+ },
+ {
+ 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.False(t, v, "result should be false")
+ },
+ },
+ {
+ 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.False(t, v, "result should be false")
+ },
+ },
+ {
+ 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.False(t, v, "result should be false")
+ },
+ },
+ {
+ 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.False(t, v, "result should be false")
+ },
+ },
+ {
+ 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.False(t, v, "result should be false")
+ },
+ },
+ {
+ 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.False(t, v, "result should be false")
+ },
+ },
+ }
+
+ for _, testCase := range testCases {
+ t.Run(testCase.name, func(t *testing.T) {
+ json := []byte(testCase.json)
+ var v bool
+ err := Unmarshal(json, &v)
+ testCase.expectations(t, v, err)
+ })
+ }
+}
+
func TestDecoderBoolTrue(t *testing.T) {
json := []byte(`true`)
var v bool
diff --git a/decode_number.go b/decode_number.go
@@ -67,6 +67,7 @@ func (dec *Decoder) decodeInt(v *int) error {
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(c)
if err != nil {
@@ -413,11 +414,51 @@ func (dec *Decoder) getInt64(b byte) (int64, error) {
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
end = j
continue
- case ' ', '\n', '\t', '\r':
- continue
- case '.', ',', '}', ']':
+ case ' ', '\t', '\n', ',', '}', ']':
dec.cursor = j
return dec.atoi64(start, end), nil
+ case '.':
+ // if dot is found
+ // look for exponent (e,E) as exponent can change the
+ // way number should be parsed to int.
+ // if no exponent found, just unmarshal the number before decimal point
+ startDecimal := j + 1
+ endDecimal := j + 1
+ j++
+ for ; j < dec.length || dec.read(); j++ {
+ switch dec.data[j] {
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ endDecimal = j
+ continue
+ case 'e', 'E':
+ dec.cursor = j + 1
+ // can try unmarshalling to int as Exponent might change decimal number to non decimal
+ // let's get the float value first
+ // we get part before decimal as integer
+ beforeDecimal := dec.atoi64(start, end)
+ // get number after the decimal point
+ // multiple the before decimal point portion by 10 using bitwise
+ for i := startDecimal; i <= endDecimal; i++ {
+ beforeDecimal = (beforeDecimal << 3) + (beforeDecimal << 1)
+ }
+ // then we add both integers
+ // then we divide the number by the power found
+ afterDecimal := dec.atoi64(startDecimal, endDecimal)
+ pow := pow10uint64[endDecimal-startDecimal+2]
+ floatVal := float64(beforeDecimal+afterDecimal) / float64(pow)
+ // we have the floating value, now multiply by the exponent
+ exp := dec.getExponent()
+ val := floatVal * float64(pow10uint64[exp+1])
+ return int64(val), nil
+ default:
+ dec.cursor = j
+ return dec.atoi64(start, end), nil
+ }
+ }
+ return dec.atoi64(start, end), nil
+ case 'e', 'E':
+ // get init n
+ return dec.getInt64WithExp(dec.atoi64(start, end), j+1)
}
// invalid json we expect numbers, dot (single one), comma, or spaces
return 0, InvalidJSONError("Invalid JSON while parsing number")
@@ -425,6 +466,43 @@ func (dec *Decoder) getInt64(b byte) (int64, error) {
return dec.atoi64(start, end), nil
}
+func (dec *Decoder) getInt64WithExp(init int64, cursor int) (int64, error) {
+ var exp uint64
+ var sign = int64(1)
+ for ; cursor < dec.length || dec.read(); cursor++ {
+ switch dec.data[cursor] {
+ case '+':
+ continue
+ case '-':
+ sign = -1
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ uintv := uint64(digits[dec.data[cursor]])
+ exp = (exp << 3) + (exp << 1) + uintv
+ cursor++
+ for ; cursor < dec.length || dec.read(); cursor++ {
+ switch dec.data[cursor] {
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ uintv := uint64(digits[dec.data[cursor]])
+ exp = (exp << 3) + (exp << 1) + uintv
+ default:
+ if sign == -1 {
+ return init * (1 / int64(pow10uint64[exp+1])), nil
+ }
+ return init * int64(pow10uint64[exp+1]), nil
+ }
+ }
+ if sign == -1 {
+ return init * (1 / int64(pow10uint64[exp+1])), nil
+ }
+ return init * int64(pow10uint64[exp+1]), nil
+ default:
+ dec.err = InvalidJSONError("Invalid JSON")
+ return 0, dec.err
+ }
+ }
+ return 0, InvalidJSONError("Invalid JSON")
+}
+
func (dec *Decoder) getUint64(b byte) (uint64, error) {
var end = dec.cursor
var start = dec.cursor
@@ -457,7 +535,49 @@ func (dec *Decoder) getInt32(b byte) (int32, error) {
continue
case ' ', '\n', '\t', '\r':
continue
- case '.', ',', '}', ']':
+ case '.':
+ // if dot is found
+ // look for exponent (e,E) as exponent can change the
+ // way number should be parsed to int.
+ // if no exponent found, just unmarshal the number before decimal point
+ startDecimal := j + 1
+ endDecimal := j + 1
+ j++
+ for ; j < dec.length || dec.read(); j++ {
+ switch dec.data[j] {
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ endDecimal = j
+ continue
+ case 'e', 'E':
+ dec.cursor = j + 1
+ // can try unmarshalling to int as Exponent might change decimal number to non decimal
+ // let's get the float value first
+ // we get part before decimal as integer
+ beforeDecimal := dec.atoi64(start, end)
+ // get number after the decimal point
+ // multiple the before decimal point portion by 10 using bitwise
+ for i := startDecimal; i <= endDecimal; i++ {
+ beforeDecimal = (beforeDecimal << 3) + (beforeDecimal << 1)
+ }
+ // then we add both integers
+ // then we divide the number by the power found
+ afterDecimal := dec.atoi64(startDecimal, endDecimal)
+ pow := pow10uint64[endDecimal-startDecimal+2]
+ floatVal := float64(beforeDecimal+afterDecimal) / float64(pow)
+ // we have the floating value, now multiply by the exponent
+ exp := dec.getExponent()
+ val := floatVal * float64(pow10uint64[exp+1])
+ return int32(val), nil
+ default:
+ dec.cursor = j
+ return dec.atoi32(start, end), nil
+ }
+ }
+ return dec.atoi32(start, end), nil
+ case 'e', 'E':
+ // get init n
+ return dec.getInt32WithExp(dec.atoi32(start, end), j+1)
+ case ',', '}', ']':
dec.cursor = j
return dec.atoi32(start, end), nil
}
@@ -467,6 +587,43 @@ func (dec *Decoder) getInt32(b byte) (int32, error) {
return dec.atoi32(start, end), nil
}
+func (dec *Decoder) getInt32WithExp(init int32, cursor int) (int32, error) {
+ var exp uint32
+ var sign = int32(1)
+ for ; cursor < dec.length || dec.read(); cursor++ {
+ switch dec.data[cursor] {
+ case '+':
+ continue
+ case '-':
+ sign = -1
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ uintv := uint32(digits[dec.data[cursor]])
+ exp = (exp << 3) + (exp << 1) + uintv
+ cursor++
+ for ; cursor < dec.length || dec.read(); cursor++ {
+ switch dec.data[cursor] {
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ uintv := uint32(digits[dec.data[cursor]])
+ exp = (exp << 3) + (exp << 1) + uintv
+ default:
+ if sign == -1 {
+ return init * (1 / int32(pow10uint64[exp+1])), nil
+ }
+ return init * int32(pow10uint64[exp+1]), nil
+ }
+ }
+ if sign == -1 {
+ return init * (1 / int32(pow10uint64[exp+1])), nil
+ }
+ return init * int32(pow10uint64[exp+1]), nil
+ default:
+ dec.err = InvalidJSONError("Invalid JSON")
+ return 0, dec.err
+ }
+ }
+ return 0, InvalidJSONError("Invalid JSON")
+}
+
func (dec *Decoder) getUint32(b byte) (uint32, error) {
var end = dec.cursor
var start = dec.cursor
@@ -510,6 +667,17 @@ func (dec *Decoder) getFloat(b byte) (float64, error) {
end = i
beforeDecimal = (beforeDecimal << 3) + (beforeDecimal << 1)
continue
+ } else if c == 'e' || c == 'E' {
+ afterDecimal := dec.atoi64(start, end)
+ dec.cursor = i + 1
+ pow := pow10uint64[end-start+2]
+ floatVal := float64(beforeDecimal+afterDecimal) / float64(pow)
+ exp := dec.getExponent()
+ // if exponent is negative
+ if exp < 0 {
+ return float64(floatVal) * (1 / float64(pow10uint64[exp*-1+1])), nil
+ }
+ return float64(floatVal) * float64(pow10uint64[exp+1]), nil
}
dec.cursor = i
break
@@ -519,6 +687,17 @@ func (dec *Decoder) getFloat(b byte) (float64, error) {
afterDecimal := dec.atoi64(start, end)
pow := pow10uint64[end-start+2]
return float64(beforeDecimal+afterDecimal) / float64(pow), nil
+ case 'e', 'E':
+ dec.cursor = dec.cursor + 2
+ // we get part before decimal as integer
+ beforeDecimal := uint64(dec.atoi64(start, end))
+ // get exponent
+ exp := dec.getExponent()
+ // if exponent is negative
+ if exp < 0 {
+ return float64(beforeDecimal) * (1 / float64(pow10uint64[exp*-1+1])), nil
+ }
+ return float64(beforeDecimal) * float64(pow10uint64[exp+1]), nil
case ' ', '\n', '\t', '\r':
continue
case ',', '}', ']': // does not have decimal
@@ -653,3 +832,36 @@ func (dec *Decoder) atoui32(start, end int) uint32 {
}
return val
}
+
+func (dec *Decoder) getExponent() int64 {
+ start := dec.cursor
+ end := dec.cursor
+ for ; dec.cursor < dec.length || dec.read(); dec.cursor++ {
+ switch dec.data[dec.cursor] { // is positive
+ case '0':
+ // skip leading zeroes
+ if start == end {
+ start = dec.cursor
+ end = dec.cursor
+ continue
+ }
+ end = dec.cursor
+ case '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ end = dec.cursor
+ case '-':
+ dec.cursor++
+ return -dec.getExponent()
+ case '+':
+ dec.cursor++
+ return dec.getExponent()
+ default:
+ // if nothing return 0
+ // could raise error
+ if start == end {
+ return 0
+ }
+ return dec.atoi64(start, end)
+ }
+ }
+ return dec.atoi64(start, end)
+}
diff --git a/decode_number_test.go b/decode_number_test.go
@@ -1,12 +1,248 @@
package gojay
import (
+ "fmt"
+ "log"
+ "math"
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
+func TestDecoderInt64Exponent(t *testing.T) {
+ testCases := []struct {
+ name string
+ json string
+ expectedResult int64
+ }{
+ {
+ 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: "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,
+ },
+ }
+ for _, testCase := range testCases {
+ t.Run(testCase.name, func(t *testing.T) {
+ json := []byte(testCase.json)
+ var v int64
+ err := Unmarshal(json, &v)
+ assert.Nil(t, err, "Err must be nil")
+ assert.Equal(t, testCase.expectedResult, v, fmt.Sprintf("v must be equal to %d", testCase.expectedResult))
+ })
+ }
+}
+func TestDecoderInt32Exponent(t *testing.T) {
+ testCases := []struct {
+ name string
+ json string
+ expectedResult int32
+ }{
+ {
+ 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: "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,
+ },
+ }
+ for _, testCase := range testCases {
+ t.Run(testCase.name, func(t *testing.T) {
+ json := []byte(testCase.json)
+ var v int32
+ err := Unmarshal(json, &v)
+ assert.Nil(t, err, "Err must be nil")
+ assert.Equal(t, testCase.expectedResult, v, fmt.Sprintf("v must be equal to %d", testCase.expectedResult))
+ })
+ }
+}
+
+func TestDecoderFloat64Exponent(t *testing.T) {
+ testCases := []struct {
+ name string
+ json string
+ expectedResult float64
+ }{
+ {
+ 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.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,
+ },
+ }
+ for _, testCase := range testCases {
+ t.Run(testCase.name, func(t *testing.T) {
+ json := []byte(testCase.json)
+ var v float64
+ err := Unmarshal(json, &v)
+ log.Print(v)
+ assert.Nil(t, err, "Err must be nil")
+ assert.Equal(t, testCase.expectedResult*1000000, math.Round(v*1000000), fmt.Sprintf("v must be equal to %f", testCase.expectedResult))
+ })
+ }
+}
func TestDecoderIntBasic(t *testing.T) {
json := []byte(`124`)
var v int
@@ -14,6 +250,62 @@ func TestDecoderIntBasic(t *testing.T) {
assert.Nil(t, err, "Err must be nil")
assert.Equal(t, 124, v, "v must be equal to 124")
}
+func TestDecoderIntExponent(t *testing.T) {
+ json := []byte(`1E+2`)
+ var v int
+ err := Unmarshal(json, &v)
+ assert.Nil(t, err, "Err must be nil")
+ assert.Equal(t, 100, v, "v must be equal to 100")
+}
+func TestDecoderIntExponent1(t *testing.T) {
+ json := []byte(`4E+2`)
+ var v int
+ err := Unmarshal(json, &v)
+ assert.Nil(t, err, "Err must be nil")
+ assert.Equal(t, 400, v, "v must be equal to 100")
+}
+func TestDecoderIntExponentComplex(t *testing.T) {
+ json := []byte(`-3E-004`)
+ var v int
+ err := Unmarshal(json, &v)
+ assert.Nil(t, err, "Err must be nil")
+ assert.Equal(t, 0, v, "v must be equal to 0")
+}
+func TestDecoderIntExponentComplex1(t *testing.T) {
+ json := []byte(`-3.12E+005`)
+ var v int
+ err := Unmarshal(json, &v)
+ assert.Nil(t, err, "Err must be nil")
+ assert.Equal(t, -312000, v, "v must be equal to -312000")
+}
+func TestDecoderFloatExponentComplex1(t *testing.T) {
+ json := []byte(`-3E-004`)
+ var v float64
+ err := Unmarshal(json, &v)
+ assert.Nil(t, err, "Err must be nil")
+ assert.Equal(t, int64(-0.0003*10000), int64(v*10000), "v must be equal to -0.0003")
+}
+func TestDecoderFloatExponentComplex2(t *testing.T) {
+ json := []byte(`-3E+004`)
+ var v float64
+ err := Unmarshal(json, &v)
+ assert.Nil(t, err, "Err must be nil")
+ assert.Equal(t, float64(-30000), float64(v), "v must be equal to -30000")
+}
+func TestDecoderFloatExponentComplex3(t *testing.T) {
+ json := []byte(`-3.12E-004`)
+ var v float64
+ err := Unmarshal(json, &v)
+ assert.Nil(t, err, "Err must be nil")
+ assert.Equal(t, int64(-0.000312*1000000), int64(v*1000000), "v must be equal to -30000")
+}
+func TestDecoderIntExponentComplex2(t *testing.T) {
+ json := []byte(`3.12E+005`)
+ var v int
+ err := Unmarshal(json, &v)
+ assert.Nil(t, err, "Err must be nil")
+ assert.Equal(t, 312000, v, "v must be equal to 312000")
+}
func TestDecoderIntNegative(t *testing.T) {
json := []byte(` -124 `)
var v int
@@ -294,6 +586,7 @@ func TestDecoderInt64Negative(t *testing.T) {
assert.Nil(t, err, "Err must be nil")
assert.Equal(t, int64(-124), v, "v must be equal to -124")
}
+
func TestDecoderInt64Null(t *testing.T) {
json := []byte(`null`)
var v int64
diff --git a/decode_object_test.go b/decode_object_test.go
@@ -85,6 +85,10 @@ func assertResult(t *testing.T, v *TestObj, err error) {
assert.Equal(t, "complex string with spaces and some slashes\"", v.test4, "v.test4 must be equal to 'string'")
assert.Equal(t, -1.15657654376543, v.test5, "v.test5 must be equal to 1.15")
assert.Len(t, v.testArr, 2, "v.testArr must be of len 2")
+ assert.Equal(t, v.testArr[0].test, 245, "v.testArr[0].test must be equal to 245")
+ assert.Equal(t, v.testArr[0].test2, 246, "v.testArr[0].test must be equal to 246")
+ assert.Equal(t, v.testArr[1].test, 245, "v.testArr[0].test must be equal to 245")
+ assert.Equal(t, v.testArr[1].test2, 246, "v.testArr[0].test must be equal to 246")
assert.Equal(t, 121, v.testSubObj.test3, "v.testSubObj.test3 must be equal to 121")
assert.Equal(t, 122, v.testSubObj.test4, "v.testSubObj.test4 must be equal to 122")
diff --git a/encode_number.go b/encode_number.go
@@ -111,7 +111,7 @@ func (enc *Encoder) AddInt64(v int64) {
enc.buf = strconv.AppendInt(enc.buf, v, 10)
}
-// AddIntOmitEmpty adds an int to be encoded and skips it if its value is 0,
+// AddInt64OmitEmpty adds an int to be encoded and skips it if its value is 0,
// must be used inside a slice or array encoding (does not encode a key).
func (enc *Encoder) AddInt64OmitEmpty(v int64) {
if v == 0 {
diff --git a/encode_string.go b/encode_string.go
@@ -22,6 +22,7 @@ func (enc *Encoder) encodeString(v string) ([]byte, error) {
return enc.buf, nil
}
+// AppendString appends a string to the buffer
func (enc *Encoder) AppendString(v string) {
enc.grow(len(v) + 2)
enc.writeByte('"')