Go源码学习:bytes包 - 1.1 - buffer.go -(3)

bytes包官方文档

Go源码学习-索引目录

上一篇:
Go源码学习:bytes包 - 1.2 - buffer.go -(2)

21、MinRead:传递给 Read 调用的最小切片大小

// MinRead 是由 [Buffer.ReadFrom] 传递给 Read 调用的最小切片大小。
// 只要 [Buffer] 至少有 MinRead 字节超出了持有 r 内容所需的内容,ReadFrom 就不会增长底层缓冲区。

const MinRead = 512

解释:

  • MinRead 是一个常量,表示由 [Buffer.ReadFrom] 传递给 Read 调用的最小切片大小。
  • 该常量用于控制在进行读取操作时,底层缓冲区的最小大小要求。

作用:

  • MinRead 的作用是确保在执行读取操作时,底层缓冲区具有足够的空间来容纳要读取的数据,以提高读取的效率和性能。
  • 通过限定最小切片大小,可以避免在每次读取时频繁地扩展缓冲区的大小,从而提高了读取操作的效率。

22、ReadFrom:从读取器中读取数据并追加到缓冲区中

这部分代码定义了 ReadFrom 方法,用于从读取器 r 中读取数据直到 EOF,并将数据追加到缓冲区中,根据需要扩展缓冲区。返回值 n 是读取的字节数,任何除了 io.EOF 之外的错误在读取过程中也会被返回。如果缓冲区变得太大,ReadFrom 会以 [ErrTooLarge] 引发 panic。

// ReadFrom 从读取器 r 中读取数据直到 EOF,并将数据追加到缓冲区中,根据需要扩展缓冲区。
// 返回值 n 是读取的字节数。任何除了 io.EOF 之外的错误在读取过程中也会被返回。
// 如果缓冲区变得太大,ReadFrom 会以 [ErrTooLarge] 引发 panic。
func (b *Buffer) ReadFrom(r io.Reader) (n int64, err error) {
    b.lastRead = opInvalid
    for {
        // 通过 grow 方法扩展缓冲区,确保有足够的空间来读取数据。
        i := b.grow(MinRead)
        // 调整缓冲区的长度为 i。
        b.buf = b.buf[:i]
        // 从读取器 r 中读取数据到缓冲区中。
        m, e := r.Read(b.buf[i:cap(b.buf)])
        // 如果读取的字节数 m 小于零,抛出 panic,表示读取返回了负数。
        if m < 0 {
            panic(errNegativeRead)
        }

        // 调整缓冲区的长度为 i+m,表示成功读取了 m 个字节。
        b.buf = b.buf[:i+m]
        n += int64(m)
        // 如果遇到了 io.EOF,显式地返回 nil,表示到达了文件末尾。
        if e == io.EOF {
            return n, nil
        }
        // 如果遇到了其他错误,返回读取的字节数 n 和遇到的错误 e。
        if e != nil {
            return n, e
        }
    }
}

解释:

  • ReadFrom 方法是 Buffer 结构体的方法,接收一个读取器 r,并返回读取的字节数和可能遇到的错误。
  • 首先,将 lastRead 字段设置为 opInvalid,表示最近的读取操作无效。
  • 然后,通过循环不断从读取器 r 中读取数据,并将数据追加到缓冲区中。
    • 使用 grow 方法扩展缓冲区,确保有足够的空间来读取数据。
    • 调整缓冲区的长度为 i
    • 从读取器 r 中读取数据到缓冲区中。
    • 如果读取的字节数 m 小于零,抛出 panic,表示读取返回了负数。
    • 调整缓冲区的长度为 i+m,表示成功读取了 m 个字节。
    • 累加读取的字节数到 n 中。
    • 如果遇到了 io.EOF,显式地返回 nil,表示到达了文件末尾。
    • 如果遇到了其他错误,返回读取的字节数 n 和遇到的错误 e

作用:

  • ReadFrom 方法的主要作用是从读取器中读取数据并追加到缓冲区中,实现了一种高效的数据读取方式。
  • 它会根据需要扩展缓冲区,以适应从读取器中读取的数据量。
  • 在读取数据的过程中,会及时处理读取过程中可能遇到的错误,并返回读取的字节数和可能遇到的错误。

23、growSlice:扩展切片

这部分代码定义了 growSlice 函数,用于扩展切片 b 的容量,并保留原始内容。

// growSlice 扩展切片 b 的容量为 n,保留切片 b 的原始内容。
// 如果分配失败,将会以 ErrTooLarge 异常抛出。
func growSlice(b []byte, n int) []byte {
    defer func() {
        if recover() != nil {
            panic(ErrTooLarge)
        }
    }()
    // TODO(http://golang.org/issue/51462):我们应该依赖于追加-创建模式,以便编译器可以调用 runtime.growslice。例如:
    //  return append(b, make([]byte, n)...)
    // 这避免了对分配的切片的前 len(b) 个字节进行不必要的清零,但这种模式会导致切片 b 逃逸到堆上。
    //
    // 取而代之的是,使用带有空切片的追加-创建模式,以确保我们分配的缓冲区大小舍入到最接近的大小类别。
    c := len(b) + n // 确保有足够的空间容纳 n 个元素
    if c < 2*cap(b) {
        // 增长率在历史上一直是 2 倍。将来,我们可以完全依赖追加来确定增长率。
        c = 2 * cap(b)
    }
    b2 := append([]byte(nil), make([]byte, c)...)
    copy(b2, b)
    return b2[:len(b)]
}

解释:

  • growSlice 函数接收一个切片 b 和一个整数 n,用于扩展切片 b 的容量。
  • 在函数内部,使用了 defer 语句,用于在函数返回之前执行一段代码,这里用于捕获可能的异常并以 ErrTooLarge 异常抛出。
  • 首先,计算扩展后的容量 c,确保有足够的空间容纳 n 个元素,并且至少是原始容量的两倍。
  • 接着,使用追加-创建模式,创建一个新的切片 b2,并将原始切片 b 的内容复制到新的切片中。
  • 最后,返回新的切片 b2 的前 len(b) 个元素,即保留原始内容的扩展切片。

作用:

  • growSlice 函数的主要作用是扩展切片的容量,并且保留原始内容,以满足切片容量的需求。
  • 它通过重新分配内存并复制原始内容,实现了切片容量的扩展,同时保留了原始数据的完整性。

24、WriteTo:向目标写入数据

这部分代码定义了 WriteTo 方法,用于将数据写入目标 w,直到缓冲区耗尽或发生错误。

// WriteTo 方法向目标 w 写入数据,直到缓冲区耗尽或发生错误。
// 返回值 n 是写入的字节数;它总是适合 int 类型,但为了匹配 io.WriterTo 接口,它是 int64 类型。
// 在写入过程中遇到的任何错误也将被返回。
func (b *Buffer) WriteTo(w io.Writer) (n int64, err error) {
    b.lastRead = opInvalid
    if nBytes := b.Len(); nBytes > 0 {
        m, e := w.Write(b.buf[b.off:])
        if m > nBytes {
            panic("bytes.Buffer.WriteTo: invalid Write count")
        }
        b.off += m
        n = int64(m)
        if e != nil {
            return n, e
        }
        // 根据 io.Writer 中 Write 方法的定义,所有字节应该已经被写入。
        if m != nBytes {
            return n, io.ErrShortWrite
        }
    }
    // 缓冲区现在为空;重置。
    b.Reset()
    return n, nil
}

解释:

  • WriteTo 方法是 Buffer 结构体的方法,用于将缓冲区中的数据写入目标 w
  • 首先,将 lastRead 标记为 opInvalid,表示最近的读取操作无效。
  • 然后,检查缓冲区中是否有剩余数据(nBytes > 0)。
    • 如果有剩余数据,尝试向目标 w 写入数据,并获取写入的字节数 m 和可能的错误 e
    • 如果写入的字节数 m 大于剩余数据的字节数 nBytes,抛出 panic,表示写入计数无效。
    • 更新偏移量 b.off,表示成功写入了 m 个字节。
    • 将写入的字节数 m 转换为 int64 类型,赋值给返回值 n
    • 如果在写入过程中遇到错误 e,直接返回写入的字节数和错误。
    • 最后,检查是否所有的字节都已经被写入,如果不是,返回写入的字节数和错误 io.ErrShortWrite
  • 如果缓冲区现在为空,调用 b.Reset() 方法重置缓冲区。
  • 最后,返回写入的字节数 nnil 错误。

作用:

  • WriteTo 方法的作用是将缓冲区中的数据写入目标 w,并返回写入的字节数和可能的错误。
  • 它确保缓冲区中的数据被完整地写入目标,同时在写入完成后重置缓冲区,以便下一次写入操作。

25、WriteByte:向缓冲区写入字节

这部分代码定义了 WriteByte 方法,用于向缓冲区追加字节 c,并在需要时扩展缓冲区的大小。

// WriteByte 方法向缓冲区追加字节 c,根据需要扩展缓冲区的大小。
// 返回的错误始终为 nil,但包含在此处以匹配 bufio.Writer 的 WriteByte。
// 如果缓冲区变得太大,WriteByte 将以 ErrTooLarge 异常抛出。
func (b *Buffer) WriteByte(c byte) error {
    b.lastRead = opInvalid
    m, ok := b.tryGrowByReslice(1)
    if !ok {
        m = b.grow(1)
    }
    b.buf[m] = c
    return nil
}

解释:

  • WriteByte 方法是 Buffer 结构体的方法,用于向缓冲区追加字节 c,并在需要时扩展缓冲区的大小。
  • 首先,将 lastRead 标记为 opInvalid,表示最近的读取操作无效。
  • 然后,尝试通过 tryGrowByReslice 方法扩展缓冲区的大小,以容纳新的字节。
    • 如果扩展成功(ok == true),则直接将字节 c 写入扩展后的位置。
    • 如果扩展失败(ok == false),则调用 grow 方法扩展缓冲区的大小,并将字节 c 写入扩展后的位置。
  • 最后,返回的错误始终为 nil,但包含在此处以匹配 bufio.Writer 的 WriteByte。

作用:

  • WriteByte 方法的作用是向缓冲区追加单个字节,并在需要时扩展缓冲区的大小,以容纳新的字节。
  • 它确保缓冲区足够大以容纳新的字节,并将字节写入缓冲区的末尾。

26、WriteRune:向缓冲区写入 Unicode 字符的 UTF-8 编码

这部分代码定义了 WriteRune 方法,用于向缓冲区追加 Unicode 字符 r 的 UTF-8 编码,并返回其长度和一个始终为 nil 的错误,以匹配 bufio.Writer 的 WriteRune。

// WriteRune 方法向缓冲区追加 Unicode 字符 r 的 UTF-8 编码,返回其长度和一个始终为 nil 的错误,以匹配 bufio.Writer 的 WriteRune。
// 如果需要,缓冲区会根据需要进行扩展;如果缓冲区变得太大,WriteRune 将以 ErrTooLarge 异常抛出。
func (b *Buffer) WriteRune(r rune) (n int, err error) {
    // 以 uint32 类型进行比较,以正确处理负数符文。
    if uint32(r) < utf8.RuneSelf {
        b.WriteByte(byte(r))
        return 1, nil
    }
    b.lastRead = opInvalid
    m, ok := b.tryGrowByReslice(utf8.UTFMax)
    if !ok {
        m = b.grow(utf8.UTFMax)
    }
    b.buf = utf8.AppendRune(b.buf[:m], r)
    return len(b.buf) - m, nil
}

解释:

  • WriteRune 方法是 Buffer 结构体的方法,用于向缓冲区追加 Unicode 字符 r 的 UTF-8 编码,并返回其长度和一个始终为 nil 的错误,以匹配 bufio.Writer 的 WriteRune。
  • 首先,通过与 utf8.RuneSelf 的比较,判断是否可以使用一个字节表示该 Unicode 字符,如果可以,则直接将该字节写入缓冲区,并返回长度为 1 和 nil 错误。
  • 如果需要多个字节表示该 Unicode 字符,尝试通过 tryGrowByReslice 方法扩展缓冲区的大小,以容纳 UTF-8 编码所需的最大字节数(utf8.UTFMax)。
    • 如果扩展成功(ok == true),则将 Unicode 字符 r 的 UTF-8 编码追加到缓冲区中。
    • 如果扩展失败(ok == false),则调用 grow 方法扩展缓冲区的大小,并将 Unicode 字符 r 的 UTF-8 编码追加到缓冲区中。
  • 最后,返回 UTF-8 编码的长度和一个始终为 nil 的错误。

作用:

  • WriteRune 方法的作用是向缓冲区追加 Unicode 字符的 UTF-8 编码,确保缓冲区足够大以容纳编码后的字节序列,并返回编码后的长度和 nil 错误。

27、Read:从缓冲区读取数据

这部分代码定义了 Read 方法,用于从缓冲区读取下一个 len(p) 个字节的数据,或者直到缓冲区被耗尽。返回值 n 是读取的字节数。如果缓冲区没有数据可返回,err 为 io.EOF(除非 len(p) 为零);否则为 nil。

// Read 方法从缓冲区读取下一个 len(p) 个字节的数据,或者直到缓冲区被耗尽。返回值 n 是读取的字节数。如果缓冲区没有数据可返回,err 为 io.EOF(除非 len(p) 为零);否则为 nil。
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
}

解释:

  • Read 方法是 Buffer 结构体的方法,用于从缓冲区读取下一个 len(p) 个字节的数据,或者直到缓冲区被耗尽。
  • 首先,将 lastRead 标记设置为 opInvalid
  • 如果缓冲区为空,重置缓冲区以释放空间,并根据情况返回 0 和 nil,或者返回 0 和 io.EOF。
  • 否则,将缓冲区中的数据拷贝到 p 中,更新读取位置 b.off,并根据读取的字节数设置 lastRead 标记为 opRead
  • 最后,返回读取的字节数和 nil 错误,或者返回 0 和 io.EOF。

作用:

  • Read 方法的作用是从缓冲区读取数据,将数据拷贝到指定的字节切片中,并更新缓冲区的读取位置。如果缓冲区为空,则重置缓冲区以释放空间,并根据情况返回相应的值和错误。

28、Next:获取缓冲区的下一个字节切片

这部分代码定义了 Next 方法,用于返回包含缓冲区中下一个 n 个字节的切片,同时将缓冲区的读取位置向前移动 n 个字节,就像这些字节已经被 Buffer.Read 方法返回一样。如果缓冲区中的字节数少于 n,则 Next 方法返回整个缓冲区。

// Next 方法返回一个包含缓冲区中下一个 n 个字节的切片,同时将缓冲区的读取位置向前移动 n 个字节,就像这些字节已经被 Buffer.Read 方法返回一样。如果缓冲区中的字节数少于 n,则 Next 方法返回整个缓冲区。
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
}

解释:

  • Next 方法是 Buffer 结构体的方法,用于返回一个包含缓冲区中下一个 n 个字节的切片,并将缓冲区的读取位置向前移动 n 个字节,就像这些字节已经被 Buffer.Read 方法返回一样。
  • 首先,将 lastRead 标记设置为 opInvalid
  • 然后,计算缓冲区中剩余的字节数 m,并将 n 限制为不超过 m
  • 接着,获取包含下一个 n 个字节的切片 data,并将读取位置向前移动 n 个字节。
  • 如果成功获取了字节切片,将 lastRead 标记设置为 opRead
  • 最后,返回字节切片 data

作用:

  • Next 方法的作用是返回缓冲区中的下一个 n 个字节的切片,并将缓冲区的读取位置向前移动 n 个字节,以便后续读取操作可以从正确的位置开始。如果缓冲区中的字节数少于 n,则返回整个缓冲区的内容。

29、ReadByte:从缓冲区读取一个字节

这部分代码定义了 ReadByte 方法,用于从缓冲区读取并返回下一个字节。如果没有可用的字节,它会返回错误 io.EOF。

// ReadByte 方法从缓冲区读取并返回下一个字节。如果没有可用的字节,它会返回错误 io.EOF。
func (b *Buffer) ReadByte() (byte, error) {
    if b.empty() {
        // 缓冲区为空,重置以释放空间。
        b.Reset()
        return 0, io.EOF
    }
    c := b.buf[b.off]
    b.off++
    b.lastRead = opRead
    return c, nil
}

解释:

  • ReadByte 方法是 Buffer 结构体的方法,用于从缓冲区读取并返回下一个字节。
  • 首先,检查缓冲区是否为空,如果是,重置缓冲区以释放空间,并返回错误 io.EOF。
  • 否则,获取缓冲区中的下一个字节 c,并将读取位置向前移动一个字节。
  • 最后,将读取操作标记设置为 opRead,并返回读取的字节和 nil 错误。

作用:

  • ReadByte 方法的作用是从缓冲区读取并返回下一个字节,同时更新缓冲区的读取位置。如果缓冲区为空,则返回错误 io.EOF。

30、ReadRune:从缓冲区读取一个 Unicode 字符

这部分代码定义了 ReadRune 方法,用于从缓冲区读取并返回下一个 UTF-8 编码的 Unicode 码点。如果没有可用的字节,它会返回错误 io.EOF。如果字节是错误的 UTF-8 编码,它会消耗一个字节并返回 U+FFFD 和 1。

// ReadRune 方法从缓冲区读取并返回下一个 UTF-8 编码的 Unicode 码点。如果没有可用的字节,它会返回错误 io.EOF。如果字节是错误的 UTF-8 编码,它会消耗一个字节并返回 U+FFFD 和 1。
func (b *Buffer) ReadRune() (r rune, size int, err error) {
    if b.empty() {
        // 缓冲区为空,重置以释放空间。
        b.Reset()
        return 0, 0, io.EOF
    }
    c := b.buf[b.off]
    if c < utf8.RuneSelf {
        b.off++
        b.lastRead = opReadRune1
        return rune(c), 1, nil
    }
    r, n := utf8.DecodeRune(b.buf[b.off:])
    b.off += n
    b.lastRead = readOp(n)
    return r, n, nil
}

解释:

  • ReadRune 方法是 Buffer 结构体的方法,用于从缓冲区读取并返回下一个 UTF-8 编码的 Unicode 码点。
  • 首先,检查缓冲区是否为空,如果是,重置缓冲区以释放空间,并返回错误 io.EOF。
  • 接着,获取缓冲区中的下一个字节 c,并根据字节的值判断是否为单字节 UTF-8 编码。
    • 如果是单字节编码,将读取位置向前移动一个字节,返回对应的 Unicode 码点和字节数 1。
    • 如果不是单字节编码,使用 utf8.DecodeRune 解析缓冲区中的 UTF-8 编码,获取 Unicode 码点 r 和编码所占字节数 n
  • 最后,根据解析结果设置读取操作标记,并返回 Unicode 码点、编码字节数和 nil 错误。

作用:

  • ReadRune 方法的作用是从缓冲区读取并返回下一个 UTF-8 编码的 Unicode 码点,并根据情况返回相应的错误。如果缓冲区为空,将返回错误 io.EOF;如果字节是错误的 UTF-8 编码,将返回 U+FFFD 和 1。
  • 22
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风不归Alkaid

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值