IT袋

当前位置:主页 > 经验教程 > 建站编程 >

[]byte与string的两种转换方式和底层实现

[]byte与string的两种转换方式和底层实现(2)

时间:2024-03-25 20:24:26 来源:IT袋 作者:马勇
导读:[]byte与string的两种转换方式和底层实现,//[]byte转stringfunc b2s(b []byte) string { return *(*string)(unsafe.Pointer(b))} //string转[]bytefunc s2b(s string) (b []byte) { bh := (*reflect.SliceHeader)(unsafe.Pointer(b)) sh := (*reflect.Stri

[]byte与string的两种转换方式和底层实现

//[]byte转string
func b2s(b []byte) string {
    return *(*string)(unsafe.Pointer(&b))
}
 
//string转[]byte
func s2b(s string) (b []byte) {
    bh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
    sh := (*reflect.StringHeader)(unsafe.Pointer(&s))
    bh.Data = sh.Data
    bh.Cap = sh.Len
    bh.Len = sh.Len
    return b
}

可以看出利用reflect.SliceHeader(代表一个运行时的切片) 和 unsafe.Pointer进行指针替换。

为什么可以这么做呢?

前面我们在讲string和[]byte类型的时候就提了,因为两者的底层结构的字段相似!

array和str的len是一致的,而唯一不同的就是cap字段,所以他们的内存布局上是对齐的。

分析

我们看下这两种转换方式底层是如何实现的,这些实现代码在标准库中都是有的,下面底层实现的代码来自Go 1.18.6版本。

标准方式底层实现

string转[]byte底层实现

先看string转[]byte的实现,(实现源码在 src/runtime/string.go 中)

const tmpStringBufSize = 32
//长度32的数组
type tmpBuf [tmpStringBufSize]byte
//时间函数
func stringtoslicebyte(buf *tmpBuf, s string) []byte {
    var b []byte
    //判断字符串长度是否小于等于32
    if buf != nil && len(s) <= len(buf) {
        *buf = tmpBuf{}
        b = buf[:len(s)]
    } else {
        //预定义数组长度不够,重新分配内存
        b = rawbyteslice(len(s))
    }
    copy(b, s)
    return b
}
// rawbyteslice allocates a new byte slice. The byte slice is not zeroed.
//rawbyteslice函数 分配一个新的字节片。字节片未归零
func rawbyteslice(size int) (b []byte) {
    cap := roundupsize(uintptr(size))
    p := mallocgc(cap, nil, false)
    if cap != uintptr(size) {
        memclrNoHeapPointers(add(p, uintptr(size)), cap-uintptr(size))
    }
    *(*slice)(unsafe.Pointer(&b)) = slice{p, size, int(cap)}
    return
}

[]byte与string的两种转换方式和底层实现

上面代码可以看出string转[]byte是,会根据字符串长度来决定是否需要重新分配一块内存。

  • •预先定义了一个长度为32的数组
  • •若字符串的长度不超过这个长度32的数组,copy函数实现string到[]byte的拷贝
  • •若字符串的长度超过了这个长度32的数组,重新分配一块内存了,再进行copy

[]byte转string底层实现

再看[]byte转string的实现,(实现源码在 src/runtime/string.go 中)

const tmpStringBufSize = 32
//长度32的数组
type tmpBuf [tmpStringBufSize]byte
//实现函数
func slicebytetostring(buf *tmpBuf, ptr *byte, n int) (str string) {
    ...
    if n == 1 {
        p := unsafe.Pointer(&staticuint64s[*ptr])
        if goarch.BigEndian {
            p = add(p, 7)
        }
        stringStructOf(&str).str = p
        stringStructOf(&str).len = 1
        return
    }
    var p unsafe.Pointer
    //判断字符串长度是否小于等于32
    if buf != nil && n <= len(buf) {
        p = unsafe.Pointer(buf)
    } else {
        p = mallocgc(uintptr(n), nil, false)
    }
    stringStructOf(&str).str = p
    stringStructOf(&str).len = n
    //拷贝byte数组至字符串
    memmove(p, unsafe.Pointer(ptr), uintptr(n))
    return
}

相关阅读