Files
huso/ring.go
2022-05-14 14:10:04 +02:00

102 lines
2.1 KiB
Go

package main
import (
"fmt"
"sync"
"time"
)
// RingBuf implements an indefinitely writable circular buffer with a fixed size. Old data is overridden if write circle loops. Somewhat threadsafe.
type RingBuf struct {
sync.RWMutex
data []byte
writePos int
written int
loop bool
}
// NewRingBuf initializes a new RingBuf with a fixed size > 0
func NewRingBuf(size int) *RingBuf {
if size <= 0 {
return nil
}
rb := &RingBuf{
data: make([]byte, size),
writePos: 0,
written: 0,
loop: false,
}
return rb
}
func (rb *RingBuf) WriteLine(in string) (int, error) {
return rb.Write([]byte(fmt.Sprintf("[%s]: %s\n", time.Now().Format("2006-01-02 15:04:05"), in)))
}
// Write writes all data from input buf to RingBuf, overriding looped data
func (rb *RingBuf) Write(buf []byte) (int, error) {
rb.Lock()
defer rb.Unlock()
inLen := len(buf)
bufLen := len(rb.data)
if !rb.loop && rb.written < bufLen {
rb.written = rb.written + inLen
rb.loop = rb.written >= bufLen
}
// throw away bytes which would get looped over if input bigger than data
if inLen > bufLen {
buf = buf[inLen-bufLen:]
}
// Copy to data field
bytesToEnd := bufLen - rb.writePos
copy(rb.data[rb.writePos:], buf)
if len(buf) > bytesToEnd {
copy(rb.data, buf[bytesToEnd:])
}
// Move writePos
rb.writePos = ((rb.writePos + len(buf)) % bufLen)
return inLen, nil
}
// Size returns the maximum size of the RingBuf
func (rb *RingBuf) Size() int {
return len(rb.data)
}
// Bytes returns content of RingBuf. DON'T WRITE TO SLICE!
func (rb *RingBuf) Bytes() []byte {
rb.RLock()
defer rb.RUnlock()
bufLen := len(rb.data)
if rb.loop {
out := make([]byte, bufLen)
if rb.writePos == 0 {
copy(out, rb.data)
} else {
copy(out, rb.data[rb.writePos:])
copy(out[bufLen-rb.writePos:], rb.data[:rb.writePos])
}
return out
}
out := make([]byte, rb.writePos)
copy(out, rb.data[:rb.writePos])
return out
}
// Restart restarts RingBuf from 0
func (rb *RingBuf) Restart() {
rb.writePos = 0
rb.written = 0
rb.loop = false
}
// String returns content of RingBuf as string
func (rb *RingBuf) String() string {
return string(rb.Bytes())
}