Skip to main content

apache_datasketches/cpc/
sketch.rs

1use crate::error::SketchError;
2use apache_datasketches_sys::cpc_sketch::ffi as sys;
3use cxx::UniquePtr;
4
5/// A CPC (Compressed Probabilistic Counting) sketch: estimates the number
6/// of distinct items added via `update_*`. Unlike Theta's `ThetaSketch`,
7/// there is no separate compact/wrapped variant — this single type is
8/// both the mutable/update type and the serializable type, since CPC's
9/// serialized form is always compressed by construction. Build one with
10/// [`CpcSketchBuilder`](super::CpcSketchBuilder).
11pub struct CpcSketch {
12    pub(crate) inner: UniquePtr<sys::CpcSketchShim>,
13}
14
15unsafe impl Send for CpcSketch {}
16
17impl CpcSketch {
18    pub(crate) fn from_lg_k(lg_k: u8) -> Result<Self, SketchError> {
19        let inner = sys::new_cpc_sketch(lg_k)
20            .map_err(|e| SketchError::InvalidConfig(e.what().to_string()))?;
21        Ok(Self { inner })
22    }
23
24    /// Reconstructs a sketch from bytes produced by [`Self::serialize`].
25    pub fn deserialize(bytes: &[u8]) -> Result<Self, SketchError> {
26        let inner = sys::cpc_sketch_deserialize(bytes)
27            .map_err(|e| SketchError::Deserialization(e.what().to_string()))?;
28        Ok(Self { inner })
29    }
30
31    /// Adds a `u64` value to the sketch.
32    pub fn update_u64(&mut self, value: u64) {
33        self.inner.pin_mut().update_u64(value);
34    }
35
36    /// Adds an `i64` value to the sketch.
37    pub fn update_i64(&mut self, value: i64) {
38        self.inner.pin_mut().update_i64(value);
39    }
40
41    /// Adds a `u32` value to the sketch.
42    pub fn update_u32(&mut self, value: u32) {
43        self.inner.pin_mut().update_u32(value);
44    }
45
46    /// Adds an `i32` value to the sketch.
47    pub fn update_i32(&mut self, value: i32) {
48        self.inner.pin_mut().update_i32(value);
49    }
50
51    /// Adds a `u16` value to the sketch.
52    pub fn update_u16(&mut self, value: u16) {
53        self.inner.pin_mut().update_u16(value);
54    }
55
56    /// Adds an `i16` value to the sketch.
57    pub fn update_i16(&mut self, value: i16) {
58        self.inner.pin_mut().update_i16(value);
59    }
60
61    /// Adds a `u8` value to the sketch.
62    pub fn update_u8(&mut self, value: u8) {
63        self.inner.pin_mut().update_u8(value);
64    }
65
66    /// Adds an `i8` value to the sketch.
67    pub fn update_i8(&mut self, value: i8) {
68        self.inner.pin_mut().update_i8(value);
69    }
70
71    /// Adds an `f64` value to the sketch.
72    pub fn update_f64(&mut self, value: f64) {
73        self.inner.pin_mut().update_f64(value);
74    }
75
76    /// Adds an `f32` value to the sketch.
77    pub fn update_f32(&mut self, value: f32) {
78        self.inner.pin_mut().update_f32(value);
79    }
80
81    /// Adds a string value to the sketch.
82    pub fn update_str(&mut self, value: &str) {
83        self.inner.pin_mut().update_str(value);
84    }
85
86    /// Adds an arbitrary byte slice to the sketch.
87    pub fn update_bytes(&mut self, value: &[u8]) {
88        self.inner.pin_mut().update_bytes(value);
89    }
90
91    /// Returns `true` if no items have been added to this sketch.
92    pub fn is_empty(&self) -> bool {
93        self.inner.is_empty()
94    }
95
96    /// Returns the current estimate of the number of distinct items added.
97    pub fn get_estimate(&self) -> f64 {
98        self.inner.get_estimate()
99    }
100
101    /// Returns the lower bound of the confidence interval around
102    /// [`Self::get_estimate`], for the given number of standard deviations
103    /// (`1`, `2`, or `3`, corresponding to roughly 67%, 95%, and 99%
104    /// confidence — upstream calls this parameter `kappa`). Returns
105    /// [`SketchError::InvalidConfig`] for any other value.
106    pub fn get_lower_bound(&self, num_std_dev: u8) -> Result<f64, SketchError> {
107        self.inner
108            .get_lower_bound(num_std_dev)
109            .map_err(|e| SketchError::InvalidConfig(e.what().to_string()))
110    }
111
112    /// Returns the upper bound of the confidence interval around
113    /// [`Self::get_estimate`]. See [`Self::get_lower_bound`] for the meaning
114    /// of `num_std_dev`.
115    pub fn get_upper_bound(&self, num_std_dev: u8) -> Result<f64, SketchError> {
116        self.inner
117            .get_upper_bound(num_std_dev)
118            .map_err(|e| SketchError::InvalidConfig(e.what().to_string()))
119    }
120
121    /// Returns the `lg_k` this sketch was built with.
122    pub fn get_lg_k(&self) -> u8 {
123        self.inner.get_lg_k()
124    }
125
126    /// Returns a human-readable, multi-line summary of this sketch's
127    /// internal state — useful for debugging, not for parsing.
128    pub fn to_string_summary(&self) -> String {
129        self.inner.to_string_summary()
130    }
131
132    /// Serializes this sketch to bytes. CPC's on-wire format is always
133    /// compressed, so there is only this one serialization method (unlike
134    /// Theta's separate compressed/uncompressed formats).
135    pub fn serialize(&self) -> Vec<u8> {
136        self.inner.serialize()
137    }
138}
139
140/// The estimated maximum compressed serialized size, in bytes, of a CPC
141/// sketch built with the given `lg_k`. Useful for pre-allocating buffers.
142///
143/// Returns an error if `lg_k` is outside the valid range (`4..=26`).
144pub fn get_max_serialized_size_bytes(lg_k: u8) -> Result<usize, SketchError> {
145    sys::cpc_sketch_max_serialized_size_bytes(lg_k)
146        .map_err(|e| SketchError::InvalidConfig(e.what().to_string()))
147}