mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-04-28 19:41:17 +00:00
295 lines
6 KiB
Go
295 lines
6 KiB
Go
package utils
|
|
|
|
import (
|
|
"sync"
|
|
)
|
|
|
|
// SafeMap is a generic thread-safe map
|
|
type SafeMap[K comparable, V any] struct {
|
|
mu sync.RWMutex
|
|
m map[K]V
|
|
}
|
|
|
|
// NewSafeMap creates a new thread-safe map
|
|
func NewSafeMap[K comparable, V any]() *SafeMap[K, V] {
|
|
return &SafeMap[K, V]{
|
|
m: make(map[K]V),
|
|
}
|
|
}
|
|
|
|
// NewSafeMapWithCapacity creates a new thread-safe map with initial capacity
|
|
func NewSafeMapWithCapacity[K comparable, V any](capacity int) *SafeMap[K, V] {
|
|
return &SafeMap[K, V]{
|
|
m: make(map[K]V, capacity),
|
|
}
|
|
}
|
|
|
|
// Get retrieves a value from the map
|
|
func (sm *SafeMap[K, V]) Get(key K) (V, bool) {
|
|
sm.mu.RLock()
|
|
defer sm.mu.RUnlock()
|
|
val, ok := sm.m[key]
|
|
return val, ok
|
|
}
|
|
|
|
// Set sets a value in the map
|
|
func (sm *SafeMap[K, V]) Set(key K, value V) {
|
|
sm.mu.Lock()
|
|
defer sm.mu.Unlock()
|
|
sm.m[key] = value
|
|
}
|
|
|
|
// Delete removes a key from the map
|
|
func (sm *SafeMap[K, V]) Delete(key K) {
|
|
sm.mu.Lock()
|
|
defer sm.mu.Unlock()
|
|
delete(sm.m, key)
|
|
}
|
|
|
|
// Len returns the number of items in the map
|
|
func (sm *SafeMap[K, V]) Len() int {
|
|
sm.mu.RLock()
|
|
defer sm.mu.RUnlock()
|
|
return len(sm.m)
|
|
}
|
|
|
|
// Keys returns all keys in the map
|
|
func (sm *SafeMap[K, V]) Keys() []K {
|
|
sm.mu.RLock()
|
|
defer sm.mu.RUnlock()
|
|
keys := make([]K, 0, len(sm.m))
|
|
for k := range sm.m {
|
|
keys = append(keys, k)
|
|
}
|
|
return keys
|
|
}
|
|
|
|
// Values returns all values in the map
|
|
func (sm *SafeMap[K, V]) Values() []V {
|
|
sm.mu.RLock()
|
|
defer sm.mu.RUnlock()
|
|
values := make([]V, 0, len(sm.m))
|
|
for _, v := range sm.m {
|
|
values = append(values, v)
|
|
}
|
|
return values
|
|
}
|
|
|
|
// Clear removes all items from the map
|
|
func (sm *SafeMap[K, V]) Clear() {
|
|
sm.mu.Lock()
|
|
defer sm.mu.Unlock()
|
|
sm.m = make(map[K]V)
|
|
}
|
|
|
|
// Range iterates over the map with a callback function
|
|
func (sm *SafeMap[K, V]) Range(f func(key K, value V) bool) {
|
|
sm.mu.RLock()
|
|
defer sm.mu.RUnlock()
|
|
for k, v := range sm.m {
|
|
if !f(k, v) {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
// CircularBuffer is a generic circular buffer for storing time-series data
|
|
type CircularBuffer[T any] struct {
|
|
mu sync.RWMutex
|
|
data []T
|
|
capacity int
|
|
head int
|
|
tail int
|
|
size int
|
|
}
|
|
|
|
// NewCircularBuffer creates a new circular buffer
|
|
func NewCircularBuffer[T any](capacity int) *CircularBuffer[T] {
|
|
return &CircularBuffer[T]{
|
|
data: make([]T, capacity),
|
|
capacity: capacity,
|
|
}
|
|
}
|
|
|
|
// Push adds an item to the buffer
|
|
func (cb *CircularBuffer[T]) Push(item T) {
|
|
cb.mu.Lock()
|
|
defer cb.mu.Unlock()
|
|
|
|
cb.data[cb.tail] = item
|
|
cb.tail = (cb.tail + 1) % cb.capacity
|
|
|
|
if cb.size < cb.capacity {
|
|
cb.size++
|
|
} else {
|
|
cb.head = (cb.head + 1) % cb.capacity
|
|
}
|
|
}
|
|
|
|
// GetAll returns all items in the buffer in order
|
|
func (cb *CircularBuffer[T]) GetAll() []T {
|
|
cb.mu.RLock()
|
|
defer cb.mu.RUnlock()
|
|
|
|
if cb.size == 0 {
|
|
return []T{}
|
|
}
|
|
|
|
result := make([]T, cb.size)
|
|
if cb.head < cb.tail {
|
|
copy(result, cb.data[cb.head:cb.tail])
|
|
} else {
|
|
n := copy(result, cb.data[cb.head:])
|
|
copy(result[n:], cb.data[:cb.tail])
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// Size returns the current number of items in the buffer
|
|
func (cb *CircularBuffer[T]) Size() int {
|
|
cb.mu.RLock()
|
|
defer cb.mu.RUnlock()
|
|
return cb.size
|
|
}
|
|
|
|
// Clear empties the buffer
|
|
func (cb *CircularBuffer[T]) Clear() {
|
|
cb.mu.Lock()
|
|
defer cb.mu.Unlock()
|
|
cb.head = 0
|
|
cb.tail = 0
|
|
cb.size = 0
|
|
}
|
|
|
|
// SliceContains checks if a slice contains a value
|
|
func SliceContains[T comparable](slice []T, value T) bool {
|
|
for _, v := range slice {
|
|
if v == value {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// SliceMap applies a function to each element of a slice
|
|
func SliceMap[T any, U any](slice []T, f func(T) U) []U {
|
|
result := make([]U, len(slice))
|
|
for i, v := range slice {
|
|
result[i] = f(v)
|
|
}
|
|
return result
|
|
}
|
|
|
|
// SliceFilter filters a slice based on a predicate
|
|
func SliceFilter[T any](slice []T, predicate func(T) bool) []T {
|
|
result := make([]T, 0, len(slice))
|
|
for _, v := range slice {
|
|
if predicate(v) {
|
|
result = append(result, v)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
// SliceReduce reduces a slice to a single value
|
|
func SliceReduce[T any, U any](slice []T, initial U, reducer func(U, T) U) U {
|
|
result := initial
|
|
for _, v := range slice {
|
|
result = reducer(result, v)
|
|
}
|
|
return result
|
|
}
|
|
|
|
// SliceUnique returns unique elements from a slice
|
|
func SliceUnique[T comparable](slice []T) []T {
|
|
seen := make(map[T]struct{})
|
|
result := make([]T, 0, len(slice))
|
|
for _, v := range slice {
|
|
if _, ok := seen[v]; !ok {
|
|
seen[v] = struct{}{}
|
|
result = append(result, v)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
// Ptr returns a pointer to the given value
|
|
func Ptr[T any](v T) *T {
|
|
return &v
|
|
}
|
|
|
|
// ValueOr returns the value if not nil, otherwise returns the default
|
|
func ValueOr[T any](ptr *T, defaultValue T) T {
|
|
if ptr != nil {
|
|
return *ptr
|
|
}
|
|
return defaultValue
|
|
}
|
|
|
|
// Result represents a result that can be either a value or an error
|
|
type Result[T any] struct {
|
|
value T
|
|
err error
|
|
}
|
|
|
|
// NewResult creates a new Result
|
|
func NewResult[T any](value T, err error) Result[T] {
|
|
return Result[T]{value: value, err: err}
|
|
}
|
|
|
|
// Ok creates a successful Result
|
|
func Ok[T any](value T) Result[T] {
|
|
return Result[T]{value: value}
|
|
}
|
|
|
|
// Err creates an error Result
|
|
func Err[T any](err error) Result[T] {
|
|
var zero T
|
|
return Result[T]{value: zero, err: err}
|
|
}
|
|
|
|
// Value returns the value and error
|
|
func (r Result[T]) Value() (T, error) {
|
|
return r.value, r.err
|
|
}
|
|
|
|
// IsOk returns true if the result is successful
|
|
func (r Result[T]) IsOk() bool {
|
|
return r.err == nil
|
|
}
|
|
|
|
// IsErr returns true if the result is an error
|
|
func (r Result[T]) IsErr() bool {
|
|
return r.err != nil
|
|
}
|
|
|
|
// Unwrap returns the value or panics if there's an error
|
|
// Note: Use UnwrapOr for safer error handling
|
|
func (r Result[T]) Unwrap() T {
|
|
if r.err != nil {
|
|
panic(r.err)
|
|
}
|
|
return r.value
|
|
}
|
|
|
|
// UnwrapSafe returns the value and error without panicking
|
|
func (r Result[T]) UnwrapSafe() (T, error) {
|
|
return r.value, r.err
|
|
}
|
|
|
|
// UnwrapOr returns the value or a default if there's an error
|
|
func (r Result[T]) UnwrapOr(defaultValue T) T {
|
|
if r.err != nil {
|
|
return defaultValue
|
|
}
|
|
return r.value
|
|
}
|
|
|
|
// Map transforms the value if successful
|
|
func (r Result[T]) Map(f func(T) T) Result[T] {
|
|
if r.err != nil {
|
|
return r
|
|
}
|
|
return Ok(f(r.value))
|
|
}
|