最近接触到 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 型写入函数。

 

posted on 2021-03-10 15:00  i野老i  阅读(1440)  评论(0编辑  收藏  举报