carousel

package module
v1.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 11, 2026 License: MIT Imports: 4 Imported by: 0

README

CI Go Reference Go

Generic fixed-capacity ring data structures for Go.

Quick install

go get github.com/maxence2997/carousel

Types

Type Description Doc
RingBuffer[T] Fixed-capacity FIFO circular buffer. Not safe for concurrent use. docs/ringbuffer.md
RingQueue[T] Concurrent blocking FIFO queue backed by RingBuffer. Supports drop-on-full and evict-oldest-on-full strategies. docs/ringqueue.md

Quick start

RingBuffer
buf := carousel.NewRingBuffer[int](3)

buf.Push(1)
buf.Push(2)
buf.Push(3)
buf.ForcePush(4)

value, _ := buf.Pop()
fmt.Println(value)
fmt.Println(buf.Drain())
RingQueue
q := carousel.NewRingQueue[string](3)
defer q.Close()

_ = q.Enqueue("alpha")
_ = q.Enqueue("beta")

item, _ := q.Pop(context.Background())
fmt.Println(item)
fmt.Println(q.Drain())

Benchmarks

See per-type results in docs/ringbuffer.md and docs/ringqueue.md.

Benchmark history (CI, linux/amd64): benchmarks

Tooling

  • make examples-sync refreshes runnable examples in the README and package docs from examples_test.go.
  • make bench-sync reruns local benchmarks and rewrites the benchmark tables in docs/.
  • make stresslab runs the local race/stress matrix for the queue regression tests.

License

MIT

Documentation

Overview

Package carousel provides generic fixed-capacity ring data structures.

Two types are available:

  • RingBuffer — a fixed-capacity FIFO circular buffer. Not safe for concurrent use; callers must synchronize externally.

  • RingQueue — a concurrent blocking FIFO queue backed by RingBuffer. Supports both drop-on-full and evict-oldest-on-full enqueue strategies, and a blocking RingQueue.Pop that integrates with context.Context.

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrClosed = errors.New("carousel: queue is closed")

ErrClosed is returned by RingQueue.Enqueue, RingQueue.ForceEnqueue, and RingQueue.Pop after RingQueue.Close has been called.

View Source
var ErrFull = errors.New("carousel: queue is full")

ErrFull is returned by RingQueue.Enqueue when the queue is at capacity.

Functions

This section is empty.

Types

type RingBuffer

type RingBuffer[T any] struct {
	// contains filtered or unexported fields
}

RingBuffer is a fixed-capacity FIFO circular buffer.

The zero value is not usable; create instances with NewRingBuffer. Not safe for concurrent use — callers are responsible for synchronization when shared across goroutines.

Internal layout: head is the index of the oldest element; the write position (tail) is derived as (head+size)%len(data) and is not stored.

Example
package main

import (
	"fmt"

	"github.com/maxence2997/carousel"
)

func main() {
	buf := carousel.NewRingBuffer[int](3)

	buf.Push(1)
	buf.Push(2)
	buf.Push(3)
	buf.ForcePush(4)

	value, _ := buf.Pop()
	fmt.Println(value)
	fmt.Println(buf.Drain())

}
Output:
2
[3 4]

func NewRingBuffer

func NewRingBuffer[T any](capacity int) *RingBuffer[T]

NewRingBuffer creates a RingBuffer with the given capacity.

Panics if capacity < 1.

func (*RingBuffer[T]) Cap

func (rb *RingBuffer[T]) Cap() int

Cap returns the maximum number of items the buffer can hold.

func (*RingBuffer[T]) Clear

func (rb *RingBuffer[T]) Clear()

Clear removes all items and releases slot references for GC. No-op when the buffer is already empty.

func (*RingBuffer[T]) Drain

func (rb *RingBuffer[T]) Drain() []T

Drain removes and returns all items in FIFO order. Returns nil if the buffer is empty. After Drain, the buffer is empty and all internal slots are zeroed.

func (*RingBuffer[T]) ForcePush

func (rb *RingBuffer[T]) ForcePush(item T) (evicted bool)

ForcePush appends item to the back of the buffer. If the buffer is full, the oldest item is evicted to make room. Returns true if an item was evicted.

func (*RingBuffer[T]) Len

func (rb *RingBuffer[T]) Len() int

Len returns the number of items currently in the buffer.

func (*RingBuffer[T]) Peek

func (rb *RingBuffer[T]) Peek() (T, bool)

Peek returns the oldest item without removing it. Returns the zero value of T and false if the buffer is empty.

func (*RingBuffer[T]) Pop

func (rb *RingBuffer[T]) Pop() (T, bool)

Pop removes and returns the oldest item. Returns the zero value of T and false if the buffer is empty.

func (*RingBuffer[T]) Push

func (rb *RingBuffer[T]) Push(item T) bool

Push appends item to the back of the buffer. Returns false if the buffer is full; the item is not added.

func (*RingBuffer[T]) Snapshot added in v1.1.0

func (rb *RingBuffer[T]) Snapshot() []T

Snapshot returns a copy of all items in FIFO order (oldest first). Returns nil if the buffer is empty.

The returned slice is independent of the buffer; mutations to either do not affect the other. Non-destructive: buffer state is unchanged.

Independence is shallow: if T is a pointer type or contains pointers, the pointed-to values are shared between the snapshot and the buffer.

type RingQueue

type RingQueue[T any] struct {
	// contains filtered or unexported fields
}

RingQueue is a concurrent fixed-capacity FIFO queue backed by RingBuffer.

The zero value is not usable; create instances with NewRingQueue. Multiple goroutines may call Enqueue and ForceEnqueue concurrently. Pop is intended to be called by a single goroutine (consumer). Close is idempotent and must be called when the queue is no longer needed.

Example
package main

import (
	"context"
	"fmt"

	"github.com/maxence2997/carousel"
)

func main() {
	q := carousel.NewRingQueue[string](3)
	defer q.Close()

	_ = q.Enqueue("alpha")
	_ = q.Enqueue("beta")

	item, _ := q.Pop(context.Background())
	fmt.Println(item)
	fmt.Println(q.Drain())

}
Output:
alpha
[beta]

func NewRingQueue

func NewRingQueue[T any](capacity int) *RingQueue[T]

NewRingQueue creates a RingQueue with the given capacity.

Panics if capacity < 1.

func (*RingQueue[T]) Cap

func (q *RingQueue[T]) Cap() int

Cap returns the fixed capacity of the queue. Capacity is set at construction and never changes; RingQueue does not support resizing.

func (*RingQueue[T]) Close

func (q *RingQueue[T]) Close()

Close marks the queue as closed and wakes any goroutine blocked in Pop. Idempotent — safe to call more than once; subsequent calls are no-ops.

func (*RingQueue[T]) Drain

func (q *RingQueue[T]) Drain() []T

Drain removes and returns all current items in FIFO order without blocking. Returns nil if the queue is empty.

Unlike RingQueue.Pop, Drain does not wait for new items — it returns whatever is in the buffer at the moment of the call. Useful for bulk reads or flushing remaining items during shutdown without going through the blocking Pop interface.

func (*RingQueue[T]) Enqueue

func (q *RingQueue[T]) Enqueue(item T) error

Enqueue adds item to the queue. Returns ErrFull if the queue is at capacity. Returns ErrClosed if the queue has been closed.

func (*RingQueue[T]) ForceEnqueue

func (q *RingQueue[T]) ForceEnqueue(item T) (evicted bool, err error)

ForceEnqueue adds item to the queue, evicting the oldest item if full. Returns true if an item was evicted. Returns ErrClosed if the queue has been closed.

func (*RingQueue[T]) Len

func (q *RingQueue[T]) Len() int

Len returns the number of items currently in the queue.

func (*RingQueue[T]) Pop

func (q *RingQueue[T]) Pop(ctx context.Context) (T, error)

Pop blocks until an item is available, the context is canceled, or the queue is closed. Available items are always delivered before cancellation or close signals are returned. Returns ErrClosed when the queue is closed and all remaining items have been consumed. Returns ctx.Err() when the context is canceled and the buffer is empty.

Concurrent calls to Pop are not supported — use a single consumer goroutine. ctx must be non-nil; pass context.Background to opt out of cancellation.

func (*RingQueue[T]) Snapshot added in v1.1.0

func (q *RingQueue[T]) Snapshot() []T

Snapshot returns a copy of all current items in FIFO order without removing them. Returns nil if the queue is empty.

Acquires the queue lock for the duration of the in-package copy; no user-supplied code runs under the lock. Non-destructive: queue state is unchanged after the call.

Lock hold time is O(N) where N = RingQueue.Len at the moment of the call. For very large capacities under contention, prefer a steady-state design that does not call Snapshot from a hot path.

Directories

Path Synopsis
cmd
benchsync command
examplesync command
stresslab command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL