Coverage Report

Created: 2025-09-19 18:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/root/bitcoin/src/minisketch/include/minisketch.h
Line
Count
Source
1
#ifndef _MINISKETCH_H_
2
#define _MINISKETCH_H_ 1
3
4
#include <stdint.h>
5
#include <stdlib.h>
6
7
#ifdef _MSC_VER
8
#  include <BaseTsd.h>
9
   typedef SSIZE_T ssize_t;
10
#else
11
#  include <unistd.h>
12
#endif
13
14
#ifndef MINISKETCH_API
15
# if defined(_WIN32)
16
#  ifdef MINISKETCH_BUILD
17
#   define MINISKETCH_API __declspec(dllexport)
18
#  else
19
#   define MINISKETCH_API
20
#  endif
21
# elif defined(__GNUC__) && (__GNUC__ >= 4) && defined(MINISKETCH_BUILD)
22
#  define MINISKETCH_API __attribute__ ((visibility ("default")))
23
# else
24
#  define MINISKETCH_API
25
# endif
26
#endif
27
28
#ifdef __cplusplus
29
#  if __cplusplus >= 201103L
30
#    include <memory>
31
#    include <vector>
32
#    include <cassert>
33
#    if __cplusplus >= 201703L
34
#      include <optional>
35
#    endif // __cplusplus >= 201703L
36
#  endif // __cplusplus >= 201103L
37
extern "C" {
38
#endif // __cplusplus
39
40
/** Opaque type for decoded sketches. */
41
typedef struct minisketch minisketch;
42
43
/** Determine whether support for elements of `bits` bits was compiled in. */
44
MINISKETCH_API int minisketch_bits_supported(uint32_t bits);
45
46
/** Determine the maximum number of implementations available.
47
 *
48
 * Multiple implementations may be available for a given element size, with
49
 * different performance characteristics on different hardware.
50
 *
51
 * Each implementation is identified by a number from 0 to the output of this
52
 * function call, inclusive. Note that not every combination of implementation
53
 * and element size may exist (see further).
54
*/
55
MINISKETCH_API uint32_t minisketch_implementation_max(void);
56
57
/** Determine if the a combination of bits and implementation number is available.
58
 *
59
 * Returns 1 if it is, 0 otherwise.
60
 */
61
MINISKETCH_API int minisketch_implementation_supported(uint32_t bits, uint32_t implementation);
62
63
/** Construct a sketch for a given element size, implementation and capacity.
64
 *
65
 * If the combination of `bits` and `implementation` is unavailable, or when
66
 * OOM occurs, NULL is returned. If minisketch_implementation_supported
67
 * returns 1 for the specified bits and implementation, this will always succeed
68
 * (except when allocation fails).
69
 *
70
 * If the result is not NULL, it must be destroyed using minisketch_destroy.
71
 */
72
MINISKETCH_API minisketch* minisketch_create(uint32_t bits, uint32_t implementation, size_t capacity);
73
74
/** Get the element size of a sketch in bits. */
75
MINISKETCH_API uint32_t minisketch_bits(const minisketch* sketch);
76
77
/** Get the capacity of a sketch. */
78
MINISKETCH_API size_t minisketch_capacity(const minisketch* sketch);
79
80
/** Get the implementation of a sketch. */
81
MINISKETCH_API uint32_t minisketch_implementation(const minisketch* sketch);
82
83
/** Set the seed for randomizing algorithm choices to a fixed value.
84
 *
85
 * By default, sketches are initialized with a random seed. This is important
86
 * to avoid scenarios where an attacker could force worst-case behavior.
87
 *
88
 * This function initializes the seed to a user-provided value (any 64-bit
89
 * integer is acceptable, regardless of field size).
90
 *
91
 * When seed is -1, a fixed internal value with predictable behavior is
92
 * used. It is only intended for testing.
93
 */
94
MINISKETCH_API void minisketch_set_seed(minisketch* sketch, uint64_t seed);
95
96
/** Clone a sketch.
97
 *
98
 * The result must be destroyed using minisketch_destroy.
99
 */
100
MINISKETCH_API minisketch* minisketch_clone(const minisketch* sketch);
101
102
/** Destroy a sketch.
103
 *
104
 * The pointer that was passed in may not be used anymore afterwards.
105
 */
106
MINISKETCH_API void minisketch_destroy(minisketch* sketch);
107
108
/** Compute the size in bytes for serializing a given sketch. */
109
MINISKETCH_API size_t minisketch_serialized_size(const minisketch* sketch);
110
111
/** Serialize a sketch to bytes. */
112
MINISKETCH_API void minisketch_serialize(const minisketch* sketch, unsigned char* output);
113
114
/** Deserialize a sketch from bytes. */
115
MINISKETCH_API void minisketch_deserialize(minisketch* sketch, const unsigned char* input);
116
117
/** Add an element to a sketch.
118
 *
119
 * If the element to be added is too large for the sketch, the most significant
120
 * bits of the element are dropped. More precisely, if the element size of
121
 * `sketch` is b bits, then this function adds the unsigned integer represented
122
 * by the b least significant bits of `element` to `sketch`.
123
 *
124
 * If the element to be added is 0 (after potentially dropping the most significant
125
 * bits), then this function is a no-op. Sketches cannot contain an element with
126
 * the value 0.
127
 *
128
 * Note that adding the same element a second time removes it again.
129
 */
130
MINISKETCH_API void minisketch_add_uint64(minisketch* sketch, uint64_t element);
131
132
/** Merge the elements of another sketch into this sketch.
133
 *
134
 * After merging, `sketch` will contain every element that existed in one but not
135
 * both of the input sketches. It can be seen as an exclusive or operation on
136
 * the set elements.  If the capacity of `other_sketch` is lower than `sketch`'s,
137
 * merging reduces the capacity of `sketch` to that of `other_sketch`.
138
 *
139
 * This function returns the capacity of `sketch` after merging has been performed
140
 * (where this capacity is at least 1), or 0 to indicate that merging has failed because
141
 * the two input sketches differ in their element size or implementation. If 0 is
142
 * returned, `sketch` (and its capacity) have not been modified.
143
 *
144
 * It is also possible to perform this operation directly on the serializations
145
 * of two sketches with the same element size and capacity by performing a bitwise XOR
146
 * of the serializations.
147
 */
148
MINISKETCH_API size_t minisketch_merge(minisketch* sketch, const minisketch* other_sketch);
149
150
/** Decode a sketch.
151
 *
152
 * `output` is a pointer to an array of `max_element` uint64_t's, which will be
153
 * filled with the elements in this sketch.
154
 *
155
 * The return value is the number of decoded elements, or -1 if decoding failed.
156
 */
157
MINISKETCH_API ssize_t minisketch_decode(const minisketch* sketch, size_t max_elements, uint64_t* output);
158
159
/** Compute the capacity needed to achieve a certain rate of false positives.
160
 *
161
 * A sketch with capacity c and no more than c elements can always be decoded
162
 * correctly. However, if it has more than c elements, or contains just random
163
 * bytes, it is possible that it will still decode, but the result will be
164
 * nonsense. This can be counteracted by increasing the capacity slightly.
165
 *
166
 * Given a field size bits, an intended number of elements that can be decoded
167
 * max_elements, and a false positive probability of 1 in 2**fpbits, this
168
 * function computes the necessary capacity. It is only guaranteed to be
169
 * accurate up to fpbits=256.
170
 */
171
MINISKETCH_API size_t minisketch_compute_capacity(uint32_t bits, size_t max_elements, uint32_t fpbits);
172
173
/** Compute what max_elements can be decoded for a certain rate of false positives.
174
 *
175
 * This is the inverse operation of minisketch_compute_capacity. It determines,
176
 * given a field size bits, a capacity of a sketch, and an acceptable false
177
 * positive probability of 1 in 2**fpbits, what the maximum allowed
178
 * max_elements value is. If no value of max_elements would give the desired
179
 * false positive probability, 0 is returned.
180
 *
181
 * Note that this is not an exact inverse of minisketch_compute_capacity. For
182
 * example, with bits=32, fpbits=16, and max_elements=8,
183
 * minisketch_compute_capacity will return 9, as capacity 8 would only have a
184
 * false positive chance of 1 in 2^15.3. Increasing the capacity to 9 however
185
 * decreases the fp chance to 1 in 2^47.3, enough for max_elements=9 (with fp
186
 * chance of 1 in 2^18.5). Therefore, minisketch_compute_max_elements with
187
 * capacity=9 will return 9.
188
 */
189
MINISKETCH_API size_t minisketch_compute_max_elements(uint32_t bits, size_t capacity, uint32_t fpbits);
190
191
#ifdef __cplusplus
192
}
193
194
#if __cplusplus >= 201103L
195
/** Simple RAII C++11 wrapper around the minisketch API. */
196
class Minisketch
197
{
198
    struct Deleter
199
    {
200
        void operator()(minisketch* ptr) const
201
0
        {
202
0
            minisketch_destroy(ptr);
203
0
        }
204
    };
205
206
    std::unique_ptr<minisketch, Deleter> m_minisketch;
207
208
public:
209
    /** Check whether the library supports fields of the given size. */
210
0
    static bool BitsSupported(uint32_t bits) noexcept { return minisketch_bits_supported(bits); }
211
212
    /** Get the highest supported implementation number. */
213
0
    static uint32_t MaxImplementation() noexcept { return minisketch_implementation_max(); }
214
215
    /** Check whether the library supports fields with a given size and implementation number.
216
     *  If a particular field size `bits` is supported, implementation 0 is always supported for it.
217
     *  Higher implementation numbers may or may not be available as well, up to MaxImplementation().
218
     */
219
0
    static bool ImplementationSupported(uint32_t bits, uint32_t implementation) noexcept { return minisketch_implementation_supported(bits, implementation); }
220
221
    /** Given field size and a maximum number of decodable elements n, compute what capacity c to
222
     *  use so that sketches with more elements than n have a chance no higher than 2^-fpbits of
223
     *  being decoded incorrectly (and will instead fail when decoding for up to n elements).
224
     *
225
     *  See minisketch_compute_capacity for more details. */
226
0
    static size_t ComputeCapacity(uint32_t bits, size_t max_elements, uint32_t fpbits) noexcept { return minisketch_compute_capacity(bits, max_elements, fpbits); }
227
228
    /** Reverse operation of ComputeCapacity. See minisketch_compute_max_elements. */
229
0
    static size_t ComputeMaxElements(uint32_t bits, size_t capacity, uint32_t fpbits) noexcept { return minisketch_compute_max_elements(bits, capacity, fpbits); }
230
231
    /** Construct a clone of the specified sketch. */
232
    Minisketch(const Minisketch& sketch) noexcept
233
0
    {
234
0
        if (sketch.m_minisketch) {
235
0
            m_minisketch = std::unique_ptr<minisketch, Deleter>(minisketch_clone(sketch.m_minisketch.get()));
236
0
        }
237
0
    }
238
239
    /** Make this Minisketch a clone of the specified one. */
240
    Minisketch& operator=(const Minisketch& sketch) noexcept
241
0
    {
242
0
        if (this != &sketch && sketch.m_minisketch) {
243
0
            m_minisketch = std::unique_ptr<minisketch, Deleter>(minisketch_clone(sketch.m_minisketch.get()));
244
0
        }
245
0
        return *this;
246
0
    }
247
248
    /** Check whether this Minisketch object is valid. */
249
0
    explicit operator bool() const noexcept { return bool{m_minisketch}; }
250
251
    /** Default constructor is deleted. */
252
    Minisketch() noexcept = delete;
253
254
    /** Move constructor. */
255
0
    Minisketch(Minisketch&&) noexcept = default;
256
257
    /** Move assignment. */
258
    Minisketch& operator=(Minisketch&&) noexcept = default;
259
260
    /** Construct a Minisketch object with the specified parameters.
261
     *
262
     * If bits is not BitsSupported(), or the combination of bits and capacity is not
263
     * ImplementationSupported(), or OOM occurs internally, an invalid Minisketch
264
     * object will be constructed. Use operator bool() to check that this isn't the
265
     * case before performing any other operations. */
266
    Minisketch(uint32_t bits, uint32_t implementation, size_t capacity) noexcept
267
0
    {
268
0
        m_minisketch = std::unique_ptr<minisketch, Deleter>(minisketch_create(bits, implementation, capacity));
269
0
    }
270
271
    /** Create a Minisketch object sufficiently large for the specified number of elements at given fpbits.
272
     *  It may construct an invalid object, which you may need to check for. */
273
    static Minisketch CreateFP(uint32_t bits, uint32_t implementation, size_t max_elements, uint32_t fpbits) noexcept
274
0
    {
275
0
        return Minisketch(bits, implementation, ComputeCapacity(bits, max_elements, fpbits));
276
0
    }
277
278
    /** Return the field size for a (valid) Minisketch object. */
279
0
    uint32_t GetBits() const noexcept { return minisketch_bits(m_minisketch.get()); }
280
281
    /** Return the capacity for a (valid) Minisketch object. */
282
0
    size_t GetCapacity() const noexcept { return minisketch_capacity(m_minisketch.get()); }
283
284
    /** Return the implementation number for a (valid) Minisketch object. */
285
0
    uint32_t GetImplementation() const noexcept { return minisketch_implementation(m_minisketch.get()); }
286
287
    /** Set the seed for a (valid) Minisketch object. See minisketch_set_seed(). */
288
    Minisketch& SetSeed(uint64_t seed) noexcept
289
0
    {
290
0
        minisketch_set_seed(m_minisketch.get(), seed);
291
0
        return *this;
292
0
    }
293
294
    /** Add (or remove, if already present) an element to a (valid) Minisketch object.
295
     *  See minisketch_add_uint64(). */
296
    Minisketch& Add(uint64_t element) noexcept
297
0
    {
298
0
        minisketch_add_uint64(m_minisketch.get(), element);
299
0
        return *this;
300
0
    }
301
302
    /** Merge sketch into *this; both have to be valid Minisketch objects.
303
     *  See minisketch_merge for details. */
304
    Minisketch& Merge(const Minisketch& sketch) noexcept
305
0
    {
306
0
        minisketch_merge(m_minisketch.get(), sketch.m_minisketch.get());
307
0
        return *this;
308
0
    }
309
310
    /** Decode this (valid) Minisketch object into the result vector, up to as many elements as the
311
     *  vector's size permits. */
312
    bool Decode(std::vector<uint64_t>& result) const
313
0
    {
314
0
        ssize_t ret = minisketch_decode(m_minisketch.get(), result.size(), result.data());
315
0
        if (ret == -1) return false;
316
0
        result.resize(ret);
317
0
        return true;
318
0
    }
319
320
    /** Get the serialized size in bytes for this (valid) Minisketch object.. */
321
0
    size_t GetSerializedSize() const noexcept { return minisketch_serialized_size(m_minisketch.get()); }
322
323
    /** Serialize this (valid) Minisketch object as a byte vector. */
324
    std::vector<unsigned char> Serialize() const
325
0
    {
326
0
        std::vector<unsigned char> result(GetSerializedSize());
327
0
        minisketch_serialize(m_minisketch.get(), result.data());
328
0
        return result;
329
0
    }
330
331
    /** Deserialize into this (valid) Minisketch from an object containing its bytes (which has data()
332
     *  and size() members). */
333
    template<typename T>
334
    Minisketch& Deserialize(
335
        const T& obj,
336
        typename std::enable_if<
337
            std::is_convertible<typename std::remove_pointer<decltype(obj.data())>::type (*)[], const unsigned char (*)[]>::value &&
338
            std::is_convertible<decltype(obj.size()), std::size_t>::value,
339
            std::nullptr_t
340
        >::type = nullptr) noexcept
341
0
    {
342
0
        assert(GetSerializedSize() == obj.size());
343
0
        minisketch_deserialize(m_minisketch.get(), obj.data());
344
0
        return *this;
345
0
    }
346
347
#if __cplusplus >= 201703L
348
    /** C++17 only: like Decode(), but up to a specified number of elements into an optional vector. */
349
    std::optional<std::vector<uint64_t>> Decode(size_t max_elements) const
350
0
    {
351
0
        std::vector<uint64_t> result(max_elements);
352
0
        ssize_t ret = minisketch_decode(m_minisketch.get(), max_elements, result.data());
353
0
        if (ret == -1) return {};
354
0
        result.resize(ret);
355
0
        return result;
356
0
    }
357
358
    /** C++17 only: similar to Decode(), but with specified false positive probability. */
359
    std::optional<std::vector<uint64_t>> DecodeFP(uint32_t fpbits) const
360
0
    {
361
0
        return Decode(ComputeMaxElements(GetBits(), GetCapacity(), fpbits));
362
0
    }
363
#endif // __cplusplus >= 201703L
364
};
365
#endif // __cplusplus >= 201103L
366
#endif // __cplusplus
367
368
#endif  // _MINISKETCH_H_