Mikio Hara | 4aeb0fc | 2013-03-20 02:40:29 +0900 | [diff] [blame] | 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 | |
Rémy Oudompheng | 77f3e18 | 2012-08-05 21:35:41 +0200 | [diff] [blame] | 5 | package runtime_test |
| 6 | |
| 7 | import ( |
Keith Randall | 0f4b53c | 2014-06-17 00:36:23 -0700 | [diff] [blame] | 8 | "runtime" |
Josh Bleecher Snyder | d445136 | 2017-03-04 16:28:59 -0800 | [diff] [blame] | 9 | "strconv" |
Dmitriy Vyukov | 97c8b24 | 2014-07-08 22:37:18 +0400 | [diff] [blame] | 10 | "strings" |
Rémy Oudompheng | 77f3e18 | 2012-08-05 21:35:41 +0200 | [diff] [blame] | 11 | "testing" |
| 12 | ) |
| 13 | |
Matthew Dempsky | 995fb03 | 2016-03-25 21:11:33 -0700 | [diff] [blame] | 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 | |
Rémy Oudompheng | 77f3e18 | 2012-08-05 21:35:41 +0200 | [diff] [blame] | 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 | } |
Keith Randall | 3d5daa2 | 2013-04-02 16:26:15 -0700 | [diff] [blame] | 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 | } |
Keith Randall | 61dca94 | 2014-06-16 23:03:03 -0700 | [diff] [blame] | 85 | |
Martin Möhrmann | 150de94 | 2016-09-10 22:44:00 +0200 | [diff] [blame] | 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 | |
Josh Bleecher Snyder | d445136 | 2017-03-04 16:28:59 -0800 | [diff] [blame] | 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 | |
Martin Möhrmann | 0dae9dfb | 2016-08-26 15:00:46 +0200 | [diff] [blame] | 107 | var stringdata = []struct{ name, data string }{ |
| 108 | {"ASCII", "01234567890"}, |
| 109 | {"Japanese", "日本語日本語日本語"}, |
Martin Möhrmann | d295174 | 2016-09-02 17:04:41 +0200 | [diff] [blame] | 110 | {"MixedLength", "$Ѐࠀက퀀𐀀\U00040000\U0010FFFF"}, |
Keith Randall | 61dca94 | 2014-06-16 23:03:03 -0700 | [diff] [blame] | 111 | } |
| 112 | |
Martin Möhrmann | 0dae9dfb | 2016-08-26 15:00:46 +0200 | [diff] [blame] | 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 | }) |
Keith Randall | 61dca94 | 2014-06-16 23:03:03 -0700 | [diff] [blame] | 122 | } |
Martin Möhrmann | 0dae9dfb | 2016-08-26 15:00:46 +0200 | [diff] [blame] | 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 | }) |
Keith Randall | 61dca94 | 2014-06-16 23:03:03 -0700 | [diff] [blame] | 144 | } |
Keith Randall | 0f4b53c | 2014-06-17 00:36:23 -0700 | [diff] [blame] | 145 | |
Keith Randall | bd70bd9 | 2016-02-22 13:20:38 -0800 | [diff] [blame] | 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 | |
Keith Randall | 0f4b53c | 2014-06-17 00:36:23 -0700 | [diff] [blame] | 157 | func TestStringW(t *testing.T) { |
| 158 | strings := []string{ |
| 159 | "hello", |
Keith Randall | 2b309c6 | 2014-06-17 09:17:33 -0700 | [diff] [blame] | 160 | "a\u5566\u7788b", |
Keith Randall | 0f4b53c | 2014-06-17 00:36:23 -0700 | [diff] [blame] | 161 | } |
| 162 | |
| 163 | for _, s := range strings { |
Keith Randall | 2b309c6 | 2014-06-17 09:17:33 -0700 | [diff] [blame] | 164 | var b []uint16 |
Keith Randall | 0f4b53c | 2014-06-17 00:36:23 -0700 | [diff] [blame] | 165 | for _, c := range s { |
Keith Randall | 2b309c6 | 2014-06-17 09:17:33 -0700 | [diff] [blame] | 166 | b = append(b, uint16(c)) |
| 167 | if c != rune(uint16(c)) { |
Keith Randall | 0f4b53c | 2014-06-17 00:36:23 -0700 | [diff] [blame] | 168 | t.Errorf("bad test: stringW can't handle >16 bit runes") |
| 169 | } |
| 170 | } |
Keith Randall | 2b309c6 | 2014-06-17 09:17:33 -0700 | [diff] [blame] | 171 | b = append(b, 0) |
Keith Randall | 0f4b53c | 2014-06-17 00:36:23 -0700 | [diff] [blame] | 172 | r := runtime.GostringW(b) |
| 173 | if r != s { |
| 174 | t.Errorf("gostringW(%v) = %s, want %s", b, r, s) |
| 175 | } |
| 176 | } |
| 177 | } |
Dmitriy Vyukov | 97c8b24 | 2014-07-08 22:37:18 +0400 | [diff] [blame] | 178 | |
| 179 | func TestLargeStringConcat(t *testing.T) { |
Russ Cox | 8d5ff2e | 2015-12-21 10:29:21 -0500 | [diff] [blame] | 180 | output := runTestProg(t, "testprog", "stringconcat") |
Dmitriy Vyukov | 97c8b24 | 2014-07-08 22:37:18 +0400 | [diff] [blame] | 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 | |
Dmitry Vyukov | 69cd91a | 2015-01-27 23:57:12 +0300 | [diff] [blame] | 188 | func TestCompareTempString(t *testing.T) { |
Matthew Dempsky | 995fb03 | 2016-03-25 21:11:33 -0700 | [diff] [blame] | 189 | s := strings.Repeat("x", sizeNoStack) |
Dmitry Vyukov | 69cd91a | 2015-01-27 23:57:12 +0300 | [diff] [blame] | 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 | } |
Dmitry Vyukov | e6fac08 | 2015-01-21 17:37:59 +0300 | [diff] [blame] | 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 | } |
Dmitry Vyukov | 4ce4d8b | 2015-01-28 08:42:20 +0300 | [diff] [blame] | 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 | } |
Dmitry Vyukov | 71be013 | 2015-02-03 20:50:58 +0300 | [diff] [blame] | 250 | |
| 251 | func TestRangeStringCast(t *testing.T) { |
Matthew Dempsky | 995fb03 | 2016-03-25 21:11:33 -0700 | [diff] [blame] | 252 | s := strings.Repeat("x", sizeNoStack) |
Dmitry Vyukov | 71be013 | 2015-02-03 20:50:58 +0300 | [diff] [blame] | 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 | } |
Keith Randall | 4d02b12 | 2016-02-04 13:38:38 -0800 | [diff] [blame] | 264 | |
Keith Randall | 6f3f02f | 2016-04-24 17:04:32 -0700 | [diff] [blame] | 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 | |
Keith Randall | 4d02b12 | 2016-02-04 13:38:38 -0800 | [diff] [blame] | 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 |
Brad Fitzpatrick | 5fea2cc | 2016-03-01 23:21:55 +0000 | [diff] [blame] | 286 | // between len and cap. See issue 14232. |
Keith Randall | 4d02b12 | 2016-02-04 13:38:38 -0800 | [diff] [blame] | 287 | s := "foož" |
| 288 | b := ([]byte)(s) |
Keith Randall | 6f3f02f | 2016-04-24 17:04:32 -0700 | [diff] [blame] | 289 | if !isZeroed(b[len(b):cap(b)]) { |
| 290 | t.Errorf("extra bytes not zeroed") |
Keith Randall | 4d02b12 | 2016-02-04 13:38:38 -0800 | [diff] [blame] | 291 | } |
| 292 | r := ([]rune)(s) |
Keith Randall | 6f3f02f | 2016-04-24 17:04:32 -0700 | [diff] [blame] | 293 | if !isZeroedR(r[len(r):cap(r)]) { |
| 294 | t.Errorf("extra runes not zeroed") |
Keith Randall | 4d02b12 | 2016-02-04 13:38:38 -0800 | [diff] [blame] | 295 | } |
| 296 | } |
Martin Möhrmann | d7b34d5 | 2016-10-30 01:54:19 +0200 | [diff] [blame] | 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 | } |