libxtc 0.4.0
Async concurrency for C: Tokio + Seastar + BEAM, in one library
Loading...
Searching...
No Matches
xtc_prob.h
1/*-
2 * Copyright (c) 2026, The XTC Project
3 * @stability stable
4 * Use of this source code is governed by the ISC License.
5 *
6 * src/inc/xtc_prob.h
7 * Probabilistic data structures: a Bloom filter (xtc_bloom) and a
8 * HyperLogLog cardinality estimator (xtc_hll). Both trade a small,
9 * bounded, tunable error for memory that stays flat regardless of
10 * how many distinct keys pass through -- the classic space/accuracy
11 * bargain for membership tests and distinct-count estimates over
12 * streams far too large to keep exactly.
13 *
14 * Keys are opaque byte spans (const void *, size_t len), hashed
15 * internally with FNV-1a plus a second independent mix, so any key
16 * type works with no caller-supplied hash or comparator. Nothing
17 * is copied or retained -- only the derived hash bits update the
18 * structure, so a key buffer may be freed the moment the call
19 * returns.
20 *
21 * SINGLE-THREADED, v1. Neither structure is concurrency-safe:
22 * unlike xtc_chash / xtc_cskip there is no internal locking and no
23 * RCU. Concurrent xtc_bloom_add / xtc_hll_add on one instance is a
24 * data race. Serialize externally, or give each thread its own
25 * instance and (for HLL) xtc_hll_merge them at the end -- the merge
26 * is exact register-wise max, so N per-thread sketches combine into
27 * one whole-stream estimate with no loss.
28 *
29 * Bloom filter (xtc_bloom): a bit array of m bits probed by k hash
30 * functions. xtc_bloom_init derives the optimal m and k from the
31 * expected element count n and the target false-positive rate p:
32 * m = ceil(-n*ln(p) / (ln2)^2), k = round((m/n)*ln2). A membership
33 * test has NO false negatives (a key that was added always tests
34 * "maybe present") and a false-positive probability that, once the
35 * filter holds about n elements, sits near the configured p. Push
36 * well past n and the false-positive rate climbs -- the filter does
37 * not resize, so size it for the true expected load.
38 *
39 * HyperLogLog (xtc_hll): 2^p registers (precision p in [4,18]) each
40 * holding the max leading-zero run seen for the hashes that fall in
41 * that register's bucket. xtc_hll_count applies the standard
42 * bias-corrected harmonic-mean estimator with the small- and
43 * large-range corrections (linear counting for sparse fills, the
44 * 2^32 wrap correction at the top), giving a distinct-count estimate
45 * with a relative standard error of about 1.04/sqrt(2^p) -- roughly
46 * 1.6% at p=12, well under 1% at p=16 -- in a few KB regardless of
47 * the true cardinality. xtc_hll_merge takes the register-wise max
48 * of two sketches, which is exactly the sketch of the union.
49 */
50
51#ifndef XTC_PROB_H
52#define XTC_PROB_H
53
54#include "xtc_export.h"
55
56#include <stddef.h>
57#include <stdint.h>
58
59#include "xtc.h"
60
61typedef struct xtc_bloom xtc_bloom_t;
62typedef struct xtc_hll xtc_hll_t;
63
64/*
65 * PUBLIC: int xtc_bloom_init __P((xtc_bloom_t **, size_t, double));
66 * PUBLIC: void xtc_bloom_add __P((xtc_bloom_t *, const void *, size_t));
67 * PUBLIC: int xtc_bloom_maybe_contains __P((const xtc_bloom_t *, const void *, size_t));
68 * PUBLIC: void xtc_bloom_fini __P((xtc_bloom_t *));
69 * PUBLIC: int xtc_hll_init __P((xtc_hll_t **, int));
70 * PUBLIC: void xtc_hll_add __P((xtc_hll_t *, const void *, size_t));
71 * PUBLIC: uint64_t xtc_hll_count __P((const xtc_hll_t *));
72 * PUBLIC: int xtc_hll_merge __P((xtc_hll_t *, const xtc_hll_t *));
73 * PUBLIC: void xtc_hll_fini __P((xtc_hll_t *));
74 */
75
76/*
77 * Create a Bloom filter sized for `n_expected` distinct elements at a
78 * target false-positive rate `fp_rate` (0 < fp_rate < 1). The optimal
79 * bit count m and probe count k are computed from those two inputs.
80 * n_expected of 0 is treated as 1 (a one-element filter). Returns
81 * XTC_OK and writes *out, or XTC_E_INVAL (NULL out, or fp_rate not in
82 * the open interval (0,1)) / XTC_E_NOMEM.
83 */
84XTC_API int xtc_bloom_init(xtc_bloom_t **out, size_t n_expected,
85 double fp_rate);
86
87/*
88 * Add `len` bytes at `key` to the filter. Idempotent: adding the same
89 * key twice is a no-op the second time. A NULL filter is ignored; a
90 * NULL key is allowed only when len is 0 (the empty key).
91 */
92XTC_API void xtc_bloom_add(xtc_bloom_t *b, const void *key, size_t len);
93
94/*
95 * Test membership of the `len` bytes at `key`. Returns 1 if the key
96 * MAY be present (every added key returns 1 -- no false negatives) and
97 * 0 if it is DEFINITELY absent. A 1 result carries the filter's
98 * configured false-positive probability. A NULL filter returns 0.
99 */
100XTC_API int xtc_bloom_maybe_contains(const xtc_bloom_t *b,
101 const void *key, size_t len);
102
103/* Free the filter. A NULL argument is a no-op. */
104XTC_API void xtc_bloom_fini(xtc_bloom_t *b);
105
106/*
107 * Create a HyperLogLog sketch with 2^precision registers; `precision`
108 * must be in [4,18] (16 to 262144 registers -- more precision, less
109 * error, more memory). Returns XTC_OK and writes *out, or
110 * XTC_E_INVAL (NULL out or out-of-range precision) / XTC_E_NOMEM.
111 */
112XTC_API int xtc_hll_init(xtc_hll_t **out, int precision);
113
114/*
115 * Add `len` bytes at `key` to the sketch. A NULL sketch is ignored; a
116 * NULL key is allowed only when len is 0.
117 */
118XTC_API void xtc_hll_add(xtc_hll_t *h, const void *key, size_t len);
119
120/*
121 * Estimated number of DISTINCT keys added, via the bias-corrected
122 * harmonic-mean estimator with the standard small-/large-range
123 * corrections. Relative standard error is about 1.04/sqrt(2^p). A
124 * NULL sketch returns 0.
125 */
126XTC_API uint64_t xtc_hll_count(const xtc_hll_t *h);
127
128/*
129 * Merge `src` into `dst` (register-wise max), so dst afterward
130 * estimates the cardinality of the UNION of the two key sets -- exact,
131 * no additional error introduced by the merge. Both must have the
132 * same precision. Returns XTC_OK, or XTC_E_INVAL (NULL argument or
133 * mismatched precision).
134 */
135XTC_API int xtc_hll_merge(xtc_hll_t *dst, const xtc_hll_t *src);
136
137/* Free the sketch. A NULL argument is a no-op. */
138XTC_API void xtc_hll_fini(xtc_hll_t *h);
139
140#endif /* XTC_PROB_H */