Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions datasketches/src/bloom/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,18 @@
//! [`difference()`](BloomFilter::difference) excludes the right filter's items exactly, but
//! may also drop left items whose hash positions collide with the right filter.
//!
//! # Cross-language hashing
//!
//! For cross-language use, apply the following input conventions:
//!
//! * Use `i64`/`u64` for 64-bit integers and [`natural_extend`](crate::hash::value::natural_extend)
//! for narrower integers.
//! * Use [`canonical_float`](crate::hash::value::canonical_float) for floating-point values.
//! * Use [`raw_bytes`](crate::hash::value::raw_bytes) for UTF-8 strings and byte slices.
//!
//! Skip empty strings and byte slices before inserting into the filter. For these inputs,
//! return `false` without querying the filter, including for queries combined with insertion.
//!
//! # Usage
//!
//! ```
Expand Down
2 changes: 2 additions & 0 deletions datasketches/src/bloom/sketch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ const EMPTY_FLAG_MASK: u8 = 1 << 2;
/// * No false negatives (inserted items always return `true`)
/// * Tunable false positive rate
/// * Constant space usage
///
/// See the [module level documentation](super) for more.
#[derive(Debug, Clone, PartialEq)]
pub struct BloomFilter {
/// Hash seed for all hash functions
Expand Down
64 changes: 52 additions & 12 deletions datasketches/src/countmin/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,30 +20,70 @@
//! The CountMin sketch provides approximate frequency counts for streaming data
//! with configurable relative error and confidence bounds.
//!
//! Serial version 2 sketches with `i64` counters support cross-language binary interchange when
//! items use compatible hashing; see [`CountMinSketch`] and [`hash::value`](crate::hash::value).
//! # Cross-language hashing
//!
//! For cross-language use, apply the following input conventions:
//!
//! * Use `i64`/`u64` for 64-bit integers and [`natural_extend`](crate::hash::value::natural_extend)
//! for narrower integers.
//! * Use [`canonical_float`](crate::hash::value::canonical_float) for floating-point values.
//! * Use [`raw_bytes`](crate::hash::value::raw_bytes) for UTF-8 strings and byte slices.
//!
//! Use `i64` counters for cross-language interchange. Skip empty strings and byte slices before
//! updating the sketch. For these inputs, return zero for the estimate and both bounds without
//! querying the sketch.
//!
//! # Usage
//!
//! Choose the number of buckets and hash functions from the desired relative error and
//! confidence, then update the sketch as items arrive:
//!
//! ```
//! use datasketches::countmin::CountMinSketch;
//! use datasketches::hash::value::raw_bytes;
//!
//! let mut sketch = CountMinSketch::<i64>::new(5, 256).unwrap();
//! sketch.update(raw_bytes::from_str("apple"));
//! sketch.update_with_weight(raw_bytes::from_str("banana"), 3);
//! assert!(sketch.estimate(raw_bytes::from_str("banana")) >= 3);
//! let buckets = CountMinSketch::<i64>::suggest_num_buckets(0.01)?;
//! let hashes = CountMinSketch::<i64>::suggest_num_hashes(0.99)?;
//! let mut sketch = CountMinSketch::<i64>::new(hashes, buckets)?;
//! sketch.update(42i64);
//! sketch.update_with_weight(42i64, 5);
//!
//! assert_eq!(sketch.estimate(42i64), 6);
//! assert!(sketch.lower_bound(42i64) <= 6);
//! assert!(sketch.upper_bound(42i64) >= 6);
//! # Ok::<(), datasketches::error::Error>(())
//! ```
//!
//! # Configuration Helpers
//! ## Empty inputs
//!
//! This example handles empty strings before updating or querying. Byte slices use the same
//! `is_empty()` checks with `raw_bytes::from_slice`.
//!
//! ```
//! use datasketches::countmin::CountMinSketch;
//! use datasketches::hash::value::raw_bytes;
//!
//! let mut sketch = CountMinSketch::<i64>::new(3, 1000)?;
//! for input in ["", "apple", ""] {
//! let item = raw_bytes::from_str(input);
//! if !item.is_empty() {
//! sketch.update_with_weight(item, 5);
//! }
//! }
//! assert_eq!(sketch.total_weight(), 5);
//! assert_eq!(sketch.estimate(raw_bytes::from_str("apple")), 5);
//!
//! let buckets = CountMinSketch::<i64>::suggest_num_buckets(0.01).unwrap();
//! let hashes = CountMinSketch::<i64>::suggest_num_hashes(0.99).unwrap();
//! let sketch = CountMinSketch::<i64>::new(hashes, buckets).unwrap();
//! assert_eq!(sketch.estimate(1i64), 0);
//! let item = raw_bytes::from_str("");
//! let (estimate, lower, upper) = if item.is_empty() {
//! (0, 0, 0)
//! } else {
//! (
//! sketch.estimate(item),
//! sketch.lower_bound(item),
//! sketch.upper_bound(item),
//! )
//! };
//! assert_eq!((estimate, lower, upper), (0, 0, 0));
//! # Ok::<(), datasketches::error::Error>(())
//! ```

mod sketch;
Expand Down
39 changes: 12 additions & 27 deletions datasketches/src/countmin/sketch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,7 @@ const MAX_TABLE_ENTRIES: usize = 1 << 30;
/// The sketch provides upper and lower bounds on estimated item frequencies
/// with configurable relative error and confidence.
///
/// New sketches use serial version 2. Deserialized version 1 sketches retain their legacy
/// hashing and serialization format, and sketches of different versions cannot be merged.
///
/// For cross-language version 2 interchange, use `i64` counters and encode inputs consistently:
/// * `i64` and `u64` values are hashed as eight little-endian bytes; use
/// [`crate::hash::value::natural_extend`] for narrower integers.
/// * Use [`crate::hash::value::raw_bytes`] for UTF-8 strings and byte slices, and skip empty inputs
/// when interoperating with implementations that ignore them.
/// See the [module level documentation](super) for more.
#[derive(Debug, Clone, PartialEq)]
pub struct CountMinSketch<T: CountMinValue> {
num_hashes: u8,
Expand Down Expand Up @@ -320,9 +313,6 @@ impl<T: CountMinValue> CountMinSketch<T> {

/// Updates the sketch with a single occurrence of the item.
///
/// Use [`hash::value`](crate::hash::value) wrappers to select an input encoding for
/// cross-language hashing.
///
/// # Panics
///
/// Panics without modifying the sketch if the total absolute weight would exceed `T::MAX`.
Expand All @@ -342,9 +332,6 @@ impl<T: CountMinValue> CountMinSketch<T> {

/// Updates the sketch with the given item and weight.
///
/// Use [`hash::value`](crate::hash::value) wrappers to select an input encoding for
/// cross-language hashing.
///
/// # Panics
///
/// Panics without modifying the sketch if the absolute weight or the total absolute weight
Expand Down Expand Up @@ -452,11 +439,7 @@ impl<T: CountMinValue> CountMinSketch<T> {

/// Serializes this sketch into the Count-Min binary format.
///
/// New sketches are written as version 2. A sketch deserialized from a version 1 image
/// retains its legacy hashing for queries and updates and is written back as version 1.
///
/// Version 1 images are not portable across implementations. Version 2 supports cross-language
/// interchange with `i64` counters and the input encodings documented on [`CountMinSketch`].
/// A sketch restored by [`deserialize`](Self::deserialize) retains its original format.
///
/// # Examples
///
Expand Down Expand Up @@ -507,13 +490,15 @@ impl<T: CountMinValue> CountMinSketch<T> {

/// Deserializes a sketch from bytes using the default seed.
///
/// Accepts versions 1 and 2, preserving the image's hashing and serialization version.
/// Counter values must fit `T`. See [`CountMinSketch`] for input encodings and compatibility.
/// Accepts the current format and legacy version 1 images. Legacy images retain their original
/// hashing and serialization format and cannot be merged with newly created sketches. Use the
/// same input encodings that were used to create the image; version 1 images are not portable
/// across implementations. Counter values must fit `T`.
///
/// # Errors
///
/// Returns `InvalidData` if the image is malformed or its seed hash does not match the default
/// seed.
/// Returns `InvalidData` if the image is malformed, its serialization version is unsupported,
/// or its seed hash does not match the default seed.
///
/// # Examples
///
Expand All @@ -532,13 +517,13 @@ impl<T: CountMinValue> CountMinSketch<T> {

/// Deserializes a sketch from bytes using the provided seed.
///
/// Accepts versions 1 and 2, preserving the image's hashing and serialization version.
/// Counter values must fit `T`. See [`CountMinSketch`] for input encodings and compatibility.
/// The format and counter requirements are the same as for [`deserialize`](Self::deserialize).
///
/// # Errors
///
/// Returns `InvalidData` if the image is malformed, its seed hash does not match `seed`, or
/// `seed` itself computes to the reserved zero seed hash.
/// Returns `InvalidData` if the image is malformed, its serialization version is unsupported,
/// its seed hash does not match `seed`, or `seed` itself computes to the reserved zero seed
/// hash.
///
/// # Examples
///
Expand Down
11 changes: 11 additions & 0 deletions datasketches/src/cpc/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,17 @@
//! configurations of K.
//!
//! For additional security this sketch can be configured with a user-specified hash seed.
//!
//! # Cross-language hashing
//!
//! For cross-language use, apply the following input conventions:
//!
//! * Use `i64`/`u64` for 64-bit integers and [`sign_extend`](crate::hash::value::sign_extend) for
//! narrower integers.
//! * Use [`canonical_float`](crate::hash::value::canonical_float) for floating-point values.
//! * Use [`raw_bytes`](crate::hash::value::raw_bytes) for UTF-8 strings and byte slices.
//!
//! Skip empty strings and byte slices before updating the sketch.

mod compression;
mod compression_data;
Expand Down
3 changes: 0 additions & 3 deletions datasketches/src/cpc/sketch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,9 +179,6 @@ impl CpcSketch {

/// Updates the sketch with a hashable value.
///
/// Use [`hash::value`](crate::hash::value) wrappers to select an input encoding for
/// cross-language hashing.
///
/// # Examples
///
/// ```rust
Expand Down
2 changes: 0 additions & 2 deletions datasketches/src/hash/value/canonical_float.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@
//! [`CanonicalFloat`] maps `f32` and `f64` values through a canonical `f64` bit pattern before
//! hashing. Signed zero values hash the same, all NaN values use one canonical NaN bit pattern,
//! and equal `f32`/`f64` values hash the same.
//!
//! Use this encoding to match floating-point hashing in DataSketches Java and DataSketches C++.

use std::hash::Hash;
use std::hash::Hasher;
Expand Down
60 changes: 11 additions & 49 deletions datasketches/src/hash/value/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,58 +19,20 @@
//!
//! Sketch update APIs accept any value that implements [`Hash`]. For most Rust values,
//! passing the value directly is sufficient. This module provides value wrappers for
//! cases where the default implementation does not match a sketch's compatibility rules.
//! cases where the default implementation does not match the required input encoding.
//!
//! ## Floating-point Numbers
//! ## Encoding strategies
//!
//! [`canonical_float::CanonicalFloat`] maps `f32` and `f64` values through a canonical `f64` bit
//! pattern before hashing. Signed zero values hash the same, all NaN values use one canonical NaN
//! bit pattern, and equal `f32`/`f64` values hash the same.
//! * [`canonical_float`] widens floating-point values to `f64`, normalizes signed zero to `+0.0`,
//! and maps all NaNs to one bit pattern before hashing.
//! * [`sign_extend`] interprets integers as signed at their original width and sign-extends them to
//! 64 bits before hashing.
//! * [`natural_extend`] widens signed integers to `i64` and unsigned integers to `u64` before
//! hashing.
//! * [`raw_bytes`] hashes byte slices as their contents and strings as UTF-8 bytes, without length
//! prefixes or terminators.
//!
//! Use this encoding to match floating-point hashing in DataSketches Java and DataSketches C++.
//!
//! The concrete wrapper documentation provides more details and examples.
//!
//! * [`canonical_float::from_f32`]
//! * [`canonical_float::from_f64`]
//!
//! ## Integers
//!
//! [`sign_extend::SignExtend`] first sign-extends values to 64-bits, and then hashes the resulting
//! integers. Use this encoding to match short-integer hashing in DataSketches C++'s HLL and CPC
//! sketches.
//!
//! The concrete wrapper documentation provides more details and examples.
//!
//! * [`sign_extend::from_i8`], [`sign_extend::from_u8`]
//! * [`sign_extend::from_i16`], [`sign_extend::from_u16`]
//! * [`sign_extend::from_i32`], [`sign_extend::from_u32`]
//!
//! [`natural_extend::NaturalExtend`] first widens signed values to `i64` and unsigned values to
//! `u64`, and then hashes the resulting integers. Use this encoding to match short-integer hashing
//! in DataSketches C++'s Bloom filters and Count-Min sketches.
//!
//! The concrete wrapper documentation provides more details and examples.
//!
//! * [`natural_extend::from_i8`], [`natural_extend::from_u8`]
//! * [`natural_extend::from_i16`], [`natural_extend::from_u16`]
//! * [`natural_extend::from_i32`], [`natural_extend::from_u32`]
//!
//! ## Raw Bytes
//!
//! [`raw_bytes::RawBytes`] hashes byte and string inputs as raw bytes without Rust's slice or
//! string length prefix.
//!
//! Use this encoding to match byte and UTF-8 string hashing in DataSketches Java and
//! DataSketches C++. Their string update APIs skip empty strings; check `is_empty` before updating
//! here as well.
//!
//! The concrete wrapper documentation provides more details and examples.
//!
//! * [`raw_bytes::from_vec`]
//! * [`raw_bytes::from_string`]
//! * [`raw_bytes::from_slice`]
//! * [`raw_bytes::from_str`]
//! Each strategy module provides constructors and examples.

pub mod canonical_float;
pub mod natural_extend;
Expand Down
3 changes: 0 additions & 3 deletions datasketches/src/hash/value/natural_extend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,6 @@
//!
//! [`NaturalExtend`] first widens signed values to `i64` and unsigned values to `u64`, and then
//! hashes the resulting integers.
//!
//! Use this encoding to match short-integer hashing in DataSketches C++'s Bloom filters and
//! Count-Min sketches.

use std::hash::Hash;
use std::hash::Hasher;
Expand Down
12 changes: 4 additions & 8 deletions datasketches/src/hash/value/raw_bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,8 @@

//! Raw byte and string hash value wrappers.
//!
//! [`RawBytes`] hashes byte and string inputs as raw bytes without Rust's slice or string length
//! prefix.
//!
//! Use this encoding to match byte and UTF-8 string hashing in DataSketches Java and
//! DataSketches C++. Their string update APIs skip empty strings; check `is_empty` before updating
//! here as well.
//! [`RawBytes`] hashes byte slices as their contents and strings as UTF-8 bytes, without length
//! prefixes or terminators.

use std::hash::Hasher;

Expand Down Expand Up @@ -62,7 +58,7 @@ pub fn from_vec(v: Vec<u8>) -> RawBytes<Vec<u8>> {

/// Creates a raw-byte hashable value from a string.
///
/// This hashes the UTF-8 bytes of the string without Rust's string length prefix.
/// This hashes the UTF-8 bytes of the string without a length prefix or terminator.
///
/// # Examples
///
Expand Down Expand Up @@ -106,7 +102,7 @@ pub fn from_slice(v: &[u8]) -> RawBytes<&[u8]> {

/// Creates a raw-byte hashable value from a string slice.
///
/// This hashes the UTF-8 bytes of the string slice without Rust's string length prefix.
/// This hashes the UTF-8 bytes of the string slice without a length prefix or terminator.
///
/// # Examples
///
Expand Down
3 changes: 1 addition & 2 deletions datasketches/src/hash/value/sign_extend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@
//! Sign-extended integer hash value wrappers.
//!
//! [`SignExtend`] first sign-extends values to 64-bits and then hashes the resulting integers.
//!
//! Use this encoding to match short-integer hashing in DataSketches C++'s HLL and CPC sketches.
//! Unsigned values are interpreted as signed at their original width before extension.

use std::hash::Hash;
use std::hash::Hasher;
Expand Down
18 changes: 13 additions & 5 deletions datasketches/src/hll/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,17 @@
//! [`HllType`] controls register storage; `lg_k` controls the register count and accuracy.
//!
//! The binary serialization format supports cross-language interchange.
//! Cross-language updates must also use compatible hashing; see
//! [`hash::value`](crate::hash::value).
//!
//! # Cross-language hashing
//!
//! For cross-language use, apply the following input conventions:
//!
//! * Use `i64`/`u64` for 64-bit integers and [`sign_extend`](crate::hash::value::sign_extend) for
//! narrower integers.
//! * Use [`canonical_float`](crate::hash::value::canonical_float) for floating-point values.
//! * Use [`raw_bytes`](crate::hash::value::raw_bytes) for UTF-8 strings and byte slices.
//!
//! Skip empty strings and byte slices before updating the sketch.
//!
//! # Usage
//!
Expand Down Expand Up @@ -113,6 +122,8 @@ const RESIZE_DENOMINATOR: u32 = 4;
/// Compute a coupon once with [`Coupon::from_value`] and pass it to
/// [`HllSketch::update_with_coupon`] on sketches with any `lg_k` or [`HllType`].
///
/// See the [module level documentation](crate::hll) for more.
///
/// # Examples
///
/// ```
Expand Down Expand Up @@ -149,9 +160,6 @@ impl Coupon {

/// Computes the HLL coupon for a hashable value.
///
/// Use [`hash::value`](crate::hash::value) wrappers to select an input encoding for
/// cross-language hashing.
///
/// # Examples
///
/// ```
Expand Down
Loading
Loading