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
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ cargo bench --package benchmarks --bench benchmarks -- cpc::serde
- End summary sentences with punctuation, and format Rust identifiers, literals, and numeric ranges as inline code.
- Put contract sections and compatibility notes before examples. When applicable, order sections as `# Errors`, `# Panics`, and `# Examples`. Include only sections that describe an actual contract.
- Document return values and error conditions on the relevant methods rather than repeating them in crate or module overviews.
- Describe shared capabilities in language-neutral terms. Name another implementation when it clarifies interoperability requirements, a specific source, a format variant, or a behavior difference. Retain useful implementation references in internal comments.
- Keep imports visible in doctests. A trailing hidden `Ok(...)` may be used to support `?`.

## Query semantics
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ The minimum supported Rust version is 1.86.0. The crate currently supports littl

Supported serialization formats are tested with fixtures produced by Apache DataSketches Java, C++, and Go through the [DataSketches TCK](https://github.com/apache/datasketches-tck).

Serialization compatibility does not imply that an ordinary Rust `Hash` implementation produces the same update bytes as another language. When sketches must represent the same inputs across implementations, use `hash::value::{raw_bytes, canonical_float, sign_extend, natural_extend}` (and the constructors within those modules) to match the other language implementations’ hashing rules. Other DataSketches implementations skip empty strings, so skip them before updating when that behavior matters.
Serialization compatibility does not imply that an ordinary Rust `Hash` implementation produces the same update bytes across languages. When sketches must represent the same inputs across implementations, use `hash::value::{raw_bytes, canonical_float, sign_extend, natural_extend}` (and the constructors within those modules) to select matching input encodings. When interoperating with an implementation that skips empty strings, skip them before updating here as well.

See the [changelog](CHANGELOG.md) for release notes and migration guidance.

Expand Down
6 changes: 4 additions & 2 deletions datasketches/src/bloom/sketch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ impl BloomFilter {
/// * Items never inserted into this filter may return `true` (false positives), at a rate no
/// higher than this filter's false positive rate before the operation.
///
/// This is the transformation other DataSketches libraries expose as A NOT B.
/// This operation is also known as A NOT B.
///
/// # Errors
///
Expand Down Expand Up @@ -379,7 +379,7 @@ impl BloomFilter {

/// Serializes the filter to a byte vector.
///
/// The format is compatible with other Apache DataSketches implementations.
/// The serialized data can be read by other DataSketches implementations.
///
/// # Examples
///
Expand Down Expand Up @@ -441,6 +441,8 @@ impl BloomFilter {

/// Deserializes a filter from bytes.
///
/// Also accepts data serialized by other DataSketches implementations.
///
/// # Errors
///
/// Returns an error if:
Expand Down
2 changes: 1 addition & 1 deletion datasketches/src/common/inv_pow2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
// specific language governing permissions and limitations
// under the License.

/// Compute 1 / 2^exp using the same bit construction as DataSketches Java.
/// Returns `2^-exp` by constructing the floating-point exponent bits, as in datasketches-java.
#[inline]
pub fn inv_pow2(exp: u8) -> f64 {
f64::from_bits((1023 - exp as u64) << 52)
Expand Down
13 changes: 12 additions & 1 deletion datasketches/src/countmin/sketch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,10 @@ impl<T: CountMinValue> CountMinSketch<T> {
Ok(())
}

/// Serializes this sketch into the DataSketches CountMin format.
/// Serializes this sketch into the Count-Min binary format.
///
/// DataSketches C++ can read the serialized data, but item queries and updates there are
/// unreliable because the two implementations map items to counters differently.
///
/// # Examples
///
Expand Down Expand Up @@ -357,6 +360,10 @@ impl<T: CountMinValue> CountMinSketch<T> {

/// Deserializes a sketch from bytes using the default seed.
///
/// Can read sketches serialized by DataSketches C++.
/// Item queries and updates on those sketches are unreliable because the two
/// implementations map items to counters differently.
///
/// # Errors
///
/// Returns `InvalidData` if the image is malformed or its seed hash does not match the default
Expand All @@ -379,6 +386,10 @@ impl<T: CountMinValue> CountMinSketch<T> {

/// Deserializes a sketch from bytes using the provided seed.
///
/// Can read sketches serialized by DataSketches C++.
/// Item queries and updates on those sketches are unreliable because the two
/// implementations map items to counters differently.
///
/// # Errors
///
/// Returns `InvalidData` if the image is malformed, its seed hash does not match `seed`, or
Expand Down
10 changes: 8 additions & 2 deletions datasketches/src/cpc/sketch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,8 @@ impl CpcSketch {

/// Updates the sketch with a hashable value.
///
/// You may use [`hash::value`](crate::hash::value) wrappers when another DataSketches
/// implementation requires a specific value hashing strategy.
/// Use [`hash::value`](crate::hash::value) wrappers to select an input encoding for
/// cross-language hashing.
///
/// # Examples
///
Expand Down Expand Up @@ -490,6 +490,8 @@ impl fmt::Debug for CpcSketch {

impl CpcSketch {
/// Serializes this `CpcSketch` to bytes.
///
/// The serialized data can be read by other DataSketches implementations.
pub fn serialize(&self) -> Vec<u8> {
let flavor = self.flavor();
let has_hip = !self.merge_flag;
Expand Down Expand Up @@ -623,6 +625,8 @@ impl CpcSketch {

/// Deserializes a `CpcSketch` from bytes.
///
/// Also accepts data serialized by other DataSketches implementations.
///
/// # Errors
///
/// Returns `InvalidData` if the image is malformed or its seed hash does not match the default
Expand All @@ -633,6 +637,8 @@ impl CpcSketch {

/// Deserializes a `CpcSketch` from bytes with the provided seed.
///
/// Also accepts data serialized by other DataSketches implementations.
///
/// # Errors
///
/// Returns `InvalidData` if the image is malformed, its seed hash does not match `seed`, or
Expand Down
2 changes: 2 additions & 0 deletions datasketches/src/cpc/wrapper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ impl CpcWrapper {
/// Reads the cardinality metadata without copying or fully deserializing the compressed
/// payload.
///
/// Also accepts data serialized by other DataSketches implementations.
///
/// # Errors
///
/// Returns `InvalidData` if the preamble is malformed or does not describe a compressed CPC
Expand Down
15 changes: 10 additions & 5 deletions datasketches/src/frequencies/sketch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,8 @@ type DeserializeItems<T> = fn(SketchSlice<'_>, usize) -> Result<Vec<T>, Error>;

const LG_MIN_MAP_SIZE: u8 = 3;
const MIN_MAP_SIZE: usize = 1 << LG_MIN_MAP_SIZE;
// Java represents map sizes as positive `int` powers of two, while the C++
// implementation uses 32-bit table indices. Keep Rust configurations within
// the same cross-language range.
// datasketches-java represents map sizes as positive `int` powers of two, while datasketches-cpp
// uses 32-bit table indices. Keep Rust configurations within the same cross-language range.
const LG_MAX_MAP_SIZE: u8 = 30;
const MAX_MAP_SIZE: usize = 1 << LG_MAX_MAP_SIZE;
const SAMPLE_SIZE: usize = 1024;
Expand Down Expand Up @@ -680,8 +679,8 @@ impl<T: Eq + Hash> FrequentItemsSketch<T> {
return Err(Error::deserial("empty flag does not match preamble longs"));
}
if is_empty {
// Java also restores empty images at the minimum size. `lg_cur` does
// not carry item state here and must not control an eager allocation.
// datasketches-java also restores empty images at the minimum size. `lg_cur` carries
// no item state here and must not control an eager allocation.
return Ok(Self::with_lg_map_sizes(lg_max, LG_MIN_MAP_SIZE));
}

Expand Down Expand Up @@ -755,6 +754,9 @@ impl<T: Eq + Hash> FrequentItemsSketch<T> {
impl<T: FrequentItemValue> FrequentItemsSketch<T> {
/// Serializes this sketch into a byte vector.
///
/// The serialized data can be read by other DataSketches implementations when both
/// sides use the same [`FrequentItemValue`] item encoding.
///
/// # Examples
///
/// Built-in support for `i64`:
Expand Down Expand Up @@ -787,6 +789,9 @@ impl<T: FrequentItemValue> FrequentItemsSketch<T> {

/// Deserializes a sketch from bytes.
///
/// Also accepts data serialized by other DataSketches implementations when both
/// sides use the same [`FrequentItemValue`] item encoding.
///
/// # Errors
///
/// Returns `InvalidData` if the image is truncated, its metadata is inconsistent, or an item
Expand Down
4 changes: 2 additions & 2 deletions datasketches/src/hash/value/canonical_float.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
//! hashing. Signed zero values hash the same, all NaN values use one canonical NaN bit pattern,
//! and equal `f32`/`f64` values hash the same.
//!
//! This strategy is compatible with how other datasketches hashes floating-point numbers.
//! Use this encoding to match floating-point hashing in DataSketches Java and DataSketches C++.

use std::hash::Hash;
use std::hash::Hasher;
Expand Down Expand Up @@ -109,7 +109,7 @@ impl HashStrategy<f64> for CanonicalFloatStrategy {
#[inline(always)]
fn hash<H: Hasher>(value: &f64, state: &mut H) {
let canonical = if value.is_nan() {
// Java's Double.doubleToLongBits() NaN value.
// Canonical NaN used by datasketches-java via Double.doubleToLongBits().
0x7ff8000000000000u64
} else {
// -0.0 + 0.0 == +0.0 under IEEE754 roundTiesToEven rounding mode,
Expand Down
16 changes: 8 additions & 8 deletions datasketches/src/hash/value/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
//! 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.
//!
//! This strategy is the same as how other datasketches implementations hash floating-point numbers.
//! Use this encoding to match floating-point hashing in DataSketches Java and DataSketches C++.
//!
//! The concrete wrapper documentation provides more details and examples.
//!
Expand All @@ -37,8 +37,8 @@
//! ## Integers
//!
//! [`sign_extend::SignExtend`] first sign-extends values to 64-bits, and then hashes the resulting
//! integers. This strategy is the same as how datasketches-cpp hashes short integers for
//! `HllSketch` and `CpcSketch`.
//! 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.
//!
Expand All @@ -47,8 +47,8 @@
//! * [`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. This strategy is the same as how datasketches-cpp
//! hashes short integers for `BloomFilter`.
//! `u64`, and then hashes the resulting integers. Use this encoding to match short-integer hashing
//! in DataSketches C++'s Bloom filters.
//!
//! The concrete wrapper documentation provides more details and examples.
//!
Expand All @@ -61,9 +61,9 @@
//! [`raw_bytes::RawBytes`] hashes byte and string inputs as raw bytes without Rust's slice or
//! string length prefix.
//!
//! Empty byte and string inputs have zero bytes to hash. Other datasketches implementations skip
//! empty strings before hashing, so check `is_empty` before updating a sketch when that behavior
//! matters.
//! 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.
//!
Expand Down
2 changes: 1 addition & 1 deletion datasketches/src/hash/value/natural_extend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
//! [`NaturalExtend`] first widens signed values to `i64` and unsigned values to `u64`, and then
//! hashes the resulting integers.
//!
//! This strategy is the same as how datasketches-cpp hashes short integers for `BloomFilter`.
//! Use this encoding to match short-integer hashing in DataSketches C++'s Bloom filters.

use std::hash::Hash;
use std::hash::Hasher;
Expand Down
6 changes: 3 additions & 3 deletions datasketches/src/hash/value/raw_bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@
//! [`RawBytes`] hashes byte and string inputs as raw bytes without Rust's slice or string length
//! prefix.
//!
//! Empty byte and string inputs have zero bytes to hash. Other datasketches implementations skip
//! empty strings before hashing, so check `is_empty` before updating a sketch when that behavior
//! matters.
//! 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.

use std::hash::Hasher;

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 @@ -19,8 +19,7 @@
//!
//! [`SignExtend`] first sign-extends values to 64-bits and then hashes the resulting integers.
//!
//! This strategy is the same as how datasketches-cpp hashes short integers for `HllSketch` and
//! `CpcSketch`.
//! Use this encoding to match short-integer hashing in DataSketches C++'s HLL and CPC sketches.

use std::hash::Hash;
use std::hash::Hasher;
Expand Down
2 changes: 1 addition & 1 deletion datasketches/src/hll/aux_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ impl PartialEq for AuxMap {
}
}

/// Initial table sizes from DataSketches C++:
/// Initial table sizes from datasketches-cpp:
/// <https://github.com/apache/datasketches-cpp/blob/5a055521/hll/include/HllUtil.hpp>
fn lg_aux_arr_ints(lg_config_k: u8) -> u8 {
static LG_AUX_ARR_INTS: &[u8] = &[
Expand Down
4 changes: 2 additions & 2 deletions datasketches/src/hll/composite_interpolation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
// specific language governing permissions and limitations
// under the License.

//! Bias-correction tables for `lg_k` 4 through 21 from DataSketches C++:
//! Bias-correction tables for `lg_k` 4 through 21 from datasketches-cpp:
//! <https://github.com/apache/datasketches-cpp/blob/5a055521/hll/include/CompositeInterpolationXTable-internal.hpp>

const NUM_X_VALUES: usize = 257;
Expand Down Expand Up @@ -4769,7 +4769,7 @@ mod tests {
let x_arr = get_x_arr(4);
assert_eq!(x_arr.len(), 257);

// Check first few values match the C++ data
// Check first few values match the datasketches-cpp data.
assert_that!(x_arr[0], near(10.767999803534, 1e-6));
assert_that!(x_arr[1], near(11.237701481774, 1e-6));
}
Expand Down
2 changes: 1 addition & 1 deletion datasketches/src/hll/coupon_mapping.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
// under the License.

//! Coupon-count correction for the 26-bit slot encoding. These X/Y pairs are used with
//! cubic interpolation and come from DataSketches C++:
//! cubic interpolation and come from datasketches-cpp:
//! <https://github.com/apache/datasketches-cpp/blob/5a055521/hll/include/CubicInterpolation-internal.hpp>

/// X values (coupon counts) for interpolation table
Expand Down
6 changes: 3 additions & 3 deletions datasketches/src/hll/estimator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
//! Sequential register updates use HIP (Historical Inverse Probability). Bulk merges use the
//! composite estimator because register values do not retain their update order.
//!
//! Estimator formulas and crossover constants follow DataSketches C++:
//! Estimator formulas and crossover constants follow datasketches-cpp:
//! <https://github.com/apache/datasketches-cpp/blob/5a055521/hll/include/HllArray-internal.hpp>

use crate::common::NumStdDev;
Expand Down Expand Up @@ -274,7 +274,7 @@ impl Estimator {
}

/// Relative error used as `estimate / (1 + error)`: negative for upper bounds.
/// Follows DataSketches C++ `HllUtil::getRelErr`:
/// Follows datasketches-cpp's `HllUtil::getRelErr`:
/// <https://github.com/apache/datasketches-cpp/blob/5a055521/hll/include/HllUtil.hpp>
fn relative_error(
lg_config_k: u8,
Expand All @@ -283,7 +283,7 @@ fn relative_error(
num_std_dev: NumStdDev,
) -> f64 {
if lg_config_k > 12 {
// RSE factors from Apache DataSketches C++ implementation
// RSE factors from datasketches-cpp.
// HLL_HIP_RSE_FACTOR = sqrt(ln(2)) ≈ 0.8325546
// HLL_NON_HIP_RSE_FACTOR = sqrt((3 * ln(2)) - 1) ≈ 1.03896
let rse_factor = if composite {
Expand Down
2 changes: 1 addition & 1 deletion datasketches/src/hll/harmonic_numbers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
// specific language governing permissions and limitations
// under the License.

//! Harmonic-number approximation used by the bitmap estimator, following DataSketches C++:
//! Harmonic-number approximation used by the bitmap estimator, following datasketches-cpp:
//! <https://github.com/apache/datasketches-cpp/blob/5a055521/hll/include/HarmonicNumbers-internal.hpp>

const NUM_EXACT: usize = 25;
Expand Down
6 changes: 3 additions & 3 deletions datasketches/src/hll/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
//! Use [`HllSketch`] to collect values and [`HllUnion`] to combine sketches.
//! [`HllType`] controls register storage; `lg_k` controls the register count and accuracy.
//!
//! Serialized sketches are compatible with Apache DataSketches in Java and C++.
//! The binary serialization format supports cross-language interchange.
//! Cross-language updates must also use compatible hashing; see
//! [`hash::value`](crate::hash::value).
//!
Expand Down Expand Up @@ -149,8 +149,8 @@ impl Coupon {

/// Computes the HLL coupon for a hashable value.
///
/// Use [`hash::value`](crate::hash::value) wrappers when another DataSketches
/// implementation requires a specific value hashing strategy.
/// Use [`hash::value`](crate::hash::value) wrappers to select an input encoding for
/// cross-language hashing.
///
/// # Examples
///
Expand Down
8 changes: 6 additions & 2 deletions datasketches/src/hll/sketch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,8 @@ impl HllSketch {

/// Updates the sketch with a value.
///
/// You may use [`hash::value`](crate::hash::value) wrappers when another DataSketches
/// implementation requires a specific value hashing strategy.
/// Use [`hash::value`](crate::hash::value) wrappers to select an input encoding for
/// cross-language hashing.
///
/// To reuse the hash across sketches, see [`Coupon`] and
/// [`update_with_coupon`](Self::update_with_coupon).
Expand Down Expand Up @@ -314,6 +314,8 @@ impl HllSketch {

/// Deserializes an HLL sketch from bytes.
///
/// Also accepts data serialized by other DataSketches implementations.
///
/// # Errors
///
/// Returns `InvalidData` if the image is truncated or contains an invalid preamble,
Expand Down Expand Up @@ -443,6 +445,8 @@ impl HllSketch {

/// Serializes the HLL sketch to bytes.
///
/// The serialized data can be read by other DataSketches implementations.
///
/// # Examples
///
/// ```
Expand Down
Loading
Loading