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

6,015 阅读5分钟
原文链接: lailin.xyz

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

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

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

源码实现

1. ReadAll

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

// 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当中恢复回来

// 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方法都会从这个位置开始

// 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方法

// 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读取所有数据。

示意图: 示意图

// 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
}