go标准库——ioutil.ReadAll的实现

注:本文已发布超过一年,请注意您所使用工具的相关版本是否适用

最近准备学习一下golang的标准库,详细的阅读部分源码,这个目录记录一下学习的过程和心得

go语言的ioutil包提供了很多方便的io操作的工具集,本文主要详细分析ReadAll方法的源码实现。

ReadAll是很常用的一个方法,用来一次性的读取io.Reader当中的数据。

源码实现

1. ReadAll

阅读下方的源码我们可以发现,ReadAll其实调用了一个非导出的方法,我们一步一步的追踪

1
2
3
4
5
6
7
8
9
10
// ReadAll reads from r until an error or EOF and returns the data it read.
// A successful call returns err == nil, not err == EOF. Because ReadAll is
// defined to read from src until EOF, it does not treat an EOF from Read
// as an error to be reported.
// ReadAll从r读取数据直到EOF或遇到error,返回读取的数据和遇到的错误。
// 成功的调用返回的err为nil而非EOF。
// 因为本函数定义为读取r直到EOF,它不会将读取返回的EOF视为应报告的错误。
func ReadAll(r io.Reader) ([]byte, error) {
return readAll(r, bytes.MinRead)
}
2. readAll

阅读这个函数代码,可以发现,ioutil.ReadAll实质上是使用bytes.buffer实现的,在这里面调用了两个bytes.buffer的方法,一个用来初始化buffer的容量,一个用于读取所有的io.Reader数据

除此之外我们还可以学习到go当中panic recover的使用方法,在buffer当中,如果无法分配足够的内存的时候,会直接panic bytes.ErrTooLarge的错误,但是,这个方法我们期望把这个错误给返回回来,这个时候就可以使用defer recoverpanic当中恢复回来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// readAll reads from r until an error or EOF and returns the data it read
// from the internal buffer allocated with a specified capacity.
// readAll从r读取到一个错误或EOF,并返回从指定容量分配的内部缓冲区中读取的数据。
func readAll(r io.Reader, capacity int64) (b []byte, err error) {
// 新建了一个buffer
var buf bytes.Buffer
// If the buffer overflows, we will get bytes.ErrTooLarge.
// Return that as an error. Any other panic remains.
// 如果buffer溢出了,会得到一个bytes.ErrTooLarge的错误
// 如果得到的是bytes.ErrTooLarge错误,将其返回,其他panic错误,仍然panic
defer func() {
e := recover()
if e == nil {
return
}
if panicErr, ok := e.(error); ok && panicErr == bytes.ErrTooLarge {
err = panicErr
} else {
panic(e)
}
}()
// 判断capacity的值是否超过了int类型的上限
if int64(int(capacity)) == capacity {
// 向buffer当中增加capacity的容量
buf.Grow(int(capacity))
}
// 使用buffer ReadFrom 方法读取所有的io.Reader数据
_, err = buf.ReadFrom(r)
return buf.Bytes(), err
}
3. buffer

要看buffer相关函数的实现,先看看buffer的定义,不然到时候可能会懵B

主要会用到两个字段,一个是buf,buf的内容是用offlen(buf)的,off之前的表示已经读取了数据,off表示当前位置,bufferwrite和read方法都会从这个位置开始

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// A Buffer is a variable-sized buffer of bytes with Read and Write methods.
// The zero value for Buffer is an empty buffer ready to use.
// Buffer是一个实现了读写方法的可变大小的字节缓冲。
// 本类型的零值是一个空的可用于读写的缓冲。
type Buffer struct {
buf []byte // contents are the bytes buf[off : len(buf)]
off int // read at &buf[off], write at &buf[len(buf)]
lastRead readOp // last read operation, so that Unread* can work correctly.
// FIXME: lastRead can fit in a single byte

// memory to hold first slice; helps small buffers avoid allocation.
// FIXME: it would be advisable to align Buffer to cachelines to avoid false
// sharing.
bootstrap [64]byte
}
4. buffer.Grow

这个方法主要用来增加缓冲区的内存,实际还是调用了非导出的grow方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Grow grows the buffer's capacity, if necessary, to guarantee space for
// another n bytes. After Grow(n), at least n bytes can be written to the
// buffer without another allocation.
// If n is negative, Grow will panic.
// If the buffer can't grow it will panic with ErrTooLarge.
// 必要时会增加缓冲的容量,以保证n字节的剩余空间。
// 调用Grow(n)后至少可以向缓冲中写入n字节数据而无需申请内存。
// 如果n小于零或者不能增加容量都会panic ErrTooLarge 错误。
func (b *Buffer) Grow(n int) {
if n < 0 {
panic("bytes.Buffer.Grow: negative count")
}
m := b.grow(n)
b.buf = b.buf[0:m]
}
5. buffer.ReadFrom

主要看看这个方法的实现,实现了从io.Reader读取所有数据。

示意图:
示意图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
// ReadFrom reads data from r until EOF and appends it to the buffer, growing
// the buffer as needed. The return value n is the number of bytes read. Any
// error except io.EOF encountered during the read is also returned. If the
// buffer becomes too large, ReadFrom will panic with ErrTooLarge.
// ReadFrom从r中读取数据直到结束并将读取的数据写入缓冲中,如必要会增加缓冲容量。
// 返回值n为从r读取并写入b的字节数;会返回读取时遇到的除了io.EOF之外的错误。
// 如果缓冲太大,ReadFrom会采用错误值ErrTooLarge引发panic。
func (b *Buffer) ReadFrom(r io.Reader) (n int64, err error) {
// const opInvalid = 0 // Non-read operation. 表示之前没有读操作
b.lastRead = opInvalid
// If buffer is empty, reset to recover space.
// 如果缓冲区为空,重置为恢复空间。
if b.off >= len(b.buf) {
b.Reset()
}
// 循环读取io.Reader 的数据
for {
// 判断当前剩余空间是否小于MinRead,MinRead = 512
if free := cap(b.buf) - len(b.buf); free < MinRead {
// not enough space at end
// 空间不足
// 新建一个buf
newBuf := b.buf
// 判断实际剩余容量是否小于MinRead = 512
if b.off+free < MinRead {
// not enough space using beginning of buffer;
// double buffer capacity
// 实际剩余空间不足,分配双倍的缓冲空间,缓冲的最小值为MinRead所以加上一个MinRead,避免双倍之后仍然比MinRead小
// makeSlice函数用于分配缓冲空间,如果分配失败会panic ErrTooLarge 错误
newBuf = makeSlice(2*cap(b.buf) + MinRead)
}
// 将原有buf数据复制到新的buf
copy(newBuf, b.buf[b.off:])
// len(b.buf)-b.off 就等于buf当前内容的长度
// 例子: a:=make([]byte,20)
// b:=a[:10]
// len(b) // 10
// cap(b) //20
b.buf = newBuf[:len(b.buf)-b.off]
// 将off置0
b.off = 0
}
// 从io.Reader当中读取数据,传入buf的剩余空间
// 第一种情况,数据读取完毕,返回读取长度m以及,io.EOF错误
// 第二种情况, 数据未读完,遇到错误
// 第三种情况,数据未读完,缓冲区容量不够,返回读取数据长度m以及nil
m, e := r.Read(b.buf[len(b.buf):cap(b.buf)])
// 只获取有数据的buf,无数据的空间转化为cap
b.buf = b.buf[0 : len(b.buf)+m]
n += int64(m)
// 数据读取完毕跳出循环
if e == io.EOF {
break
}
// 遇到错误返回
if e != nil {
return n, e
}
}
return n, nil // err is EOF, so return nil explicitly
}

关注我获取更新

wechat
知乎
github

猜你喜欢


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议,转载请注明出处,禁止全文转载