slices.go

  1package csync
  2
  3import (
  4	"iter"
  5	"slices"
  6	"sync"
  7)
  8
  9// LazySlice is a thread-safe lazy-loaded slice.
 10type LazySlice[K any] struct {
 11	inner []K
 12	wg    sync.WaitGroup
 13}
 14
 15// NewLazySlice creates a new slice and runs the [load] function in a goroutine
 16// to populate it.
 17func NewLazySlice[K any](load func() []K) *LazySlice[K] {
 18	s := &LazySlice[K]{}
 19	s.wg.Add(1)
 20	go func() {
 21		s.inner = load()
 22		s.wg.Done()
 23	}()
 24	return s
 25}
 26
 27// Seq returns an iterator that yields elements from the slice.
 28func (s *LazySlice[K]) Seq() iter.Seq[K] {
 29	s.wg.Wait()
 30	return func(yield func(K) bool) {
 31		for _, v := range s.inner {
 32			if !yield(v) {
 33				return
 34			}
 35		}
 36	}
 37}
 38
 39// Slice is a thread-safe slice implementation that provides concurrent access.
 40type Slice[T any] struct {
 41	inner []T
 42	mu    sync.RWMutex
 43}
 44
 45// NewSlice creates a new thread-safe slice.
 46func NewSlice[T any]() *Slice[T] {
 47	return &Slice[T]{
 48		inner: make([]T, 0),
 49	}
 50}
 51
 52// NewSliceFrom creates a new thread-safe slice from an existing slice.
 53func NewSliceFrom[T any](s []T) *Slice[T] {
 54	inner := make([]T, len(s))
 55	copy(inner, s)
 56	return &Slice[T]{
 57		inner: inner,
 58	}
 59}
 60
 61// Append adds an element to the end of the slice.
 62func (s *Slice[T]) Append(item T) {
 63	s.mu.Lock()
 64	defer s.mu.Unlock()
 65	s.inner = append(s.inner, item)
 66}
 67
 68// Prepend adds an element to the beginning of the slice.
 69func (s *Slice[T]) Prepend(item T) {
 70	s.mu.Lock()
 71	defer s.mu.Unlock()
 72	s.inner = append([]T{item}, s.inner...)
 73}
 74
 75// Delete removes the element at the specified index.
 76func (s *Slice[T]) Delete(index int) bool {
 77	s.mu.Lock()
 78	defer s.mu.Unlock()
 79	if index < 0 || index >= len(s.inner) {
 80		return false
 81	}
 82	s.inner = slices.Delete(s.inner, index, index+1)
 83	return true
 84}
 85
 86// Get returns the element at the specified index.
 87func (s *Slice[T]) Get(index int) (T, bool) {
 88	s.mu.RLock()
 89	defer s.mu.RUnlock()
 90	var zero T
 91	if index < 0 || index >= len(s.inner) {
 92		return zero, false
 93	}
 94	return s.inner[index], true
 95}
 96
 97// Set updates the element at the specified index.
 98func (s *Slice[T]) Set(index int, item T) bool {
 99	s.mu.Lock()
100	defer s.mu.Unlock()
101	if index < 0 || index >= len(s.inner) {
102		return false
103	}
104	s.inner[index] = item
105	return true
106}
107
108// Len returns the number of elements in the slice.
109func (s *Slice[T]) Len() int {
110	s.mu.RLock()
111	defer s.mu.RUnlock()
112	return len(s.inner)
113}
114
115// Slice returns a copy of the underlying slice.
116func (s *Slice[T]) Slice() []T {
117	s.mu.RLock()
118	defer s.mu.RUnlock()
119	result := make([]T, len(s.inner))
120	copy(result, s.inner)
121	return result
122}
123
124// SetSlice replaces the entire slice with a new one.
125func (s *Slice[T]) SetSlice(items []T) {
126	s.mu.Lock()
127	defer s.mu.Unlock()
128	s.inner = make([]T, len(items))
129	copy(s.inner, items)
130}
131
132// Clear removes all elements from the slice.
133func (s *Slice[T]) Clear() {
134	s.mu.Lock()
135	defer s.mu.Unlock()
136	s.inner = s.inner[:0]
137}
138
139// Seq returns an iterator that yields elements from the slice.
140func (s *Slice[T]) Seq() iter.Seq[T] {
141	// Take a snapshot to avoid holding the lock during iteration
142	items := s.Slice()
143	return func(yield func(T) bool) {
144		for _, v := range items {
145			if !yield(v) {
146				return
147			}
148		}
149	}
150}
151
152// SeqWithIndex returns an iterator that yields index-value pairs from the slice.
153func (s *Slice[T]) SeqWithIndex() iter.Seq2[int, T] {
154	// Take a snapshot to avoid holding the lock during iteration
155	items := s.Slice()
156	return func(yield func(int, T) bool) {
157		for i, v := range items {
158			if !yield(i, v) {
159				return
160			}
161		}
162	}
163}