You have to work really hard to look effortless!

Wechat search public number [long Coding road], together From Zero To Hero!

preface

The structure of the bytes.Buffer is divided into three sections: Read data, unread data, free part, forget the friends to review oh. Buffer exists, is for reading and writing services, so in this article we will learn how to read and write methods using buffer implementation!

Source code analysis

Write()

The Write method writes data from byte slice P to the buffer slice, returning the length of bytes written and the error generated

Because Write calls the grow method, a panic with ErrTooLarge occurs if the underlying buffer slice is too large to be redistributed

func (b *Buffer) Write(p []byte) (n int, err error) {
	b.lastRead = opInvalid

	Ensure that the underlying buffer slice can be written to len(p) bytes by calling tryGrowByReslice and grow.
	m, ok := b.tryGrowByReslice(len(p))
	if! ok { m = b.grow(len(p))
	}

	// Buf has len(p) free bytes from position m
	return copy(b.buf[m:], p), nil
}
Copy the code

WriteString()

The WriteString and Write methods are similar. The string s is written to the underlying buffer slice buf, returning the number of bytes successfully written and an error

func (b *Buffer) WriteString(s string) (n int, err error) {
	b.lastRead = opInvalid

	Ensure that the length of the underlying buffer slice can be written to len(s) bytes by calling tryGrowByReslice and grow.
	m, ok := b.tryGrowByReslice(len(s))
	if! ok { m = b.grow(len(s))
	}
	Buf has len(s) free bytes from position m, and copies s to the underlying buffer slice
	return copy(b.buf[m:], s), nil
}
Copy the code

WriteByte()

Similar to the Write method, Write a single byte instead of a byte slice

func (b *Buffer) WriteByte(c byte) error {
	b.lastRead = opInvalid

	Ensure that the underlying buffer slice can be written to 1 byte by calling the tryGrowByReslice and grow methods
	m, ok := b.tryGrowByReslice(1)
	if! ok { m = b.grow(1)}// m indicates the start position of the write after expansion. The value is directly assigned to the byte to be written
	b.buf[m] = c
	return nil
}
Copy the code

WriteRune()

Similar to the Write method, except that rune is written instead of byte slicing

func (b *Buffer) WriteRune(r rune) (n int, err error) {

	// if r < utf8.runeself, r is a byte, then call WriteByte directly
	if r < utf8.RuneSelf {
		b.WriteByte(byte(r))
		return 1.nil
	}
	b.lastRead = opInvalid

	// Ensure that the length of the underlying buffer slice can be written to UTf8.utfmax by calling tryGrowByReslice and grow
	m, ok := b.tryGrowByReslice(utf8.UTFMax)
	if! ok { m = b.grow(utf8.UTFMax) }Len (buf)+ utf8.utfmax. Utf8.utfmax is the maximum possible length of rune, but the current rune size may be smaller than this value
	// Call utf8.encoderune () to write rune r to buf, returning the number of bytes written
	n = utf8.EncodeRune(b.buf[m:m+utf8.UTFMax], r)

	// Update the length of buf because n<= utf8.utfmax
	b.buf = b.buf[:m+n]
	return n, nil
}
Copy the code

ReadFrom()

  • The ReadFrom method reads data from Reader R and writes it to the underlying buffer slice buf, returning the number of bytes written and the error generated
  • The grow method is called to increase the size of the buffer slice if the size of the buffer slice is insufficient during data reading and writing
  • Read and write and this keeps going through the loop until it generates an error, and if it eventually generates an EOF error, that is, reader R reads the data at the end of the file, this method will eventually return nil error because it’s done
// The minimum free space left by the buffer slice
// This parameter is used by the ReadFrom method, which is the minimum amount of free space left by the buF when writing data from a Reader to the underlying buffer slice
const MinRead = 512


func (b *Buffer) ReadFrom(r io.Reader) (n int64, err error) {
	b.lastRead = opInvalid

	// A for loop that reads and writes data until it encounters an EOF error or other error
	for {
    
		// Ensure that at least MinRead is set free and return the start position of the write I
		i := b.grow(MinRead)

		// The grow method changes the length to I +MinRead
		b.buf = b.buf[:i]

		// the space from I to the end of cap(buf) is all available for reader r to read and write
		// The Read method returns the number of bytes Read and the error generated. By definition, m should be processed first, then e
		m, e := r.Read(b.buf[i:cap(b.buf)])
		if m < 0 {
			panic(errNegativeRead)
		}

		// If m is greater than or equal to 0, data is being read and written to buF
		b.buf = b.buf[:i+m]
		// Number of read bytes n plus m
		n += int64(m)

		// If Reader r encounters an EOF error, it has completed reading data, error=nil
		if e == io.EOF {
			return n, nil // e is EOF, so return nil explicitly
		}

		// Another error is encountered, return error
		ife ! =nil {
			return n, e
		}
	}
}
Copy the code

The above describes the operations related to writing to the buffer. Next, we will look at the operations related to reading.

WriteTo()

The WriteTo method reads the data in the byte buffer slice and sends it to Writer W for consumption. Writer W finally returns the number of bytes consumed by Writer W and the error generated

func (b *Buffer) WriteTo(w io.Writer) (n int64, err error) {
	b.lastRead = opInvalid

	// nBytes: length of unread data
	if nBytes := b.Len(); nBytes > 0 {
		// If the length of unread data is greater than 0, the unread data from off to len() is written to Writer W
		// Write Returns the number of bytes consumed and the error generated
		m, e := w.Write(b.buf[b.off:])
		// If the consumed length is greater than the available length, it is not logical, panic
		if m > nBytes {
			panic("bytes.Buffer.WriteTo: invalid Write count")}// If m bytes are consumed, the read count is increased by m
		b.off += m

		// Consume bytes n = int64(m)
		n = int64(m)

		// If error is generated, return
		ife ! =nil {
			return n, e
		}

		// According to the IO.Writer interface definition of Write method, if the number of writes m! = nBytes, must return error! =nil
		// Hence e! =nil is absolutely true, it's going to return, so it's not going to get to this step, so it's kind of doing a double check
		ifm ! = nBytes {return n, io.ErrShortWrite
		}
	}

	// Call the Reset() method to Reset the unread data in the buffer slice
	b.Reset()
	return n, nil
}
Copy the code

Read()

The Read method reads data from the underlying buffer byte slice buf and writes it to byte slice P

// The number of bytes read by the method, and the error generated
func (b *Buffer) Read(p []byte) (n int, err error) {
	b.lastRead = opInvalid

  Error =nil if len(p)=0 and no data is readable in buf, otherwise error=EOF
	// If the unread data part is empty, there is no data to read
	if b.empty() {
		// First the byte buffer slice is reset
		b.Reset()
		Len (p)=0, error=nil
		if len(p) == 0 {
			return 0.nil
		}
		// Read less than len(p), return EOF error
		return 0, io.EOF
	}

	// If there is unread data, call copy, copy data from off to p, return the number of bytes copied
	n = copy(p, b.buf[b.off:])

	// Update the read count
	b.off += n

	// Update lastRead to opRead
	if n > 0 {
		b.lastRead = opRead
	}
	// Return the number of bytes read n, error=nil
	return n, nil
}
Copy the code

Next()

The Next method returns the first n bytes of unread data, or all unread data if the length is less than n bytes. Because the data returned by the method is a SLICE based on BUF, there is a risk of data leakage, and the data is valid until the next call to the Read or write methods, because the underlying data is modified.

func (b *Buffer) Next(n int) []byte {
	b.lastRead = opInvalid

	// m: length of unread data
	m := b.Len()

	// If the number of bytes required is greater than the length of unread data, then n=m
	if n > m {
		n = m
	}

	// Assign data to the required n bytes
	data := b.buf[b.off : b.off+n]

	// The read count is increased by n
	b.off += n
	if n > 0 {
		b.lastRead = opRead
	}
	return data
}
Copy the code

ReadByte()

Similar to the Read method, ReadByte reads a byte and returns the Read bytes and the error generated

func (b *Buffer) ReadByte(a) (byte, error) {

	// If there is no unread data, reset and return EOF error
	if b.empty() {
		// Buffer is empty, reset to recover space.
		b.Reset()
		return 0, io.EOF
	}

	// Reads a byte, then modifies the read count
	c := b.buf[b.off]
	b.off++
	b.lastRead = opRead
	return c, nil
}
Copy the code

ReadRune()

Like the Read method, ReadRune reads a UTF-8 encoded rune and returns the value, size, and error of rune

func (b *Buffer) ReadRune(a) (r rune, size int, err error) {
	// If there is no data to read, reset and return EOF error
	if b.empty() {
		b.Reset()
		return 0.0, io.EOF
	}

	// c stands for the first byte to be read
	c := b.buf[b.off]
	// If c < utf8.runeself, c is a one-byte rune, return the rune, add one to the read count
	if c < utf8.RuneSelf {
		b.off++
		b.lastRead = opReadRune1
		return rune(c), 1.nil
	}

	// From the read count position, call utf8.decoderune, which returns rune from the read count position and the corresponding number of bytes
	r, n := utf8.DecodeRune(b.buf[b.off:])

	// Change the read count
	b.off += n

	/ / modify lastRead
	b.lastRead = readOp(n)
	return r, n, nil
}
Copy the code

UnreadRune()

A rollback of a rune is valid only if the method is called after ReadRune. It is illegal to call the method after any other read method because the related read method records lastRead = opRead, not opReadRune*

func (b *Buffer) UnreadRune(a) error {

	// lastRead <= opInvalid, indicating that the last call was non-readRune and cannot be rolled back
	if b.lastRead <= opInvalid {
		return errors.New("bytes.Buffer: UnreadRune: previous operation was not a successful ReadRune")}/ / back to back
	if b.off >= int(b.lastRead) {
		b.off -= int(b.lastRead)
	}
	// The rollback can be performed only once
	b.lastRead = opInvalid
	return nil
}
Copy the code

UnreadByte()

Rollback a byte. This method is less demanding than UnreadRune, and any read-related method can rollback a byte

var errUnreadByte = errors.New("bytes.Buffer: UnreadByte: previous operation was not a successful read")

func (b *Buffer) UnreadByte(a) error {
	// only lastRead == opInvalid can be returned.
	if b.lastRead == opInvalid {
		return errUnreadByte
	}

	// Can be rolled back only once
	b.lastRead = opInvalid

	// The read count is reduced by one
	if b.off > 0 {
		b.off--
	}
	return nil
}
Copy the code

readSlice()

Private methods, readSlice reads unread data until it finds the delim character and stops, then returns the traversed data. The returned data is a reference based on the underlying buffer slice, and there is a risk of data leakage.

func (b *Buffer) readSlice(delim byte) (line []byte, err error) {

	// Call IndexByte(), starting at the off position, to find the first index where Delim appears, and return -1 if not found
	i := IndexByte(b.buf[b.off:], delim)

	// Since the index in the previous step started at 0, we need to add 1
	end := b.off + i + 1

	// All unread data needs to be returned, so end is the buffer array length and err is EOF
	if i < 0 {
		end = len(b.buf)
		err = io.EOF
	}

	// Returns the traversed data to update the read count
	line = b.buf[b.off:end]
	b.off = end

	// This operation is also opRead
	b.lastRead = opRead

	// Return data and error
	return line, err
}
Copy the code

ReadBytes()

ReadBytes iterates through the unread data until it encounters a delimiter with a byte value of Delim, then returns the traversed data (including the delimiter) and the resulting error.

ReadBytes directly calls readSlice, so only one case returns error! =nil, that is, in unread data, all unread data and an EOF error are returned if the separator is not found after traversing all data.

func (b *Buffer) ReadBytes(delim byte) (line []byte, err error) {
	// Call the readSlice() method directly, but it returns a reference to the underlying slice
	// The readSlice() method has modified the read count and lastRead
	slice, err := b.readSlice(delim)

	// Since readSlice() returns references, the data may have been modified by other method calls, so copy the data
	line = append(line, slice...)
	return line, err
}
Copy the code

ReadString()

ReadString is similar to ReadBytes except that it returns a string.

func (b *Buffer) ReadString(delim byte) (line string, err error) {
	// Call the readSlice() method directly, but it returns a reference to the underlying slice
	// The readSlice() method has modified the read count and lastRead
	slice, err := b.readSlice(delim)
	// Returns as a string
	return string(slice), err
}
Copy the code

NewBuffer()

  • Instantiate a Buffer, using the passed byte slice buf as the underlying Buffer byte slice, or passing nil
  • After initialization, the caller can no longer manipulate the passed byte slice BUf, otherwise data correctness will be affected
  • The array of passed bytes is used to read the existing data in the slice, or to write the data in the write method, so the size of the passed byte slice should not be 0
  • Bytes.buffer is out-of-the-box. In most cases, just new(Buffer) or declare a variable will do. There is no need to call the NewBuffer() method
func NewBuffer(buf []byte) *Buffer { return &Buffer{buf: buf} }
Copy the code

NewBufferString()

  • NewBufferString is passed a string to initialize bytes.buffer, so the underlying byte Buffer slice has an initial value and data to read.
  • Bytes.buffer is available out of the box. In most cases, just new(Buffer) or declare a variable will do, without calling the NewBufferString() method
func NewBufferString(s string) *Buffer {
	return &Buffer{buf: []byte(s)}
}
Copy the code

Use the sample

The following example uses bytes.buffer as a Buffer to complete a file copy operation.

func main(a) {
	var buffer bytes.Buffer
	srcFile, _ := os.OpenFile("test.txt", os.O_RDWR, 0666)
	n, err := buffer.ReadFrom(srcFile)
	fmt.Println(n, err)                     // 303190 <nil>
	fmt.Println(buffer.Len(), buffer.Cap()) / / 303190 523776

	targetFile, _ := os.OpenFile("target.txt", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
	n, err = buffer.WriteTo(targetFile)
	fmt.Println(n, err) 					          // 303190 <nil>
	fmt.Println(buffer.Len(), buffer.Cap()) / / 0, 523776
}
Copy the code

conclusion

In this article we learned about the source code implementation for reading and writing bytes.buffer. For write operations, ensure that there is sufficient free space before copying data to the unread part of the buffer. For read operations, the unread part of the data is copied out and the read count is updated.

As the saying goes, you can win a hundred battles with no danger of defeat. If you understand the principle of bytes.Buffer, you will be much more comfortable using it.

More and more

Personal blog: lifelmy.github. IO /

Wechat official account: Long Coding road