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