Skip to main content

reifydb_value/util/
cowvec.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4#[cfg(feature = "cow-stats")]
5use std::sync::atomic::Ordering;
6use std::{borrow::Borrow, mem, ops::Deref, sync::Arc, vec};
7
8use serde::{Deserialize, Deserializer, Serialize, Serializer};
9
10#[cfg(feature = "cow-stats")]
11pub mod stats {
12	use std::sync::atomic::{AtomicU64, Ordering};
13
14	pub static MUTATIONS: AtomicU64 = AtomicU64::new(0);
15	pub static COPIES: AtomicU64 = AtomicU64::new(0);
16	pub static ELEMENTS_COPIED: AtomicU64 = AtomicU64::new(0);
17	pub static CLONES: AtomicU64 = AtomicU64::new(0);
18	pub static BYTES_CLONED: AtomicU64 = AtomicU64::new(0);
19	pub static EMPTY_CLONES: AtomicU64 = AtomicU64::new(0);
20
21	#[derive(Debug, Clone, Copy)]
22	pub struct Snapshot {
23		pub mutations: u64,
24		pub copies: u64,
25		pub elements_copied: u64,
26		pub clones: u64,
27		pub bytes_cloned: u64,
28		pub empty_clones: u64,
29	}
30
31	pub fn reset() {
32		MUTATIONS.store(0, Ordering::Relaxed);
33		COPIES.store(0, Ordering::Relaxed);
34		ELEMENTS_COPIED.store(0, Ordering::Relaxed);
35		CLONES.store(0, Ordering::Relaxed);
36		BYTES_CLONED.store(0, Ordering::Relaxed);
37		EMPTY_CLONES.store(0, Ordering::Relaxed);
38	}
39
40	pub fn snapshot() -> Snapshot {
41		Snapshot {
42			mutations: MUTATIONS.load(Ordering::Relaxed),
43			copies: COPIES.load(Ordering::Relaxed),
44			elements_copied: ELEMENTS_COPIED.load(Ordering::Relaxed),
45			clones: CLONES.load(Ordering::Relaxed),
46			bytes_cloned: BYTES_CLONED.load(Ordering::Relaxed),
47			empty_clones: EMPTY_CLONES.load(Ordering::Relaxed),
48		}
49	}
50}
51
52#[derive(Debug, PartialOrd, PartialEq, Ord, Eq, Hash)]
53pub struct CowVec<T>
54where
55	T: Clone + PartialEq,
56{
57	inner: Arc<Vec<T>>,
58}
59
60impl<T> CowVec<T>
61where
62	T: Clone + PartialEq,
63{
64	pub fn with_capacity(capacity: usize) -> Self {
65		let aligned_capacity = (capacity + 7) & !7;
66		Self {
67			inner: Arc::new(Vec::with_capacity(aligned_capacity)),
68		}
69	}
70
71	pub fn with_aligned_capacity(capacity: usize) -> Self {
72		let simd_alignment = 32 / mem::size_of::<T>().max(1);
73		let aligned_capacity = capacity.div_ceil(simd_alignment) * simd_alignment;
74		Self {
75			inner: Arc::new(Vec::with_capacity(aligned_capacity)),
76		}
77	}
78
79	pub fn len(&self) -> usize {
80		self.inner.len()
81	}
82
83	pub fn is_empty(&self) -> bool {
84		self.len() == 0
85	}
86
87	pub fn capacity(&self) -> usize {
88		self.inner.capacity()
89	}
90}
91
92#[macro_export]
93macro_rules! cow_vec {
94    () => {
95        $crate::util::cowvec::CowVec::new(Vec::new())
96    };
97    ($($elem:expr),+ $(,)?) => {
98        $crate::util::cowvec::CowVec::new(vec![$($elem),+])
99    };
100}
101
102impl<T> Default for CowVec<T>
103where
104	T: Clone + PartialEq,
105{
106	fn default() -> Self {
107		Self {
108			inner: Arc::new(Vec::new()),
109		}
110	}
111}
112
113impl<T: Clone + PartialEq> PartialEq<[T]> for &CowVec<T> {
114	fn eq(&self, other: &[T]) -> bool {
115		self.inner.as_slice() == other
116	}
117}
118
119impl<T: Clone + PartialEq> PartialEq<[T]> for CowVec<T> {
120	fn eq(&self, other: &[T]) -> bool {
121		self.inner.as_slice() == other
122	}
123}
124
125impl<T: Clone + PartialEq> PartialEq<CowVec<T>> for [T] {
126	fn eq(&self, other: &CowVec<T>) -> bool {
127		self == other.inner.as_slice()
128	}
129}
130
131impl<T: Clone + PartialEq> Clone for CowVec<T> {
132	fn clone(&self) -> Self {
133		#[cfg(feature = "cow-stats")]
134		{
135			stats::CLONES.fetch_add(1, Ordering::Relaxed);
136			let len = self.inner.len();
137			if len == 0 {
138				stats::EMPTY_CLONES.fetch_add(1, Ordering::Relaxed);
139			}
140			stats::BYTES_CLONED.fetch_add((len * mem::size_of::<T>()) as u64, Ordering::Relaxed);
141		}
142		CowVec {
143			inner: Arc::clone(&self.inner),
144		}
145	}
146}
147
148impl<T: Clone + PartialEq> CowVec<T> {
149	pub fn new(vec: Vec<T>) -> Self {
150		CowVec {
151			inner: Arc::new(vec),
152		}
153	}
154
155	pub fn from_rc(rc: Arc<Vec<T>>) -> Self {
156		CowVec {
157			inner: rc,
158		}
159	}
160
161	pub fn try_into_vec(self) -> Result<Vec<T>, Self> {
162		match Arc::try_unwrap(self.inner) {
163			Ok(vec) => Ok(vec),
164			Err(arc) => Err(CowVec {
165				inner: arc,
166			}),
167		}
168	}
169
170	pub fn into_inner(self) -> Vec<T> {
171		match Arc::try_unwrap(self.inner) {
172			Ok(vec) => vec,
173			Err(arc) => (*arc).clone(),
174		}
175	}
176
177	pub fn as_slice(&self) -> &[T] {
178		&self.inner
179	}
180
181	pub fn is_owned(&self) -> bool {
182		Arc::strong_count(&self.inner) == 1
183	}
184
185	pub fn is_shared(&self) -> bool {
186		Arc::strong_count(&self.inner) > 1
187	}
188
189	pub fn get(&self, idx: usize) -> Option<&T> {
190		self.inner.get(idx)
191	}
192
193	pub fn make_mut(&mut self) -> &mut Vec<T> {
194		#[cfg(feature = "cow-stats")]
195		{
196			stats::MUTATIONS.fetch_add(1, Ordering::Relaxed);
197			if Arc::strong_count(&self.inner) > 1 {
198				stats::COPIES.fetch_add(1, Ordering::Relaxed);
199				stats::ELEMENTS_COPIED.fetch_add(self.inner.len() as u64, Ordering::Relaxed);
200			}
201		}
202		Arc::make_mut(&mut self.inner)
203	}
204
205	pub fn set(&mut self, idx: usize, value: T) {
206		self.make_mut()[idx] = value;
207	}
208
209	pub fn push(&mut self, value: T) {
210		self.make_mut().push(value);
211	}
212
213	pub fn clear(&mut self) {
214		self.make_mut().clear();
215	}
216
217	pub fn extend(&mut self, iter: impl IntoIterator<Item = T>) {
218		self.make_mut().extend(iter);
219	}
220
221	pub fn extend_from_slice(&mut self, slice: &[T]) {
222		self.make_mut().extend_from_slice(slice);
223	}
224
225	pub fn reorder(&mut self, indices: &[usize]) {
226		let vec = self.make_mut();
227		let len = vec.len();
228		assert_eq!(len, indices.len());
229
230		let mut visited = vec![false; len];
231		for start in 0..len {
232			if visited[start] || indices[start] == start {
233				continue;
234			}
235			let mut current = start;
236			while !visited[current] {
237				visited[current] = true;
238				let next = indices[current];
239				if next == start {
240					break;
241				}
242				vec.swap(current, next);
243				current = next;
244			}
245		}
246	}
247
248	pub fn aligned_chunks(&self, chunk_size: usize) -> impl Iterator<Item = &[T]> {
249		self.inner.chunks(chunk_size)
250	}
251
252	pub fn aligned_chunks_mut(&mut self, chunk_size: usize) -> impl Iterator<Item = &mut [T]> {
253		self.make_mut().chunks_mut(chunk_size)
254	}
255
256	pub fn is_simd_aligned(&self) -> bool {
257		let alignment = 32;
258		let ptr = self.inner.as_ptr() as usize;
259		ptr.is_multiple_of(alignment)
260	}
261
262	pub fn take(&self, n: usize) -> Self {
263		let len = n.min(self.len());
264		CowVec::new(self.inner[..len].to_vec())
265	}
266}
267
268impl<T: Clone + PartialEq> IntoIterator for CowVec<T> {
269	type Item = T;
270	type IntoIter = vec::IntoIter<T>;
271
272	fn into_iter(self) -> Self::IntoIter {
273		match Arc::try_unwrap(self.inner) {
274			Ok(vec) => vec.into_iter(),
275			Err(arc) => (*arc).clone().into_iter(),
276		}
277	}
278}
279
280impl<T: Clone + PartialEq> Deref for CowVec<T> {
281	type Target = [T];
282
283	fn deref(&self) -> &Self::Target {
284		self.as_slice()
285	}
286}
287
288impl<T: Clone + PartialEq> Borrow<[T]> for CowVec<T> {
289	fn borrow(&self) -> &[T] {
290		self.as_slice()
291	}
292}
293
294impl<T> Serialize for CowVec<T>
295where
296	T: Clone + PartialEq + Serialize,
297{
298	fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
299	where
300		S: Serializer,
301	{
302		self.inner.serialize(serializer)
303	}
304}
305
306impl<'de, T> Deserialize<'de> for CowVec<T>
307where
308	T: Clone + PartialEq + Deserialize<'de>,
309{
310	fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
311	where
312		D: Deserializer<'de>,
313	{
314		let vec = Vec::<T>::deserialize(deserializer)?;
315		Ok(CowVec {
316			inner: Arc::new(vec),
317		})
318	}
319}
320
321#[cfg(test)]
322pub mod tests {
323	use super::CowVec;
324
325	#[test]
326	fn test_new() {
327		let cow = CowVec::new(vec![1, 2, 3]);
328		assert_eq!(cow.get(0), Some(&1));
329		assert_eq!(cow.get(1), Some(&2));
330		assert_eq!(cow.get(2), Some(&3));
331	}
332
333	#[test]
334	fn test_is_owned() {
335		let mut owned = CowVec::new(Vec::with_capacity(16));
336		owned.extend([1, 2]);
337
338		assert!(owned.is_owned());
339
340		let shared = owned.clone();
341		assert!(!owned.is_owned());
342		assert!(!shared.is_owned());
343
344		drop(shared);
345
346		assert!(owned.is_owned());
347	}
348
349	#[test]
350	fn test_is_shared() {
351		let mut owned = CowVec::new(Vec::with_capacity(16));
352		owned.extend([1, 2]);
353
354		assert!(!owned.is_shared());
355
356		let shared = owned.clone();
357		assert!(owned.is_shared());
358		assert!(shared.is_shared());
359
360		drop(shared);
361
362		assert!(!owned.is_shared());
363	}
364
365	#[test]
366	fn test_extend() {
367		let mut owned = CowVec::new(Vec::with_capacity(16));
368		owned.extend([1, 2]);
369
370		let ptr_before_owned = ptr_of(&owned);
371		owned.extend([9, 9, 24]);
372		assert_eq!(ptr_before_owned, ptr_of(&owned)); // no copy
373		assert_eq!(owned.len(), 5);
374
375		let mut shared = owned.clone();
376
377		let ptr_before_shared = ptr_of(&shared);
378		shared.extend([9, 9, 24]);
379		assert_ne!(ptr_before_shared, ptr_of(&shared)); // copy-on-write
380		assert_eq!(owned.len(), 5);
381	}
382
383	#[test]
384	fn test_push() {
385		let mut owned = CowVec::new(Vec::with_capacity(16));
386		owned.extend([1, 2]);
387
388		let ptr_before_owned = ptr_of(&owned);
389		owned.push(99);
390		assert_eq!(ptr_before_owned, ptr_of(&owned)); // no copy
391		assert_eq!(owned.len(), 3);
392
393		let mut shared = owned.clone();
394
395		let ptr_before_shared = ptr_of(&shared);
396		shared.push(99);
397		assert_ne!(ptr_before_shared, ptr_of(&shared)); // copy-on-write
398		assert_eq!(owned.len(), 3);
399	}
400
401	#[test]
402	fn test_set() {
403		let mut owned = CowVec::new(Vec::with_capacity(16));
404		owned.extend([1, 2]);
405
406		let ptr_before_owned = ptr_of(&owned);
407		owned.set(1, 99);
408		assert_eq!(ptr_before_owned, ptr_of(&owned)); // no copy
409		assert_eq!(*owned, [1, 99]);
410
411		let mut shared = owned.clone();
412
413		let ptr_before_shared = ptr_of(&shared);
414		shared.set(1, 99);
415		assert_ne!(ptr_before_shared, ptr_of(&shared)); // copy-on-write
416		assert_eq!(*owned, [1, 99]);
417	}
418
419	#[test]
420	fn test_reorder() {
421		let mut owned = CowVec::new(Vec::with_capacity(16));
422		owned.extend([1, 2]);
423
424		let ptr_before_owned = ptr_of(&owned);
425		owned.reorder(&[1usize, 0]);
426		assert_eq!(ptr_before_owned, ptr_of(&owned)); // no copy
427		assert_eq!(*owned, [2, 1]);
428
429		let mut shared = owned.clone();
430
431		let ptr_before_shared = ptr_of(&shared);
432		shared.reorder(&[1usize, 0]);
433		assert_ne!(ptr_before_shared, ptr_of(&shared)); // copy-on-write
434		assert_eq!(*shared, [1, 2]);
435	}
436
437	#[test]
438	fn test_reorder_identity() {
439		let mut cow = CowVec::new(vec![10, 20, 30]);
440		cow.reorder(&[0, 1, 2]); // no-op
441		assert_eq!(cow.as_slice(), &[10, 20, 30]);
442	}
443
444	#[test]
445	fn test_reorder_basic() {
446		let mut cow = CowVec::new(vec![10, 20, 30]);
447		cow.reorder(&[2, 0, 1]);
448		assert_eq!(cow.as_slice(), &[30, 10, 20]);
449	}
450
451	fn ptr_of(v: &CowVec<i32>) -> *const i32 {
452		v.as_slice().as_ptr()
453	}
454}