This is an automated email from the ASF dual-hosted git repository.

tisonkun pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datasketches-rust.git


The following commit(s) were added to refs/heads/main by this push:
     new aeda4de  refactor: simplify estimated_size accounting (#302)
aeda4de is described below

commit aeda4de8f029050dd75063ba6b21a4db8ec09947
Author: tison <[email protected]>
AuthorDate: Sun Oct 25 18:42:22 2026 +0800

    refactor: simplify estimated_size accounting (#302)
---
 CHANGELOG.md                                       |  1 +
 datasketches/src/bloom/sketch.rs                   |  4 +-
 datasketches/src/countmin/sketch.rs                |  6 +--
 datasketches/src/cpc/pair_table.rs                 |  2 +-
 datasketches/src/cpc/sketch.rs                     |  8 ++-
 datasketches/src/cpc/union.rs                      | 10 ++--
 .../src/frequencies/reverse_purge_item_hash_map.rs |  2 +-
 datasketches/src/frequencies/sketch.rs             |  4 +-
 datasketches/src/hll/array4.rs                     |  2 +-
 datasketches/src/hll/array6.rs                     |  2 +-
 datasketches/src/hll/array8.rs                     |  2 +-
 datasketches/src/hll/aux_map.rs                    |  2 +-
 datasketches/src/hll/container.rs                  |  2 +-
 datasketches/src/hll/sketch.rs                     |  8 ++-
 datasketches/src/hll/union.rs                      |  5 +-
 datasketches/src/tdigest/sketch.rs                 |  9 ++--
 datasketches/src/thetafamily/common/hash_table.rs  |  2 +-
 .../src/thetafamily/common/intersection.rs         |  4 +-
 datasketches/src/thetafamily/common/union.rs       |  4 +-
 datasketches/src/thetafamily/theta/intersection.rs |  4 +-
 datasketches/src/thetafamily/theta/sketch.rs       |  8 +--
 datasketches/src/thetafamily/theta/union.rs        |  4 +-
 datasketches/src/thetafamily/tuple/intersection.rs |  4 +-
 datasketches/src/thetafamily/tuple/sketch.rs       |  9 ++--
 datasketches/src/thetafamily/tuple/union.rs        |  4 +-
 tests-integration/tests/bloom_test/sketch.rs       |  6 +++
 tests-integration/tests/countmin_test/sketch.rs    |  4 +-
 tests-integration/tests/cpc_test/union.rs          |  4 +-
 tests-integration/tests/cpc_test/update.rs         | 12 +++++
 tests-integration/tests/frequencies_test/update.rs | 20 ++++++-
 tests-integration/tests/hll_test/union.rs          |  4 +-
 tests-integration/tests/hll_test/update.rs         | 15 ++++++
 tests-integration/tests/tdigest_test/sketch.rs     |  6 +--
 tests-integration/tests/theta_test/intersection.rs |  4 +-
 tests-integration/tests/theta_test/sketch.rs       |  2 +
 tests-integration/tests/theta_test/union.rs        | 15 +++++-
 tests-integration/tests/tuple_test/intersection.rs |  4 +-
 tests-integration/tests/tuple_test/sketch.rs       | 61 ++++++++++++++++++++++
 tests-integration/tests/tuple_test/union.rs        | 15 +++++-
 39 files changed, 204 insertions(+), 80 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6aeb2a9..733a773 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,7 @@ All significant changes to this project will be documented in 
this file.
 * `ReqSketch` queries now use `datasketches::common::SearchCriteria` instead 
of `datasketches::req::SearchCriteria`. `SearchCriteria` no longer implements 
`Default`; explicitly choose `Inclusive` or `Exclusive`.
 * `ReqSketch` and `req::SortedView` queries now distinguish empty sketches 
from invalid arguments. `rank` returns `Option<f64>`; `quantile`, `quantiles`, 
`cdf`, and `pmf`, where available, return `Result<Option<_>, Error>`. Handle 
`None` for valid queries on an empty sketch, and propagate or handle 
`InvalidArgument` for invalid ranks or split points, which are checked before 
emptiness.
 * `TDigest` and `TDigestMut` rank, quantile, CDF, and PMF queries now return 
`Result<Option<_>, Error>`. Invalid arguments return `InvalidArgument` instead 
of panicking, including on empty digests. Propagate or handle the `Result` 
before handling `None` for an empty digest; `min_value` and `max_value` still 
return `Option<f64>`.
+* `estimated_size` now returns an implementation-dependent estimate of memory 
usage. Returned values may change as implementations evolve and should not be 
treated as exact totals or a stable accounting contract.
 
 ### New features
 
diff --git a/datasketches/src/bloom/sketch.rs b/datasketches/src/bloom/sketch.rs
index 0d399d5..245a726 100644
--- a/datasketches/src/bloom/sketch.rs
+++ b/datasketches/src/bloom/sketch.rs
@@ -639,9 +639,9 @@ impl BloomFilter {
         was_set
     }
 
-    /// Returns the estimated size of the filter in bytes.
+    /// Returns an estimate of the filter's memory usage in bytes.
     pub fn estimated_size(&self) -> usize {
-        size_of::<Self>() + self.bit_array.len() * size_of::<u64>()
+        self.bit_array.len() * size_of::<u64>()
     }
 }
 
diff --git a/datasketches/src/countmin/sketch.rs 
b/datasketches/src/countmin/sketch.rs
index 7e3c3ed..b81576f 100644
--- a/datasketches/src/countmin/sketch.rs
+++ b/datasketches/src/countmin/sketch.rs
@@ -532,11 +532,9 @@ impl<T: CountMinValue> CountMinSketch<T> {
         Ok(sketch)
     }
 
-    /// Returns the estimated size of the sketch in bytes.
+    /// Returns an estimate of the sketch's memory usage in bytes.
     pub fn estimated_size(&self) -> usize {
-        size_of::<Self>()
-            + self.counts.capacity() * size_of::<T>()
-            + self.hash_seeds.capacity() * size_of::<u64>()
+        self.counts.capacity() * size_of::<T>() + self.hash_seeds.capacity() * 
size_of::<u64>()
     }
 
     fn make(num_hashes: u8, num_buckets: u32, seed: u64, seed_hash: u16, 
entries: usize) -> Self {
diff --git a/datasketches/src/cpc/pair_table.rs 
b/datasketches/src/cpc/pair_table.rs
index a476ec1..d8ab5d8 100644
--- a/datasketches/src/cpc/pair_table.rs
+++ b/datasketches/src/cpc/pair_table.rs
@@ -258,7 +258,7 @@ impl PairTable {
         }
     }
 
-    /// Returns the estimated size of the heap allocations in bytes
+    /// Returns an estimate of memory usage in bytes.
     pub fn estimated_size(&self) -> usize {
         self.slots.capacity() * size_of::<u32>()
     }
diff --git a/datasketches/src/cpc/sketch.rs b/datasketches/src/cpc/sketch.rs
index c42b367..3222cff 100644
--- a/datasketches/src/cpc/sketch.rs
+++ b/datasketches/src/cpc/sketch.rs
@@ -459,16 +459,14 @@ impl CpcSketch {
         matrix
     }
 
-    /// Returns the estimated size of the sketch in bytes.
+    /// Returns an estimate of the sketch's memory usage in bytes.
     pub fn estimated_size(&self) -> usize {
-        let heap_size = self.sliding_window.capacity()
+        self.sliding_window.capacity()
             + self
                 .surprising_value_table
                 .as_ref()
                 .map(|t| t.estimated_size())
-                .unwrap_or(0);
-
-        size_of::<Self>() + heap_size
+                .unwrap_or(0)
     }
 }
 
diff --git a/datasketches/src/cpc/union.rs b/datasketches/src/cpc/union.rs
index 67870f5..abd473d 100644
--- a/datasketches/src/cpc/union.rs
+++ b/datasketches/src/cpc/union.rs
@@ -347,14 +347,12 @@ impl CpcUnion {
         }
     }
 
-    /// Returns the estimated size of the union in bytes.
+    /// Returns an estimate of the union's memory usage in bytes.
     pub fn estimated_size(&self) -> usize {
-        // The state's inline size is already covered by size_of::<Self>().
-        let heap_size = match &self.state {
-            UnionState::Accumulator(sketch) => sketch.estimated_size() - 
size_of::<CpcSketch>(),
+        match &self.state {
+            UnionState::Accumulator(sketch) => sketch.estimated_size(),
             UnionState::BitMatrix(matrix) => matrix.capacity() * 
size_of::<u64>(),
-        };
-        size_of::<Self>() + heap_size
+        }
     }
 }
 
diff --git a/datasketches/src/frequencies/reverse_purge_item_hash_map.rs 
b/datasketches/src/frequencies/reverse_purge_item_hash_map.rs
index ce3d62d..7f430f6 100644
--- a/datasketches/src/frequencies/reverse_purge_item_hash_map.rs
+++ b/datasketches/src/frequencies/reverse_purge_item_hash_map.rs
@@ -197,7 +197,7 @@ impl<T: Eq + Hash> ReversePurgeItemHashMap<T> {
         self.num_active
     }
 
-    /// Returns the estimated size of the heap allocations in bytes.
+    /// Returns an estimate of memory usage in bytes.
     pub fn estimated_size(&self) -> usize {
         self.keys.capacity() * size_of::<Option<T>>()
             + self.values.capacity() * size_of::<u64>()
diff --git a/datasketches/src/frequencies/sketch.rs 
b/datasketches/src/frequencies/sketch.rs
index 4808ef5..617b98e 100644
--- a/datasketches/src/frequencies/sketch.rs
+++ b/datasketches/src/frequencies/sketch.rs
@@ -323,9 +323,9 @@ impl<T: Eq + Hash> FrequentItemsSketch<T> {
         self.hash_map.lg_length()
     }
 
-    /// Returns the estimated size of the sketch in bytes.
+    /// Returns an estimate of the sketch's memory usage in bytes.
     pub fn estimated_size(&self) -> usize {
-        size_of::<Self>() + self.hash_map.estimated_size()
+        self.hash_map.estimated_size()
     }
 
     /// Updates the sketch with a count of one.
diff --git a/datasketches/src/hll/array4.rs b/datasketches/src/hll/array4.rs
index 19c9632..4190a3f 100644
--- a/datasketches/src/hll/array4.rs
+++ b/datasketches/src/hll/array4.rs
@@ -419,7 +419,7 @@ impl Array4 {
         bytes.into_bytes()
     }
 
-    /// Returns the estimated size of the heap allocations in bytes
+    /// Returns an estimate of memory usage in bytes.
     pub fn estimated_size(&self) -> usize {
         self.bytes.len()
             + self
diff --git a/datasketches/src/hll/array6.rs b/datasketches/src/hll/array6.rs
index c29a901..636da15 100644
--- a/datasketches/src/hll/array6.rs
+++ b/datasketches/src/hll/array6.rs
@@ -233,7 +233,7 @@ impl Array6 {
         bytes.into_bytes()
     }
 
-    /// Returns the estimated size of the heap allocations in bytes
+    /// Returns an estimate of memory usage in bytes.
     pub fn estimated_size(&self) -> usize {
         self.bytes.len()
     }
diff --git a/datasketches/src/hll/array8.rs b/datasketches/src/hll/array8.rs
index 355bcc1..1269d17 100644
--- a/datasketches/src/hll/array8.rs
+++ b/datasketches/src/hll/array8.rs
@@ -278,7 +278,7 @@ impl Array8 {
         bytes.into_bytes()
     }
 
-    /// Returns the estimated size of the heap allocations in bytes
+    /// Returns an estimate of memory usage in bytes.
     pub fn estimated_size(&self) -> usize {
         self.bytes.len()
     }
diff --git a/datasketches/src/hll/aux_map.rs b/datasketches/src/hll/aux_map.rs
index e14ef31..2ce25a3 100644
--- a/datasketches/src/hll/aux_map.rs
+++ b/datasketches/src/hll/aux_map.rs
@@ -192,7 +192,7 @@ impl AuxMap {
         })
     }
 
-    /// Returns the estimated size of the heap allocations in bytes
+    /// Returns an estimate of memory usage in bytes.
     pub fn estimated_size(&self) -> usize {
         self.entries.len() * size_of::<Coupon>()
     }
diff --git a/datasketches/src/hll/container.rs 
b/datasketches/src/hll/container.rs
index 8f0313c..bc8cab6 100644
--- a/datasketches/src/hll/container.rs
+++ b/datasketches/src/hll/container.rs
@@ -121,7 +121,7 @@ impl Container {
         self.coupons.iter().filter(|&&c| !c.is_empty()).copied()
     }
 
-    /// Returns the estimated size of the heap allocations in bytes
+    /// Returns an estimate of memory usage in bytes.
     pub fn estimated_size(&self) -> usize {
         self.coupons.len() * size_of::<Coupon>()
     }
diff --git a/datasketches/src/hll/sketch.rs b/datasketches/src/hll/sketch.rs
index 92d4d5b..910a730 100644
--- a/datasketches/src/hll/sketch.rs
+++ b/datasketches/src/hll/sketch.rs
@@ -470,17 +470,15 @@ impl HllSketch {
         }
     }
 
-    /// Returns the estimated memory usage in bytes, including owned heap 
allocations.
+    /// Returns an estimate of the sketch's memory usage in bytes.
     pub fn estimated_size(&self) -> usize {
-        let heap_size = match &self.mode {
+        match &self.mode {
             Mode::List { list, .. } => list.container().estimated_size(),
             Mode::Set { set, .. } => set.container().estimated_size(),
             Mode::Array4(arr) => arr.estimated_size(),
             Mode::Array6(arr) => arr.estimated_size(),
             Mode::Array8(arr) => arr.estimated_size(),
-        };
-
-        size_of::<Self>() + heap_size
+        }
     }
 }
 
diff --git a/datasketches/src/hll/union.rs b/datasketches/src/hll/union.rs
index 0f6e20e..b245fa7 100644
--- a/datasketches/src/hll/union.rs
+++ b/datasketches/src/hll/union.rs
@@ -365,10 +365,9 @@ impl HllUnion {
         self.gadget.lower_bound(num_std_dev)
     }
 
-    /// Returns the estimated memory usage in bytes, including owned heap 
allocations.
+    /// Returns an estimate of the union's memory usage in bytes.
     pub fn estimated_size(&self) -> usize {
-        // The gadget's inline size is already covered by size_of::<Self>().
-        size_of::<Self>() - size_of::<HllSketch>() + 
self.gadget.estimated_size()
+        self.gadget.estimated_size()
     }
 }
 
diff --git a/datasketches/src/tdigest/sketch.rs 
b/datasketches/src/tdigest/sketch.rs
index 1fb4b16..9bc7b03 100644
--- a/datasketches/src/tdigest/sketch.rs
+++ b/datasketches/src/tdigest/sketch.rs
@@ -156,6 +156,7 @@ impl TDigestBuffer {
         self.centroids
     }
 
+    /// Returns an estimate of memory usage in bytes.
     fn estimated_size(&self) -> usize {
         self.centroids.capacity() * size_of::<Centroid>()
     }
@@ -1012,9 +1013,9 @@ impl TDigestMut {
         centroids.shrink_to(target_capacity);
     }
 
-    /// Returns the estimated size of the sketch in bytes.
+    /// Returns an estimate of the sketch's memory usage in bytes.
     pub fn estimated_size(&self) -> usize {
-        size_of::<Self>() + self.buffer.estimated_size()
+        self.buffer.estimated_size()
     }
 }
 
@@ -1630,9 +1631,9 @@ impl TDigest {
         )
     }
 
-    /// Returns the estimated size of the sketch in bytes.
+    /// Returns an estimate of the sketch's memory usage in bytes.
     pub fn estimated_size(&self) -> usize {
-        size_of::<Self>() + self.centroids.capacity() * size_of::<Centroid>()
+        self.centroids.capacity() * size_of::<Centroid>()
     }
 }
 
diff --git a/datasketches/src/thetafamily/common/hash_table.rs 
b/datasketches/src/thetafamily/common/hash_table.rs
index d2138c9..e0b7523 100644
--- a/datasketches/src/thetafamily/common/hash_table.rs
+++ b/datasketches/src/thetafamily/common/hash_table.rs
@@ -344,7 +344,7 @@ where
             })
     }
 
-    /// Returns the estimated size of the heap allocations in bytes.
+    /// Returns an estimate of memory usage in bytes.
     pub fn estimated_size(&self) -> usize {
         self.entries.capacity() * size_of::<Option<E>>()
     }
diff --git a/datasketches/src/thetafamily/common/intersection.rs 
b/datasketches/src/thetafamily/common/intersection.rs
index 486b2cb..535beba 100644
--- a/datasketches/src/thetafamily/common/intersection.rs
+++ b/datasketches/src/thetafamily/common/intersection.rs
@@ -234,9 +234,9 @@ where
         self.result_state != IntersectionResultState::Uninitialized
     }
 
-    /// Returns the estimated size of the heap allocations in bytes.
+    /// Returns an estimate of memory usage in bytes.
     pub fn estimated_size(&self) -> usize {
-        self.table.estimated_size()
+        size_of::<P>() + self.table.estimated_size()
     }
 
     /// Returns the current intersection as canonical compact-sketch state.
diff --git a/datasketches/src/thetafamily/common/union.rs 
b/datasketches/src/thetafamily/common/union.rs
index 0131b55..4ad3b46 100644
--- a/datasketches/src/thetafamily/common/union.rs
+++ b/datasketches/src/thetafamily/common/union.rs
@@ -150,8 +150,8 @@ where
         self.result_theta = None;
     }
 
-    /// Returns the estimated size of the heap allocations in bytes.
+    /// Returns an estimate of memory usage in bytes.
     pub fn estimated_size(&self) -> usize {
-        self.table.estimated_size()
+        size_of::<P>() + self.table.estimated_size()
     }
 }
diff --git a/datasketches/src/thetafamily/theta/intersection.rs 
b/datasketches/src/thetafamily/theta/intersection.rs
index ace0d41..137891d 100644
--- a/datasketches/src/thetafamily/theta/intersection.rs
+++ b/datasketches/src/thetafamily/theta/intersection.rs
@@ -78,9 +78,9 @@ impl ThetaIntersection {
         self.state.has_result()
     }
 
-    /// Returns the estimated size of the intersection in bytes.
+    /// Returns an estimate of the intersection's memory usage in bytes.
     pub fn estimated_size(&self) -> usize {
-        size_of::<Self>() + self.state.estimated_size()
+        self.state.estimated_size()
     }
 
     /// Returns the current intersection as a compact theta sketch.
diff --git a/datasketches/src/thetafamily/theta/sketch.rs 
b/datasketches/src/thetafamily/theta/sketch.rs
index b75d1af..4ec71b7 100644
--- a/datasketches/src/thetafamily/theta/sketch.rs
+++ b/datasketches/src/thetafamily/theta/sketch.rs
@@ -427,9 +427,9 @@ impl ThetaSketch {
         .expect("theta should always be valid")
     }
 
-    /// Returns the estimated size of the sketch in bytes.
+    /// Returns an estimate of the sketch's memory usage in bytes.
     pub fn estimated_size(&self) -> usize {
-        size_of::<Self>() + self.table.estimated_size()
+        self.table.estimated_size()
     }
 }
 
@@ -1027,9 +1027,9 @@ impl CompactThetaSketch {
         Ok(Self::from_compact_state(compact_state))
     }
 
-    /// Returns the estimated size of the sketch in bytes.
+    /// Returns an estimate of the sketch's memory usage in bytes.
     pub fn estimated_size(&self) -> usize {
-        size_of::<Self>() + self.compact_state.retained_entries_capacity() * 
size_of::<u64>()
+        self.compact_state.retained_entries_capacity() * size_of::<u64>()
     }
 }
 
diff --git a/datasketches/src/thetafamily/theta/union.rs 
b/datasketches/src/thetafamily/theta/union.rs
index c315c4f..1b26d7a 100644
--- a/datasketches/src/thetafamily/theta/union.rs
+++ b/datasketches/src/thetafamily/theta/union.rs
@@ -64,9 +64,9 @@ impl ThetaUnion {
         self.state.reset();
     }
 
-    /// Returns the estimated size of the union in bytes.
+    /// Returns an estimate of the union's memory usage in bytes.
     pub fn estimated_size(&self) -> usize {
-        size_of::<Self>() + self.state.estimated_size()
+        self.state.estimated_size()
     }
 }
 
diff --git a/datasketches/src/thetafamily/tuple/intersection.rs 
b/datasketches/src/thetafamily/tuple/intersection.rs
index 38dfe9e..58dce8f 100644
--- a/datasketches/src/thetafamily/tuple/intersection.rs
+++ b/datasketches/src/thetafamily/tuple/intersection.rs
@@ -138,9 +138,9 @@ where
         self.state.has_result()
     }
 
-    /// Returns the estimated size of the intersection in bytes.
+    /// Returns an estimate of the intersection's memory usage in bytes.
     pub fn estimated_size(&self) -> usize {
-        size_of::<Self>() + self.state.estimated_size()
+        self.state.estimated_size()
     }
 
     /// Returns the current intersection as a compact Tuple sketch.
diff --git a/datasketches/src/thetafamily/tuple/sketch.rs 
b/datasketches/src/thetafamily/tuple/sketch.rs
index e3a690f..0fee821 100644
--- a/datasketches/src/thetafamily/tuple/sketch.rs
+++ b/datasketches/src/thetafamily/tuple/sketch.rs
@@ -404,9 +404,9 @@ where
         .expect("theta should always be valid")
     }
 
-    /// Returns the estimated size of the sketch in bytes.
+    /// Returns an estimate of the sketch's memory usage in bytes.
     pub fn estimated_size(&self) -> usize {
-        size_of::<Self>() + self.table.estimated_size()
+        size_of::<P>() + self.table.estimated_size()
     }
 }
 
@@ -541,10 +541,9 @@ impl<S> CompactTupleSketch<S> {
         .expect("compact theta should always be valid")
     }
 
-    /// Returns the estimated size of the sketch in bytes.
+    /// Returns an estimate of the sketch's memory usage in bytes.
     pub fn estimated_size(&self) -> usize {
-        size_of::<Self>()
-            + self.compact_state.retained_entries_capacity() * 
size_of::<TupleEntry<S>>()
+        self.compact_state.retained_entries_capacity() * 
size_of::<TupleEntry<S>>()
     }
 
     fn preamble_longs(&self) -> u8 {
diff --git a/datasketches/src/thetafamily/tuple/union.rs 
b/datasketches/src/thetafamily/tuple/union.rs
index df7786b..029aeba 100644
--- a/datasketches/src/thetafamily/tuple/union.rs
+++ b/datasketches/src/thetafamily/tuple/union.rs
@@ -112,9 +112,9 @@ where
         self.state.reset();
     }
 
-    /// Returns the estimated size of the union in bytes.
+    /// Returns an estimate of the union's memory usage in bytes.
     pub fn estimated_size(&self) -> usize {
-        size_of::<Self>() + self.state.estimated_size()
+        self.state.estimated_size()
     }
 }
 
diff --git a/tests-integration/tests/bloom_test/sketch.rs 
b/tests-integration/tests/bloom_test/sketch.rs
index db0a0db..7d2b7df 100644
--- a/tests-integration/tests/bloom_test/sketch.rs
+++ b/tests-integration/tests/bloom_test/sketch.rs
@@ -211,6 +211,12 @@ fn test_requested_size_rounds_to_word_boundary() {
     assert_eq!(filter.capacity(), 128);
 }
 
+#[test]
+fn test_estimated_size() {
+    let filter = BloomFilterBuilder::with_size(65, 3).build().unwrap();
+    assert_eq!(filter.estimated_size(), 16);
+}
+
 #[test]
 fn test_accuracy_builder_rejects_zero_items_at_build() {
     let error = BloomFilterBuilder::with_accuracy(0, 0.01)
diff --git a/tests-integration/tests/countmin_test/sketch.rs 
b/tests-integration/tests/countmin_test/sketch.rs
index 9edae10..b85a887 100644
--- a/tests-integration/tests/countmin_test/sketch.rs
+++ b/tests-integration/tests/countmin_test/sketch.rs
@@ -445,11 +445,11 @@ fn test_increment_single_key_like_rust_count_min_sketch() 
{
 #[test]
 fn test_estimated_size() {
     let mut sketch = CountMinSketch::<i64>::new(4, 128).unwrap();
-    assert_eq!(sketch.estimated_size(), 4240);
+    assert_eq!(sketch.estimated_size(), 4128);
 
     // The backing tables are allocated up front; updates do not grow the 
sketch.
     sketch.update("apple");
-    assert_eq!(sketch.estimated_size(), 4240);
+    assert_eq!(sketch.estimated_size(), 4128);
 }
 
 #[test]
diff --git a/tests-integration/tests/cpc_test/union.rs 
b/tests-integration/tests/cpc_test/union.rs
index 7b28034..d72a7d4 100644
--- a/tests-integration/tests/cpc_test/union.rs
+++ b/tests-integration/tests/cpc_test/union.rs
@@ -183,12 +183,12 @@ fn test_lg_k_too_large_returns_error() {
 #[test]
 fn test_union_estimated_size() {
     let mut union = CpcUnion::new(11).unwrap();
-    assert_eq!(union.estimated_size(), 112);
+    assert_eq!(union.estimated_size(), 0);
 
     let mut sketch = CpcSketch::new(11).unwrap();
     for i in 0..1000 {
         sketch.update(i);
     }
     union.update(&sketch).unwrap();
-    assert_eq!(union.estimated_size(), 16496);
+    assert_eq!(union.estimated_size(), 16384);
 }
diff --git a/tests-integration/tests/cpc_test/update.rs 
b/tests-integration/tests/cpc_test/update.rs
index 99a1cb5..8aec8df 100644
--- a/tests-integration/tests/cpc_test/update.rs
+++ b/tests-integration/tests/cpc_test/update.rs
@@ -67,3 +67,15 @@ fn test_max_serialized_bytes_validates_lg_k() {
         assert_eq!(error.kind(), ErrorKind::InvalidArgument);
     }
 }
+
+#[test]
+fn test_estimated_size() {
+    let mut sketch = CpcSketch::new(11).unwrap();
+    let empty_size = sketch.estimated_size();
+    assert_eq!(empty_size, 0);
+
+    for i in 0..10_000 {
+        sketch.update(i);
+    }
+    assert_eq!(sketch.estimated_size(), 2304);
+}
diff --git a/tests-integration/tests/frequencies_test/update.rs 
b/tests-integration/tests/frequencies_test/update.rs
index 06489d3..ca357d6 100644
--- a/tests-integration/tests/frequencies_test/update.rs
+++ b/tests-integration/tests/frequencies_test/update.rs
@@ -634,11 +634,27 @@ fn 
test_map_size_above_cross_language_limit_returns_error() {
 #[test]
 fn test_estimated_size() {
     let mut sketch: FrequentItemsSketch<i64> = 
FrequentItemsSketch::new(64).unwrap();
-    assert_eq!(sketch.estimated_size(), 344);
+    assert_eq!(sketch.estimated_size(), 208);
 
     // The internal map grows from its starting size up to the maximum size.
     for i in 0..100 {
         sketch.update(i);
     }
-    assert_eq!(sketch.estimated_size(), 1800);
+    assert_eq!(sketch.estimated_size(), 1664);
+}
+
+#[test]
+fn estimated_size_is_shallow_for_generic_items() {
+    let mut small_item_sketch = FrequentItemsSketch::new(8).unwrap();
+    small_item_sketch.update("key".to_string());
+
+    let mut large_item = String::with_capacity(4096);
+    large_item.push_str("key");
+    let mut large_item_sketch = FrequentItemsSketch::new(8).unwrap();
+    large_item_sketch.update(large_item);
+
+    assert_eq!(
+        small_item_sketch.estimated_size(),
+        large_item_sketch.estimated_size()
+    );
 }
diff --git a/tests-integration/tests/hll_test/union.rs 
b/tests-integration/tests/hll_test/union.rs
index 7a6409a..c61a171 100644
--- a/tests-integration/tests/hll_test/union.rs
+++ b/tests-integration/tests/hll_test/union.rs
@@ -710,14 +710,14 @@ fn test_union_validation() {
 #[test]
 fn test_union_estimated_size() {
     let mut union = HllUnion::new(10).unwrap();
-    assert_eq!(union.estimated_size(), 128);
+    assert_eq!(union.estimated_size(), 32);
 
     let mut sketch = HllSketch::new(10, HllType::Hll8).unwrap();
     for i in 0..1000 {
         sketch.update(i);
     }
     union.update(&sketch);
-    assert_eq!(union.estimated_size(), 1120);
+    assert_eq!(union.estimated_size(), 1024);
 }
 
 #[test]
diff --git a/tests-integration/tests/hll_test/update.rs 
b/tests-integration/tests/hll_test/update.rs
index 50fd0b4..5ec6ac8 100644
--- a/tests-integration/tests/hll_test/update.rs
+++ b/tests-integration/tests/hll_test/update.rs
@@ -201,3 +201,18 @@ fn test_invalid_lg_k_high_returns_error() {
     let error = HllSketch::new(22, HllType::Hll8).unwrap_err();
     assert_eq!(error.kind(), ErrorKind::InvalidArgument);
 }
+
+#[test]
+fn estimated_size_tracks_retained_storage() {
+    let mut sketch = HllSketch::new(10, HllType::Hll8).unwrap();
+    let initial_size = sketch.estimated_size();
+    assert_eq!(initial_size, 32);
+
+    sketch.update(0);
+    assert_eq!(sketch.estimated_size(), initial_size);
+
+    for i in 1..1000 {
+        sketch.update(i);
+    }
+    assert_eq!(sketch.estimated_size(), 1024);
+}
diff --git a/tests-integration/tests/tdigest_test/sketch.rs 
b/tests-integration/tests/tdigest_test/sketch.rs
index 86bc45f..4eba82d 100644
--- a/tests-integration/tests/tdigest_test/sketch.rs
+++ b/tests-integration/tests/tdigest_test/sketch.rs
@@ -15,7 +15,6 @@
 // specific language governing permissions and limitations
 // under the License.
 
-use std::mem::size_of;
 use std::panic::AssertUnwindSafe;
 use std::panic::catch_unwind;
 
@@ -119,15 +118,14 @@ fn test_estimated_size_reuses_buffer_after_compression() {
     const TARGET_CENTROIDS: usize = 410;
     const MAX_UNMERGED: usize = TARGET_CENTROIDS * 4;
 
-    let inline_size = size_of::<TDigestMut>();
     let mut tdigest = TDigestMut::new(K).unwrap();
-    assert_eq!(tdigest.estimated_size(), inline_size);
+    assert_eq!(tdigest.estimated_size(), 0);
 
     for value in 0..MAX_UNMERGED {
         tdigest.update(value as f64);
     }
     let size_before_compression = tdigest.estimated_size();
-    assert!(size_before_compression > inline_size);
+    assert!(size_before_compression > 0);
     tdigest.rank(0.5).unwrap();
     assert!(tdigest.estimated_size() <= size_before_compression);
 
diff --git a/tests-integration/tests/theta_test/intersection.rs 
b/tests-integration/tests/theta_test/intersection.rs
index 87f545f..f6068a1 100644
--- a/tests-integration/tests/theta_test/intersection.rs
+++ b/tests-integration/tests/theta_test/intersection.rs
@@ -326,9 +326,9 @@ fn test_seed_mismatch_non_empty_returns_error() {
 #[test]
 fn test_intersection_estimated_size() {
     let mut intersection = ThetaIntersection::default();
-    assert_eq!(intersection.estimated_size(), 72);
+    assert_eq!(intersection.estimated_size(), 0);
 
     let sketch = sketch_with_range(0, 1000);
     intersection.update(&sketch).unwrap();
-    assert_eq!(intersection.estimated_size(), 16456);
+    assert_eq!(intersection.estimated_size(), 16384);
 }
diff --git a/tests-integration/tests/theta_test/sketch.rs 
b/tests-integration/tests/theta_test/sketch.rs
index b900ab1..cd43f36 100644
--- a/tests-integration/tests/theta_test/sketch.rs
+++ b/tests-integration/tests/theta_test/sketch.rs
@@ -157,6 +157,7 @@ fn test_reset() {
     assert_that!(sketch.num_retained(), gt(32));
     assert_that!(sketch.theta(), lt(1.0));
 
+    assert_eq!(sketch.estimated_size(), 512);
     sketch.reset();
     assert!(sketch.is_empty());
     assert_eq!(sketch.estimate(), 0.0);
@@ -165,6 +166,7 @@ fn test_reset() {
     assert!(!sketch.is_estimation_mode());
     assert_eq!(sketch.lower_bound(NumStdDev::One), 0.0);
     assert_eq!(sketch.upper_bound(NumStdDev::One), 0.0);
+    assert_eq!(sketch.estimated_size(), 512);
 }
 
 #[test]
diff --git a/tests-integration/tests/theta_test/union.rs 
b/tests-integration/tests/theta_test/union.rs
index 14df5bf..3935b82 100644
--- a/tests-integration/tests/theta_test/union.rs
+++ b/tests-integration/tests/theta_test/union.rs
@@ -734,9 +734,20 @@ fn test_corner_case_union_states() {
 #[test]
 fn test_union_estimated_size() {
     let mut union = ThetaUnionBuilder::default().build().unwrap();
-    assert_eq!(union.estimated_size(), 1104);
+    assert_eq!(union.estimated_size(), 1024);
 
     let sketch = sketch_with_range(12, 0, 1000);
     union.update(&sketch).unwrap();
-    assert_eq!(union.estimated_size(), 65616);
+    assert_eq!(union.estimated_size(), 65536);
+}
+
+#[test]
+fn compact_estimated_size_includes_retained_capacity() {
+    let sketch = sketch_with_range(5, 0, 1000);
+    let mut union = ThetaUnionBuilder::default().lg_k(5).build().unwrap();
+    union.update(&sketch).unwrap();
+    let result = union.to_sketch(true);
+
+    assert_eq!(result.num_retained(), 32);
+    assert_eq!(result.estimated_size(), 512);
 }
diff --git a/tests-integration/tests/tuple_test/intersection.rs 
b/tests-integration/tests/tuple_test/intersection.rs
index 9caa0eb..d01381a 100644
--- a/tests-integration/tests/tuple_test/intersection.rs
+++ b/tests-integration/tests/tuple_test/intersection.rs
@@ -185,9 +185,9 @@ fn estimation_bounds_cover_the_true_intersection() {
 #[test]
 fn intersection_estimated_size_grows_with_updates() {
     let mut intersection = TupleIntersection::new(SumPolicy);
-    assert_eq!(intersection.estimated_size(), 72);
+    assert_eq!(intersection.estimated_size(), 0);
 
     let sketch = tuple_sketch_with_range(0, 1000);
     intersection.update(&sketch).unwrap();
-    assert_eq!(intersection.estimated_size(), 32840);
+    assert_eq!(intersection.estimated_size(), 32768);
 }
diff --git a/tests-integration/tests/tuple_test/sketch.rs 
b/tests-integration/tests/tuple_test/sketch.rs
index 194060d..694644e 100644
--- a/tests-integration/tests/tuple_test/sketch.rs
+++ b/tests-integration/tests/tuple_test/sketch.rs
@@ -131,6 +131,67 @@ fn 
custom_update_policy_accepts_multiple_value_representations() {
     );
 }
 
+#[test]
+fn estimated_size_is_shallow_for_generic_summaries() {
+    let mut small_summary_sketch = TupleSketchBuilder::new(ArraySumPolicy { 
num_values: 1 })
+        .build()
+        .unwrap();
+    small_summary_sketch.update("key", [1.0]);
+
+    let mut large_summary_sketch = TupleSketchBuilder::new(ArraySumPolicy { 
num_values: 1024 })
+        .build()
+        .unwrap();
+    large_summary_sketch.update("key", vec![1.0; 1024]);
+
+    assert_eq!(
+        small_summary_sketch.estimated_size(),
+        large_summary_sketch.estimated_size()
+    );
+    assert_eq!(
+        small_summary_sketch.compact(false).estimated_size(),
+        large_summary_sketch.compact(false).estimated_size()
+    );
+}
+
+struct PaddedArraySumPolicy {
+    inner: ArraySumPolicy,
+    _padding: [u8; 1024],
+}
+
+impl SummaryPolicy for PaddedArraySumPolicy {
+    type Summary = Vec<f64>;
+
+    fn create(&self) -> Self::Summary {
+        self.inner.create()
+    }
+}
+
+impl<U> SummaryUpdatePolicy<U> for PaddedArraySumPolicy
+where
+    U: AsRef<[f64]>,
+{
+    fn update(&self, summary: &mut Self::Summary, value: U) {
+        self.inner.update(summary, value);
+    }
+}
+
+#[test]
+fn estimated_size_includes_policy_inline_storage() {
+    let mut small_policy_sketch = TupleSketchBuilder::new(ArraySumPolicy { 
num_values: 1 })
+        .build()
+        .unwrap();
+    small_policy_sketch.update("key", [1.0]);
+
+    let policy = PaddedArraySumPolicy {
+        inner: ArraySumPolicy { num_values: 1 },
+        _padding: [0; 1024],
+    };
+    let mut large_policy_sketch = 
TupleSketchBuilder::new(policy).build().unwrap();
+    large_policy_sketch.update("key", [1.0]);
+
+    assert!(large_policy_sketch.estimated_size() > 
small_policy_sketch.estimated_size());
+}
+
 #[test]
 fn trim_and_reset_update_public_state() {
     let mut sketch = default_tuple_sketch_builder().lg_k(5).build().unwrap();
diff --git a/tests-integration/tests/tuple_test/union.rs 
b/tests-integration/tests/tuple_test/union.rs
index 90b68b0..6c23c10 100644
--- a/tests-integration/tests/tuple_test/union.rs
+++ b/tests-integration/tests/tuple_test/union.rs
@@ -186,9 +186,20 @@ fn estimation_bounds_cover_the_true_union() {
 #[test]
 fn union_estimated_size_grows_with_updates() {
     let mut union = default_union_builder().build().unwrap();
-    assert_eq!(union.estimated_size(), 2128);
+    assert_eq!(union.estimated_size(), 2048);
 
     let sketch = tuple_sketch_with_range(0, 1000);
     union.update(&sketch).unwrap();
-    assert_eq!(union.estimated_size(), 131152);
+    assert_eq!(union.estimated_size(), 131072);
+}
+
+#[test]
+fn compact_estimated_size_includes_retained_capacity() {
+    let sketch = tuple_sketch_with_range(0, 1000);
+    let mut union = default_union_builder().lg_k(5).build().unwrap();
+    union.update(&sketch).unwrap();
+    let result = union.to_sketch(true);
+
+    assert_eq!(result.num_retained(), 32);
+    assert_eq!(result.estimated_size(), 1024);
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to