Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

20 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Ringslice

go workflow go reference mit license

Ringslice is a type-safe generic ring buffer backed by a Go slice, designed for production use cases like sliding window and streaming workloads.

Most ring buffer implementations focus on storage. Ringslice adds lifecycle hooks so you can react to what happens as data moves through the buffer. It also features iter.Seq iteration for idiomatic Go range support.

For pointer-based circular lists and round-robin traversal, see Go’s standard container/ring.

Basic Usage

const maxRingCapacity = 128

ring := ringslice.New[string](maxRingCapacity)

ring.Add("generic")
ring.Add("ring")
ring.Add("buffer")

// prints: "generic", "ring", "buffer"
for v := range ring.All() {
    fmt.Println(v)
}

// prints: "buffer", "ring", "generic"
for v := range ring.AllDesc() {
    fmt.Println(v)
}

Hooks

Ringslice provides callback hooks that let you react to internal lifecycle events.

OnBeforeAdd

Called before the value is added to the ring. The value will be rejected if false is returned. This is particularly useful if you want to exclude values with certain characteristics.

// reject empty strings
ring.OnBeforeAdd(func(value string) bool {
    return len(value) > 0
})

OnRotate

Called each time the write index wraps around the ring. Useful for logging, flushing, or instrumentation.

ring.OnRotate(func(values []string) {
    fmt.Println("lap complete 🏁")
})

OnFlush

Called by Flush() before the buffer is cleared. Useful for draining the buffer, persisting elements, or instrumentation.

ring.OnFlush(func(values []string) {
    for _, v := range values {
        db.Insert(v)
    }
})

Note: Clear() discards all elements without invoking this callback.

Serialization

Ringslice implements the json.Marshaler interface, allowing you to serialize the buffer directly with json.Marshal().

ring := ringslice.New[int](8)

ring.Add(1)
ring.Add(2)
ring.Add(3)

buf, err := json.Marshal(ring)

This functionality is useful when you want to persist the ring buffer, even though ringslice is inherently volatile. The serialized output preserves insertion order regardless of internal buffer rotation.

Custom types

When using custom structs as ring values, only exported fields are serialized. Unexported fields are lost during serialization and zero-initialized during deserialization. If your struct has unexported fields that must be persisted, implement MarshalJSON() and UnmarshalJSON() on that struct type to handle them explicitly.

Concurrency Model

Ringslice uses a read-write lock to allow multiple concurrent readers while serializing writers. This ensures thread-safety while maintaining high throughput for read-heavy workloads.

About

A type-safe generic ring buffer backed by a Go slice.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Contributors

Languages