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 | |
| 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 | ccad956 | 2013-03-01 19:31:43 | [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 | ccad956 | 2013-03-01 19:31:43 | [diff] [blame] | 36 | func (b *buffer) fill(need int) (err 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 | d1deaee | 2013-03-06 02:06:50 | [diff] [blame] | 54 | var n int |
Julien Schmidt | e2532ba | 2013-06-02 01:40:54 | [diff] [blame] | 55 | for { |
Julien Schmidt | ccad956 | 2013-03-01 19:31:43 | [diff] [blame] | 56 | n, err = b.rd.Read(b.buf[b.length:]) |
| 57 | b.length += n |
Julien Schmidt | 74a6452 | 2013-03-03 17:41:13 | [diff] [blame] | 58 | |
Julien Schmidt | e2532ba | 2013-06-02 01:40:54 | [diff] [blame] | 59 | if b.length < need && err == nil { |
Julien Schmidt | 74a6452 | 2013-03-03 17:41:13 | [diff] [blame] | 60 | continue |
| 61 | } |
| 62 | return // err |
Julien Schmidt | ccad956 | 2013-03-01 19:31:43 | [diff] [blame] | 63 | } |
Julien Schmidt | b494bac | 2013-06-02 13:53:19 | [diff] [blame] | 64 | return |
Julien Schmidt | ccad956 | 2013-03-01 19:31:43 | [diff] [blame] | 65 | } |
| 66 | |
Julien Schmidt | 96a4f13 | 2013-04-21 12:57:58 | [diff] [blame] | 67 | // returns next N bytes from buffer. |
| 68 | // The returned slice is only guaranteed to be valid until the next read |
| 69 | func (b *buffer) readNext(need int) (p []byte, err error) { |
Julien Schmidt | a602069 | 2013-04-24 14:40:08 | [diff] [blame] | 70 | if b.length < need { |
| 71 | // refill |
| 72 | err = b.fill(need) // err deferred |
Arne Hormann | 035e985 | 2013-07-11 08:14:23 | [diff] [blame] | 73 | if err == io.EOF && b.length >= need { |
| 74 | err = nil |
| 75 | } |
Julien Schmidt | ccad956 | 2013-03-01 19:31:43 | [diff] [blame] | 76 | } |
Julien Schmidt | a602069 | 2013-04-24 14:40:08 | [diff] [blame] | 77 | |
| 78 | p = b.buf[b.idx : b.idx+need] |
| 79 | b.idx += need |
| 80 | b.length -= need |
Julien Schmidt | ccad956 | 2013-03-01 19:31:43 | [diff] [blame] | 81 | return |
| 82 | } |
Julien Schmidt | ddf24e6 | 2013-10-22 08:54:55 | [diff] [blame] | 83 | |
| 84 | // returns a buffer with the requested size. |
| 85 | // If possible, a slice from the existing buffer is returned. |
| 86 | // Otherwise a bigger buffer is made. |
| 87 | // Only one buffer (total) can be used at a time. |
Julien Schmidt | 8751b72 | 2013-10-23 15:32:44 | [diff] [blame] | 88 | func (b *buffer) takeBuffer(length int) []byte { |
Julien Schmidt | ddf24e6 | 2013-10-22 08:54:55 | [diff] [blame] | 89 | if b.length > 0 { |
| 90 | return nil |
| 91 | } |
| 92 | |
| 93 | // test (cheap) general case first |
| 94 | if length <= defaultBufSize || length <= cap(b.buf) { |
| 95 | return b.buf[:length] |
| 96 | } |
| 97 | |
| 98 | if length < maxPacketSize { |
| 99 | b.buf = make([]byte, length) |
| 100 | return b.buf |
| 101 | } |
| 102 | return make([]byte, length) |
| 103 | } |
| 104 | |
| 105 | // shortcut which can be used if the requested buffer is guaranteed to be |
| 106 | // smaller than defaultBufSize |
| 107 | // Only one buffer (total) can be used at a time. |
Julien Schmidt | 8751b72 | 2013-10-23 15:32:44 | [diff] [blame] | 108 | func (b *buffer) takeSmallBuffer(length int) []byte { |
Julien Schmidt | ddf24e6 | 2013-10-22 08:54:55 | [diff] [blame] | 109 | if b.length == 0 { |
| 110 | return b.buf[:length] |
| 111 | } |
| 112 | return nil |
| 113 | } |
| 114 | |
| 115 | // takeCompleteBuffer returns the complete existing buffer. |
| 116 | // This can be used if the necessary buffer size is unknown. |
| 117 | // Only one buffer (total) can be used at a time. |
| 118 | func (b *buffer) takeCompleteBuffer() []byte { |
| 119 | if b.length == 0 { |
| 120 | return b.buf |
| 121 | } |
| 122 | return nil |
| 123 | } |