Skip to main content

surreal_sync_runtime/pipeline/
cow.rs

1//! Arc copy-on-write batch adapter over [`InPlaceTransform`].
2
3use anyhow::Result;
4use std::sync::Arc;
5use surreal_sync_core::InPlaceTransform;
6use surreal_sync_core::{Change, Relation, Row};
7
8/// Batch of `Arc`-shared items with copy-on-write mutation via [`Arc::make_mut`].
9///
10/// Prefer owned `Vec` + [`InPlaceTransform::transform_rows_inplace`] /
11/// [`InPlaceTransform::transform_changes_inplace`] when sharing is not required.
12/// Use [`CowBatch`] only when the same items may be shared across holders and
13/// mutation must not affect other owners.
14///
15/// # Cost of `apply_inplace`
16///
17/// [`Self::apply_inplace`] always calls [`Arc::make_mut`] before invoking the
18/// transform. When an item's strong count is greater than one, **`make_mut`
19/// clones even if the transform is a no-op** (e.g. [`crate::pipeline::Passthrough`]).
20/// Unique arcs (refcount 1) are mutated without allocation regardless of whether
21/// the transform writes.
22#[derive(Debug, Clone)]
23pub struct CowBatch<T> {
24    /// Shared items; mutated through [`Arc::make_mut`] in [`Self::apply_inplace`].
25    pub items: Vec<Arc<T>>,
26}
27
28impl<T> CowBatch<T> {
29    /// Create a batch from already-shared items.
30    pub fn new(items: Vec<Arc<T>>) -> Self {
31        Self { items }
32    }
33
34    /// Create a batch by wrapping each owned item in a fresh `Arc` (refcount 1).
35    pub fn from_owned(items: Vec<T>) -> Self {
36        Self {
37            items: items.into_iter().map(Arc::new).collect(),
38        }
39    }
40
41    /// Number of items in the batch.
42    pub fn len(&self) -> usize {
43        self.items.len()
44    }
45
46    /// Whether the batch is empty.
47    pub fn is_empty(&self) -> bool {
48        self.items.is_empty()
49    }
50
51    /// Consume the batch, returning the underlying `Arc`s.
52    pub fn into_items(self) -> Vec<Arc<T>> {
53        self.items
54    }
55}
56
57impl CowBatch<Row> {
58    /// Apply an in-place transform with Arc COW semantics.
59    ///
60    /// Always calls [`Arc::make_mut`] per item. Clones when the arc is shared
61    /// (strong count &gt; 1), including when `t` is a no-op such as
62    /// [`crate::pipeline::Passthrough`]. Unique arcs are mutated without allocation.
63    pub fn apply_inplace(&mut self, t: &impl InPlaceTransform) -> Result<()> {
64        for item in &mut self.items {
65            t.transform_row(Arc::make_mut(item))?;
66        }
67        Ok(())
68    }
69}
70
71impl CowBatch<Change> {
72    /// Apply an in-place transform with Arc COW semantics.
73    ///
74    /// Always calls [`Arc::make_mut`] per item. Clones when the arc is shared
75    /// (strong count &gt; 1), including when `t` is a no-op such as
76    /// [`crate::pipeline::Passthrough`]. Unique arcs are mutated without allocation.
77    pub fn apply_inplace(&mut self, t: &impl InPlaceTransform) -> Result<()> {
78        for item in &mut self.items {
79            t.transform_change(Arc::make_mut(item))?;
80        }
81        Ok(())
82    }
83}
84
85impl CowBatch<Relation> {
86    /// Apply an in-place transform with Arc COW semantics for relations.
87    pub fn apply_inplace(&mut self, t: &impl InPlaceTransform) -> Result<()> {
88        for item in &mut self.items {
89            t.transform_relation(Arc::make_mut(item))?;
90        }
91        Ok(())
92    }
93}