diff options
Diffstat (limited to 'vendor/github.com/glycerine/go-unsnap-stream/rbuf.go')
-rw-r--r-- | vendor/github.com/glycerine/go-unsnap-stream/rbuf.go | 375 |
1 files changed, 375 insertions, 0 deletions
diff --git a/vendor/github.com/glycerine/go-unsnap-stream/rbuf.go b/vendor/github.com/glycerine/go-unsnap-stream/rbuf.go new file mode 100644 index 0000000000..f771c392d0 --- /dev/null +++ b/vendor/github.com/glycerine/go-unsnap-stream/rbuf.go @@ -0,0 +1,375 @@ +package unsnap + +// copyright (c) 2014, Jason E. Aten +// license: MIT + +// Some text from the Golang standard library doc is adapted and +// reproduced in fragments below to document the expected behaviors +// of the interface functions Read()/Write()/ReadFrom()/WriteTo() that +// are implemented here. Those descriptions (see +// http://golang.org/pkg/io/#Reader for example) are +// copyright 2010 The Go Authors. + +import "io" + +// FixedSizeRingBuf: +// +// a fixed-size circular ring buffer. Yes, just what is says. +// +// We keep a pair of ping/pong buffers so that we can linearize +// the circular buffer into a contiguous slice if need be. +// +// For efficiency, a FixedSizeRingBuf may be vastly preferred to +// a bytes.Buffer. The ReadWithoutAdvance(), Advance(), and Adopt() +// methods are all non-standard methods written for speed. +// +// For an I/O heavy application, I have replaced bytes.Buffer with +// FixedSizeRingBuf and seen memory consumption go from 8GB to 25MB. +// Yes, that is a 300x reduction in memory footprint. Everything ran +// faster too. +// +// Note that Bytes(), while inescapable at times, is expensive: avoid +// it if possible. Instead it is better to use the FixedSizeRingBuf.Readable +// member to get the number of bytes available. Bytes() is expensive because +// it may copy the back and then the front of a wrapped buffer A[Use] +// into A[1-Use] in order to get a contiguous slice. If possible use ContigLen() +// first to get the size that can be read without copying, Read() that +// amount, and then Read() a second time -- to avoid the copy. + +type FixedSizeRingBuf struct { + A [2][]byte // a pair of ping/pong buffers. Only one is active. + Use int // which A buffer is in active use, 0 or 1 + N int // MaxViewInBytes, the size of A[0] and A[1] in bytes. + Beg int // start of data in A[Use] + Readable int // number of bytes available to read in A[Use] + + OneMade bool // lazily instantiate the [1] buffer. If we never call Bytes(), + // we may never need it. If OneMade is false, the Use must be = 0. +} + +func (b *FixedSizeRingBuf) Make2ndBuffer() { + if b.OneMade { + return + } + b.A[1] = make([]byte, b.N, b.N) + b.OneMade = true +} + +// get the length of the largest read that we can provide to a contiguous slice +// without an extra linearizing copy of all bytes internally. +func (b *FixedSizeRingBuf) ContigLen() int { + extent := b.Beg + b.Readable + firstContigLen := intMin(extent, b.N) - b.Beg + return firstContigLen +} + +func NewFixedSizeRingBuf(maxViewInBytes int) *FixedSizeRingBuf { + n := maxViewInBytes + r := &FixedSizeRingBuf{ + Use: 0, // 0 or 1, whichever is actually in use at the moment. + // If we are asked for Bytes() and we wrap, linearize into the other. + + N: n, + Beg: 0, + Readable: 0, + OneMade: false, + } + r.A[0] = make([]byte, n, n) + + // r.A[1] initialized lazily now. + + return r +} + +// from the standard library description of Bytes(): +// Bytes() returns a slice of the contents of the unread portion of the buffer. +// If the caller changes the contents of the +// returned slice, the contents of the buffer will change provided there +// are no intervening method calls on the Buffer. +// +func (b *FixedSizeRingBuf) Bytes() []byte { + + extent := b.Beg + b.Readable + if extent <= b.N { + // we fit contiguously in this buffer without wrapping to the other + return b.A[b.Use][b.Beg:(b.Beg + b.Readable)] + } + + // wrap into the other buffer + b.Make2ndBuffer() + + src := b.Use + dest := 1 - b.Use + + n := copy(b.A[dest], b.A[src][b.Beg:]) + n += copy(b.A[dest][n:], b.A[src][0:(extent%b.N)]) + + b.Use = dest + b.Beg = 0 + + return b.A[b.Use][:n] +} + +// Read(): +// +// from bytes.Buffer.Read(): Read reads the next len(p) bytes +// from the buffer or until the buffer is drained. The return +// value n is the number of bytes read. If the buffer has no data +// to return, err is io.EOF (unless len(p) is zero); otherwise it is nil. +// +// from the description of the Reader interface, +// http://golang.org/pkg/io/#Reader +// +/* +Reader is the interface that wraps the basic Read method. + +Read reads up to len(p) bytes into p. It returns the number +of bytes read (0 <= n <= len(p)) and any error encountered. +Even if Read returns n < len(p), it may use all of p as scratch +space during the call. If some data is available but not +len(p) bytes, Read conventionally returns what is available +instead of waiting for more. + +When Read encounters an error or end-of-file condition after +successfully reading n > 0 bytes, it returns the number of bytes +read. It may return the (non-nil) error from the same call or +return the error (and n == 0) from a subsequent call. An instance +of this general case is that a Reader returning a non-zero number +of bytes at the end of the input stream may return +either err == EOF or err == nil. The next Read should +return 0, EOF regardless. + +Callers should always process the n > 0 bytes returned before +considering the error err. Doing so correctly handles I/O errors +that happen after reading some bytes and also both of the +allowed EOF behaviors. + +Implementations of Read are discouraged from returning a zero +byte count with a nil error, and callers should treat that +situation as a no-op. +*/ +// + +func (b *FixedSizeRingBuf) Read(p []byte) (n int, err error) { + return b.ReadAndMaybeAdvance(p, true) +} + +// if you want to Read the data and leave it in the buffer, so as +// to peek ahead for example. +func (b *FixedSizeRingBuf) ReadWithoutAdvance(p []byte) (n int, err error) { + return b.ReadAndMaybeAdvance(p, false) +} + +func (b *FixedSizeRingBuf) ReadAndMaybeAdvance(p []byte, doAdvance bool) (n int, err error) { + if len(p) == 0 { + return 0, nil + } + if b.Readable == 0 { + return 0, io.EOF + } + extent := b.Beg + b.Readable + if extent <= b.N { + n += copy(p, b.A[b.Use][b.Beg:extent]) + } else { + n += copy(p, b.A[b.Use][b.Beg:b.N]) + if n < len(p) { + n += copy(p[n:], b.A[b.Use][0:(extent%b.N)]) + } + } + if doAdvance { + b.Advance(n) + } + return +} + +// +// Write writes len(p) bytes from p to the underlying data stream. +// It returns the number of bytes written from p (0 <= n <= len(p)) +// and any error encountered that caused the write to stop early. +// Write must return a non-nil error if it returns n < len(p). +// +func (b *FixedSizeRingBuf) Write(p []byte) (n int, err error) { + for { + if len(p) == 0 { + // nothing (left) to copy in; notice we shorten our + // local copy p (below) as we read from it. + return + } + + writeCapacity := b.N - b.Readable + if writeCapacity <= 0 { + // we are all full up already. + return n, io.ErrShortWrite + } + if len(p) > writeCapacity { + err = io.ErrShortWrite + // leave err set and + // keep going, write what we can. + } + + writeStart := (b.Beg + b.Readable) % b.N + + upperLim := intMin(writeStart+writeCapacity, b.N) + + k := copy(b.A[b.Use][writeStart:upperLim], p) + + n += k + b.Readable += k + p = p[k:] + + // we can fill from b.A[b.Use][0:something] from + // p's remainder, so loop + } +} + +// WriteTo and ReadFrom avoid intermediate allocation and copies. + +// WriteTo writes data to w until there's no more data to write +// or when an error occurs. The return value n is the number of +// bytes written. Any error encountered during the write is also returned. +func (b *FixedSizeRingBuf) WriteTo(w io.Writer) (n int64, err error) { + + if b.Readable == 0 { + return 0, io.EOF + } + + extent := b.Beg + b.Readable + firstWriteLen := intMin(extent, b.N) - b.Beg + secondWriteLen := b.Readable - firstWriteLen + if firstWriteLen > 0 { + m, e := w.Write(b.A[b.Use][b.Beg:(b.Beg + firstWriteLen)]) + n += int64(m) + b.Advance(m) + + if e != nil { + return n, e + } + // all bytes should have been written, by definition of + // Write method in io.Writer + if m != firstWriteLen { + return n, io.ErrShortWrite + } + } + if secondWriteLen > 0 { + m, e := w.Write(b.A[b.Use][0:secondWriteLen]) + n += int64(m) + b.Advance(m) + + if e != nil { + return n, e + } + // all bytes should have been written, by definition of + // Write method in io.Writer + if m != secondWriteLen { + return n, io.ErrShortWrite + } + } + + return n, nil +} + +// ReadFrom() reads data from r until EOF or error. The return value n +// is the number of bytes read. Any error except io.EOF encountered +// during the read is also returned. +func (b *FixedSizeRingBuf) ReadFrom(r io.Reader) (n int64, err error) { + for { + writeCapacity := b.N - b.Readable + if writeCapacity <= 0 { + // we are all full + return n, nil + } + writeStart := (b.Beg + b.Readable) % b.N + upperLim := intMin(writeStart+writeCapacity, b.N) + + m, e := r.Read(b.A[b.Use][writeStart:upperLim]) + n += int64(m) + b.Readable += m + if e == io.EOF { + return n, nil + } + if e != nil { + return n, e + } + } +} + +func (b *FixedSizeRingBuf) Reset() { + b.Beg = 0 + b.Readable = 0 + b.Use = 0 +} + +// Advance(): non-standard, but better than Next(), +// because we don't have to unwrap our buffer and pay the cpu time +// for the copy that unwrapping may need. +// Useful in conjuction/after ReadWithoutAdvance() above. +func (b *FixedSizeRingBuf) Advance(n int) { + if n <= 0 { + return + } + if n > b.Readable { + n = b.Readable + } + b.Readable -= n + b.Beg = (b.Beg + n) % b.N +} + +// Adopt(): non-standard. +// +// For efficiency's sake, (possibly) take ownership of +// already allocated slice offered in me. +// +// If me is large we will adopt it, and we will potentially then +// write to the me buffer. +// If we already have a bigger buffer, copy me into the existing +// buffer instead. +func (b *FixedSizeRingBuf) Adopt(me []byte) { + n := len(me) + if n > b.N { + b.A[0] = me + b.OneMade = false + b.N = n + b.Use = 0 + b.Beg = 0 + b.Readable = n + } else { + // we already have a larger buffer, reuse it. + copy(b.A[0], me) + b.Use = 0 + b.Beg = 0 + b.Readable = n + } +} + +func intMax(a, b int) int { + if a > b { + return a + } else { + return b + } +} + +func intMin(a, b int) int { + if a < b { + return a + } else { + return b + } +} + +// Get the (beg, end] indices of the tailing empty buffer of bytes slice that from that is free for writing. +// Note: not guaranteed to be zeroed. At all. +func (b *FixedSizeRingBuf) GetEndmostWritable() (beg int, end int) { + extent := b.Beg + b.Readable + if extent < b.N { + return extent, b.N + } + + return extent % b.N, b.Beg +} + +// Note: not guaranteed to be zeroed. +func (b *FixedSizeRingBuf) GetEndmostWritableSlice() []byte { + beg, e := b.GetEndmostWritable() + return b.A[b.Use][beg:e] +} |