mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-31 03:32:25 -05:00 
			
		
		
		
	
		
			
	
	
		
			98 lines
		
	
	
	
		
			2.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			98 lines
		
	
	
	
		
			2.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
|  | // 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 | ||
|  | } |