bytes.Buffer是具有读取和写入方法的可变大小的字节缓冲区。 在高频率操作文件IO时使用用作缓冲区。
本文主要对源码进行注释讲解。
结构体定义:
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.
)
(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源码的部份内容,本文仅介绍了一些常用函数,剩余函数会继续补充。