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}