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  }