Julien Schmidt | ccad956 | 2013-03-01 19:31:43 | [diff] [blame] | 1 | // Go MySQL Driver - A MySQL-Driver for Go's database/sql package |
| 2 | // |
Julien Schmidt | ff97004 | 2013-09-13 18:36:04 | [diff] [blame] | 3 | // Copyright 2013 The Go-MySQL-Driver Authors. All rights reserved. |
Julien Schmidt | ccad956 | 2013-03-01 19:31:43 | [diff] [blame] | 4 | // |
| 5 | // This Source Code Form is subject to the terms of the Mozilla Public |
| 6 | // License, v. 2.0. If a copy of the MPL was not distributed with this file, |
| 7 | // You can obtain one at http://mozilla.org/MPL/2.0/. |
| 8 | |
| 9 | package mysql |
| 10 | |
Julien Schmidt | a602069 | 2013-04-24 14:40:08 | [diff] [blame] | 11 | import "io" |
Julien Schmidt | ccad956 | 2013-03-01 19:31:43 | [diff] [blame] | 12 | |
Julien Schmidt | a602069 | 2013-04-24 14:40:08 | [diff] [blame] | 13 | const defaultBufSize = 4096 |
Julien Schmidt | ccad956 | 2013-03-01 19:31:43 | [diff] [blame] | 14 | |
Julien Schmidt | ddf24e6 | 2013-10-22 08:54:55 | [diff] [blame] | 15 | // A buffer which is used for both reading and writing. |
| 16 | // This is possible since communication on each connection is synchronous. |
| 17 | // In other words, we can't write and read simultaneously on the same connection. |
| 18 | // The buffer is similar to bufio.Reader / Writer but zero-copy-ish |
Julien Schmidt | e2532ba | 2013-06-02 01:40:54 | [diff] [blame] | 19 | // Also highly optimized for this particular use case. |
Julien Schmidt | ccad956 | 2013-03-01 19:31:43 | [diff] [blame] | 20 | type buffer struct { |
| 21 | buf []byte |
| 22 | rd io.Reader |
| 23 | idx int |
| 24 | length int |
| 25 | } |
| 26 | |
Julien Schmidt | 5b79995 | 2014-06-03 13:46:28 | [diff] [blame] | 27 | func newBuffer(rd io.Reader) buffer { |
Julien Schmidt | 04653f2 | 2013-06-03 20:34:22 | [diff] [blame] | 28 | var b [defaultBufSize]byte |
Julien Schmidt | 5b79995 | 2014-06-03 13:46:28 | [diff] [blame] | 29 | return buffer{ |
Julien Schmidt | 04653f2 | 2013-06-03 20:34:22 | [diff] [blame] | 30 | buf: b[:], |
Julien Schmidt | ccad956 | 2013-03-01 19:31:43 | [diff] [blame] | 31 | rd: rd, |
| 32 | } |
| 33 | } |
| 34 | |
Julien Schmidt | c2dde2d | 2013-04-21 13:16:44 | [diff] [blame] | 35 | // fill reads into the buffer until at least _need_ bytes are in it |
Julien Schmidt | 4fe7e92 | 2013-10-25 16:15:27 | [diff] [blame] | 36 | func (b *buffer) fill(need int) error { |
Julien Schmidt | c2dde2d | 2013-04-21 13:16:44 | [diff] [blame] | 37 | // move existing data to the beginning |
| 38 | if b.length > 0 && b.idx > 0 { |
| 39 | copy(b.buf[0:b.length], b.buf[b.idx:]) |
| 40 | } |
| 41 | |
Julien Schmidt | a602069 | 2013-04-24 14:40:08 | [diff] [blame] | 42 | // grow buffer if necessary |
Julien Schmidt | ddf24e6 | 2013-10-22 08:54:55 | [diff] [blame] | 43 | // TODO: let the buffer shrink again at some point |
| 44 | // Maybe keep the org buf slice and swap back? |
Julien Schmidt | a602069 | 2013-04-24 14:40:08 | [diff] [blame] | 45 | if need > len(b.buf) { |
Julien Schmidt | ddf24e6 | 2013-10-22 08:54:55 | [diff] [blame] | 46 | // Round up to the next multiple of the default size |
| 47 | newBuf := make([]byte, ((need/defaultBufSize)+1)*defaultBufSize) |
Julien Schmidt | 04653f2 | 2013-06-03 20:34:22 | [diff] [blame] | 48 | copy(newBuf, b.buf) |
| 49 | b.buf = newBuf |
Julien Schmidt | a602069 | 2013-04-24 14:40:08 | [diff] [blame] | 50 | } |
| 51 | |
Julien Schmidt | ccad956 | 2013-03-01 19:31:43 | [diff] [blame] | 52 | b.idx = 0 |
Julien Schmidt | ccad956 | 2013-03-01 19:31:43 | [diff] [blame] | 53 | |
Julien Schmidt | e2532ba | 2013-06-02 01:40:54 | [diff] [blame] | 54 | for { |
Julien Schmidt | 4fe7e92 | 2013-10-25 16:15:27 | [diff] [blame] | 55 | n, err := b.rd.Read(b.buf[b.length:]) |
Julien Schmidt | ccad956 | 2013-03-01 19:31:43 | [diff] [blame] | 56 | b.length += n |
Julien Schmidt | 74a6452 | 2013-03-03 17:41:13 | [diff] [blame] | 57 | |
Julien Schmidt | 4fe7e92 | 2013-10-25 16:15:27 | [diff] [blame] | 58 | if err == nil { |
| 59 | if b.length < need { |
| 60 | continue |
| 61 | } |
| 62 | return nil |
Julien Schmidt | 74a6452 | 2013-03-03 17:41:13 | [diff] [blame] | 63 | } |
Julien Schmidt | 4fe7e92 | 2013-10-25 16:15:27 | [diff] [blame] | 64 | if b.length >= need && err == io.EOF { |
| 65 | return nil |
| 66 | } |
| 67 | return err |
Julien Schmidt | ccad956 | 2013-03-01 19:31:43 | [diff] [blame] | 68 | } |
Julien Schmidt | ccad956 | 2013-03-01 19:31:43 | [diff] [blame] | 69 | } |
| 70 | |
Julien Schmidt | 96a4f13 | 2013-04-21 12:57:58 | [diff] [blame] | 71 | // returns next N bytes from buffer. |
| 72 | // The returned slice is only guaranteed to be valid until the next read |
Julien Schmidt | 4fe7e92 | 2013-10-25 16:15:27 | [diff] [blame] | 73 | func (b *buffer) readNext(need int) ([]byte, error) { |
Julien Schmidt | a602069 | 2013-04-24 14:40:08 | [diff] [blame] | 74 | if b.length < need { |
| 75 | // refill |
Julien Schmidt | 4fe7e92 | 2013-10-25 16:15:27 | [diff] [blame] | 76 | if err := b.fill(need); err != nil { |
| 77 | return nil, err |
Arne Hormann | 035e985 | 2013-07-11 08:14:23 | [diff] [blame] | 78 | } |
Julien Schmidt | ccad956 | 2013-03-01 19:31:43 | [diff] [blame] | 79 | } |
Julien Schmidt | a602069 | 2013-04-24 14:40:08 | [diff] [blame] | 80 | |
Julien Schmidt | 620bcdd | 2013-10-25 18:29:22 | [diff] [blame] | 81 | offset := b.idx |
Julien Schmidt | a602069 | 2013-04-24 14:40:08 | [diff] [blame] | 82 | b.idx += need |
| 83 | b.length -= need |
Julien Schmidt | 620bcdd | 2013-10-25 18:29:22 | [diff] [blame] | 84 | return b.buf[offset:b.idx], nil |
Julien Schmidt | ccad956 | 2013-03-01 19:31:43 | [diff] [blame] | 85 | } |
Julien Schmidt | ddf24e6 | 2013-10-22 08:54:55 | [diff] [blame] | 86 | |
| 87 | // returns a buffer with the requested size. |
| 88 | // If possible, a slice from the existing buffer is returned. |
| 89 | // Otherwise a bigger buffer is made. |
| 90 | // Only one buffer (total) can be used at a time. |
Julien Schmidt | 8751b72 | 2013-10-23 15:32:44 | [diff] [blame] | 91 | func (b *buffer) takeBuffer(length int) []byte { |
Julien Schmidt | ddf24e6 | 2013-10-22 08:54:55 | [diff] [blame] | 92 | if b.length > 0 { |
| 93 | return nil |
| 94 | } |
| 95 | |
| 96 | // test (cheap) general case first |
| 97 | if length <= defaultBufSize || length <= cap(b.buf) { |
| 98 | return b.buf[:length] |
| 99 | } |
| 100 | |
| 101 | if length < maxPacketSize { |
| 102 | b.buf = make([]byte, length) |
| 103 | return b.buf |
| 104 | } |
| 105 | return make([]byte, length) |
| 106 | } |
| 107 | |
| 108 | // shortcut which can be used if the requested buffer is guaranteed to be |
| 109 | // smaller than defaultBufSize |
| 110 | // Only one buffer (total) can be used at a time. |
Julien Schmidt | 8751b72 | 2013-10-23 15:32:44 | [diff] [blame] | 111 | func (b *buffer) takeSmallBuffer(length int) []byte { |
Julien Schmidt | ddf24e6 | 2013-10-22 08:54:55 | [diff] [blame] | 112 | if b.length == 0 { |
| 113 | return b.buf[:length] |
| 114 | } |
| 115 | return nil |
| 116 | } |
| 117 | |
| 118 | // takeCompleteBuffer returns the complete existing buffer. |
| 119 | // This can be used if the necessary buffer size is unknown. |
| 120 | // Only one buffer (total) can be used at a time. |
| 121 | func (b *buffer) takeCompleteBuffer() []byte { |
| 122 | if b.length == 0 { |
| 123 | return b.buf |
| 124 | } |
| 125 | return nil |
| 126 | } |