源码分析之Bufio

Posted 魏小言

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了源码分析之Bufio相关的知识,希望对你有一定的参考价值。

源码分析之Bufio

源码路径:/usr/local/go/src/bufio

Bufio 有缓冲区的IO,在数据批量操作上相对原生IO效率具有提高; Bufio含有三个对象【type Reader\\type
Writer\\type
ReadWriter】;三个常量【defaultBufSize\\minReadBufferSize\\maxConsecutiveEmptyReads】;四个全局错误变量【ErrInvalidUnreadByte\\ErrInvalidUnreadRune\\ErrBufferFull\\ErrNegativeCount】

三个对象之【Reader】

type Reader struct {
    buf          []byte    //buffer area array byte.  缓冲区[]byte
    rd           io.Reader // reader provided by the client.    原生io
    r, w         int       // buf read and write positions\\index.   读写下标,判断缓冲区是否可读
    err          error     // 错误产出Msg
    lastByte     int     // last byte read for UnreadByte; -1 means invalid
    lastRuneSize int     // size of last rune read for UnreadRune; -1 means invalid
}
Func Read
当读取目的数组字段为空时,返回0+读取状态;
    当缓冲区为空&&目的字段长度大于\\等于缓冲区时,直接从原生io中读取;
    当缓冲区为空&&目的字段长度小于缓冲区时,原生io读至缓冲区;
        读取缓冲区至目的数组;
func (b *Reader) Read(p []byte) (n int, err error) {
    n = len(p)
    if n == 0 {
        if b.Buffered() > 0 {
            return 0, nil
        }
        return 0, b.readErr()
    }
    if b.r == b.w {
        if b.err != nil {
            return 0, b.readErr()
        }
        if len(p) >= len(b.buf) {
            // Large read, empty buffer.
            // Read directly into p to avoid copy.
            n, b.err = b.rd.Read(p)
            if n < 0 {
                panic(errNegativeRead)
            }
            if n > 0 {
                b.lastByte = int(p[n-1])
                b.lastRuneSize = -1
            }
            return n, b.readErr()
        }
        // One read.
        // Do not use b.fill, which will loop.
        b.r = 0
        b.w = 0
        n, b.err = b.rd.Read(b.buf)
        if n < 0 {
            panic(errNegativeRead)
        }
        if n == 0 {
            return 0, b.readErr()
        }
        b.w += n
    }
    // copy as much as we can
    n = copy(p, b.buf[b.r:b.w])
    b.r += n
    b.lastByte = int(b.buf[b.r-1])
    b.lastRuneSize = -1
    return n, nil
}

另注:【copy原、目的Size差异场景】

// The copy built-in function copies elements from a source slice into a
// destination slice. (As a special case, it also will copy bytes from a
// string to a slice of bytes.) The source and destination may overlap. Copy
// returns the number of elements copied, which will be the minimum of
// len(src) and len(dst).
func copy(dst, src []Type) int
Func ReadBytes
ReadBytes 在 b 中查找 delim 并读出 delim 及其之前的所有数据
 如果 ReadBytes 在找到 delim 之前遇到错误
 则返回遇到错误之前的所有数据,同时返回遇到的错误(通常是 io.EOF)
 只有当 ReadBytes 找不到 delim 时,err 才不为 nil
 对于简单的用途,使用 Scanner 可能更方便
func (b *Reader) ReadBytes(delim byte) (line []byte, err error)
Func ReadLine
 ReadLine 是一个低级的原始的行读取操作
 大多数情况下,应该使用 ReadBytes('\\n')ReadString('\\n')
 或者使用一个 Scanner
ReadLine 通过调用 ReadSlice 方法实现,返回的也是缓存的切片
ReadLine 尝试返回一个单行数据,不包括行尾标记(\\n 或 \\r\\n)
 如果在缓存中找不到行尾标记,则设置 isPrefix 为 true,表示查找未完成
 同时读出缓存中的数据并作为切片返回
 只有在当前缓存中找到行尾标记,才将 isPrefix 设置为 false,表示查找完成
可以多次调用 ReadLine 来读出一行
 返回的数据在下一次读取操作之前是有效的
 如果 ReadLine 无法获取任何数据,则返回一个错误信息(通常是 io.EOF)
func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error)
Func ReadSlice
 ReadSlice 在 b 中查找 delim 并返回 delim 及其之前的所有数据的切片
 该操作会读出数据,返回的切片是已读出数据的引用
 切片中的数据在下一次读取操作之前是有效的
 如果 ReadSlice 在找到 delim 之前遇到错误
 则读出缓存中的所有数据并返回,同时返回遇到的错误(通常是 io.EOF)
 如果在整个缓存中都找不到 delim,则 err 返回 ErrBufferFull
 如果 ReadSlice 能找到 delim,则 err 始终返回 nil
 因为返回的切片中的数据有可能被下一次读写操作修改
 因此大多数操作应该使用 ReadBytes 或 ReadString,它们返回的不是数据引用
func (b *Reader) ReadSlice(delim byte) (line []byte, err error)
Func ReadRune\\Peek\\…
…...

三个对象之【Writer】
type Writer struct {
   err   error
   buf  []byte
   n     int
   wr   io.Writer
}
Func Write
Write方法首先会判断写入的数据长度是否大于设置的缓冲长度,如果小于,则会将数据copy到缓冲中;当数据长度大于缓冲长度时,如果数据特别大,则会跳过copy环节,直接写入文件。其他情况依然先会将数据拷贝到缓冲队列中,然后再将缓冲中的数据写入到文件中。
// 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
        if b.Buffered() == 0 {
            // Large write, empty buffer.
            // Write directly from p to avoid copy.
            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
}
Func NewWriterSize
// NewWriterSize returns a new Writer whose buffer has at least the specified
// size. If the argument io.Writer is already a Writer with large enough
// size, it returns the underlying Writer.
func NewWriterSize(w io.Writer, size int) *Writer {
    // Is it already a 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,
    }
}
Flush 将缓存中的数据提交到底层的 io.Writer 中
func (b *Writer) Flush() error
Available 返回缓存中的可以空间
func (b *Writer) Available() int
 Buffered 返回缓存中未提交的数据长度
func (b *Writer) Buffered() int
 Write 将 p 中的数据写入 b 中,返回写入的字节数
 如果写入的字节数小于 p 的长度,则返回一个错误信息
func (b *Writer) Write(p []byte) (nn int, err error)
 WriteString 同 Write,只不过写入的是字符串
func (b *Writer) WriteString(s string) (int, error)

以上是关于源码分析之Bufio的主要内容,如果未能解决你的问题,请参考以下文章

Android 逆向整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 | /bin/dexopt 源码分析 )(代码片段

Android 事件分发事件分发源码分析 ( Activity 中各层级的事件传递 | Activity -> PhoneWindow -> DecorView -> ViewGroup )(代码片段

golang中bufio包

ArrayPool 源码解读之 byte[] 也能池化?

ArrayPool 源码解读之 byte[] 也能池化?

《Docker 源码分析》全球首发啦!