diff options
| author | Ondřej Surý <ondrej@sury.org> | 2011-04-26 09:55:32 +0200 |
|---|---|---|
| committer | Ondřej Surý <ondrej@sury.org> | 2011-04-26 09:55:32 +0200 |
| commit | 7b15ed9ef455b6b66c6b376898a88aef5d6a9970 (patch) | |
| tree | 3ef530baa80cdf29436ba981f5783be6b4d2202b /src/pkg/strings | |
| parent | 50104cc32a498f7517a51c8dc93106c51c7a54b4 (diff) | |
| download | golang-7b15ed9ef455b6b66c6b376898a88aef5d6a9970.tar.gz | |
Imported Upstream version 2011.04.13upstream/2011.04.13
Diffstat (limited to 'src/pkg/strings')
| -rw-r--r-- | src/pkg/strings/strings.go | 51 | ||||
| -rw-r--r-- | src/pkg/strings/strings_test.go | 84 |
2 files changed, 113 insertions, 22 deletions
diff --git a/src/pkg/strings/strings.go b/src/pkg/strings/strings.go index 98a0d5731..93c7c4647 100644 --- a/src/pkg/strings/strings.go +++ b/src/pkg/strings/strings.go @@ -119,9 +119,19 @@ func LastIndex(s, sep string) int { // IndexRune returns the index of the first instance of the Unicode code point // rune, or -1 if rune is not present in s. func IndexRune(s string, rune int) int { - for i, c := range s { - if c == rune { - return i + switch { + case rune < 0x80: + b := byte(rune) + for i := 0; i < len(s); i++ { + if s[i] == b { + return i + } + } + default: + for i, c := range s { + if c == rune { + return i + } } } return -1 @@ -265,20 +275,10 @@ func Join(a []string, sep string) string { } b := make([]byte, n) - bp := 0 - for i := 0; i < len(a); i++ { - s := a[i] - for j := 0; j < len(s); j++ { - b[bp] = s[j] - bp++ - } - if i+1 < len(a) { - s = sep - for j := 0; j < len(s); j++ { - b[bp] = s[j] - bp++ - } - } + bp := copy(b, a[0]) + for _, s := range a[1:] { + bp += copy(b[bp:], sep) + bp += copy(b[bp:], s) } return string(b) } @@ -302,9 +302,19 @@ func Map(mapping func(rune int) int, s string) string { // fine. It could also shrink but that falls out naturally. maxbytes := len(s) // length of b nbytes := 0 // number of bytes encoded in b - b := make([]byte, maxbytes) - for _, c := range s { + // The output buffer b is initialized on demand, the first + // time a character differs. + var b []byte + + for i, c := range s { rune := mapping(c) + if b == nil { + if rune == c { + continue + } + b = make([]byte, maxbytes) + nbytes = copy(b, s[:i]) + } if rune >= 0 { wid := 1 if rune >= utf8.RuneSelf { @@ -320,6 +330,9 @@ func Map(mapping func(rune int) int, s string) string { nbytes += utf8.EncodeRune(b[nbytes:maxbytes], rune) } } + if b == nil { + return s + } return string(b[0:nbytes]) } diff --git a/src/pkg/strings/strings_test.go b/src/pkg/strings/strings_test.go index 734fdd33d..c45b1485d 100644 --- a/src/pkg/strings/strings_test.go +++ b/src/pkg/strings/strings_test.go @@ -6,9 +6,12 @@ package strings_test import ( "os" + "reflect" + "strconv" . "strings" "testing" "unicode" + "unsafe" "utf8" ) @@ -116,6 +119,57 @@ func TestLastIndex(t *testing.T) { runIndexTests(t, LastIndex, "LastIndex", l func TestIndexAny(t *testing.T) { runIndexTests(t, IndexAny, "IndexAny", indexAnyTests) } func TestLastIndexAny(t *testing.T) { runIndexTests(t, LastIndexAny, "LastIndexAny", lastIndexAnyTests) } +type IndexRuneTest struct { + s string + rune int + out int +} + +var indexRuneTests = []IndexRuneTest{ + {"a A x", 'A', 2}, + {"some_text=some_value", '=', 9}, + {"☺a", 'a', 3}, + {"a☻☺b", '☺', 4}, +} + +func TestIndexRune(t *testing.T) { + for _, test := range indexRuneTests { + if actual := IndexRune(test.s, test.rune); actual != test.out { + t.Errorf("IndexRune(%q,%d)= %v; want %v", test.s, test.rune, actual, test.out) + } + } +} + +const benchmarkString = "some_text=some☺value" + +func BenchmarkIndexRune(b *testing.B) { + if got := IndexRune(benchmarkString, '☺'); got != 14 { + panic("wrong index: got=" + strconv.Itoa(got)) + } + for i := 0; i < b.N; i++ { + IndexRune(benchmarkString, '☺') + } +} + +func BenchmarkIndexRuneFastPath(b *testing.B) { + if got := IndexRune(benchmarkString, 'v'); got != 17 { + panic("wrong index: got=" + strconv.Itoa(got)) + } + for i := 0; i < b.N; i++ { + IndexRune(benchmarkString, 'v') + } +} + +func BenchmarkIndex(b *testing.B) { + if got := Index(benchmarkString, "v"); got != 17 { + panic("wrong index: got=" + strconv.Itoa(got)) + } + for i := 0; i < b.N; i++ { + Index(benchmarkString, "v") + } +} + + type ExplodeTest struct { s string n int @@ -377,12 +431,32 @@ func TestMap(t *testing.T) { if m != expect { t.Errorf("drop: expected %q got %q", expect, m) } + + // 6. Identity + identity := func(rune int) int { + return rune + } + orig := "Input string that we expect not to be copied." + m = Map(identity, orig) + if (*reflect.StringHeader)(unsafe.Pointer(&orig)).Data != + (*reflect.StringHeader)(unsafe.Pointer(&m)).Data { + t.Error("unexpected copy during identity map") + } } func TestToUpper(t *testing.T) { runStringTests(t, ToUpper, "ToUpper", upperTests) } func TestToLower(t *testing.T) { runStringTests(t, ToLower, "ToLower", lowerTests) } +func BenchmarkMapNoChanges(b *testing.B) { + identity := func(rune int) int { + return rune + } + for i := 0; i < b.N; i++ { + Map(identity, "Some string that won't be modified.") + } +} + func TestSpecialCase(t *testing.T) { lower := "abcçdefgğhıijklmnoöprsştuüvyz" upper := "ABCÇDEFGĞHIİJKLMNOÖPRSŞTUÜVYZ" @@ -565,7 +639,11 @@ func equal(m string, s1, s2 string, t *testing.T) bool { func TestCaseConsistency(t *testing.T) { // Make a string of all the runes. - a := make([]int, unicode.MaxRune+1) + numRunes := unicode.MaxRune + 1 + if testing.Short() { + numRunes = 1000 + } + a := make([]int, numRunes) for i := range a { a[i] = i } @@ -575,10 +653,10 @@ func TestCaseConsistency(t *testing.T) { lower := ToLower(s) // Consistency checks - if n := utf8.RuneCountInString(upper); n != unicode.MaxRune+1 { + if n := utf8.RuneCountInString(upper); n != numRunes { t.Error("rune count wrong in upper:", n) } - if n := utf8.RuneCountInString(lower); n != unicode.MaxRune+1 { + if n := utf8.RuneCountInString(lower); n != numRunes { t.Error("rune count wrong in lower:", n) } if !equal("ToUpper(upper)", ToUpper(upper), upper, t) { |
