Skip to content

Commit

Permalink
strings: add Clone function
Browse files Browse the repository at this point in the history
The new strings.Clone function copies the input string
without the returned cloned string referencing the
input strings memory.

goarch: amd64
cpu: Intel(R) Core(TM) i5-1038NG7 CPU @ 2.00GHz

name     time/op
Clone-8  24.2ns ± 2%

name     alloc/op
Clone-8   48.0B ± 0%

name     allocs/op
Clone-8    1.00 ± 0%

Update #45038
Fixes #40200

Change-Id: Id9116c21c14328ec3931ef9a67a2e4f30ff301f9
Reviewed-on: https://go-review.googlesource.com/c/go/+/345849
Trust: Martin Möhrmann <martin@golang.org>
Run-TryBot: Martin Möhrmann <martin@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Joe Tsai <joetsai@digital-static.net>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
  • Loading branch information
Martin Möhrmann committed Sep 13, 2021
1 parent bced369 commit e74e363
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 0 deletions.
23 changes: 23 additions & 0 deletions src/strings/clone.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package strings

import (
"unsafe"
)

// Clone returns a fresh copy of s.
// It guarantees to make a copy of s into a new allocation,
// which can be important when retaining only a small substring
// of a much larger string. Using Clone can help such programs
// use less memory. Of course, since using Clone makes a copy,
// overuse of Clone can make programs use more memory.
// Clone should typically be used only rarely, and only when
// profiling indicates that it is needed.
func Clone(s string) string {
b := make([]byte, len(s))
copy(b, s)
return *(*string)(unsafe.Pointer(&b))
}
40 changes: 40 additions & 0 deletions src/strings/clone_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.ß

package strings_test

import (
"reflect"
"strings"
"testing"
"unsafe"
)

func TestClone(t *testing.T) {
var cloneTests = []string{
"",
"short",
strings.Repeat("a", 42),
}
for _, input := range cloneTests {
clone := strings.Clone(input)
if clone != input {
t.Errorf("Clone(%q) = %q; want %q", input, clone, input)
}

inputHeader := (*reflect.StringHeader)(unsafe.Pointer(&input))
cloneHeader := (*reflect.StringHeader)(unsafe.Pointer(&clone))
if inputHeader.Data == cloneHeader.Data {
t.Errorf("Clone(%q) return value should not reference inputs backing memory.", input)
}
}
}

func BenchmarkClone(b *testing.B) {
var str = strings.Repeat("a", 42)
b.ReportAllocs()
for i := 0; i < b.N; i++ {
stringSink = strings.Clone(str)
}
}

0 comments on commit e74e363

Please sign in to comment.