slices.go

  1package csync
  2
  3import (
  4	"iter"
  5	"sync"
  6)
  7
  8// LazySlice is a thread-safe lazy-loaded slice.
  9type LazySlice[K any] struct {
 10	inner []K
 11	wg    sync.WaitGroup
 12}
 13
 14// NewLazySlice creates a new slice and runs the [load] function in a goroutine
 15// to populate it.
 16func NewLazySlice[K any](load func() []K) *LazySlice[K] {
 17	s := &LazySlice[K]{}
 18	s.wg.Go(func() {
 19		s.inner = load()
 20	})
 21	return s
 22}
 23
 24// Seq returns an iterator that yields elements from the slice.
 25func (s *LazySlice[K]) Seq() iter.Seq[K] {
 26	s.wg.Wait()
 27	return func(yield func(K) bool) {
 28		for _, v := range s.inner {
 29			if !yield(v) {
 30				return
 31			}
 32		}
 33	}
 34}
 35
 36// Slice is a thread-safe slice implementation that provides concurrent access.
 37type Slice[T any] struct {
 38	inner []T
 39	mu    sync.RWMutex
 40}
 41
 42// NewSlice creates a new thread-safe slice.
 43func NewSlice[T any]() *Slice[T] {
 44	return &Slice[T]{
 45		inner: make([]T, 0),
 46	}
 47}
 48
 49// NewSliceFrom creates a new thread-safe slice from an existing slice.
 50func NewSliceFrom[T any](s []T) *Slice[T] {
 51	inner := make([]T, len(s))
 52	copy(inner, s)
 53	return &Slice[T]{
 54		inner: inner,
 55	}
 56}
 57
 58// Append adds an element to the end of the slice.
 59func (s *Slice[T]) Append(items ...T) {
 60	s.mu.Lock()
 61	defer s.mu.Unlock()
 62	s.inner = append(s.inner, items...)
 63}
 64
 65// Get returns the element at the specified index.
 66func (s *Slice[T]) Get(index int) (T, bool) {
 67	s.mu.RLock()
 68	defer s.mu.RUnlock()
 69	var zero T
 70	if index < 0 || index >= len(s.inner) {
 71		return zero, false
 72	}
 73	return s.inner[index], true
 74}
 75
 76// Len returns the number of elements in the slice.
 77func (s *Slice[T]) Len() int {
 78	s.mu.RLock()
 79	defer s.mu.RUnlock()
 80	return len(s.inner)
 81}
 82
 83// SetSlice replaces the entire slice with a new one.
 84func (s *Slice[T]) SetSlice(items []T) {
 85	s.mu.Lock()
 86	defer s.mu.Unlock()
 87	s.inner = make([]T, len(items))
 88	copy(s.inner, items)
 89}
 90
 91// Seq returns an iterator that yields elements from the slice.
 92func (s *Slice[T]) Seq() iter.Seq[T] {
 93	return func(yield func(T) bool) {
 94		for _, v := range s.Seq2() {
 95			if !yield(v) {
 96				return
 97			}
 98		}
 99	}
100}
101
102// Seq2 returns an iterator that yields index-value pairs from the slice.
103func (s *Slice[T]) Seq2() iter.Seq2[int, T] {
104	items := s.Copy()
105	return func(yield func(int, T) bool) {
106		for i, v := range items {
107			if !yield(i, v) {
108				return
109			}
110		}
111	}
112}
113
114// Copy returns a copy of the inner slice.
115func (s *Slice[T]) Copy() []T {
116	s.mu.RLock()
117	defer s.mu.RUnlock()
118	items := make([]T, len(s.inner))
119	copy(items, s.inner)
120	return items
121}