Skip to content

Commit

Permalink
bytes, strings: add CutByte
Browse files Browse the repository at this point in the history
CutByte optimizes slicing operations for single-byte
separators, offering a more efficient alternative when only a single byte
is involved.

There is more discussion on https://golang.org/issue/67101.

Fixes golang#67101.
  • Loading branch information
aimuz committed Apr 29, 2024
1 parent db5f2b4 commit ed17861
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 0 deletions.
13 changes: 13 additions & 0 deletions src/bytes/bytes.go
Original file line number Diff line number Diff line change
Expand Up @@ -1336,6 +1336,19 @@ func Cut(s, sep []byte) (before, after []byte, found bool) {
return s, nil, false
}

// CutByte slices s around the first instance of sep,
// returning the text before and after sep.
// The found result reports whether sep appears in s.
// If sep does not appear in s, cut returns s, nil, false.
//
// CutByte returns slices of the original slice s, not copies.
func CutByte(s []byte, sep byte) (before, after []byte, found bool) {
if i := IndexByte(s, sep); i >= 0 {
return s[:i], s[i+1:], true
}
return s, nil, false
}

// Clone returns a copy of b[:len(b)].
// The result may have additional unused capacity.
// Clone(nil) returns nil.
Expand Down
20 changes: 20 additions & 0 deletions src/bytes/bytes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1742,6 +1742,26 @@ func TestCut(t *testing.T) {
}
}

var cutByteTests = []struct {
s string
sep byte
before, after string
found bool
}{
{"abc", 'b', "a", "c", true},
{"abc", 'a', "", "bc", true},
{"abc", 'c', "ab", "", true},
{"", 'd', "", "", false},
}

func TestCutByte(t *testing.T) {
for _, tt := range cutByteTests {
if before, after, found := CutByte([]byte(tt.s), tt.sep); string(before) != tt.before || string(after) != tt.after || found != tt.found {
t.Errorf("Cut(%q, %q) = %q, %q, %v, want %q, %q, %v", tt.s, tt.sep, before, after, found, tt.before, tt.after, tt.found)
}
}
}

var cutPrefixTests = []struct {
s, sep string
after string
Expand Down
11 changes: 11 additions & 0 deletions src/strings/strings.go
Original file line number Diff line number Diff line change
Expand Up @@ -1315,6 +1315,17 @@ func Cut(s, sep string) (before, after string, found bool) {
return s, "", false
}

// CutByte slices s around the first instance of sep,
// returning the text before and after sep.
// The found result reports whether sep appears in s.
// If sep does not appear in s, cut returns s, "", false.
func CutByte(s string, sep byte) (before, after string, found bool) {
if i := IndexByte(s, sep); i >= 0 {
return s[:i], s[i+1:], true
}
return s, "", false
}

// CutPrefix returns s without the provided leading prefix string
// and reports whether it found the prefix.
// If s doesn't start with prefix, CutPrefix returns s, false.
Expand Down
20 changes: 20 additions & 0 deletions src/strings/strings_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1669,6 +1669,26 @@ func TestCut(t *testing.T) {
}
}

var cutByteTests = []struct {
s string
sep byte
before, after string
found bool
}{
{"abc", 'b', "a", "c", true},
{"abc", 'a', "", "bc", true},
{"abc", 'c', "ab", "", true},
{"", 'd', "", "", false},
}

func TestCutByte(t *testing.T) {
for _, tt := range cutByteTests {
if before, after, found := CutByte(tt.s, tt.sep); before != tt.before || after != tt.after || found != tt.found {
t.Errorf("Cut(%q, %q) = %q, %q, %v, want %q, %q, %v", tt.s, tt.sep, before, after, found, tt.before, tt.after, tt.found)
}
}
}

var cutPrefixTests = []struct {
s, sep string
after string
Expand Down

0 comments on commit ed17861

Please sign in to comment.