-
Notifications
You must be signed in to change notification settings - Fork 418
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
internal/strs: use go1.20 unsafe interface for strings
This is slightly more efficient and removes explicit dependence on string layout and alignment. Build tags control use. Because of what is arguably a bug in go 1.20's treatment of build tags, is build-tagged to go1.21, not go1.20. (1.20 sees that it matches the build tag and tries to compile the file, but then demands that it only use language features from the version mentioned in go.mod. Go 1.21 and 1.22 behave more sensibly.) Change-Id: I0cfc3b2e5651595edd3a82d9071d918700f961cd Reviewed-on: https://go-review.googlesource.com/c/protobuf/+/545195 Reviewed-by: Cassondra Foesch <cfoesch@gmail.com> Run-TryBot: David Chase <drchase@google.com> Reviewed-by: Lasse Folger <lassefolger@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
- Loading branch information
1 parent
9b87403
commit 2087447
Showing
2 changed files
with
76 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
// Copyright 2018 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. | ||
|
||
//go:build !purego && !appengine && go1.21 | ||
// +build !purego,!appengine,go1.21 | ||
|
||
package strs | ||
|
||
import ( | ||
"unsafe" | ||
|
||
"google.golang.org/protobuf/reflect/protoreflect" | ||
) | ||
|
||
// UnsafeString returns an unsafe string reference of b. | ||
// The caller must treat the input slice as immutable. | ||
// | ||
// WARNING: Use carefully. The returned result must not leak to the end user | ||
// unless the input slice is provably immutable. | ||
func UnsafeString(b []byte) string { | ||
return unsafe.String(unsafe.SliceData(b), len(b)) | ||
} | ||
|
||
// UnsafeBytes returns an unsafe bytes slice reference of s. | ||
// The caller must treat returned slice as immutable. | ||
// | ||
// WARNING: Use carefully. The returned result must not leak to the end user. | ||
func UnsafeBytes(s string) []byte { | ||
return unsafe.Slice(unsafe.StringData(s), len(s)) | ||
} | ||
|
||
// Builder builds a set of strings with shared lifetime. | ||
// This differs from strings.Builder, which is for building a single string. | ||
type Builder struct { | ||
buf []byte | ||
} | ||
|
||
// AppendFullName is equivalent to protoreflect.FullName.Append, | ||
// but optimized for large batches where each name has a shared lifetime. | ||
func (sb *Builder) AppendFullName(prefix protoreflect.FullName, name protoreflect.Name) protoreflect.FullName { | ||
n := len(prefix) + len(".") + len(name) | ||
if len(prefix) == 0 { | ||
n -= len(".") | ||
} | ||
sb.grow(n) | ||
sb.buf = append(sb.buf, prefix...) | ||
sb.buf = append(sb.buf, '.') | ||
sb.buf = append(sb.buf, name...) | ||
return protoreflect.FullName(sb.last(n)) | ||
} | ||
|
||
// MakeString is equivalent to string(b), but optimized for large batches | ||
// with a shared lifetime. | ||
func (sb *Builder) MakeString(b []byte) string { | ||
sb.grow(len(b)) | ||
sb.buf = append(sb.buf, b...) | ||
return sb.last(len(b)) | ||
} | ||
|
||
func (sb *Builder) grow(n int) { | ||
if cap(sb.buf)-len(sb.buf) >= n { | ||
return | ||
} | ||
|
||
// Unlike strings.Builder, we do not need to copy over the contents | ||
// of the old buffer since our builder provides no API for | ||
// retrieving previously created strings. | ||
sb.buf = make([]byte, 0, 2*(cap(sb.buf)+n)) | ||
} | ||
|
||
func (sb *Builder) last(n int) string { | ||
return UnsafeString(sb.buf[len(sb.buf)-n:]) | ||
} |