datasketches 0.5.1 → 0.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/ext/datasketches/cpc_wrapper.cpp +8 -9
- data/ext/datasketches/fi_wrapper.cpp +4 -5
- data/ext/datasketches/hll_wrapper.cpp +9 -10
- data/ext/datasketches/kll_wrapper.cpp +13 -12
- data/ext/datasketches/theta_wrapper.cpp +11 -13
- data/ext/datasketches/vo_wrapper.cpp +1 -2
- data/lib/datasketches/version.rb +1 -1
- data/vendor/datasketches-cpp/CMakeLists.txt +5 -3
- data/vendor/datasketches-cpp/CODE_OF_CONDUCT.md +1 -1
- data/vendor/datasketches-cpp/LICENSE +14 -0
- data/vendor/datasketches-cpp/README.md +67 -73
- data/vendor/datasketches-cpp/benchmarks/CMakeLists.txt +52 -0
- data/vendor/datasketches-cpp/benchmarks/benchmark_count_min_sketch.cpp +153 -0
- data/vendor/datasketches-cpp/benchmarks/benchmark_count_min_sketch_serialization.cpp +161 -0
- data/vendor/datasketches-cpp/common/CMakeLists.txt +1 -0
- data/vendor/datasketches-cpp/common/include/binomial_bounds.hpp +2 -2
- data/vendor/datasketches-cpp/common/include/fdlibm_log.hpp +101 -0
- data/vendor/datasketches-cpp/common/include/serde.hpp +6 -0
- data/vendor/datasketches-cpp/common/test/CMakeLists.txt +44 -2
- data/vendor/datasketches-cpp/common/test/binomial_bounds_test.cpp +279 -0
- data/vendor/datasketches-cpp/common/test/deserialize_hardening_test.cpp +188 -0
- data/vendor/datasketches-cpp/count/include/count_min.hpp +17 -4
- data/vendor/datasketches-cpp/count/include/count_min_impl.hpp +65 -83
- data/vendor/datasketches-cpp/count/test/count_min_test.cpp +63 -7
- data/vendor/datasketches-cpp/cpc/include/compression_data.hpp +2 -0
- data/vendor/datasketches-cpp/cpc/include/cpc_compressor_impl.hpp +16 -7
- data/vendor/datasketches-cpp/cpc/include/cpc_sketch.hpp +1 -0
- data/vendor/datasketches-cpp/cpc/include/cpc_sketch_impl.hpp +54 -36
- data/vendor/datasketches-cpp/cpc/include/cpc_union_impl.hpp +27 -27
- data/vendor/datasketches-cpp/cpc/include/cpc_util.hpp +7 -7
- data/vendor/datasketches-cpp/cpc/include/icon_estimator.hpp +5 -5
- data/vendor/datasketches-cpp/cpc/include/u32_table_impl.hpp +16 -16
- data/vendor/datasketches-cpp/cpc/test/cpc_sketch_test.cpp +35 -0
- data/vendor/datasketches-cpp/fi/include/frequent_items_sketch.hpp +28 -3
- data/vendor/datasketches-cpp/fi/include/frequent_items_sketch_impl.hpp +41 -25
- data/vendor/datasketches-cpp/fi/include/reverse_purge_hash_map.hpp +3 -1
- data/vendor/datasketches-cpp/fi/include/reverse_purge_hash_map_impl.hpp +10 -5
- data/vendor/datasketches-cpp/fi/test/frequent_items_sketch_serialize_for_java.cpp +24 -0
- data/vendor/datasketches-cpp/fi/test/frequent_items_sketch_test.cpp +116 -0
- data/vendor/datasketches-cpp/filters/include/bloom_filter.hpp +1 -1
- data/vendor/datasketches-cpp/filters/include/bloom_filter_impl.hpp +32 -12
- data/vendor/datasketches-cpp/filters/test/bloom_filter_test.cpp +28 -1
- data/vendor/datasketches-cpp/hll/include/CouponHashSet-internal.hpp +1 -2
- data/vendor/datasketches-cpp/hll/include/CouponList-internal.hpp +19 -5
- data/vendor/datasketches-cpp/hll/include/CubicInterpolation-internal.hpp +4 -4
- data/vendor/datasketches-cpp/hll/include/HarmonicNumbers-internal.hpp +2 -1
- data/vendor/datasketches-cpp/hll/include/Hll4Array-internal.hpp +4 -4
- data/vendor/datasketches-cpp/hll/include/Hll4Array.hpp +1 -1
- data/vendor/datasketches-cpp/hll/include/Hll6Array-internal.hpp +3 -3
- data/vendor/datasketches-cpp/hll/include/Hll6Array.hpp +1 -1
- data/vendor/datasketches-cpp/hll/include/Hll8Array-internal.hpp +6 -3
- data/vendor/datasketches-cpp/hll/include/Hll8Array.hpp +1 -1
- data/vendor/datasketches-cpp/hll/include/HllArray-internal.hpp +35 -29
- data/vendor/datasketches-cpp/hll/include/HllArray.hpp +1 -1
- data/vendor/datasketches-cpp/hll/include/HllSketch-internal.hpp +3 -5
- data/vendor/datasketches-cpp/hll/include/HllSketchImpl-internal.hpp +5 -11
- data/vendor/datasketches-cpp/hll/include/HllSketchImpl.hpp +2 -4
- data/vendor/datasketches-cpp/hll/include/HllSketchImplFactory.hpp +11 -10
- data/vendor/datasketches-cpp/hll/include/HllUnion-internal.hpp +17 -8
- data/vendor/datasketches-cpp/hll/include/HllUtil.hpp +12 -3
- data/vendor/datasketches-cpp/hll/include/coupon_iterator-internal.hpp +2 -2
- data/vendor/datasketches-cpp/hll/include/coupon_iterator.hpp +3 -0
- data/vendor/datasketches-cpp/hll/include/hll.hpp +11 -4
- data/vendor/datasketches-cpp/hll/include/hll.private.hpp +19 -0
- data/vendor/datasketches-cpp/hll/test/CMakeLists.txt +2 -0
- data/vendor/datasketches-cpp/hll/test/CouponListTest.cpp +64 -0
- data/vendor/datasketches-cpp/hll/test/HllFullSizeTest.cpp +137 -0
- data/vendor/datasketches-cpp/hll/test/HllKxqRebuildTest.cpp +150 -0
- data/vendor/datasketches-cpp/hll/test/HllSketchTest.cpp +3 -3
- data/vendor/datasketches-cpp/hll/test/HllUnionTest.cpp +71 -1
- data/vendor/datasketches-cpp/kll/include/kll_helper_impl.hpp +10 -10
- data/vendor/datasketches-cpp/kll/include/kll_sketch.hpp +10 -1
- data/vendor/datasketches-cpp/kll/include/kll_sketch_impl.hpp +33 -24
- data/vendor/datasketches-cpp/kll/test/kll_sketch_deserialize_from_java_test.cpp +24 -0
- data/vendor/datasketches-cpp/kll/test/kll_sketch_serialize_for_java.cpp +10 -0
- data/vendor/datasketches-cpp/quantiles/include/quantiles_sketch.hpp +11 -2
- data/vendor/datasketches-cpp/quantiles/include/quantiles_sketch_impl.hpp +24 -15
- data/vendor/datasketches-cpp/req/include/req_sketch.hpp +9 -0
- data/vendor/datasketches-cpp/req/include/req_sketch_impl.hpp +24 -15
- data/vendor/datasketches-cpp/req/test/req_sketch_deserialize_from_java_test.cpp +46 -0
- data/vendor/datasketches-cpp/req/test/req_sketch_serialize_for_java.cpp +20 -0
- data/vendor/datasketches-cpp/req/test/req_sketch_test.cpp +70 -0
- data/vendor/datasketches-cpp/sampling/include/ebpps_sample_impl.hpp +17 -8
- data/vendor/datasketches-cpp/sampling/include/ebpps_sketch.hpp +13 -0
- data/vendor/datasketches-cpp/sampling/include/var_opt_sketch.hpp +10 -1
- data/vendor/datasketches-cpp/sampling/include/var_opt_sketch_impl.hpp +6 -7
- data/vendor/datasketches-cpp/sampling/include/var_opt_union.hpp +5 -1
- data/vendor/datasketches-cpp/sampling/include/var_opt_union_impl.hpp +2 -1
- data/vendor/datasketches-cpp/sampling/test/ebpps_allocation_test.cpp +1 -1
- data/vendor/datasketches-cpp/sampling/test/ebpps_sketch_test.cpp +2 -2
- data/vendor/datasketches-cpp/sampling/test/var_opt_allocation_test.cpp +1 -1
- data/vendor/datasketches-cpp/sampling/test/var_opt_sketch_test.cpp +10 -4
- data/vendor/datasketches-cpp/sampling/test/var_opt_union_test.cpp +12 -0
- data/vendor/datasketches-cpp/tdigest/include/tdigest.hpp +38 -2
- data/vendor/datasketches-cpp/tdigest/include/tdigest_impl.hpp +168 -9
- data/vendor/datasketches-cpp/tdigest/test/CMakeLists.txt +1 -0
- data/vendor/datasketches-cpp/tdigest/test/tdigest_iterator_test.cpp +274 -0
- data/vendor/datasketches-cpp/tdigest/test/tdigest_test.cpp +275 -0
- data/vendor/datasketches-cpp/theta/include/compact_theta_sketch_parser.hpp +2 -0
- data/vendor/datasketches-cpp/theta/include/compact_theta_sketch_parser_impl.hpp +24 -3
- data/vendor/datasketches-cpp/theta/include/theta_constants.hpp +4 -2
- data/vendor/datasketches-cpp/theta/include/theta_helpers.hpp +32 -0
- data/vendor/datasketches-cpp/theta/include/theta_set_difference_base_impl.hpp +4 -2
- data/vendor/datasketches-cpp/theta/include/theta_sketch.hpp +22 -4
- data/vendor/datasketches-cpp/theta/include/theta_sketch_impl.hpp +60 -38
- data/vendor/datasketches-cpp/theta/include/theta_union_base_impl.hpp +2 -6
- data/vendor/datasketches-cpp/theta/include/theta_update_sketch_base_impl.hpp +2 -2
- data/vendor/datasketches-cpp/theta/test/bit_packing_test.cpp +50 -0
- data/vendor/datasketches-cpp/theta/test/theta_a_not_b_test.cpp +22 -0
- data/vendor/datasketches-cpp/theta/test/theta_sketch_test.cpp +315 -0
- data/vendor/datasketches-cpp/tools/rat-check.sh +68 -0
- data/vendor/datasketches-cpp/tuple/include/array_tuple_sketch.hpp +35 -4
- data/vendor/datasketches-cpp/tuple/include/array_tuple_sketch_impl.hpp +2 -2
- data/vendor/datasketches-cpp/tuple/include/tuple_sketch.hpp +41 -0
- data/vendor/datasketches-cpp/tuple/include/tuple_sketch_impl.hpp +5 -4
- data/vendor/datasketches-cpp/tuple/test/tuple_sketch_test.cpp +59 -0
- data/vendor/datasketches-cpp/version.cfg.in +1 -1
- metadata +12 -2
|
@@ -22,6 +22,8 @@
|
|
|
22
22
|
#include <sstream>
|
|
23
23
|
#include <vector>
|
|
24
24
|
#include <stdexcept>
|
|
25
|
+
#include <algorithm>
|
|
26
|
+
#include <cstring>
|
|
25
27
|
|
|
26
28
|
#include <catch2/catch.hpp>
|
|
27
29
|
#include <theta_sketch.hpp>
|
|
@@ -57,6 +59,48 @@ TEST_CASE("theta sketch: empty", "[theta_sketch]") {
|
|
|
57
59
|
REQUIRE(update_sketch.compact(false).is_ordered());
|
|
58
60
|
}
|
|
59
61
|
|
|
62
|
+
TEST_CASE("theta sketch: min lg_k", "[theta_sketch]") {
|
|
63
|
+
// lg_k = 4 (nominal 16) is the smallest allowed nominal size, matching Java's
|
|
64
|
+
// ThetaUtil.MIN_LG_NOM_LONGS. Anything below it must throw.
|
|
65
|
+
REQUIRE(theta_constants::MIN_LG_K == 4);
|
|
66
|
+
REQUIRE_THROWS_AS(update_theta_sketch::builder().set_lg_k(theta_constants::MIN_LG_K - 1),
|
|
67
|
+
std::invalid_argument);
|
|
68
|
+
update_theta_sketch min_sketch = update_theta_sketch::builder().set_lg_k(theta_constants::MIN_LG_K).build();
|
|
69
|
+
REQUIRE(min_sketch.get_lg_k() == theta_constants::MIN_LG_K);
|
|
70
|
+
|
|
71
|
+
// update well past the nominal size to force estimation mode and exercise the rebuild path,
|
|
72
|
+
// tracking the peak number of retained entries seen between rebuilds.
|
|
73
|
+
const int n = 10000;
|
|
74
|
+
uint32_t max_retained = 0;
|
|
75
|
+
for (int i = 0; i < n; ++i) {
|
|
76
|
+
min_sketch.update(i);
|
|
77
|
+
max_retained = std::max(max_retained, min_sketch.get_num_retained());
|
|
78
|
+
}
|
|
79
|
+
REQUIRE(min_sketch.is_estimation_mode());
|
|
80
|
+
REQUIRE(min_sketch.get_theta() < 1.0);
|
|
81
|
+
|
|
82
|
+
// The internal hash table is floored at MIN_LG_ARR (5, i.e. 32 slots), one lg above the
|
|
83
|
+
// nominal size. This is exactly what MIN_LG_ARR guarantees: between rebuilds the sketch holds
|
|
84
|
+
// more than the nominal 2^MIN_LG_K (16) entries, but never more than the 2^MIN_LG_ARR (32)
|
|
85
|
+
// slots of the table. Were the table sized to the nominal 16, it could not retain more than 16.
|
|
86
|
+
REQUIRE(max_retained > (1 << theta_constants::MIN_LG_K));
|
|
87
|
+
REQUIRE(max_retained <= (1 << theta_constants::MIN_LG_ARR));
|
|
88
|
+
|
|
89
|
+
// the true count is bracketed by the 2-standard-deviation confidence bounds
|
|
90
|
+
REQUIRE(min_sketch.get_lower_bound(2) <= n);
|
|
91
|
+
REQUIRE(min_sketch.get_upper_bound(2) >= n);
|
|
92
|
+
|
|
93
|
+
// trimming reduces the sketch to exactly the nominal number of entries (2^4 = 16)
|
|
94
|
+
min_sketch.trim();
|
|
95
|
+
REQUIRE(min_sketch.get_num_retained() == (1 << theta_constants::MIN_LG_K));
|
|
96
|
+
|
|
97
|
+
// a compacted min-size sketch round trips through serialization
|
|
98
|
+
auto bytes = min_sketch.compact().serialize();
|
|
99
|
+
compact_theta_sketch deserialized = compact_theta_sketch::deserialize(bytes.data(), bytes.size());
|
|
100
|
+
REQUIRE(deserialized.get_num_retained() == min_sketch.get_num_retained());
|
|
101
|
+
REQUIRE(deserialized.get_estimate() == min_sketch.get_estimate());
|
|
102
|
+
}
|
|
103
|
+
|
|
60
104
|
TEST_CASE("theta sketch: non empty no retained keys", "[theta_sketch]") {
|
|
61
105
|
update_theta_sketch update_sketch = update_theta_sketch::builder().set_p(0.001f).build();
|
|
62
106
|
update_sketch.update(1);
|
|
@@ -109,6 +153,196 @@ TEST_CASE("theta sketch: single item", "[theta_sketch]") {
|
|
|
109
153
|
REQUIRE(update_sketch.compact(false).is_ordered());
|
|
110
154
|
}
|
|
111
155
|
|
|
156
|
+
// Java writes exactly these images (EmptyCompactSketch.EMPTY_COMPACT_SKETCH_ARR and SingleItemSketch),
|
|
157
|
+
// and the cross-language tests compare the two languages byte for byte.
|
|
158
|
+
// An empty sketch carries no hashes, so its seed hash bytes are zero; a single item keeps its seed hash.
|
|
159
|
+
static std::vector<uint8_t> serialized_bytes(const compact_theta_sketch& sketch) {
|
|
160
|
+
auto bytes = sketch.serialize();
|
|
161
|
+
return std::vector<uint8_t>(bytes.begin(), bytes.end());
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
static std::vector<uint8_t> serialized_stream(const compact_theta_sketch& sketch) {
|
|
165
|
+
std::stringstream s(std::ios::in | std::ios::out | std::ios::binary);
|
|
166
|
+
sketch.serialize(s);
|
|
167
|
+
const std::string str = s.str();
|
|
168
|
+
return std::vector<uint8_t>(str.begin(), str.end());
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
static std::vector<uint8_t> serialized_compressed(const compact_theta_sketch& sketch) {
|
|
172
|
+
auto bytes = sketch.serialize_compressed();
|
|
173
|
+
return std::vector<uint8_t>(bytes.begin(), bytes.end());
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
TEST_CASE("theta sketch: empty serialized image", "[theta_sketch]") {
|
|
177
|
+
const uint64_t seed = 12345; // not the default, to prove the seed hash is not written
|
|
178
|
+
auto sketch = update_theta_sketch::builder().set_seed(seed).build().compact();
|
|
179
|
+
const std::vector<uint8_t> expected = {1, 3, 3, 0, 0, 0x1E, 0, 0};
|
|
180
|
+
REQUIRE(serialized_bytes(sketch) == expected);
|
|
181
|
+
REQUIRE(serialized_stream(sketch) == expected);
|
|
182
|
+
REQUIRE(serialized_compressed(sketch) == expected); // no compressed form of an empty sketch
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
TEST_CASE("theta sketch: single item serialized image", "[theta_sketch]") {
|
|
186
|
+
const uint64_t seed = 12345;
|
|
187
|
+
const uint16_t seed_hash = compute_seed_hash(seed);
|
|
188
|
+
update_theta_sketch update_sketch = update_theta_sketch::builder().set_seed(seed).build();
|
|
189
|
+
update_sketch.update(1);
|
|
190
|
+
auto sketch = update_sketch.compact();
|
|
191
|
+
const uint64_t hash = *sketch.begin();
|
|
192
|
+
|
|
193
|
+
// flags: single item | ordered | compact | read-only, then seed hash and the one hash, little-endian
|
|
194
|
+
std::vector<uint8_t> expected = {1, 3, 3, 0, 0, 0x3A,
|
|
195
|
+
static_cast<uint8_t>(seed_hash), static_cast<uint8_t>(seed_hash >> 8)};
|
|
196
|
+
for (unsigned i = 0; i < 8; ++i) expected.push_back(static_cast<uint8_t>(hash >> (i * 8)));
|
|
197
|
+
REQUIRE(serialized_bytes(sketch) == expected);
|
|
198
|
+
REQUIRE(serialized_stream(sketch) == expected);
|
|
199
|
+
REQUIRE(serialized_compressed(sketch) == expected); // no compressed form of a single item
|
|
200
|
+
|
|
201
|
+
// the single item flag is informational: the reader does not depend on it
|
|
202
|
+
auto deserialized = compact_theta_sketch::deserialize(expected.data(), expected.size(), seed);
|
|
203
|
+
REQUIRE(deserialized.get_num_retained() == 1);
|
|
204
|
+
REQUIRE(*deserialized.begin() == hash);
|
|
205
|
+
expected[5] = 0x1A; // as written before this flag existed
|
|
206
|
+
deserialized = compact_theta_sketch::deserialize(expected.data(), expected.size(), seed);
|
|
207
|
+
REQUIRE(deserialized.get_num_retained() == 1);
|
|
208
|
+
REQUIRE(*deserialized.begin() == hash);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
TEST_CASE("theta sketch: one entry in estimation mode is not a single item", "[theta_sketch]") {
|
|
212
|
+
update_theta_sketch update_sketch = update_theta_sketch::builder().set_p(0.5f).build();
|
|
213
|
+
for (uint64_t i = 0; update_sketch.get_num_retained() < 1; ++i) update_sketch.update(i);
|
|
214
|
+
auto sketch = update_sketch.compact();
|
|
215
|
+
REQUIRE(sketch.get_num_retained() == 1);
|
|
216
|
+
REQUIRE(sketch.is_estimation_mode());
|
|
217
|
+
const auto bytes = serialized_bytes(sketch);
|
|
218
|
+
REQUIRE(bytes[0] == 3); // theta is present, so this is the 3-long preamble
|
|
219
|
+
REQUIRE(bytes[5] == 0x1A); // ordered | compact | read-only, no single item flag
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
TEST_CASE("theta sketch: compact with trim, all four cases", "[theta_sketch]") {
|
|
223
|
+
update_theta_sketch update_sketch = update_theta_sketch::builder().build();
|
|
224
|
+
for (int i = 0; i < 8000; i++) update_sketch.update(i);
|
|
225
|
+
const uint32_t k = 1 << theta_constants::DEFAULT_LG_K;
|
|
226
|
+
// an update sketch retains more than k between rebuilds, so trimming has work to do
|
|
227
|
+
REQUIRE(update_sketch.get_num_retained() > k);
|
|
228
|
+
const uint32_t retained_before = update_sketch.get_num_retained();
|
|
229
|
+
const uint64_t theta_before = update_sketch.get_theta64();
|
|
230
|
+
|
|
231
|
+
// the trimmed result is what trim() + compact() would produce
|
|
232
|
+
update_theta_sketch trimmed = update_sketch;
|
|
233
|
+
trimmed.trim();
|
|
234
|
+
compact_theta_sketch expected = trimmed.compact(true);
|
|
235
|
+
const std::vector<uint64_t> expected_entries(expected.begin(), expected.end());
|
|
236
|
+
|
|
237
|
+
// case 1: ordered, not trimmed (the default) keeps every retained entry
|
|
238
|
+
compact_theta_sketch c1 = update_sketch.compact(true, false);
|
|
239
|
+
REQUIRE(c1.is_ordered());
|
|
240
|
+
REQUIRE(c1.get_num_retained() == retained_before);
|
|
241
|
+
REQUIRE(c1.get_theta64() == theta_before);
|
|
242
|
+
REQUIRE(std::is_sorted(c1.begin(), c1.end()));
|
|
243
|
+
|
|
244
|
+
// case 2: unordered, not trimmed
|
|
245
|
+
compact_theta_sketch c2 = update_sketch.compact(false, false);
|
|
246
|
+
REQUIRE_FALSE(c2.is_ordered());
|
|
247
|
+
REQUIRE(c2.get_num_retained() == retained_before);
|
|
248
|
+
REQUIRE(c2.get_theta64() == theta_before);
|
|
249
|
+
|
|
250
|
+
// case 3: ordered and trimmed
|
|
251
|
+
compact_theta_sketch c3 = update_sketch.compact(true, true);
|
|
252
|
+
REQUIRE(c3.is_ordered());
|
|
253
|
+
REQUIRE(c3.get_num_retained() == k);
|
|
254
|
+
REQUIRE(c3.get_theta64() < theta_before); // new theta is the k-th smallest hash
|
|
255
|
+
REQUIRE(c3.get_theta64() == expected.get_theta64());
|
|
256
|
+
REQUIRE(std::is_sorted(c3.begin(), c3.end()));
|
|
257
|
+
REQUIRE(std::vector<uint64_t>(c3.begin(), c3.end()) == expected_entries);
|
|
258
|
+
// every retained hash is strictly below the new theta
|
|
259
|
+
for (auto h: c3) REQUIRE(h < c3.get_theta64());
|
|
260
|
+
|
|
261
|
+
// case 4: unordered and trimmed: same set and theta, no sort
|
|
262
|
+
compact_theta_sketch c4 = update_sketch.compact(false, true);
|
|
263
|
+
REQUIRE_FALSE(c4.is_ordered());
|
|
264
|
+
REQUIRE(c4.get_num_retained() == k);
|
|
265
|
+
REQUIRE(c4.get_theta64() == expected.get_theta64());
|
|
266
|
+
std::vector<uint64_t> c4_entries(c4.begin(), c4.end());
|
|
267
|
+
std::sort(c4_entries.begin(), c4_entries.end());
|
|
268
|
+
REQUIRE(c4_entries == expected_entries);
|
|
269
|
+
|
|
270
|
+
// the source sketch must be untouched by any of the four
|
|
271
|
+
REQUIRE(update_sketch.get_num_retained() == retained_before);
|
|
272
|
+
REQUIRE(update_sketch.get_theta64() == theta_before);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
TEST_CASE("theta sketch: compact with trim widens the bounds in estimation mode", "[theta_sketch]") {
|
|
276
|
+
// trimming is lossy even when the source is already estimating: it discards
|
|
277
|
+
// retained entries, and the relative error scales with 1 / sqrt(retained)
|
|
278
|
+
const uint32_t k = 1 << theta_constants::DEFAULT_LG_K;
|
|
279
|
+
update_theta_sketch sketch = update_theta_sketch::builder().build();
|
|
280
|
+
for (int i = 0; i < 40000; i++) sketch.update(i);
|
|
281
|
+
REQUIRE(sketch.is_estimation_mode());
|
|
282
|
+
REQUIRE(sketch.get_num_retained() > k);
|
|
283
|
+
|
|
284
|
+
compact_theta_sketch plain = sketch.compact(true, false);
|
|
285
|
+
compact_theta_sketch trimmed = sketch.compact(true, true);
|
|
286
|
+
REQUIRE(trimmed.get_num_retained() == k);
|
|
287
|
+
REQUIRE(plain.get_num_retained() > trimmed.get_num_retained());
|
|
288
|
+
|
|
289
|
+
// fewer retained entries -> strictly wider confidence interval
|
|
290
|
+
const double plain_width = plain.get_upper_bound(2) - plain.get_lower_bound(2);
|
|
291
|
+
const double trimmed_width = trimmed.get_upper_bound(2) - trimmed.get_lower_bound(2);
|
|
292
|
+
REQUIRE(trimmed_width > plain_width);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
TEST_CASE("theta sketch: compact with trim converts exact mode to estimation", "[theta_sketch]") {
|
|
296
|
+
// an update sketch can retain far more than k entries while still in exact mode:
|
|
297
|
+
// nothing has been evicted yet, so theta is still 1.0 and the count is exact
|
|
298
|
+
const uint32_t k = 1 << theta_constants::DEFAULT_LG_K;
|
|
299
|
+
update_theta_sketch sketch = update_theta_sketch::builder().build();
|
|
300
|
+
const int n = 5000;
|
|
301
|
+
for (int i = 0; i < n; i++) sketch.update(i);
|
|
302
|
+
REQUIRE(sketch.get_num_retained() > k);
|
|
303
|
+
REQUIRE_FALSE(sketch.is_estimation_mode());
|
|
304
|
+
REQUIRE(sketch.get_theta() == 1.0);
|
|
305
|
+
|
|
306
|
+
// not trimming keeps every entry and the exact count
|
|
307
|
+
compact_theta_sketch exact = sketch.compact(true, false);
|
|
308
|
+
REQUIRE_FALSE(exact.is_estimation_mode());
|
|
309
|
+
REQUIRE(exact.get_num_retained() == static_cast<uint32_t>(n));
|
|
310
|
+
REQUIRE(exact.get_estimate() == Approx(n));
|
|
311
|
+
|
|
312
|
+
// trimming is lossy: it discards real data, lowers theta below 1.0 and the
|
|
313
|
+
// result is an estimate carrying error where the source held an exact count
|
|
314
|
+
compact_theta_sketch trimmed = sketch.compact(true, true);
|
|
315
|
+
REQUIRE(trimmed.is_estimation_mode());
|
|
316
|
+
REQUIRE(trimmed.get_num_retained() == k);
|
|
317
|
+
REQUIRE(trimmed.get_theta() < 1.0);
|
|
318
|
+
REQUIRE(trimmed.get_estimate() != Approx(n));
|
|
319
|
+
// the estimate is still sound: n must lie inside the 3-sigma bounds
|
|
320
|
+
REQUIRE(trimmed.get_lower_bound(3) <= n);
|
|
321
|
+
REQUIRE(trimmed.get_upper_bound(3) >= n);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
TEST_CASE("theta sketch: compact with trim, empty and below k", "[theta_sketch]") {
|
|
325
|
+
// empty: trimming changes nothing
|
|
326
|
+
update_theta_sketch empty_sketch = update_theta_sketch::builder().build();
|
|
327
|
+
compact_theta_sketch empty_result = empty_sketch.compact(true, true);
|
|
328
|
+
REQUIRE(empty_result.is_empty());
|
|
329
|
+
REQUIRE(empty_result.get_num_retained() == 0);
|
|
330
|
+
REQUIRE(empty_result.is_ordered());
|
|
331
|
+
|
|
332
|
+
// below k: nothing to trim, theta and entries are preserved
|
|
333
|
+
update_theta_sketch small = update_theta_sketch::builder().build();
|
|
334
|
+
for (int i = 0; i < 100; i++) small.update(i);
|
|
335
|
+
REQUIRE_FALSE(small.is_estimation_mode());
|
|
336
|
+
|
|
337
|
+
compact_theta_sketch small_result = small.compact(true, true);
|
|
338
|
+
REQUIRE_FALSE(small_result.is_estimation_mode());
|
|
339
|
+
REQUIRE(small_result.get_num_retained() == 100);
|
|
340
|
+
REQUIRE(small_result.get_theta64() == small.get_theta64());
|
|
341
|
+
REQUIRE(small_result.get_estimate() == Approx(100.0));
|
|
342
|
+
REQUIRE(std::is_sorted(small_result.begin(), small_result.end()));
|
|
343
|
+
REQUIRE_FALSE(small.compact(false, true).is_ordered());
|
|
344
|
+
}
|
|
345
|
+
|
|
112
346
|
TEST_CASE("theta sketch: resize exact", "[theta_sketch]") {
|
|
113
347
|
update_theta_sketch update_sketch = update_theta_sketch::builder().build();
|
|
114
348
|
for (int i = 0; i < 2000; i++) update_sketch.update(i);
|
|
@@ -337,6 +571,50 @@ TEST_CASE("theta sketch: deserialize estimation mode buffer overrun", "[theta_sk
|
|
|
337
571
|
REQUIRE_THROWS_AS(compact_theta_sketch::deserialize(bytes.data(), bytes.size() - 1), std::out_of_range);
|
|
338
572
|
}
|
|
339
573
|
|
|
574
|
+
// The header fields of serial versions 1 to 3 that follow the first 8 bytes must not be read
|
|
575
|
+
// before the size of the buffer is checked. Each buffer below holds exactly the preamble bytes,
|
|
576
|
+
// so that reading beyond them would be an out-of-bounds read, and is followed by the bytes
|
|
577
|
+
// that would make the sketch look empty if they were read.
|
|
578
|
+
TEST_CASE("theta sketch: deserialize truncated preamble", "[theta_sketch]") {
|
|
579
|
+
const uint16_t seed_hash = compute_seed_hash(DEFAULT_SEED);
|
|
580
|
+
const uint8_t seed_hash_lo = seed_hash & 0xff;
|
|
581
|
+
const uint8_t seed_hash_hi = seed_hash >> 8;
|
|
582
|
+
const uint8_t max_theta[8] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f};
|
|
583
|
+
|
|
584
|
+
// serial version 1: three preamble longs, num_entries = 0 and theta = max would be empty
|
|
585
|
+
std::vector<uint8_t> v1 = {3, 1, 3, 0, 0, 0x1a, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
586
|
+
v1.insert(v1.end(), max_theta, max_theta + 8);
|
|
587
|
+
for (size_t size: {8, 16}) {
|
|
588
|
+
const std::vector<uint8_t> bytes(v1.begin(), v1.begin() + size);
|
|
589
|
+
REQUIRE_THROWS_AS(compact_theta_sketch::deserialize(bytes.data(), bytes.size()), std::out_of_range);
|
|
590
|
+
REQUIRE_THROWS_AS(wrapped_compact_theta_sketch::wrap(bytes.data(), bytes.size()), std::out_of_range);
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
// serial version 2: two preamble longs, num_entries = 0 would be empty
|
|
594
|
+
std::vector<uint8_t> v2_exact = {2, 2, 3, 0, 0, 0x1a, seed_hash_lo, seed_hash_hi, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
595
|
+
{
|
|
596
|
+
const std::vector<uint8_t> bytes(v2_exact.begin(), v2_exact.begin() + 8);
|
|
597
|
+
REQUIRE_THROWS_AS(compact_theta_sketch::deserialize(bytes.data(), bytes.size()), std::out_of_range);
|
|
598
|
+
REQUIRE_THROWS_AS(wrapped_compact_theta_sketch::wrap(bytes.data(), bytes.size()), std::out_of_range);
|
|
599
|
+
}
|
|
600
|
+
REQUIRE(compact_theta_sketch::deserialize(v2_exact.data(), v2_exact.size()).is_empty());
|
|
601
|
+
|
|
602
|
+
// serial version 2: three preamble longs, num_entries = 0 and theta = max would be empty
|
|
603
|
+
std::vector<uint8_t> v2_estimation = {3, 2, 3, 0, 0, 0x1a, seed_hash_lo, seed_hash_hi, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
604
|
+
v2_estimation.insert(v2_estimation.end(), max_theta, max_theta + 8);
|
|
605
|
+
for (size_t size: {8, 16}) {
|
|
606
|
+
const std::vector<uint8_t> bytes(v2_estimation.begin(), v2_estimation.begin() + size);
|
|
607
|
+
REQUIRE_THROWS_AS(compact_theta_sketch::deserialize(bytes.data(), bytes.size()), std::out_of_range);
|
|
608
|
+
REQUIRE_THROWS_AS(wrapped_compact_theta_sketch::wrap(bytes.data(), bytes.size()), std::out_of_range);
|
|
609
|
+
}
|
|
610
|
+
REQUIRE(compact_theta_sketch::deserialize(v2_estimation.data(), v2_estimation.size()).is_empty());
|
|
611
|
+
|
|
612
|
+
// serial version 3: two preamble longs, not empty
|
|
613
|
+
const std::vector<uint8_t> v3 = {2, 3, 3, 0, 0, 0x1a, seed_hash_lo, seed_hash_hi};
|
|
614
|
+
REQUIRE_THROWS_AS(compact_theta_sketch::deserialize(v3.data(), v3.size()), std::out_of_range);
|
|
615
|
+
REQUIRE_THROWS_AS(wrapped_compact_theta_sketch::wrap(v3.data(), v3.size()), std::out_of_range);
|
|
616
|
+
}
|
|
617
|
+
|
|
340
618
|
TEST_CASE("theta sketch: conversion constructor and wrapped compact", "[theta_sketch]") {
|
|
341
619
|
update_theta_sketch update_sketch = update_theta_sketch::builder().build();
|
|
342
620
|
const int n = 8192;
|
|
@@ -625,4 +903,41 @@ TEST_CASE("max serialized size", "[theta_sketch]") {
|
|
|
625
903
|
REQUIRE(max_size_bytes == compact_theta_sketch::get_max_serialized_size_bytes(lg_k));
|
|
626
904
|
}
|
|
627
905
|
|
|
906
|
+
TEST_CASE("theta sketch: deserialize v4 corrupt header", "[theta_sketch]") {
|
|
907
|
+
update_theta_sketch update_sketch = update_theta_sketch::builder().build();
|
|
908
|
+
for (int i = 0; i < 100; ++i) update_sketch.update(i);
|
|
909
|
+
const auto bytes = update_sketch.compact().serialize_compressed();
|
|
910
|
+
REQUIRE(bytes[1] == 4);
|
|
911
|
+
for (uint8_t entry_bits: {0, 64, 255}) {
|
|
912
|
+
auto corrupt = bytes;
|
|
913
|
+
corrupt[3] = entry_bits;
|
|
914
|
+
REQUIRE_THROWS_AS(compact_theta_sketch::deserialize(corrupt.data(), corrupt.size()), std::invalid_argument);
|
|
915
|
+
std::stringstream s;
|
|
916
|
+
s.write(reinterpret_cast<const char*>(corrupt.data()), corrupt.size());
|
|
917
|
+
REQUIRE_THROWS_AS(compact_theta_sketch::deserialize(s), std::invalid_argument);
|
|
918
|
+
}
|
|
919
|
+
for (uint8_t num_entries_bytes: {0, 5, 255}) {
|
|
920
|
+
auto corrupt = bytes;
|
|
921
|
+
corrupt[4] = num_entries_bytes;
|
|
922
|
+
REQUIRE_THROWS_AS(compact_theta_sketch::deserialize(corrupt.data(), corrupt.size()), std::invalid_argument);
|
|
923
|
+
std::stringstream s;
|
|
924
|
+
s.write(reinterpret_cast<const char*>(corrupt.data()), corrupt.size());
|
|
925
|
+
REQUIRE_THROWS_AS(compact_theta_sketch::deserialize(s), std::invalid_argument);
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
TEST_CASE("theta sketch: deserialize v4 entry bits overflow", "[theta_sketch]") {
|
|
930
|
+
update_theta_sketch update_sketch = update_theta_sketch::builder().build();
|
|
931
|
+
for (int i = 0; i < 100; ++i) update_sketch.update(i);
|
|
932
|
+
auto bytes = update_sketch.compact().serialize_compressed();
|
|
933
|
+
REQUIRE(bytes[0] == 1); // exact mode, num_entries follows the first preamble long
|
|
934
|
+
// 8 bits * 2^29 entries = 2^32 bits wraps to 0 in 32-bit arithmetic
|
|
935
|
+
bytes.resize(12);
|
|
936
|
+
bytes[3] = 8;
|
|
937
|
+
bytes[4] = 4;
|
|
938
|
+
const uint32_t num_entries = 1U << 29;
|
|
939
|
+
std::memcpy(bytes.data() + 8, &num_entries, sizeof(num_entries));
|
|
940
|
+
REQUIRE_THROWS_AS(compact_theta_sketch::deserialize(bytes.data(), bytes.size()), std::out_of_range);
|
|
941
|
+
}
|
|
942
|
+
|
|
628
943
|
} /* namespace datasketches */
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# Licensed to the Apache Software Foundation (ASF) under one
|
|
4
|
+
# or more contributor license agreements. See the NOTICE file
|
|
5
|
+
# distributed with this work for additional information
|
|
6
|
+
# regarding copyright ownership. The ASF licenses this file
|
|
7
|
+
# to you under the Apache License, Version 2.0 (the
|
|
8
|
+
# "License"); you may not use this file except in compliance
|
|
9
|
+
# with the License. You may obtain a copy of the License at
|
|
10
|
+
#
|
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
#
|
|
13
|
+
# Unless required by applicable law or agreed to in writing,
|
|
14
|
+
# software distributed under the License is distributed on an
|
|
15
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
16
|
+
# KIND, either express or implied. See the License for the
|
|
17
|
+
# specific language governing permissions and limitations
|
|
18
|
+
# under the License.
|
|
19
|
+
#
|
|
20
|
+
# Runs the Apache Release Audit Tool over the tracked files of this repository
|
|
21
|
+
# and fails if any file lacks an approved license header.
|
|
22
|
+
#
|
|
23
|
+
# Only tracked files are examined: the tree is exported with `git archive`, so a
|
|
24
|
+
# local build directory or other untracked scratch cannot affect the result.
|
|
25
|
+
#
|
|
26
|
+
# Usage: tools/rat-check.sh [git-ref] (default: HEAD)
|
|
27
|
+
|
|
28
|
+
set -euo pipefail
|
|
29
|
+
|
|
30
|
+
RAT_VERSION="${RAT_VERSION:-0.16.1}"
|
|
31
|
+
RAT_JAR="${RAT_JAR:-}"
|
|
32
|
+
REF="${1:-HEAD}"
|
|
33
|
+
|
|
34
|
+
repo_root="$(git rev-parse --show-toplevel)"
|
|
35
|
+
cd "$repo_root"
|
|
36
|
+
|
|
37
|
+
work="$(mktemp -d)"
|
|
38
|
+
trap 'rm -rf "$work"' EXIT
|
|
39
|
+
|
|
40
|
+
if [[ -z "$RAT_JAR" ]]; then
|
|
41
|
+
RAT_JAR="$work/apache-rat.jar"
|
|
42
|
+
url="https://repo1.maven.org/maven2/org/apache/rat/apache-rat/${RAT_VERSION}/apache-rat-${RAT_VERSION}.jar"
|
|
43
|
+
echo "Downloading Apache RAT ${RAT_VERSION}"
|
|
44
|
+
curl -fsSL -o "$RAT_JAR" "$url"
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
mkdir -p "$work/src"
|
|
48
|
+
git archive "$REF" | tar -x -C "$work/src"
|
|
49
|
+
cp .rat-excludes "$work/src/.rat-excludes"
|
|
50
|
+
|
|
51
|
+
report="$work/rat-report.txt"
|
|
52
|
+
java -jar "$RAT_JAR" -E "$work/src/.rat-excludes" -d "$work/src" > "$report" 2>/dev/null
|
|
53
|
+
|
|
54
|
+
unknown="$(sed -n 's/^\([0-9][0-9]*\) Unknown Licenses$/\1/p' "$report" | head -1)"
|
|
55
|
+
unknown="${unknown:-0}"
|
|
56
|
+
|
|
57
|
+
if [[ "$unknown" -ne 0 ]]; then
|
|
58
|
+
echo
|
|
59
|
+
echo "Apache RAT found ${unknown} file(s) without an approved license header:"
|
|
60
|
+
awk '/^Files with unapproved licenses:/{f=1;next} f&&/^\*\*\*\*/{exit} f&&NF{print " " $0}' "$report" \
|
|
61
|
+
| sed "s|$work/src/||"
|
|
62
|
+
echo
|
|
63
|
+
echo "Add the ASF header to each file. If a file is third-party source that must"
|
|
64
|
+
echo "keep its own notice, add it to .rat-excludes AND record it in LICENSE."
|
|
65
|
+
exit 1
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
echo "Apache RAT: all tracked files carry an approved license header."
|
|
@@ -22,6 +22,8 @@
|
|
|
22
22
|
|
|
23
23
|
#include <vector>
|
|
24
24
|
#include <memory>
|
|
25
|
+
#include <type_traits>
|
|
26
|
+
#include <algorithm>
|
|
25
27
|
|
|
26
28
|
#include "serde.hpp"
|
|
27
29
|
#include "tuple_sketch.hpp"
|
|
@@ -34,17 +36,18 @@ class array {
|
|
|
34
36
|
public:
|
|
35
37
|
using value_type = T;
|
|
36
38
|
using allocator_type = Allocator;
|
|
39
|
+
using alloc_traits = std::allocator_traits<Allocator>;
|
|
37
40
|
|
|
38
|
-
explicit array(uint8_t size, T value, const Allocator& allocator = Allocator()):
|
|
41
|
+
explicit array(uint8_t size, const T& value, const Allocator& allocator = Allocator()):
|
|
39
42
|
allocator_(allocator), size_(size), array_(allocator_.allocate(size_)) {
|
|
40
|
-
std::
|
|
43
|
+
init_values(value, std::is_trivially_copyable<T>());
|
|
41
44
|
}
|
|
42
45
|
array(const array& other):
|
|
43
46
|
allocator_(other.allocator_),
|
|
44
47
|
size_(other.size_),
|
|
45
48
|
array_(allocator_.allocate(size_))
|
|
46
49
|
{
|
|
47
|
-
|
|
50
|
+
copy_from(other, std::is_trivially_copyable<T>());
|
|
48
51
|
}
|
|
49
52
|
array(array&& other) noexcept:
|
|
50
53
|
allocator_(std::move(other.allocator_)),
|
|
@@ -52,9 +55,13 @@ public:
|
|
|
52
55
|
array_(other.array_)
|
|
53
56
|
{
|
|
54
57
|
other.array_ = nullptr;
|
|
58
|
+
other.size_ = 0;
|
|
55
59
|
}
|
|
56
60
|
~array() {
|
|
57
|
-
if (array_ != nullptr)
|
|
61
|
+
if (array_ != nullptr) {
|
|
62
|
+
destroy_values(std::is_trivially_destructible<T>());
|
|
63
|
+
allocator_.deallocate(array_, size_);
|
|
64
|
+
}
|
|
58
65
|
}
|
|
59
66
|
array& operator=(const array& other) {
|
|
60
67
|
array copy(other);
|
|
@@ -75,10 +82,34 @@ public:
|
|
|
75
82
|
T* data() { return array_; }
|
|
76
83
|
const T* data() const { return array_; }
|
|
77
84
|
bool operator==(const array& other) const {
|
|
85
|
+
if (size_ != other.size_) return false;
|
|
78
86
|
for (uint8_t i = 0; i < size_; ++i) if (array_[i] != other.array_[i]) return false;
|
|
79
87
|
return true;
|
|
80
88
|
}
|
|
81
89
|
private:
|
|
90
|
+
void init_values(const T& value, std::true_type) {
|
|
91
|
+
std::fill(array_, array_ + size_, value);
|
|
92
|
+
}
|
|
93
|
+
void init_values(const T& value, std::false_type) {
|
|
94
|
+
for (uint8_t i = 0; i < size_; ++i) {
|
|
95
|
+
alloc_traits::construct(allocator_, array_ + i, value);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
void copy_from(const array& other, std::true_type) {
|
|
99
|
+
std::copy(other.array_, other.array_ + size_, array_);
|
|
100
|
+
}
|
|
101
|
+
void copy_from(const array& other, std::false_type) {
|
|
102
|
+
for (uint8_t i = 0; i < size_; ++i) {
|
|
103
|
+
alloc_traits::construct(allocator_, array_ + i, other.array_[i]);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
void destroy_values(std::true_type) {}
|
|
107
|
+
void destroy_values(std::false_type) {
|
|
108
|
+
for (uint8_t i = 0; i < size_; ++i) {
|
|
109
|
+
alloc_traits::destroy(allocator_, array_ + i);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
82
113
|
Allocator allocator_;
|
|
83
114
|
uint8_t size_;
|
|
84
115
|
T* array_;
|
|
@@ -166,7 +166,7 @@ compact_array_tuple_sketch<Array, Allocator> compact_array_tuple_sketch<Array, A
|
|
|
166
166
|
for (size_t i = 0; i < num_entries; ++i) {
|
|
167
167
|
Array summary(num_values, 0, allocator);
|
|
168
168
|
read(is, summary.data(), num_values * sizeof(typename Array::value_type));
|
|
169
|
-
entries.
|
|
169
|
+
entries.emplace_back(keys[i], std::move(summary));
|
|
170
170
|
}
|
|
171
171
|
}
|
|
172
172
|
if (!is.good()) throw std::runtime_error("error reading from std::istream");
|
|
@@ -213,7 +213,7 @@ compact_array_tuple_sketch<Array, Allocator> compact_array_tuple_sketch<Array, A
|
|
|
213
213
|
for (size_t i = 0; i < num_entries; ++i) {
|
|
214
214
|
Array summary(num_values, 0, allocator);
|
|
215
215
|
ptr += copy_from_mem(ptr, summary.data(), num_values * sizeof(typename Array::value_type));
|
|
216
|
-
entries.
|
|
216
|
+
entries.emplace_back(keys[i], std::move(summary));
|
|
217
217
|
}
|
|
218
218
|
}
|
|
219
219
|
const bool is_empty = flags_byte & (1 << flags::IS_EMPTY);
|
|
@@ -46,6 +46,11 @@ struct pair_extract_key {
|
|
|
46
46
|
/**
|
|
47
47
|
* Base class for Tuple sketch.
|
|
48
48
|
* This is an extension of Theta sketch that allows keeping arbitrary Summary associated with each retained key.
|
|
49
|
+
*
|
|
50
|
+
* Summary that may retain string values.
|
|
51
|
+
* For Summary containing strings, cross-language portability depends on
|
|
52
|
+
* using compatible string encodings. This class does not by itself enforce
|
|
53
|
+
* UTF-8 validity for all string inputs.
|
|
49
54
|
*/
|
|
50
55
|
template<
|
|
51
56
|
typename Summary,
|
|
@@ -253,6 +258,9 @@ public:
|
|
|
253
258
|
|
|
254
259
|
/**
|
|
255
260
|
* Update this sketch with a given string.
|
|
261
|
+
* If the summary contains strings and cross-language portability is required,
|
|
262
|
+
* callers should ensure that any strings in the summary
|
|
263
|
+
* use a compatible encoding (valid UTF-8).
|
|
256
264
|
* @param key string to update the sketch with
|
|
257
265
|
* @param value to update the sketch with
|
|
258
266
|
*/
|
|
@@ -261,6 +269,9 @@ public:
|
|
|
261
269
|
|
|
262
270
|
/**
|
|
263
271
|
* Update this sketch with a given unsigned 64-bit integer.
|
|
272
|
+
* If the summary contains strings and cross-language portability is required,
|
|
273
|
+
* callers should ensure that any strings in the summary
|
|
274
|
+
* use a compatible encoding (valid UTF-8).
|
|
264
275
|
* @param key uint64_t to update the sketch with
|
|
265
276
|
* @param value to update the sketch with
|
|
266
277
|
*/
|
|
@@ -269,6 +280,9 @@ public:
|
|
|
269
280
|
|
|
270
281
|
/**
|
|
271
282
|
* Update this sketch with a given signed 64-bit integer.
|
|
283
|
+
* If the summary contains strings and cross-language portability is required,
|
|
284
|
+
* callers should ensure that any strings in the summary
|
|
285
|
+
* use a compatible encoding (valid UTF-8).
|
|
272
286
|
* @param key int64_t to update the sketch with
|
|
273
287
|
* @param value to update the sketch with
|
|
274
288
|
*/
|
|
@@ -277,6 +291,9 @@ public:
|
|
|
277
291
|
|
|
278
292
|
/**
|
|
279
293
|
* Update this sketch with a given unsigned 32-bit integer.
|
|
294
|
+
* If the summary contains strings and cross-language portability is required,
|
|
295
|
+
* callers should ensure that any strings in the summary
|
|
296
|
+
* use a compatible encoding (valid UTF-8).
|
|
280
297
|
* For compatibility with Java implementation.
|
|
281
298
|
* @param key uint32_t to update the sketch with
|
|
282
299
|
* @param value to update the sketch with
|
|
@@ -286,6 +303,9 @@ public:
|
|
|
286
303
|
|
|
287
304
|
/**
|
|
288
305
|
* Update this sketch with a given signed 32-bit integer.
|
|
306
|
+
* If the summary contains strings and cross-language portability is required,
|
|
307
|
+
* callers should ensure that any strings in the summary
|
|
308
|
+
* use a compatible encoding (valid UTF-8).
|
|
289
309
|
* For compatibility with Java implementation.
|
|
290
310
|
* @param key int32_t to update the sketch with
|
|
291
311
|
* @param value to update the sketch with
|
|
@@ -295,6 +315,9 @@ public:
|
|
|
295
315
|
|
|
296
316
|
/**
|
|
297
317
|
* Update this sketch with a given unsigned 16-bit integer.
|
|
318
|
+
* If the summary contains strings and cross-language portability is required,
|
|
319
|
+
* callers should ensure that any strings in the summary
|
|
320
|
+
* use a compatible encoding (valid UTF-8).
|
|
298
321
|
* For compatibility with Java implementation.
|
|
299
322
|
* @param key uint16_t to update the sketch with
|
|
300
323
|
* @param value to update the sketch with
|
|
@@ -304,6 +327,9 @@ public:
|
|
|
304
327
|
|
|
305
328
|
/**
|
|
306
329
|
* Update this sketch with a given signed 16-bit integer.
|
|
330
|
+
* If the summary contains strings and cross-language portability is required,
|
|
331
|
+
* callers should ensure that any strings in the summary
|
|
332
|
+
* use a compatible encoding (valid UTF-8).
|
|
307
333
|
* For compatibility with Java implementation.
|
|
308
334
|
* @param key int16_t to update the sketch with
|
|
309
335
|
* @param value to update the sketch with
|
|
@@ -313,6 +339,9 @@ public:
|
|
|
313
339
|
|
|
314
340
|
/**
|
|
315
341
|
* Update this sketch with a given unsigned 8-bit integer.
|
|
342
|
+
* If the summary contains strings and cross-language portability is required,
|
|
343
|
+
* callers should ensure that any strings in the summary
|
|
344
|
+
* use a compatible encoding (valid UTF-8).
|
|
316
345
|
* For compatibility with Java implementation.
|
|
317
346
|
* @param key uint8_t to update the sketch with
|
|
318
347
|
* @param value to update the sketch with
|
|
@@ -322,6 +351,9 @@ public:
|
|
|
322
351
|
|
|
323
352
|
/**
|
|
324
353
|
* Update this sketch with a given signed 8-bit integer.
|
|
354
|
+
* If the summary contains strings and cross-language portability is required,
|
|
355
|
+
* callers should ensure that any strings in the summary
|
|
356
|
+
* use a compatible encoding (valid UTF-8).
|
|
325
357
|
* For compatibility with Java implementation.
|
|
326
358
|
* @param key int8_t to update the sketch with
|
|
327
359
|
* @param value to update the sketch with
|
|
@@ -331,6 +363,9 @@ public:
|
|
|
331
363
|
|
|
332
364
|
/**
|
|
333
365
|
* Update this sketch with a given double-precision floating point value.
|
|
366
|
+
* If the summary contains strings and cross-language portability is required,
|
|
367
|
+
* callers should ensure that any strings in the summary
|
|
368
|
+
* use a compatible encoding (valid UTF-8).
|
|
334
369
|
* For compatibility with Java implementation.
|
|
335
370
|
* @param key double to update the sketch with
|
|
336
371
|
* @param value to update the sketch with
|
|
@@ -340,6 +375,9 @@ public:
|
|
|
340
375
|
|
|
341
376
|
/**
|
|
342
377
|
* Update this sketch with a given floating point value.
|
|
378
|
+
* If the summary contains strings and cross-language portability is required,
|
|
379
|
+
* callers should ensure that any strings in the summary
|
|
380
|
+
* use a compatible encoding (valid UTF-8).
|
|
343
381
|
* For compatibility with Java implementation.
|
|
344
382
|
* @param key float to update the sketch with
|
|
345
383
|
* @param value to update the sketch with
|
|
@@ -357,6 +395,9 @@ public:
|
|
|
357
395
|
* Otherwise two sketches that should represent overlapping sets will be disjoint
|
|
358
396
|
* For instance, for signed 32-bit values call update(int32_t) method above,
|
|
359
397
|
* which does widening conversion to int64_t, if compatibility with Java is expected
|
|
398
|
+
* If the summary contains strings and cross-language portability is required,
|
|
399
|
+
* callers should ensure that any strings in the summary
|
|
400
|
+
* use a compatible encoding (valid UTF-8).
|
|
360
401
|
* @param key pointer to the data
|
|
361
402
|
* @param length of the data in bytes
|
|
362
403
|
* @param value to update the sketch with
|
|
@@ -315,7 +315,7 @@ entries_(other.get_allocator())
|
|
|
315
315
|
{
|
|
316
316
|
entries_.reserve(other.get_num_retained());
|
|
317
317
|
for (uint64_t hash: other) {
|
|
318
|
-
entries_.
|
|
318
|
+
entries_.emplace_back(hash, summary);
|
|
319
319
|
}
|
|
320
320
|
if (ordered && !other.is_ordered()) std::sort(entries_.begin(), entries_.end(), comparator());
|
|
321
321
|
}
|
|
@@ -347,7 +347,7 @@ uint32_t compact_tuple_sketch<S, A>::get_num_retained() const {
|
|
|
347
347
|
|
|
348
348
|
template<typename S, typename A>
|
|
349
349
|
uint16_t compact_tuple_sketch<S, A>::get_seed_hash() const {
|
|
350
|
-
return seed_hash_;
|
|
350
|
+
return is_empty_ ? 0 : seed_hash_;
|
|
351
351
|
}
|
|
352
352
|
|
|
353
353
|
template<typename S, typename A>
|
|
@@ -518,7 +518,7 @@ compact_tuple_sketch<S, A> compact_tuple_sketch<S, A>::deserialize(std::istream&
|
|
|
518
518
|
for (size_t i = 0; i < num_entries; ++i) {
|
|
519
519
|
const auto key = read<uint64_t>(is);
|
|
520
520
|
sd.deserialize(is, summary.get(), 1);
|
|
521
|
-
entries.
|
|
521
|
+
entries.emplace_back(key, std::move(*summary));
|
|
522
522
|
(*summary).~S();
|
|
523
523
|
}
|
|
524
524
|
}
|
|
@@ -583,9 +583,10 @@ compact_tuple_sketch<S, A> compact_tuple_sketch<S, A>::deserialize(const void* b
|
|
|
583
583
|
std::unique_ptr<S, deleter_of_summaries> summary(alloc.allocate(1), deleter_of_summaries(1, false, allocator));
|
|
584
584
|
for (size_t i = 0; i < num_entries; ++i) {
|
|
585
585
|
uint64_t key;
|
|
586
|
+
ensure_minimum_memory(base + size - ptr, sizeof(uint64_t));
|
|
586
587
|
ptr += copy_from_mem(ptr, key);
|
|
587
588
|
ptr += sd.deserialize(ptr, base + size - ptr, summary.get(), 1);
|
|
588
|
-
entries.
|
|
589
|
+
entries.emplace_back(key, std::move(*summary));
|
|
589
590
|
(*summary).~S();
|
|
590
591
|
}
|
|
591
592
|
}
|