(转)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
}
技术链接
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
2018-03-01 (转)DB2中的一些函数
2018-03-01 (转)DB2 db2diag.log 日志分析