apache_datasketches/tuple/generic/compact.rs
1use super::summary::{unerase, TupleSummary};
2use crate::error::SketchError;
3use apache_datasketches_sys::tuple_generic::ffi as sys;
4use cxx::UniquePtr;
5use std::marker::PhantomData;
6
7/// An immutable snapshot of a generic Tuple sketch, produced by
8/// [`TupleSketch::compact`](super::TupleSketch::compact) or by any set
9/// operation's result.
10///
11/// Serialization is not part of this version; it is the subject of a
12/// follow-up design.
13pub struct CompactTupleSketch<S: TupleSummary> {
14 pub(crate) inner: UniquePtr<sys::CompactTupleGenericSketchShim>,
15 pub(crate) _marker: PhantomData<fn() -> S>,
16}
17
18// Sound because `S: Send` is a supertrait of `TupleSummary` and the erased
19// box is `Box<dyn RawSummaryOps + Send>`. Deliberately NOT `Sync`: the C++
20// shim lazily populates a `mutable` entry cache (`entries_`/`entries_built_`
21// in `tuple_generic_compact_shim.h`) from otherwise-`const` methods, which is
22// only safe without concurrent `&`-access to the same instance. Do not add a
23// `Sync` impl, and do not wrap the shim in a `Sync` newtype.
24unsafe impl<S: TupleSummary> Send for CompactTupleSketch<S> {}
25
26impl<S: TupleSummary> CompactTupleSketch<S> {
27 /// Wraps a shim produced by [`TupleSketch::compact`](super::TupleSketch::compact)
28 /// or a set operation.
29 ///
30 /// `inner` should have every summary reachable from it be an `Adapter<S>`
31 /// for this same `S` — that is, it should have originated from a sketch
32 /// or operation typed over `S`.
33 ///
34 /// # Panics
35 ///
36 /// [`Self::entries`] calls `unerase` on every summary it reads back.
37 /// `unerase`'s invariant panic is otherwise unreachable, but passing a
38 /// shim whose summaries were erased for a different `S` makes it
39 /// reachable, and the panic fires there instead of here.
40 pub(crate) fn from_shim(inner: UniquePtr<sys::CompactTupleGenericSketchShim>) -> Self {
41 Self {
42 inner,
43 _marker: PhantomData,
44 }
45 }
46
47 /// Returns the current estimate of the number of distinct keys.
48 pub fn get_estimate(&self) -> f64 {
49 self.inner.get_estimate()
50 }
51
52 /// Returns the lower bound of the confidence interval around
53 /// [`Self::get_estimate`], for `num_std_dev` of `1`, `2`, or `3`.
54 pub fn get_lower_bound(&self, num_std_dev: u8) -> Result<f64, SketchError> {
55 self.inner
56 .get_lower_bound(num_std_dev)
57 .map_err(|e| SketchError::InvalidConfig(e.what().to_string()))
58 }
59
60 /// Returns the upper bound of the confidence interval around
61 /// [`Self::get_estimate`]. See [`Self::get_lower_bound`].
62 pub fn get_upper_bound(&self, num_std_dev: u8) -> Result<f64, SketchError> {
63 self.inner
64 .get_upper_bound(num_std_dev)
65 .map_err(|e| SketchError::InvalidConfig(e.what().to_string()))
66 }
67
68 /// Returns `true` if this sketch represents an empty set.
69 pub fn is_empty(&self) -> bool {
70 self.inner.is_empty()
71 }
72
73 /// Returns `true` if the estimate is a statistical estimate rather than
74 /// an exact count.
75 pub fn is_estimation_mode(&self) -> bool {
76 self.inner.is_estimation_mode()
77 }
78
79 /// Returns `true` if retained entries are sorted by hash value.
80 pub fn is_ordered(&self) -> bool {
81 self.inner.is_ordered()
82 }
83
84 /// Returns the current theta threshold.
85 pub fn get_theta(&self) -> f64 {
86 self.inner.get_theta()
87 }
88
89 /// Returns the number of retained entries.
90 pub fn get_num_retained(&self) -> u32 {
91 self.inner.get_num_retained()
92 }
93
94 /// Iterates the retained entries as `(hash, summary)` pairs.
95 ///
96 /// Each summary is cloned out of C++, so the items are owned. Ordered by
97 /// hash if [`Self::is_ordered`] is `true`.
98 pub fn entries(&self) -> impl Iterator<Item = (u64, S)> + '_ {
99 (0..self.inner.entry_count()).map(move |i| {
100 let hash = self
101 .inner
102 .entry_hash(i)
103 .expect("index derived from entry_count is always in range");
104 let summary = self
105 .inner
106 .entry_summary(i)
107 .expect("index derived from entry_count is always in range");
108 (hash, unerase::<S>(&summary))
109 })
110 }
111}