...
Run Format

Source file src/runtime/string_test.go

Documentation: runtime

     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  
     5  package runtime_test
     6  
     7  import (
     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.
    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  	// 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  
   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 TestStringOnStack(t *testing.T) {
   244  	s := ""
   245  	for i := 0; i < 3; i++ {
   246  		s = "a" + s + "b" + s + "c"
   247  	}
   248  
   249  	if want := "aaabcbabccbaabcbabccc"; s != want {
   250  		t.Fatalf("want: '%v', got '%v'", want, s)
   251  	}
   252  }
   253  
   254  func TestIntString(t *testing.T) {
   255  	// Non-escaping result of intstring.
   256  	s := ""
   257  	for i := 0; i < 4; i++ {
   258  		s += string(i+'0') + string(i+'0'+1)
   259  	}
   260  	if want := "01122334"; s != want {
   261  		t.Fatalf("want '%v', got '%v'", want, s)
   262  	}
   263  
   264  	// Escaping result of intstring.
   265  	var a [4]string
   266  	for i := 0; i < 4; i++ {
   267  		a[i] = string(i + '0')
   268  	}
   269  	s = a[0] + a[1] + a[2] + a[3]
   270  	if want := "0123"; s != want {
   271  		t.Fatalf("want '%v', got '%v'", want, s)
   272  	}
   273  }
   274  
   275  func TestIntStringAllocs(t *testing.T) {
   276  	unknown := '0'
   277  	n := testing.AllocsPerRun(1000, func() {
   278  		s1 := string(unknown)
   279  		s2 := string(unknown + 1)
   280  		if s1 == s2 {
   281  			t.Fatalf("bad")
   282  		}
   283  	})
   284  	if n != 0 {
   285  		t.Fatalf("want 0 allocs, got %v", n)
   286  	}
   287  }
   288  
   289  func TestRangeStringCast(t *testing.T) {
   290  	s := strings.Repeat("x", sizeNoStack)
   291  	n := testing.AllocsPerRun(1000, func() {
   292  		for i, c := range []byte(s) {
   293  			if c != s[i] {
   294  				t.Fatalf("want '%c' at pos %v, got '%c'", s[i], i, c)
   295  			}
   296  		}
   297  	})
   298  	if n != 0 {
   299  		t.Fatalf("want 0 allocs, got %v", n)
   300  	}
   301  }
   302  
   303  func isZeroed(b []byte) bool {
   304  	for _, x := range b {
   305  		if x != 0 {
   306  			return false
   307  		}
   308  	}
   309  	return true
   310  }
   311  
   312  func isZeroedR(r []rune) bool {
   313  	for _, x := range r {
   314  		if x != 0 {
   315  			return false
   316  		}
   317  	}
   318  	return true
   319  }
   320  
   321  func TestString2Slice(t *testing.T) {
   322  	// Make sure we don't return slices that expose
   323  	// an unzeroed section of stack-allocated temp buf
   324  	// between len and cap. See issue 14232.
   325  	s := "foož"
   326  	b := ([]byte)(s)
   327  	if !isZeroed(b[len(b):cap(b)]) {
   328  		t.Errorf("extra bytes not zeroed")
   329  	}
   330  	r := ([]rune)(s)
   331  	if !isZeroedR(r[len(r):cap(r)]) {
   332  		t.Errorf("extra runes not zeroed")
   333  	}
   334  }
   335  
   336  const intSize = 32 << (^uint(0) >> 63)
   337  
   338  type atoi64Test struct {
   339  	in  string
   340  	out int64
   341  	ok  bool
   342  }
   343  
   344  var atoi64tests = []atoi64Test{
   345  	{"", 0, false},
   346  	{"0", 0, true},
   347  	{"-0", 0, true},
   348  	{"1", 1, true},
   349  	{"-1", -1, true},
   350  	{"12345", 12345, true},
   351  	{"-12345", -12345, true},
   352  	{"012345", 12345, true},
   353  	{"-012345", -12345, true},
   354  	{"12345x", 0, false},
   355  	{"-12345x", 0, false},
   356  	{"98765432100", 98765432100, true},
   357  	{"-98765432100", -98765432100, true},
   358  	{"20496382327982653440", 0, false},
   359  	{"-20496382327982653440", 0, false},
   360  	{"9223372036854775807", 1<<63 - 1, true},
   361  	{"-9223372036854775807", -(1<<63 - 1), true},
   362  	{"9223372036854775808", 0, false},
   363  	{"-9223372036854775808", -1 << 63, true},
   364  	{"9223372036854775809", 0, false},
   365  	{"-9223372036854775809", 0, false},
   366  }
   367  
   368  func TestAtoi(t *testing.T) {
   369  	switch intSize {
   370  	case 32:
   371  		for i := range atoi32tests {
   372  			test := &atoi32tests[i]
   373  			out, ok := runtime.Atoi(test.in)
   374  			if test.out != int32(out) || test.ok != ok {
   375  				t.Errorf("atoi(%q) = (%v, %v) want (%v, %v)",
   376  					test.in, out, ok, test.out, test.ok)
   377  			}
   378  		}
   379  	case 64:
   380  		for i := range atoi64tests {
   381  			test := &atoi64tests[i]
   382  			out, ok := runtime.Atoi(test.in)
   383  			if test.out != int64(out) || test.ok != ok {
   384  				t.Errorf("atoi(%q) = (%v, %v) want (%v, %v)",
   385  					test.in, out, ok, test.out, test.ok)
   386  			}
   387  		}
   388  	}
   389  }
   390  
   391  type atoi32Test struct {
   392  	in  string
   393  	out int32
   394  	ok  bool
   395  }
   396  
   397  var atoi32tests = []atoi32Test{
   398  	{"", 0, false},
   399  	{"0", 0, true},
   400  	{"-0", 0, true},
   401  	{"1", 1, true},
   402  	{"-1", -1, true},
   403  	{"12345", 12345, true},
   404  	{"-12345", -12345, true},
   405  	{"012345", 12345, true},
   406  	{"-012345", -12345, true},
   407  	{"12345x", 0, false},
   408  	{"-12345x", 0, false},
   409  	{"987654321", 987654321, true},
   410  	{"-987654321", -987654321, true},
   411  	{"2147483647", 1<<31 - 1, true},
   412  	{"-2147483647", -(1<<31 - 1), true},
   413  	{"2147483648", 0, false},
   414  	{"-2147483648", -1 << 31, true},
   415  	{"2147483649", 0, false},
   416  	{"-2147483649", 0, false},
   417  }
   418  
   419  func TestAtoi32(t *testing.T) {
   420  	for i := range atoi32tests {
   421  		test := &atoi32tests[i]
   422  		out, ok := runtime.Atoi32(test.in)
   423  		if test.out != out || test.ok != ok {
   424  			t.Errorf("atoi32(%q) = (%v, %v) want (%v, %v)",
   425  				test.in, out, ok, test.out, test.ok)
   426  		}
   427  	}
   428  }
   429  

View as plain text