Skip to content

Commit 1caf647

Browse files
committed
Merge pull request #55 from go-sql-driver/zero-copy
zero-copy buffer
2 parents 26af0eb + f3fa1f4 commit 1caf647

File tree

3 files changed

+39
-37
lines changed

3 files changed

+39
-37
lines changed

buffer.go

+35-32
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,9 @@
99

1010
package mysql
1111

12-
import (
13-
"io"
14-
)
12+
import "io"
1513

16-
const (
17-
defaultBufSize = 4096
18-
)
14+
const defaultBufSize = 4096
1915

2016
type buffer struct {
2117
buf []byte
@@ -31,11 +27,19 @@ func newBuffer(rd io.Reader) *buffer {
3127
}
3228
}
3329

34-
// fill reads at least _need_ bytes in the buffer
35-
// existing data in the buffer gets lost
30+
// fill reads into the buffer until at least _need_ bytes are in it
3631
func (b *buffer) fill(need int) (err error) {
32+
// move existing data to the beginning
33+
if b.length > 0 && b.idx > 0 {
34+
copy(b.buf[0:b.length], b.buf[b.idx:])
35+
}
36+
37+
// grow buffer if necessary
38+
if need > len(b.buf) {
39+
b.grow(need)
40+
}
41+
3742
b.idx = 0
38-
b.length = 0
3943

4044
var n int
4145
for b.length < need {
@@ -51,34 +55,33 @@ func (b *buffer) fill(need int) (err error) {
5155
return
5256
}
5357

54-
// read len(p) bytes
55-
func (b *buffer) read(p []byte) (err error) {
56-
need := len(p)
57-
58-
if b.length < need {
59-
if b.length > 0 {
60-
copy(p[0:b.length], b.buf[b.idx:])
61-
need -= b.length
62-
p = p[b.length:]
63-
64-
b.idx = 0
65-
b.length = 0
66-
}
58+
// grow the buffer to at least the given size
59+
// credit for this code snippet goes to Maxim Khitrov
60+
// https://groups.google.com/forum/#!topic/golang-nuts/ETbw1ECDgRs
61+
func (b *buffer) grow(size int) {
62+
// If append would be too expensive, alloc a new slice
63+
if size > 2*cap(b.buf) {
64+
newBuf := make([]byte, size)
65+
copy(newBuf, b.buf)
66+
b.buf = newBuf
67+
return
68+
}
6769

68-
if need >= len(b.buf) {
69-
var n int
70-
has := 0
71-
for err == nil && need > has {
72-
n, err = b.rd.Read(p[has:])
73-
has += n
74-
}
75-
return
76-
}
70+
for cap(b.buf) < size {
71+
b.buf = append(b.buf[:cap(b.buf)], 0)
72+
}
73+
b.buf = b.buf[:cap(b.buf)]
74+
}
7775

76+
// returns next N bytes from buffer.
77+
// The returned slice is only guaranteed to be valid until the next read
78+
func (b *buffer) readNext(need int) (p []byte, err error) {
79+
if b.length < need {
80+
// refill
7881
err = b.fill(need) // err deferred
7982
}
8083

81-
copy(p, b.buf[b.idx:])
84+
p = b.buf[b.idx : b.idx+need]
8285
b.idx += need
8386
b.length -= need
8487
return

connection.go

+1
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ func (mc *mysqlConn) Query(query string, args []driver.Value) (driver.Rows, erro
212212
}
213213

214214
// Gets the value of the given MySQL System Variable
215+
// The returned byte slice is only valid until the next read
215216
func (mc *mysqlConn) getSystemVar(name string) (val []byte, err error) {
216217
// Send command
217218
err = mc.writeCommandPacketStr(comQuery, "SELECT @@"+name)

packets.go

+3-5
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,14 @@ import (
2626
// Read packet to buffer 'data'
2727
func (mc *mysqlConn) readPacket() (data []byte, err error) {
2828
// Read packet header
29-
data = make([]byte, 4)
30-
err = mc.buf.read(data)
29+
data, err = mc.buf.readNext(4)
3130
if err != nil {
3231
errLog.Print(err.Error())
3332
return nil, driver.ErrBadConn
3433
}
3534

3635
// Packet Length [24 bit]
37-
pktLen := uint32(data[0]) | uint32(data[1])<<8 | uint32(data[2])<<16
36+
pktLen := int(uint32(data[0]) | uint32(data[1])<<8 | uint32(data[2])<<16)
3837

3938
if pktLen < 1 {
4039
errLog.Print(errMalformPkt.Error())
@@ -52,8 +51,7 @@ func (mc *mysqlConn) readPacket() (data []byte, err error) {
5251
mc.sequence++
5352

5453
// Read packet body [pktLen bytes]
55-
data = make([]byte, pktLen)
56-
err = mc.buf.read(data)
54+
data, err = mc.buf.readNext(pktLen)
5755
if err == nil {
5856
if pktLen < maxPacketSize {
5957
return data, nil

0 commit comments

Comments
 (0)