| // Copyright 2012 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package ssh |
| |
| import ( |
| "io" |
| "sync" |
| ) |
| |
| // buffer provides a linked list buffer for data exchange |
| // between producer and consumer. Theoretically the buffer is |
| // of unlimited capacity as it does no allocation of its own. |
| type buffer struct { |
| // protects concurrent access to head, tail and closed |
| *sync.Cond |
| |
| head *element // the buffer that will be read first |
| tail *element // the buffer that will be read last |
| |
| closed bool |
| } |
| |
| // An element represents a single link in a linked list. |
| type element struct { |
| buf []byte |
| next *element |
| } |
| |
| // newBuffer returns an empty buffer that is not closed. |
| func newBuffer() *buffer { |
| e := new(element) |
| b := &buffer{ |
| Cond: newCond(), |
| head: e, |
| tail: e, |
| } |
| return b |
| } |
| |
| // write makes buf available for Read to receive. |
| // buf must not be modified after the call to write. |
| func (b *buffer) write(buf []byte) { |
| b.Cond.L.Lock() |
| e := &element{buf: buf} |
| b.tail.next = e |
| b.tail = e |
| b.Cond.Signal() |
| b.Cond.L.Unlock() |
| } |
| |
| // eof closes the buffer. Reads from the buffer once all |
| // the data has been consumed will receive io.EOF. |
| func (b *buffer) eof() { |
| b.Cond.L.Lock() |
| b.closed = true |
| b.Cond.Signal() |
| b.Cond.L.Unlock() |
| } |
| |
| // Read reads data from the internal buffer in buf. Reads will block |
| // if no data is available, or until the buffer is closed. |
| func (b *buffer) Read(buf []byte) (n int, err error) { |
| b.Cond.L.Lock() |
| defer b.Cond.L.Unlock() |
| |
| for len(buf) > 0 { |
| // if there is data in b.head, copy it |
| if len(b.head.buf) > 0 { |
| r := copy(buf, b.head.buf) |
| buf, b.head.buf = buf[r:], b.head.buf[r:] |
| n += r |
| continue |
| } |
| // if there is a next buffer, make it the head |
| if len(b.head.buf) == 0 && b.head != b.tail { |
| b.head = b.head.next |
| continue |
| } |
| |
| // if at least one byte has been copied, return |
| if n > 0 { |
| break |
| } |
| |
| // if nothing was read, and there is nothing outstanding |
| // check to see if the buffer is closed. |
| if b.closed { |
| err = io.EOF |
| break |
| } |
| // out of buffers, wait for producer |
| b.Cond.Wait() |
| } |
| return |
| } |