go 源码1.10.3 bufio.go
一、bufio介绍
bufio实现了带缓冲的IO功能,它是在io.Reader和io.Writer接口对象上提供了进一步的封装,从而提供了更加丰富的操作方法。bufio包主要
二、bufio.Reader
1.结构体
type Reader struct {
buf []byte //缓冲区的数据
rd io.Reader // 底层的io.Reader
r, w int // r ,w分别表示 buf中读和写的指针位置
err error //记录本次读取的error,后续操作中调用readErr函数后会重置err
lastByte int //记录读取的最后一个字节(用于撤销)
lastRuneSize int //记录读取的最后一个字符(Rune)的长度(用于撤销)
}
bufio.Reader封装了io.Reader对象,并提供了带缓冲的功能。
2. 初始化方法
func NewReaderSize(rd io.Reader, size int) *Reader {
// Is it already a Reader?
b, ok := rd.(*Reader)
if ok && len(b.buf) >= size {
return b
}
if size < minReadBufferSize {
size = minReadBufferSize
}
r := new(Reader)
r.reset(make([]byte, size), rd)
return r
}
size用于指定缓冲区的大小,如果size小于minReadBufferSize,则重置size的值为minReadBufferSize(16)。如果该rd是*bufio.Reader对象,并且rd的缓冲区大于size,则不会创建Reader对象,而是直接返回原来的rd对象。否则会创建一个*bufio.Reader对象,并指定buf的大小为size。
const (
defaultBufSize = 4096
)
func NewReader(rd io.Reader) *Reader {
return NewReaderSize(rd, defaultBufSize)
}
创建缓冲区默认大小为defaultBufSize(4096)的*bufio.Reader对象
3.Reader常用方法
func (b *Reader) Size() int { return len(r.buf) }
Size方法返回底层缓冲区的大小
func (b *Reader) Reset(r io.Reader) {
b.reset(b.buf, r)
}
func (b *Reader) reset(buf []byte, r io.Reader) {
*b = Reader{
buf: buf,
rd: r,
lastByte: -1,
lastRuneSize: -1,
}
}
Reset方法丢弃所有缓冲数据,重置所有状态,并切换reader到r,从而从r中读取数据。
func (b *Reader) fill() {
// Slide existing data to beginning.
if b.r > 0 {
/*
将buf中未读的数据copy到buf中首部位置
重置r和w 位置
*/
copy(b.buf, b.buf[b.r:b.w])
b.w -= b.r
b.r = 0
}
if b.w >= len(b.buf) {
panic("bufio: tried to fill full buffer")
}
// Read new data: try a limited number of times.
/*
maxConsecutiveEmptyReads是最多尝试次数
从rd Reader中读取数据到缓冲区buf中,并重置w的位置索引
*/
for i := maxConsecutiveEmptyReads; i > 0; i-- {
n, err := b.rd.Read(b.buf[b.w:])
if n < 0 {
panic(errNegativeRead)
}
b.w += n
if err != nil {
b.err = err
return
}
if n > 0 {
return
}
}
b.err = io.ErrNoProgress
}
fill方法用于将缓冲区读满,可以读入的最大长度是:len(buf)-未读的字节数,如果尝试读取了maxConsecutiveEmptyReads(100)次都没有读取到数据,则会返回。
func (b *Reader) Peek(n int) ([]byte, error) {
if n < 0 {
return nil, ErrNegativeCount
}
/*
如果buf中未读的字节数小于n
并且buf中未读的字节数小于buf的总大小
并且err为nil
满足以上3个条件,则调用fill方法尝试从Reader中读取部分数据块
*/
for b.w-b.r < n && b.w-b.r < len(b.buf) && b.err == nil {
b.fill() // b.w-b.r < len(b.buf) => buffer is not full
}
/*
如果n大于buf的长度,则返回所有未读的内容,和ErrBufferFull的错误信息
*/
if n > len(b.buf) {
return b.buf[b.r:b.w], ErrBufferFull
}
// 0 <= n <= len(b.buf)
var err error
/*
如果n大于可读的长度,则返回error信息
*/
if avail := b.w - b.r; avail < n {
// not enough data in buffer
n = avail
err = b.readErr()
if err == nil {
err = ErrBufferFull
}
}
return b.buf[b.r : b.r+n], err
}
Peek返回输入流的前n个字节,而不会移动读取位置。该操作不会将数据读出,只是引用,引用的数据在下一次读取操作之前是有效的,如果Peek返回的切片长度比n小,它也会返会一个错误说明原因。如果n比缓冲尺寸还大,返回的错误将是ErrBufferFull。