util/buffer: add DelayedBuffer
Signed-off-by: Vasiliy Tolstov <v.tolstov@unistack.org>
This commit is contained in:
parent
558c6f4d7c
commit
653bd386cc
@ -1,27 +0,0 @@
|
||||
package buf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
)
|
||||
|
||||
var _ io.Closer = &Buffer{}
|
||||
|
||||
// Buffer bytes.Buffer wrapper to satisfie io.Closer interface
|
||||
type Buffer struct {
|
||||
*bytes.Buffer
|
||||
}
|
||||
|
||||
// Close reset buffer contents
|
||||
func (b *Buffer) Close() error {
|
||||
b.Buffer.Reset()
|
||||
return nil
|
||||
}
|
||||
|
||||
// New creates new buffer that satisfies Closer interface
|
||||
func New(b *bytes.Buffer) *Buffer {
|
||||
if b == nil {
|
||||
b = bytes.NewBuffer(nil)
|
||||
}
|
||||
return &Buffer{b}
|
||||
}
|
85
util/buffer/buffer.go
Normal file
85
util/buffer/buffer.go
Normal file
@ -0,0 +1,85 @@
|
||||
package buffer
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var _ io.WriteCloser = (*DelayedBuffer)(nil)
|
||||
|
||||
// DelayedBuffer is the buffer that holds items until either the buffer filled or a specified time limit is reached
|
||||
type DelayedBuffer struct {
|
||||
mu sync.Mutex
|
||||
maxWait time.Duration
|
||||
flushTime time.Time
|
||||
buffer chan []byte
|
||||
ticker *time.Ticker
|
||||
w io.Writer
|
||||
err error
|
||||
}
|
||||
|
||||
func NewDelayedBuffer(size int, maxWait time.Duration, w io.Writer) *DelayedBuffer {
|
||||
b := &DelayedBuffer{
|
||||
buffer: make(chan []byte, size),
|
||||
ticker: time.NewTicker(maxWait),
|
||||
w: w,
|
||||
flushTime: time.Now(),
|
||||
maxWait: maxWait,
|
||||
}
|
||||
b.loop()
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *DelayedBuffer) loop() {
|
||||
go func() {
|
||||
for range b.ticker.C {
|
||||
b.mu.Lock()
|
||||
if time.Since(b.flushTime) > b.maxWait {
|
||||
b.flush()
|
||||
}
|
||||
b.mu.Unlock()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (b *DelayedBuffer) flush() {
|
||||
bufLen := len(b.buffer)
|
||||
if bufLen > 0 {
|
||||
tmp := make([][]byte, bufLen)
|
||||
for i := 0; i < bufLen; i++ {
|
||||
tmp[i] = <-b.buffer
|
||||
}
|
||||
for _, t := range tmp {
|
||||
_, b.err = b.w.Write(t)
|
||||
}
|
||||
b.flushTime = time.Now()
|
||||
}
|
||||
}
|
||||
|
||||
func (b *DelayedBuffer) Put(items ...[]byte) {
|
||||
b.mu.Lock()
|
||||
for _, item := range items {
|
||||
select {
|
||||
case b.buffer <- item:
|
||||
default:
|
||||
b.flush()
|
||||
b.buffer <- item
|
||||
}
|
||||
}
|
||||
b.mu.Unlock()
|
||||
}
|
||||
|
||||
func (b *DelayedBuffer) Close() error {
|
||||
b.mu.Lock()
|
||||
b.flush()
|
||||
close(b.buffer)
|
||||
b.ticker.Stop()
|
||||
b.mu.Unlock()
|
||||
return b.err
|
||||
}
|
||||
|
||||
func (b *DelayedBuffer) Write(data []byte) (int, error) {
|
||||
b.Put(data)
|
||||
return len(data), b.err
|
||||
}
|
22
util/buffer/buffer_test.go
Normal file
22
util/buffer/buffer_test.go
Normal file
@ -0,0 +1,22 @@
|
||||
package buffer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestTimedBuffer(t *testing.T) {
|
||||
buf := bytes.NewBuffer(nil)
|
||||
b := NewDelayedBuffer(100, 300*time.Millisecond, buf)
|
||||
for i := 0; i < 100; i++ {
|
||||
_, _ = b.Write([]byte(`test`))
|
||||
}
|
||||
if buf.Len() != 0 {
|
||||
t.Fatal("delayed write not worked")
|
||||
}
|
||||
time.Sleep(400 * time.Millisecond)
|
||||
if buf.Len() == 0 {
|
||||
t.Fatal("delayed write not worked")
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user