Go语言在unsafe
包中引入了一些函数,用于实现快速的string
到[]byte
和[]byte
到string
的转换。它非常快,详情请见https://www.sobyte.net/post/2022-09/string-byte-convertion/,但由于该解决方案使用内存指针而不是值拷贝,改变此类值会产生副作用。
快速string到[]byte转换
传统方式
通常的转换方式很简单,例如:
// string to []byte
bs := []byte("Hello")
// []byte to string
txt := string([]byte{72, 101, 108, 108, 111})
但是,值拷贝消耗更多资源(内存、CPU时间),这可能是关键的。
快速方式
Go 1.20在unsafe
包中引入了新函数(SliceData
、String
和StringData
),以提供完整的构造和解构切片和字符串值的能力,而不依赖于它们的确切表示方式,详情请见https://tip.golang.org/doc/go1.20。
unsafe
函数的使用示例可在https://go101.org/article/unsafe.html中找到:
import "unsafe"
func String2ByteSlice(str string) []byte {
if str == "" {
return nil
}
return unsafe.Slice(unsafe.StringData(str), len(str))
}
func ByteSlice2String(bs []byte) string {
if len(bs) == 0 {
return ""
}
return unsafe.String(unsafe.SliceData(bs), len(bs))
}
这些函数返回具有相同内存引用的另一种类型,因此比传统拷贝快得多。它有效,直到所引用的内存被改变。string
是不可变的,但[]byte
随时都可以被更改。让我们看看如果我们更改它会发生什么。
更改值
string到[]byte
在第一个示例中,返回的[]byte
将被更改:
func TestString2ByteSlice(t *testing.T) {
// given
sampleStr1 := "012"
// when
sampleBytes1 := String2ByteSlice(sampleStr1)
// then
assert.Equal(t, []byte{0x30, 0x31, 0x32}, sampleBytes1)
// when segfault!
// sampleBytes1[1] = 0x39
}
sampleBytes1[1]
会将'1'
处的字节设置为'9'
,但会产生段错误,因为string
类型是不可变的,Go会防止其更改:
unexpected fault address 0x5d523d
fatal error: fault
[signal SIGSEGV: segmentation violation code=0x2 addr=0x5d523d pc=0x5878b4]
[]byte到string
在这个示例中,原始的字节切片将被更改:
func TestByteSlice2String(t *testing.T) {
// given
sampleBytes1 := []byte{0x30, 0x31, 0x32}
// when
sampleStr1 := ByteSlice2String(sampleBytes1)
// then
assert.Equal(t, "012", sampleStr1)
// when
sampleBytes1[1] = 0x39
// then
assert.Equal(t, "012", sampleStr1)
}
sampleBytes1[1]
将'1'
处的字节设置为'9'
,这是成功的,返回的sampleStr1
也将被更改:
--- FAIL: TestByteSlice2String (0.00s)
Error: Not equal:
expected: "012"
actual : "092"
总结
新的转换方式很快,但是并非没有代价:如果更改了值,将会产生段错误或不想要的字符串更改。
译自:https://blog.devops.dev/fast-string-to-byte-and-byte-to-string-conversion-in-go-1-20-85bdb859ee67
评论(0)