Source File
buffer.go
Belonging Package
github.com/go-sql-driver/mysql
// Go MySQL Driver - A MySQL-Driver for Go's database/sql package
//
// Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at http://mozilla.org/MPL/2.0/.
package mysql
import (
)
const defaultBufSize = 4096
const maxCachedBufSize = 256 * 1024
// A buffer which is used for both reading and writing.
// This is possible since communication on each connection is synchronous.
// In other words, we can't write and read simultaneously on the same connection.
// The buffer is similar to bufio.Reader / Writer but zero-copy-ish
// Also highly optimized for this particular use case.
// This buffer is backed by two byte slices in a double-buffering scheme
type buffer struct {
buf []byte // buf is a byte buffer who's length and capacity are equal.
nc net.Conn
idx int
length int
timeout time.Duration
dbuf [2][]byte // dbuf is an array with the two byte slices that back this buffer
flipcnt uint // flipccnt is the current buffer counter for double-buffering
}
// newBuffer allocates and returns a new buffer.
func newBuffer( net.Conn) buffer {
:= make([]byte, defaultBufSize)
return buffer{
buf: ,
nc: ,
dbuf: [2][]byte{, nil},
}
}
// flip replaces the active buffer with the background buffer
// this is a delayed flip that simply increases the buffer counter;
// the actual flip will be performed the next time we call `buffer.fill`
func ( *buffer) () {
.flipcnt += 1
}
// fill reads into the buffer until at least _need_ bytes are in it
func ( *buffer) ( int) error {
:= .length
// fill data into its double-buffering target: if we've called
// flip on this buffer, we'll be copying to the background buffer,
// and then filling it with network data; otherwise we'll just move
// the contents of the current buffer to the front before filling it
:= .dbuf[.flipcnt&1]
// grow buffer if necessary to fit the whole packet.
if > len() {
// Round up to the next multiple of the default size
= make([]byte, ((/defaultBufSize)+1)*defaultBufSize)
// if the allocated buffer is not too large, move it to backing storage
// to prevent extra allocations on applications that perform large reads
if len() <= maxCachedBufSize {
.dbuf[.flipcnt&1] =
}
}
// if we're filling the fg buffer, move the existing data to the start of it.
// if we're filling the bg buffer, copy over the data
if > 0 {
copy([:], .buf[.idx:])
}
.buf =
.idx = 0
for {
if .timeout > 0 {
if := .nc.SetReadDeadline(time.Now().Add(.timeout)); != nil {
return
}
}
, := .nc.Read(.buf[:])
+=
switch {
case nil:
if < {
continue
}
.length =
return nil
case io.EOF:
if >= {
.length =
return nil
}
return io.ErrUnexpectedEOF
default:
return
}
}
}
// returns next N bytes from buffer.
// The returned slice is only guaranteed to be valid until the next read
func ( *buffer) ( int) ([]byte, error) {
if .length < {
// refill
if := .fill(); != nil {
return nil,
}
}
:= .idx
.idx +=
.length -=
return .buf[:.idx], nil
}
// takeBuffer returns a buffer with the requested size.
// If possible, a slice from the existing buffer is returned.
// Otherwise a bigger buffer is made.
// Only one buffer (total) can be used at a time.
func ( *buffer) ( int) ([]byte, error) {
if .length > 0 {
return nil, ErrBusyBuffer
}
// test (cheap) general case first
if <= cap(.buf) {
return .buf[:], nil
}
if < maxPacketSize {
.buf = make([]byte, )
return .buf, nil
}
// buffer is larger than we want to store.
return make([]byte, ), nil
}
// takeSmallBuffer is shortcut which can be used if length is
// known to be smaller than defaultBufSize.
// Only one buffer (total) can be used at a time.
func ( *buffer) ( int) ([]byte, error) {
if .length > 0 {
return nil, ErrBusyBuffer
}
return .buf[:], nil
}
// takeCompleteBuffer returns the complete existing buffer.
// This can be used if the necessary buffer size is unknown.
// cap and len of the returned buffer will be equal.
// Only one buffer (total) can be used at a time.
func ( *buffer) () ([]byte, error) {
if .length > 0 {
return nil, ErrBusyBuffer
}
return .buf, nil
}
// store stores buf, an updated buffer, if its suitable to do so.
func ( *buffer) ( []byte) error {
if .length > 0 {
return ErrBusyBuffer
} else if cap() <= maxPacketSize && cap() > cap(.buf) {
.buf = [:cap()]
}
return nil
}
![]() |
The pages are generated with Golds v0.6.7. (GOOS=linux GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @Go100and1 (reachable from the left QR code) to get the latest news of Golds. |