commit bf45cd129177639df8c9bfdf126d38ba66db949e
parent 3d991e95a8197d6bc05e10dff748d8a0464784e9
Author: francoispqt <francois@parquet.ninja>
Date: Thu, 26 Apr 2018 23:09:00 +0800
begin adding support to exponent syntax for numbers unmarshaling
Diffstat:
4 files changed, 99 insertions(+), 4 deletions(-)
diff --git a/README.md b/README.md
@@ -6,6 +6,9 @@
![MIT License](https://img.shields.io/badge/license-mit-blue.svg?style=flat-square)
# GoJay
+
+**Package is currently at version 0.9 and still under development**
+
GoJay is a performant JSON encoder/decoder for Golang (currently the most performant, [see benchmarks](#benchmark-results)).
It has a simple API and doesn't use reflection. It relies on small interfaces to decode/encode structures and slices.
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_number.go b/decode_number.go
@@ -61,6 +61,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 {
@@ -350,11 +351,29 @@ 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 ',', '}', ']':
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
+ for ; j < dec.length || dec.read(); j++ {
+ switch dec.data[j] {
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ continue
+ case 'e', 'E':
+ // can try unmarshalling to int as Exponent might change decimal number to non decimal
+ 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")
@@ -362,6 +381,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
diff --git a/decode_number_test.go b/decode_number_test.go
@@ -13,6 +13,41 @@ 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 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
@@ -185,6 +220,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