From 7787838f98ef98a47ff249b61baaa315399fbedc Mon Sep 17 00:00:00 2001 From: tdakkota Date: Sun, 20 Mar 2022 19:39:51 +0300 Subject: [PATCH 1/3] test: use integers.json file to benchmark integer decoder --- dec_int_test.go | 58 ++++++++++++++++++++++++++++++++++--------------- enc_test.go | 6 ++--- 2 files changed, 43 insertions(+), 21 deletions(-) diff --git a/dec_int_test.go b/dec_int_test.go index bf78a0c..f2b6209 100644 --- a/dec_int_test.go +++ b/dec_int_test.go @@ -7,28 +7,50 @@ import ( ) func BenchmarkDecoder_Int(b *testing.B) { - data := []byte(`69315063`) - d := GetDecoder() - for i := 0; i < b.N; i++ { - d.ResetBytes(data) - if _, err := d.Int(); err != nil { - b.Fatal(err) - } - } + runTestdataFile("integers.json", b.Fatal, func(name string, data []byte) { + b.Run(name, func(b *testing.B) { + d := GetDecoder() + cb := func(d *Decoder) error { + _, err := d.Int() + return err + } + b.ReportAllocs() + b.ResetTimer() + + for i := 0; i < b.N; i++ { + d.ResetBytes(data) + + if err := d.Arr(cb); err != nil { + b.Fatal(err) + } + } + }) + }) } func BenchmarkDecoder_Uint(b *testing.B) { - data := []byte(`69315063`) - d := GetDecoder() - for i := 0; i < b.N; i++ { - d.ResetBytes(data) - if _, err := d.UInt(); err != nil { - b.Fatal(err) - } - } + runTestdataFile("integers.json", b.Fatal, func(name string, data []byte) { + b.Run(name, func(b *testing.B) { + d := GetDecoder() + cb := func(d *Decoder) error { + _, err := d.UInt() + return err + } + b.ReportAllocs() + b.ResetTimer() + + for i := 0; i < b.N; i++ { + d.ResetBytes(data) + + if err := d.Arr(cb); err != nil { + b.Fatal(err) + } + } + }) + }) } -func TestDecoder_int_sizes(t *testing.T) { +func TestDecoderIntSizes(t *testing.T) { data := []byte(`69315063`) d := GetDecoder() for _, size := range []int{32, 64} { @@ -39,7 +61,7 @@ func TestDecoder_int_sizes(t *testing.T) { } } -func TestDecoder_uint_sizes(t *testing.T) { +func TestDecoderUintSizes(t *testing.T) { data := []byte(`69315063`) d := GetDecoder() for _, size := range []int{32, 64} { diff --git a/enc_test.go b/enc_test.go index aa5fdb8..8c07611 100644 --- a/enc_test.go +++ b/enc_test.go @@ -7,7 +7,7 @@ import ( "github.com/stretchr/testify/require" ) -func TestEncoder_byte_should_grow_buffer(t *testing.T) { +func TestEncoderByteShouldGrowBuffer(t *testing.T) { should := require.New(t) e := GetEncoder() e.byte('1') @@ -41,14 +41,14 @@ func TestEncoder(t *testing.T) { }) } -func TestEncoder_Raw_should_grow_buffer(t *testing.T) { +func TestEncoderRawShouldGrowBuffer(t *testing.T) { should := require.New(t) e := GetEncoder() e.RawStr("123") should.Equal("123", string(e.Bytes())) } -func TestEncoder_Str_should_grow_buffer(t *testing.T) { +func TestEncoderStrShouldGrowBuffer(t *testing.T) { should := require.New(t) e := GetEncoder() e.Str("123") From eb5ab64261437192a2ea12f58f0685c6d4457d5f Mon Sep 17 00:00:00 2001 From: tdakkota Date: Mon, 21 Mar 2022 14:59:17 +0300 Subject: [PATCH 2/3] feat: reduce bound checks in integer decoder --- dec_int.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/dec_int.go b/dec_int.go index 64eab15..f9d351f 100644 --- a/dec_int.go +++ b/dec_int.go @@ -8,13 +8,14 @@ import ( "github.com/go-faster/errors" ) -var intDigits []int8 +var intDigits [256]int8 -const uint32SafeToMultiply10 = uint32(0xffffffff)/10 - 1 -const uint64SafeToMultiple10 = uint64(0xffffffffffffffff)/10 - 1 +const ( + uint32SafeToMultiply10 = uint32(0xffffffff)/10 - 1 + uint64SafeToMultiple10 = uint64(0xffffffffffffffff)/10 - 1 +) func init() { - intDigits = make([]int8, 256) for i := 0; i < len(intDigits); i++ { intDigits[i] = invalidCharForNumber } From 1535c41c06f57af3dc3f2f8e1649dfed5a13f438 Mon Sep 17 00:00:00 2001 From: tdakkota Date: Mon, 21 Mar 2022 15:26:02 +0300 Subject: [PATCH 3/3] test: improve integer test table, add more cases --- dec_int_test.go | 2 +- dec_test.go | 31 +++++++------ int_test.go | 119 ++++++++++++++++++++++++++++++++++++------------ 3 files changed, 109 insertions(+), 43 deletions(-) diff --git a/dec_int_test.go b/dec_int_test.go index f2b6209..5b7ccb0 100644 --- a/dec_int_test.go +++ b/dec_int_test.go @@ -78,7 +78,7 @@ func TestDecoder_Int(t *testing.T) { return &Decoder{ buf: []byte{'1', '2'}, tail: 2, - reader: errReader{}, + reader: r, } } t.Run("32", func(t *testing.T) { diff --git a/dec_test.go b/dec_test.go index 38e2cb7..9190c70 100644 --- a/dec_test.go +++ b/dec_test.go @@ -12,8 +12,8 @@ import ( "github.com/stretchr/testify/require" ) -func runTestCases(t *testing.T, cases []string, cb func(t *testing.T, d *Decoder) error) { - testCase := func(d *Decoder, input string, valid bool) func(t *testing.T) { +func createTestCase(input string, cb func(t *testing.T, d *Decoder) error) func(t *testing.T) { + run := func(d *Decoder, input string, valid bool) func(t *testing.T) { return func(t *testing.T) { t.Cleanup(func() { if t.Failed() { @@ -29,21 +29,26 @@ func runTestCases(t *testing.T, cases []string, cb func(t *testing.T, d *Decoder } } } - for i, input := range cases { + + return func(t *testing.T) { valid := json.Valid([]byte(input)) - t.Run(fmt.Sprintf("Test%d", i), func(t *testing.T) { - t.Run("Buffer", testCase(DecodeStr(input), input, valid)) + t.Run("Buffer", run(DecodeStr(input), input, valid)) - r := strings.NewReader(input) - d := Decode(r, 512) - t.Run("Reader", testCase(d, input, valid)) + r := strings.NewReader(input) + d := Decode(r, 512) + t.Run("Reader", run(d, input, valid)) - r.Reset(input) - obr := iotest.OneByteReader(r) - d.Reset(obr) - t.Run("OneByteReader", testCase(d, input, valid)) - }) + r.Reset(input) + obr := iotest.OneByteReader(r) + d.Reset(obr) + t.Run("OneByteReader", run(d, input, valid)) + } +} + +func runTestCases(t *testing.T, cases []string, cb func(t *testing.T, d *Decoder) error) { + for i, input := range cases { + t.Run(fmt.Sprintf("Test%d", i), createTestCase(input, cb)) } } diff --git a/int_test.go b/int_test.go index 79fe90f..274e793 100644 --- a/int_test.go +++ b/int_test.go @@ -1,11 +1,11 @@ package jx import ( - "bytes" "fmt" "io" "math" "strconv" + "strings" "testing" "github.com/stretchr/testify/assert" @@ -73,26 +73,56 @@ func TestDecoderIntNumbers(t *testing.T) { } func TestReadInt32(t *testing.T) { - inputs := []string{`1`, `12`, `123`, `1234`, `12345`, `123456`, `2147483647`, `-2147483648`} - for _, input := range inputs { - t.Run(input, func(t *testing.T) { + inputs := []string{ + `-12`, + `-1`, + `0`, + `1`, + `12`, + `123`, + `1234`, + `12345`, + `123456`, + `1234567`, + `12345678`, + `123456789`, + `1234567890`, + `2147483647`, + `-2147483648`, + } + for i, input := range inputs { + input := input + t.Run(fmt.Sprintf("Test%d", i+1), createTestCase(input, func(t *testing.T, d *Decoder) error { should := require.New(t) - iter := DecodeStr(input) expected, err := strconv.ParseInt(input, 10, 32) should.NoError(err) - v, err := iter.Int32() + v, err := d.Int32() should.NoError(err) should.Equal(int32(expected), v) - }) - t.Run(input, func(t *testing.T) { + return nil + })) + } + + { + input := "[" + strings.Join(inputs, ",") + "]" + t.Run("Array", createTestCase(input, func(t *testing.T, d *Decoder) error { should := require.New(t) - iter := Decode(bytes.NewBufferString(input), 2) - expected, err := strconv.ParseInt(input, 10, 32) - should.NoError(err) - v, err := iter.Int32() - should.NoError(err) - should.Equal(int32(expected), v) - }) + i := 0 + + return d.Arr(func(d *Decoder) error { + expected, err := strconv.ParseInt(inputs[i], 10, 32) + should.NoError(err) + + v, err := d.Int32() + if err != nil { + return err + } + should.Equal(int32(expected), v) + + i++ + return nil + }) + })) } } @@ -175,26 +205,57 @@ func TestReadInt64Overflow(t *testing.T) { } func TestReadInt64(t *testing.T) { - inputs := []string{`1`, `12`, `123`, `1234`, `12345`, `123456`, `9223372036854775807`, `-9223372036854775808`} - for _, input := range inputs { - t.Run(input, func(t *testing.T) { + inputs := []string{ + `-12`, + `-1`, + `0`, + `1`, + `12`, + `123`, + `1234`, + `12345`, + `123456`, + `1234567`, + `12345678`, + `123456789`, + `1234567890`, + `12345678901`, + `9223372036854775807`, + `-9223372036854775808`, + } + for i, input := range inputs { + input := input + t.Run(fmt.Sprintf("Test%d", i+1), createTestCase(input, func(t *testing.T, d *Decoder) error { should := require.New(t) - iter := DecodeStr(input) expected, err := strconv.ParseInt(input, 10, 64) should.NoError(err) - v, err := iter.Int64() + v, err := d.Int64() should.NoError(err) should.Equal(expected, v) - }) - t.Run(input, func(t *testing.T) { + return nil + })) + } + + { + input := "[" + strings.Join(inputs, ",") + "]" + t.Run("Array", createTestCase(input, func(t *testing.T, d *Decoder) error { should := require.New(t) - iter := Decode(bytes.NewBufferString(input), 2) - expected, err := strconv.ParseInt(input, 10, 64) - should.NoError(err) - v, err := iter.Int64() - should.NoError(err) - should.Equal(expected, v) - }) + i := 0 + + return d.Arr(func(d *Decoder) error { + expected, err := strconv.ParseInt(inputs[i], 10, 64) + should.NoError(err) + + v, err := d.Int64() + if err != nil { + return err + } + should.Equal(expected, v) + + i++ + return nil + }) + })) } }