mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Use unsafe.String/StringData to implement string functions
unsafe.String and unsafe.StringData provide compatibility guarantees that don't require us to verify compatibility in each release. For #8422. PiperOrigin-RevId: 505150667
This commit is contained in:
committed by
gVisor bot
parent
a8d6cc4072
commit
16c24b1cec
+6
-1
@@ -8,6 +8,8 @@ go_library(
|
||||
"gohacks_unsafe.go",
|
||||
"slice_go113_unsafe.go",
|
||||
"slice_go120_unsafe.go",
|
||||
"string_go113_unsafe.go",
|
||||
"string_go120_unsafe.go",
|
||||
],
|
||||
stateify = False,
|
||||
visibility = ["//:sandbox"],
|
||||
@@ -16,7 +18,10 @@ go_library(
|
||||
go_test(
|
||||
name = "gohacks_test",
|
||||
size = "small",
|
||||
srcs = ["gohacks_test.go"],
|
||||
srcs = [
|
||||
"gohacks_test.go",
|
||||
"string_test.go",
|
||||
],
|
||||
library = ":gohacks",
|
||||
deps = ["@org_golang_x_sys//unix:go_default_library"],
|
||||
)
|
||||
|
||||
@@ -30,14 +30,6 @@ import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// StringHeader is equivalent to reflect.StringHeader, but represents the
|
||||
// pointer to the underlying array as unsafe.Pointer rather than uintptr,
|
||||
// allowing StringHeaders to be directly converted to strings.
|
||||
type StringHeader struct {
|
||||
Data unsafe.Pointer
|
||||
Len int
|
||||
}
|
||||
|
||||
// Noescape hides a pointer from escape analysis. Noescape is the identity
|
||||
// function but escape analysis doesn't think the output depends on the input.
|
||||
// Noescape is inlined and currently compiles down to zero instructions.
|
||||
@@ -51,24 +43,6 @@ func Noescape(p unsafe.Pointer) unsafe.Pointer {
|
||||
return unsafe.Pointer(x ^ 0)
|
||||
}
|
||||
|
||||
// ImmutableBytesFromString is equivalent to []byte(s), except that it uses the
|
||||
// same memory backing s instead of making a heap-allocated copy. This is only
|
||||
// valid if the returned slice is never mutated.
|
||||
func ImmutableBytesFromString(s string) []byte {
|
||||
shdr := (*StringHeader)(unsafe.Pointer(&s))
|
||||
return Slice((*byte)(shdr.Data), shdr.Len)
|
||||
}
|
||||
|
||||
// StringFromImmutableBytes is equivalent to string(bs), except that it uses
|
||||
// the same memory backing bs instead of making a heap-allocated copy. This is
|
||||
// only valid if bs is never mutated after StringFromImmutableBytes returns.
|
||||
func StringFromImmutableBytes(bs []byte) string {
|
||||
// This is cheaper than messing with StringHeader and SliceHeader, which as
|
||||
// of this writing produces many dead stores of zeroes. Compare
|
||||
// strings.Builder.String().
|
||||
return *(*string)(unsafe.Pointer(&bs))
|
||||
}
|
||||
|
||||
// Note that go:linkname silently doesn't work if the local name is exported,
|
||||
// necessitating an indirection for exported functions.
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright 2023 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build go1.13 && !go1.20
|
||||
// +build go1.13,!go1.20
|
||||
|
||||
// TODO(go.dev/issue/8422): Remove this file once Go 1.19 is no longer
|
||||
// supported.
|
||||
|
||||
package gohacks
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// stringHeader is equivalent to reflect.StringHeader, but represents the
|
||||
// pointer to the underlying array as unsafe.Pointer rather than uintptr,
|
||||
// allowing StringHeaders to be directly converted to strings.
|
||||
type stringHeader struct {
|
||||
Data unsafe.Pointer
|
||||
Len int
|
||||
}
|
||||
|
||||
// ImmutableBytesFromString is equivalent to []byte(s), except that it uses the
|
||||
// same memory backing s instead of making a heap-allocated copy. This is only
|
||||
// valid if the returned slice is never mutated.
|
||||
func ImmutableBytesFromString(s string) []byte {
|
||||
shdr := (*stringHeader)(unsafe.Pointer(&s))
|
||||
return Slice((*byte)(shdr.Data), shdr.Len)
|
||||
}
|
||||
|
||||
// StringFromImmutableBytes is equivalent to string(bs), except that it uses
|
||||
// the same memory backing bs instead of making a heap-allocated copy. This is
|
||||
// only valid if bs is never mutated after StringFromImmutableBytes returns.
|
||||
func StringFromImmutableBytes(bs []byte) string {
|
||||
// This is cheaper than messing with StringHeader and SliceHeader, which as
|
||||
// of this writing produces many dead stores of zeroes. Compare
|
||||
// strings.Builder.String().
|
||||
return *(*string)(unsafe.Pointer(&bs))
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright 2023 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build go1.20
|
||||
|
||||
package gohacks
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// ImmutableBytesFromString is equivalent to []byte(s), except that it uses the
|
||||
// same memory backing s instead of making a heap-allocated copy. This is only
|
||||
// valid if the returned slice is never mutated.
|
||||
func ImmutableBytesFromString(s string) []byte {
|
||||
b := unsafe.StringData(s)
|
||||
return unsafe.Slice(b, len(s))
|
||||
}
|
||||
|
||||
// StringFromImmutableBytes is equivalent to string(bs), except that it uses
|
||||
// the same memory backing bs instead of making a heap-allocated copy. This is
|
||||
// only valid if bs is never mutated after StringFromImmutableBytes returns.
|
||||
func StringFromImmutableBytes(bs []byte) string {
|
||||
if len(bs) == 0 {
|
||||
return ""
|
||||
}
|
||||
return unsafe.String(&bs[0], len(bs))
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
// Copyright 2023 The gVisor Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package gohacks
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestImmutableBytesFromString(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
want []byte
|
||||
}{
|
||||
{
|
||||
name: "abc",
|
||||
input: "abc",
|
||||
want: []byte("abc"),
|
||||
},
|
||||
{
|
||||
name: "empty",
|
||||
input: "",
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "subslice-empty",
|
||||
input: "abc"[:0],
|
||||
want: []byte(""),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got := ImmutableBytesFromString(tc.input)
|
||||
if !reflect.DeepEqual(got, tc.want) {
|
||||
t.Errorf("got contents %v (len %d cap %d) want %v (len %d cap %d)", got, len(got), cap(got), tc.want, len(tc.want), cap(tc.want))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStringFromImmutableBytes(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input []byte
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "abc",
|
||||
input: []byte("abc"),
|
||||
want: "abc",
|
||||
},
|
||||
{
|
||||
name: "empty",
|
||||
input: []byte{},
|
||||
want: "",
|
||||
},
|
||||
{
|
||||
name: "nil",
|
||||
input: nil,
|
||||
want: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got := StringFromImmutableBytes(tc.input)
|
||||
if got != tc.want {
|
||||
t.Errorf("got %q want %q", got, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user