github.com/minio/simdjson-go@v0.4.6-0.20231116094823-04d21cddf993/parse_number_test.go (about) 1 /* 2 * MinIO Cloud Storage, (C) 2020 MinIO, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package simdjson 18 19 import ( 20 "fmt" 21 "math" 22 "math/rand" 23 "regexp" 24 "strconv" 25 "sync" 26 "testing" 27 "time" 28 ) 29 30 func TestNumberIsValid(t *testing.T) { 31 // From: https://stackoverflow.com/a/13340826 32 var jsonNumberRegexp = regexp.MustCompile(`^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$`) 33 isValidNumber := func(s string) bool { 34 tag, _ := parseNumber([]byte(s)) 35 return tag != 0 36 } 37 validTests := []string{ 38 "0", 39 "-0", 40 "1", 41 "-1", 42 "0.1", 43 "-0.1", 44 "1234", 45 "-1234", 46 "12.34", 47 "-12.34", 48 "12E0", 49 "12E1", 50 "12e34", 51 "12E-0", 52 "12e+1", 53 "12e-34", 54 "-12E0", 55 "-12E1", 56 "-12e34", 57 "-12E-0", 58 "-12e+1", 59 "-12e-34", 60 "1.2E0", 61 "1.2E1", 62 "1.2e34", 63 "1.2E-0", 64 "1.2e+1", 65 "1.2e-34", 66 "-1.2E0", 67 "-1.2E1", 68 "-1.2e34", 69 "-1.2E-0", 70 "-1.2e+1", 71 "-1.2e-34", 72 "0E0", 73 "0E1", 74 "0e34", 75 "0E-0", 76 "0e+1", 77 "0e-34", 78 "-0E0", 79 "-0E1", 80 "-0e34", 81 "-0E-0", 82 "-0e+1", 83 "-0e-34", 84 } 85 86 for _, test := range validTests { 87 if !isValidNumber(test) { 88 t.Errorf("%s should be valid", test) 89 } 90 91 if !jsonNumberRegexp.MatchString(test) { 92 t.Errorf("%s should be valid but regexp does not match", test) 93 } 94 } 95 96 invalidTests := []string{ 97 "", 98 "invalid", 99 "1.0.1", 100 "1..1", 101 "-1-2", 102 "012a42", 103 "01.2", 104 "012", 105 "12E12.12", 106 "1e2e3", 107 "1e+-2", 108 "1e--23", 109 "1e", 110 "e1", 111 "1e+", 112 "1ea", 113 "1a", 114 "1.a", 115 "1.", 116 "01", 117 "1.e1", 118 } 119 120 for _, test := range invalidTests { 121 if isValidNumber(test) { 122 t.Errorf("%s should be invalid", test) 123 } 124 125 if jsonNumberRegexp.MatchString(test) { 126 t.Errorf("%s should be invalid but matches regexp", test) 127 } 128 } 129 } 130 131 func closeEnough(d1, d2 float64) (ce bool) { 132 return math.Abs((d1-d2)/(0.5*(d1+d2))) < 1e-20 133 } 134 135 // The following benchmarking code is borrowed from Golang (https://golang.org/src/strconv/atoi_test.go) 136 137 func BenchmarkParseIntGolang(b *testing.B) { 138 b.Run("Pos", func(b *testing.B) { 139 benchmarkParseIntGolang(b, 1) 140 }) 141 b.Run("Neg", func(b *testing.B) { 142 benchmarkParseIntGolang(b, -1) 143 }) 144 } 145 146 type benchCase struct { 147 name string 148 num int64 149 } 150 151 func benchmarkParseIntGolang(b *testing.B, neg int) { 152 cases := []benchCase{ 153 {"63bit", 1<<63 - 1}, 154 } 155 for _, cs := range cases { 156 b.Run(cs.name, func(b *testing.B) { 157 s := fmt.Sprintf("%d", cs.num*int64(neg)) 158 for i := 0; i < b.N; i++ { 159 out, _ := strconv.ParseInt(s, 10, 64) 160 BenchSink += int(out) 161 } 162 }) 163 } 164 } 165 166 var BenchSink int // make sure compiler cannot optimize away benchmarks 167 168 var ( 169 atofOnce sync.Once 170 benchmarksRandomBits [1024]string 171 benchmarksRandomNormal [1024]string 172 benchmarksRandomBitsSimd [1024]string 173 benchmarksRandomNormalSimd [1024]string 174 ) 175 176 func initAtof() { 177 atofOnce.Do(initAtofOnce) 178 } 179 180 func initAtofOnce() { 181 182 // Generate random inputs for tests and benchmarks 183 rand.Seed(time.Now().UnixNano()) 184 185 for i := range benchmarksRandomBits { 186 bits := uint64(rand.Uint32())<<32 | uint64(rand.Uint32()) 187 x := math.Float64frombits(bits) 188 benchmarksRandomBits[i] = strconv.FormatFloat(x, 'g', -1, 64) 189 benchmarksRandomBitsSimd[i] = benchmarksRandomBits[i] + ":" 190 } 191 192 for i := range benchmarksRandomNormal { 193 x := rand.NormFloat64() 194 benchmarksRandomNormal[i] = strconv.FormatFloat(x, 'g', -1, 64) 195 benchmarksRandomNormalSimd[i] = benchmarksRandomNormal[i] + ":" 196 } 197 }