github.com/tidwall/go@v0.0.0-20170415222209-6694a6888b7d/src/runtime/string_test.go (about) 1 // Copyright 2012 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package runtime_test 6 7 import ( 8 "runtime" 9 "strconv" 10 "strings" 11 "testing" 12 ) 13 14 // Strings and slices that don't escape and fit into tmpBuf are stack allocated, 15 // which defeats using AllocsPerRun to test other optimizations. 16 const sizeNoStack = 100 17 18 func BenchmarkCompareStringEqual(b *testing.B) { 19 bytes := []byte("Hello Gophers!") 20 s1, s2 := string(bytes), string(bytes) 21 for i := 0; i < b.N; i++ { 22 if s1 != s2 { 23 b.Fatal("s1 != s2") 24 } 25 } 26 } 27 28 func BenchmarkCompareStringIdentical(b *testing.B) { 29 s1 := "Hello Gophers!" 30 s2 := s1 31 for i := 0; i < b.N; i++ { 32 if s1 != s2 { 33 b.Fatal("s1 != s2") 34 } 35 } 36 } 37 38 func BenchmarkCompareStringSameLength(b *testing.B) { 39 s1 := "Hello Gophers!" 40 s2 := "Hello, Gophers" 41 for i := 0; i < b.N; i++ { 42 if s1 == s2 { 43 b.Fatal("s1 == s2") 44 } 45 } 46 } 47 48 func BenchmarkCompareStringDifferentLength(b *testing.B) { 49 s1 := "Hello Gophers!" 50 s2 := "Hello, Gophers!" 51 for i := 0; i < b.N; i++ { 52 if s1 == s2 { 53 b.Fatal("s1 == s2") 54 } 55 } 56 } 57 58 func BenchmarkCompareStringBigUnaligned(b *testing.B) { 59 bytes := make([]byte, 0, 1<<20) 60 for len(bytes) < 1<<20 { 61 bytes = append(bytes, "Hello Gophers!"...) 62 } 63 s1, s2 := string(bytes), "hello"+string(bytes) 64 for i := 0; i < b.N; i++ { 65 if s1 != s2[len("hello"):] { 66 b.Fatal("s1 != s2") 67 } 68 } 69 b.SetBytes(int64(len(s1))) 70 } 71 72 func BenchmarkCompareStringBig(b *testing.B) { 73 bytes := make([]byte, 0, 1<<20) 74 for len(bytes) < 1<<20 { 75 bytes = append(bytes, "Hello Gophers!"...) 76 } 77 s1, s2 := string(bytes), string(bytes) 78 for i := 0; i < b.N; i++ { 79 if s1 != s2 { 80 b.Fatal("s1 != s2") 81 } 82 } 83 b.SetBytes(int64(len(s1))) 84 } 85 86 func BenchmarkConcatStringAndBytes(b *testing.B) { 87 s1 := []byte("Gophers!") 88 for i := 0; i < b.N; i++ { 89 _ = "Hello " + string(s1) 90 } 91 } 92 93 var escapeString string 94 95 func BenchmarkSliceByteToString(b *testing.B) { 96 buf := []byte{'!'} 97 for n := 0; n < 8; n++ { 98 b.Run(strconv.Itoa(len(buf)), func(b *testing.B) { 99 for i := 0; i < b.N; i++ { 100 escapeString = string(buf) 101 } 102 }) 103 buf = append(buf, buf...) 104 } 105 } 106 107 var stringdata = []struct{ name, data string }{ 108 {"ASCII", "01234567890"}, 109 {"Japanese", "日本語日本語日本語"}, 110 {"MixedLength", "$Ѐࠀက퀀𐀀\U00040000\U0010FFFF"}, 111 } 112 113 func BenchmarkRuneIterate(b *testing.B) { 114 b.Run("range", func(b *testing.B) { 115 for _, sd := range stringdata { 116 b.Run(sd.name, func(b *testing.B) { 117 for i := 0; i < b.N; i++ { 118 for range sd.data { 119 } 120 } 121 }) 122 } 123 }) 124 b.Run("range1", func(b *testing.B) { 125 for _, sd := range stringdata { 126 b.Run(sd.name, func(b *testing.B) { 127 for i := 0; i < b.N; i++ { 128 for _ = range sd.data { 129 } 130 } 131 }) 132 } 133 }) 134 b.Run("range2", func(b *testing.B) { 135 for _, sd := range stringdata { 136 b.Run(sd.name, func(b *testing.B) { 137 for i := 0; i < b.N; i++ { 138 for _, _ = range sd.data { 139 } 140 } 141 }) 142 } 143 }) 144 } 145 146 func BenchmarkArrayEqual(b *testing.B) { 147 a1 := [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} 148 a2 := [16]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16} 149 b.ResetTimer() 150 for i := 0; i < b.N; i++ { 151 if a1 != a2 { 152 b.Fatal("not equal") 153 } 154 } 155 } 156 157 func TestStringW(t *testing.T) { 158 strings := []string{ 159 "hello", 160 "a\u5566\u7788b", 161 } 162 163 for _, s := range strings { 164 var b []uint16 165 for _, c := range s { 166 b = append(b, uint16(c)) 167 if c != rune(uint16(c)) { 168 t.Errorf("bad test: stringW can't handle >16 bit runes") 169 } 170 } 171 b = append(b, 0) 172 r := runtime.GostringW(b) 173 if r != s { 174 t.Errorf("gostringW(%v) = %s, want %s", b, r, s) 175 } 176 } 177 } 178 179 func TestLargeStringConcat(t *testing.T) { 180 output := runTestProg(t, "testprog", "stringconcat") 181 want := "panic: " + strings.Repeat("0", 1<<10) + strings.Repeat("1", 1<<10) + 182 strings.Repeat("2", 1<<10) + strings.Repeat("3", 1<<10) 183 if !strings.HasPrefix(output, want) { 184 t.Fatalf("output does not start with %q:\n%s", want, output) 185 } 186 } 187 188 func TestCompareTempString(t *testing.T) { 189 s := strings.Repeat("x", sizeNoStack) 190 b := []byte(s) 191 n := testing.AllocsPerRun(1000, func() { 192 if string(b) != s { 193 t.Fatalf("strings are not equal: '%v' and '%v'", string(b), s) 194 } 195 if string(b) == s { 196 } else { 197 t.Fatalf("strings are not equal: '%v' and '%v'", string(b), s) 198 } 199 }) 200 if n != 0 { 201 t.Fatalf("want 0 allocs, got %v", n) 202 } 203 } 204 205 func TestStringOnStack(t *testing.T) { 206 s := "" 207 for i := 0; i < 3; i++ { 208 s = "a" + s + "b" + s + "c" 209 } 210 211 if want := "aaabcbabccbaabcbabccc"; s != want { 212 t.Fatalf("want: '%v', got '%v'", want, s) 213 } 214 } 215 216 func TestIntString(t *testing.T) { 217 // Non-escaping result of intstring. 218 s := "" 219 for i := 0; i < 4; i++ { 220 s += string(i+'0') + string(i+'0'+1) 221 } 222 if want := "01122334"; s != want { 223 t.Fatalf("want '%v', got '%v'", want, s) 224 } 225 226 // Escaping result of intstring. 227 var a [4]string 228 for i := 0; i < 4; i++ { 229 a[i] = string(i + '0') 230 } 231 s = a[0] + a[1] + a[2] + a[3] 232 if want := "0123"; s != want { 233 t.Fatalf("want '%v', got '%v'", want, s) 234 } 235 } 236 237 func TestIntStringAllocs(t *testing.T) { 238 unknown := '0' 239 n := testing.AllocsPerRun(1000, func() { 240 s1 := string(unknown) 241 s2 := string(unknown + 1) 242 if s1 == s2 { 243 t.Fatalf("bad") 244 } 245 }) 246 if n != 0 { 247 t.Fatalf("want 0 allocs, got %v", n) 248 } 249 } 250 251 func TestRangeStringCast(t *testing.T) { 252 s := strings.Repeat("x", sizeNoStack) 253 n := testing.AllocsPerRun(1000, func() { 254 for i, c := range []byte(s) { 255 if c != s[i] { 256 t.Fatalf("want '%c' at pos %v, got '%c'", s[i], i, c) 257 } 258 } 259 }) 260 if n != 0 { 261 t.Fatalf("want 0 allocs, got %v", n) 262 } 263 } 264 265 func isZeroed(b []byte) bool { 266 for _, x := range b { 267 if x != 0 { 268 return false 269 } 270 } 271 return true 272 } 273 274 func isZeroedR(r []rune) bool { 275 for _, x := range r { 276 if x != 0 { 277 return false 278 } 279 } 280 return true 281 } 282 283 func TestString2Slice(t *testing.T) { 284 // Make sure we don't return slices that expose 285 // an unzeroed section of stack-allocated temp buf 286 // between len and cap. See issue 14232. 287 s := "foož" 288 b := ([]byte)(s) 289 if !isZeroed(b[len(b):cap(b)]) { 290 t.Errorf("extra bytes not zeroed") 291 } 292 r := ([]rune)(s) 293 if !isZeroedR(r[len(r):cap(r)]) { 294 t.Errorf("extra runes not zeroed") 295 } 296 } 297 298 const intSize = 32 << (^uint(0) >> 63) 299 300 type atoi64Test struct { 301 in string 302 out int64 303 ok bool 304 } 305 306 var atoi64tests = []atoi64Test{ 307 {"", 0, false}, 308 {"0", 0, true}, 309 {"-0", 0, true}, 310 {"1", 1, true}, 311 {"-1", -1, true}, 312 {"12345", 12345, true}, 313 {"-12345", -12345, true}, 314 {"012345", 12345, true}, 315 {"-012345", -12345, true}, 316 {"12345x", 0, false}, 317 {"-12345x", 0, false}, 318 {"98765432100", 98765432100, true}, 319 {"-98765432100", -98765432100, true}, 320 {"20496382327982653440", 0, false}, 321 {"-20496382327982653440", 0, false}, 322 {"9223372036854775807", 1<<63 - 1, true}, 323 {"-9223372036854775807", -(1<<63 - 1), true}, 324 {"9223372036854775808", 0, false}, 325 {"-9223372036854775808", -1 << 63, true}, 326 {"9223372036854775809", 0, false}, 327 {"-9223372036854775809", 0, false}, 328 } 329 330 func TestAtoi(t *testing.T) { 331 switch intSize { 332 case 32: 333 for i := range atoi32tests { 334 test := &atoi32tests[i] 335 out, ok := runtime.Atoi(test.in) 336 if test.out != int32(out) || test.ok != ok { 337 t.Errorf("atoi(%q) = (%v, %v) want (%v, %v)", 338 test.in, out, ok, test.out, test.ok) 339 } 340 } 341 case 64: 342 for i := range atoi64tests { 343 test := &atoi64tests[i] 344 out, ok := runtime.Atoi(test.in) 345 if test.out != int64(out) || test.ok != ok { 346 t.Errorf("atoi(%q) = (%v, %v) want (%v, %v)", 347 test.in, out, ok, test.out, test.ok) 348 } 349 } 350 } 351 } 352 353 type atoi32Test struct { 354 in string 355 out int32 356 ok bool 357 } 358 359 var atoi32tests = []atoi32Test{ 360 {"", 0, false}, 361 {"0", 0, true}, 362 {"-0", 0, true}, 363 {"1", 1, true}, 364 {"-1", -1, true}, 365 {"12345", 12345, true}, 366 {"-12345", -12345, true}, 367 {"012345", 12345, true}, 368 {"-012345", -12345, true}, 369 {"12345x", 0, false}, 370 {"-12345x", 0, false}, 371 {"987654321", 987654321, true}, 372 {"-987654321", -987654321, true}, 373 {"2147483647", 1<<31 - 1, true}, 374 {"-2147483647", -(1<<31 - 1), true}, 375 {"2147483648", 0, false}, 376 {"-2147483648", -1 << 31, true}, 377 {"2147483649", 0, false}, 378 {"-2147483649", 0, false}, 379 } 380 381 func TestAtoi32(t *testing.T) { 382 for i := range atoi32tests { 383 test := &atoi32tests[i] 384 out, ok := runtime.Atoi32(test.in) 385 if test.out != out || test.ok != ok { 386 t.Errorf("atoi32(%q) = (%v, %v) want (%v, %v)", 387 test.in, out, ok, test.out, test.ok) 388 } 389 } 390 }