github.com/bingoohuang/gg@v0.0.0-20240325092523-45da7dee9335/pkg/jsoni/misc_tests/jsoniter_int_test.go (about) 1 //go:build go1.8 2 // +build go1.8 3 4 package misc_tests 5 6 import ( 7 "bytes" 8 "context" 9 "encoding/json" 10 "io" 11 "io/ioutil" 12 "math/rand" 13 "strconv" 14 "testing" 15 16 "github.com/bingoohuang/gg/pkg/jsoni" 17 "github.com/stretchr/testify/require" 18 ) 19 20 func Test_read_uint64_invalid(t *testing.T) { 21 should := require.New(t) 22 iter := jsoni.ParseString(jsoni.ConfigDefault, ",") 23 iter.ReadUint64() 24 should.NotNil(iter.Error) 25 } 26 27 func Test_read_int32_array(t *testing.T) { 28 should := require.New(t) 29 input := `[123,456,789]` 30 val := make([]int32, 0) 31 jsoni.UnmarshalFromString(input, &val) 32 should.Equal(3, len(val)) 33 } 34 35 func Test_read_int64_array(t *testing.T) { 36 should := require.New(t) 37 input := `[123,456,789]` 38 val := make([]int64, 0) 39 jsoni.UnmarshalFromString(input, &val) 40 should.Equal(3, len(val)) 41 } 42 43 func Test_wrap_int(t *testing.T) { 44 should := require.New(t) 45 str, err := jsoni.MarshalToString(jsoni.WrapInt64(100)) 46 should.Nil(err) 47 should.Equal("100", str) 48 } 49 50 func Test_write_val_int(t *testing.T) { 51 should := require.New(t) 52 buf := &bytes.Buffer{} 53 stream := jsoni.NewStream(jsoni.ConfigDefault, buf, 4096) 54 stream.WriteVal(context.Background(), 1001) 55 stream.Flush() 56 should.Nil(stream.Error) 57 should.Equal("1001", buf.String()) 58 } 59 60 func Test_write_val_int_ptr(t *testing.T) { 61 should := require.New(t) 62 buf := &bytes.Buffer{} 63 stream := jsoni.NewStream(jsoni.ConfigDefault, buf, 4096) 64 val := 1001 65 stream.WriteVal(context.Background(), &val) 66 stream.Flush() 67 should.Nil(stream.Error) 68 should.Equal("1001", buf.String()) 69 } 70 71 func Test_float_as_int(t *testing.T) { 72 should := require.New(t) 73 var i int 74 should.NotNil(jsoni.Unmarshal([]byte(`1.1`), &i)) 75 } 76 77 // chunkedData is io.Reader which returns random amount of data in range [1, chunkedData.chunkSize]. 78 // It simulates chunked data on from HTTP server, which is commonly used by net/http package. 79 type chunkedData struct { 80 chunkSize int 81 data []byte 82 head int 83 } 84 85 // Read is implementation of the io.Reader which returns random amount of data in range [1, chunkedData.chunkSize]. 86 func (c *chunkedData) Read(p []byte) (n int, err error) { 87 to := c.head + int(rand.Int31n(int32(c.chunkSize))+1) 88 89 // copy does not copy more data then p can consume 90 n = copy(p, c.data[c.head:to]) 91 c.head = c.head + n 92 if c.head >= len(c.data) { 93 err = io.EOF 94 } 95 return n, err 96 } 97 98 // TestIterator_ReadInt_chunkedInput validates the behaviour of Iterator.ReadInt() method in where: 99 // - it reads data from io.Reader, 100 // - expected value is 0 (zero) 101 // - Iterator.tail == Iterator.head 102 // - Iterator.tail < len(Iterator.buf) 103 // - value in buffer after Iterator.tail is presented from previous read and has '.' character. 104 func TestIterator_ReadInt_chunkedInput(t *testing.T) { 105 should := require.New(t) 106 107 data := &chunkedData{ 108 data: jsonFloatIntArray(t, 10), 109 } 110 111 // because this test is rely on randomness of chunkedData, we are doing multiple iterations to 112 // be sure, that we can hit a required case. 113 for data.chunkSize = 3; data.chunkSize <= len(data.data); data.chunkSize++ { 114 data.head = 0 115 116 iter := jsoni.Parse(jsoni.ConfigDefault, data, data.chunkSize) 117 i := 0 118 for iter.ReadArray() { 119 // every even item is float, let's just skip it. 120 if i%2 == 0 { 121 iter.Skip() 122 i++ 123 continue 124 } 125 126 should.Zero(iter.ReadInt()) 127 should.NoError(iter.Error) 128 129 i++ 130 } 131 } 132 } 133 134 // jsonFloatIntArray generates JSON array where every 135 // 136 // - even item is float 0.1 137 // 138 // - odd item is integer 0 139 // 140 // [0.1, 0, 0.1, 0] 141 func jsonFloatIntArray(t *testing.T, numberOfItems int) []byte { 142 t.Helper() 143 numbers := make([]jsoni.Any, numberOfItems) 144 for i := range numbers { 145 switch i % 2 { 146 case 0: 147 numbers[i] = jsoni.WrapFloat64(0.1) 148 default: 149 numbers[i] = jsoni.WrapInt64(0) 150 } 151 } 152 153 fixture, err := jsoni.ConfigFastest.Marshal(context.Background(), numbers) 154 if err != nil { 155 panic(err) 156 } 157 158 b := &bytes.Buffer{} 159 160 require.NoError( 161 t, 162 json.Compact(b, fixture), 163 "json should be compactable", 164 ) 165 return b.Bytes() 166 } 167 168 func Benchmark_jsoniter_encode_int(b *testing.B) { 169 stream := jsoni.NewStream(jsoni.ConfigDefault, ioutil.Discard, 64) 170 for n := 0; n < b.N; n++ { 171 stream.Reset(nil) 172 stream.WriteUint64(0xffffffff) 173 } 174 } 175 176 func Benchmark_itoa(b *testing.B) { 177 for n := 0; n < b.N; n++ { 178 strconv.FormatInt(0xffffffff, 10) 179 } 180 } 181 182 func Benchmark_jsoniter_int(b *testing.B) { 183 iter := jsoni.NewIterator(jsoni.ConfigDefault) 184 input := []byte(`100`) 185 for n := 0; n < b.N; n++ { 186 iter.ResetBytes(input) 187 iter.ReadInt64() 188 } 189 } 190 191 func Benchmark_json_int(b *testing.B) { 192 for n := 0; n < b.N; n++ { 193 result := int64(0) 194 json.Unmarshal([]byte(`-100`), &result) 195 } 196 }