@@ -21,41 +21,58 @@ const defaultBufSize = 4096
2121// In other words, we can't write and read simultaneously on the same connection.
2222// The buffer is similar to bufio.Reader / Writer but zero-copy-ish
2323// Also highly optimized for this particular use case.
24+ // This buffer is backed by two byte slices in a double-buffering scheme
2425type buffer struct {
2526 buf []byte // buf is a byte buffer who's length and capacity are equal.
2627 nc net.Conn
2728 idx int
2829 length int
2930 timeout time.Duration
31+ dbuf [2 ][]byte // dbuf is an array with the two byte slices that back this buffer
32+ flipcnt int // flipccnt is the current buffer counter for double-buffering
3033}
3134
3235// newBuffer allocates and returns a new buffer.
3336func newBuffer (nc net.Conn ) buffer {
37+ fg := make ([]byte , defaultBufSize )
38+ bg := make ([]byte , defaultBufSize )
3439 return buffer {
35- buf : make ([]byte , defaultBufSize ),
36- nc : nc ,
40+ buf : fg ,
41+ nc : nc ,
42+ dbuf : [2 ][]byte {fg , bg },
3743 }
3844}
3945
46+ // flip replaces the active buffer with the background buffer
47+ // this is a delayed flip that simply increases the buffer counter;
48+ // the actual flip will be performed the next time we call `buffer.fill`
49+ func (b * buffer ) flip () {
50+ b .flipcnt += 1
51+ }
52+
4053// fill reads into the buffer until at least _need_ bytes are in it
4154func (b * buffer ) fill (need int ) error {
4255 n := b .length
43-
44- // move existing data to the beginning
45- if n > 0 && b .idx > 0 {
46- copy (b .buf [0 :n ], b .buf [b .idx :])
56+ // fill data into its double-buffering target: if we've called
57+ // flip on this buffer, we'll be copying to the background buffer,
58+ // and then filling it with network data; otherwise we'll just move
59+ // the contents of the current buffer to the front before filling it
60+ dest := b .dbuf [b .flipcnt & 1 ]
61+
62+ // grow buffer if necessary to fit the whole packet.
63+ if need > len (dest ) {
64+ // Round up to the next multiple of the default size;
65+ // this buffer will be discarded the next time we flip buffers
66+ dest = make ([]byte , ((need / defaultBufSize )+ 1 )* defaultBufSize )
4767 }
4868
49- // grow buffer if necessary
50- // TODO: let the buffer shrink again at some point
51- // Maybe keep the org buf slice and swap back?
52- if need > len (b .buf ) {
53- // Round up to the next multiple of the default size
54- newBuf := make ([]byte , ((need / defaultBufSize )+ 1 )* defaultBufSize )
55- copy (newBuf , b .buf )
56- b .buf = newBuf
69+ // if we're filling the fg buffer, move the existing data to the start of it.
70+ // if we're filling the bg buffer, copy over the data
71+ if n > 0 {
72+ copy (dest [0 :n ], b .buf [b .idx :])
5773 }
5874
75+ b .buf = dest
5976 b .idx = 0
6077
6178 for {
0 commit comments