ringbuffer

package module
v0.3.2 Latest Latest
Warning

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

Go to latest
Published: May 11, 2025 License: MIT Imports: 7 Imported by: 2

README

Ring Buffer

This is a thread-safe ring buffer that uses generics instead of raw []byte as its main type.
Forked from smallnest/ringbuffer.

GoDoc Go Report Card MIT License CI

Circular Buffer Animation

Table of Contents

Features

  • Generic type support
  • Built-in hooks
  • Configurable blocking behavior
  • Timeout support for operations
  • Memory-efficient view operations

Installation

go get github.com/AlexsanderHamir/ringbuffer

Quick Start

package main

import (
    "fmt"
    "time"
    "github.com/AlexsanderHamir/ringbuffer/errors"
    "github.com/AlexsanderHamir/ringbuffer"
    "github.com/AlexsanderHamir/ringbuffer/config"
)

func main() {
    // Create a new ring buffer for integers with size 100
    rb := ringbuffer.New[int](100)

    // Write data to the buffer
    rb.Write(1)
    rb.WriteMany([]int{2, 3, 4, 5})

    // Read data from the buffer
    item, err := rb.GetOne()
    if err != nil {
        fmt.Printf("Error reading: %v\n", err)
        return
    }
    fmt.Println(item) // Output: 1

    items, err := rb.GetN(3)
    if err != nil {
        fmt.Printf("Error reading: %v\n", err)
        return
    }
    fmt.Println(items) // Output: [2 3 4]

    // Example with custom type
    type Message struct {
        ID   int
        Text string
        Time time.Time
    }

    // Create a ring buffer for Message type with custom configuration
    config := &config.RingBufferConfig{
        Block:     true,
        RTimeout:  5 * time.Second,
        WTimeout:  5 * time.Second,
    }
    msgBuffer, err := ringbuffer.NewWithConfig[Message](1000, config)
    if err != nil {
        fmt.Printf("Error creating buffer: %v\n", err)
        return
    }

    // Write messages
    messages := []Message{
        {ID: 1, Text: "Hello", Time: time.Now()},
        {ID: 2, Text: "World", Time: time.Now()},
    }

    _, err = msgBuffer.WriteMany(messages)
    if err != nil {
        fmt.Printf("Error writing messages: %v\n", err)
        return
    }
}

Configuration

The ring buffer can be configured using the RingBufferConfig struct:

type RingBufferConfig struct {
    Block            bool          // Enable/disable blocking behavior
    RTimeout         time.Duration // Read operation timeout
    WTimeout         time.Duration // Write operation timeout
    PreReadBlockHook func() bool   // Hook called before blocking on read
    PreWriteBlockHook func() bool  // Hook called before blocking on write
}
Configuration Options

The ring buffer's behavior can be dynamically configured at runtime using the following methods:

  • WithBlocking(block bool): Enables or disables blocking behavior
  • WithTimeout(d time.Duration): Sets both read and write timeouts
  • WithReadTimeout(d time.Duration): Sets the timeout for read operations
  • WithWriteTimeout(d time.Duration): Sets the timeout for write operations
  • WithPreReadBlockHook(hook func() bool): Sets hook called before blocking on read
  • WithPreWriteBlockHook(hook func() bool): Sets hook called before blocking on write

API Documentation

Core Operations
  • New[T](size int) - Creates a new ring buffer with default configuration for type T
  • NewWithConfig[T](size int, config *Config) - Creates a new ring buffer with custom configuration for type T
  • Write(item T) - Writes a single item to the buffer
  • WriteMany(items []T) - Writes multiple items to the buffer
  • GetOne() (item T, err error) - Reads a single item from the buffer
  • GetN(n int) (items []T, err error) - Reads n items from the buffer
  • PeekOne() (item T, err error) - Peeks at data without removing it from the buffer
  • PeekN(n int) (items []T, err error) - Peeks at n items without removing them from the buffer
  • Close() error - Closes the buffer and releases resources
Buffer State Operations
  • IsEmpty() bool - Checks if the buffer is empty
  • IsFull() bool - Checks if the buffer is full
  • Length() int - Returns the number of items in the buffer
  • Capacity() int - Returns the maximum number of items the buffer can hold
  • Free() int - Returns the number of elements that can be written without blocking
  • GetBlockedReaders() int - Returns the number of readers currently blocked
  • GetBlockedWriters() int - Returns the number of writers currently blocked
View Operations

View operations provide direct access to the underlying buffer data without copying:

  • GetAllView() (part1, part2 []T, err error) - Returns two slices containing all items
  • GetNView(n int) (part1, part2 []T, err error) - Returns two slices containing n items
  • PeekNView(n int) (part1, part2 []T, err error) - Returns two slices containing n items without removing them

⚠️ Important: View operations return references to the actual buffer data. Modifications to these slices will affect the original buffer data. Use with caution and ensure proper synchronization.

Hook Methods
  • WithPreReadBlockHook(hook func() bool) - Sets hook called before blocking on read
  • WithPreWriteBlockHook(hook func() bool) - Sets hook called before blocking on write

Error Handling

The ring buffer provides comprehensive error handling for various scenarios:

// Example of error handling
rb := ringbuffer.New[int](10)

// Write with error handling
err := rb.Write(1)
if err != nil {
    switch {
    case errors.Is(err, ringbuffer.ErrTooMuchDataToWrite):
        fmt.Println("Data to write exceeds buffer size")
    case errors.Is(err, ringbuffer.ErrTooMuchDataToPeek):
        fmt.Println("Trying to peek more data than available")
    case errors.Is(err, ringbuffer.ErrIsFull):
        fmt.Println("Buffer is full and not blocking")
    case errors.Is(err, ringbuffer.ErrIsEmpty):
        fmt.Println("Buffer is empty and not blocking")
    case errors.Is(err, ringbuffer.ErrIsNotEmpty):
        fmt.Println("Buffer is not empty and not blocking")
    case errors.Is(err, ringbuffer.ErrAcquireLock):
        fmt.Println("Unable to acquire lock on Try operations")
    case errors.Is(err, ringbuffer.ErrInvalidLength):
        fmt.Println("Invalid buffer length")
    case errors.Is(err, ringbuffer.ErrNilBuffer):
        fmt.Println("Operations performed on nil buffer")
    default:
        fmt.Printf("Unexpected error: %v\n", err)
    }
}

The following errors can be returned by the ring buffer operations:

  • ErrTooMuchDataToWrite: Returned when the data to write is more than the buffer size
  • ErrTooMuchDataToPeek: Returned when trying to peek more data than available
  • ErrIsFull: Returned when the buffer is full and not blocking
  • ErrIsEmpty: Returned when the buffer is empty and not blocking
  • ErrIsNotEmpty: Returned when the buffer is not empty and not blocking
  • ErrAcquireLock: Returned when the lock is not acquired on Try operations
  • ErrInvalidLength: Returned when the length of the buffer is invalid
  • ErrNilBuffer: Returned when operations are performed on a nil buffer

Performance Considerations

  1. Buffer Size: Choose an appropriate buffer size based on your use case. Too small buffers may cause frequent blocking, while too large buffers may waste memory.

  2. Blocking vs Non-blocking: Use non-blocking operations when you need to handle full/empty conditions in your application logic.

  3. View Operations: Use view operations when memory efficiency is critical, but be aware of the implications of working with direct buffer references.

  4. Hooks: Use hooks if there's something you can do before blocking, but keep hook functions lightweight to avoid impacting performance.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License

This project is licensed under the MIT License - see the LICENSE file for details.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type RingBuffer

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

RingBuffer is a circular buffer that implements io.ReaderWriter interface. It operates like a buffered pipe, where data is written to a RingBuffer and can be read back from another goroutine. It is safe to concurrently read and write RingBuffer.

Key features: - Thread-safe concurrent read/write operations - Configurable blocking/non-blocking behavior - Timeout support for read/write operations - Pre-read hook for custom blocking behavior - Efficient circular buffer implementation

func New

func New[T any](size int) *RingBuffer[T]

New returns a new RingBuffer whose buffer has the given size.

func NewWithConfig

func NewWithConfig[T any](size int, cfg *config.RingBufferConfig[T]) (*RingBuffer[T], error)

NewWithConfig creates a new RingBuffer with the given size and configuration. It returns an error if the size is less than or equal to 0.

func (*RingBuffer[T]) Capacity

func (r *RingBuffer[T]) Capacity() int

Capacity returns the size of the underlying buffer

func (*RingBuffer[T]) ClearBuffer

func (r *RingBuffer[T]) ClearBuffer()

ClearBuffer clears all items in the buffer and resets read/write positions. Useful when shrinking the buffer or cleaning up resources.

func (*RingBuffer[T]) Close

func (r *RingBuffer[T]) Close() error

Close closes the ring buffer and cleans up resources. Behavior: - Sets error to io.EOF - Clears all items in the buffer - Signals all waiting readers and writers - All subsequent operations will return io.EOF

func (*RingBuffer[T]) CopyConfig

func (r *RingBuffer[T]) CopyConfig(source *RingBuffer[T]) *RingBuffer[T]

CopyConfig copies the configuration settings from the source buffer to the target buffer. This includes blocking mode, timeouts, and cancellation context.

func (*RingBuffer[T]) Flush

func (r *RingBuffer[T]) Flush()

Flush clears all items from the buffer while maintaining its configuration. This includes: - Resetting read and write positions to 0 - Clearing the full flag - Clearing the buffer contents - Maintaining error state and configuration (blocking, timeouts, hooks)

func (*RingBuffer[T]) Free

func (r *RingBuffer[T]) Free() int

Free returns the number of items that can be written without blocking. This is the available space in the buffer.

func (*RingBuffer[T]) GetAllView

func (r *RingBuffer[T]) GetAllView() (part1, part2 []T, err error)

GetAllView returns a view of all items in the buffer. The view is not a copy, but a reference to the buffer. The view is valid until the buffer is modified. If the view is modified, the buffer will be modified. Make sure to get the items out of the slice before the buffer is modified. This is more efficient than GetAll, but less safe, depending on your use case. Returns ErrIsEmpty if the buffer is empty.

func (*RingBuffer[T]) GetBlockedReaders added in v0.2.0

func (r *RingBuffer[T]) GetBlockedReaders() int

GetBlockedReaders returns the number of blocked readers

func (*RingBuffer[T]) GetBlockedWriters

func (r *RingBuffer[T]) GetBlockedWriters() int

GetBlockedWriters returns the number of blocked writers

func (*RingBuffer[T]) GetN

func (r *RingBuffer[T]) GetN(n int) (items []T, err error)

GetMany returns n items from the buffer. Behavior: - Gets all n items or blocks until it can - Returns ErrIsEmpty if buffer is empty and not blocking - Returns context.DeadlineExceeded if timeout occurs - Handles wrapping around the buffer end

func (*RingBuffer[T]) GetNView

func (r *RingBuffer[T]) GetNView(n int) (part1, part2 []T, err error)

GetManyView returns a view of exactly n items from the buffer. The view is not a copy, but a reference to the buffer. The view is valid until the buffer is modified. If the view is modified, the buffer will be modified. Make sure to get the items out of the slice before the buffer is modified. This is more efficient than GetMany, but less safe, depending on your use case. Returns: - ErrInvalidLength if n <= 0 or n > buffer size - ErrIsEmpty if buffer is empty and not blocking - context.DeadlineExceeded if timeout occurs

func (*RingBuffer[T]) GetOne

func (r *RingBuffer[T]) GetOne() (item T, err error)

GetOne returns a single item from the buffer. Behavior: - Blocks if buffer is empty and in blocking mode - Returns ErrIsEmpty if buffer is empty and not blocking - Returns context.DeadlineExceeded if timeout occurs - Signals waiting writers when data is read

func (*RingBuffer[T]) IsEmpty

func (r *RingBuffer[T]) IsEmpty() bool

IsEmpty returns true when the ringbuffer is empty.

func (*RingBuffer[T]) IsFull

func (r *RingBuffer[T]) IsFull() bool

IsFull returns true when the ringbuffer is full.

func (*RingBuffer[T]) Length

func (r *RingBuffer[T]) Length(lock bool) int

Length returns the number of items that can be read. This is the actual number of items in the buffer.

func (*RingBuffer[T]) PeekN

func (r *RingBuffer[T]) PeekN(n int) (items []T, err error)

PeekMany returns exactly n items without removing them from the buffer. Returns ErrIsEmpty if there aren't enough items available.

func (*RingBuffer[T]) PeekNView

func (r *RingBuffer[T]) PeekNView(n int) (part1, part2 []T, err error)

PeekManyView returns a view of exactly n items from the buffer without removing them. The view is not a copy, but a reference to the buffer. The view is valid until the buffer is modified. If the view is modified, the buffer will be modified. Make sure to get the items out of the slice before the buffer is modified. This is more efficient than PeekN, but less safe, depending on your use case. Returns ErrIsEmpty if there aren't exactly n items available.

func (*RingBuffer[T]) PeekOne

func (r *RingBuffer[T]) PeekOne() (item T, err error)

PeekOne returns the next item without removing it from the buffer

func (*RingBuffer[T]) Reset

func (r *RingBuffer[T]) Reset()

Reset resets the buffer to its initial state. This includes: - Resetting read and write positions to 0 - Clearing the full flag - Clearing any error state - Clearing the buffer contents

func (*RingBuffer[T]) WakeUpOneReader added in v0.3.0

func (r *RingBuffer[T]) WakeUpOneReader()

wake up one reader

func (*RingBuffer[T]) WakeUpOneWriter added in v0.3.0

func (r *RingBuffer[T]) WakeUpOneWriter()

wake up one writer

func (*RingBuffer[T]) WithBlocking

func (r *RingBuffer[T]) WithBlocking(block bool) *RingBuffer[T]

WithBlocking sets the blocking mode of the ring buffer. When blocking is enabled: - Read operations will block when the buffer is empty - Write operations will block when the buffer is full - Condition variables are created for synchronization

func (*RingBuffer[T]) WithPreReadBlockHook

func (r *RingBuffer[T]) WithPreReadBlockHook(hook func() (obj T, tryAgain bool, success bool)) *RingBuffer[T]

WithPreReadBlockHook sets a hook function that will be called before blocking on a read or hitting a deadline. This allows for custom handling of blocking situations, such as trying alternative sources for data.

func (*RingBuffer[T]) WithPreWriteBlockHook

func (r *RingBuffer[T]) WithPreWriteBlockHook(hook func() bool) *RingBuffer[T]

WithPreWriteBlockHook sets a hook function that will be called before blocking on a write or hitting a deadline. This allows for custom handling of blocking situations, such as trying alternative destinations for data.

func (*RingBuffer[T]) WithReadTimeout

func (r *RingBuffer[T]) WithReadTimeout(d time.Duration) *RingBuffer[T]

WithReadTimeout sets the timeout for read operations. Read operations wait for writes to complete, so this sets the write timeout. This method automatically enables blocking mode since timeouts require blocking behavior.

func (*RingBuffer[T]) WithTimeout

func (r *RingBuffer[T]) WithTimeout(d time.Duration) *RingBuffer[T]

WithTimeout sets both read and write timeouts for the ring buffer. When a timeout occurs, the operation returns context.DeadlineExceeded. A timeout of 0 or less disables timeouts. This method automatically enables blocking mode since timeouts require blocking behavior.

func (*RingBuffer[T]) WithWriteTimeout

func (r *RingBuffer[T]) WithWriteTimeout(d time.Duration) *RingBuffer[T]

WithWriteTimeout sets the timeout for write operations. Write operations wait for reads to complete, so this sets the read timeout. This method automatically enables blocking mode since timeouts require blocking behavior.

func (*RingBuffer[T]) Write

func (r *RingBuffer[T]) Write(item T) error

Write writes a single item to the buffer. Behavior: - Blocks if buffer is full and in blocking mode - Returns ErrIsFull if buffer is full and not blocking - Returns context.DeadlineExceeded if timeout occurs - Signals waiting readers when data is written

func (*RingBuffer[T]) WriteMany

func (r *RingBuffer[T]) WriteMany(items []T) (n int, err error)

WriteMany writes multiple items to the buffer. Behavior: - Writes all items or none - Returns ErrIsFull if buffer doesn't have enough space and not blocking - Blocks until all items can be written or timeout occurs - Returns number of items written and any error - Handles wrapping around the buffer end

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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