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.
gosuv/vendor/github.com/glycerine/rbuf/fbuf.go

233 lines
5.8 KiB

package rbuf
// copyright (c) 2016, Jason E. Aten
// license: MIT
import (
"io"
)
// Float64RingBuf:
//
// a fixed-size circular ring buffer of float64
//
type Float64RingBuf struct {
A []float64
N int // MaxView, the total size of A, whether or not in use.
Beg int // start of in-use data in A
Readable int // number of float64 available in A (in use)
}
// constructor. NewFloat64RingBuf will allocate internally
// a slice of maxViewItems float64.
func NewFloat64RingBuf(maxViewItems int) *Float64RingBuf {
n := maxViewItems
r := &Float64RingBuf{
N: n,
Beg: 0,
Readable: 0,
}
r.A = make([]float64, n, n)
return r
}
// TwoContig returns all readable float64, but in two separate slices,
// to avoid copying. The two slices are from the same buffer, but
// are not contiguous. Either or both may be empty slices.
func (b *Float64RingBuf) TwoContig(makeCopy bool) (first []float64, second []float64) {
extent := b.Beg + b.Readable
if extent <= b.N {
// we fit contiguously in this buffer without wrapping to the other.
// Let second stay an empty slice.
return b.A[b.Beg:(b.Beg + b.Readable)], second
}
return b.A[b.Beg:b.N], b.A[0:(extent % b.N)]
}
// Earliest returns the earliest written value v. ok will be
// true unless the ring is empty, in which case ok will be false,
// and v will be zero.
func (b *Float64RingBuf) Earliest() (v float64, ok bool) {
if b.Readable == 0 {
return
}
return b.A[b.Beg], true
}
// Values returns all readable float64 in a single buffer. Calling this function
// might allocate a new buffer to store the elements contiguously.
func (b *Float64RingBuf) Values() []float64 {
first, second := b.TwoContig(false)
if len(first) == 0 {
return second
}
if len(second) == 0 {
return first
}
out := make([]float64, len(first) + len(second))
copy(out, first)
copy(out[len(first):], second)
return out
}
// ReadFloat64():
//
// from bytes.Buffer.Read(): Read reads the next len(p) float64
// pointers 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.
func (b *Float64RingBuf) ReadFloat64(p []float64) (n int, err error) {
return b.readAndMaybeAdvance(p, true)
}
// ReadWithoutAdvance(): if you want to Read the data and leave
// it in the buffer, so as to peek ahead for example.
func (b *Float64RingBuf) ReadWithoutAdvance(p []float64) (n int, err error) {
return b.readAndMaybeAdvance(p, false)
}
func (b *Float64RingBuf) readAndMaybeAdvance(p []float64, 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.Beg:extent])
} else {
n += copy(p, b.A[b.Beg:b.N])
if n < len(p) {
n += copy(p[n:], b.A[0:(extent%b.N)])
}
}
if doAdvance {
b.Advance(n)
}
return
}
//
// WriteAndMaybeOverwriteOldestData always consumes the full
// buffer p, even if that means blowing away the oldest
// unread bytes in the ring to make room. In reality, only the last
// min(len(p),b.N) bytes of p will end up being written to the ring.
//
// This allows the ring to act as a record of the most recent
// b.N bytes of data -- a kind of temporal LRU cache, so the
// speak. The linux kernel's dmesg ring buffer is similar.
//
func (b *Float64RingBuf) WriteAndMaybeOverwriteOldestData(p []float64) (n int, err error) {
writeCapacity := b.N - b.Readable
if len(p) > writeCapacity {
b.Advance(len(p) - writeCapacity)
}
startPos := 0
if len(p) > b.N {
startPos = len(p) - b.N
}
n, err = b.Write(p[startPos:])
if err != nil {
return n, err
}
return len(p), nil
}
//
// Write writes len(p) float64 values 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 *Float64RingBuf) Write(p []float64) (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[writeStart:upperLim], p)
n += k
b.Readable += k
p = p[k:]
// we can fill from b.A[0:something] from
// p's remainder, so loop
}
}
// Reset quickly forgets any data stored in the ring buffer. The
// data is still there, but the ring buffer will ignore it and
// overwrite those buffers as new data comes in.
func (b *Float64RingBuf) Reset() {
b.Beg = 0
b.Readable = 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 *Float64RingBuf) 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 *Float64RingBuf) Adopt(me []float64) {
n := len(me)
if n > b.N {
b.A = me
b.N = n
b.Beg = 0
b.Readable = n
} else {
// we already have a larger buffer, reuse it.
copy(b.A, me)
b.Beg = 0
b.Readable = n
}
}