стандартная библиотека go — реализация ioutil.ReadAll

Go

последовательность

Недавно я собираюсь изучить стандартную библиотеку golang, подробно прочитать исходный код, в этом каталоге записан процесс обучения и опыт.

язык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данные

Кроме того, мы также можем узнать, как использовать паническое восстановление в ходу.bufferСреди них, если не может быть выделено достаточно памяти, она будет напрямуюpanic bytes.ErrTooLargeОшибка, однако, этот метод мы ожидаем вернуть эту ошибку, на этот раз можно использоватьdefer recoverотpanicвосстановление

// 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контент используетсяoffприбытьlen(buf)из,offПредыдущее представление прочитало данные,offпредставляет текущую позицию,bufferизwrite和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
}