Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
4b3155b
test(cpc): focus sliding union workload
tisonkun Aug 30, 2026
09ec0fc
fix(tdigest): accept empty split-point queries
tisonkun Aug 30, 2026
507a0be
refactor(bloom): return errors from set operations
tisonkun Aug 30, 2026
60c8997
refactor(countmin): return errors from merge
tisonkun Aug 30, 2026
a464748
refactor(cpc): return errors from union updates
tisonkun Aug 30, 2026
1d5f2e1
refactor(bloom): validate sizing suggestions
tisonkun Aug 30, 2026
646892e
refactor(cpc): validate serialized size inputs
tisonkun Aug 30, 2026
090f3f6
fix(frequencies): reject undersized maps
tisonkun Aug 30, 2026
2a2f0fd
refactor(frequencies): use map sizes in error helpers
tisonkun Aug 30, 2026
d04d586
refactor(tdigest): replace deserialize precision flag
tisonkun Aug 30, 2026
c558648
feat(tdigest): persist frozen digests directly
tisonkun Aug 30, 2026
57fc384
refactor(cpc): remove union test hook
tisonkun Aug 30, 2026
104d6b8
refactor(req): remove hidden diagnostic APIs
tisonkun Aug 30, 2026
040c46c
refactor(theta): colocate Jaccard result types
tisonkun Aug 30, 2026
907a767
refactor(tuple): hide storage entry type
tisonkun Aug 30, 2026
845f2ee
feat(countmin): reset sketches in place
tisonkun Aug 30, 2026
da834a6
feat(tdigest): reset mutable digests in place
tisonkun Aug 30, 2026
f25c92c
docs: explain sketch selection and hashing compatibility
tisonkun Aug 30, 2026
88e6ea1
fix(countmin): saturate counter arithmetic
tisonkun Aug 30, 2026
e49a579
fix(frequencies): saturate count arithmetic
tisonkun Aug 30, 2026
2fce7c2
fix(bloom): reject unattainable accuracy targets
tisonkun Aug 30, 2026
a0a9cb2
docs(cpc): clarify diagnostic methods
tisonkun Aug 30, 2026
b696cab
chore(deps): update chacha20 to a non-yanked release
tisonkun Aug 30, 2026
bbca047
fixup
tisonkun Aug 30, 2026
cac164d
revert(theta): preserve shared Jaccard API
tisonkun Aug 30, 2026
a767cb1
style: omit inferred usize suffixes
tisonkun Aug 30, 2026
85a7977
revert(countmin): remove speculative reset API
tisonkun Aug 30, 2026
ab74492
revert(tdigest): remove speculative reset API
tisonkun Aug 30, 2026
6d042a7
docs(cpc): hide test-only inspection methods
tisonkun Aug 30, 2026
170d34c
revert(countmin): remove saturating arithmetic
tisonkun Aug 30, 2026
de01332
revert(frequencies): remove saturating arithmetic
tisonkun Aug 30, 2026
7d029bd
Potential fix for pull request finding
tisonkun Aug 30, 2026
bf1af5f
refactor(bloom): keep sizing behind the builder
tisonkun Aug 30, 2026
592ae31
docs(changelog): report net release changes
tisonkun Aug 30, 2026
c2f01d7
docs(changelog): preserve existing Count-Min entries
tisonkun Aug 30, 2026
43d63a6
docs: single-source changelog guidance
tisonkun Aug 30, 2026
13139f2
docs(changelog): classify Count-Min API break
tisonkun Aug 30, 2026
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
4 changes: 1 addition & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,4 @@ Before planning or modifying this repository, read [CONTRIBUTING.md](CONTRIBUTIN

For test changes, pay particular attention to the "Integration test layout" and "Serialization snapshots" sections. Keep the documented workflow synchronized with structural changes, and run the applicable `cargo x check`, `cargo x test`, and `cargo x lint` commands before handing work back.

Apply the changelog guidance in [CONTRIBUTING.md](CONTRIBUTING.md) to every change. Update the permanent `Unreleased` section in the same pull request for significant user-visible behavior, and do not add entries mechanically for excluded maintenance work.

Treat `CHANGELOG.md` as release notes for users rather than a summary of implementation work. Name the affected API or workload and the observable outcome, and keep performance claims within the scenario supported by evidence.
For every change, follow the [changelog guidance](CONTRIBUTING.md#changelog) in `CONTRIBUTING.md` as the single source of truth.
17 changes: 15 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,25 @@ All significant changes to this project will be documented in this file.

### Breaking changes

* `BloomFilter::union` and `BloomFilter::intersect` now return `Result`. Callers must handle incompatible filter configurations instead of relying on a panic.
* `CountMinSketch::merge` now returns `Result`. Callers must handle incompatible sketch configurations instead of relying on a panic.
* `CountMinSketch::{suggest_num_buckets, suggest_num_hashes}` now return `Result`. Callers must handle invalid or unsupported targets; successful suggestions are valid inputs to `CountMinSketch::new`.
* `CpcUnion::update` now returns `Result`. Callers must handle seed mismatches instead of relying on a panic.
* Remove `BloomFilterBuilder::suggest_num_bits`, `suggest_num_hashes_from_accuracy`, and `suggest_num_hashes_from_fpp`. Use `with_accuracy(...).build()` for target-based sizing or `with_size(...).build()` for an explicit precomputed configuration.
* `CpcSketch::max_serialized_bytes` now returns `Result` and reports an invalid `lg_k` instead of panicking.
* `FrequentItemsSketch::new` now rejects map sizes below the minimum of 8 instead of silently rounding them up.
* Replace `FrequentItemsSketch::epsilon_for_lg` with the fallible `epsilon_for_max_map_size`, and change `apriori_error` to accept the same maximum map size plus an unsigned stream weight. These helpers now match the constructor's units, and `max_map_size` exposes the configured value.
* Replace the `is_f32` flag on `TDigestMut::deserialize` with separate `deserialize` and `deserialize_f32` entry points, making the serialized precision explicit at the call site.
* Remove `CpcUnion::num_coupons`, which exposed internal union state solely for tests. Inspect the resulting `CpcSketch` when diagnostics are needed.
* Remove the `TupleEntry` re-export. Tuple sketch iterators already expose retained entries as `(hash, &summary)` pairs without leaking the private storage representation.
* `ThetaIntersection::to_sketch` and `TupleIntersection::to_sketch` now return `Option`. Callers must handle `None` until the intersection receives its first successful update.
* `BloomFilterBuilder`, `ThetaSketchBuilder`, `ThetaUnionBuilder`, `TupleSketchBuilder`, and `TupleUnionBuilder` now validate their configuration when `build` is called, and `build` returns `Result`. Callers must propagate or handle construction errors.
* `BloomFilterBuilder::{MIN_NUM_BITS, MAX_NUM_BITS, MIN_NUM_HASHES, MAX_NUM_HASHES}` are no longer public. Callers should pass configurations to `build` and handle `InvalidArgument` instead of prevalidating against these constants.
* Fallible sketch and operator constructors now return `Result` directly from `new` or `with_seed`. `ReqSketch` and `TDigestMut` no longer provide `try_new`, and the Count-Min parameter suggestion methods also return `Result`.
* Fallible sketch and operator constructors now return `Result` directly from `new` or `with_seed`. `TDigestMut` no longer provides `try_new`.

### New features

* `TDigest` can now be serialized and deserialized directly without converting through `TDigestMut` at the call site.
* Add Relative Error Quantiles (REQ) sketches behind the `req` feature, including configurable high- or low-rank accuracy, rank, quantile, PMF, and CDF queries, merging, totally ordered custom item types, the `ReqFloat` adapter for non-NaN floating-point values, and C++/Java-compatible serialization.

### Performance improvements
Expand All @@ -22,7 +34,8 @@ All significant changes to this project will be documented in this file.

### Bug fixes

* Count-Min parameter suggestions now return constructor-valid values and reject relative-error targets that require more buckets than the sketch supports.
* Bloom filter accuracy construction now rejects targets that exceed the maximum serialized filter size instead of silently reducing capacity and violating the requested false-positive probability.
* T-Digest CDF and PMF queries now accept an empty split-point slice and return the single all-values bin instead of panicking.
* Bloom filter deserialization now rejects malformed images with inconsistent counts or payload lengths, while valid images with a dirty cached count are restored correctly.
* `FrequentItemsSketch` now enforces the cross-language map-size limit of `2^30` consistently. Oversized construction returns `InvalidArgument`, and malformed or oversized serialized images return `InvalidData` instead of panicking or attempting excessive allocation.
* T-Digest compression now supports `k = u16::MAX` without overflowing.
Expand Down
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@ See the [API documentation](https://docs.rs/datasketches) for configuration, acc

The minimum supported Rust version is 1.86.0. The crate currently supports little-endian targets only.

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). When values must hash identically across language implementations, use the compatibility wrappers in `hash::value`.
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.

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

Expand Down
4 changes: 2 additions & 2 deletions benchmarks/tdigest/merge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ fn serialized_partials(bencher: Bencher, rows_per_partial: usize) {
.bench_local(|| {
let mut merged = TDigestMut::default();
for partial in black_box(&partials) {
let partial = TDigestMut::deserialize(partial, false).unwrap();
let partial = TDigestMut::deserialize(partial).unwrap();
merged.merge(&partial);
}
black_box(merged.quantile(0.5))
Expand All @@ -131,7 +131,7 @@ fn serialized_overlapping_partials(bencher: Bencher) {
.bench_local(|| {
let mut merged = TDigestMut::default();
for partial in black_box(&partials) {
let partial = TDigestMut::deserialize(partial, false).unwrap();
let partial = TDigestMut::deserialize(partial).unwrap();
merged.merge(&partial);
}
black_box(merged.quantile(0.5))
Expand Down
4 changes: 2 additions & 2 deletions benchmarks/tdigest/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ fn deserialize_small_partial_groups(bencher: Bencher) {
.bench_local(|| {
let digests = bytes
.iter()
.map(|bytes| TDigestMut::deserialize(bytes, false).unwrap())
.map(|bytes| TDigestMut::deserialize(bytes).unwrap())
.collect::<Vec<_>>();
black_box(digests)
});
Expand Down Expand Up @@ -131,7 +131,7 @@ fn deserialize_partial_groups(bencher: Bencher) {
.bench_local(|| {
let digests = bytes
.iter()
.map(|bytes| TDigestMut::deserialize(bytes, false).unwrap())
.map(|bytes| TDigestMut::deserialize(bytes).unwrap())
.collect::<Vec<_>>();
black_box(digests)
});
Expand Down
17 changes: 14 additions & 3 deletions datasketches/src/bloom/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@
//!
//! ## By Accuracy (Recommended)
//!
//! Automatically calculates optimal size and hash functions:
//! Derive the size and hash-function count from an expected distinct-item count and a target
//! false-positive probability:
//!
//! ```
//! use datasketches::bloom::BloomFilterBuilder;
Expand All @@ -77,6 +78,12 @@
//! .unwrap();
//! ```
//!
//! `max_items` is a sizing assumption, not an insertion limit. The filter continues accepting
//! distinct items beyond that count, but its false-positive probability can then exceed the target.
//! Accuracy inputs are validated by `build`: `max_items` must be positive, `fpp` must be in
//! `(0.0, 1.0]`, and the requested target must fit the serialized Bloom filter format. An `fpp` of
//! `1.0` is accepted and creates the smallest allocation: 64 bits and one hash function.
//!
//! ## By Size (Manual)
//!
//! Specify requested bit count and hash functions (rounded up to a multiple of 64 bits):
Expand All @@ -92,6 +99,10 @@
//! .unwrap();
//! ```
//!
//! Manual construction requires a positive bit count supported by the serialized format and a
//! hash-function count in `1..=32767`. The requested bit count is rounded up to a multiple of 64,
//! which is the value returned by [`BloomFilter::capacity`].
//!
//! # Set Operations
//!
//! Bloom filters support efficient set operations:
Expand All @@ -110,12 +121,12 @@
//! filter2.insert("b");
//!
//! // Union: recognizes items from either filter
//! filter1.union(&filter2);
//! filter1.union(&filter2).unwrap();
//! assert!(filter1.contains(&"a"));
//! assert!(filter1.contains(&"b"));
//!
//! // Intersect: recognizes only items in both filters
//! // filter1.intersect(&filter2);
//! // filter1.intersect(&filter2).unwrap();
//!
//! // Invert: approximately inverts set membership
//! // filter1.invert();
Expand Down
Loading