mirror of https://github.com/gogits/gogs.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
98 lines
2.1 KiB
98 lines
2.1 KiB
// 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 os.EOF. |
|
func (b *buffer) eof() error { |
|
b.Cond.L.Lock() |
|
b.closed = true |
|
b.Cond.Signal() |
|
b.Cond.L.Unlock() |
|
return nil |
|
} |
|
|
|
// 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 |
|
}
|
|
|