上卷 程序设计
中卷 标准库
- bufio 1.18
- bytes 1.18
- io 1.18
- container 1.18
- encoding 1.18
- crypto 1.18
- hash 1.18
- index 1.18
- sort 1.18
- context 1.18
- database 1.18
- connection
- query
- queryrow
- exec
- prepare
- transaction
- scan & null
- context
- tcp
- udp
- http
- server
- handler
- client
- h2、tls
- url
- rpc
- exec
- signal
- embed 1.18
- plugin 1.18
- reflect 1.18
- runtime 1.18
- KeepAlived
- ReadMemStats
- SetFinalizer
- Stack
- sync 1.18
- atomic
- mutex
- rwmutex
- waitgroup
- cond
- once
- map
- pool
- copycheck
- nocopy
- unsafe 1.18
- fmt 1.18
- log 1.18
- math 1.18
- time 1.18
- timer
下卷 运行时
源码剖析
附录
文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
bytes 1.18
和 strings
组队,分别应对字符串和字节数组,函数都差不多。
Buffer
变长(variable-sized)字节缓冲区。
- 预先
Grow
足够空间,减少后续内存分配。 - 直接或间接调用
Reset
,复用内存,不回收。
func main() { buf := bytes.NewBuffer(nil) buf.Write([]byte{1, 2, 3}) fmt.Println(buf.Bytes()) }
源码剖析
结构和实现都比较简单。封装 []byte
,实现相关 I/O 接口。
// bytes/buffer.go const smallBufferSize = 64 type Buffer struct { buf []byte // 有效内容: buf[off:len(buf)] off int // 读写位置: &buf[off], &buf[len(buf)] }
func NewBuffer(buf []byte) *Buffer { return &Buffer{buf: buf} } func NewBufferString(s string) *Buffer { return &Buffer{buf: []byte(s)} }
通常会提前扩张(grow)足够内存,避免后续频繁调整。
且相关方法也会调用。
// 扩张,确保有 n 子节空间可用。 func (b *Buffer) Grow(n int) { m := b.grow(n) b.buf = b.buf[:m] } func (b *Buffer) grow(n int) int { m := b.Len() // 没有剩余可读数据,重置。 if m == 0 && b.off != 0 { b.Reset() } // 尝试对底层数组(cap)重新切片。 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(ErrTooLarge) } else { // 空间不足,重新分配 (2x + n) 缓冲区,复制数据。 buf := makeSlice(2*c + n) copy(buf, b.buf[b.off:]) b.buf = buf } // 调整状态。 b.off = 0 b.buf = b.buf[:m+n] return m }
// 如剩余空间足够,直接调整切片。 func (b *Buffer) tryGrowByReslice(n int) (int, bool) { if l := len(b.buf); n <= cap(b.buf)-l { b.buf = b.buf[:l+n] return l, true } return 0, false } // 重置状态,缓冲区复用。 func (b *Buffer) Reset() { b.buf = b.buf[:0] b.off = 0 }
和扩张(grow)对应的是截断(truncate),但并不涉及内存释放操作。
// 重新切片,保留 n 个未读子节。 // 并没有数据前移或重新分配操纵。 func (b *Buffer) Truncate(n int) { if n == 0 { b.Reset() return } b.buf = b.buf[:b.off+n] }
写操作前,确保有足够空间。在切片尾部追加数据。
func (b *Buffer) Write(p []byte) (n int, err error) { m, ok := b.tryGrowByReslice(len(p)) if !ok { m = b.grow(len(p)) } return copy(b.buf[m:], p), nil }
读操作无非是先检查,读取后调整偏移状态。
func (b *Buffer) Read(p []byte) (n int, err error) { // 没有数据。重置,复用内存。 if b.empty() { b.Reset() if len(p) == 0 { return 0, nil } return 0, io.EOF } // 复制数据后调整状态。 n = copy(p, b.buf[b.off:]) b.off += n return n, nil }
其他相关操作。需要注意,是否影响读位置偏移。
// 可读数据量。(非存储) func (b *Buffer) Len() int { return len(b.buf) - b.off } // 返回剩余未读字节,不影响偏移。 // 必要时,copy 数据,避免被影响。 func (b *Buffer) Bytes() []byte { return b.buf[b.off:] } // 按剩余数据量,返回最多 n 长度的字节切片。 // 和读操作类似,会调整偏移状态。 func (b *Buffer) Next(n int) []byte { m := b.Len() if n > m { n = m } data := b.buf[b.off : b.off+n] b.off += n return data }
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论