Go源码学习:bufio包 - 1.1 - bufio.go -(1)

bufio包官方文档

Go源码学习-索引目录

一、bufio包的作用

bufio 包是 Go 语言标准库中提供的一个缓冲 I/O 功能的工具包。它主要用于提供对 I/O 操作的缓冲支持,可以帮助提升 I/O 操作的性能和效率。

bufio 包提供了一组功能强大的工具,使得对数据的读取和写入变得更加高效、灵活,并且可以减少对系统资源的频繁占用,特别适用于处理大量数据或需要高性能的 I/O 操作场景。

具体来说,bufio 包提供了如下功能和作用:

  1. 缓冲读取(Buffered Reading)

    • bufio 包通过 Reader 类型提供了缓冲读取的功能,可以减少对底层数据源(比如文件、网络连接等)的实际读取次数,从而提高读取性能。
  2. 缓冲写入(Buffered Writing)

    • 通过 Writer 类型,bufio 包支持缓冲写入,将数据暂时存储在内存中,减少对底层数据目标的实际写入次数,提高写入性能。
  3. 高效的字节处理(Efficient Byte Handling)

    • 提供了对字节的高效处理方法,比如按字节读取和写入,以及其他基于字节的操作,这对于处理二进制数据非常有用。
  4. 行扫描(Line Scanning)

    • bufio.Scanner 类型可以方便地对文本进行分割,支持按行扫描,用户可以定义自己的分割函数,方便地处理各种文本格式。
  5. 灵活性和性能提升

    • 通过在内存中缓冲数据,减少了系统调用的次数,从而提高了 I/O 操作的效率和性能。

二、bufio.go

1、bufio 包的目的和基本结构

// Package bufio 实现了带缓冲的 I/O 操作。它封装了一个 io.Reader 或 io.Writer
// 对象,创建了另一个实现相同接口的对象(Reader 或 Writer),但提供了缓冲和
// 一些文本 I/O 的辅助功能。

package bufio

import (
	"bytes"
	"errors"
	"io"
	"strings"
	"unicode/utf8"
)

// 默认缓冲区大小
const (
	defaultBufSize = 4096
)

var (
	// ErrInvalidUnreadByte 表示对 UnreadByte 的无效使用
	ErrInvalidUnreadByte = errors.New("bufio: invalid use of UnreadByte")
	// ErrInvalidUnreadRune 表示对 UnreadRune 的无效使用
	ErrInvalidUnreadRune = errors.New("bufio: invalid use of UnreadRune")
	// ErrBufferFull 表示缓冲区已满
	ErrBufferFull        = errors.New("bufio: buffer full")
	// ErrNegativeCount 表示计数为负值
	ErrNegativeCount     = errors.New("bufio: negative count")
)

// 缓冲输入。

// Reader 实现了对 io.Reader 对象的缓冲。它包装了一个由客户端提供的 io.Reader,
// 创建了另一个对象(Reader),该对象同样实现了接口,但提供了缓冲和一些文本 I/O 的帮助。
type Reader struct {
    
    
	buf          []byte    // 缓冲区
	rd           io.Reader // 客户端提供的 reader
	r, w         int       // buf 的读写位置
	err          error     // 错误信息
	lastByte     int       // 上一次读取的字节,用于 UnreadByte;-1 表示无效
	lastRuneSize int       // 上一次读取的 rune 大小,用于 UnreadRune;-1 表示无效
}

// 最小读取缓冲区大小
const minReadBufferSize = 16
// 最大连续空读取次数
const maxConsecutiveEmptyReads = 100

// NewReaderSize 返回一个新的 [Reader],其缓冲区大小至少为指定大小。如果参数 io.Reader
// 已经是一个拥有足够大大小的 [Reader],则返回底层 [Reader]。
func NewReaderSize(rd io.Reader, size int) *Reader {
    
    
	// 它已经是一个 Reader 吗?
	b, ok := rd.(*Reader)
	if ok && len(b.buf) >= size {
    
    
		return b
	}
	r := new(Reader)
	r.reset(make([]byte, max(size, minReadBufferSize)), rd)
	return r
}

// NewReader 返回一个新的 [Reader],其缓冲区大小为默认大小。
func NewReader(rd io.Reader) *Reader {
    
    
	return NewReaderSize(rd, defaultBufSize)
}

上述注释解释了 bufio 包的目的和基本结构。

更详细的解释:

  • bufio 包实现了带缓冲的 I/O 操作,用于提高 I/O 操作的性能和效率。
  • Reader 结构体是对 io.Reader 对象的缓冲实现。它包含一个缓冲区 buf、一个客户端提供的 io.Reader 对象 rd、以及读写位置和错误信息等字段。
  • const 声明了一些常量,如默认缓冲区大小、最小读取缓冲区大小和最大连续空读取次数。
  • var 声明了一些错误变量,用于表示在使用 Reader 过程中可能发生的错误情况。
  • NewReaderSize 函数返回一个新的 Reader,其缓冲区大小至少为指定大小。如果参数 io.Reader 已经是一个拥有足够大大小的 Reader,则返回底层的 Reader
  • NewReader 函数返回一个新的 Reader,其缓冲区大小为默认大小。

2、Size:返回底层缓冲区的大小

这部分代码定义了 Size 方法,该方法用于返回 Reader 结构体中底层缓冲区的大小(以字节为单位)。

// Size 返回底层缓冲区的大小(以字节为单位)。
func (b *Reader) Size() int {
    
     return len(b.buf) }

解释:

  • Size 方法是 Reader 结构体的方法,其接收者是 b,类型是 Reader
  • 方法的主体是 return len(b.buf),它返回 b 结构体中 buf 字段的长度,即底层缓冲区的大小。

作用:

  • 这个方法的作用是提供外部访问 Reader 结构体底层缓冲区大小的接口。

3、Reset:重置缓冲区状态和读取源

这部分代码定义了 Reset 方法,用于重置 Reader 结构体的状态,并切换缓冲区以从新的读取源 r 进行读取。

// Reset 丢弃任何已缓存的数据,重置所有状态,并切换缓冲区以从读取源 r 进行读取。
// 在 [Reader] 的零值上调用 Reset 会将内部缓冲区初始化为默认大小。
// 调用 b.Reset(b)(即将 [Reader] 重置为自身)不会执行任何操作。
func (b *Reader) Reset(r io.Reader) {
    
    
    // 如果将读取源 r 传递给 NewReader,NewReader 将返回 r。
    // 代码的不同层可能会这样做,然后稍后将 r 传递给 Reset。在这种情况下避免无限递归。
    if b == r {
    
    
        return
    }
    // 如果缓冲区为空,则将其初始化为具有默认大小的新字节切片。
    if b.buf == nil {
    
    
        b.buf = make([]byte, defaultBufSize)
    }
    // 调用 reset 方法,将缓冲区、读取源以及其他状态重置。
    b.reset(b.buf, r)
}

解释:

  • Reset 方法是 Reader 结构体的方法,其接收者是 b,类型是 Reader
  • 方法的主体包含了以下几个关键步骤:
    1. 避免无限递归: 如果 b 和新的读取源 r 相同,直接返回,以避免无限递归。
    2. 初始化缓冲区: 如果 buf 字段为 nil,则将其初始化为一个具有默认大小的新字节切片。
    3. 调用重置方法: 调用 reset 方法,将缓冲区、读取源、上一个字节和上一个符文大小等状态重置。
// reset 方法用于具体执行 [Reader] 结构体的重置,将各个字段设置为新的状态。
func (b *Reader) reset(buf []byte, r io.Reader) {
    
    
    *b = Reader{
    
    
        buf:          buf,
        rd:           r,
        lastByte:     -1,
        lastRuneSize: -1,
    }
}

这个内部的 reset 方法用于具体执行 Reader 结构体的重置,将各个字段设置为新的状态。

作用:

  • Reset 方法的作用是在切换读取源或者需要丢弃任何已缓存的数据时,重置 Reader 结构体的状态,以便重新开始读取新的数据。
  • 如果 Reset 方法被调用时传入的 r 与当前的 b 相同,表示正在尝试将 Reader 重置为自身,此时直接返回,不进行任何操作。
  • Reader 是零值时(未使用 NewReader 初始化),调用 Reset 会将内部缓冲区初始化为默认大小。

4、fill:填充缓冲区

这部分代码定义了 fill 方法,用于在缓冲区中读取新的数据块。

var errNegativeRead = errors.New("bufio: reader returned negative count from Read")

// fill 读取一个新的数据块填充到缓冲区中。
func (b *Reader) fill() {
    
    
    // 将现有数据滑动到开头。
    if b.r > 0 {
    
    
        copy(b.buf, b.buf[b.r:b.w])
        b.w -= b.r
        b.r = 0
    }

    // 如果缓冲区已满,则抛出 panic。
    if b.w >= len(b.buf) {
    
    
        panic("bufio: tried to fill full buffer")
    }

    // 读取新数据:尝试有限次数。
    for i := maxConsecutiveEmptyReads; i > 0; i-- {
    
    
        // 从读取源 b.rd 中读取新的数据块到缓冲区中。
        n, err := b.rd.Read(b.buf[b.w:])
        // 如果读取的字节数 n 小于零,抛出 panic,表示读取返回了负数。
        if n < 0 {
    
    
            panic(errNegativeRead)
        }
        // 将写指针 b.w 向前移动,表示成功读取了 n 个字节。
        b.w += n
        // 如果出现错误,将错误存储在 b.err 中,并结束方法。
        if err != nil {
    
    
            b.err = err
            return
        }
        // 如果成功读取了至少一个字节,退出循环。
        if n > 0 {
    
    
            return
        }
    }
    // 如果整个循环完成后仍未读取任何字节,将 b.err 设置为 io.ErrNoProgress。
    b.err = io.ErrNoProgress
}

解释:

  • fill 方法是 Reader 结构体的方法,没有显式的接收者,但它使用了 b 结构体的字段。
  • 首先,如果缓冲区的读指针 b.r 大于零,表示有未读的数据在缓冲区中,将这些数据移动到缓冲区的开头。
  • 然后,检查缓冲区是否已满,如果是,抛出 panic。
  • 接着,通过循环一定次数(maxConsecutiveEmptyReads),尝试从读取源 b.rd 中读取新的数据块到缓冲区中。
    • 如果读取的字节数 n 小于零,抛出 panic,表示读取返回了负数。
    • 将写指针 b.w 向前移动,表示成功读取了 n 个字节。
    • 如果出现错误,将错误存储在 b.err 中,并结束方法。
    • 如果成功读取了至少一个字节,退出循环。
  • 如果整个循环完成后仍未读取任何字节,将 b.err 设置为 io.ErrNoProgress

作用:

  • fill 方法的主要作用是确保缓冲区中始终存在可读取的数据,以提高读取效率。
  • 它会从读取源中读取新的数据块,将数据填充到缓冲区中。
  • 在填充数据之前,会进行一些操作,如将未读取的数据移动到缓冲区开头。

5. Peek:预读取字节

// readErr 返回当前的错误并清空错误状态。
func (b *Reader) readErr() error {
    
    
	err := b.err
	b.err = nil
	return err
}

解释:

  • readErr 方法是 Reader 结构体的方法,没有显式的接收者,但它使用了 b 结构体的字段。
  • 该方法返回当前的错误(b.err)并将错误状态清空。
  • 将当前的错误存储在变量 err 中,然后将 b.err 设置为 nil
  • 最后,返回存储的错误。

作用:

  • 该方法用于获取当前的错误并清空错误状态,以便在需要处理错误时使用。
// Peek 返回下一个 n 个字节而不移动读取器的位置。
// 这些字节在下一次读取调用时不再有效。
// 如果 Peek 返回少于 n 个字节,它还会返回一个解释为为什么读取不完整的错误。
// 如果 n 大于 b 的缓冲区大小,则错误是 [ErrBufferFull]。
//
// 调用 Peek 会阻止 [Reader.UnreadByte] 或 [Reader.UnreadRune] 的调用成功,直到下一次读取操作。
func (b *Reader) Peek(n int) ([]byte, error) {
    
    
	if n < 0 {
    
    
		return nil, ErrNegativeCount
	}

	b.lastByte = -1
	b.lastRuneSize = -1

	// 当缓冲区中剩余数据不足 n 时,且缓冲区未满且没有错误时,调用 fill 方法填充缓冲区。
	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
	}

	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 {
    
    
		// 缓冲区中的数据不足
		n = avail
		err = b.readErr()
		if err == nil {
    
    
			err = ErrBufferFull
		}
	}
	return b.buf[b.r : b.r+n], err
}

解释:

  • Peek 方法是 Reader 结构体的方法,没有显式的接收者,但它使用了 b 结构体的字段。
  • 该方法返回下一个 n 个字节而不移动读取器的位置。
  • 如果 Peek 返回的字节数少于 n 个,它还会返回一个解释为什么读取不完整的错误。
  • 如果 n 大于 b 的缓冲区大小,则错误是 ErrBufferFull
  • 调用 Peek 会阻止 Reader.UnreadByteReader.UnreadRune 的调用成功,直到下一次读取操作。

作用:

  • Peek 方法用于预读取字节,允许查看但不消耗缓冲区中的数据。
  • 如果需要查看接下来的字节而不移动读取位置,可以使用 Peek 方法。

6. Discard:丢弃字节

// Discard 跳过接下来的 n 个字节,并返回丢弃的字节数。

// 如果 Discard 跳过的字节数少于 n 个,它也会返回一个错误。
// 如果 0 <= n <= b.Buffered(),则 Discard 保证在不从底层 io.Reader 读取的情况下成功执行。
func (b *Reader) Discard(n int) (discarded int, err error) {
    
    
	if n < 0 {
    
    
		return 0, ErrNegativeCount
	}
	if n == 0 {
    
    
		return
	}

	b.lastByte = -1
	b.lastRuneSize = -1

	remain := n
	for {
    
    
		skip := b.Buffered()
		if skip == 0 {
    
    
			b.fill()
			skip = b.Buffered()
		}
		if skip > remain {
    
    
			skip = remain
		}
		b.r += skip
		remain -= skip
		if remain == 0 {
    
    
			return n, nil
		}
		if b.err != nil {
    
    
			return n - remain, b.readErr()
		}
	}
}

解释:

  • Discard 方法是 Reader 结构体的方法,没有显式的接收者,但它使用了 b 结构体的字段。
  • 此方法用于跳过接下来的 n 个字节,并返回丢弃的字节数。
  • 如果 Discard 跳过的字节数少于 n 个,它也会返回一个错误。
  • 0 <= n <= b.Buffered() 的情况下,Discard 保证在不从底层 io.Reader 读取的情况下成功执行。

作用:

  • Discard 方法允许跳过指定数量的字节而不读取或返回它们,对于消费数据前不需要的部分是很有用的。
  • 它可以帮助快速丢弃指定数量的字节,而不必将这些字节从底层的 io.Reader 中实际读取出来。

猜你喜欢

转载自blog.csdn.net/weixin_49015143/article/details/135181691
今日推荐