返回介绍

上卷 程序设计

中卷 标准库

下卷 运行时

源码剖析

附录

bytes 1.18

发布于 2024-10-12 19:15:51 字数 2964 浏览 0 评论 0 收藏 0

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文