Source file
src/runtime/string_test.go
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 func TestParseByteCount(t *testing.T) {
394 for _, test := range []struct {
395 in string
396 out int64
397 ok bool
398 }{
399
400 {"1", 1, true},
401 {"12345", 12345, true},
402 {"012345", 12345, true},
403 {"98765432100", 98765432100, true},
404 {"9223372036854775807", 1<<63 - 1, true},
405
406
407 {"1B", 1, true},
408 {"12345B", 12345, true},
409 {"012345B", 12345, true},
410 {"98765432100B", 98765432100, true},
411 {"9223372036854775807B", 1<<63 - 1, true},
412
413
414 {"1KiB", 1 << 10, true},
415 {"05KiB", 5 << 10, true},
416 {"1MiB", 1 << 20, true},
417 {"10MiB", 10 << 20, true},
418 {"1GiB", 1 << 30, true},
419 {"100GiB", 100 << 30, true},
420 {"1TiB", 1 << 40, true},
421 {"99TiB", 99 << 40, true},
422
423
424
425
426 {"-0", 0, true},
427 {"0", 0, true},
428 {"0B", 0, true},
429 {"0KiB", 0, true},
430 {"0MiB", 0, true},
431 {"0GiB", 0, true},
432 {"0TiB", 0, true},
433
434
435 {"", 0, false},
436 {"-1", 0, false},
437 {"a12345", 0, false},
438 {"a12345B", 0, false},
439 {"12345x", 0, false},
440 {"0x12345", 0, false},
441
442
443 {"9223372036854775808", 0, false},
444 {"9223372036854775809", 0, false},
445 {"18446744073709551615", 0, false},
446 {"20496382327982653440", 0, false},
447 {"18446744073709551616", 0, false},
448 {"18446744073709551617", 0, false},
449 {"9999999999999999999999", 0, false},
450
451
452 {"9223372036854775808B", 0, false},
453 {"9223372036854775809B", 0, false},
454 {"18446744073709551615B", 0, false},
455 {"20496382327982653440B", 0, false},
456 {"18446744073709551616B", 0, false},
457 {"18446744073709551617B", 0, false},
458 {"9999999999999999999999B", 0, false},
459
460
461 {"1Ki", 0, false},
462 {"05Ki", 0, false},
463 {"10Mi", 0, false},
464 {"100Gi", 0, false},
465 {"99Ti", 0, false},
466 {"22iB", 0, false},
467 {"B", 0, false},
468 {"iB", 0, false},
469 {"KiB", 0, false},
470 {"MiB", 0, false},
471 {"GiB", 0, false},
472 {"TiB", 0, false},
473 {"-120KiB", 0, false},
474 {"-891MiB", 0, false},
475 {"-704GiB", 0, false},
476 {"-42TiB", 0, false},
477 {"99999999999999999999KiB", 0, false},
478 {"99999999999999999MiB", 0, false},
479 {"99999999999999GiB", 0, false},
480 {"99999999999TiB", 0, false},
481 {"555EiB", 0, false},
482
483
484 {"0KB", 0, false},
485 {"0MB", 0, false},
486 {"0GB", 0, false},
487 {"0TB", 0, false},
488 {"1KB", 0, false},
489 {"05KB", 0, false},
490 {"1MB", 0, false},
491 {"10MB", 0, false},
492 {"1GB", 0, false},
493 {"100GB", 0, false},
494 {"1TB", 0, false},
495 {"99TB", 0, false},
496 {"1K", 0, false},
497 {"05K", 0, false},
498 {"10M", 0, false},
499 {"100G", 0, false},
500 {"99T", 0, false},
501 {"99999999999999999999KB", 0, false},
502 {"99999999999999999MB", 0, false},
503 {"99999999999999GB", 0, false},
504 {"99999999999TB", 0, false},
505 {"99999999999TiB", 0, false},
506 {"555EB", 0, false},
507 } {
508 out, ok := runtime.ParseByteCount(test.in)
509 if test.out != out || test.ok != ok {
510 t.Errorf("parseByteCount(%q) = (%v, %v) want (%v, %v)",
511 test.in, out, ok, test.out, test.ok)
512 }
513 }
514 }
515
View as plain text