blob: 7633cfde0468a237df3ed35983554704cf97ec1b [file] [log] [blame]
Mikio Hara4aeb0fc2013-03-20 02:40:29 +09001// 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 Oudompheng77f3e182012-08-05 21:35:41 +02005package runtime_test
6
7import (
Keith Randall0f4b53c2014-06-17 00:36:23 -07008 "runtime"
Josh Bleecher Snyderd4451362017-03-04 16:28:59 -08009 "strconv"
Dmitriy Vyukov97c8b242014-07-08 22:37:18 +040010 "strings"
Rémy Oudompheng77f3e182012-08-05 21:35:41 +020011 "testing"
12)
13
Matthew Dempsky995fb032016-03-25 21:11:33 -070014// Strings and slices that don't escape and fit into tmpBuf are stack allocated,
15// which defeats using AllocsPerRun to test other optimizations.
16const sizeNoStack = 100
17
Rémy Oudompheng77f3e182012-08-05 21:35:41 +020018func 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
28func 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
38func 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
48func 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 Randall3d5daa22013-04-02 16:26:15 -070057
58func 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
72func 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 Randall61dca942014-06-16 23:03:03 -070085
Martin Möhrmann150de942016-09-10 22:44:00 +020086func 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 Snyderd4451362017-03-04 16:28:59 -080093var escapeString string
94
95func 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öhrmann0dae9dfb2016-08-26 15:00:46 +0200107var stringdata = []struct{ name, data string }{
108 {"ASCII", "01234567890"},
109 {"Japanese", "日本語日本語日本語"},
Martin Möhrmannd2951742016-09-02 17:04:41 +0200110 {"MixedLength", "$Ѐࠀက퀀𐀀\U00040000\U0010FFFF"},
Keith Randall61dca942014-06-16 23:03:03 -0700111}
112
Martin Möhrmann0dae9dfb2016-08-26 15:00:46 +0200113func 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 Randall61dca942014-06-16 23:03:03 -0700122 }
Martin Möhrmann0dae9dfb2016-08-26 15:00:46 +0200123 })
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 Randall61dca942014-06-16 23:03:03 -0700144}
Keith Randall0f4b53c2014-06-17 00:36:23 -0700145
Keith Randallbd70bd92016-02-22 13:20:38 -0800146func 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 Randall0f4b53c2014-06-17 00:36:23 -0700157func TestStringW(t *testing.T) {
158 strings := []string{
159 "hello",
Keith Randall2b309c62014-06-17 09:17:33 -0700160 "a\u5566\u7788b",
Keith Randall0f4b53c2014-06-17 00:36:23 -0700161 }
162
163 for _, s := range strings {
Keith Randall2b309c62014-06-17 09:17:33 -0700164 var b []uint16
Keith Randall0f4b53c2014-06-17 00:36:23 -0700165 for _, c := range s {
Keith Randall2b309c62014-06-17 09:17:33 -0700166 b = append(b, uint16(c))
167 if c != rune(uint16(c)) {
Keith Randall0f4b53c2014-06-17 00:36:23 -0700168 t.Errorf("bad test: stringW can't handle >16 bit runes")
169 }
170 }
Keith Randall2b309c62014-06-17 09:17:33 -0700171 b = append(b, 0)
Keith Randall0f4b53c2014-06-17 00:36:23 -0700172 r := runtime.GostringW(b)
173 if r != s {
174 t.Errorf("gostringW(%v) = %s, want %s", b, r, s)
175 }
176 }
177}
Dmitriy Vyukov97c8b242014-07-08 22:37:18 +0400178
179func TestLargeStringConcat(t *testing.T) {
Russ Cox8d5ff2e2015-12-21 10:29:21 -0500180 output := runTestProg(t, "testprog", "stringconcat")
Dmitriy Vyukov97c8b242014-07-08 22:37:18 +0400181 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 Vyukov69cd91a2015-01-27 23:57:12 +0300188func TestCompareTempString(t *testing.T) {
Matthew Dempsky995fb032016-03-25 21:11:33 -0700189 s := strings.Repeat("x", sizeNoStack)
Dmitry Vyukov69cd91a2015-01-27 23:57:12 +0300190 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 Vyukove6fac082015-01-21 17:37:59 +0300204
205func 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 Vyukov4ce4d8b2015-01-28 08:42:20 +0300215
216func 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
237func 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 Vyukov71be0132015-02-03 20:50:58 +0300250
251func TestRangeStringCast(t *testing.T) {
Matthew Dempsky995fb032016-03-25 21:11:33 -0700252 s := strings.Repeat("x", sizeNoStack)
Dmitry Vyukov71be0132015-02-03 20:50:58 +0300253 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 Randall4d02b122016-02-04 13:38:38 -0800264
Keith Randall6f3f02f2016-04-24 17:04:32 -0700265func isZeroed(b []byte) bool {
266 for _, x := range b {
267 if x != 0 {
268 return false
269 }
270 }
271 return true
272}
273
274func isZeroedR(r []rune) bool {
275 for _, x := range r {
276 if x != 0 {
277 return false
278 }
279 }
280 return true
281}
282
Keith Randall4d02b122016-02-04 13:38:38 -0800283func TestString2Slice(t *testing.T) {
284 // Make sure we don't return slices that expose
285 // an unzeroed section of stack-allocated temp buf
Brad Fitzpatrick5fea2cc2016-03-01 23:21:55 +0000286 // between len and cap. See issue 14232.
Keith Randall4d02b122016-02-04 13:38:38 -0800287 s := "foož"
288 b := ([]byte)(s)
Keith Randall6f3f02f2016-04-24 17:04:32 -0700289 if !isZeroed(b[len(b):cap(b)]) {
290 t.Errorf("extra bytes not zeroed")
Keith Randall4d02b122016-02-04 13:38:38 -0800291 }
292 r := ([]rune)(s)
Keith Randall6f3f02f2016-04-24 17:04:32 -0700293 if !isZeroedR(r[len(r):cap(r)]) {
294 t.Errorf("extra runes not zeroed")
Keith Randall4d02b122016-02-04 13:38:38 -0800295 }
296}
Martin Möhrmannd7b34d52016-10-30 01:54:19 +0200297
298const intSize = 32 << (^uint(0) >> 63)
299
300type atoi64Test struct {
301 in string
302 out int64
303 ok bool
304}
305
306var 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
330func 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
353type atoi32Test struct {
354 in string
355 out int32
356 ok bool
357}
358
359var 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
381func 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}