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

本文详细解读了Go语言bytes包中的Buffer结构及其关键方法,如Len、Cap、Available、Truncate、Reset、grow和Write系列函数,阐述它们的作用和使用场景,帮助读者更好地掌握Go内存管理与数据处理。
摘要由CSDN通过智能技术生成

bytes包官方文档

Go源码学习-索引目录

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

11、Len:返回缓冲区未读部分的字节数

// Len 返回缓冲区未读部分的字节数;
// b.Len() == len(b.Bytes())。
func (b *Buffer) Len() int { return len(b.buf) - b.off }

解释:

  • Len 方法是一个 Buffer 结构体的方法,返回缓冲区中未读部分的字节数。
  • 等效于 len(b.Bytes()),即未读部分的字节数。

作用:

  • Len 方法用于获取缓冲区中未读部分的字节数,帮助确定还有多少未读取的数据。

12、Cap:返回缓冲区底层字节切片的容量

// Cap 返回缓冲区底层字节切片的容量,即为缓冲区数据分配的总空间。
func (b *Buffer) Cap() int { return cap(b.buf) }

解释:

  • Cap 方法是一个 Buffer 结构体的方法,返回缓冲区底层字节切片的容量,即为缓冲区数据分配的总空间。

作用:

  • Cap 方法用于获取缓冲区底层字节切片的容量,即缓冲区最多能容纳多少字节的数据。

13、Available:返回缓冲区中未使用的字节数

// Available 返回缓冲区中未使用的字节数。
func (b *Buffer) Available() int { return cap(b.buf) - len(b.buf) }

解释:

  • Available 方法是一个 Buffer 结构体的方法,返回缓冲区中未使用的字节数,即缓冲区还有多少可用空间。

作用:

  • Available 方法用于获取缓冲区中未使用的字节数,帮助确定缓冲区还有多少可用空间用于写入数据。

14、Truncate:截断缓冲区保留前n个未读字节

// Truncate 保留缓冲区的前n个未读字节,丢弃其余部分,但继续使用相同的分配存储。
// 如果 n 为负数或大于缓冲区长度,则引发 panic。
func (b *Buffer) Truncate(n int) {
    // 如果 n 为零,则重置缓冲区。
    if n == 0 {
        b.Reset()
        return
    }
    // 重置上次读取的操作标记。
    b.lastRead = opInvalid
    // 如果 n 为负数或大于缓冲区长度,则引发 panic。
    if n < 0 || n > b.Len() {
        panic("bytes.Buffer: truncation out of range")
    }
    // 将缓冲区截断至前 n 个未读字节。
    b.buf = b.buf[:b.off+n]
}

解释:

  • Truncate 方法是一个 Buffer 结构体的方法,用于截断缓冲区保留前 n 个未读字节,丢弃其余部分。
  • 如果 n 为零,则调用 Reset 方法重置缓冲区。
  • 重置了上次读取的操作标记。
  • 如果 n 为负数或大于缓冲区长度,则引发 panic。
  • 最终,将缓冲区截断至前 n 个未读字节。

作用:

  • Truncate 方法用于调整缓冲区的大小,保留前 n 个未读字节,丢弃其余部分。

15、Reset:重置缓冲区为初始状态

// Reset 重置缓冲区为初始状态,
// 但保留底层存储以供将来的写入使用。
// Reset 等同于 Buffer.Truncate(0)。
func (b *Buffer) Reset() {
    // 将缓冲区切片长度设置为零,即清空缓冲区。
    b.buf = b.buf[:0]
    // 重置偏移量和上次读取的操作标记。
    b.off = 0
    b.lastRead = opInvalid
}

解释:

  • Reset 方法是一个 Buffer 结构体的方法,用于重置缓冲区为初始状态,但保留底层存储以供将来的写入使用。
  • 将缓冲区切片长度设置为零,即清空缓冲区。
  • 同时,重置偏移量和上次读取的操作标记。

作用:

  • Reset 方法用于将缓冲区重置为初始状态,清空已有数据,但保留底层存储以供后续写入使用。

16、tryGrowByReslice:尝试通过重新切片进行快速增长

// tryGrowByReslice 是 grow 的内联版本,用于内部缓冲区只需要重新切片的快速情况。
// 它返回应写入字节的索引以及是否成功。
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
}

解释:

  • tryGrowByReslice 方法是一个 Buffer 结构体的方法,用于在内部缓冲区只需要重新切片的快速情况下进行增长。
  • 如果缓冲区的剩余容量足够,直接重新切片,返回应写入字节的索引以及成功标记。
  • 如果容量不足,返回失败标记。

作用:

  • tryGrowByReslice 方法用于在缓冲区容量足够的情况下进行快速增长,通过重新切片内部缓冲区。

17、grow:增长缓冲区以保证足够空间

// grow 增长缓冲区以保证至少有 n 个额外字节的空间。
// 返回应写入字节的索引。
// 如果缓冲区无法增长,将引发 ErrTooLarge 错误。
func (b *Buffer) grow(n int) int {
    m := b.Len()
    // 如果缓冲区为空,重置以释放空间。
    if m == 0 && b.off != 0 {
        b.Reset()
    }
    // 尝试通过重新切片的方式进行增长。
    if i, ok := b.tryGrowByReslice(n); ok {
        return i
    }
    // 如果缓冲区为空且 n 小于等于 smallBufferSize,直接创建新的小缓冲区。
    if b.buf == nil && n <= smallBufferSize {
        b.buf = make([]byte, n, smallBufferSize)
        return 0
    }
    c := cap(b.buf)
    if n <= c/2-m {
        // 可以通过滑动而不是分配新的切片来增长。
        // 我们只需要 m+n <= c 来滑动,但我们让容量增长两倍,以免花费过多时间复制。
        copy(b.buf, b.buf[b.off:])
    } else if c > maxInt-c-n {
        panic(ErrTooLarge)
    } else {
        // 添加 b.off 以考虑 b.buf[:b.off] 从前面切片掉的情况。
        b.buf = growSlice(b.buf[b.off:], b.off+n)
    }
    // 恢复 b.off 和 len(b.buf)。
    b.off = 0
    b.buf = b.buf[:m+n]
    return m
}

解释:

  • grow 方法是一个 Buffer 结构体的方法,用于增长缓冲区以保证至少有 n 个额外字节的空间。
  • 获取当前缓冲区的长度 m
  • 如果缓冲区为空且偏移量不为零,则调用 Reset 方法重置缓冲区,以释放空间。
  • 尝试通过重新切片的方式进行增长,如果成功,返回写入字节的索引。
  • 如果缓冲区为空且 n 小于等于 smallBufferSize,直接创建新的小缓冲区。
  • 获取当前缓冲区的容量 c
  • 如果 n 小于等于 c/2-m,则可以通过滑动而不是分配新的切片来增长,避免过多的复制。
  • 如果缓冲区容量大于 maxInt-c-n,引发 ErrTooLarge 错误,防止溢出。
  • 否则,通过调用 growSlice 方法增长缓冲区的切片。
  • 最终,恢复偏移量和缓冲区的长度,并返回写入字节的索引。

作用:

  • grow 方法用于增长缓冲区以确保至少有 n 个额外字节的空间。
  • 它采用不同的策略,包括重新切片和创建新的缓冲区,以满足增长的需求。

18、Grow:增长缓冲区容量

// Grow 增长缓冲区的容量,以保证至少有另外 n 个字节的空间。
// 在调用 Grow(n) 后,可以写入至少 n 个字节到缓冲区而无需另外分配空间。
// 如果 n 为负数,Grow 将引发 panic。
// 如果缓冲区无法增长,将引发 [ErrTooLarge] 错误。
func (b *Buffer) Grow(n int) {
    if n < 0 {
        panic("bytes.Buffer.Grow: negative count")
    }
    m := b.grow(n)
    b.buf = b.buf[:m]
}

解释:

  • Grow 方法是一个 Buffer 结构体的方法,用于增长缓冲区的容量,以确保至少有另外 n 个字节的空间。
  • 如果 n 为负数,Grow 将引发 panic。
  • 调用内部的 grow 方法获取实际增长后的长度 m,然后截取缓冲区至该长度。

作用:

  • Grow 方法用于增长缓冲区的容量,确保至少有另外 n 个字节的空间。
  • 这样在之后的写入操作中,可以直接写入至少 n 个字节到缓冲区而无需另外分配空间。

19、Write:写入内容到缓冲区

// Write 将 p 的内容追加到缓冲区,根据需要增长缓冲区。
// 返回值 n 是 p 的长度;err 始终为 nil。如果缓冲区变得过大,Write 将引发 [ErrTooLarge] 错误。
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
}

解释:

  • Write 方法是一个 Buffer 结构体的方法,用于将字节切片 p 的内容追加到缓冲区中,并根据需要增长缓冲区的容量。
  • 在写入之前,将 lastRead 字段设置为 opInvalid,表示上一次读取的操作无效。
  • 使用 tryGrowByReslice 方法尝试通过重新切片的方式增长缓冲区,如果成功返回 ok 为 true。
  • 如果不成功,则调用 grow 方法进行增长,获取实际增长后的长度 m
  • 使用 copyp 的内容复制到缓冲区中的相应位置,返回复制的字节数和 nil 错误。

作用:

  • Write 方法用于将字节切片 p 的内容追加到缓冲区,确保缓冲区的容量足够。
  • 如果缓冲区变得过大,会引发 [ErrTooLarge] 错误,避免无限制地增长缓冲区。

20、WriteString:将字符串写入缓冲区

// WriteString 将字符串 s 的内容追加到缓冲区,根据需要增长缓冲区。
// 返回值 n 是 s 的长度;err 始终为 nil。如果缓冲区变得过大,WriteString 将引发 [ErrTooLarge] 错误。
func (b *Buffer) WriteString(s string) (n int, err error) {
    b.lastRead = opInvalid
    m, ok := b.tryGrowByReslice(len(s))
    if !ok {
        m = b.grow(len(s))
    }
    return copy(b.buf[m:], s), nil
}

解释:

  • WriteString 方法是一个 Buffer 结构体的方法,用于将字符串 s 的内容追加到缓冲区中,并根据需要增长缓冲区的容量。
  • 在写入之前,将 lastRead 字段设置为 opInvalid,表示上一次读取的操作无效。
  • 使用 tryGrowByReslice 方法尝试通过重新切片的方式增长缓冲区,如果成功返回 ok 为 true。
  • 如果不成功,则调用 grow 方法进行增长,获取实际增长后的长度 m
  • 使用 copy 将字符串 s 的内容复制到缓冲区中的相应位置,返回复制的字节数和 nil 错误。

作用:

  • WriteString 方法用于将字符串 s 的内容追加到缓冲区,确保缓冲区的容量足够。
  • 如果缓冲区变得过大,会引发 [ErrTooLarge] 错误,避免无限制地增长缓冲区。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

风不归Alkaid

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

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

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

打赏作者

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

抵扣说明:

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

余额充值