(转)Go标准库源码阅读之bytes

原文:https://juejin.cn/post/7042950529231618055

Go 语言 bytes.Buffer 源码详解之1

------------------https://juejin.cn/post/7049150687757729822

bytes标准库主要是处理 `byte` 和 `rune` 类型的处理库,这两个类型分别是 ` type byte = uint8` 和 `type rune = int32` 别名;`byte` 可以理解为计算机内存的最小存储单元字节,而 `rune` 仅仅是为了兼容Unicode字符的一种特殊处理:

var rs = []rune{'我', '是', '中', '国', '人'}
var bs = []rune{'c', 'h', 'i', 'n', 'e', 's', 'e'}
fmt.Println(len(rs), len(bs), len([]byte(string(rs)))) // 5 7 15

// go处理unicode使用的是utf-8编码方式,这是一种变长编码,实际存储1~4个字节都有可能
var rs = []rune{'我', '是', '中', '国', '人', ';'}
fmt.Println(len([]byte(string(rs)))) // 16, 最后的 `;` 只占用了1个字节
// go处理unicode使用的是utf-8编码方式,这是一种变长编码,实际存储1~4个字节都有可能
var rs = []rune{'我', '是', '中', '国', '人', ';'}
fmt.Println(len([]byte(string(rs)))) // 16, 最后的 `;` 只占用了1个字节
复制代码

byte 大部分时间都是参与字符串的处理,[]byte或[]rune可以很方便的跟 string 类型互转

str := "我是中国人"
byteStr := []byte(str)
runeStr := []rune(str)
byteStr[1] = 'a'runeStr[1] = 'a'
fmt.Println(string(byteStr), string(runeStr), len(byteStr), len(runeStr))
// �a�是中国人 我a中国人 15 5
// 带有Unicode字符的串如果转为[]byte处理,我们会发现他的长度和赋值都可能不是我们预期的内容
复制代码

bytes标准库中提供了针对[]byte和[]rune很常见的处理函数(这个可以直接查官方文档),以及一个将byte或rune封装起来的Buffer结构体(缓冲区),通过创建缓冲区,我们可以很方便的向缓冲区写入或者读取数据,并且不用关系缓冲区会溢出的问题,bytes会自动帮我们扩容

type Buffer struct {
    buf []byte // 实际存储的内容是在:buf[off : len(buf)]
    off int // 读的时候从off位置开始, 写的时候从 len(buf)开始
    lastRead readOp // last read operation, so that Unread* can work correctly.
}
复制代码

Buffer结构体非常简单,其实就是记录读写地址,另外一个自动扩容的特性,其关键函数如下:

// 每次调用写入相关函数时都会尝试判断是否需要扩容(即tryGrowByReslice函数)
// 处理完成后返回写入位置
func (b *Buffer) grow(n int) int {
    m := b.Len()
    // 如果判断Buffer中没有内容,则直接将 off 置0
    if m == 0 && b.off != 0 {
        b.Reset()
    }    
    // 再次判断是否需要扩容
    if i, ok := b.tryGrowByReslice(n); ok {
        return i
    }    
    // 这里是保证最小分配的内存块不要过小,一是防止频繁的分配内存,二是防止内存过于碎片化
    if b.buf == nil && n <= smallBufferSize {
        b.buf = make([]byte, n, smallBufferSize)
        return 0
    }
    c := cap(b.buf)
    if n <= c/2-m {
        // 这是为了防止内存无限增长,不是每一次请求扩容都会分配新的空间
        // 如果剩余的空间一半仍然大于需要的空间
        // 则移动存储有效数据的空间到最开始
        copy(b.buf, b.buf[b.off:])
    } else if c > maxInt-c-n {
        // 超出最大范围,直接panic
        panic(ErrTooLarge)
    } else {
        // 空间不足,则直接分配原空间的2倍+新申请的空间长度
        // 保证有足够的的空间避免频繁分配内存拷贝数据
        buf := makeSlice(2*c + n)
        copy(buf, b.buf[b.off:])
        b.buf = buf
    }
    // Restore b.off and len(b.buf).
    b.off = 0
    b.buf = b.buf[:m+n]
    return m
}

posted @ 2023-03-01 09:36  liujiacai  阅读(32)  评论(0编辑  收藏  举报