speicher

package module
v2.0.0 Latest Latest
Warning

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

Go to latest
Published: Dec 5, 2025 License: Zlib Imports: 9 Imported by: 0

README

Speicher

Pronunciation: [ˈʃpaɪ̯çɐ]

Speicher is a lightweight, data store.

go get -u github.com/bloodmagesoftware/speicher/v2

Look at the wiki for the documentation.

See UPGRADING.md for migration guide from v1 to v2.

Documentation

Overview

non-sucking database

speicher v2 uses a State object for lock management to prevent deadlocks. Each goroutine should create its own State and use it for all lock operations.

Example:

package main

import (
	"fmt"

	"github.com/bloodmagesoftware/speicher/v2"
)

type Foo struct {
	Bar string
	Baz int
}

func main() {
	foo, err := speicher.LoadMap[*Foo]("./data/foo.json")
	if err != nil {
		panic(err)
	}

	func() {
		s := speicher.NewState()
		s.Lock(foo) // use Lock to get write access
		defer s.Unlock(foo) // use Unlock to release write access
		foo.Set("a", &Foo{"aaa", 42})
		foo.Set("b", &Foo{"abc", 69})
	}()

	func() {
		s := speicher.NewState()
		s.RLock(foo) // use RLock to get read access
		defer s.RUnlock(foo) // use RUnlock to release read access
		for key, value := range foo.Iterate { // use the Iterate method to create an iterator
			fmt.Printf("%s => (%s, %d)\n", key, value.Bar, value.Baz)
		}
	}()

	func() {
		s := speicher.NewState()
		s.Lock(foo)
		defer s.Unlock(foo)
		a, ok := foo.Get("a")
		if ok {
			a.Baz *= 10 // a.Baz only gets modified because the store uses a pointer
		}
	}()

	func() {
		s := speicher.NewState()
		s.RLock(foo)
		defer s.RUnlock(foo)
		a, ok := foo.Get("a")
		if ok {
			fmt.Printf("changed a => (%s, %d)\n", a.Bar, a.Baz)
		}
	}()
}

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Err

func Err() <-chan error

Err returns the error channel used when saving the data stores to disk.

func Read

func Read[S Store, R any](store S, f func(s S) R) R

Read acquires a read lock on the store, executes f, then releases the lock. Returns the result of f.

func ReadE

func ReadE[S Store, R any](store S, f func(s S) (R, error)) (R, error)

ReadE acquires a read lock on the store, executes f, then releases the lock. Returns the result of f and any error.

func Write

func Write[S Store, R any](store S, f func(s S) R) R

Write acquires a write lock on the store, executes f, then releases the lock. Returns the result of f.

func WriteE

func WriteE[S Store, R any](store S, f func(s S) (R, error)) (R, error)

WriteE acquires a write lock on the store, executes f, then releases the lock. Returns the result of f and any error.

Types

type List

type List[T any] interface {

	// Get returns the value at a given index of the List and a bool that indicates whether the index exists or not.
	// If no element is found, the bool result will be false.
	// Requires at least a read lock.
	Get(index int) (T, bool)

	// Find traverses the List and returns the first element that satisfies the provided predicate function.
	// If no element is found, the bool result will be false.
	// Requires at least a read lock.
	Find(func(T) bool) (value T, found bool)

	// FindAll returns all elements in the List that satisfy the provided predicate function.
	// If no elements match, it returns an empty slice.
	// Requires at least a read lock.
	FindAll(func(T) bool) (values []T)

	// Append adds the provided value to the end of the List.
	// Requires a write lock.
	Append(value T)

	// AppendUnique adds the provided value to the List only if no existing element is equal to it,
	// based on the supplied equality function. It returns true if the value was added,
	// and false otherwise.
	// Requires a write lock.
	AppendUnique(value T, equal func(a, b T) bool) bool

	// Set assigns the provided value to the element at the specified index.
	// If the index is out of bounds, it returns an error.
	// Requires a write lock.
	Set(index int, value T) error

	// Overwrite replaces the entire List with the data provided in the slice.
	// Requires a write lock.
	Overwrite([]T)

	// Len returns the number of elements currently in the List.
	// Requires at least a read lock.
	Len() int

	// Range returns a read-only channel through which the elements of the List can be iterated.
	// It also returns a cancel function to stop the iteration process if needed.
	// Requires at least a read lock.
	//
	// Deprecated: use Iterate if you need to iterate over the entire data store.
	Range() (<-chan T, func())

	// Iterate iterates over the List and calls the provided function for each element.
	// Requires at least a read lock.
	Iterate(yield func(v T) bool)

	// Save persists the current state of the List to its underlying data store.
	// It returns an error if the operation fails.
	// This method acquires its own read lock internally.
	Save() error
	// contains filtered or unexported methods
}

List is a thread-safe list data store interface that provides basic CRUD operations, predicate-based search, and iteration functionality.

All operations require appropriate locking via a State object:

s := speicher.NewState()
s.Lock(myList)
defer s.Unlock(myList)
myList.Append(value)

func LoadList

func LoadList[T any](location string) (List[T], error)

type Map

type Map[T any] interface {

	// Get retrieves an element associated with the given key.
	// It returns the value and a boolean indicating whether the key exists.
	// Requires at least a read lock.
	Get(key string) (T, bool)

	// Find searches for an element that satisfies the given predicate.
	// It returns the found value and a boolean indicating if a match was found.
	// Requires at least a read lock.
	Find(func(T) bool) (value T, found bool)

	// FindAll retrieves all elements that satisfy the given predicate.
	// It returns a slice containing all matching elements.
	// Requires at least a read lock.
	FindAll(func(T) bool) (values []T)

	// Has checks if an element with the given key exists in the data store.
	// It returns true if the key exists.
	// Requires at least a read lock.
	Has(key string) bool

	// Set adds or updates the element associated with the given key.
	// If the key already exists, its value is overwritten.
	// Requires a write lock.
	Set(key string, value T)

	// Delete removes the element associated with the given key.
	// Requires a write lock.
	Delete(key string)

	// Overwrite replaces the entire data store with the provided map.
	// Requires a write lock.
	Overwrite(map[string]T)

	// RangeKV returns a read-only channel that emits key-value pair elements
	// (as MapRangeEl) from the data store, along with a cancellation function
	// to terminate the iteration when desired.
	// Requires at least a read lock.
	//
	// Deprecated: use Iterate if you need to iterate over the entire data store.
	RangeKV() (<-chan MapRangeEl[T], func())

	// RangeV returns a read-only channel that emits only the values stored in the
	// data store, along with a cancellation function to terminate the iteration.
	// Requires at least a read lock.
	//
	// Deprecated: use Iterate if you need to iterate over the entire data store.
	RangeV() (<-chan T, func())

	// Iterate iterates over the Map and calls the provided function for each element.
	// Requires at least a read lock.
	Iterate(yield func(key string, value T) bool)

	// Save persists the current state of the data store.
	// It returns an error if the save operation fails.
	// This method acquires its own read lock internally.
	Save() error
	// contains filtered or unexported methods
}

Map is a thread-safe key-value data store interface that provides basic CRUD operations, predicate-based search, and iteration functionality.

All operations require appropriate locking via a State object:

s := speicher.NewState()
s.Lock(myMap)
defer s.Unlock(myMap)
myMap.Set("key", value)

func LoadMap

func LoadMap[T any](location string) (Map[T], error)

type MapRangeEl

type MapRangeEl[T any] struct {
	Key   string
	Value T
}

MapRangeEl represents a key-value pair element emitted by the Map's RangeKV method.

type State

type State struct {
	// contains filtered or unexported fields
}

State manages lock state for multiple stores within a single goroutine. It tracks recursive read and write locks and handles lock upgrading.

A State must not be shared across goroutines. Each goroutine that needs to access stores should create its own State via NewState().

Example usage:

s := speicher.NewState()
s.Lock(myMap)
defer s.Unlock(myMap)
myMap.Set("key", value)

func NewState

func NewState() *State

NewState creates a new State for managing locks. Each goroutine should have its own State instance.

func (*State) HasReadLock

func (s *State) HasReadLock(store lockable) bool

HasReadLock returns true if the State holds at least one read lock on the store.

func (*State) HasWriteLock

func (s *State) HasWriteLock(store lockable) bool

HasWriteLock returns true if the State holds at least one write lock on the store.

func (*State) Lock

func (s *State) Lock(store lockable)

Lock acquires a write lock on the store. If the State already holds a write lock on this store, the lock count is incremented. If the State holds read locks, they are upgraded to a write lock.

Multiple calls to Lock must be balanced with equal calls to Unlock.

func (*State) RLock

func (s *State) RLock(store lockable)

RLock acquires a read lock on the store. If the State already holds a write lock, the read is implicitly satisfied without additional mutex operations. If the State already holds read locks, the count is incremented.

Multiple calls to RLock must be balanced with equal calls to RUnlock.

func (*State) RUnlock

func (s *State) RUnlock(store lockable)

RUnlock releases a read lock on the store. The actual mutex is only released when all read and write locks are released.

Panics if called without a matching RLock call.

func (*State) Unlock

func (s *State) Unlock(store lockable)

Unlock releases a write lock on the store. If there are pending read locks from before the write lock was acquired, the mutex downgrades to a read lock.

When the write lock is fully released, automatic save is triggered for stores that support persistence.

Panics if called without a matching Lock call.

type Store

type Store interface {
	// contains filtered or unexported methods
}

Store is a common interface for all speicher data stores. Use State.Lock/State.Unlock/State.RLock/State.RUnlock for locking operations.

Directories

Path Synopsis
cmd
docs command

Jump to

Keyboard shortcuts

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