#[cfg(feature = "cow-stats")]
use std::sync::atomic::Ordering;
use std::{borrow::Borrow, mem, ops::Deref, sync::Arc, vec};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
#[cfg(feature = "cow-stats")]
pub mod stats {
use std::sync::atomic::{AtomicU64, Ordering};
pub static MUTATIONS: AtomicU64 = AtomicU64::new(0);
pub static COPIES: AtomicU64 = AtomicU64::new(0);
pub static ELEMENTS_COPIED: AtomicU64 = AtomicU64::new(0);
pub static CLONES: AtomicU64 = AtomicU64::new(0);
pub static BYTES_CLONED: AtomicU64 = AtomicU64::new(0);
pub static EMPTY_CLONES: AtomicU64 = AtomicU64::new(0);
#[derive(Debug, Clone, Copy)]
pub struct Snapshot {
pub mutations: u64,
pub copies: u64,
pub elements_copied: u64,
pub clones: u64,
pub bytes_cloned: u64,
pub empty_clones: u64,
}
pub fn reset() {
MUTATIONS.store(0, Ordering::Relaxed);
COPIES.store(0, Ordering::Relaxed);
ELEMENTS_COPIED.store(0, Ordering::Relaxed);
CLONES.store(0, Ordering::Relaxed);
BYTES_CLONED.store(0, Ordering::Relaxed);
EMPTY_CLONES.store(0, Ordering::Relaxed);
}
pub fn snapshot() -> Snapshot {
Snapshot {
mutations: MUTATIONS.load(Ordering::Relaxed),
copies: COPIES.load(Ordering::Relaxed),
elements_copied: ELEMENTS_COPIED.load(Ordering::Relaxed),
clones: CLONES.load(Ordering::Relaxed),
bytes_cloned: BYTES_CLONED.load(Ordering::Relaxed),
empty_clones: EMPTY_CLONES.load(Ordering::Relaxed),
}
}
}
#[derive(Debug, PartialOrd, PartialEq, Ord, Eq, Hash)]
pub struct CowVec<T>
where
T: Clone + PartialEq,
{
inner: Arc<Vec<T>>,
}
impl<T> CowVec<T>
where
T: Clone + PartialEq,
{
pub fn with_capacity(capacity: usize) -> Self {
let aligned_capacity = (capacity + 7) & !7;
Self {
inner: Arc::new(Vec::with_capacity(aligned_capacity)),
}
}
pub fn with_aligned_capacity(capacity: usize) -> Self {
let simd_alignment = 32 / mem::size_of::<T>().max(1);
let aligned_capacity = capacity.div_ceil(simd_alignment) * simd_alignment;
Self {
inner: Arc::new(Vec::with_capacity(aligned_capacity)),
}
}
pub fn len(&self) -> usize {
self.inner.len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn capacity(&self) -> usize {
self.inner.capacity()
}
}
#[macro_export]
macro_rules! cow_vec {
() => {
$crate::util::cowvec::CowVec::new(Vec::new())
};
($($elem:expr),+ $(,)?) => {
$crate::util::cowvec::CowVec::new(vec![$($elem),+])
};
}
impl<T> Default for CowVec<T>
where
T: Clone + PartialEq,
{
fn default() -> Self {
Self {
inner: Arc::new(Vec::new()),
}
}
}
impl<T: Clone + PartialEq> PartialEq<[T]> for &CowVec<T> {
fn eq(&self, other: &[T]) -> bool {
self.inner.as_slice() == other
}
}
impl<T: Clone + PartialEq> PartialEq<[T]> for CowVec<T> {
fn eq(&self, other: &[T]) -> bool {
self.inner.as_slice() == other
}
}
impl<T: Clone + PartialEq> PartialEq<CowVec<T>> for [T] {
fn eq(&self, other: &CowVec<T>) -> bool {
self == other.inner.as_slice()
}
}
impl<T: Clone + PartialEq> Clone for CowVec<T> {
fn clone(&self) -> Self {
#[cfg(feature = "cow-stats")]
{
stats::CLONES.fetch_add(1, Ordering::Relaxed);
let len = self.inner.len();
if len == 0 {
stats::EMPTY_CLONES.fetch_add(1, Ordering::Relaxed);
}
stats::BYTES_CLONED.fetch_add((len * mem::size_of::<T>()) as u64, Ordering::Relaxed);
}
CowVec {
inner: Arc::clone(&self.inner),
}
}
}
impl<T: Clone + PartialEq> CowVec<T> {
pub fn new(vec: Vec<T>) -> Self {
CowVec {
inner: Arc::new(vec),
}
}
pub fn from_rc(rc: Arc<Vec<T>>) -> Self {
CowVec {
inner: rc,
}
}
pub fn try_into_vec(self) -> Result<Vec<T>, Self> {
match Arc::try_unwrap(self.inner) {
Ok(vec) => Ok(vec),
Err(arc) => Err(CowVec {
inner: arc,
}),
}
}
pub fn into_inner(self) -> Vec<T> {
match Arc::try_unwrap(self.inner) {
Ok(vec) => vec,
Err(arc) => (*arc).clone(),
}
}
pub fn as_slice(&self) -> &[T] {
&self.inner
}
pub fn is_owned(&self) -> bool {
Arc::strong_count(&self.inner) == 1
}
pub fn is_shared(&self) -> bool {
Arc::strong_count(&self.inner) > 1
}
pub fn get(&self, idx: usize) -> Option<&T> {
self.inner.get(idx)
}
pub fn make_mut(&mut self) -> &mut Vec<T> {
#[cfg(feature = "cow-stats")]
{
stats::MUTATIONS.fetch_add(1, Ordering::Relaxed);
if Arc::strong_count(&self.inner) > 1 {
stats::COPIES.fetch_add(1, Ordering::Relaxed);
stats::ELEMENTS_COPIED.fetch_add(self.inner.len() as u64, Ordering::Relaxed);
}
}
Arc::make_mut(&mut self.inner)
}
pub fn set(&mut self, idx: usize, value: T) {
self.make_mut()[idx] = value;
}
pub fn push(&mut self, value: T) {
self.make_mut().push(value);
}
pub fn clear(&mut self) {
self.make_mut().clear();
}
pub fn extend(&mut self, iter: impl IntoIterator<Item = T>) {
self.make_mut().extend(iter);
}
pub fn extend_from_slice(&mut self, slice: &[T]) {
self.make_mut().extend_from_slice(slice);
}
pub fn reorder(&mut self, indices: &[usize]) {
let vec = self.make_mut();
let len = vec.len();
assert_eq!(len, indices.len());
let mut visited = vec![false; len];
for start in 0..len {
if visited[start] || indices[start] == start {
continue;
}
let mut current = start;
while !visited[current] {
visited[current] = true;
let next = indices[current];
if next == start {
break;
}
vec.swap(current, next);
current = next;
}
}
}
pub fn aligned_chunks(&self, chunk_size: usize) -> impl Iterator<Item = &[T]> {
self.inner.chunks(chunk_size)
}
pub fn aligned_chunks_mut(&mut self, chunk_size: usize) -> impl Iterator<Item = &mut [T]> {
self.make_mut().chunks_mut(chunk_size)
}
pub fn is_simd_aligned(&self) -> bool {
let alignment = 32;
let ptr = self.inner.as_ptr() as usize;
ptr.is_multiple_of(alignment)
}
pub fn take(&self, n: usize) -> Self {
let len = n.min(self.len());
CowVec::new(self.inner[..len].to_vec())
}
}
impl<T: Clone + PartialEq> IntoIterator for CowVec<T> {
type Item = T;
type IntoIter = vec::IntoIter<T>;
fn into_iter(self) -> Self::IntoIter {
match Arc::try_unwrap(self.inner) {
Ok(vec) => vec.into_iter(),
Err(arc) => (*arc).clone().into_iter(),
}
}
}
impl<T: Clone + PartialEq> Deref for CowVec<T> {
type Target = [T];
fn deref(&self) -> &Self::Target {
self.as_slice()
}
}
impl<T: Clone + PartialEq> Borrow<[T]> for CowVec<T> {
fn borrow(&self) -> &[T] {
self.as_slice()
}
}
impl<T> Serialize for CowVec<T>
where
T: Clone + PartialEq + Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.inner.serialize(serializer)
}
}
impl<'de, T> Deserialize<'de> for CowVec<T>
where
T: Clone + PartialEq + Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let vec = Vec::<T>::deserialize(deserializer)?;
Ok(CowVec {
inner: Arc::new(vec),
})
}
}
#[cfg(test)]
pub mod tests {
use super::CowVec;
#[test]
fn test_new() {
let cow = CowVec::new(vec![1, 2, 3]);
assert_eq!(cow.get(0), Some(&1));
assert_eq!(cow.get(1), Some(&2));
assert_eq!(cow.get(2), Some(&3));
}
#[test]
fn test_is_owned() {
let mut owned = CowVec::new(Vec::with_capacity(16));
owned.extend([1, 2]);
assert!(owned.is_owned());
let shared = owned.clone();
assert!(!owned.is_owned());
assert!(!shared.is_owned());
drop(shared);
assert!(owned.is_owned());
}
#[test]
fn test_is_shared() {
let mut owned = CowVec::new(Vec::with_capacity(16));
owned.extend([1, 2]);
assert!(!owned.is_shared());
let shared = owned.clone();
assert!(owned.is_shared());
assert!(shared.is_shared());
drop(shared);
assert!(!owned.is_shared());
}
#[test]
fn test_extend() {
let mut owned = CowVec::new(Vec::with_capacity(16));
owned.extend([1, 2]);
let ptr_before_owned = ptr_of(&owned);
owned.extend([9, 9, 24]);
assert_eq!(ptr_before_owned, ptr_of(&owned)); assert_eq!(owned.len(), 5);
let mut shared = owned.clone();
let ptr_before_shared = ptr_of(&shared);
shared.extend([9, 9, 24]);
assert_ne!(ptr_before_shared, ptr_of(&shared)); assert_eq!(owned.len(), 5);
}
#[test]
fn test_push() {
let mut owned = CowVec::new(Vec::with_capacity(16));
owned.extend([1, 2]);
let ptr_before_owned = ptr_of(&owned);
owned.push(99);
assert_eq!(ptr_before_owned, ptr_of(&owned)); assert_eq!(owned.len(), 3);
let mut shared = owned.clone();
let ptr_before_shared = ptr_of(&shared);
shared.push(99);
assert_ne!(ptr_before_shared, ptr_of(&shared)); assert_eq!(owned.len(), 3);
}
#[test]
fn test_set() {
let mut owned = CowVec::new(Vec::with_capacity(16));
owned.extend([1, 2]);
let ptr_before_owned = ptr_of(&owned);
owned.set(1, 99);
assert_eq!(ptr_before_owned, ptr_of(&owned)); assert_eq!(*owned, [1, 99]);
let mut shared = owned.clone();
let ptr_before_shared = ptr_of(&shared);
shared.set(1, 99);
assert_ne!(ptr_before_shared, ptr_of(&shared)); assert_eq!(*owned, [1, 99]);
}
#[test]
fn test_reorder() {
let mut owned = CowVec::new(Vec::with_capacity(16));
owned.extend([1, 2]);
let ptr_before_owned = ptr_of(&owned);
owned.reorder(&[1usize, 0]);
assert_eq!(ptr_before_owned, ptr_of(&owned)); assert_eq!(*owned, [2, 1]);
let mut shared = owned.clone();
let ptr_before_shared = ptr_of(&shared);
shared.reorder(&[1usize, 0]);
assert_ne!(ptr_before_shared, ptr_of(&shared)); assert_eq!(*shared, [1, 2]);
}
#[test]
fn test_reorder_identity() {
let mut cow = CowVec::new(vec![10, 20, 30]);
cow.reorder(&[0, 1, 2]); assert_eq!(cow.as_slice(), &[10, 20, 30]);
}
#[test]
fn test_reorder_basic() {
let mut cow = CowVec::new(vec![10, 20, 30]);
cow.reorder(&[2, 0, 1]);
assert_eq!(cow.as_slice(), &[30, 10, 20]);
}
fn ptr_of(v: &CowVec<i32>) -> *const i32 {
v.as_slice().as_ptr()
}
}