...

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 TestStringIndexHaystack(t *testing.T) {
	 244  	// See issue 25864.
	 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  	// See issue 25864.
	 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  	// Non-escaping result of intstring.
	 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  	// Escaping result of intstring.
	 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  	// Make sure we don't return slices that expose
	 351  	// an unzeroed section of stack-allocated temp buf
	 352  	// between len and cap. See issue 14232.
	 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