@@ -20,6 +20,7 @@ use std::hash::Hash;
2020use crate :: common:: ResizeFactor ;
2121use crate :: hash:: MurmurHash3X64128 ;
2222use crate :: hash:: compute_seed_hash;
23+ use crate :: thetacommon:: RawCompactParts ;
2324use crate :: thetacommon:: RawHashTableEntry ;
2425use crate :: thetacommon:: constants:: HASH_TABLE_REBUILD_THRESHOLD ;
2526use crate :: thetacommon:: constants:: HASH_TABLE_RESIZE_THRESHOLD ;
@@ -31,6 +32,12 @@ use crate::thetacommon::constants::STRIDE_MASK;
3132///
3233/// The entry type supplies the retained hash and any sketch-specific payload. The table owns all
3334/// theta screening, probing, resizing, rebuilding, trimming, and logical-empty state.
35+ ///
36+ /// It maintains an array with capacity up to 2^lg_max_size:
37+ /// * Before it reaches the max capacity, it will extend the array based on resize_factor.
38+ /// * After it reaches the capacity bigger than 2^lg_nom_size, every time the number of entries
39+ /// exceeds the threshold, it will rebuild the table: only keep the min 2^lg_nom_size entries and
40+ /// update the theta to the k-th smallest entry.
3441#[ derive( Debug ) ]
3542pub struct RawHashTable < E > {
3643 lg_cur_size : u8 ,
@@ -125,6 +132,16 @@ where
125132
126133 /// Inserts or updates the entry slot for a pre-hashed key.
127134 ///
135+ /// The callback `f` is invoked with the current entry for `hash`:
136+ /// * `Some(existing)` if the key is already retained. The callback should modify it in place;
137+ /// its return value is ignored.
138+ /// * `None` if the key is new. The callback returns `Some(entry)` to insert it, or `None` to
139+ /// decline insertion.
140+ ///
141+ /// Using a single callback ensures any captured update value is consumed exactly once, so it
142+ /// works for both the update sketch (folding an update value) and set operations (merging an
143+ /// incoming entry) without requiring the value to be `Copy` or `Clone`.
144+ ///
128145 /// Returns true if a new entry was created, false otherwise (existing key, declined insertion,
129146 /// or a hash screened out by theta).
130147 pub fn upsert_entry < F > ( & mut self , hash : u64 , f : F ) -> bool
@@ -168,6 +185,7 @@ where
168185 }
169186
170187 /// Returns a reference to the entry stored for `hash`, or `None` if the hash is not retained.
188+ #[ allow( dead_code) ] // used by the set operations; tuple set operations arrive in follow-ups
171189 pub fn get_entry ( & self , hash : u64 ) -> Option < & E > {
172190 if hash == 0 {
173191 return None ;
@@ -234,6 +252,33 @@ where
234252 self . entries . iter ( ) . filter_map ( Option :: as_ref)
235253 }
236254
255+ /// Returns the retained entries and theta as raw compact-sketch parts.
256+ ///
257+ /// An empty table reports `MAX_THETA` rather than its current theta, matching Java's
258+ /// `correctThetaOnCompact()` behavior for never-updated sketches initialized with p < 1.0.
259+ /// Empty and single-entry exact-mode results are always marked ordered (Java/C++
260+ /// compatibility).
261+ pub fn to_compact_parts ( & self , ordered : bool ) -> RawCompactParts < E >
262+ where
263+ E : Clone ,
264+ {
265+ let mut entries: Vec < E > = self . iter_entries ( ) . cloned ( ) . collect ( ) ;
266+ let empty = self . is_empty ( ) ;
267+ let theta = if empty { MAX_THETA } else { self . theta ( ) } ;
268+ let is_single = entries. len ( ) == 1 && theta == MAX_THETA ;
269+ let ordered = ordered || empty || is_single;
270+ if ordered && entries. len ( ) > 1 {
271+ entries. sort_unstable_by_key ( RawHashTableEntry :: hash) ;
272+ }
273+ RawCompactParts {
274+ entries,
275+ theta,
276+ seed_hash : self . seed_hash ( ) ,
277+ ordered,
278+ empty,
279+ }
280+ }
281+
237282 /// Return number of all entries.
238283 #[ cfg( test) ]
239284 pub fn num_entries ( & self ) -> usize {
@@ -257,16 +302,19 @@ where
257302 }
258303
259304 /// Set empty flag.
305+ #[ allow( dead_code) ] // used by the set operations; tuple set operations arrive in follow-ups
260306 pub fn set_empty ( & mut self , is_empty : bool ) {
261307 self . is_empty = is_empty;
262308 }
263309
264310 /// Get the hash seed used by this table.
311+ #[ allow( dead_code) ] // used by the set operations; tuple set operations arrive in follow-ups
265312 pub fn hash_seed ( & self ) -> u64 {
266313 self . hash_seed
267314 }
268315
269316 /// Sets theta value.
317+ #[ allow( dead_code) ] // used by the set operations and tests; tuple set operations arrive in follow-ups
270318 pub fn set_theta ( & mut self , theta : u64 ) {
271319 assert ! (
272320 ( 1 ..=MAX_THETA ) . contains( & theta) ,
@@ -276,6 +324,7 @@ where
276324 }
277325
278326 /// Returns minimal lg_size where rebuild-capacity can hold `count`.
327+ #[ allow( dead_code) ] // used by the set operations; tuple set operations arrive in follow-ups
279328 pub fn lg_size_from_count_for_rebuild ( count : usize , load_factor : f64 ) -> u8 {
280329 let log2 = |n : usize | {
281330 if n == 0 { 0_u8 } else { n. ilog2 ( ) as u8 }
0 commit comments