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 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
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
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
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
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
351
352
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 }
457
View as plain text