golang原始碼分析buifo包
b, ok := rd.(*Reader)
上面的語句的作用是判斷介面的動態型別是不是*Reader型別,如果是,那麼ok的值為true,這種動態型別的判斷方法必須rd的型別為一個介面
type Reader struct { buf []byte rd io.Reader // reader provided by the client r, w int // buf read and write positions err error lastByte int // last byte read for UnreadByte; -1 means invalid lastRuneSize int // size of last rune read for UnreadRune; -1 means invalid }
lastByte和lastRuneSize分別是上一次讀取到的位元組或者rune,-1表示上一次讀取的是無效的位元組或者rune,也就是說,如果上一次對這個結構體的例項不是讀取,那麼他們的值就會被賦值為-1,例如上一次讀取的是一個位元組,那麼lastByte的值就是讀取的位元組,然而lastRuneSize就是-1,同理,例如Peek方法既不讀取byte,也不讀取rune,那麼他們兩個的值就都會賦值為-1
附加知識點
如果一個結構體中的某個成員是一個介面型別,那麼初始化這個結構體的時候,需要一個實現了該介面的型別例項來初始化這個成員
NewReaderSize(rd io.Reader,size int) *Reader
func NewReaderSize(rd io.Reader, size int) *Reader { // Is it already a Reader? b, ok := rd.(*Reader) if ok && len(b.buf) >= size { return b } if size < minReadBufferSize { size = minReadBufferSize } r := new(Reader) r.reset(make([]byte, size), rd) return r }
這個函式首先判斷是否引數已經是一個緩衝區比size大的緩衝了,如果不是,並且size比最小緩衝區大小還小,就修改size大小,並且重新建立一個具有size大小的緩衝區。
NewReader(rd io.Reader) *Reader
func NewReader(rd io.Reader) *Reader { return NewReaderSize(rd, defaultBufSize) }
這個函式直接返回一個具有預設大小的緩衝區變數
上面初始化完成以後,就是結構體Reader結構體的方法了
Reader的方法
(b *Reader) Size() int
func (b *Reader) Size() int { return len(b.buf) }
這個方法的作用是返回緩衝區的大小
(b *Reader) Reset(r io.Reader)
func (b *Reader) Reset(r io.Reader) { b.reset(b.buf, r) }
這個方法不改變當前緩衝區的大小,但是丟棄緩衝區中的資料,將讀和寫的位置都初始化為0
(b *Reader) Peek(n int) ([]byte, error)
func (b *Reader) Peek(n int) ([]byte, error) { if n < 0 { return nil, ErrNegativeCount } b.lastByte = -1 b.lastRuneSize = -1 // 如果可讀的資料少於n,並且緩衝區沒有填滿,並且沒有錯誤時,就在底層記憶體中填入向緩衝區中填入足夠的資料。是0?或者隨機數? for b.w-b.r < n && b.w-b.r < len(b.buf) && b.err == nil { // 一直填充資料 b.fill() // b.w-b.r < len(b.buf) => buffer is not full } // n大於緩衝區的長度,那麼就將緩衝區所有資料讀取出來 if n > len(b.buf) { return b.buf[b.r:b.w], ErrBufferFull } // 0 <= n <= len(b.buf) var err error // 緩衝區夠大,但是可讀資料不夠,就返回所有的可讀資料 if avail := b.w - b.r; avail < n { // not enough data in buffer n = avail // 這個readErr返回b.err err = b.readErr() if err == nil { err = ErrBufferFull } } // 以切片形式返回資料 return b.buf[b.r : b.r+n], err }
Peek方法的作用就是在不改變緩衝區中的讀和寫的位置的前提下,返回從讀的位置開始的後面n個位元組的資料。如果緩衝區的資料不夠,那麼就返回返回緩衝區中所有的可讀資料。
(b *Reader) Discard(n int) (discarded int, err error)
func (b *Reader) Discard(n int) (discarded int, err error) { if n < 0 { return 0, ErrNegativeCount } if n == 0 { return } // 初始情況是剩餘n位元組資料需要丟棄 remain := n for { // 獲取當前緩衝區中可以讀取的資料長度 skip := b.Buffered() if skip == 0 { // 如果可讀資料為0,就向緩衝區中填充資料 b.fill() skip = b.Buffered() } // 填充後緩衝區可讀資料長度如果大於剩餘丟棄長度,就只需要丟棄剩餘長度的資料就可以了 if skip > remain { skip = remain } // 丟棄就是將讀取的位置向後移一定數量 b.r += skip // 更新剩餘丟棄長度 remain -= skip if remain == 0 { return n, nil } // 這裡會出錯的情況是在呼叫fill()方法向緩衝區填充資料資料的時候有可能會出錯 if b.err != nil { return n - remain, b.readErr() } } }
Discard方法的作用是丟棄緩衝區中n位元組的資料,不管緩衝區中資料是否足夠n,都要丟棄n位元組,如果不夠n位元組就呼叫fill()方法強行向緩衝區填充資料。
(b *Reader) Read(p []byte) (n int, err error)
func (b *Reader) Read(p []byte) (n int, err error) { n = len(p) // 目的陣列p的長度為0,就返回讀取資料0位元組 if n == 0 { // 本來緩衝區中的可讀資料也是0 if b.Buffered() > 0 { return 0, nil } return 0, b.readErr() } // 讀寫的位置重疊,也就是緩衝區中可讀的資料為0 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. // 呼叫Reader的引數實現的Read方法強行從緩衝區中讀取資料到陣列p中 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 // 注意此時緩衝區中的可讀資料是為0的,此時是強行向緩衝區中填入資料 n, b.err = b.rd.Read(b.buf) if n < 0 { panic(errNegativeRead) } if n == 0 { return 0, b.readErr() } // 記錄此時填寫資料的地方,所以此時緩衝區中的可讀取的資料就不為0了 b.w += n } // copy as much as we can // 將緩衝區中的可讀取資料copy到目的陣列中,如果p的長度更長,那麼就將緩衝區的所有資料都讀到p中,如果p的長度更短,那麼就將p讀滿就結束了 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 }
總之Read方法的作用就是將緩衝區中的資料讀出來存入到陣列p中(並且是儘可能的讀取,哪怕換衝區中的可讀資料為0)
(b *Reader) ReadByte() (byte, error)
func (b *Reader) ReadByte() (byte, error) { b.lastRuneSize = -1 // 如果緩衝區為空,呼叫fill()方法強行向緩衝區填資料 for b.r == b.w { if b.err != nil { return 0, b.readErr() } b.fill() // buffer is empty } c := b.buf[b.r] // 後移一位讀位置 b.r++ // 修改上一次讀取的位元組 b.lastByte = int(c) return c, nil }
ReadByte從緩衝區中讀取一個位元組的資料
(b *Reader) UnreadByte() error
func (b *Reader) UnreadByte() error { // 如果上一次對這個例項的操作不是讀取了位元組(可以讀取多個或者一個位元組),或者讀位置在開始,但是緩衝區有資料(表明上一次肯定不是讀資料) // 直接返回錯誤 if b.lastByte < 0 || b.r == 0 && b.w > 0 { return ErrInvalidUnreadByte } // b.r > 0 || b.w == 0 // b.r>0說明了前面肯定有已經讀過的資料,lastByte又大於0,說明上一次肯定讀取了一定的位元組數 if b.r > 0 { // 直接將讀位置向前挪一位,就恢復了上一次讀的最後一個位元組的資料 b.r-- } else { // 緩衝區是真的空,因為讀的位置和寫的位置都是0,但是lastByte大於0,說明後面有資料,直接將寫位置向後挪一位 // b.r == 0 && b.w == 0 b.w = 1 } b.buf[b.r] = byte(b.lastByte) // 這次對例項的操作既不讀byte也不讀rune,所以兩個成員變數都置為-1 b.lastByte = -1 b.lastRuneSize = -1 return nil }
UnreadByte方法的作用就是將上一次讀取的byte重新寫入緩衝區,並且是寫到緩衝區的最前面,也就是當前讀取位置的前面一個位置,並且將讀位置向前挪一位
(b *Reader) ReadRune() (r rune, size int, err error)
func (b *Reader) ReadRune() (r rune, size int, err error) { // utf8.UTFMax=4 // utf8.FullRune(p []byte)判斷給定的切片是否是以合法的utf-8編碼開始的,不合法的碼值會被轉化為長度為1的錯誤碼值而視為完整的 // 當讀寫的位置相距小於4,並且不是以合法的utf8編碼開始,並且錯誤資訊為空,並且緩衝區沒有填滿,就用fill()向緩衝區填資料 // 講道理,我沒有弄明白utf8.FullRune()這個方法的作用,哈哈哈,先就這樣吧 for b.r+utf8.UTFMax > b.w && !utf8.FullRune(b.buf[b.r:b.w]) && b.err == nil && b.w-b.r < len(b.buf) { b.fill() // b.w-b.r < len(buf) => buffer is not full } b.lastRuneSize = -1 // 緩衝區可讀資料為空 // 此時b.err!=nil if b.r == b.w { return 0, 0, b.readErr() } // 將第一個位元組的資料轉化為rune型別 r, size = rune(b.buf[b.r]), 1 // utf8.RuneSelf=0x80 // 當某個值小於0x80,那麼這個值就是一個單個的位元組,並且表示自身的值,也就是說不能按照unicode編碼來解碼 if r >= utf8.RuneSelf { // 這個值大於0x80,所以解碼為unicode值 r, size = utf8.DecodeRune(b.buf[b.r:b.w]) } b.r += size // 記錄讀取的最後一個位元組的資料和unicode值的長度 b.lastByte = int(b.buf[b.r-1]) b.lastRuneSize = size return r, size, nil }
讀取一個rune值,返回這個rune型別中的unicode值的長度和值,當緩衝區中第一個位元組的utf8編碼不合法,就讀取一個位元組,這個位元組就表示他自身的值(小於0x80)
(b *Reader) UnreadRune() error
func (b *Reader) UnreadRune() error { // 這個條件判斷就一定讓上一次對本例項的操作是讀取了一個rune,並且最新讀取位置一定是大於或者等於那個rune的長度,當從0開始讀rune,等號成立 if b.lastRuneSize < 0 || b.r < b.lastRuneSize { return ErrInvalidUnreadRune } // 直接將讀座標回溯 b.r -= b.lastRuneSize b.lastByte = -1 b.lastRuneSize = -1 return nil }
當上一次對本例項的操作是讀取了一個rune型別的值,那麼本次操作就是回溯這個值,將他重新新增在緩衝區的讀座標的前面。
(b *Reader) Buffered() int
func (b *Reader) Buffered() int { return b.w - b.r }
這個函式的作用是返回當前緩衝區中能夠讀取的資料,b.w是緩衝區中當前寫入資料的地方,b.r是緩衝區中前一次讀取資料的地方
(b *Reader) ReadSlice(delim byte) (line []byte, err error)
func (b *Reader) ReadSlice(delim byte) (line []byte, err error) { s := 0 // search start index for { // Search buffer. // bytes.IndexByte()返回delim在b.buf[b.r+s:b.w]中的最先出現的位置,如果不存在這個位置就返回-1 if i := bytes.IndexByte(b.buf[b.r+s:b.w], delim); i >= 0 { // 第一次就找到這個位置,然後就直接結束這個迴圈,並且將結果存入了line i += s line = b.buf[b.r : b.r+i+1] b.r += i + 1 break } // Pending error? // 沒有找到我們設定的中斷切片字元 if b.err != nil { // 讀出緩衝區中所有可讀取字元 line = b.buf[b.r:b.w] b.r = b.w err = b.readErr() break } // Buffer full? if b.Buffered() >= len(b.buf) { b.r = b.w // 直接返回整個緩衝區 line = b.buf err = ErrBufferFull break } s = b.w - b.r // do not rescan area we scanned before // 上面這個s的作用是在使用bytes.IndexByte()方法檢索字元的時候,只檢索fill()方法增加的字元,而不檢索之前檢索過的字元 b.fill() // buffer is not full } // Handle last byte, if any. // 更新例項的兩個成員變數的值 if i := len(line) - 1; i >= 0 { b.lastByte = int(line[i]) b.lastRuneSize = -1 } return }
ReadSlice方法的作用是返回緩衝區的第一個切片,切片的結尾是我們傳入的byte引數,如果緩衝區中不存在我們傳入的引數,那麼就會呼叫fill()方法向緩衝區中填入資料。然後重新讀取,直到出現我們傳入的引數或者是出現了錯誤
(b *Reader) ReadLine() (line []byte, isPrefix bool, err error)
func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error) { // 呼叫ReadSlice,以\n為標誌讀取一個切片 line, err = b.ReadSlice('\n') // 參考ReadSlice方法,出現這個錯誤的原因是可讀資料的長度超出了緩衝區的長度 if err == ErrBufferFull { // Handle the case where "\r\n" straddles the buffer. // 此時切片的結束字元就不一定是指定的\n,所以當結束是字元是\r時,就回溯一位,讓\r和\n同時被讀取 if len(line) > 0 && line[len(line)-1] == '\r' { // Put the '\r' back on buf and drop it from line. // Let the next call to ReadLine check for "\r\n". if b.r == 0 { // should be unreachable panic("bufio: tried to rewind past start of buffer") } b.r-- line = line[:len(line)-1] } return line, true, nil } if len(line) == 0 { if err != nil { line = nil } return } err = nil // 如果結束字元是\n,那麼如果前一個字元是\r,就回溯兩個字元,如果不是,就回溯一個字元 if line[len(line)-1] == '\n' { drop := 1 if len(line) > 1 && line[len(line)-2] == '\r' { drop = 2 } line = line[:len(line)-drop] } return }
ReadLine()方法從緩衝區中讀取一行字元,遇到\n或者\r\n結束,但是讀取出來的字元切片中不包含換行符
(b *Reader) ReadBytes(delim byte) ([]byte, error)
func (b *Reader) ReadBytes(delim byte) ([]byte, error) { // Use ReadSlice to look for array, // accumulating full buffers. var frag []byte var full [][]byte var err error for { var e error // 使用ReadSlice()將資料讀到切片裡面 frag, e = b.ReadSlice(delim) // 讀取沒有出錯,說明讀到delim了,讀取已經結束了,所以結束這個迴圈 if e == nil { // got final fragment break } // 如果不是緩衝區滿這個錯誤,也結束這個迴圈,說明遇到了其他非正常的錯誤,需要結束讀取資料了 if e != ErrBufferFull { // unexpected error err = e break } // Make a copy of the buffer. // 將當前讀到的資料存入二維切片中 buf := make([]byte, len(frag)) copy(buf, frag) full = append(full, buf) } // Allocate new buffer to hold the full pieces and the fragment. // 這是真的讀完了,那麼就將二維切片和最後的切片中的資料整合成為一個整個的切片 n := 0 for i := range full { n += len(full[i]) } n += len(frag) // Copy full pieces and fragment in. buf := make([]byte, n) n = 0 for i := range full { // copy返回複製的位元組數 n += copy(buf[n:], full[i]) } copy(buf[n:], frag) return buf, err }
ReadBytes方法返回緩衝區中直到引數指定的字元的字串切片。如果遇到錯誤就返回啦。其中io.EOF是正常的錯誤。
(b *Reader) ReadString(delim byte) (string, error)
func (b *Reader) ReadString(delim byte) (string, error) { bytes, err := b.ReadBytes(delim) return string(bytes), err }
這個方法沒什麼說的,就是結果將[]byte轉化為string
(b *Reader) WriteTo(w io.Writer) (n int64, err error)
func (b *Reader) WriteTo(w io.Writer) (n int64, err error) { // 這個b.writeBuf()方法的作用是將緩衝區b中的資料寫入w中,並且返回寫入的位元組數n,而且將b的獨白位置向後移動n位 n, err = b.writeBuf(w) if err != nil { return } // 如果b.rd是一個實現了io.WriteTo介面的型別,那麼就用WriteTo方法向w中寫入資料 if r, ok := b.rd.(io.WriterTo); ok { m, err := r.WriteTo(w) n += m return n, err } // 如果b.rd是一個實現了io.ReaderFrom介面的型別,那麼就用ReadFrom方法向w中寫資料 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 } // 如果緩衝區不空,先用writeBuf向w中寫資料,直到遇到io.EOF // 也就是這個緩衝區中資料一定要讀完唄,直到底層記憶體中也沒有資料讀 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 } if b.err == io.EOF { b.err = nil } return n, b.readErr() }
WriteTo這個方法的作用就是將緩衝區包括緩衝區底層的資料全部讀取出來存入引數w中
type Writer struct { err error//錯誤資訊 buf []byte//緩衝區 n int//緩衝區中資料的位元組數 wr io.Writer//下層的io.Writer介面 }
Writer的方法
NewWriterSize(w io.Writer, size int) *Writer
func NewWriterSize(w io.Writer, size int) *Writer { // Is it already a 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, } }
NewWriterSize這個方法根據引數size建立一個大小為size的寫入資料緩衝區
NewWriter(w io.Writer) *Writer
func NewWriter(w io.Writer) *Writer { return NewWriterSize(w, defaultBufSize) }
NewWriter方法按照預設緩衝區大小建立一個Writer
(b *Writer) Size() int
func (b *Writer) Size() int { return len(b.buf) }
返回緩衝區大小
(b *Writer) Reset(w io.Writer)
func (b *Writer) Reset(w io.Writer) { b.err = nil// 清除錯誤 b.n = 0 // 丟棄資料 b.wr = w // 將輸出寫入w,w是用來寫入緩衝區中的資料的 }
Reset丟棄緩衝區中的所有資料,清除所有的錯誤,將b重設為將其輸出寫入w
(b *Writer) Flush() error
func (b *Writer) Flush() error { // 首先判斷緩衝區中是否有錯誤資訊 if b.err != nil { return b.err } // 緩衝區中資料為空 if b.n == 0 { return nil } // 向下層io.Writer介面寫入資料 n, err := b.wr.Write(b.buf[0:b.n]) // 寫入沒有出錯,但是寫入的位元組數小於緩衝區中的位元組數 if n < b.n && err == nil { // 錯誤資訊是Short Writer err = io.ErrShortWrite } // 向下層io.Writer介面寫入資料出錯 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 } // 寫入成功,緩衝區中的資料全部寫出去了,設定緩衝區中資料量為0 b.n = 0 return nil }
Flush方法將緩衝區中的資料寫入下層的io.Writer介面
(b *Writer) Available() int
// 緩衝區長度減去緩衝區中現存的資料量,就是緩衝區沒有使用的位元組數 func (b *Writer) Available() int { return len(b.buf) - b.n }
Available方法返回緩衝區中沒有使用的位元組數
(b *Writer) Buffered() int
func (b *Writer) Buffered() int { return b.n }
Buffered方法返回緩衝區中資料的位元組數
(b *Writer) Write(p []byte) (nn int, err error)
func (b *Writer) Write(p []byte) (nn int, err error) { // 如果不存在錯誤,並且p中的資料量大於緩衝區可以獲取的記憶體空間,將資料寫入底層io.Writer介面 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. // 直接向底層io.Writer介面寫資料 n, b.err = b.wr.Write(p) } else { // 先將p的資料寫入緩衝區,記錄寫入的資料量 n = copy(b.buf[b.n:], p) b.n += n // 呼叫Flush方法將資料寫入底層io.Writer b.Flush() } // 累加寫入資料的量 nn += n // 重新切片P 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方法的思想是如果p中的資料不能填充滿緩衝區剩餘的空間,那麼就將p中的資料寫入緩衝區,如果p中的資料量大於緩衝區中剩餘的空間,那麼就直接呼叫底層的io.Writer介面,將資料寫入這個介面,然後可能p中會有剩餘的內容,如果剩餘內容的量還是大於緩衝區可以獲取的空間,那麼就繼續向底層的io.Writer寫入資料,如果剩餘的資料量不比緩衝區可以獲取的空間大,那麼就將資料寫入緩衝區就可以了,但是這個方法返回的寫入的資料量是包括寫入到底層io.writer介面的資料量的
(b *Writer) WriteByte(c byte) error
func (b *Writer) WriteByte(c byte) error { // 慣例先判斷是否存在錯誤 if b.err != nil { return b.err } // 如果緩衝區滿了並且不能將緩衝區中的資料寫入底層的io.Writer介面,那麼就報錯啦 if b.Available() <= 0 && b.Flush() != nil { return b.err } // 寫入一個位元組 b.buf[b.n] = c // 緩衝區中資料量加1 b.n++ return nil }
WriteByte向緩衝區中寫入一個byte的資料
(b *Writer) WriteRune(r rune) (size int, err error)
func (b *Writer) WriteRune(r rune) (size int, err error) { // 小於utf8.RuneSelf的都是單個位元組,在unicode編碼中表示他自己,所以直接寫入單個位元組的資料即可 if r < utf8.RuneSelf { // 呼叫WriteByte()方法 err = b.WriteByte(byte(r)) if err != nil { return 0, err } return 1, nil } if b.err != nil { return 0, b.err } // 獲取可獲取的緩衝區長度 n := b.Available() // utf8.UTFMax表示unicode編碼的最長位元組數,值為4 // 可獲取的緩衝區長度位元組數小於4,就將緩衝區中的資料寫入底層的io.Writer介面 if n < utf8.UTFMax { if b.Flush(); b.err != nil { return 0, b.err } // 重新獲取長度 n = b.Available() if n < utf8.UTFMax { // Can only happen if buffer is silly small. // 呼叫b.WriteString()方法寫入資料,這個方法將會在下面講到 return b.WriteString(string(r)) } } // 將資料r轉化為utf8編碼,並存入緩衝區中,儲存的開始位置是b.n,返回編碼後的位元組數 size = utf8.EncodeRune(b.buf[b.n:], r) // 更新緩衝區中的資料的位元組數 b.n += size return size, nil }
WriteRune寫入一個unicode碼值,並且返回位元組數和可能的錯誤
(b *Writer) WriteString(s string) (int, error)
func (b *Writer) WriteString(s string) (int, error) { nn := 0 // 當字串的長度大於可獲取的緩衝區長度,並且錯誤資訊為nil,那麼就將前面的資料通過Flush()方法寫入底層io.Writer介面 // 然後重新獲取字串的長度和緩衝區的長度 for len(s) > b.Available() && b.err == nil { n := copy(b.buf[b.n:], s) // 強行將s的資料拷貝進緩衝區,並且記錄當前緩衝區中的資料量 b.n += n // 累加已經寫入資料的長度 nn += n // 更新字串 s = s[n:] // 將當前緩衝區的資料寫入底層io.Writer介面,這裡會改變b.n的值 b.Flush() } if b.err != nil { return nn, b.err } // 此時緩衝區可獲取的長度大於了字串剩餘資料的長度,直接將資料寫入緩衝區即可 n := copy(b.buf[b.n:], s) b.n += n nn += n return nn, nil }
WriteString方法向緩衝區中寫入一個字串,返回寫入的位元組數,如果返回的位元組數小於字串的長度,還會返回一個錯誤原因
(b *Writer) ReadFrom(r io.Reader) (n int64, err error)
func (b *Writer) ReadFrom(r io.Reader) (n int64, err error) { // 緩衝區b中沒有資料 if b.Buffered() == 0 { // 通過介面的動態型別判定是否在底層已經實現了io.Writer介面 // 如果實現了,那麼就直接呼叫底層ReadFrom介面的方法將讀緩衝區r的資料寫入寫緩衝區b中 if w, ok := b.wr.(io.ReaderFrom); ok { return w.ReadFrom(r) } } var m int // 開始迴圈 for { // b中沒有可獲取的緩衝區空間 if b.Available() == 0 { // 將寫緩衝區b中的資料寫入底層io.Writer介面 if err1 := b.Flush(); err1 != nil { // 如果出錯,此時n是0,表示沒有資料從讀緩衝區寫入寫緩衝區 return n, err1 } } nr := 0 // maxConsecutiveEmptyReads的值為100,表示每次從記憶體中讀取空的資料的次數不能超過100次 for nr < maxConsecutiveEmptyReads { // 呼叫r的Read方法將讀緩衝區中的資料一直迴圈讀入寫緩衝區 // 如果讀到的資料為空,或者沒有出現讀錯誤,就迴圈讀取,最多100次 m, err = r.Read(b.buf[b.n:]) if m != 0 || err != nil { break } nr++ } // 讀了100次還是沒有資料呢,但是也沒報錯 if nr == maxConsecutiveEmptyReads { return n, io.ErrNoProgress } // 到這一步說明了要麼讀出了資料,要麼出現了錯誤 // 此時位元組數為m的資料已經寫入寫緩衝區中了 // 所以修改b.n的值 b.n += m // 累加已經從讀緩衝區中寫入的值的位元組數 n += int64(m) // 如果遇到了錯誤,還要結束大迴圈 if err != nil { break } } // 有可能錯誤不是真正的錯誤,而是檔案讀完了 if err == io.EOF { // If we filled the buffer exactly, flush preemptively. // 如果此時寫緩衝區已經被填滿了,就將資料寫入底層io.Writer介面 if b.Available() == 0 { err = b.Flush() } else { err = nil } } return n, err }
ReadFrom實現了io.ReadFrom介面
type ReadWriter struct { *Reader *Writer }
NewReadWriter(r *Reader, w *Writer) *ReadWriter
func NewReadWriter(r *Reader, w *Writer) *ReadWriter { return &ReadWriter{r, w} }
NewReadWriter申請建立一個新的、將讀寫操作分派給r和w 的ReadWriter。
結構體Scanner的初始化
type Scanner struct { // 底層的io.Reader介面,Scanner從這介面讀取資料 r io.Reader // The reader provided by the client. // 分割函式 split SplitFunc // The function to split the tokens. // token的最大值 maxTokenSize int // Maximum size of a token; modified by tests. // 用來儲存緩衝區中即將被使用的資料 token []byte // Last token returned by split. // 緩衝區 buf []byte // Buffer used as argument to split. // 緩衝區中第一個沒有被處理的位元組 start int // First non-processed byte in buf. // 緩衝區中的資料的最後一個位元組的位置,start和end類似於Reader中的讀指標和寫指標 end int // End of data in buf. // 記錄錯誤 err error // Sticky error. // 記錄從緩衝區中讀出空token的次數 empties int // Count of successive empty tokens. // 如果已經呼叫了Scan這個函式,那麼說明它的緩衝區已經正在被使用了 scanCalled bool // Scan has been called; buffer is in use. //Scan 向buf中寫資料是否已經完畢 done bool // Scan has finished. }
Scanner的方法
NewScanner(r io.Reader) *Scanner
func NewScanner(r io.Reader) *Scanner { return &Scanner{ r: r, split: ScanLines, maxTokenSize: MaxScanTokenSize, } }
NewScanner建立一個從r讀取資料的Scanner,預設的分割函式是ScanLines
(s *Scanner) Err() error
func (s *Scanner) Err() error { if s.err == io.EOF { return nil } return s.err }
Err方法返回Scanner的非io.EOF錯誤
(s *Scanner) Bytes() []byte
func (s *Scanner) Bytes() []byte { return s.token }
Bytes方法返回資料成員token,是最近一次使用Scanner生成的token,token是會隨著呼叫Scanner而一直變化的
(s *Scanner) Text() string
func (s *Scanner) Text() string { return string(s.token) }
Text方法以string型別返回token
(s *Scanner) Scan() bool
func (s *Scanner) Scan() bool { // Scan已經完成,不需要再寫資料進入Scan的緩衝區了 if s.done { return false } // Scan已經被呼叫了,它的緩衝區正在被使用 s.scanCalled = true // Loop until we have a token. // 進入迴圈 for { // See if we can get a token with what we already have. // If we've run out of data but have an error, give the split function // a chance to recover any remaining, possibly empty token. // s.end>s.start表示緩衝區中有資料 // s.err有可能是一個io.EOF錯誤,也可能是其他錯誤 if s.end > s.start || s.err != nil { // s.split的預設是函式是ScanLines作用是從Scanner的緩衝區讀取一行資料,然後返回前進的位元組數和儲存資料的token advance, token, err := s.split(s.buf[s.start:s.end], s.err != nil) if err != nil { // 如果是最後一個token說明緩衝區中沒有資料了,返回正確 if err == ErrFinalToken { // token中包含緩衝區中的資料 s.token = token // Scan已經完成 s.done = true return true } // 如果是其他錯誤,就將他賦值給s.err s.setErr(err) return false } // s.advance()函式檢查前進的位元組數是否超出了s.end,並且將s.start向後移動advance個位元組 if !s.advance(advance) { return false } // 將返回的token賦值給s.token s.token = token // 如果token不為空 if token != nil { // 沒有出現錯誤或者向token中寫入了非空的位元組 if s.err == nil || advance > 0 { // 將記錄連續寫空token次數的計數器歸零 s.empties = 0 } else { // 出現了錯誤或者寫入了空token,計數器加1 // Returning tokens not advancing input at EOF. s.empties++ // 如果超出連續寫空token的最大次數,就panic了 if s.empties > maxConsecutiveEmptyReads { panic("bufio.Scan: too many empty tokens without progressing") } } return true } } // We cannot generate a token with what we are holding. // If we've already hit EOF or an I/O error, we are done. // 使用s.splict函式不能產生一個token(只要上面產生了空的token並且有錯誤資訊就能進入這個if分支) if s.err != nil { // Shut it down. // 清空資料 s.start = 0 s.end = 0 return false } // Must read more data. // First, shift data to beginning of buffer if there's lots of empty space // or space is needed. // s.start>0說明了這個緩衝區已經被使用了,但是如果被使用了很多,就要將緩衝區後面的資料向前移動到緩衝區開始的地方 if s.start > 0 && (s.end == len(s.buf) || s.start > len(s.buf)/2) { copy(s.buf, s.buf[s.start:s.end]) s.end -= s.start s.start = 0 } // Is the buffer full? If so, resize. // s.start==0,此時緩衝區滿了,那麼就要擴大緩衝區 if s.end == len(s.buf) { // Guarantee no overflow in the multiplication below. // 生成最大的int型別的值 const maxInt = int(^uint(0) >> 1) // 緩衝區已經不能再擴大了,就返回錯誤 if len(s.buf) >= s.maxTokenSize || len(s.buf) > maxInt/2 { s.setErr(ErrTooLong) return false } // 緩衝區變為原來的2倍 newSize := len(s.buf) * 2 if newSize == 0 { // 如果兩倍以後緩衝區大小還是0,就直接使用預設的緩衝區大小 newSize = startBufSize } // 如果新的緩衝區大小大於最大的限制,就使用最大的限制大小 if newSize > s.maxTokenSize { newSize = s.maxTokenSize } // 建立新的換衝過去 newBuf := make([]byte, newSize) // 移植資料,並更新下標等資料成員 copy(newBuf, s.buf[s.start:s.end]) s.buf = newBuf s.end -= s.start s.start = 0 } // Finally we can read some input. Make sure we don't get stuck with // a misbehaving Reader. Officially we don't need to do this, but let's // be extra careful: Scanner is for safe, simple jobs. for loop := 0; ; { // 將底層Reader介面中的資料讀進緩衝區中,以拼接的方式讀取 n, err := s.r.Read(s.buf[s.end:len(s.buf)]) s.end += n if err != nil { s.setErr(err) break } // 讀出了資料,不是空資料,那麼空計數器歸零,並退出迴圈 if n > 0 { s.empties = 0 break } // 讀到空資料,迴圈變數加1 loop++ // 連續讀出空資料的次數大於了限制次數就報錯了 if loop > maxConsecutiveEmptyReads { s.setErr(io.ErrNoProgress) break } } } }
Scan方法從緩衝區中讀出一個token儲存在s.token中,token的劃分是通過分割函式劃分,例如SplitLines就是一行資料是一個token
(s *Scanner) Buffer(buf []byte, max int)
func (s *Scanner) Buffer(buf []byte, max int) { if s.scanCalled { panic("Buffer called after Scan") } s.buf = buf[0:cap(buf)] // 緩衝區的最大限制 s.maxTokenSize = max }
Buffer方法在緩衝區被使用前初始化緩衝區,並且設定緩衝區的最大限制
(s *Scanner) Split(split SplitFunc)
func (s *Scanner) Split(split SplitFunc) { if s.scanCalled { panic("Split called after Scan") } s.split = split }
Split在緩衝區被使用前,設定緩衝區的分割函式
ScanBytes(data []byte, atEOF bool) (advance int, token []byte, err error)
func ScanBytes(data []byte, atEOF bool) (advance int, token []byte, err error) { if atEOF && len(data) == 0 { return 0, nil, nil } return 1, data[0:1], nil }
ScanBytes函式按照位元組分割,每次分割一個位元組
ScanRunes(data []byte, atEOF bool) (advance int, token []byte, err error)
func ScanRunes(data []byte, atEOF bool) (advance int, token []byte, err error) { // 沒有資料,且到資料最後一行 if atEOF && len(data) == 0 { return 0, nil, nil } // Fast path 1: ASCII. // 小於urf8.Runeself的表示他們自己,只有一個位元組 if data[0] < utf8.RuneSelf { return 1, data[0:1], nil } // Fast path 2: Correct UTF-8 decode without error. // 使用utf8解碼,獲取解碼的原始[]byte長度 _, width := utf8.DecodeRune(data) if width > 1 { // It's a valid encoding. Width cannot be one for a correctly encoded // non-ASCII rune. // 解碼成功,返回一個utf8編碼的[]byte return width, data[0:width], nil } // We know it's an error: we have width==1 and implicitly r==utf8.RuneError. // Is the error because there wasn't a full rune to be decoded? // FullRune distinguishes correctly between erroneous and incomplete encodings. // utf8.FullRune()說的是不是一個完整的Rune編碼,沒弄懂 if !atEOF && !utf8.FullRune(data) { // Incomplete; get more bytes. return 0, nil, nil } // We have a real UTF-8 encoding error. Return a properly encoded error rune // but advance only one byte. This matches the behavior of a range loop over // an incorrectly encoded string. // 真的遇到錯誤,前進一個位元組 return 1, errorRune, nil }
ScanRunes按照unicode編碼分割,每次分割一個unicode編碼
ScanLines(data []byte, atEOF bool) (advance int, token []byte, err error)
func ScanLines(data []byte, atEOF bool) (advance int, token []byte, err error) { // atEOF是一個標識Reader是否還有資料(是不是最後一行)的標誌 // 如果是最後一行,並且最後一行沒有資料 if atEOF && len(data) == 0 { return 0, nil, nil } // 在資料data中找到了`\n`,那麼就將`\n`之前的資料作為token返回 if i := bytes.IndexByte(data, '\n'); i >= 0 { // We have a full newline-terminated line. // dropCR函式的作用是去掉[]byte末尾的`\r`(回車) // token向前移動了i+1個位元組(包含了\r\n) return i + 1, dropCR(data[0:i]), nil } // If we're at EOF, we have a final, non-terminated line. Return it. // 到了資料的最後一行,但是有資料,就將資料末尾的`\r`去掉,直接返回資料 if atEOF { return len(data), dropCR(data), nil } // Request more data. // 直接就是沒有資料,也不是檔案末尾識別符號 return 0, nil, nil }
ScanLines會將每一行文字去掉末尾換行符然後作為一個token返回,並且返回的行可以是空串,最後一行即使沒有換行符也要作為一個token返回
ScanWords(data []byte, atEOF bool) (advance int, token []byte, err error)
func ScanWords(data []byte, atEOF bool) (advance int, token []byte, err error) { // Skip leading spaces. start := 0 for width := 0; start < len(data); start += width { var r rune r, width = utf8.DecodeRune(data[start:]) if !isSpace(r) { break } } // Scan until space, marking end of word. for width, i := 0, start; i < len(data); i += width { var r rune r, width = utf8.DecodeRune(data[i:]) // isSpace函式檢查r是不是一個unicode的空字元,unicode的空字元挺多的,可以去看看 if isSpace(r) { // 前進的位元組數包含了空字元,但是返回的token中不包含空字元喲 return i + width, data[start:i], nil } } // If we're at EOF, we have a final, non-empty, non-terminated word. Return it. // 在資料最後一行,並且最後一行資料不為空,直接返回資料即可 if atEOF && len(data) > start { return len(data), data[start:], nil } // Request more data. // 引數data中沒有資料,返回0,nil,nil return start, nil, nil }
ScanWords以空白分割,所以永遠不會返回一個空的token