最近接触到 go 语言相关的 fileserver 项目,夯实基础,以图后效
bufio.Writer 类型定义
// Writer implements buffering for an io.Writer object.
// If an error occurs writing to a Writer, no more data will be
// accepted and all subsequent writes, and Flush, will return the error.
// After all data has been written, the client should call the
// Flush method to guarantee all data has been forwarded to
// the underlying io.Writer.
type Writer struct {
err error
buf []byte
n int
wr io.Writer
}
bufio包中Writer类型对 io.Writer 进行了包装,各字段说明:
- err: 用于记录异常
- buf: 用于缓冲的Slice, 默认大小 defaultBufSize = 4096
-
const ( defaultBufSize = 4096 ) // NewWriter returns a new Writer whose buffer has the default size. func NewWriter(w io.Writer) *Writer { return NewWriterSize(w, defaultBufSize) }
-
- n: buf 中已经缓存的字节数,并且提供了一个访问方法
-
// Buffered returns the number of bytes that have been written into the current buffer. func (b *Writer) Buffered() int { return b.n }// Available returns how many bytes are unused in the buffer.
func (b *Writer) Available() int { return len(b.buf) - b.n }
-
bufio.Writer 的写实现
// Write writes the contents of p into the buffer.
// It returns the number of bytes written.
// If nn < len(p), it also returns an error explaining
// why the write is short.
func (b *Writer) Write(p []byte) (nn int, err error) {
// 如果待写数据长度大于缓存可接收长度
for len(p) > b.Available() && b.err == nil {
var n int
// 如果当前 buffer 中没有缓存数据, 则不经过缓存,直接将数据写入文件
if b.Buffered() == 0 {
// Large write, empty buffer.
// Write directly from p to avoid copy.
n, b.err = b.wr.Write(p)
} else { // 如果当前 buffer 中 有数据,则将待写数据塞满 buffer 后flush到文件中,进入下一次判断
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
}
写逻辑:
if: 待写数据长度大于缓存可接收长度 { if: 当前 buffer 中没有缓存数据 { 则不经过缓存,直接将数据写入文件 } else: 当前 buffer 中有数据 { 则将待写数据塞满 buffer 后flush到文件中,进入下一次判断 } } else: 待写数据长度 小于 缓存可接收长度 { 则直接写入缓存 }
补充1: io.Writer 功能, io包中Writer方法功能(返回值n 为成功写入的数据,如果 n 小于 待写入数据长度则报error,注意即使报错也有n个字节数据被写入)
// Writer is the interface that wraps the basic Write method.
//
// Write writes len(p) bytes from p to the underlying data stream.
// It returns the number of bytes written from p (0 <= n <= len(p))
// and any error encountered that caused the write to stop early.
// Write must return a non-nil error if it returns n < len(p).
// Write must not modify the slice data, even temporarily.
//
// Implementations must not retain p.
type Writer interface {
Write(p []byte) (n int, err error)
}
补充2: Flush 实现,主要三个功能(将缓存数据写入文件, 写入错误重置缓存, 写入成功缓存索引置零)
// Flush writes any buffered data to the underlying io.Writer.
func (b *Writer) Flush() error {
if b.err != nil {
return b.err
}
if b.n == 0 {
return nil
}
// 将缓存数据写到文件
n, err := b.wr.Write(b.buf[0:b.n])
if n < b.n && err == nil {
err = io.ErrShortWrite
}
if err != nil {
if n > 0 && n < b.n { // 出现错误时,重置 buffer(删除buffer中错误前已经写入到文件的字节数)
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
}
至此,bufio Writer 写入数据的实现逻辑分析完毕。
一些思考:
在go的文件 buffer 型写入实现方式中,使用定义slice 作为内存缓存的方式缓冲写入的数据,当待写入数据的长度大于当前 slice 剩余空间(buffer 的 可接收数据长度)或者调用flush 将slice数据写入文件。
可以关注学习操作系统层级的文件缓存,是否有不使用go语言slice作为buf,而使用操作系统提供的 buffer 型写入函数。
浙公网安备 33010602011771号