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 5package runtime_test 6 7import ( 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. 17const sizeNoStack = 100 18 19func 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 29func 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 39func 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 49func 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 59func 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 73func 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 87func BenchmarkConcatStringAndBytes(b *testing.B) { 88 s1 := []byte("Gophers!") 89 for i := 0; i < b.N; i++ { 90 _ = "Hello " + string(s1) 91 } 92} 93 94var escapeString string 95 96func 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 108var stringdata = []struct{ name, data string }{ 109 {"ASCII", "01234567890"}, 110 {"Japanese", "日本語日本語日本語"}, 111 {"MixedLength", "$Ѐࠀက퀀\U00040000\U0010FFFF"}, 112} 113 114var sinkInt int 115 116func 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 151func 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 184func 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/* 196func TestStringW(t *testing.T) { 197 strings := []string{ 198 "hello", 199 "a\u5566\u7788b", 200 } 201 202 for _, s := range strings { 203 var b []uint16 204 for _, c := range s { 205 b = append(b, uint16(c)) 206 if c != rune(uint16(c)) { 207 t.Errorf("bad test: stringW can't handle >16 bit runes") 208 } 209 } 210 b = append(b, 0) 211 r := runtime.GostringW(b) 212 if r != s { 213 t.Errorf("gostringW(%v) = %s, want %s", b, r, s) 214 } 215 } 216} 217*/ 218 219func TestLargeStringConcat(t *testing.T) { 220 output := runTestProg(t, "testprog", "stringconcat") 221 want := "panic: " + strings.Repeat("0", 1<<10) + strings.Repeat("1", 1<<10) + 222 strings.Repeat("2", 1<<10) + strings.Repeat("3", 1<<10) 223 if !strings.HasPrefix(output, want) { 224 t.Fatalf("output does not start with %q:\n%s", want, output) 225 } 226} 227 228func TestCompareTempString(t *testing.T) { 229 s := strings.Repeat("x", sizeNoStack) 230 b := []byte(s) 231 n := testing.AllocsPerRun(1000, func() { 232 if string(b) != s { 233 t.Fatalf("strings are not equal: '%v' and '%v'", string(b), s) 234 } 235 if string(b) == s { 236 } else { 237 t.Fatalf("strings are not equal: '%v' and '%v'", string(b), s) 238 } 239 }) 240 // was n != 0, changed for gccgo. 241 if n > 2 { 242 t.Fatalf("want 0 allocs, got %v", n) 243 } 244} 245 246func TestStringIndexHaystack(t *testing.T) { 247 // See issue 25864. 248 haystack := []byte("hello") 249 needle := "ll" 250 n := testing.AllocsPerRun(1000, func() { 251 if strings.Index(string(haystack), needle) != 2 { 252 t.Fatalf("needle not found") 253 } 254 }) 255 // was n != 0, changed for gccgo. 256 if n > 1 { 257 t.Fatalf("want 0 allocs, got %v", n) 258 } 259} 260 261func TestStringIndexNeedle(t *testing.T) { 262 // See issue 25864. 263 haystack := "hello" 264 needle := []byte("ll") 265 n := testing.AllocsPerRun(1000, func() { 266 if strings.Index(haystack, string(needle)) != 2 { 267 t.Fatalf("needle not found") 268 } 269 }) 270 // was n != 0, changed for gccgo 271 if n > 1 { 272 t.Fatalf("want 0 allocs, got %v", n) 273 } 274} 275 276func TestStringOnStack(t *testing.T) { 277 s := "" 278 for i := 0; i < 3; i++ { 279 s = "a" + s + "b" + s + "c" 280 } 281 282 if want := "aaabcbabccbaabcbabccc"; s != want { 283 t.Fatalf("want: '%v', got '%v'", want, s) 284 } 285} 286 287func TestIntString(t *testing.T) { 288 // Non-escaping result of intstring. 289 s := "" 290 for i := 0; i < 4; i++ { 291 s += string(i+'0') + string(i+'0'+1) 292 } 293 if want := "01122334"; s != want { 294 t.Fatalf("want '%v', got '%v'", want, s) 295 } 296 297 // Escaping result of intstring. 298 var a [4]string 299 for i := 0; i < 4; i++ { 300 a[i] = string(i + '0') 301 } 302 s = a[0] + a[1] + a[2] + a[3] 303 if want := "0123"; s != want { 304 t.Fatalf("want '%v', got '%v'", want, s) 305 } 306} 307 308func TestIntStringAllocs(t *testing.T) { 309 unknown := '0' 310 n := testing.AllocsPerRun(1000, func() { 311 s1 := string(unknown) 312 s2 := string(unknown + 1) 313 if s1 == s2 { 314 t.Fatalf("bad") 315 } 316 }) 317 // was n != 0, changed for gccgo, which currently does one 318 // allocation for each call to string(unknown). 319 if n > 2 { 320 t.Fatalf("want 0 allocs, got %v", n) 321 } 322} 323 324func TestRangeStringCast(t *testing.T) { 325 s := strings.Repeat("x", sizeNoStack) 326 n := testing.AllocsPerRun(1000, func() { 327 for i, c := range []byte(s) { 328 if c != s[i] { 329 t.Fatalf("want '%c' at pos %v, got '%c'", s[i], i, c) 330 } 331 } 332 }) 333 // was n != 0, changed for gccgo. 334 if n > 1 { 335 t.Fatalf("want 0 allocs, got %v", n) 336 } 337} 338 339func isZeroed(b []byte) bool { 340 for _, x := range b { 341 if x != 0 { 342 return false 343 } 344 } 345 return true 346} 347 348func isZeroedR(r []rune) bool { 349 for _, x := range r { 350 if x != 0 { 351 return false 352 } 353 } 354 return true 355} 356 357func TestString2Slice(t *testing.T) { 358 // Make sure we don't return slices that expose 359 // an unzeroed section of stack-allocated temp buf 360 // between len and cap. See issue 14232. 361 s := "foož" 362 b := ([]byte)(s) 363 if !isZeroed(b[len(b):cap(b)]) { 364 t.Errorf("extra bytes not zeroed") 365 } 366 r := ([]rune)(s) 367 if !isZeroedR(r[len(r):cap(r)]) { 368 t.Errorf("extra runes not zeroed") 369 } 370} 371 372const intSize = 32 << (^uint(0) >> 63) 373 374type atoi64Test struct { 375 in string 376 out int64 377 ok bool 378} 379 380var atoi64tests = []atoi64Test{ 381 {"", 0, false}, 382 {"0", 0, true}, 383 {"-0", 0, true}, 384 {"1", 1, true}, 385 {"-1", -1, true}, 386 {"12345", 12345, true}, 387 {"-12345", -12345, true}, 388 {"012345", 12345, true}, 389 {"-012345", -12345, true}, 390 {"12345x", 0, false}, 391 {"-12345x", 0, false}, 392 {"98765432100", 98765432100, true}, 393 {"-98765432100", -98765432100, true}, 394 {"20496382327982653440", 0, false}, 395 {"-20496382327982653440", 0, false}, 396 {"9223372036854775807", 1<<63 - 1, true}, 397 {"-9223372036854775807", -(1<<63 - 1), true}, 398 {"9223372036854775808", 0, false}, 399 {"-9223372036854775808", -1 << 63, true}, 400 {"9223372036854775809", 0, false}, 401 {"-9223372036854775809", 0, false}, 402} 403 404func TestAtoi(t *testing.T) { 405 switch intSize { 406 case 32: 407 for i := range atoi32tests { 408 test := &atoi32tests[i] 409 out, ok := runtime.Atoi(test.in) 410 if test.out != int32(out) || test.ok != ok { 411 t.Errorf("atoi(%q) = (%v, %v) want (%v, %v)", 412 test.in, out, ok, test.out, test.ok) 413 } 414 } 415 case 64: 416 for i := range atoi64tests { 417 test := &atoi64tests[i] 418 out, ok := runtime.Atoi(test.in) 419 if test.out != int64(out) || test.ok != ok { 420 t.Errorf("atoi(%q) = (%v, %v) want (%v, %v)", 421 test.in, out, ok, test.out, test.ok) 422 } 423 } 424 } 425} 426 427type atoi32Test struct { 428 in string 429 out int32 430 ok bool 431} 432 433var atoi32tests = []atoi32Test{ 434 {"", 0, false}, 435 {"0", 0, true}, 436 {"-0", 0, true}, 437 {"1", 1, true}, 438 {"-1", -1, true}, 439 {"12345", 12345, true}, 440 {"-12345", -12345, true}, 441 {"012345", 12345, true}, 442 {"-012345", -12345, true}, 443 {"12345x", 0, false}, 444 {"-12345x", 0, false}, 445 {"987654321", 987654321, true}, 446 {"-987654321", -987654321, true}, 447 {"2147483647", 1<<31 - 1, true}, 448 {"-2147483647", -(1<<31 - 1), true}, 449 {"2147483648", 0, false}, 450 {"-2147483648", -1 << 31, true}, 451 {"2147483649", 0, false}, 452 {"-2147483649", 0, false}, 453} 454 455func TestAtoi32(t *testing.T) { 456 for i := range atoi32tests { 457 test := &atoi32tests[i] 458 out, ok := runtime.Atoi32(test.in) 459 if test.out != out || test.ok != ok { 460 t.Errorf("atoi32(%q) = (%v, %v) want (%v, %v)", 461 test.in, out, ok, test.out, test.ok) 462 } 463 } 464} 465