github.com/glycerine/xcryptossh@v7.0.4+incompatible/buffer.go (about) 1 // Copyright 2012 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package ssh 6 7 import ( 8 "io" 9 "sync" 10 ) 11 12 // buffer provides a linked list buffer for data exchange 13 // between producer and consumer. Theoretically the buffer is 14 // of unlimited capacity as it does no allocation of its own. 15 type buffer struct { 16 // protects concurrent access to head, tail and closed 17 *sync.Cond 18 19 head *element // the buffer that will be read first 20 tail *element // the buffer that will be read last 21 22 closed bool 23 idle *IdleTimer 24 } 25 26 // An element represents a single link in a linked list. 27 type element struct { 28 buf []byte 29 next *element 30 } 31 32 // newBuffer returns an empty buffer that is not closed. 33 func newBuffer(idle *IdleTimer) *buffer { 34 e := new(element) 35 b := &buffer{ 36 Cond: newCond(), 37 head: e, 38 tail: e, 39 idle: idle, 40 } 41 return b 42 } 43 44 // write makes buf available for Read to receive. 45 // buf must not be modified after the call to write. 46 func (b *buffer) write(buf []byte) { 47 b.Cond.L.Lock() 48 e := &element{buf: buf} 49 b.tail.next = e 50 b.tail = e 51 b.Cond.Signal() 52 b.Cond.L.Unlock() 53 } 54 55 // eof closes the buffer. Reads from the buffer once all 56 // the data has been consumed will receive os.EOF. 57 func (b *buffer) eof() error { 58 b.Cond.L.Lock() 59 b.closed = true 60 b.Cond.Broadcast() 61 b.Cond.L.Unlock() 62 return nil 63 } 64 65 // timeout does not close the buffer. Reads from the buffer once all 66 // the data has been consumed will receive ErrTimeout. 67 // b.idle.TimeOut() must return true when queried for 68 // this to be succesful. 69 func (b *buffer) timeout() error { 70 b.Cond.Broadcast() 71 return nil 72 } 73 74 // Read reads data from the internal buffer in buf. Reads will block 75 // if no data is available, or until the buffer is closed. 76 func (b *buffer) Read(buf []byte) (n int, err error) { 77 b.idle.BeginAttempt() 78 b.Cond.L.Lock() 79 defer func() { 80 b.Cond.L.Unlock() 81 if err == nil { 82 b.idle.AttemptOK() 83 } 84 }() 85 86 for len(buf) > 0 { 87 // if there is data in b.head, copy it 88 if len(b.head.buf) > 0 { 89 r := copy(buf, b.head.buf) 90 buf, b.head.buf = buf[r:], b.head.buf[r:] 91 n += r 92 continue 93 } 94 // if there is a next buffer, make it the head 95 if len(b.head.buf) == 0 && b.head != b.tail { 96 b.head = b.head.next 97 continue 98 } 99 100 // if at least one byte has been copied, return 101 if n > 0 { 102 break 103 } 104 105 // if nothing was read, and there is nothing outstanding 106 // check to see if the buffer is closed. 107 if b.closed { 108 err = io.EOF 109 break 110 } 111 timedOut := "" 112 select { 113 case timedOut = <-b.idle.TimedOut: 114 case <-b.idle.Halt.ReqStopChan(): 115 } 116 if timedOut != "" { 117 err = newErrTimeout(timedOut, b.idle) 118 break 119 } 120 // out of buffers, wait for producer 121 b.Cond.Wait() 122 } 123 return 124 }