bufio包学习的上一篇文章:Go源码学习:bufio包 - 1.1 - bufio.go -(2)
17、WriteTo:实现io.WriterTo接口
这部分代码定义了 WriteTo
方法,用于实现 io.WriterTo
接口。该方法可能对底层的 Reader
调用多次 Reader.Read
方法。如果底层读取器支持 Reader.WriteTo
方法,它将直接调用底层的 Reader.WriteTo
而不进行缓冲。
func (b *Reader) WriteTo(w io.Writer) (n int64, err error) {
// 重置上一个字节和上一个rune的大小。
b.lastByte = -1
b.lastRuneSize = -1
// 调用writeBuf将缓冲区中的数据写入目标io.Writer。
n, err = b.writeBuf(w)
if err != nil {
return
}
// 如果底层的读取器支持Reader.WriteTo方法,则直接调用它。
if r, ok := b.rd.(io.WriterTo); ok {
m, err := r.WriteTo(w)
n += m
return n, err
}
// 如果目标io.Writer支持io.ReaderFrom方法,则直接调用它。
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
}
// 循环写入数据直到缓冲区为空。
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
}
// 如果错误为io.EOF,则将其重置为nil。
if b.err == io.EOF {
b.err = nil
}
// 返回写入的总字节数和读取时可能出现的错误。
return n, b.readErr()
}
解释:
WriteTo
方法是Reader
结构体的方法,实现了io.WriterTo
接口。- 重置了上一个字节和上一个rune的大小。
- 调用
writeBuf
方法将缓冲区中的数据写入目标io.Writer
。 - 如果底层的读取器支持
Reader.WriteTo
方法,则直接调用它。 - 如果目标
io.Writer
支持io.ReaderFrom
方法,则直接调用它。 - 如果缓冲区未满,则调用
fill
方法(缓冲区非满状态)。 - 在循环中写入数据直到缓冲区为空,同时调用
writeBuf
方法和fill
方法。 - 如果错误为
io.EOF
,则将其重置为nil
。 - 返回写入的总字节数和读取时可能出现的错误。
作用:
WriteTo
方法实现了io.WriterTo
接口,允许将缓冲区中的数据写入目标io.Writer
。- 在写入数据之前,会调用底层的读取器的相关方法,或者通过缓冲区的填充操作来保证有足够的数据可用。
- 通过多次调用
writeBuf
和可能的底层方法,将数据从读取器传输到目标io.Writer
。
18、writeBuf:写入缓冲区
这段代码定义了 writeBuf
方法,用于将 Reader
的缓冲区内容写入到指定的写入器(io.Writer
)中。
var errNegativeWrite = errors.New("bufio: writer returned negative count from Write")
// writeBuf 将 Reader 的缓冲区内容写入到写入器中。
func (b *Reader) writeBuf(w io.Writer) (int64, error) {
// 将缓冲区的内容写入到指定的写入器。
n, err := w.Write(b.buf[b.r:b.w])
// 如果写入的字节数 n 小于零,抛出 panic,表示写入返回了负数。
if n < 0 {
panic(errNegativeWrite)
}
// 将读指针 b.r 向前移动,表示成功写入了 n 个字节。
b.r += n
return int64(n), err
}
解释:
writeBuf
方法是Reader
结构体的方法,用于将缓冲区的内容写入到指定的写入器。- 它接收一个写入器
w
作为参数,将缓冲区中未读取的数据段写入到该写入器中。 - 方法返回成功写入的字节数以及可能出现的错误。
- 在写入过程中,如果写入的字节数
n
小于零,表示写入操作返回了负数,会触发 panic。
作用:
writeBuf
方法负责将缓冲区中未读取的数据段写入到指定的写入器中。- 它在实现
Reader
结构体的写入操作中发挥着关键作用,将数据从缓冲区传输到目标写入器中。
19、buffered output(缓冲输出)
该部分代码实现了带缓冲的输出,通过 [Writer] 结构体进行缓冲写入。
// Writer 实现了对 io.Writer 对象的缓冲。
// 如果写入 [Writer] 时发生错误,将不再接受更多数据,并且所有后续的写入和 [Writer.Flush] 都将返回错误。
// 在写入所有数据后,客户端应调用 [Writer.Flush] 方法,以确保所有数据已被转发到底层的 [io.Writer]。
type Writer struct {
err error // 写入过程中的错误
buf []byte // 缓冲区
n int // 缓冲区中已有数据的长度
wr io.Writer // 底层的写入器
}
// NewWriterSize 返回一个新的 [Writer],其缓冲区大小至少为指定大小。
// 如果参数 io.Writer 已经是一个具有足够大大小的 [Writer],它将返回底层的 [Writer]。
func NewWriterSize(w io.Writer, size int) *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,
}
}
// NewWriter 返回一个新的 [Writer],其缓冲区大小为默认大小。
// 如果参数 io.Writer 已经是一个具有足够大缓冲区大小的 [Writer],它将返回底层的 [Writer]。
func NewWriter(w io.Writer) *Writer {
return NewWriterSize(w, defaultBufSize)
}
解释:
Writer
结构体用于实现对io.Writer
对象的缓冲写入。- 结构体字段包括
err
(写入过程中的错误)、buf
(缓冲区)、n
(缓冲区中已有数据的长度)、wr
(底层的写入器)。 NewWriterSize
方法返回一个新的Writer
,其缓冲区大小至少为指定大小。如果参数io.Writer
已经是一个具有足够大大小的Writer
,它将返回底层的Writer
。NewWriter
方法返回一个新的Writer
,其缓冲区大小为默认大小。如果参数io.Writer
已经是一个具有足够大缓冲区大小的Writer
,它将返回底层的Writer
。
20、Size:获取缓冲区大小
// Size 返回底层缓冲区的大小(以字节为单位)。
func (b *Writer) Size() int { return len(b.buf) }
解释:
Size
方法是Writer
结构体的方法,用于返回该写入器的底层缓冲区的大小。- 方法通过返回
len(b.buf)
,即底层缓冲区b.buf
的长度(以字节为单位),来表示缓冲区的大小。
作用:
Size
方法允许用户查询当前写入器的底层缓冲区大小,以便了解可以容纳的数据量或进行必要的优化。- 这个大小值反映了写入器底层缓冲区的实际容量,用于处理写入数据的限制和优化缓冲区大小的相关操作。
21、Reset:重置写入器状态
// Reset 丢弃任何未刷新的缓冲数据,清除任何错误,并将 b 重置为将其输出写入到 w。
// 在零值的 [Writer] 上调用 Reset 会将内部缓冲区初始化为默认大小。
// 调用 w.Reset(w)(即将 [Writer] 重置为自身)不会产生任何效果。
func (b *Writer) Reset(w io.Writer) {
// 如果一个 Writer w 被传递给 NewWriter,NewWriter 将返回 w。
// 代码的不同层可能会这样做,然后稍后将 w 传递给 Reset。在这种情况下避免无限递归。
if b == w {
return
}
if b.buf == nil {
b.buf = make([]byte, defaultBufSize)
}
b.err = nil
b.n = 0
b.wr = w
}
解释:
Reset
方法是Writer
结构体的方法,用于重置写入器的状态。- 该方法会丢弃任何未刷新的缓冲数据,清除任何错误,并将写入器
b
重置为将其输出写入到指定的写入器w
。 - 如果在零值的
Writer
上调用Reset
,会将内部缓冲区初始化为默认大小。 - 调用
w.Reset(w)
(即将Writer
重置为自身)不会产生任何效果。
作用:
Reset
方法允许用户在不创建新的写入器实例的情况下,重置现有写入器的状态,以便重新使用该写入器。- 通过重置写入器,可以清除任何错误状态并将其重新定位到指定的写入器,同时丢弃任何未刷新的缓冲数据。
22、Flush:刷新缓冲区
该部分代码定义了 Flush
方法,用于将任何缓冲的数据写入底层的 [io.Writer]。
// Flush 将任何缓冲的数据写入底层的 [io.Writer]。
func (b *Writer) Flush() error {
// 如果在写入过程中发生了错误,直接返回错误。
if b.err != nil {
return b.err
}
// 如果缓冲区中没有数据,直接返回 nil。
if b.n == 0 {
return nil
}
// 将缓冲区中的数据写入底层的 [io.Writer]。
n, err := b.wr.Write(b.buf[0:b.n])
// 如果写入的字节数 n 小于缓冲区中的数据长度 b.n 且没有出现错误,设置错误为 io.ErrShortWrite。
if n < b.n && err == nil {
err = io.ErrShortWrite
}
// 处理可能出现的错误。
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
}
// 如果写入成功,重置缓冲区中已有数据的长度。
b.n = 0
return nil
}
解释:
Flush
方法是Writer
结构体的方法,接收者为b
结构体。- 首先,检查是否在写入过程中发生了错误,如果是,则直接返回错误。
- 然后,检查缓冲区中是否有数据,如果没有,则直接返回 nil。
- 接着,将缓冲区中的数据写入底层的
io.Writer
。 - 如果写入的字节数
n
小于缓冲区中的数据长度b.n
且没有出现错误,设置错误为io.ErrShortWrite
。 - 处理可能出现的错误,包括部分写入成功的情况。如果成功写入了部分数据,将剩余未写入的数据移到缓冲区的开头。
- 更新缓冲区中已有数据的长度。
- 如果写入过程中发生错误,存储错误并返回。
- 如果写入成功,重置缓冲区中已有数据的长度。
作用:
Flush
方法用于确保将缓冲区中的数据写入底层的io.Writer
。- 在写入所有数据后,客户端应调用
Flush
方法以确保所有数据已被转发到底层的io.Writer
。 - 如果在写入过程中发生错误,后续的写入和
Flush
将返回相同的错误。
23、Available:获取缓冲区中未使用的字节数
该部分代码定义了 Available
方法,用于返回缓冲区中未使用的字节数。
// Available 返回缓冲区中未使用的字节数。
func (b *Writer) Available() int {
// 计算并返回缓冲区中未使用的字节数。
return len(b.buf) - b.n
}
解释:
Available
方法是Writer
结构体的方法,接收者为b
结构体。- 方法直接计算并返回缓冲区中未使用的字节数,即缓冲区总长度减去已使用的字节数。
作用:
Available
方法用于获取缓冲区中未使用的字节数。- 客户端可以通过调用此方法来了解当前缓冲区中还有多少空间可用于写入数据。
24、AvailableBuffer:获取可用缓冲区
该部分代码定义了 AvailableBuffer
方法,用于获取一个具有可用容量的空缓冲区。
// AvailableBuffer 返回一个空缓冲区,其容量为 b.Available()。
// 此缓冲区用于追加内容,并传递给随后的 [Writer.Write] 调用。
// 缓冲区仅在 b 上的下一次写操作之前有效。
func (b *Writer) AvailableBuffer() []byte {
return b.buf[b.n:][:0]
}
解释:
AvailableBuffer
方法是Writer
结构体的方法,接收者为b
结构体。- 此方法返回一个空的缓冲区,其容量为
b.Available()
,表示可用的写入容量。 - 返回的缓冲区可以被用于追加内容,并且意图将其传递给接下来的
[Writer.Write]
调用。 - 注意,该缓冲区仅在下一次对
b
执行写操作之前有效,因为它直接与b
共享底层的缓冲区。
作用:
AvailableBuffer
方法的作用是提供一个可用的空缓冲区,其容量与当前Writer
结构体中可用的写入容量相匹配。- 此缓冲区可以用于填充数据,并传递给后续的写入操作,以最大限度地利用当前缓冲区的剩余空间,提高写入效率。
25、Buffered:获取当前缓冲区中已写入的字节数
该部分代码定义了 Buffered
方法,用于返回当前缓冲区中已写入的字节数。
// Buffered 返回当前缓冲区中已写入的字节数。
func (b *Writer) Buffered() int {
return b.n
}
解释:
Buffered
方法是Writer
结构体的方法,接收者为b
结构体。- 此方法返回了当前缓冲区中已写入的字节数,即已经占用的写入空间大小。
作用:
Buffered
方法用于获取当前缓冲区中已经写入的字节数。- 客户端可以通过调用此方法来了解当前缓冲区已被使用了多少空间,以决定是否需要执行刷新操作或其他操作来处理已写入的数据。
26、Write:向缓冲区写入数据
该部分代码定义了 Write
方法,用于将数据写入缓冲区。
// Write 将 p 的内容写入缓冲区。
// 它返回写入的字节数。
// 如果 nn < len(p),它还返回一个解释为什么写入不完整的错误。
func (b *Writer) Write(p []byte) (nn int, err error) {
for len(p) > b.Available() && b.err == nil {
var n int
if b.Buffered() == 0 {
// 大写入,空缓冲区。
// 直接从 p 写入,避免复制。
n, b.err = b.wr.Write(p)
} else {
n = copy(b.buf[b.n:], p)
b.n += n
b.Flush()
}
nn += n
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
方法是Writer
结构体的方法,接收者为b
结构体。- 该方法用于将字节切片
p
的内容写入缓冲区。 - 如果写入的字节数
nn
小于p
的长度,它还会返回一个解释为什么写入不完整的错误。 - 在循环中,当待写入的数据长度大于当前缓冲区的可用空间,并且没有发生错误时,会执行以下操作:
- 如果缓冲区中没有已写入的数据(
b.Buffered() == 0
),表示是大写入且缓冲区为空,直接从p
中写入数据到底层写入源b.wr
,避免复制。 - 否则,将
p
中的部分数据复制到缓冲区中,并执行Flush
操作将缓冲区中的数据写入底层写入源b.wr
。 - 更新已写入的字节数
nn
,并将p
中已写入的部分截取掉。
- 如果缓冲区中没有已写入的数据(
- 如果在写入过程中发生了错误,直接返回已写入的字节数
nn
和错误信息。 - 最后,将
p
中剩余的数据复制到缓冲区中,并更新已写入的字节数nn
,然后返回已写入的字节数nn
和nil
错误。
作用:
Write
方法用于将数据写入缓冲区,并在必要时将缓冲区中的数据刷新到底层的写入源b.wr
中。- 如果待写入的数据长度大于当前缓冲区的可用空间,会根据情况直接写入或者先写入缓冲区再刷新。