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