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
5package ssh
6
7import (
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.
15type 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}
24
25// An element represents a single link in a linked list.
26type element struct {
27	buf  []byte
28	next *element
29}
30
31// newBuffer returns an empty buffer that is not closed.
32func newBuffer() *buffer {
33	e := new(element)
34	b := &buffer{
35		Cond: newCond(),
36		head: e,
37		tail: e,
38	}
39	return b
40}
41
42// write makes buf available for Read to receive.
43// buf must not be modified after the call to write.
44func (b *buffer) write(buf []byte) {
45	b.Cond.L.Lock()
46	e := &element{buf: buf}
47	b.tail.next = e
48	b.tail = e
49	b.Cond.Signal()
50	b.Cond.L.Unlock()
51}
52
53// eof closes the buffer. Reads from the buffer once all
54// the data has been consumed will receive io.EOF.
55func (b *buffer) eof() {
56	b.Cond.L.Lock()
57	b.closed = true
58	b.Cond.Signal()
59	b.Cond.L.Unlock()
60}
61
62// Read reads data from the internal buffer in buf.  Reads will block
63// if no data is available, or until the buffer is closed.
64func (b *buffer) Read(buf []byte) (n int, err error) {
65	b.Cond.L.Lock()
66	defer b.Cond.L.Unlock()
67
68	for len(buf) > 0 {
69		// if there is data in b.head, copy it
70		if len(b.head.buf) > 0 {
71			r := copy(buf, b.head.buf)
72			buf, b.head.buf = buf[r:], b.head.buf[r:]
73			n += r
74			continue
75		}
76		// if there is a next buffer, make it the head
77		if len(b.head.buf) == 0 && b.head != b.tail {
78			b.head = b.head.next
79			continue
80		}
81
82		// if at least one byte has been copied, return
83		if n > 0 {
84			break
85		}
86
87		// if nothing was read, and there is nothing outstanding
88		// check to see if the buffer is closed.
89		if b.closed {
90			err = io.EOF
91			break
92		}
93		// out of buffers, wait for producer
94		b.Cond.Wait()
95	}
96	return
97}
98