Source file
src/runtime/string_test.go
Documentation: runtime
1
2
3
4
5 package runtime_test
6
7 import (
8 "runtime"
9 "strconv"
10 "strings"
11 "testing"
12 "unicode/utf8"
13 )
14
15
16
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
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 TestConcatTempString(t *testing.T) {
227 s := "bytes"
228 b := []byte(s)
229 n := testing.AllocsPerRun(1000, func() {
230 if "prefix "+string(b)+" suffix" != "prefix bytes suffix" {
231 t.Fatalf("strings are not equal: '%v' and '%v'", "prefix "+string(b)+" suffix", "prefix bytes suffix")
232 }
233 })
234 if n != 0 {
235 t.Fatalf("want 0 allocs, got %v", n)
236 }
237 }
238
239 func TestCompareTempString(t *testing.T) {
240 s := strings.Repeat("x", sizeNoStack)
241 b := []byte(s)
242 n := testing.AllocsPerRun(1000, func() {
243 if string(b) != s {
244 t.Fatalf("strings are not equal: '%v' and '%v'", string(b), s)
245 }
246 if string(b) < s {
247 t.Fatalf("strings are not equal: '%v' and '%v'", string(b), s)
248 }
249 if string(b) > s {
250 t.Fatalf("strings are not equal: '%v' and '%v'", string(b), s)
251 }
252 if string(b) == s {
253 } else {
254 t.Fatalf("strings are not equal: '%v' and '%v'", string(b), s)
255 }
256 if string(b) <= s {
257 } else {
258 t.Fatalf("strings are not equal: '%v' and '%v'", string(b), s)
259 }
260 if string(b) >= s {
261 } else {
262 t.Fatalf("strings are not equal: '%v' and '%v'", string(b), s)
263 }
264 })
265 if n != 0 {
266 t.Fatalf("want 0 allocs, got %v", n)
267 }
268 }
269
270 func TestStringIndexHaystack(t *testing.T) {
271
272 haystack := []byte("hello")
273 needle := "ll"
274 n := testing.AllocsPerRun(1000, func() {
275 if strings.Index(string(haystack), needle) != 2 {
276 t.Fatalf("needle not found")
277 }
278 })
279 if n != 0 {
280 t.Fatalf("want 0 allocs, got %v", n)
281 }
282 }
283
284 func TestStringIndexNeedle(t *testing.T) {
285
286 haystack := "hello"
287 needle := []byte("ll")
288 n := testing.AllocsPerRun(1000, func() {
289 if strings.Index(haystack, string(needle)) != 2 {
290 t.Fatalf("needle not found")
291 }
292 })
293 if n != 0 {
294 t.Fatalf("want 0 allocs, got %v", n)
295 }
296 }
297
298 func TestStringOnStack(t *testing.T) {
299 s := ""
300 for i := 0; i < 3; i++ {
301 s = "a" + s + "b" + s + "c"
302 }
303
304 if want := "aaabcbabccbaabcbabccc"; s != want {
305 t.Fatalf("want: '%v', got '%v'", want, s)
306 }
307 }
308
309 func TestIntString(t *testing.T) {
310
311 s := ""
312 for i := rune(0); i < 4; i++ {
313 s += string(i+'0') + string(i+'0'+1)
314 }
315 if want := "01122334"; s != want {
316 t.Fatalf("want '%v', got '%v'", want, s)
317 }
318
319
320 var a [4]string
321 for i := rune(0); i < 4; i++ {
322 a[i] = string(i + '0')
323 }
324 s = a[0] + a[1] + a[2] + a[3]
325 if want := "0123"; s != want {
326 t.Fatalf("want '%v', got '%v'", want, s)
327 }
328 }
329
330 func TestIntStringAllocs(t *testing.T) {
331 unknown := '0'
332 n := testing.AllocsPerRun(1000, func() {
333 s1 := string(unknown)
334 s2 := string(unknown + 1)
335 if s1 == s2 {
336 t.Fatalf("bad")
337 }
338 })
339 if n != 0 {
340 t.Fatalf("want 0 allocs, got %v", n)
341 }
342 }
343
344 func TestRangeStringCast(t *testing.T) {
345 s := strings.Repeat("x", sizeNoStack)
346 n := testing.AllocsPerRun(1000, func() {
347 for i, c := range []byte(s) {
348 if c != s[i] {
349 t.Fatalf("want '%c' at pos %v, got '%c'", s[i], i, c)
350 }
351 }
352 })
353 if n != 0 {
354 t.Fatalf("want 0 allocs, got %v", n)
355 }
356 }
357
358 func isZeroed(b []byte) bool {
359 for _, x := range b {
360 if x != 0 {
361 return false
362 }
363 }
364 return true
365 }
366
367 func isZeroedR(r []rune) bool {
368 for _, x := range r {
369 if x != 0 {
370 return false
371 }
372 }
373 return true
374 }
375
376 func TestString2Slice(t *testing.T) {
377
378
379
380 s := "foož"
381 b := ([]byte)(s)
382 if !isZeroed(b[len(b):cap(b)]) {
383 t.Errorf("extra bytes not zeroed")
384 }
385 r := ([]rune)(s)
386 if !isZeroedR(r[len(r):cap(r)]) {
387 t.Errorf("extra runes not zeroed")
388 }
389 }
390
391 const intSize = 32 << (^uint(0) >> 63)
392
393 type atoi64Test struct {
394 in string
395 out int64
396 ok bool
397 }
398
399 var atoi64tests = []atoi64Test{
400 {"", 0, false},
401 {"0", 0, true},
402 {"-0", 0, true},
403 {"1", 1, true},
404 {"-1", -1, true},
405 {"12345", 12345, true},
406 {"-12345", -12345, true},
407 {"012345", 12345, true},
408 {"-012345", -12345, true},
409 {"12345x", 0, false},
410 {"-12345x", 0, false},
411 {"98765432100", 98765432100, true},
412 {"-98765432100", -98765432100, true},
413 {"20496382327982653440", 0, false},
414 {"-20496382327982653440", 0, false},
415 {"9223372036854775807", 1<<63 - 1, true},
416 {"-9223372036854775807", -(1<<63 - 1), true},
417 {"9223372036854775808", 0, false},
418 {"-9223372036854775808", -1 << 63, true},
419 {"9223372036854775809", 0, false},
420 {"-9223372036854775809", 0, false},
421 }
422
423 func TestAtoi(t *testing.T) {
424 switch intSize {
425 case 32:
426 for i := range atoi32tests {
427 test := &atoi32tests[i]
428 out, ok := runtime.Atoi(test.in)
429 if test.out != int32(out) || test.ok != ok {
430 t.Errorf("atoi(%q) = (%v, %v) want (%v, %v)",
431 test.in, out, ok, test.out, test.ok)
432 }
433 }
434 case 64:
435 for i := range atoi64tests {
436 test := &atoi64tests[i]
437 out, ok := runtime.Atoi(test.in)
438 if test.out != int64(out) || test.ok != ok {
439 t.Errorf("atoi(%q) = (%v, %v) want (%v, %v)",
440 test.in, out, ok, test.out, test.ok)
441 }
442 }
443 }
444 }
445
446 type atoi32Test struct {
447 in string
448 out int32
449 ok bool
450 }
451
452 var atoi32tests = []atoi32Test{
453 {"", 0, false},
454 {"0", 0, true},
455 {"-0", 0, true},
456 {"1", 1, true},
457 {"-1", -1, true},
458 {"12345", 12345, true},
459 {"-12345", -12345, true},
460 {"012345", 12345, true},
461 {"-012345", -12345, true},
462 {"12345x", 0, false},
463 {"-12345x", 0, false},
464 {"987654321", 987654321, true},
465 {"-987654321", -987654321, true},
466 {"2147483647", 1<<31 - 1, true},
467 {"-2147483647", -(1<<31 - 1), true},
468 {"2147483648", 0, false},
469 {"-2147483648", -1 << 31, true},
470 {"2147483649", 0, false},
471 {"-2147483649", 0, false},
472 }
473
474 func TestAtoi32(t *testing.T) {
475 for i := range atoi32tests {
476 test := &atoi32tests[i]
477 out, ok := runtime.Atoi32(test.in)
478 if test.out != out || test.ok != ok {
479 t.Errorf("atoi32(%q) = (%v, %v) want (%v, %v)",
480 test.in, out, ok, test.out, test.ok)
481 }
482 }
483 }
484
485 func TestParseByteCount(t *testing.T) {
486 for _, test := range []struct {
487 in string
488 out int64
489 ok bool
490 }{
491
492 {"1", 1, true},
493 {"12345", 12345, true},
494 {"012345", 12345, true},
495 {"98765432100", 98765432100, true},
496 {"9223372036854775807", 1<<63 - 1, true},
497
498
499 {"1B", 1, true},
500 {"12345B", 12345, true},
501 {"012345B", 12345, true},
502 {"98765432100B", 98765432100, true},
503 {"9223372036854775807B", 1<<63 - 1, true},
504
505
506 {"1KiB", 1 << 10, true},
507 {"05KiB", 5 << 10, true},
508 {"1MiB", 1 << 20, true},
509 {"10MiB", 10 << 20, true},
510 {"1GiB", 1 << 30, true},
511 {"100GiB", 100 << 30, true},
512 {"1TiB", 1 << 40, true},
513 {"99TiB", 99 << 40, true},
514
515
516
517
518 {"-0", 0, true},
519 {"0", 0, true},
520 {"0B", 0, true},
521 {"0KiB", 0, true},
522 {"0MiB", 0, true},
523 {"0GiB", 0, true},
524 {"0TiB", 0, true},
525
526
527 {"", 0, false},
528 {"-1", 0, false},
529 {"a12345", 0, false},
530 {"a12345B", 0, false},
531 {"12345x", 0, false},
532 {"0x12345", 0, false},
533
534
535 {"9223372036854775808", 0, false},
536 {"9223372036854775809", 0, false},
537 {"18446744073709551615", 0, false},
538 {"20496382327982653440", 0, false},
539 {"18446744073709551616", 0, false},
540 {"18446744073709551617", 0, false},
541 {"9999999999999999999999", 0, false},
542
543
544 {"9223372036854775808B", 0, false},
545 {"9223372036854775809B", 0, false},
546 {"18446744073709551615B", 0, false},
547 {"20496382327982653440B", 0, false},
548 {"18446744073709551616B", 0, false},
549 {"18446744073709551617B", 0, false},
550 {"9999999999999999999999B", 0, false},
551
552
553 {"1Ki", 0, false},
554 {"05Ki", 0, false},
555 {"10Mi", 0, false},
556 {"100Gi", 0, false},
557 {"99Ti", 0, false},
558 {"22iB", 0, false},
559 {"B", 0, false},
560 {"iB", 0, false},
561 {"KiB", 0, false},
562 {"MiB", 0, false},
563 {"GiB", 0, false},
564 {"TiB", 0, false},
565 {"-120KiB", 0, false},
566 {"-891MiB", 0, false},
567 {"-704GiB", 0, false},
568 {"-42TiB", 0, false},
569 {"99999999999999999999KiB", 0, false},
570 {"99999999999999999MiB", 0, false},
571 {"99999999999999GiB", 0, false},
572 {"99999999999TiB", 0, false},
573 {"555EiB", 0, false},
574
575
576 {"0KB", 0, false},
577 {"0MB", 0, false},
578 {"0GB", 0, false},
579 {"0TB", 0, false},
580 {"1KB", 0, false},
581 {"05KB", 0, false},
582 {"1MB", 0, false},
583 {"10MB", 0, false},
584 {"1GB", 0, false},
585 {"100GB", 0, false},
586 {"1TB", 0, false},
587 {"99TB", 0, false},
588 {"1K", 0, false},
589 {"05K", 0, false},
590 {"10M", 0, false},
591 {"100G", 0, false},
592 {"99T", 0, false},
593 {"99999999999999999999KB", 0, false},
594 {"99999999999999999MB", 0, false},
595 {"99999999999999GB", 0, false},
596 {"99999999999TB", 0, false},
597 {"99999999999TiB", 0, false},
598 {"555EB", 0, false},
599 } {
600 out, ok := runtime.ParseByteCount(test.in)
601 if test.out != out || test.ok != ok {
602 t.Errorf("parseByteCount(%q) = (%v, %v) want (%v, %v)",
603 test.in, out, ok, test.out, test.ok)
604 }
605 }
606 }
607
View as plain text