137 lines
		
	
	
		
			2.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			137 lines
		
	
	
		
			2.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Package buffer provides a simple ring buffer for storing local data
 | |
| package buffer
 | |
| 
 | |
| import (
 | |
| 	"sync"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/google/uuid"
 | |
| )
 | |
| 
 | |
| type stream struct {
 | |
| 	id      string
 | |
| 	entries chan *Entry
 | |
| 	stop    chan bool
 | |
| }
 | |
| 
 | |
| // Buffer is ring buffer
 | |
| type Buffer struct {
 | |
| 	size int
 | |
| 	sync.RWMutex
 | |
| 	vals    []*Entry
 | |
| 	streams map[string]stream
 | |
| }
 | |
| 
 | |
| // Entry is ring buffer data entry
 | |
| type Entry struct {
 | |
| 	Value     interface{}
 | |
| 	Timestamp time.Time
 | |
| }
 | |
| 
 | |
| // New returns a new buffer of the given size
 | |
| func New(i int) *Buffer {
 | |
| 	return &Buffer{
 | |
| 		size:    i,
 | |
| 		streams: make(map[string]stream),
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Put adds a new value to ring buffer
 | |
| func (b *Buffer) Put(v interface{}) {
 | |
| 	b.Lock()
 | |
| 	defer b.Unlock()
 | |
| 
 | |
| 	// append to values
 | |
| 	entry := &Entry{
 | |
| 		Value:     v,
 | |
| 		Timestamp: time.Now(),
 | |
| 	}
 | |
| 	b.vals = append(b.vals, entry)
 | |
| 
 | |
| 	// trim if bigger than size required
 | |
| 	if len(b.vals) > b.size {
 | |
| 		b.vals = b.vals[1:]
 | |
| 	}
 | |
| 
 | |
| 	// TODO: this is fucking ugly
 | |
| 	for _, stream := range b.streams {
 | |
| 		select {
 | |
| 		case <-stream.stop:
 | |
| 			delete(b.streams, stream.id)
 | |
| 			close(stream.entries)
 | |
| 		case stream.entries <- entry:
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // Get returns the last n entries
 | |
| func (b *Buffer) Get(n int) []*Entry {
 | |
| 	b.RLock()
 | |
| 	defer b.RUnlock()
 | |
| 
 | |
| 	// reset any invalid values
 | |
| 	if n > b.size || n < 0 {
 | |
| 		n = b.size
 | |
| 	}
 | |
| 
 | |
| 	// create a delta
 | |
| 	delta := b.size - n
 | |
| 
 | |
| 	// if all the values are less than delta
 | |
| 	if len(b.vals) < delta {
 | |
| 		return b.vals
 | |
| 	}
 | |
| 
 | |
| 	// return the delta set
 | |
| 	return b.vals[delta:]
 | |
| }
 | |
| 
 | |
| // Return the entries since a specific time
 | |
| func (b *Buffer) Since(t time.Time) []*Entry {
 | |
| 	b.RLock()
 | |
| 	defer b.RUnlock()
 | |
| 
 | |
| 	// return all the values
 | |
| 	if t.IsZero() {
 | |
| 		return b.vals
 | |
| 	}
 | |
| 
 | |
| 	// if its in the future return nothing
 | |
| 	if time.Since(t).Seconds() < 0.0 {
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	for i, v := range b.vals {
 | |
| 		// find the starting point
 | |
| 		d := v.Timestamp.Sub(t)
 | |
| 
 | |
| 		// return the values
 | |
| 		if d.Seconds() > 0.0 {
 | |
| 			return b.vals[i:]
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // Stream logs from the buffer
 | |
| func (b *Buffer) Stream(stop chan bool) <-chan *Entry {
 | |
| 	b.Lock()
 | |
| 	defer b.Unlock()
 | |
| 
 | |
| 	entries := make(chan *Entry, 128)
 | |
| 	id := uuid.New().String()
 | |
| 	b.streams[id] = stream{
 | |
| 		id:      id,
 | |
| 		entries: entries,
 | |
| 		stop:    stop,
 | |
| 	}
 | |
| 
 | |
| 	return entries
 | |
| }
 | |
| 
 | |
| // Size returns the size of the ring buffer
 | |
| func (b *Buffer) Size() int {
 | |
| 	return b.size
 | |
| }
 |