golang源码分析buifo包
b, ok := rd.(*Reader)
上面的语句的作用是判断接口的动态类型是不是*Reader类型,如果是,那么ok的值为true,这种动态类型的判断方法必须rd的类型为一个接口
type Reader struct {
buf []byte
rd io.Reader // reader provided by the client
r, w int // buf read and write positions
err error
lastByte int // last byte read for UnreadByte; -1 means invalid
lastRuneSize int // size of last rune read for UnreadRune; -1 means invalid
}
lastByte和lastRuneSize分别是上一次读取到的字节或者rune,-1表示上一次读取的是无效的字节或者rune,也就是说,如果上一次对这个结构体的实例不是读取,那么他们的值就会被赋值为-1,例如上一次读取的是一个字节,那么lastByte的值就是读取的字节,然而lastRuneSize就是-1,同理,例如Peek方法既不读取byte,也不读取rune,那么他们两个的值就都会赋值为-1
附加知识点
如果一个结构体中的某个成员是一个接口类型,那么初始化这个结构体的时候,需要一个实现了该接口的类型实例来初始化这个成员
NewReaderSize(rd io.Reader,size int) *Reader
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比最小缓冲区大小还小,就修改size大小,并且重新创建一个具有size大小的缓冲区。
NewReader(rd io.Reader) *Reader
func NewReader(rd io.Reader) *Reader {
return NewReaderSize(rd, defaultBufSize)
}
这个函数直接返回一个具有默认大小的缓冲区变量
上面初始化完成以后,就是结构体Reader结构体的方法了
Reader的方法
(b *Reader) Size() int
func (b *Reader) Size() int { return len(b.buf) }
这个方法的作用是返回缓冲区的大小
(b *Reader) Reset(r io.Reader)
func (b *Reader) Reset(r io.Reader) {
b.reset(b.buf, r)
}
这个方法不改变当前缓冲区的大小,但是丢弃缓冲区中的数据,将读和写的位置都初始化为0
(b *Reader) Peek(n int) ([]byte, error)
func (b *Reader) Peek(n int) ([]byte, error) {
if n < 0 {
return nil, ErrNegativeCount
}
b.lastByte = -1
b.lastRuneSize = -1
// 如果可读的数据少于n,并且缓冲区没有填满,并且没有错误时,就在底层内存中填入向缓冲区中填入足够的数据。是0?或者随机数?
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大于缓冲区的长度,那么就将缓冲区所有数据读取出来
if n > len(b.buf) {
return b.buf[b.r:b.w], ErrBufferFull
}
// 0 <= n <= len(b.buf)
var err error
// 缓冲区够大,但是可读数据不够,就返回所有的可读数据
if avail := b.w - b.r; avail < n {
// not enough data in buffer
n = avail
// 这个readErr返回b.err
err = b.readErr()
if err == nil {
err = ErrBufferFull
}
}
// 以切片形式返回数据
return b.buf[b.r : b.r+n], err
}
Peek方法的作用就是在不改变缓冲区中的读和写的位置的前提下,返回从读的位置开始的后面n个字节的数据。如果缓冲区的数据不够,那么就返回返回缓冲区中所有的可读数据。
(b *Reader) Discard(n int) (discarded int, err error)
func (b *Reader) Discard(n int) (discarded int, err error) {
if n < 0 {
return 0, ErrNegativeCount
}
if n == 0 {
return
}
// 初始情况是剩余n字节数据需要丢弃
remain := n
for {
// 获取当前缓冲区中可以读取的数据长度
skip := b.Buffered()
if skip == 0 {
// 如果可读数据为0,就向缓冲区中填充数据
b.fill()
skip = b.Buffered()
}
// 填充后缓冲区可读数据长度如果大于剩余丢弃长度,就只需要丢弃剩余长度的数据就可以了
if skip > remain {
skip = remain
}
// 丢弃就是将读取的位置向后移一定数量
b.r += skip
// 更新剩余丢弃长度
remain -= skip
if remain == 0 {
return n, nil
}
// 这里会出错的情况是在调用fill()方法向缓冲区填充数据数据的时候有可能会出错
if b.err != nil {
return n - remain, b.readErr()
}
}
}
Discard方法的作用是丢弃缓冲区中n字节的数据,不管缓冲区中数据是否足够n,都要丢弃n字节,如果不够n字节就调用fill()方法强行向缓冲区填充数据。
(b *Reader) Read(p []byte) (n int, err error)
func (b *Reader) Read(p []byte) (n int, err error) {
n = len(p)
// 目的数组p的长度为0,就返回读取数据0字节
if n == 0 {
// 本来缓冲区中的可读数据也是0
if b.Buffered() > 0 {
return 0, nil
}
return 0, b.readErr()
}
// 读写的位置重叠,也就是缓冲区中可读的数据为0
if b.r == b.w {
if b.err != nil {
return 0, b.readErr()
}
// 目的数组长度大于缓冲区的长度的情况下
if len(p) >= len(b.buf) {
// Large read, empty buffer.
// Read directly into p to avoid copy.
// 调用Reader的参数实现的Read方法强行从缓冲区中读取数据到数组p中
n, b.err = b.rd.Read(p)
if n < 0 {
panic(errNegativeRead)
}
// 读取成功
if n > 0 {
// 记录读取到的最后一个字节
b.lastByte = int(p[n-1])
b.lastRuneSize = -1
}
return n, b.readErr()
}
// One read.
// Do not use b.fill, which will loop.
// 目的缓冲区的长度小于缓冲区的长度
b.r = 0
b.w = 0
// 注意此时缓冲区中的可读数据是为0的,此时是强行向缓冲区中填入数据
n, b.err = b.rd.Read(b.buf)
if n < 0 {
panic(errNegativeRead)
}
if n == 0 {
return 0, b.readErr()
}
// 记录此时填写数据的地方,所以此时缓冲区中的可读取的数据就不为0了
b.w += n
}
// copy as much as we can
// 将缓冲区中的可读取数据copy到目的数组中,如果p的长度更长,那么就将缓冲区的所有数据都读到p中,如果p的长度更短,那么就将p读满就结束了
n = copy(p, b.buf[b.r:b.w])
// 更新缓冲区读数据的位置
b.r += n
b.lastByte = int(b.buf[b.r-1])
b.lastRuneSize = -1
return n, nil
}
总之Read方法的作用就是将缓冲区中的数据读出来存入到数组p中(并且是尽可能的读取,哪怕换冲区中的可读数据为0)
(b *Reader) ReadByte() (byte, error)
func (b *Reader) ReadByte() (byte, error) {
b.lastRuneSize = -1
// 如果缓冲区为空,调用fill()方法强行向缓冲区填数据
for b.r == b.w {
if b.err != nil {
return 0, b.readErr()
}
b.fill() // buffer is empty
}
c := b.buf[b.r]
// 后移一位读位置
b.r++
// 修改上一次读取的字节
b.lastByte = int(c)
return c, nil
}
ReadByte从缓冲区中读取一个字节的数据
(b *Reader) UnreadByte() error
func (b *Reader) UnreadByte() error {
// 如果上一次对这个实例的操作不是读取了字节(可以读取多个或者一个字节),或者读位置在开始,但是缓冲区有数据(表明上一次肯定不是读数据)
// 直接返回错误
if b.lastByte < 0 || b.r == 0 && b.w > 0 {
return ErrInvalidUnreadByte
}
// b.r > 0 || b.w == 0
// b.r>0说明了前面肯定有已经读过的数据,lastByte又大于0,说明上一次肯定读取了一定的字节数
if b.r > 0 {
// 直接将读位置向前挪一位,就恢复了上一次读的最后一个字节的数据
b.r--
} else {
// 缓冲区是真的空,因为读的位置和写的位置都是0,但是lastByte大于0,说明后面有数据,直接将写位置向后挪一位
// b.r == 0 && b.w == 0
b.w = 1
}
b.buf[b.r] = byte(b.lastByte)
// 这次对实例的操作既不读byte也不读rune,所以两个成员变量都置为-1
b.lastByte = -1
b.lastRuneSize = -1
return nil
}
UnreadByte方法的作用就是将上一次读取的byte重新写入缓冲区,并且是写到缓冲区的最前面,也就是当前读取位置的前面一个位置,并且将读位置向前挪一位
(b *Reader) ReadRune() (r rune, size int, err error)
func (b *Reader) ReadRune() (r rune, size int, err error) {
// utf8.UTFMax=4
// utf8.FullRune(p []byte)判断给定的切片是否是以合法的utf-8编码开始的,不合法的码值会被转化为长度为1的错误码值而视为完整的
// 当读写的位置相距小于4,并且不是以合法的utf8编码开始,并且错误信息为空,并且缓冲区没有填满,就用fill()向缓冲区填数据
// 讲道理,我没有弄明白utf8.FullRune()这个方法的作用,哈哈哈,先就这样吧
for b.r+utf8.UTFMax > b.w && !utf8.FullRune(b.buf[b.r:b.w]) && b.err == nil && b.w-b.r < len(b.buf) {
b.fill() // b.w-b.r < len(buf) => buffer is not full
}
b.lastRuneSize = -1
// 缓冲区可读数据为空
// 此时b.err!=nil
if b.r == b.w {
return 0, 0, b.readErr()
}
// 将第一个字节的数据转化为rune类型
r, size = rune(b.buf[b.r]), 1
// utf8.RuneSelf=0x80
// 当某个值小于0x80,那么这个值就是一个单个的字节,并且表示自身的值,也就是说不能按照unicode编码来解码
if r >= utf8.RuneSelf {
// 这个值大于0x80,所以解码为unicode值
r, size = utf8.DecodeRune(b.buf[b.r:b.w])
}
b.r += size
// 记录读取的最后一个字节的数据和unicode值的长度
b.lastByte = int(b.buf[b.r-1])
b.lastRuneSize = size
return r, size, nil
}
读取一个rune值,返回这个rune类型中的unicode值的长度和值,当缓冲区中第一个字节的utf8编码不合法,就读取一个字节,这个字节就表示他自身的值(小于0x80)
(b *Reader) UnreadRune() error
func (b *Reader) UnreadRune() error {
// 这个条件判断就一定让上一次对本实例的操作是读取了一个rune,并且最新读取位置一定是大于或者等于那个rune的长度,当从0开始读rune,等号成立
if b.lastRuneSize < 0 || b.r < b.lastRuneSize {
return ErrInvalidUnreadRune
}
// 直接将读坐标回溯
b.r -= b.lastRuneSize
b.lastByte = -1
b.lastRuneSize = -1
return nil
}
当上一次对本实例的操作是读取了一个rune类型的值,那么本次操作就是回溯这个值,将他重新添加在缓冲区的读坐标的前面。
(b *Reader) Buffered() int
func (b *Reader) Buffered() int { return b.w - b.r }
这个函数的作用是返回当前缓冲区中能够读取的数据,b.w是缓冲区中当前写入数据的地方,b.r是缓冲区中前一次读取数据的地方
(b *Reader) ReadSlice(delim byte) (line []byte, err error)
func (b *Reader) ReadSlice(delim byte) (line []byte, err error) {
s := 0 // search start index
for {
// Search buffer.
// bytes.IndexByte()返回delim在b.buf[b.r+s:b.w]中的最先出现的位置,如果不存在这个位置就返回-1
if i := bytes.IndexByte(b.buf[b.r+s:b.w], delim); i >= 0 {
// 第一次就找到这个位置,然后就直接结束这个循环,并且将结果存入了line
i += s
line = b.buf[b.r : b.r+i+1]
b.r += i + 1
break
}
// Pending error?
// 没有找到我们设置的中断切片字符
if b.err != nil {
// 读出缓冲区中所有可读取字符
line = b.buf[b.r:b.w]
b.r = b.w
err = b.readErr()
break
}
// Buffer full?
if b.Buffered() >= len(b.buf) {
b.r = b.w
// 直接返回整个缓冲区
line = b.buf
err = ErrBufferFull
break
}
s = b.w - b.r // do not rescan area we scanned before
// 上面这个s的作用是在使用bytes.IndexByte()方法检索字符的时候,只检索fill()方法增加的字符,而不检索之前检索过的字符
b.fill() // buffer is not full
}
// Handle last byte, if any.
// 更新实例的两个成员变量的值
if i := len(line) - 1; i >= 0 {
b.lastByte = int(line[i])
b.lastRuneSize = -1
}
return
}
ReadSlice方法的作用是返回缓冲区的第一个切片,切片的结尾是我们传入的byte参数,如果缓冲区中不存在我们传入的参数,那么就会调用fill()方法向缓冲区中填入数据。然后重新读取,直到出现我们传入的参数或者是出现了错误
(b *Reader) ReadLine() (line []byte, isPrefix bool, err error)
func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error) {
// 调用ReadSlice,以\n为标志读取一个切片
line, err = b.ReadSlice('\n')
// 参考ReadSlice方法,出现这个错误的原因是可读数据的长度超出了缓冲区的长度
if err == ErrBufferFull {
// Handle the case where "\r\n" straddles the buffer.
// 此时切片的结束字符就不一定是指定的\n,所以当结束是字符是\r时,就回溯一位,让\r和\n同时被读取
if len(line) > 0 && line[len(line)-1] == '\r' {
// Put the '\r' back on buf and drop it from line.
// Let the next call to ReadLine check for "\r\n".
if b.r == 0 {
// should be unreachable
panic("bufio: tried to rewind past start of buffer")
}
b.r--
line = line[:len(line)-1]
}
return line, true, nil
}
if len(line) == 0 {
if err != nil {
line = nil
}
return
}
err = nil
// 如果结束字符是\n,那么如果前一个字符是\r,就回溯两个字符,如果不是,就回溯一个字符
if line[len(line)-1] == '\n' {
drop := 1
if len(line) > 1 && line[len(line)-2] == '\r' {
drop = 2
}
line = line[:len(line)-drop]
}
return
}
ReadLine()方法从缓冲区中读取一行字符,遇到\n或者\r\n结束,但是读取出来的字符切片中不包含换行符
(b *Reader) ReadBytes(delim byte) ([]byte, error)
func (b *Reader) ReadBytes(delim byte) ([]byte, error) {
// Use ReadSlice to look for array,
// accumulating full buffers.
var frag []byte
var full [][]byte
var err error
for {
var e error
// 使用ReadSlice()将数据读到切片里面
frag, e = b.ReadSlice(delim)
// 读取没有出错,说明读到delim了,读取已经结束了,所以结束这个循环
if e == nil { // got final fragment
break
}
// 如果不是缓冲区满这个错误,也结束这个循环,说明遇到了其他非正常的错误,需要结束读取数据了
if e != ErrBufferFull { // unexpected error
err = e
break
}
// Make a copy of the buffer.
// 将当前读到的数据存入二维切片中
buf := make([]byte, len(frag))
copy(buf, frag)
full = append(full, buf)
}
// Allocate new buffer to hold the full pieces and the fragment.
// 这是真的读完了,那么就将二维切片和最后的切片中的数据整合成为一个整个的切片
n := 0
for i := range full {
n += len(full[i])
}
n += len(frag)
// Copy full pieces and fragment in.
buf := make([]byte, n)
n = 0
for i := range full {
// copy返回复制的字节数
n += copy(buf[n:], full[i])
}
copy(buf[n:], frag)
return buf, err
}
ReadBytes方法返回缓冲区中直到参数指定的字符的字符串切片。如果遇到错误就返回啦。其中io.EOF是正常的错误。
(b *Reader) ReadString(delim byte) (string, error)
func (b *Reader) ReadString(delim byte) (string, error) {
bytes, err := b.ReadBytes(delim)
return string(bytes), err
}
这个方法没什么说的,就是结果将[]byte转化为string
(b *Reader) WriteTo(w io.Writer) (n int64, err error)
func (b *Reader) WriteTo(w io.Writer) (n int64, err error) {
// 这个b.writeBuf()方法的作用是将缓冲区b中的数据写入w中,并且返回写入的字节数n,而且将b的独白位置向后移动n位
n, err = b.writeBuf(w)
if err != nil {
return
}
// 如果b.rd是一个实现了io.WriteTo接口的类型,那么就用WriteTo方法向w中写入数据
if r, ok := b.rd.(io.WriterTo); ok {
m, err := r.WriteTo(w)
n += m
return n, err
}
// 如果b.rd是一个实现了io.ReaderFrom接口的类型,那么就用ReadFrom方法向w中写数据
if w, ok := w.(io.ReaderFrom); ok {
m, err := w.ReadFrom(b.rd)
n += m
return n, err
}
// 缓冲区不满,就用fill()强行往缓冲区中写入数据
if b.w-b.r < len(b.buf) {
b.fill() // buffer not full
}
// 如果缓冲区不空,先用writeBuf向w中写数据,直到遇到io.EOF
// 也就是这个缓冲区中数据一定要读完呗,直到底层内存中也没有数据读
for b.r < b.w {
// b.r < b.w => buffer is not empty
m, err := b.writeBuf(w)
n += m
if err != nil {
return n, err
}
b.fill() // buffer is empty
}
if b.err == io.EOF {
b.err = nil
}
return n, b.readErr()
}
WriteTo这个方法的作用就是将缓冲区包括缓冲区底层的数据全部读取出来存入参数w中
type Writer struct {
err error//错误信息
buf []byte//缓冲区
n int//缓冲区中数据的字节数
wr io.Writer//下层的io.Writer接口
}
Writer的方法
NewWriterSize(w io.Writer, size int) *Writer
func NewWriterSize(w io.Writer, size int) *Writer {
// Is it already a Writer?
// 通过接口的动态类型判断,是否已经是一个Writer了
b, ok := w.(*Writer)
if ok && len(b.buf) >= size {
return b
}
if size <= 0 {
size = defaultBufSize
}
return &Writer{
buf: make([]byte, size),
wr: w,
}
}
NewWriterSize这个方法根据参数size创建一个大小为size的写入数据缓冲区
NewWriter(w io.Writer) *Writer
func NewWriter(w io.Writer) *Writer {
return NewWriterSize(w, defaultBufSize)
}
NewWriter方法按照默认缓冲区大小创建一个Writer
(b *Writer) Size() int
func (b *Writer) Size() int { return len(b.buf) }
返回缓冲区大小
(b *Writer) Reset(w io.Writer)
func (b *Writer) Reset(w io.Writer) {
b.err = nil// 清除错误
b.n = 0 // 丢弃数据
b.wr = w // 将输出写入w,w是用来写入缓冲区中的数据的
}
Reset丢弃缓冲区中的所有数据,清除所有的错误,将b重设为将其输出写入w
(b *Writer) Flush() error
func (b *Writer) Flush() error {
// 首先判断缓冲区中是否有错误信息
if b.err != nil {
return b.err
}
// 缓冲区中数据为空
if b.n == 0 {
return nil
}
// 向下层io.Writer接口写入数据
n, err := b.wr.Write(b.buf[0:b.n])
// 写入没有出错,但是写入的字节数小于缓冲区中的字节数
if n < b.n && err == nil {
// 错误信息是Short Writer
err = io.ErrShortWrite
}
// 向下层io.Writer接口写入数据出错
if err != nil {
// 缓冲区中的数据没有写出去完,那么将缓冲区中的剩余数据移到缓冲区的最前面来
if n > 0 && n < b.n {
copy(b.buf[0:b.n-n], b.buf[n:b.n])
}
// 更改缓冲区中的数据字节数
b.n -= n
b.err = err
return err
}
// 写入成功,缓冲区中的数据全部写出去了,设置缓冲区中数据量为0
b.n = 0
return nil
}
Flush方法将缓冲区中的数据写入下层的io.Writer接口
(b *Writer) Available() int
// 缓冲区长度减去缓冲区中现存的数据量,就是缓冲区没有使用的字节数
func (b *Writer) Available() int { return len(b.buf) - b.n }
Available方法返回缓冲区中没有使用的字节数
(b *Writer) Buffered() int
func (b *Writer) Buffered() int { return b.n }
Buffered方法返回缓冲区中数据的字节数
(b *Writer) Write(p []byte) (nn int, err error)
func (b *Writer) Write(p []byte) (nn int, err error) {
// 如果不存在错误,并且p中的数据量大于缓冲区可以获取的内存空间,将数据写入底层io.Writer接口
for len(p) > b.Available() && b.err == nil {
var n int
// 缓冲区中本来就没有数据
if b.Buffered() == 0 {
// Large write, empty buffer.
// Write directly from p to avoid copy.
// 直接向底层io.Writer接口写数据
n, b.err = b.wr.Write(p)
} else {
// 先将p的数据写入缓冲区,记录写入的数据量
n = copy(b.buf[b.n:], p)
b.n += n
// 调用Flush方法将数据写入底层io.Writer
b.Flush()
}
// 累加写入数据的量
nn += n
// 重新切片P
p = p[n:]
}
if b.err != nil {
return nn, b.err
}
// 剩余数据写入缓冲区
n := copy(b.buf[b.n:], p)
// 记录数据量的大小
b.n += n
// 累加写入的数据量
nn += n
return nn, nil
}
Write方法的思想是如果p中的数据不能填充满缓冲区剩余的空间,那么就将p中的数据写入缓冲区,如果p中的数据量大于缓冲区中剩余的空间,那么就直接调用底层的io.Writer接口,将数据写入这个接口,然后可能p中会有剩余的内容,如果剩余内容的量还是大于缓冲区可以获取的空间,那么就继续向底层的io.Writer写入数据,如果剩余的数据量不比缓冲区可以获取的空间大,那么就将数据写入缓冲区就可以了,但是这个方法返回的写入的数据量是包括写入到底层io.writer接口的数据量的
(b *Writer) WriteByte(c byte) error
func (b *Writer) WriteByte(c byte) error {
// 惯例先判断是否存在错误
if b.err != nil {
return b.err
}
// 如果缓冲区满了并且不能将缓冲区中的数据写入底层的io.Writer接口,那么就报错啦
if b.Available() <= 0 && b.Flush() != nil {
return b.err
}
// 写入一个字节
b.buf[b.n] = c
// 缓冲区中数据量加1
b.n++
return nil
}
WriteByte向缓冲区中写入一个byte的数据
(b *Writer) WriteRune(r rune) (size int, err error)
func (b *Writer) WriteRune(r rune) (size int, err error) {
// 小于utf8.RuneSelf的都是单个字节,在unicode编码中表示他自己,所以直接写入单个字节的数据即可
if r < utf8.RuneSelf {
// 调用WriteByte()方法
err = b.WriteByte(byte(r))
if err != nil {
return 0, err
}
return 1, nil
}
if b.err != nil {
return 0, b.err
}
// 获取可获取的缓冲区长度
n := b.Available()
// utf8.UTFMax表示unicode编码的最长字节数,值为4
// 可获取的缓冲区长度字节数小于4,就将缓冲区中的数据写入底层的io.Writer接口
if n < utf8.UTFMax {
if b.Flush(); b.err != nil {
return 0, b.err
}
// 重新获取长度
n = b.Available()
if n < utf8.UTFMax {
// Can only happen if buffer is silly small.
// 调用b.WriteString()方法写入数据,这个方法将会在下面讲到
return b.WriteString(string(r))
}
}
// 将数据r转化为utf8编码,并存入缓冲区中,存储的开始位置是b.n,返回编码后的字节数
size = utf8.EncodeRune(b.buf[b.n:], r)
// 更新缓冲区中的数据的字节数
b.n += size
return size, nil
}
WriteRune写入一个unicode码值,并且返回字节数和可能的错误
(b *Writer) WriteString(s string) (int, error)
func (b *Writer) WriteString(s string) (int, error) {
nn := 0
// 当字符串的长度大于可获取的缓冲区长度,并且错误信息为nil,那么就将前面的数据通过Flush()方法写入底层io.Writer接口
// 然后重新获取字符串的长度和缓冲区的长度
for len(s) > b.Available() && b.err == nil {
n := copy(b.buf[b.n:], s)
// 强行将s的数据拷贝进缓冲区,并且记录当前缓冲区中的数据量
b.n += n
// 累加已经写入数据的长度
nn += n
// 更新字符串
s = s[n:]
// 将当前缓冲区的数据写入底层io.Writer接口,这里会改变b.n的值
b.Flush()
}
if b.err != nil {
return nn, b.err
}
// 此时缓冲区可获取的长度大于了字符串剩余数据的长度,直接将数据写入缓冲区即可
n := copy(b.buf[b.n:], s)
b.n += n
nn += n
return nn, nil
}
WriteString方法向缓冲区中写入一个字符串,返回写入的字节数,如果返回的字节数小于字符串的长度,还会返回一个错误原因
(b *Writer) ReadFrom(r io.Reader) (n int64, err error)
func (b *Writer) ReadFrom(r io.Reader) (n int64, err error) {
// 缓冲区b中没有数据
if b.Buffered() == 0 {
// 通过接口的动态类型判定是否在底层已经实现了io.Writer接口
// 如果实现了,那么就直接调用底层ReadFrom接口的方法将读缓冲区r的数据写入写缓冲区b中
if w, ok := b.wr.(io.ReaderFrom); ok {
return w.ReadFrom(r)
}
}
var m int
// 开始循环
for {
// b中没有可获取的缓冲区空间
if b.Available() == 0 {
// 将写缓冲区b中的数据写入底层io.Writer接口
if err1 := b.Flush(); err1 != nil {
// 如果出错,此时n是0,表示没有数据从读缓冲区写入写缓冲区
return n, err1
}
}
nr := 0
// maxConsecutiveEmptyReads的值为100,表示每次从内存中读取空的数据的次数不能超过100次
for nr < maxConsecutiveEmptyReads {
// 调用r的Read方法将读缓冲区中的数据一直循环读入写缓冲区
// 如果读到的数据为空,或者没有出现读错误,就循环读取,最多100次
m, err = r.Read(b.buf[b.n:])
if m != 0 || err != nil {
break
}
nr++
}
// 读了100次还是没有数据呢,但是也没报错
if nr == maxConsecutiveEmptyReads {
return n, io.ErrNoProgress
}
// 到这一步说明了要么读出了数据,要么出现了错误
// 此时字节数为m的数据已经写入写缓冲区中了
// 所以修改b.n的值
b.n += m
// 累加已经从读缓冲区中写入的值的字节数
n += int64(m)
// 如果遇到了错误,还要结束大循环
if err != nil {
break
}
}
// 有可能错误不是真正的错误,而是文件读完了
if err == io.EOF {
// If we filled the buffer exactly, flush preemptively.
// 如果此时写缓冲区已经被填满了,就将数据写入底层io.Writer接口
if b.Available() == 0 {
err = b.Flush()
} else {
err = nil
}
}
return n, err
}
ReadFrom实现了io.ReadFrom接口
type ReadWriter struct {
*Reader
*Writer
}
NewReadWriter(r *Reader, w *Writer) *ReadWriter
func NewReadWriter(r *Reader, w *Writer) *ReadWriter {
return &ReadWriter{r, w}
}
NewReadWriter申请创建一个新的、将读写操作分派给r和w 的ReadWriter。
结构体Scanner的初始化
type Scanner struct {
// 底层的io.Reader接口,Scanner从这接口读取数据
r io.Reader // The reader provided by the client.
// 分割函数
split SplitFunc // The function to split the tokens.
// token的最大值
maxTokenSize int // Maximum size of a token; modified by tests.
// 用来存储缓冲区中即将被使用的数据
token []byte // Last token returned by split.
// 缓冲区
buf []byte // Buffer used as argument to split.
// 缓冲区中第一个没有被处理的字节
start int // First non-processed byte in buf.
// 缓冲区中的数据的最后一个字节的位置,start和end类似于Reader中的读指针和写指针
end int // End of data in buf.
// 记录错误
err error // Sticky error.
// 记录从缓冲区中读出空token的次数
empties int // Count of successive empty tokens.
// 如果已经调用了Scan这个函数,那么说明它的缓冲区已经正在被使用了
scanCalled bool // Scan has been called; buffer is in use.
//Scan 向buf中写数据是否已经完毕
done bool // Scan has finished.
}
Scanner的方法
NewScanner(r io.Reader) *Scanner
func NewScanner(r io.Reader) *Scanner {
return &Scanner{
r: r,
split: ScanLines,
maxTokenSize: MaxScanTokenSize,
}
}
NewScanner创建一个从r读取数据的Scanner,默认的分割函数是ScanLines
(s *Scanner) Err() error
func (s *Scanner) Err() error {
if s.err == io.EOF {
return nil
}
return s.err
}
Err方法返回Scanner的非io.EOF错误
(s *Scanner) Bytes() []byte
func (s *Scanner) Bytes() []byte {
return s.token
}
Bytes方法返回数据成员token,是最近一次使用Scanner生成的token,token是会随着调用Scanner而一直变化的
(s *Scanner) Text() string
func (s *Scanner) Text() string {
return string(s.token)
}
Text方法以string类型返回token
(s *Scanner) Scan() bool
func (s *Scanner) Scan() bool {
// Scan已经完成,不需要再写数据进入Scan的缓冲区了
if s.done {
return false
}
// Scan已经被调用了,它的缓冲区正在被使用
s.scanCalled = true
// Loop until we have a token.
// 进入循环
for {
// See if we can get a token with what we already have.
// If we've run out of data but have an error, give the split function
// a chance to recover any remaining, possibly empty token.
// s.end>s.start表示缓冲区中有数据
// s.err有可能是一个io.EOF错误,也可能是其他错误
if s.end > s.start || s.err != nil {
// s.split的默认是函数是ScanLines作用是从Scanner的缓冲区读取一行数据,然后返回前进的字节数和保存数据的token
advance, token, err := s.split(s.buf[s.start:s.end], s.err != nil)
if err != nil {
// 如果是最后一个token说明缓冲区中没有数据了,返回正确
if err == ErrFinalToken {
// token中包含缓冲区中的数据
s.token = token
// Scan已经完成
s.done = true
return true
}
// 如果是其他错误,就将他赋值给s.err
s.setErr(err)
return false
}
// s.advance()函数检查前进的字节数是否超出了s.end,并且将s.start向后移动advance个字节
if !s.advance(advance) {
return false
}
// 将返回的token赋值给s.token
s.token = token
// 如果token不为空
if token != nil {
// 没有出现错误或者向token中写入了非空的字节
if s.err == nil || advance > 0 {
// 将记录连续写空token次数的计数器归零
s.empties = 0
} else {
// 出现了错误或者写入了空token,计数器加1
// Returning tokens not advancing input at EOF.
s.empties++
// 如果超出连续写空token的最大次数,就panic了
if s.empties > maxConsecutiveEmptyReads {
panic("bufio.Scan: too many empty tokens without progressing")
}
}
return true
}
}
// We cannot generate a token with what we are holding.
// If we've already hit EOF or an I/O error, we are done.
// 使用s.splict函数不能产生一个token(只要上面产生了空的token并且有错误信息就能进入这个if分支)
if s.err != nil {
// Shut it down.
// 清空数据
s.start = 0
s.end = 0
return false
}
// Must read more data.
// First, shift data to beginning of buffer if there's lots of empty space
// or space is needed.
// s.start>0说明了这个缓冲区已经被使用了,但是如果被使用了很多,就要将缓冲区后面的数据向前移动到缓冲区开始的地方
if s.start > 0 && (s.end == len(s.buf) || s.start > len(s.buf)/2) {
copy(s.buf, s.buf[s.start:s.end])
s.end -= s.start
s.start = 0
}
// Is the buffer full? If so, resize.
// s.start==0,此时缓冲区满了,那么就要扩大缓冲区
if s.end == len(s.buf) {
// Guarantee no overflow in the multiplication below.
// 生成最大的int类型的值
const maxInt = int(^uint(0) >> 1)
// 缓冲区已经不能再扩大了,就返回错误
if len(s.buf) >= s.maxTokenSize || len(s.buf) > maxInt/2 {
s.setErr(ErrTooLong)
return false
}
// 缓冲区变为原来的2倍
newSize := len(s.buf) * 2
if newSize == 0 {
// 如果两倍以后缓冲区大小还是0,就直接使用默认的缓冲区大小
newSize = startBufSize
}
// 如果新的缓冲区大小大于最大的限制,就使用最大的限制大小
if newSize > s.maxTokenSize {
newSize = s.maxTokenSize
}
// 创建新的换冲过去
newBuf := make([]byte, newSize)
// 移植数据,并更新下标等数据成员
copy(newBuf, s.buf[s.start:s.end])
s.buf = newBuf
s.end -= s.start
s.start = 0
}
// Finally we can read some input. Make sure we don't get stuck with
// a misbehaving Reader. Officially we don't need to do this, but let's
// be extra careful: Scanner is for safe, simple jobs.
for loop := 0; ; {
// 将底层Reader接口中的数据读进缓冲区中,以拼接的方式读取
n, err := s.r.Read(s.buf[s.end:len(s.buf)])
s.end += n
if err != nil {
s.setErr(err)
break
}
// 读出了数据,不是空数据,那么空计数器归零,并退出循环
if n > 0 {
s.empties = 0
break
}
// 读到空数据,循环变量加1
loop++
// 连续读出空数据的次数大于了限制次数就报错了
if loop > maxConsecutiveEmptyReads {
s.setErr(io.ErrNoProgress)
break
}
}
}
}
Scan方法从缓冲区中读出一个token存储在s.token中,token的划分是通过分割函数划分,例如SplitLines就是一行数据是一个token
(s *Scanner) Buffer(buf []byte, max int)
func (s *Scanner) Buffer(buf []byte, max int) {
if s.scanCalled {
panic("Buffer called after Scan")
}
s.buf = buf[0:cap(buf)]
// 缓冲区的最大限制
s.maxTokenSize = max
}
Buffer方法在缓冲区被使用前初始化缓冲区,并且设置缓冲区的最大限制
(s *Scanner) Split(split SplitFunc)
func (s *Scanner) Split(split SplitFunc) {
if s.scanCalled {
panic("Split called after Scan")
}
s.split = split
}
Split在缓冲区被使用前,设置缓冲区的分割函数
ScanBytes(data []byte, atEOF bool) (advance int, token []byte, err error)
func ScanBytes(data []byte, atEOF bool) (advance int, token []byte, err error) {
if atEOF && len(data) == 0 {
return 0, nil, nil
}
return 1, data[0:1], nil
}
ScanBytes函数按照字节分割,每次分割一个字节
ScanRunes(data []byte, atEOF bool) (advance int, token []byte, err error)
func ScanRunes(data []byte, atEOF bool) (advance int, token []byte, err error) {
// 没有数据,且到数据最后一行
if atEOF && len(data) == 0 {
return 0, nil, nil
}
// Fast path 1: ASCII.
// 小于urf8.Runeself的表示他们自己,只有一个字节
if data[0] < utf8.RuneSelf {
return 1, data[0:1], nil
}
// Fast path 2: Correct UTF-8 decode without error.
// 使用utf8解码,获取解码的原始[]byte长度
_, width := utf8.DecodeRune(data)
if width > 1 {
// It's a valid encoding. Width cannot be one for a correctly encoded
// non-ASCII rune.
// 解码成功,返回一个utf8编码的[]byte
return width, data[0:width], nil
}
// We know it's an error: we have width==1 and implicitly r==utf8.RuneError.
// Is the error because there wasn't a full rune to be decoded?
// FullRune distinguishes correctly between erroneous and incomplete encodings.
// utf8.FullRune()说的是不是一个完整的Rune编码,没弄懂
if !atEOF && !utf8.FullRune(data) {
// Incomplete; get more bytes.
return 0, nil, nil
}
// We have a real UTF-8 encoding error. Return a properly encoded error rune
// but advance only one byte. This matches the behavior of a range loop over
// an incorrectly encoded string.
// 真的遇到错误,前进一个字节
return 1, errorRune, nil
}
ScanRunes按照unicode编码分割,每次分割一个unicode编码
ScanLines(data []byte, atEOF bool) (advance int, token []byte, err error)
func ScanLines(data []byte, atEOF bool) (advance int, token []byte, err error) {
// atEOF是一个标识Reader是否还有数据(是不是最后一行)的标志
// 如果是最后一行,并且最后一行没有数据
if atEOF && len(data) == 0 {
return 0, nil, nil
}
// 在数据data中找到了`\n`,那么就将`\n`之前的数据作为token返回
if i := bytes.IndexByte(data, '\n'); i >= 0 {
// We have a full newline-terminated line.
// dropCR函数的作用是去掉[]byte末尾的`\r`(回车)
// token向前移动了i+1个字节(包含了\r\n)
return i + 1, dropCR(data[0:i]), nil
}
// If we're at EOF, we have a final, non-terminated line. Return it.
// 到了数据的最后一行,但是有数据,就将数据末尾的`\r`去掉,直接返回数据
if atEOF {
return len(data), dropCR(data), nil
}
// Request more data.
// 直接就是没有数据,也不是文件末尾标识符
return 0, nil, nil
}
ScanLines会将每一行文本去掉末尾换行符然后作为一个token返回,并且返回的行可以是空串,最后一行即使没有换行符也要作为一个token返回
ScanWords(data []byte, atEOF bool) (advance int, token []byte, err error)
func ScanWords(data []byte, atEOF bool) (advance int, token []byte, err error) {
// Skip leading spaces.
start := 0
for width := 0; start < len(data); start += width {
var r rune
r, width = utf8.DecodeRune(data[start:])
if !isSpace(r) {
break
}
}
// Scan until space, marking end of word.
for width, i := 0, start; i < len(data); i += width {
var r rune
r, width = utf8.DecodeRune(data[i:])
// isSpace函数检查r是不是一个unicode的空字符,unicode的空字符挺多的,可以去看看
if isSpace(r) {
// 前进的字节数包含了空字符,但是返回的token中不包含空字符哟
return i + width, data[start:i], nil
}
}
// If we're at EOF, we have a final, non-empty, non-terminated word. Return it.
// 在数据最后一行,并且最后一行数据不为空,直接返回数据即可
if atEOF && len(data) > start {
return len(data), data[start:], nil
}
// Request more data.
// 参数data中没有数据,返回0,nil,nil
return start, nil, nil
}
ScanWords以空白分割,所以永远不会返回一个空的token
浙公网安备 33010602011771号