datasketches 0.5.2 → 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 (115) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/ext/datasketches/theta_wrapper.cpp +1 -1
  4. data/lib/datasketches/version.rb +1 -1
  5. data/vendor/datasketches-cpp/CMakeLists.txt +5 -3
  6. data/vendor/datasketches-cpp/CODE_OF_CONDUCT.md +1 -1
  7. data/vendor/datasketches-cpp/LICENSE +14 -0
  8. data/vendor/datasketches-cpp/README.md +67 -73
  9. data/vendor/datasketches-cpp/benchmarks/CMakeLists.txt +52 -0
  10. data/vendor/datasketches-cpp/benchmarks/benchmark_count_min_sketch.cpp +153 -0
  11. data/vendor/datasketches-cpp/benchmarks/benchmark_count_min_sketch_serialization.cpp +161 -0
  12. data/vendor/datasketches-cpp/common/CMakeLists.txt +1 -0
  13. data/vendor/datasketches-cpp/common/include/binomial_bounds.hpp +2 -2
  14. data/vendor/datasketches-cpp/common/include/fdlibm_log.hpp +101 -0
  15. data/vendor/datasketches-cpp/common/include/serde.hpp +6 -0
  16. data/vendor/datasketches-cpp/common/test/CMakeLists.txt +44 -2
  17. data/vendor/datasketches-cpp/common/test/binomial_bounds_test.cpp +279 -0
  18. data/vendor/datasketches-cpp/common/test/deserialize_hardening_test.cpp +188 -0
  19. data/vendor/datasketches-cpp/count/include/count_min.hpp +17 -4
  20. data/vendor/datasketches-cpp/count/include/count_min_impl.hpp +65 -83
  21. data/vendor/datasketches-cpp/count/test/count_min_test.cpp +63 -7
  22. data/vendor/datasketches-cpp/cpc/include/compression_data.hpp +2 -0
  23. data/vendor/datasketches-cpp/cpc/include/cpc_compressor_impl.hpp +16 -7
  24. data/vendor/datasketches-cpp/cpc/include/cpc_sketch.hpp +1 -0
  25. data/vendor/datasketches-cpp/cpc/include/cpc_sketch_impl.hpp +54 -36
  26. data/vendor/datasketches-cpp/cpc/include/cpc_union_impl.hpp +27 -27
  27. data/vendor/datasketches-cpp/cpc/include/cpc_util.hpp +7 -7
  28. data/vendor/datasketches-cpp/cpc/include/icon_estimator.hpp +5 -5
  29. data/vendor/datasketches-cpp/cpc/include/u32_table_impl.hpp +16 -16
  30. data/vendor/datasketches-cpp/cpc/test/cpc_sketch_test.cpp +35 -0
  31. data/vendor/datasketches-cpp/fi/include/frequent_items_sketch.hpp +28 -3
  32. data/vendor/datasketches-cpp/fi/include/frequent_items_sketch_impl.hpp +41 -25
  33. data/vendor/datasketches-cpp/fi/include/reverse_purge_hash_map.hpp +3 -1
  34. data/vendor/datasketches-cpp/fi/include/reverse_purge_hash_map_impl.hpp +10 -5
  35. data/vendor/datasketches-cpp/fi/test/frequent_items_sketch_serialize_for_java.cpp +24 -0
  36. data/vendor/datasketches-cpp/fi/test/frequent_items_sketch_test.cpp +116 -0
  37. data/vendor/datasketches-cpp/filters/include/bloom_filter.hpp +1 -1
  38. data/vendor/datasketches-cpp/filters/include/bloom_filter_impl.hpp +32 -12
  39. data/vendor/datasketches-cpp/filters/test/bloom_filter_test.cpp +28 -1
  40. data/vendor/datasketches-cpp/hll/include/CouponHashSet-internal.hpp +1 -2
  41. data/vendor/datasketches-cpp/hll/include/CouponList-internal.hpp +19 -5
  42. data/vendor/datasketches-cpp/hll/include/CubicInterpolation-internal.hpp +4 -4
  43. data/vendor/datasketches-cpp/hll/include/HarmonicNumbers-internal.hpp +2 -1
  44. data/vendor/datasketches-cpp/hll/include/Hll4Array-internal.hpp +4 -4
  45. data/vendor/datasketches-cpp/hll/include/Hll4Array.hpp +1 -1
  46. data/vendor/datasketches-cpp/hll/include/Hll6Array-internal.hpp +3 -3
  47. data/vendor/datasketches-cpp/hll/include/Hll6Array.hpp +1 -1
  48. data/vendor/datasketches-cpp/hll/include/Hll8Array-internal.hpp +6 -3
  49. data/vendor/datasketches-cpp/hll/include/Hll8Array.hpp +1 -1
  50. data/vendor/datasketches-cpp/hll/include/HllArray-internal.hpp +35 -29
  51. data/vendor/datasketches-cpp/hll/include/HllArray.hpp +1 -1
  52. data/vendor/datasketches-cpp/hll/include/HllSketch-internal.hpp +3 -5
  53. data/vendor/datasketches-cpp/hll/include/HllSketchImpl-internal.hpp +5 -11
  54. data/vendor/datasketches-cpp/hll/include/HllSketchImpl.hpp +2 -4
  55. data/vendor/datasketches-cpp/hll/include/HllSketchImplFactory.hpp +11 -10
  56. data/vendor/datasketches-cpp/hll/include/HllUnion-internal.hpp +17 -8
  57. data/vendor/datasketches-cpp/hll/include/HllUtil.hpp +12 -3
  58. data/vendor/datasketches-cpp/hll/include/coupon_iterator-internal.hpp +2 -2
  59. data/vendor/datasketches-cpp/hll/include/coupon_iterator.hpp +3 -0
  60. data/vendor/datasketches-cpp/hll/include/hll.hpp +11 -4
  61. data/vendor/datasketches-cpp/hll/include/hll.private.hpp +19 -0
  62. data/vendor/datasketches-cpp/hll/test/CMakeLists.txt +2 -0
  63. data/vendor/datasketches-cpp/hll/test/CouponListTest.cpp +64 -0
  64. data/vendor/datasketches-cpp/hll/test/HllFullSizeTest.cpp +137 -0
  65. data/vendor/datasketches-cpp/hll/test/HllKxqRebuildTest.cpp +150 -0
  66. data/vendor/datasketches-cpp/hll/test/HllSketchTest.cpp +3 -3
  67. data/vendor/datasketches-cpp/hll/test/HllUnionTest.cpp +71 -1
  68. data/vendor/datasketches-cpp/kll/include/kll_helper_impl.hpp +10 -10
  69. data/vendor/datasketches-cpp/kll/include/kll_sketch.hpp +10 -1
  70. data/vendor/datasketches-cpp/kll/include/kll_sketch_impl.hpp +33 -24
  71. data/vendor/datasketches-cpp/kll/test/kll_sketch_deserialize_from_java_test.cpp +24 -0
  72. data/vendor/datasketches-cpp/kll/test/kll_sketch_serialize_for_java.cpp +10 -0
  73. data/vendor/datasketches-cpp/quantiles/include/quantiles_sketch.hpp +11 -2
  74. data/vendor/datasketches-cpp/quantiles/include/quantiles_sketch_impl.hpp +24 -15
  75. data/vendor/datasketches-cpp/req/include/req_sketch.hpp +9 -0
  76. data/vendor/datasketches-cpp/req/include/req_sketch_impl.hpp +24 -15
  77. data/vendor/datasketches-cpp/req/test/req_sketch_deserialize_from_java_test.cpp +46 -0
  78. data/vendor/datasketches-cpp/req/test/req_sketch_serialize_for_java.cpp +20 -0
  79. data/vendor/datasketches-cpp/req/test/req_sketch_test.cpp +70 -0
  80. data/vendor/datasketches-cpp/sampling/include/ebpps_sample_impl.hpp +17 -8
  81. data/vendor/datasketches-cpp/sampling/include/ebpps_sketch.hpp +13 -0
  82. data/vendor/datasketches-cpp/sampling/include/var_opt_sketch.hpp +10 -1
  83. data/vendor/datasketches-cpp/sampling/include/var_opt_sketch_impl.hpp +6 -7
  84. data/vendor/datasketches-cpp/sampling/include/var_opt_union.hpp +5 -1
  85. data/vendor/datasketches-cpp/sampling/include/var_opt_union_impl.hpp +2 -1
  86. data/vendor/datasketches-cpp/sampling/test/ebpps_allocation_test.cpp +1 -1
  87. data/vendor/datasketches-cpp/sampling/test/ebpps_sketch_test.cpp +2 -2
  88. data/vendor/datasketches-cpp/sampling/test/var_opt_allocation_test.cpp +1 -1
  89. data/vendor/datasketches-cpp/sampling/test/var_opt_sketch_test.cpp +10 -4
  90. data/vendor/datasketches-cpp/sampling/test/var_opt_union_test.cpp +12 -0
  91. data/vendor/datasketches-cpp/tdigest/include/tdigest.hpp +38 -2
  92. data/vendor/datasketches-cpp/tdigest/include/tdigest_impl.hpp +168 -9
  93. data/vendor/datasketches-cpp/tdigest/test/CMakeLists.txt +1 -0
  94. data/vendor/datasketches-cpp/tdigest/test/tdigest_iterator_test.cpp +274 -0
  95. data/vendor/datasketches-cpp/tdigest/test/tdigest_test.cpp +275 -0
  96. data/vendor/datasketches-cpp/theta/include/compact_theta_sketch_parser.hpp +2 -0
  97. data/vendor/datasketches-cpp/theta/include/compact_theta_sketch_parser_impl.hpp +24 -3
  98. data/vendor/datasketches-cpp/theta/include/theta_constants.hpp +4 -2
  99. data/vendor/datasketches-cpp/theta/include/theta_helpers.hpp +32 -0
  100. data/vendor/datasketches-cpp/theta/include/theta_set_difference_base_impl.hpp +4 -2
  101. data/vendor/datasketches-cpp/theta/include/theta_sketch.hpp +22 -4
  102. data/vendor/datasketches-cpp/theta/include/theta_sketch_impl.hpp +60 -38
  103. data/vendor/datasketches-cpp/theta/include/theta_union_base_impl.hpp +2 -6
  104. data/vendor/datasketches-cpp/theta/include/theta_update_sketch_base_impl.hpp +2 -2
  105. data/vendor/datasketches-cpp/theta/test/bit_packing_test.cpp +50 -0
  106. data/vendor/datasketches-cpp/theta/test/theta_a_not_b_test.cpp +22 -0
  107. data/vendor/datasketches-cpp/theta/test/theta_sketch_test.cpp +315 -0
  108. data/vendor/datasketches-cpp/tools/rat-check.sh +68 -0
  109. data/vendor/datasketches-cpp/tuple/include/array_tuple_sketch.hpp +35 -4
  110. data/vendor/datasketches-cpp/tuple/include/array_tuple_sketch_impl.hpp +2 -2
  111. data/vendor/datasketches-cpp/tuple/include/tuple_sketch.hpp +41 -0
  112. data/vendor/datasketches-cpp/tuple/include/tuple_sketch_impl.hpp +5 -4
  113. data/vendor/datasketches-cpp/tuple/test/tuple_sketch_test.cpp +59 -0
  114. data/vendor/datasketches-cpp/version.cfg.in +1 -1
  115. metadata +12 -2
@@ -46,6 +46,11 @@ namespace kll_constants {
46
46
  * and nearly optimal accuracy per retained item.
47
47
  * See <a href="https://arxiv.org/abs/1603.05346v2">Optimal Quantile Approximation in Streams</a>.
48
48
  *
49
+ * Sketch that may retain string values.
50
+ * For sketches containing strings, cross-language portability depends on
51
+ * using compatible string encodings. This class does not by itself enforce
52
+ * UTF-8 validity for all string inputs.
53
+ *
49
54
  * <p>This is a stochastic streaming sketch that enables near real-time analysis of the
50
55
  * approximate distribution of items from a very large stream in a single pass, requiring only
51
56
  * that the items are comparable.
@@ -56,7 +61,7 @@ namespace kll_constants {
56
61
  * <p>As of May 2020, this implementation produces serialized sketches which are binary-compatible
57
62
  * with the equivalent Java implementation only when template parameter T = float
58
63
  * (32-bit single precision values).
59
- *
64
+ *
60
65
  * <p>Given an input stream of <i>N</i> items, the <i>natural rank</i> of any specific
61
66
  * item is defined as its index <i>(1 to N)</i> in inclusive mode
62
67
  * or <i>(0 to N-1)</i> in exclusive mode
@@ -225,6 +230,8 @@ class kll_sketch {
225
230
 
226
231
  /**
227
232
  * Updates this sketch with the given data item.
233
+ * If cross-language portability is required, callers should ensure that
234
+ * the input string uses a compatible encoding (valid UTF-8).
228
235
  * @param item from a stream of items
229
236
  */
230
237
  template<typename FwdT>
@@ -232,6 +239,8 @@ class kll_sketch {
232
239
 
233
240
  /**
234
241
  * Merges another sketch into this one.
242
+ * If sketches contain strings, callers are responsible for ensuring that
243
+ * both sketches were built using compatible string encodings.
235
244
  * @param other sketch to merge into this one
236
245
  */
237
246
  template<typename FwdSk>
@@ -24,6 +24,7 @@
24
24
  #include <iomanip>
25
25
  #include <sstream>
26
26
  #include <stdexcept>
27
+ #include <type_traits>
27
28
 
28
29
  #include "conditional_forward.hpp"
29
30
  #include "count_zeros.hpp"
@@ -198,7 +199,7 @@ void kll_sketch<T, C, A>::update_min_max(const T& item) {
198
199
 
199
200
  template<typename T, typename C, typename A>
200
201
  uint32_t kll_sketch<T, C, A>::internal_update() {
201
- if (levels_[0] == 0) compress_while_updating();
202
+ if (levels_[0] == 0) { compress_while_updating(); }
202
203
  n_++;
203
204
  is_level_zero_sorted_ = false;
204
205
  return --levels_[0];
@@ -207,7 +208,7 @@ uint32_t kll_sketch<T, C, A>::internal_update() {
207
208
  template<typename T, typename C, typename A>
208
209
  template<typename FwdSk>
209
210
  void kll_sketch<T, C, A>::merge(FwdSk&& other) {
210
- if (other.is_empty()) return;
211
+ if (other.is_empty()) { return; }
211
212
  if (m_ != other.m_) {
212
213
  throw std::invalid_argument("incompatible M: " + std::to_string(m_) + " and " + std::to_string(other.m_));
213
214
  }
@@ -223,9 +224,9 @@ void kll_sketch<T, C, A>::merge(FwdSk&& other) {
223
224
  const uint32_t index = internal_update();
224
225
  new (&items_[index]) T(conditional_forward<FwdSk>(other.items_[i]));
225
226
  }
226
- if (other.num_levels_ >= 2) merge_higher_levels(other, final_n);
227
+ if (other.num_levels_ >= 2) { merge_higher_levels(other, final_n); }
227
228
  n_ = final_n;
228
- if (other.is_estimation_mode()) min_k_ = std::min(min_k_, other.min_k_);
229
+ if (other.is_estimation_mode()) { min_k_ = std::min(min_k_, other.min_k_); }
229
230
  assert_correct_total_weight();
230
231
  reset_sorted_view();
231
232
  }
@@ -257,13 +258,13 @@ bool kll_sketch<T, C, A>::is_estimation_mode() const {
257
258
 
258
259
  template<typename T, typename C, typename A>
259
260
  T kll_sketch<T, C, A>::get_min_item() const {
260
- if (is_empty()) throw std::runtime_error("operation is undefined for an empty sketch");
261
+ if (is_empty()) { throw std::runtime_error("operation is undefined for an empty sketch"); }
261
262
  return *min_item_;
262
263
  }
263
264
 
264
265
  template<typename T, typename C, typename A>
265
266
  T kll_sketch<T, C, A>::get_max_item() const {
266
- if (is_empty()) throw std::runtime_error("operation is undefined for an empty sketch");
267
+ if (is_empty()) { throw std::runtime_error("operation is undefined for an empty sketch"); }
267
268
  return *max_item_;
268
269
  }
269
270
 
@@ -279,28 +280,28 @@ A kll_sketch<T, C, A>::get_allocator() const {
279
280
 
280
281
  template<typename T, typename C, typename A>
281
282
  double kll_sketch<T, C, A>::get_rank(const T& item, bool inclusive) const {
282
- if (is_empty()) throw std::runtime_error("operation is undefined for an empty sketch");
283
+ if (is_empty()) { throw std::runtime_error("operation is undefined for an empty sketch"); }
283
284
  setup_sorted_view();
284
285
  return sorted_view_->get_rank(item, inclusive);
285
286
  }
286
287
 
287
288
  template<typename T, typename C, typename A>
288
289
  auto kll_sketch<T, C, A>::get_PMF(const T* split_points, uint32_t size, bool inclusive) const -> vector_double {
289
- if (is_empty()) throw std::runtime_error("operation is undefined for an empty sketch");
290
+ if (is_empty()) { throw std::runtime_error("operation is undefined for an empty sketch"); }
290
291
  setup_sorted_view();
291
292
  return sorted_view_->get_PMF(split_points, size, inclusive);
292
293
  }
293
294
 
294
295
  template<typename T, typename C, typename A>
295
296
  auto kll_sketch<T, C, A>::get_CDF(const T* split_points, uint32_t size, bool inclusive) const -> vector_double {
296
- if (is_empty()) throw std::runtime_error("operation is undefined for an empty sketch");
297
+ if (is_empty()) { throw std::runtime_error("operation is undefined for an empty sketch"); }
297
298
  setup_sorted_view();
298
299
  return sorted_view_->get_CDF(split_points, size, inclusive);
299
300
  }
300
301
 
301
302
  template<typename T, typename C, typename A>
302
303
  auto kll_sketch<T, C, A>::get_quantile(double rank, bool inclusive) const -> quantile_return_type {
303
- if (is_empty()) throw std::runtime_error("operation is undefined for an empty sketch");
304
+ if (is_empty()) { throw std::runtime_error("operation is undefined for an empty sketch"); }
304
305
  if ((rank < 0.0) || (rank > 1.0)) {
305
306
  throw std::invalid_argument("normalized rank cannot be less than zero or greater than 1.0");
306
307
  }
@@ -481,18 +482,22 @@ kll_sketch<T, C, A> kll_sketch<T, C, A>::deserialize(std::istream& is, const Ser
481
482
  read(is, levels.data(), sizeof(levels[0]) * num_levels);
482
483
  }
483
484
  levels[num_levels] = capacity;
484
- optional<T> tmp; // space to deserialize min and max
485
485
  optional<T> min_item;
486
486
  optional<T> max_item;
487
487
  if (!is_single_item) {
488
- sd.deserialize(is, &*tmp, 1);
488
+ // Space to deserialize min and max.
489
+ // serde::deserialize expects allocated but not initialized storage.
490
+ typename std::aligned_storage<sizeof(T), alignof(T)>::type tmp_storage;
491
+ T* tmp = reinterpret_cast<T*>(&tmp_storage);
492
+
493
+ sd.deserialize(is, tmp, 1);
489
494
  // serde call did not throw, repackage and cleanup
490
- min_item.emplace(*tmp);
491
- (*tmp).~T();
492
- sd.deserialize(is, &*tmp, 1);
495
+ min_item.emplace(std::move(*tmp));
496
+ tmp->~T();
497
+ sd.deserialize(is, tmp, 1);
493
498
  // serde call did not throw, repackage and cleanup
494
- max_item.emplace(*tmp);
495
- (*tmp).~T();
499
+ max_item.emplace(std::move(*tmp));
500
+ tmp->~T();
496
501
  }
497
502
  A alloc(allocator);
498
503
  auto items_buffer_deleter = [capacity, &alloc](T* ptr) { alloc.deallocate(ptr, capacity); };
@@ -565,18 +570,22 @@ kll_sketch<T, C, A> kll_sketch<T, C, A>::deserialize(const void* bytes, size_t s
565
570
  ptr += copy_from_mem(ptr, levels.data(), sizeof(levels[0]) * num_levels);
566
571
  }
567
572
  levels[num_levels] = capacity;
568
- optional<T> tmp; // space to deserialize min and max
569
573
  optional<T> min_item;
570
574
  optional<T> max_item;
571
575
  if (!is_single_item) {
572
- ptr += sd.deserialize(ptr, end_ptr - ptr, &*tmp, 1);
576
+ // Space to deserialize min and max.
577
+ // serde::deserialize expects allocated but not initialized storage.
578
+ typename std::aligned_storage<sizeof(T), alignof(T)>::type tmp_storage;
579
+ T* tmp = reinterpret_cast<T*>(&tmp_storage);
580
+
581
+ ptr += sd.deserialize(ptr, end_ptr - ptr, tmp, 1);
573
582
  // serde call did not throw, repackage and cleanup
574
- min_item.emplace(*tmp);
575
- (*tmp).~T();
576
- ptr += sd.deserialize(ptr, end_ptr - ptr, &*tmp, 1);
583
+ min_item.emplace(std::move(*tmp));
584
+ tmp->~T();
585
+ ptr += sd.deserialize(ptr, end_ptr - ptr, tmp, 1);
577
586
  // serde call did not throw, repackage and cleanup
578
- max_item.emplace(*tmp);
579
- (*tmp).~T();
587
+ max_item.emplace(std::move(*tmp));
588
+ tmp->~T();
580
589
  }
581
590
  A alloc(allocator);
582
591
  auto items_buffer_deleter = [capacity, &alloc](T* ptr) { alloc.deallocate(ptr, capacity); };
@@ -100,4 +100,28 @@ TEST_CASE("kll string", "[serde_compat]") {
100
100
  }
101
101
  }
102
102
 
103
+ TEST_CASE("kll long", "[serde_compat]") {
104
+ const unsigned n_arr[] = {0, 1, 10, 100, 1000, 10000, 100000, 1000000};
105
+ for (const unsigned n: n_arr) {
106
+ std::ifstream is;
107
+ is.exceptions(std::ios::failbit | std::ios::badbit);
108
+ is.open(testBinaryInputPath + "kll_long_n" + std::to_string(n) + "_java.sk", std::ios::binary);
109
+ const auto sketch = kll_sketch<long>::deserialize(is);
110
+ REQUIRE(sketch.is_empty() == (n == 0));
111
+ REQUIRE(sketch.is_estimation_mode() == (n > kll_constants::DEFAULT_K));
112
+ REQUIRE(sketch.get_n() == n);
113
+ if (n > 0) {
114
+ REQUIRE(sketch.get_min_item() == 1);
115
+ REQUIRE(sketch.get_max_item() == static_cast<long>(n));
116
+ uint64_t weight = 0;
117
+ for (const auto pair: sketch) {
118
+ REQUIRE(pair.first >= sketch.get_min_item());
119
+ REQUIRE(pair.first <= sketch.get_max_item());
120
+ weight += pair.second;
121
+ }
122
+ REQUIRE(weight == sketch.get_n());
123
+ }
124
+ }
125
+ }
126
+
103
127
  } /* namespace datasketches */
@@ -43,6 +43,16 @@ TEST_CASE("kll sketch double generate", "[serialize_for_java]") {
43
43
  }
44
44
  }
45
45
 
46
+ TEST_CASE("kll sketch long generate", "[serialize_for_java]") {
47
+ const unsigned n_arr[] = {0, 1, 10, 100, 1000, 10000, 100000, 1000000};
48
+ for (const unsigned n: n_arr) {
49
+ kll_sketch<long> sketch;
50
+ for (unsigned i = 1; i <= n; ++i) sketch.update(i);
51
+ std::ofstream os("kll_long_n" + std::to_string(n) + "_cpp.sk", std::ios::binary);
52
+ sketch.serialize(os);
53
+ }
54
+ }
55
+
46
56
  struct compare_as_number {
47
57
  bool operator()(const std::string& a, const std::string& b) const {
48
58
  return std::stoi(a) < std::stoi(b);
@@ -47,6 +47,11 @@ namespace quantiles_constants {
47
47
  * The analysis is obtained using get_rank() and get_quantile() functions,
48
48
  * the Probability Mass Function from get_PMF() and the Cumulative Distribution Function from get_CDF().
49
49
  *
50
+ * Sketch that may retain string values.
51
+ * For sketches 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.
54
+ *
50
55
  * <p>Consider a large stream of one million values such as packet sizes coming into a network node.
51
56
  * The natural rank of any specific size value is its index in the hypothetical sorted
52
57
  * array of values.
@@ -206,6 +211,8 @@ public:
206
211
 
207
212
  /**
208
213
  * Updates this sketch with the given data item.
214
+ * If cross-language portability is required, callers should ensure that
215
+ * the input string uses a compatible encoding (valid UTF-8).
209
216
  * @param item from a stream of items
210
217
  */
211
218
  template<typename FwdT>
@@ -213,6 +220,8 @@ public:
213
220
 
214
221
  /**
215
222
  * Merges another sketch into this one.
223
+ * If sketches contain strings, callers are responsible for ensuring that
224
+ * both sketches were built using compatible string encodings.
216
225
  * @param other sketch to merge into this one
217
226
  */
218
227
  template<typename FwdSk>
@@ -537,10 +546,10 @@ private:
537
546
  static void merge_two_size_k_buffers(Level& arr_in_1, Level& arr_in_2, Level& arr_out, const Comparator& comparator);
538
547
 
539
548
  template<typename SerDe>
540
- static Level deserialize_array(std::istream& is, uint32_t num_items, uint32_t capcacity, const SerDe& serde, const Allocator& allocator);
549
+ static Level deserialize_array(std::istream& is, uint32_t num_items, uint32_t capacity, const SerDe& serde, const Allocator& allocator);
541
550
 
542
551
  template<typename SerDe>
543
- static std::pair<Level, size_t> deserialize_array(const void* bytes, size_t size, uint32_t num_items, uint32_t capcacity, const SerDe& serde, const Allocator& allocator);
552
+ static std::pair<Level, size_t> deserialize_array(const void* bytes, size_t size, uint32_t num_items, uint32_t capacity, const SerDe& serde, const Allocator& allocator);
544
553
 
545
554
  static void check_k(uint16_t k);
546
555
  static void check_serial_version(uint8_t serial_version);
@@ -25,6 +25,7 @@
25
25
  #include <stdexcept>
26
26
  #include <iomanip>
27
27
  #include <sstream>
28
+ #include <type_traits>
28
29
 
29
30
  #include "count_zeros.hpp"
30
31
  #include "conditional_forward.hpp"
@@ -393,18 +394,22 @@ auto quantiles_sketch<T, C, A>::deserialize(std::istream &is, const SerDe& serde
393
394
  const bool is_compact = (serial_version == 2) | ((flags_byte & (1 << flags::IS_COMPACT)) > 0);
394
395
  const bool is_sorted = (flags_byte & (1 << flags::IS_SORTED)) > 0;
395
396
 
396
- optional<T> tmp; // space to deserialize min and max
397
397
  optional<T> min_item;
398
398
  optional<T> max_item;
399
399
 
400
- serde.deserialize(is, &*tmp, 1);
400
+ // Space to deserialize min and max.
401
+ // serde::deserialize expects allocated but not initialized storage.
402
+ typename std::aligned_storage<sizeof(T), alignof(T)>::type tmp_storage;
403
+ T* tmp = reinterpret_cast<T*>(&tmp_storage);
404
+
405
+ serde.deserialize(is, tmp, 1);
401
406
  // serde call did not throw, repackage and cleanup
402
- min_item.emplace(*tmp);
403
- (*tmp).~T();
404
- serde.deserialize(is, &*tmp, 1);
407
+ min_item.emplace(std::move(*tmp));
408
+ tmp->~T();
409
+ serde.deserialize(is, tmp, 1);
405
410
  // serde call did not throw, repackage and cleanup
406
- max_item.emplace(*tmp);
407
- (*tmp).~T();
411
+ max_item.emplace(std::move(*tmp));
412
+ tmp->~T();
408
413
 
409
414
  if (serial_version == 1) {
410
415
  read<uint64_t>(is); // no longer used
@@ -507,18 +512,22 @@ auto quantiles_sketch<T, C, A>::deserialize(const void* bytes, size_t size, cons
507
512
  const bool is_compact = (serial_version == 2) | ((flags_byte & (1 << flags::IS_COMPACT)) > 0);
508
513
  const bool is_sorted = (flags_byte & (1 << flags::IS_SORTED)) > 0;
509
514
 
510
- optional<T> tmp; // space to deserialize min and max
511
515
  optional<T> min_item;
512
516
  optional<T> max_item;
513
517
 
514
- ptr += serde.deserialize(ptr, end_ptr - ptr, &*tmp, 1);
518
+ // Space to deserialize min and max.
519
+ // serde::deserialize expects allocated but not initialized storage.
520
+ typename std::aligned_storage<sizeof(T), alignof(T)>::type tmp_storage;
521
+ T* tmp = reinterpret_cast<T*>(&tmp_storage);
522
+
523
+ ptr += serde.deserialize(ptr, end_ptr - ptr, tmp, 1);
515
524
  // serde call did not throw, repackage and cleanup
516
- min_item.emplace(*tmp);
517
- (*tmp).~T();
518
- ptr += serde.deserialize(ptr, end_ptr - ptr, &*tmp, 1);
525
+ min_item.emplace(std::move(*tmp));
526
+ tmp->~T();
527
+ ptr += serde.deserialize(ptr, end_ptr - ptr, tmp, 1);
519
528
  // serde call did not throw, repackage and cleanup
520
- max_item.emplace(*tmp);
521
- (*tmp).~T();
529
+ max_item.emplace(std::move(*tmp));
530
+ tmp->~T();
522
531
 
523
532
  if (serial_version == 1) {
524
533
  uint64_t unused_long;
@@ -581,7 +590,7 @@ auto quantiles_sketch<T, C, A>::deserialize_array(const void* bytes, size_t size
581
590
  // serde did not throw, enable destructors
582
591
  items.get_deleter().set_destroy(true);
583
592
 
584
- // succesfully read, now put into a Level
593
+ // successfully read, now put into a Level
585
594
  Level level(allocator);
586
595
  level.reserve(capacity);
587
596
  level.insert(level.begin(),
@@ -35,6 +35,11 @@ namespace datasketches {
35
35
  * "Relative Error Streaming Quantiles" by Graham Cormode, Zohar Karnin, Edo Liberty,
36
36
  * Justin Thaler, Pavel Veselý, and loosely derived from a Python prototype written by Pavel Veselý.
37
37
  *
38
+ * Sketch that may retain string values.
39
+ * For sketches containing strings, cross-language portability depends on
40
+ * using compatible string encodings. This class does not by itself enforce
41
+ * UTF-8 validity for all string inputs.
42
+ *
38
43
  * <p>Reference: https://arxiv.org/abs/2004.01668</p>
39
44
  *
40
45
  * <p>This implementation differs from the algorithm described in the paper in the following:</p>
@@ -179,6 +184,8 @@ public:
179
184
 
180
185
  /**
181
186
  * Updates this sketch with the given data item.
187
+ * If cross-language portability is required, callers should ensure that
188
+ * the input string uses a compatible encoding (valid UTF-8).
182
189
  * @param item from a stream of items
183
190
  */
184
191
  template<typename FwdT>
@@ -186,6 +193,8 @@ public:
186
193
 
187
194
  /**
188
195
  * Merges another sketch into this one.
196
+ * If sketches contain strings, callers are responsible for ensuring that
197
+ * both sketches were built using compatible string encodings.
189
198
  * @param other sketch to merge into this one
190
199
  */
191
200
  template<typename FwdSk>
@@ -22,6 +22,7 @@
22
22
 
23
23
  #include <sstream>
24
24
  #include <stdexcept>
25
+ #include <type_traits>
25
26
 
26
27
  namespace datasketches {
27
28
 
@@ -292,7 +293,7 @@ double req_sketch<T, C, A>::get_rank_upper_bound(double rank, uint8_t num_std_de
292
293
 
293
294
  template<typename T, typename C, typename A>
294
295
  double req_sketch<T, C, A>::get_RSE(uint16_t k, double rank, bool hra, uint64_t n) {
295
- return get_rank_lb(k, 2, rank, 1, n, hra);
296
+ return get_rank_ub(k, 2, rank, 1, n, hra) - rank;
296
297
  }
297
298
 
298
299
  template<typename T, typename C, typename A>
@@ -461,7 +462,6 @@ req_sketch<T, C, A> req_sketch<T, C, A>::deserialize(std::istream& is, const Ser
461
462
  const bool hra = flags_byte & (1 << flags::IS_HIGH_RANK);
462
463
  if (is_empty) return req_sketch(k, hra, comparator, allocator);
463
464
 
464
- optional<T> tmp; // space to deserialize min and max
465
465
  optional<T> min_item;
466
466
  optional<T> max_item;
467
467
 
@@ -472,14 +472,19 @@ req_sketch<T, C, A> req_sketch<T, C, A>::deserialize(std::istream& is, const Ser
472
472
  uint64_t n = 1;
473
473
  if (num_levels > 1) {
474
474
  n = read<uint64_t>(is);
475
- sd.deserialize(is, &*tmp, 1);
475
+ // Space to deserialize min and max.
476
+ // serde::deserialize expects allocated but not initialized storage.
477
+ typename std::aligned_storage<sizeof(T), alignof(T)>::type tmp_storage;
478
+ T* tmp = reinterpret_cast<T*>(&tmp_storage);
479
+
480
+ sd.deserialize(is, tmp, 1);
476
481
  // serde call did not throw, repackage and cleanup
477
- min_item.emplace(*tmp);
478
- (*tmp).~T();
479
- sd.deserialize(is, &*tmp, 1);
482
+ min_item.emplace(std::move(*tmp));
483
+ tmp->~T();
484
+ sd.deserialize(is, tmp, 1);
480
485
  // serde call did not throw, repackage and cleanup
481
- max_item.emplace(*tmp);
482
- (*tmp).~T();
486
+ max_item.emplace(std::move(*tmp));
487
+ tmp->~T();
483
488
  }
484
489
 
485
490
  if (raw_items) {
@@ -537,7 +542,6 @@ req_sketch<T, C, A> req_sketch<T, C, A>::deserialize(const void* bytes, size_t s
537
542
  const bool hra = flags_byte & (1 << flags::IS_HIGH_RANK);
538
543
  if (is_empty) return req_sketch(k, hra, comparator, allocator);
539
544
 
540
- optional<T> tmp; // space to deserialize min and max
541
545
  optional<T> min_item;
542
546
  optional<T> max_item;
543
547
 
@@ -549,14 +553,19 @@ req_sketch<T, C, A> req_sketch<T, C, A>::deserialize(const void* bytes, size_t s
549
553
  if (num_levels > 1) {
550
554
  ensure_minimum_memory(end_ptr - ptr, sizeof(n));
551
555
  ptr += copy_from_mem(ptr, n);
552
- ptr += sd.deserialize(ptr, end_ptr - ptr, &*tmp, 1);
556
+ // Space to deserialize min and max.
557
+ // serde::deserialize expects allocated but not initialized storage.
558
+ typename std::aligned_storage<sizeof(T), alignof(T)>::type tmp_storage;
559
+ T* tmp = reinterpret_cast<T*>(&tmp_storage);
560
+
561
+ ptr += sd.deserialize(ptr, end_ptr - ptr, tmp, 1);
553
562
  // serde call did not throw, repackage and cleanup
554
- min_item.emplace(*tmp);
555
- (*tmp).~T();
556
- ptr += sd.deserialize(ptr, end_ptr - ptr, &*tmp, 1);
563
+ min_item.emplace(std::move(*tmp));
564
+ tmp->~T();
565
+ ptr += sd.deserialize(ptr, end_ptr - ptr, tmp, 1);
557
566
  // serde call did not throw, repackage and cleanup
558
- max_item.emplace(*tmp);
559
- (*tmp).~T();
567
+ max_item.emplace(std::move(*tmp));
568
+ tmp->~T();
560
569
  }
561
570
 
562
571
  if (raw_items) {
@@ -52,4 +52,50 @@ TEST_CASE("req float", "[serde_compat]") {
52
52
  }
53
53
  }
54
54
 
55
+ TEST_CASE("req float negative", "[serde_compat]") {
56
+ const unsigned n_arr[] = {1, 10};
57
+ for (const unsigned n: n_arr) {
58
+ std::ifstream is;
59
+ is.exceptions(std::ios::failbit | std::ios::badbit);
60
+ is.open(testBinaryInputPath + "req_float_negative_n" + std::to_string(n) + "_java.sk", std::ios::binary);
61
+ const auto sketch = req_sketch<float>::deserialize(is);
62
+ REQUIRE(sketch.is_HRA());
63
+ REQUIRE_FALSE(sketch.is_empty());
64
+ REQUIRE_FALSE(sketch.is_estimation_mode());
65
+ REQUIRE(sketch.get_n() == n);
66
+ REQUIRE(sketch.get_min_item() == -static_cast<float>(n));
67
+ REQUIRE(sketch.get_max_item() == -1.0f);
68
+ uint64_t weight = 0;
69
+ for (const auto pair: sketch) {
70
+ REQUIRE(pair.first >= sketch.get_min_item());
71
+ REQUIRE(pair.first <= sketch.get_max_item());
72
+ weight += pair.second;
73
+ }
74
+ REQUIRE(weight == sketch.get_n());
75
+ }
76
+ }
77
+
78
+ TEST_CASE("req float mixed", "[serde_compat]") {
79
+ const unsigned n_arr[] = {1, 10};
80
+ for (const unsigned n: n_arr) {
81
+ std::ifstream is;
82
+ is.exceptions(std::ios::failbit | std::ios::badbit);
83
+ is.open(testBinaryInputPath + "req_float_mixed_n" + std::to_string(n) + "_java.sk", std::ios::binary);
84
+ const auto sketch = req_sketch<float>::deserialize(is);
85
+ REQUIRE(sketch.is_HRA());
86
+ REQUIRE_FALSE(sketch.is_empty());
87
+ REQUIRE_FALSE(sketch.is_estimation_mode());
88
+ REQUIRE(sketch.get_n() == 2 * n + 1);
89
+ REQUIRE(sketch.get_min_item() == -static_cast<float>(n));
90
+ REQUIRE(sketch.get_max_item() == static_cast<float>(n));
91
+ uint64_t weight = 0;
92
+ for (const auto pair: sketch) {
93
+ REQUIRE(pair.first >= sketch.get_min_item());
94
+ REQUIRE(pair.first <= sketch.get_max_item());
95
+ weight += pair.second;
96
+ }
97
+ REQUIRE(weight == sketch.get_n());
98
+ }
99
+ }
100
+
55
101
  } /* namespace datasketches */
@@ -33,4 +33,24 @@ TEST_CASE("req sketch float generate", "[serialize_for_java]") {
33
33
  }
34
34
  }
35
35
 
36
+ TEST_CASE("req sketch float negative generate", "[serialize_for_java]") {
37
+ const unsigned n_arr[] = {1, 10};
38
+ for (const unsigned n: n_arr) {
39
+ req_sketch<float> sketch(12);
40
+ for (unsigned i = 1; i <= n; ++i) sketch.update(-static_cast<float>(i));
41
+ std::ofstream os("req_float_negative_n" + std::to_string(n) + "_cpp.sk", std::ios::binary);
42
+ sketch.serialize(os);
43
+ }
44
+ }
45
+
46
+ TEST_CASE("req sketch float mixed generate", "[serialize_for_java]") {
47
+ const unsigned n_arr[] = {1, 10};
48
+ for (const unsigned n: n_arr) {
49
+ req_sketch<float> sketch(12);
50
+ for (int i = -static_cast<int>(n); i <= static_cast<int>(n); ++i) sketch.update(static_cast<float>(i));
51
+ std::ofstream os("req_float_mixed_n" + std::to_string(n) + "_cpp.sk", std::ios::binary);
52
+ sketch.serialize(os);
53
+ }
54
+ }
55
+
36
56
  } /* namespace datasketches */
@@ -43,6 +43,7 @@ TEST_CASE("req sketch: empty", "[req_sketch]") {
43
43
  REQUIRE_FALSE(sketch.is_estimation_mode());
44
44
  REQUIRE(sketch.get_n() == 0);
45
45
  REQUIRE(sketch.get_num_retained() == 0);
46
+ REQUIRE(sketch.get_RSE(sketch.get_k(), 0.5, true, 0) == 0);
46
47
  REQUIRE_THROWS_AS(sketch.get_min_item(), std::runtime_error);
47
48
  REQUIRE_THROWS_AS(sketch.get_max_item(), std::runtime_error);
48
49
  REQUIRE_THROWS_AS(sketch.get_rank(0), std::runtime_error);
@@ -61,6 +62,7 @@ TEST_CASE("req sketch: single value, lra", "[req_sketch]") {
61
62
  REQUIRE_FALSE(sketch.is_estimation_mode());
62
63
  REQUIRE(sketch.get_n() == 1);
63
64
  REQUIRE(sketch.get_num_retained() == 1);
65
+ REQUIRE(sketch.get_RSE(sketch.get_k(), 0.5, false, sketch.get_n()) == 0);
64
66
  REQUIRE(sketch.get_rank(1.0f, false) == 0);
65
67
  REQUIRE(sketch.get_rank(1.0f) == 1);
66
68
  REQUIRE(sketch.get_rank(1.1f, false) == 1);
@@ -314,6 +316,74 @@ TEST_CASE("req sketch: byte serialize-deserialize estimation mode", "[req_sketch
314
316
  REQUIRE(sketch2.get_max_item() == sketch.get_max_item());
315
317
  }
316
318
 
319
+ TEST_CASE("req sketch: stream serialize-deserialize negative values", "[req_sketch]") {
320
+ req_sketch<float> sketch(12);
321
+ const size_t n = 50;
322
+ for (size_t i = 1; i <= n; ++i) sketch.update(-static_cast<float>(i));
323
+
324
+ std::stringstream s(std::ios::in | std::ios::out | std::ios::binary);
325
+ sketch.serialize(s);
326
+ auto sketch2 = req_sketch<float>::deserialize(s);
327
+ REQUIRE(s.tellg() == s.tellp());
328
+ REQUIRE(sketch2.is_empty() == sketch.is_empty());
329
+ REQUIRE(sketch2.is_estimation_mode() == sketch.is_estimation_mode());
330
+ REQUIRE(sketch2.get_num_retained() == sketch.get_num_retained());
331
+ REQUIRE(sketch2.get_n() == sketch.get_n());
332
+ REQUIRE(sketch2.get_min_item() == -static_cast<float>(n));
333
+ REQUIRE(sketch2.get_max_item() == -1.0f);
334
+ }
335
+
336
+ TEST_CASE("req sketch: byte serialize-deserialize negative values", "[req_sketch]") {
337
+ req_sketch<float> sketch(12);
338
+ const size_t n = 50;
339
+ for (size_t i = 1; i <= n; ++i) sketch.update(-static_cast<float>(i));
340
+
341
+ auto bytes = sketch.serialize();
342
+ REQUIRE(bytes.size() == sketch.get_serialized_size_bytes());
343
+ auto sketch2 = req_sketch<float>::deserialize(bytes.data(), bytes.size());
344
+ REQUIRE(bytes.size() == sketch2.get_serialized_size_bytes());
345
+ REQUIRE(sketch2.is_empty() == sketch.is_empty());
346
+ REQUIRE(sketch2.is_estimation_mode() == sketch.is_estimation_mode());
347
+ REQUIRE(sketch2.get_num_retained() == sketch.get_num_retained());
348
+ REQUIRE(sketch2.get_n() == sketch.get_n());
349
+ REQUIRE(sketch2.get_min_item() == -static_cast<float>(n));
350
+ REQUIRE(sketch2.get_max_item() == -1.0f);
351
+ }
352
+
353
+ TEST_CASE("req sketch: stream serialize-deserialize mixed values", "[req_sketch]") {
354
+ req_sketch<float> sketch(12);
355
+ const int half = 25;
356
+ for (int i = -half; i < half; ++i) sketch.update(static_cast<float>(i));
357
+
358
+ std::stringstream s(std::ios::in | std::ios::out | std::ios::binary);
359
+ sketch.serialize(s);
360
+ auto sketch2 = req_sketch<float>::deserialize(s);
361
+ REQUIRE(s.tellg() == s.tellp());
362
+ REQUIRE(sketch2.is_empty() == sketch.is_empty());
363
+ REQUIRE(sketch2.is_estimation_mode() == sketch.is_estimation_mode());
364
+ REQUIRE(sketch2.get_num_retained() == sketch.get_num_retained());
365
+ REQUIRE(sketch2.get_n() == sketch.get_n());
366
+ REQUIRE(sketch2.get_min_item() == static_cast<float>(-half));
367
+ REQUIRE(sketch2.get_max_item() == static_cast<float>(half - 1));
368
+ }
369
+
370
+ TEST_CASE("req sketch: byte serialize-deserialize mixed values", "[req_sketch]") {
371
+ req_sketch<float> sketch(12);
372
+ const int half = 25;
373
+ for (int i = -half; i < half; ++i) sketch.update(static_cast<float>(i));
374
+
375
+ auto bytes = sketch.serialize();
376
+ REQUIRE(bytes.size() == sketch.get_serialized_size_bytes());
377
+ auto sketch2 = req_sketch<float>::deserialize(bytes.data(), bytes.size());
378
+ REQUIRE(bytes.size() == sketch2.get_serialized_size_bytes());
379
+ REQUIRE(sketch2.is_empty() == sketch.is_empty());
380
+ REQUIRE(sketch2.is_estimation_mode() == sketch.is_estimation_mode());
381
+ REQUIRE(sketch2.get_num_retained() == sketch.get_num_retained());
382
+ REQUIRE(sketch2.get_n() == sketch.get_n());
383
+ REQUIRE(sketch2.get_min_item() == static_cast<float>(-half));
384
+ REQUIRE(sketch2.get_max_item() == static_cast<float>(half - 1));
385
+ }
386
+
317
387
  TEST_CASE("req sketch: serialize deserialize stream and bytes equivalence", "[req_sketch]") {
318
388
  req_sketch<float> sketch(12);
319
389
  const size_t n = 100000;