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
5package runtime_test
6
7import (
8	"runtime"
9	"strconv"
10	"strings"
11	"testing"
12	"unicode/utf8"
13)
14
15// Strings and slices that don't escape and fit into tmpBuf are stack allocated,
16// which defeats using AllocsPerRun to test other optimizations.
17const sizeNoStack = 100
18
19func 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
29func 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
39func 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
49func 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
59func 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
73func 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
87func BenchmarkConcatStringAndBytes(b *testing.B) {
88	s1 := []byte("Gophers!")
89	for i := 0; i < b.N; i++ {
90		_ = "Hello " + string(s1)
91	}
92}
93
94var escapeString string
95
96func 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
108var stringdata = []struct{ name, data string }{
109	{"ASCII", "01234567890"},
110	{"Japanese", "日本語日本語日本語"},
111	{"MixedLength", "$Ѐࠀက퀀��\U00040000\U0010FFFF"},
112}
113
114var sinkInt int
115
116func BenchmarkRuneCount(b *testing.B) {
117	// Each sub-benchmark counts the runes in a string in a different way.
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
151func 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
184func 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
195func 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
217func 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
226func 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
239func 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
270func TestStringIndexHaystack(t *testing.T) {
271	// See issue 25864.
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
284func TestStringIndexNeedle(t *testing.T) {
285	// See issue 25864.
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
298func 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
309func TestIntString(t *testing.T) {
310	// Non-escaping result of intstring.
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	// Escaping result of intstring.
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
330func 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
344func 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
358func isZeroed(b []byte) bool {
359	for _, x := range b {
360		if x != 0 {
361			return false
362		}
363	}
364	return true
365}
366
367func isZeroedR(r []rune) bool {
368	for _, x := range r {
369		if x != 0 {
370			return false
371		}
372	}
373	return true
374}
375
376func TestString2Slice(t *testing.T) {
377	// Make sure we don't return slices that expose
378	// an unzeroed section of stack-allocated temp buf
379	// between len and cap. See issue 14232.
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
391const intSize = 32 << (^uint(0) >> 63)
392
393type atoi64Test struct {
394	in  string
395	out int64
396	ok  bool
397}
398
399var 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
423func 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
446type atoi32Test struct {
447	in  string
448	out int32
449	ok  bool
450}
451
452var 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
474func 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
485func TestParseByteCount(t *testing.T) {
486	for _, test := range []struct {
487		in  string
488		out int64
489		ok  bool
490	}{
491		// Good numeric inputs.
492		{"1", 1, true},
493		{"12345", 12345, true},
494		{"012345", 12345, true},
495		{"98765432100", 98765432100, true},
496		{"9223372036854775807", 1<<63 - 1, true},
497
498		// Good trivial suffix inputs.
499		{"1B", 1, true},
500		{"12345B", 12345, true},
501		{"012345B", 12345, true},
502		{"98765432100B", 98765432100, true},
503		{"9223372036854775807B", 1<<63 - 1, true},
504
505		// Good binary suffix inputs.
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		// Good zero inputs.
516		//
517		// -0 is an edge case, but no harm in supporting it.
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		// Bad inputs.
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		// Bad numeric inputs.
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		// Bad trivial suffix inputs.
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		// Bad binary suffix inputs.
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		// Mistaken SI suffix inputs.
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