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.
Files changed (120) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -0
  3. data/ext/datasketches/cpc_wrapper.cpp +8 -9
  4. data/ext/datasketches/fi_wrapper.cpp +4 -5
  5. data/ext/datasketches/hll_wrapper.cpp +9 -10
  6. data/ext/datasketches/kll_wrapper.cpp +13 -12
  7. data/ext/datasketches/theta_wrapper.cpp +11 -13
  8. data/ext/datasketches/vo_wrapper.cpp +1 -2
  9. data/lib/datasketches/version.rb +1 -1
  10. data/vendor/datasketches-cpp/CMakeLists.txt +5 -3
  11. data/vendor/datasketches-cpp/CODE_OF_CONDUCT.md +1 -1
  12. data/vendor/datasketches-cpp/LICENSE +14 -0
  13. data/vendor/datasketches-cpp/README.md +67 -73
  14. data/vendor/datasketches-cpp/benchmarks/CMakeLists.txt +52 -0
  15. data/vendor/datasketches-cpp/benchmarks/benchmark_count_min_sketch.cpp +153 -0
  16. data/vendor/datasketches-cpp/benchmarks/benchmark_count_min_sketch_serialization.cpp +161 -0
  17. data/vendor/datasketches-cpp/common/CMakeLists.txt +1 -0
  18. data/vendor/datasketches-cpp/common/include/binomial_bounds.hpp +2 -2
  19. data/vendor/datasketches-cpp/common/include/fdlibm_log.hpp +101 -0
  20. data/vendor/datasketches-cpp/common/include/serde.hpp +6 -0
  21. data/vendor/datasketches-cpp/common/test/CMakeLists.txt +44 -2
  22. data/vendor/datasketches-cpp/common/test/binomial_bounds_test.cpp +279 -0
  23. data/vendor/datasketches-cpp/common/test/deserialize_hardening_test.cpp +188 -0
  24. data/vendor/datasketches-cpp/count/include/count_min.hpp +17 -4
  25. data/vendor/datasketches-cpp/count/include/count_min_impl.hpp +65 -83
  26. data/vendor/datasketches-cpp/count/test/count_min_test.cpp +63 -7
  27. data/vendor/datasketches-cpp/cpc/include/compression_data.hpp +2 -0
  28. data/vendor/datasketches-cpp/cpc/include/cpc_compressor_impl.hpp +16 -7
  29. data/vendor/datasketches-cpp/cpc/include/cpc_sketch.hpp +1 -0
  30. data/vendor/datasketches-cpp/cpc/include/cpc_sketch_impl.hpp +54 -36
  31. data/vendor/datasketches-cpp/cpc/include/cpc_union_impl.hpp +27 -27
  32. data/vendor/datasketches-cpp/cpc/include/cpc_util.hpp +7 -7
  33. data/vendor/datasketches-cpp/cpc/include/icon_estimator.hpp +5 -5
  34. data/vendor/datasketches-cpp/cpc/include/u32_table_impl.hpp +16 -16
  35. data/vendor/datasketches-cpp/cpc/test/cpc_sketch_test.cpp +35 -0
  36. data/vendor/datasketches-cpp/fi/include/frequent_items_sketch.hpp +28 -3
  37. data/vendor/datasketches-cpp/fi/include/frequent_items_sketch_impl.hpp +41 -25
  38. data/vendor/datasketches-cpp/fi/include/reverse_purge_hash_map.hpp +3 -1
  39. data/vendor/datasketches-cpp/fi/include/reverse_purge_hash_map_impl.hpp +10 -5
  40. data/vendor/datasketches-cpp/fi/test/frequent_items_sketch_serialize_for_java.cpp +24 -0
  41. data/vendor/datasketches-cpp/fi/test/frequent_items_sketch_test.cpp +116 -0
  42. data/vendor/datasketches-cpp/filters/include/bloom_filter.hpp +1 -1
  43. data/vendor/datasketches-cpp/filters/include/bloom_filter_impl.hpp +32 -12
  44. data/vendor/datasketches-cpp/filters/test/bloom_filter_test.cpp +28 -1
  45. data/vendor/datasketches-cpp/hll/include/CouponHashSet-internal.hpp +1 -2
  46. data/vendor/datasketches-cpp/hll/include/CouponList-internal.hpp +19 -5
  47. data/vendor/datasketches-cpp/hll/include/CubicInterpolation-internal.hpp +4 -4
  48. data/vendor/datasketches-cpp/hll/include/HarmonicNumbers-internal.hpp +2 -1
  49. data/vendor/datasketches-cpp/hll/include/Hll4Array-internal.hpp +4 -4
  50. data/vendor/datasketches-cpp/hll/include/Hll4Array.hpp +1 -1
  51. data/vendor/datasketches-cpp/hll/include/Hll6Array-internal.hpp +3 -3
  52. data/vendor/datasketches-cpp/hll/include/Hll6Array.hpp +1 -1
  53. data/vendor/datasketches-cpp/hll/include/Hll8Array-internal.hpp +6 -3
  54. data/vendor/datasketches-cpp/hll/include/Hll8Array.hpp +1 -1
  55. data/vendor/datasketches-cpp/hll/include/HllArray-internal.hpp +35 -29
  56. data/vendor/datasketches-cpp/hll/include/HllArray.hpp +1 -1
  57. data/vendor/datasketches-cpp/hll/include/HllSketch-internal.hpp +3 -5
  58. data/vendor/datasketches-cpp/hll/include/HllSketchImpl-internal.hpp +5 -11
  59. data/vendor/datasketches-cpp/hll/include/HllSketchImpl.hpp +2 -4
  60. data/vendor/datasketches-cpp/hll/include/HllSketchImplFactory.hpp +11 -10
  61. data/vendor/datasketches-cpp/hll/include/HllUnion-internal.hpp +17 -8
  62. data/vendor/datasketches-cpp/hll/include/HllUtil.hpp +12 -3
  63. data/vendor/datasketches-cpp/hll/include/coupon_iterator-internal.hpp +2 -2
  64. data/vendor/datasketches-cpp/hll/include/coupon_iterator.hpp +3 -0
  65. data/vendor/datasketches-cpp/hll/include/hll.hpp +11 -4
  66. data/vendor/datasketches-cpp/hll/include/hll.private.hpp +19 -0
  67. data/vendor/datasketches-cpp/hll/test/CMakeLists.txt +2 -0
  68. data/vendor/datasketches-cpp/hll/test/CouponListTest.cpp +64 -0
  69. data/vendor/datasketches-cpp/hll/test/HllFullSizeTest.cpp +137 -0
  70. data/vendor/datasketches-cpp/hll/test/HllKxqRebuildTest.cpp +150 -0
  71. data/vendor/datasketches-cpp/hll/test/HllSketchTest.cpp +3 -3
  72. data/vendor/datasketches-cpp/hll/test/HllUnionTest.cpp +71 -1
  73. data/vendor/datasketches-cpp/kll/include/kll_helper_impl.hpp +10 -10
  74. data/vendor/datasketches-cpp/kll/include/kll_sketch.hpp +10 -1
  75. data/vendor/datasketches-cpp/kll/include/kll_sketch_impl.hpp +33 -24
  76. data/vendor/datasketches-cpp/kll/test/kll_sketch_deserialize_from_java_test.cpp +24 -0
  77. data/vendor/datasketches-cpp/kll/test/kll_sketch_serialize_for_java.cpp +10 -0
  78. data/vendor/datasketches-cpp/quantiles/include/quantiles_sketch.hpp +11 -2
  79. data/vendor/datasketches-cpp/quantiles/include/quantiles_sketch_impl.hpp +24 -15
  80. data/vendor/datasketches-cpp/req/include/req_sketch.hpp +9 -0
  81. data/vendor/datasketches-cpp/req/include/req_sketch_impl.hpp +24 -15
  82. data/vendor/datasketches-cpp/req/test/req_sketch_deserialize_from_java_test.cpp +46 -0
  83. data/vendor/datasketches-cpp/req/test/req_sketch_serialize_for_java.cpp +20 -0
  84. data/vendor/datasketches-cpp/req/test/req_sketch_test.cpp +70 -0
  85. data/vendor/datasketches-cpp/sampling/include/ebpps_sample_impl.hpp +17 -8
  86. data/vendor/datasketches-cpp/sampling/include/ebpps_sketch.hpp +13 -0
  87. data/vendor/datasketches-cpp/sampling/include/var_opt_sketch.hpp +10 -1
  88. data/vendor/datasketches-cpp/sampling/include/var_opt_sketch_impl.hpp +6 -7
  89. data/vendor/datasketches-cpp/sampling/include/var_opt_union.hpp +5 -1
  90. data/vendor/datasketches-cpp/sampling/include/var_opt_union_impl.hpp +2 -1
  91. data/vendor/datasketches-cpp/sampling/test/ebpps_allocation_test.cpp +1 -1
  92. data/vendor/datasketches-cpp/sampling/test/ebpps_sketch_test.cpp +2 -2
  93. data/vendor/datasketches-cpp/sampling/test/var_opt_allocation_test.cpp +1 -1
  94. data/vendor/datasketches-cpp/sampling/test/var_opt_sketch_test.cpp +10 -4
  95. data/vendor/datasketches-cpp/sampling/test/var_opt_union_test.cpp +12 -0
  96. data/vendor/datasketches-cpp/tdigest/include/tdigest.hpp +38 -2
  97. data/vendor/datasketches-cpp/tdigest/include/tdigest_impl.hpp +168 -9
  98. data/vendor/datasketches-cpp/tdigest/test/CMakeLists.txt +1 -0
  99. data/vendor/datasketches-cpp/tdigest/test/tdigest_iterator_test.cpp +274 -0
  100. data/vendor/datasketches-cpp/tdigest/test/tdigest_test.cpp +275 -0
  101. data/vendor/datasketches-cpp/theta/include/compact_theta_sketch_parser.hpp +2 -0
  102. data/vendor/datasketches-cpp/theta/include/compact_theta_sketch_parser_impl.hpp +24 -3
  103. data/vendor/datasketches-cpp/theta/include/theta_constants.hpp +4 -2
  104. data/vendor/datasketches-cpp/theta/include/theta_helpers.hpp +32 -0
  105. data/vendor/datasketches-cpp/theta/include/theta_set_difference_base_impl.hpp +4 -2
  106. data/vendor/datasketches-cpp/theta/include/theta_sketch.hpp +22 -4
  107. data/vendor/datasketches-cpp/theta/include/theta_sketch_impl.hpp +60 -38
  108. data/vendor/datasketches-cpp/theta/include/theta_union_base_impl.hpp +2 -6
  109. data/vendor/datasketches-cpp/theta/include/theta_update_sketch_base_impl.hpp +2 -2
  110. data/vendor/datasketches-cpp/theta/test/bit_packing_test.cpp +50 -0
  111. data/vendor/datasketches-cpp/theta/test/theta_a_not_b_test.cpp +22 -0
  112. data/vendor/datasketches-cpp/theta/test/theta_sketch_test.cpp +315 -0
  113. data/vendor/datasketches-cpp/tools/rat-check.sh +68 -0
  114. data/vendor/datasketches-cpp/tuple/include/array_tuple_sketch.hpp +35 -4
  115. data/vendor/datasketches-cpp/tuple/include/array_tuple_sketch_impl.hpp +2 -2
  116. data/vendor/datasketches-cpp/tuple/include/tuple_sketch.hpp +41 -0
  117. data/vendor/datasketches-cpp/tuple/include/tuple_sketch_impl.hpp +5 -4
  118. data/vendor/datasketches-cpp/tuple/test/tuple_sketch_test.cpp +59 -0
  119. data/vendor/datasketches-cpp/version.cfg.in +1 -1
  120. metadata +12 -2
@@ -0,0 +1,279 @@
1
+ /*
2
+ * Licensed to the Apache Software Foundation (ASF) under one
3
+ * or more contributor license agreements. See the NOTICE file
4
+ * distributed with this work for additional information
5
+ * regarding copyright ownership. The ASF licenses this file
6
+ * to you under the Apache License, Version 2.0 (the
7
+ * "License"); you may not use this file except in compliance
8
+ * with the License. You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing,
13
+ * software distributed under the License is distributed on an
14
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
+ * KIND, either express or implied. See the License for the
16
+ * specific language governing permissions and limitations
17
+ * under the License.
18
+ */
19
+
20
+ #include <catch2/catch.hpp>
21
+
22
+ #include "binomial_bounds.hpp"
23
+
24
+ namespace datasketches {
25
+
26
+ TEST_CASE("binomial_bounds: get_lower_bound", "[common]") {
27
+
28
+ SECTION("num_samples == 0") {
29
+ double result = binomial_bounds::get_lower_bound(0, 0.5, 1);
30
+ REQUIRE(result == 0.0);
31
+ }
32
+
33
+ SECTION("theta == 1.0") {
34
+ double result = binomial_bounds::get_lower_bound(100, 1.0, 1);
35
+ REQUIRE(result == 100.0);
36
+ }
37
+
38
+ SECTION("num_samples == 1") {
39
+ double result = binomial_bounds::get_lower_bound(1, 0.5, 1);
40
+ REQUIRE(result >= 0.0);
41
+ }
42
+
43
+ SECTION("num_samples == 1, stddev=2") {
44
+ double result = binomial_bounds::get_lower_bound(1, 0.5, 2);
45
+ REQUIRE(result >= 0.0);
46
+ }
47
+
48
+ SECTION("num_samples == 1, stddev=3") {
49
+ double result = binomial_bounds::get_lower_bound(1, 0.5, 3);
50
+ REQUIRE(result >= 0.0);
51
+ }
52
+
53
+ SECTION("num_samples > 120") {
54
+ double result = binomial_bounds::get_lower_bound(121, 0.5, 1);
55
+ REQUIRE(result >= 0.0);
56
+ }
57
+
58
+ SECTION("num_samples > 120, stddev=2") {
59
+ double result = binomial_bounds::get_lower_bound(200, 0.5, 2);
60
+ REQUIRE(result >= 0.0);
61
+ }
62
+
63
+ SECTION("num_samples > 120, stddev=3") {
64
+ double result = binomial_bounds::get_lower_bound(500, 0.5, 3);
65
+ REQUIRE(result >= 0.0);
66
+ }
67
+
68
+ SECTION("2 <= num_samples <= 120 AND theta > (1-1e-5)") {
69
+ double result = binomial_bounds::get_lower_bound(50, 1.0 - 1e-6, 1);
70
+ REQUIRE(std::abs(result - 50.0) < 50.0 * 0.01);
71
+ }
72
+
73
+ SECTION("2 <= num_samples <= 120 AND theta > (1-1e-5), stddev=2") {
74
+ double result = binomial_bounds::get_lower_bound(50, 1.0 - 1e-6, 2);
75
+ REQUIRE(std::abs(result - 50.0) < 50.0 * 0.01);
76
+ }
77
+
78
+ SECTION("2 <= num_samples <= 120 AND theta > (1-1e-5), stddev=3") {
79
+ double result = binomial_bounds::get_lower_bound(50, 1.0 - 1e-6, 3);
80
+ REQUIRE(std::abs(result - 50.0) < 50.0 * 0.01);
81
+ }
82
+
83
+ SECTION("2 <= num_samples <= 120 AND theta < num_samples/360") {
84
+ double result = binomial_bounds::get_lower_bound(100, 0.001, 1);
85
+ REQUIRE(result >= 0.0);
86
+ }
87
+
88
+ SECTION("2 <= num_samples <= 120 AND theta < num_samples/360, stddev=2") {
89
+ double result = binomial_bounds::get_lower_bound(100, 0.001, 2);
90
+ REQUIRE(result >= 0.0);
91
+ }
92
+
93
+ SECTION("2 <= num_samples <= 120 AND theta < num_samples/360, stddev=3") {
94
+ double result = binomial_bounds::get_lower_bound(100, 0.001, 3);
95
+ REQUIRE(result >= 0.0);
96
+ }
97
+
98
+ SECTION("2 <= num_samples <= 120 AND middle range theta (exact calculation)") {
99
+ double result = binomial_bounds::get_lower_bound(10, 0.5, 1);
100
+ REQUIRE(result >= 0.0);
101
+ }
102
+
103
+ SECTION("2 <= num_samples <= 120 AND middle range theta, stddev=2") {
104
+ double result = binomial_bounds::get_lower_bound(10, 0.5, 2);
105
+ REQUIRE(result >= 0.0);
106
+ }
107
+
108
+ SECTION("2 <= num_samples <= 120 AND middle range theta, stddev=3") {
109
+ double result = binomial_bounds::get_lower_bound(10, 0.5, 3);
110
+ REQUIRE(result >= 0.0);
111
+ }
112
+
113
+ SECTION("theta=0") {
114
+ REQUIRE_THROWS_AS(binomial_bounds::get_lower_bound(10, 0.0, 1), std::invalid_argument);
115
+ }
116
+
117
+ SECTION("theta very close to 0") {
118
+ double result = binomial_bounds::get_lower_bound(10, 1e-10, 1);
119
+ REQUIRE(result >= 0.0);
120
+ }
121
+
122
+ SECTION("num_samples=2 boundary") {
123
+ double result = binomial_bounds::get_lower_bound(2, 0.5, 1);
124
+ REQUIRE(result >= 0.0);
125
+ }
126
+
127
+ SECTION("num_samples=120 boundary") {
128
+ double result = binomial_bounds::get_lower_bound(120, 0.5, 1);
129
+ REQUIRE(result >= 0.0);
130
+ }
131
+
132
+ SECTION("estimate clamping case") {
133
+ double result = binomial_bounds::get_lower_bound(10, 0.9, 1);
134
+ double estimate = 10.0 / 0.9;
135
+ REQUIRE(result <= estimate);
136
+ }
137
+
138
+ SECTION("invalid theta < 0") {
139
+ REQUIRE_THROWS_AS(binomial_bounds::get_lower_bound(100, -0.1, 1), std::invalid_argument);
140
+ }
141
+
142
+ SECTION("invalid theta > 1") {
143
+ REQUIRE_THROWS_AS(binomial_bounds::get_lower_bound(100, 1.1, 1), std::invalid_argument);
144
+ }
145
+
146
+ SECTION("invalid stddev = 0") {
147
+ REQUIRE_THROWS_AS(binomial_bounds::get_lower_bound(100, 0.5, 0), std::invalid_argument);
148
+ }
149
+
150
+ SECTION("invalid stddev = 4") {
151
+ REQUIRE_THROWS_AS(binomial_bounds::get_lower_bound(100, 0.5, 4), std::invalid_argument);
152
+ }
153
+ }
154
+
155
+ TEST_CASE("binomial_bounds: get_upper_bound", "[common]") {
156
+
157
+ SECTION("theta == 1.0") {
158
+ double result = binomial_bounds::get_upper_bound(100, 1.0, 1);
159
+ REQUIRE(result == 100.0);
160
+ }
161
+
162
+ SECTION("num_samples == 0") {
163
+ double result = binomial_bounds::get_upper_bound(0, 0.5, 1);
164
+ REQUIRE(result > 0.0);
165
+ }
166
+
167
+ SECTION("num_samples == 0, stddev=2") {
168
+ double result = binomial_bounds::get_upper_bound(0, 0.5, 2);
169
+ REQUIRE(result > 0.0);
170
+ }
171
+
172
+ SECTION("num_samples == 0, stddev=3") {
173
+ double result = binomial_bounds::get_upper_bound(0, 0.5, 3);
174
+ REQUIRE(result > 0.0);
175
+ }
176
+
177
+ SECTION("num_samples > 120") {
178
+ double result = binomial_bounds::get_upper_bound(121, 0.5, 1);
179
+ REQUIRE(result >= 0.0);
180
+ }
181
+
182
+ SECTION("num_samples > 120, stddev=2") {
183
+ double result = binomial_bounds::get_upper_bound(200, 0.5, 2);
184
+ REQUIRE(result >= 0.0);
185
+ }
186
+
187
+ SECTION("num_samples > 120, stddev=3") {
188
+ double result = binomial_bounds::get_upper_bound(500, 0.5, 3);
189
+ REQUIRE(result >= 0.0);
190
+ }
191
+
192
+ SECTION("1 <= num_samples <= 120 AND theta > (1-1e-5)") {
193
+ double result = binomial_bounds::get_upper_bound(50, 1.0 - 1e-6, 1);
194
+ REQUIRE(result == 51.0);
195
+ }
196
+
197
+ SECTION("1 <= num_samples <= 120 AND theta > (1-1e-5), stddev=2") {
198
+ double result = binomial_bounds::get_upper_bound(50, 1.0 - 1e-6, 2);
199
+ REQUIRE(result == 51.0);
200
+ }
201
+
202
+ SECTION("1 <= num_samples <= 120 AND theta > (1-1e-5), stddev=3") {
203
+ double result = binomial_bounds::get_upper_bound(50, 1.0 - 1e-6, 3);
204
+ REQUIRE(result == 51.0);
205
+ }
206
+
207
+ SECTION("1 <= num_samples <= 120 AND theta < num_samples/360") {
208
+ double result = binomial_bounds::get_upper_bound(100, 0.001, 1);
209
+ REQUIRE(result >= 0.0);
210
+ }
211
+
212
+ SECTION("1 <= num_samples <= 120 AND theta < num_samples/360, stddev=2") {
213
+ double result = binomial_bounds::get_upper_bound(100, 0.001, 2);
214
+ REQUIRE(result >= 0.0);
215
+ }
216
+
217
+ SECTION("1 <= num_samples <= 120 AND theta < num_samples/360, stddev=3") {
218
+ double result = binomial_bounds::get_upper_bound(100, 0.001, 3);
219
+ REQUIRE(result >= 0.0);
220
+ }
221
+
222
+ SECTION("1 <= num_samples <= 120 AND middle range theta (exact calculation)") {
223
+ double result = binomial_bounds::get_upper_bound(10, 0.5, 1);
224
+ REQUIRE(result >= 0.0);
225
+ }
226
+
227
+ SECTION("1 <= num_samples <= 120 AND middle range theta, stddev=2") {
228
+ double result = binomial_bounds::get_upper_bound(10, 0.5, 2);
229
+ REQUIRE(result >= 0.0);
230
+ }
231
+
232
+ SECTION("1 <= num_samples <= 120 AND middle range theta, stddev=3") {
233
+ double result = binomial_bounds::get_upper_bound(10, 0.5, 3);
234
+ REQUIRE(result >= 0.0);
235
+ }
236
+
237
+ SECTION("theta=0") {
238
+ REQUIRE_THROWS_AS(binomial_bounds::get_upper_bound(10, 0.0, 1), std::invalid_argument);
239
+ }
240
+
241
+ SECTION("theta very close to 0") {
242
+ double result = binomial_bounds::get_upper_bound(10, 1e-10, 1);
243
+ REQUIRE(result >= 0.0);
244
+ }
245
+
246
+ SECTION("num_samples=1 boundary") {
247
+ double result = binomial_bounds::get_upper_bound(1, 0.5, 1);
248
+ REQUIRE(result >= 0.0);
249
+ }
250
+
251
+ SECTION("num_samples=120 boundary") {
252
+ double result = binomial_bounds::get_upper_bound(120, 0.5, 1);
253
+ REQUIRE(result >= 0.0);
254
+ }
255
+
256
+ SECTION("estimate clamping case") {
257
+ double result = binomial_bounds::get_upper_bound(10, 0.9, 1);
258
+ double estimate = 10.0 / 0.9;
259
+ REQUIRE(result >= estimate);
260
+ }
261
+
262
+ SECTION("invalid theta < 0") {
263
+ REQUIRE_THROWS_AS(binomial_bounds::get_upper_bound(100, -0.1, 1), std::invalid_argument);
264
+ }
265
+
266
+ SECTION("invalid theta > 1") {
267
+ REQUIRE_THROWS_AS(binomial_bounds::get_upper_bound(100, 1.1, 1), std::invalid_argument);
268
+ }
269
+
270
+ SECTION("invalid stddev = 0") {
271
+ REQUIRE_THROWS_AS(binomial_bounds::get_upper_bound(100, 0.5, 0), std::invalid_argument);
272
+ }
273
+
274
+ SECTION("invalid stddev = 4") {
275
+ REQUIRE_THROWS_AS(binomial_bounds::get_upper_bound(100, 0.5, 4), std::invalid_argument);
276
+ }
277
+ }
278
+
279
+ } /* namespace datasketches */
@@ -0,0 +1,188 @@
1
+ /*
2
+ * Licensed to the Apache Software Foundation (ASF) under one
3
+ * or more contributor license agreements. See the NOTICE file
4
+ * distributed with this work for additional information
5
+ * regarding copyright ownership. The ASF licenses this file
6
+ * to you under the Apache License, Version 2.0 (the
7
+ * "License"); you may not use this file except in compliance
8
+ * with the License. You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing,
13
+ * software distributed under the License is distributed on an
14
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
+ * KIND, either express or implied. See the License for the
16
+ * specific language governing permissions and limitations
17
+ * under the License.
18
+ */
19
+
20
+ #include <catch2/catch.hpp>
21
+ #include <sstream>
22
+ #include <vector>
23
+
24
+ // Include all affected sketch types
25
+ #include <quantiles_sketch.hpp>
26
+ #include <kll_sketch.hpp>
27
+ #include <req_sketch.hpp>
28
+
29
+ namespace datasketches {
30
+
31
+ /**
32
+ * Test for fix of issue #477:
33
+ * BUG: SIGABRT in deserialize(): dereferencing empty std::optional (libc++ verbose_abort)
34
+ *
35
+ * These tests exercise the actual deserialization code path that contained the bug.
36
+ * With buggy code (&*tmp on empty optional) and hardening enabled, these will SIGABRT.
37
+ * With fixed code (aligned_storage), these pass normally.
38
+ *
39
+ * IMPORTANT: These tests actually call deserialize() on multi-item sketches, which
40
+ * exercises the buggy code path where min/max are deserialized.
41
+ */
42
+
43
+ TEST_CASE("quantiles_sketch: deserialize multi-item sketch", "[deserialize_hardening]") {
44
+ // Create sketch with multiple items (so min/max are stored in serialization)
45
+ quantiles_sketch<double> sketch(128);
46
+ for (int i = 0; i < 1000; i++) {
47
+ sketch.update(static_cast<double>(i));
48
+ }
49
+
50
+ // Serialize
51
+ auto bytes = sketch.serialize();
52
+
53
+ // Deserialize - WITH BUGGY CODE AND HARDENING, THIS WILL SIGABRT HERE
54
+ // The bug is: sd.deserialize(is, &*tmp, 1) where tmp is empty optional
55
+ auto sketch2 = quantiles_sketch<double>::deserialize(bytes.data(), bytes.size());
56
+
57
+ // Verify deserialization worked correctly
58
+ REQUIRE(sketch2.get_n() == sketch.get_n());
59
+ REQUIRE(sketch2.get_min_item() == sketch.get_min_item());
60
+ REQUIRE(sketch2.get_max_item() == sketch.get_max_item());
61
+ REQUIRE(sketch2.get_quantile(0.5) == sketch.get_quantile(0.5));
62
+ }
63
+
64
+ TEST_CASE("quantiles_sketch: deserialize from stream", "[deserialize_hardening]") {
65
+ quantiles_sketch<float> sketch(256);
66
+ for (int i = 0; i < 2000; i++) {
67
+ sketch.update(static_cast<float>(i) * 0.5f);
68
+ }
69
+
70
+ // Serialize to stream
71
+ std::stringstream ss;
72
+ sketch.serialize(ss);
73
+
74
+ // Deserialize from stream - exercises the buggy code path
75
+ auto sketch2 = quantiles_sketch<float>::deserialize(ss);
76
+
77
+ REQUIRE(sketch2.get_n() == sketch.get_n());
78
+ REQUIRE(sketch2.get_min_item() == sketch.get_min_item());
79
+ REQUIRE(sketch2.get_max_item() == sketch.get_max_item());
80
+ }
81
+
82
+ TEST_CASE("kll_sketch: deserialize multi-item sketch", "[deserialize_hardening]") {
83
+ kll_sketch<float> sketch(200);
84
+ for (int i = 0; i < 1500; i++) {
85
+ sketch.update(static_cast<float>(i));
86
+ }
87
+
88
+ auto bytes = sketch.serialize();
89
+
90
+ // Deserialize - exercises buggy &*tmp code path
91
+ auto sketch2 = kll_sketch<float>::deserialize(bytes.data(), bytes.size());
92
+
93
+ REQUIRE(sketch2.get_n() == sketch.get_n());
94
+ REQUIRE(sketch2.get_min_item() == sketch.get_min_item());
95
+ REQUIRE(sketch2.get_max_item() == sketch.get_max_item());
96
+ }
97
+
98
+ TEST_CASE("kll_sketch: deserialize from stream", "[deserialize_hardening]") {
99
+ kll_sketch<int> sketch(400);
100
+ for (int i = 0; i < 3000; i++) {
101
+ sketch.update(i);
102
+ }
103
+
104
+ std::stringstream ss;
105
+ sketch.serialize(ss);
106
+
107
+ // Deserialize from stream
108
+ auto sketch2 = kll_sketch<int>::deserialize(ss);
109
+
110
+ REQUIRE(sketch2.get_n() == sketch.get_n());
111
+ REQUIRE(sketch2.get_min_item() == sketch.get_min_item());
112
+ REQUIRE(sketch2.get_max_item() == sketch.get_max_item());
113
+ }
114
+
115
+ TEST_CASE("req_sketch: deserialize multi-level sketch", "[deserialize_hardening]") {
116
+ // REQ sketch only has the bug when num_levels > 1
117
+ // We need to add enough items to trigger multiple levels
118
+ req_sketch<float> sketch(12);
119
+ for (int i = 0; i < 10000; i++) {
120
+ sketch.update(static_cast<float>(i));
121
+ }
122
+
123
+ auto bytes = sketch.serialize();
124
+
125
+ // Deserialize - exercises buggy code path when num_levels > 1
126
+ auto sketch2 = req_sketch<float>::deserialize(bytes.data(), bytes.size());
127
+
128
+ REQUIRE(sketch2.get_n() == sketch.get_n());
129
+ REQUIRE(sketch2.get_min_item() == sketch.get_min_item());
130
+ REQUIRE(sketch2.get_max_item() == sketch.get_max_item());
131
+ }
132
+
133
+ TEST_CASE("req_sketch: deserialize from stream", "[deserialize_hardening]") {
134
+ req_sketch<double> sketch(20);
135
+ for (int i = 0; i < 15000; i++) {
136
+ sketch.update(static_cast<double>(i) * 0.1);
137
+ }
138
+
139
+ std::stringstream ss;
140
+ sketch.serialize(ss);
141
+
142
+ // Deserialize from stream
143
+ auto sketch2 = req_sketch<double>::deserialize(ss);
144
+
145
+ REQUIRE(sketch2.get_n() == sketch.get_n());
146
+ REQUIRE(sketch2.get_min_item() == sketch.get_min_item());
147
+ REQUIRE(sketch2.get_max_item() == sketch.get_max_item());
148
+ }
149
+
150
+ TEST_CASE("multiple sketch types: stress test", "[deserialize_hardening]") {
151
+ SECTION("quantiles with various sizes") {
152
+ for (int k : {64, 128, 256}) {
153
+ quantiles_sketch<int> sketch(k);
154
+ for (int i = 0; i < 5000; i++) {
155
+ sketch.update(i);
156
+ }
157
+ auto bytes = sketch.serialize();
158
+ auto sketch2 = quantiles_sketch<int>::deserialize(bytes.data(), bytes.size());
159
+ REQUIRE(sketch2.get_n() == sketch.get_n());
160
+ }
161
+ }
162
+
163
+ SECTION("kll with various sizes") {
164
+ for (int k : {100, 200, 400}) {
165
+ kll_sketch<double> sketch(k);
166
+ for (int i = 0; i < 4000; i++) {
167
+ sketch.update(static_cast<double>(i) / 10.0);
168
+ }
169
+ auto bytes = sketch.serialize();
170
+ auto sketch2 = kll_sketch<double>::deserialize(bytes.data(), bytes.size());
171
+ REQUIRE(sketch2.get_n() == sketch.get_n());
172
+ }
173
+ }
174
+
175
+ SECTION("req with various sizes") {
176
+ for (int k : {12, 20}) {
177
+ req_sketch<float> sketch(k);
178
+ for (int i = 0; i < 8000; i++) {
179
+ sketch.update(static_cast<float>(i));
180
+ }
181
+ auto bytes = sketch.serialize();
182
+ auto sketch2 = req_sketch<float>::deserialize(bytes.data(), bytes.size());
183
+ REQUIRE(sketch2.get_n() == sketch.get_n());
184
+ }
185
+ }
186
+ }
187
+
188
+ } // namespace datasketches
@@ -21,6 +21,8 @@
21
21
  #define COUNT_MIN_HPP_
22
22
 
23
23
  #include <iterator>
24
+ #include <type_traits>
25
+ #include <vector>
24
26
  #include "common_defs.hpp"
25
27
 
26
28
  namespace datasketches {
@@ -36,6 +38,7 @@ template <typename W,
36
38
  typename Allocator = std::allocator<W>>
37
39
  class count_min_sketch{
38
40
  static_assert(std::is_arithmetic<W>::value, "Arithmetic type expected");
41
+ static_assert(!std::is_same<W, bool>::value, "Boolean weight type is not supported");
39
42
  public:
40
43
  using allocator_type = Allocator;
41
44
  using const_iterator = typename std::vector<W, Allocator>::const_iterator;
@@ -326,6 +329,15 @@ public:
326
329
  */
327
330
  void serialize(std::ostream& os) const;
328
331
 
332
+ /**
333
+ * This method serializes the sketch by passing binary fragments to a callback.
334
+ * The callback must be callable as write_bytes(const void* data, size_t size).
335
+ * The data pointer is valid only until the callback returns.
336
+ * @return size in bytes written to the callback
337
+ */
338
+ template<typename Sink>
339
+ size_t serialize_to(Sink&& sink) const;
340
+
329
341
  // This is a convenience alias for users
330
342
  // The type returned by the following serialize method
331
343
  using vector_bytes = std::vector<uint8_t, typename std::allocator_traits<Allocator>::template rebind_alloc<uint8_t>>;
@@ -389,12 +401,13 @@ private:
389
401
  static void check_header_validity(uint8_t preamble_longs, uint8_t serial_version, uint8_t family_id, uint8_t flags_byte);
390
402
 
391
403
  /*
392
- * Obtain the hash values when inserting an item into the sketch.
393
- * @param item pointer to the data item to be inserted into the sketch.
404
+ * Compute the hash locations for an input item
405
+ * @param item pointer to the data item to be inserted into or queried from the sketch.
394
406
  * @param size of the data in bytes
395
- * @return vector of uint64_t which each represent the index to which `value' must update in the sketch
407
+ * @param callback function to invoke for each sketch array location
396
408
  */
397
- std::vector<uint64_t> get_hashes(const void* item, size_t size) const;
409
+ template<typename F>
410
+ void foreach_hash_location(const void* item, size_t size, F callback) const;
398
411
 
399
412
  };
400
413