如何实现字符串和 byte 切片的零复制转换
这是一个非常经典的例子:实现字符串和 bytes 切片之间的转换,要求是 zero-copy。为了完成这个转换,需要了解 slice 和 string 的底层数据结构:
// src/reflect/value.go
type StringHeader struct {
Data uintptr
Len int
}
type SliceHeader struct {
Data uintptr
Len int
Cap int
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
上面的代码是反射包下的结构体,只需要共享底层 Data 和 Len 就可以实现 zero-copy:
func string2bytes(s string) []byte {
return *(*[]byte)(unsafe.Pointer(&s))
}
func bytes2string(b []byte) string{
return *(*string)(unsafe.Pointer(&b))
}
1
2
3
4
5
6
7
2
3
4
5
6
7
原理上是利用指针的强转,代码比较简单,不做详细解释。需要注意的是,下面这种形式的实现是错误的:
func string2bytes(s string) []byte {
stringHeader := (*reflect.StringHeader)(unsafe.Pointer(&s))
bh := reflect.SliceHeader{
Data: stringHeader.Data,
Len: stringHeader.Len,
Cap: stringHeader.Len,
}
return *(*[]byte)(unsafe.Pointer(&bh))
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
原因在于 stringHeader.Data 本身是 uintptr 类型,由于 goroutine 的栈空间可能发生移动,因此不能将其作为中间态的值复制到 bh,再转换为 [] byte。
上次更新: 7/12/2024, 2:37:05 PM