Go bytes.Buffer 源码详解

Go bytes.Buffer 源码详解


一、bytes.Buffer 简介

bytes.Buffer是具有读取和写入方法的可变大小的字节缓冲区。 在高频率操作文件IO时使用用作缓冲区。
本文主要对源码进行注释讲解。

二、源码详解

1.定义

结构体定义:

type Buffer struct {
	buf      []byte //  byte切片  contents are the bytes buf[off : len(buf)]
	off      int    //  偏移量,read中记录已读入的位置,write中记录写入的位置
	lastRead readOp //  记录最后读入的位置 
}

定义常量用于检查和记录缓冲区使用位置

type readOp int8
const (
	opRead      readOp = -1 // Any other read operation.
	opInvalid   readOp = 0  // Non-read operation.
	opReadRune1 readOp = 1  // Read rune of size 1.
	opReadRune2 readOp = 2  // Read rune of size 2.
	opReadRune3 readOp = 3  // Read rune of size 3.
	opReadRune4 readOp = 4  // Read rune of size 4.
)

2.常用方法

(1)NewBuffer(),该方法传入与一个byte切片,然后将该切片当作buffer缓冲使用,传入后不可以再使用该切片。
该函数返回一个Buffer结构体指针。

func NewBuffer(buf []byte) *Buffer { return &Buffer{buf: buf} }

(2)NewBufferString(),该方法与NewBuffer类似,传入string,返回Buffer指针,这里使用了强制类型转换来实现。

func NewBufferString(s string) *Buffer {
	return &Buffer{buf: []byte(s)}
}

(3)从缓冲区读取数据
Read(p ). 如果缓冲区为空,则清空后返回。否则将缓冲区所有内容拷贝至切片p.
并修改Buffer的读标记位,表示缓冲区全部读取完成。

func (b *Buffer) Read(p []byte) (n int, err error) {
	b.lastRead = opInvalid
	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
	if n > 0 {
		b.lastRead = opRead
	}
	return n, nil
}

buffer.Next(n)。实现与Read()类似,先判断缓冲区大小,然后将指定大小(如果不够则将剩余内容全部读出)的缓冲区内容返回(这里返回的是切片)。

func (b *Buffer) Next(n int) []byte {
	b.lastRead = opInvalid
	m := b.Len()
	if n > m {
		n = m
	}
	data := b.buf[b.off : b.off+n]
	b.off += n
	if n > 0 {
		b.lastRead = opRead
	}
	return data
}

(4) 向缓冲区写入数据
buffer.Write( p) 将切片p中的内容通过copy写入缓冲。这里先调用tryGrowByReslice(),通过cap(b.buf)来检测Buffer中切片容量是否足够。如果可以增加,则调用grow()函数。

func (b *Buffer) Write(p []byte) (n int, err error) {
	b.lastRead = opInvalid
	m, ok := b.tryGrowByReslice(len(p))
	if !ok {
		m = b.grow(len(p))
	}
	return copy(b.buf[m:], p), nil
}
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
}

(5)grow()函数首先检查buffer本身大小。如果buffer为空,直接调用make()分配一个大小为n,容量为64的切片。
如果buffer已经分配过内存,再次判断buffer容量和大小。如果buffer的大小还不到容量的一半,直接将buffer使用copy翻倍
如果超过一半了,再使用makeSlice来make一个n大小的切片,然后将原buffer迁移至新切片。

func (b *Buffer) grow(n int) int {
	m := b.Len()
//...
	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 {
		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
}

总结

以上就是bytes源码的部份内容,本文仅介绍了一些常用函数,剩余函数会继续补充。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值