Skip to content

Commit 20bdb1c

Browse files
committed
Encode decode store
1 parent 50d513e commit 20bdb1c

9 files changed

Lines changed: 220 additions & 3 deletions

ddsketch/include/collapsing_dense_store.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ class CollapsingDenseStore : public DenseStore<Derived, Allocator> {
3232
explicit CollapsingDenseStore(size_type max_num_bins);
3333
CollapsingDenseStore(const CollapsingDenseStore<Derived, Allocator>& other) = default;
3434

35+
CollapsingDenseStore<Derived,Allocator>& operator=(const CollapsingDenseStore<Derived, Allocator>& other);
36+
37+
void serialize(std::ostream& os) const;
38+
static Derived deserialize(std::istream& is);
39+
3540
~CollapsingDenseStore() = default;
3641

3742
void clear();

ddsketch/include/collapsing_dense_store_impl.hpp

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,20 @@ namespace datasketches {
2626
template<class Derived, typename Allocator>
2727
CollapsingDenseStore<Derived, Allocator>::CollapsingDenseStore(size_type max_num_bins): DenseStore<Derived, Allocator>(), max_num_bins(max_num_bins), is_collapsed(false) {}
2828

29-
// TODO implement copy constructor
29+
template<class Derived, typename Allocator>
30+
CollapsingDenseStore<Derived, Allocator>& CollapsingDenseStore<Derived, Allocator>::operator=(const CollapsingDenseStore<Derived, Allocator>& other) {
31+
if (max_num_bins != other.max_num_bins) {
32+
throw std::runtime_error("stores must have same maximum number of bins");
33+
}
34+
35+
this->bins = other.bins;
36+
this->offset = other.offset;
37+
this->min_index = other.min_index;
38+
this->max_index = other.max_index;
39+
40+
41+
return *this;
42+
}
3043

3144
template<class Derived, typename Allocator>
3245
typename CollapsingDenseStore<Derived, Allocator>::size_type CollapsingDenseStore<Derived, Allocator>::get_new_length(size_type new_min_index, size_type new_max_index) const {
@@ -39,5 +52,29 @@ void CollapsingDenseStore<Derived, Allocator>::clear() {
3952
is_collapsed = false;
4053
}
4154

55+
template<class Derived, typename Allocator>
56+
void CollapsingDenseStore<Derived, Allocator>::serialize(std::ostream& os) const {
57+
write(os, max_num_bins);
58+
if (this->is_empty()) {
59+
return;
60+
}
61+
write(os, is_collapsed);
62+
63+
this->serialize_common(os);
64+
}
65+
66+
template<class Derived, typename Allocator>
67+
Derived CollapsingDenseStore<Derived, Allocator>::deserialize(std::istream& is) {
68+
Derived store(read<int>(is));
69+
70+
if (is.peek() == std::istream::traits_type::eof()) {
71+
return store;
72+
}
73+
store.is_collapsed = read<bool>(is);
74+
Derived::deserialize_common(store, is);
75+
76+
return store;
77+
}
78+
4279
}
4380
#endif //COLLAPSING_DENSE_STORE_IMPL_HPP

ddsketch/include/dense_store.hpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,10 @@ class DenseStore {
5050

5151
void merge(const SparseStore<Allocator>& other);
5252

53-
void serialize(std::ostream& is) const;
54-
DenseStore<Derived, Allocator> deserialize(std::ostream& is);
53+
void serialize(std::ostream& os) const;
54+
static Derived deserialize(std::istream& is);
55+
56+
bool operator==(const DenseStore<Derived, Allocator>& other) const;
5557

5658
iterator begin() const;
5759
iterator end() const;
@@ -132,6 +134,9 @@ class DenseStore {
132134
void reset_bins();
133135
void reset_bins(size_type from_index, size_type to_index);
134136

137+
void serialize_common(std::ostream& os) const;
138+
static void deserialize_common(Derived& store, std::istream& is);
139+
135140
Derived& derived();
136141
const Derived& derived() const;
137142
};

ddsketch/include/dense_store_impl.hpp

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
#define DENSE_STORE_IMPL_HPP
2222

2323
#include <algorithm>
24+
#include <filesystem>
25+
26+
#include "common_defs.hpp"
2427

2528
namespace datasketches {
2629

@@ -247,6 +250,14 @@ void DenseStore<Derived, Allocator>::reset_bins(size_type from_index, size_type
247250
std::fill(bins.begin() + from_index - offset, bins.begin() + to_index - offset + 1, 0);
248251
}
249252

253+
template<class Derived, typename Allocator>
254+
bool DenseStore<Derived, Allocator>::operator==(const DenseStore<Derived, Allocator>& other) const {
255+
return offset == other.offset &&
256+
min_index == other.min_index &&
257+
max_index == other.max_index &&
258+
bins == other.bins;
259+
}
260+
250261
template<class Derived, typename Allocator>
251262
Derived& DenseStore<Derived, Allocator>::derived() {
252263
return static_cast<Derived&>(*this);
@@ -338,11 +349,62 @@ typename DenseStore<Derived, Allocator>::reverse_iterator::reference DenseStore<
338349

339350
template<class Derived, typename Allocator>
340351
void DenseStore<Derived, Allocator>::serialize(std::ostream& os) const {
352+
derived().serialize(os);
353+
}
354+
355+
template<class Derived, typename Allocator>
356+
Derived DenseStore<Derived, Allocator>::deserialize(std::istream& is) {
357+
return Derived::deserialize(is);
358+
}
359+
360+
template<class Derived, typename Allocator>
361+
void DenseStore<Derived, Allocator>::serialize_common(std::ostream& os) const {
341362
if (is_empty()) {
342363
return;
343364
}
344365

366+
// Serialize the range information
367+
write(os, min_index);
368+
write(os, max_index);
369+
write(os, offset);
345370

371+
// Serialize the bins array (only the used portion)
372+
const size_type num_bins = bins.size();
373+
write(os, num_bins);
374+
375+
size_type non_empty_bins = 0;
376+
for (const double& count : bins) {
377+
non_empty_bins += (count > 0.0);
378+
}
379+
write(os, non_empty_bins);
380+
381+
for (const Bin& bin : *this) {
382+
write(os, bin.getIndex());
383+
write(os, bin.getCount());
384+
}
385+
}
386+
387+
template<class Derived, typename Allocator>
388+
void DenseStore<Derived, Allocator>::deserialize_common(Derived& store, std::istream& is) {
389+
if (is.peek() == std::istream::traits_type::eof()) {
390+
return;
391+
}
392+
// Deserialize the range information
393+
store.min_index = read<size_type>(is);
394+
store.max_index = read<size_type>(is);
395+
store.offset = read<size_type>(is);
396+
397+
// Deserialize the bins array
398+
const auto num_bins = read<size_type>(is);
399+
store.bins.resize(num_bins, 0.0);
400+
401+
const auto non_empty_bins = read<size_type>(is);
402+
// Read the actual bin counts
403+
for (size_type i = 0; i < non_empty_bins; ++i) {
404+
const auto index = read<int>(is);
405+
const auto count = read<double>(is);
406+
store.add(index, count);
407+
}
346408
}
347409
}
348410

ddsketch/include/sparse_store.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ class SparseStore {
4343

4444
SparseStore() = default;
4545

46+
bool operator==(const SparseStore &other) const;
47+
4648
void add(int index);
4749
void add(int index, double count);
4850
void add(const Bin& bin);
@@ -57,6 +59,9 @@ class SparseStore {
5759
bool is_empty() const;
5860
double get_total_count() const;
5961

62+
void serialize(std::ostream& os) const;
63+
static SparseStore deserialize(std::istream& is);
64+
6065
iterator begin() const;
6166
iterator end() const;
6267
reverse_iterator rbegin() const;

ddsketch/include/sparse_store_impl.hpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@
2323
#include "sparse_store.hpp"
2424

2525
namespace datasketches {
26+
27+
template<typename Allocator>
28+
bool SparseStore<Allocator>::operator==(const SparseStore &other) const {
29+
return bins == other.bins;
30+
}
31+
2632
template<typename Allocator>
2733
void SparseStore<Allocator>::add(int index) {
2834
add(index, 1);
@@ -190,6 +196,29 @@ double SparseStore<Allocator>::get_total_count() const {
190196
}
191197
return total_count;
192198
}
199+
200+
template<typename Allocator>
201+
void SparseStore<Allocator>::serialize(std::ostream &os) const {
202+
write(os, bins.size());
203+
for (const auto& [index, count] : bins) {
204+
write(os, index);
205+
write(os, count);
206+
}
207+
}
208+
209+
template<typename Allocator>
210+
SparseStore<Allocator> SparseStore<Allocator>::deserialize(std::istream& is) {
211+
SparseStore<Allocator> store;
212+
const auto num_bins = read<typename bins_type::size_type>(is);
213+
for (typename bins_type::size_type i = 0; i < num_bins; ++i) {
214+
const auto index = read<typename bins_type::key_type>(is);
215+
const auto count = read<typename bins_type::mapped_type>(is);
216+
store.bins[index] = count;
217+
}
218+
219+
return store;
220+
}
221+
193222
}
194223

195224
#endif //SPARSE_STORE_IMPL_HPP

ddsketch/include/unbounded_size_dense_store.hpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,15 @@ class UnboundedSizeDenseStore: public DenseStore<UnboundedSizeDenseStore<Allocat
3535
UnboundedSizeDenseStore* copy() const;
3636
~UnboundedSizeDenseStore() = default;
3737

38+
UnboundedSizeDenseStore& operator=(const UnboundedSizeDenseStore& other);
39+
3840
void merge(const UnboundedSizeDenseStore<Allocator>& other);
3941
using DenseStore<UnboundedSizeDenseStore, Allocator>::merge;
4042

43+
44+
void serialize(std::ostream& os) const;
45+
static UnboundedSizeDenseStore deserialize(std::istream& is);
46+
4147
protected:
4248
size_type normalize(size_type index);
4349
void adjust(size_type new_min_index, size_type new_max_index);

ddsketch/include/unbounded_size_dense_store_impl.hpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,19 @@ UnboundedSizeDenseStore<Allocator> *UnboundedSizeDenseStore<Allocator>::copy() c
4848
return new (alloc.allocate(1)) UnboundedSizeDenseStore<Allocator>(*this);
4949
}
5050

51+
template<typename Allocator>
52+
UnboundedSizeDenseStore<Allocator> &UnboundedSizeDenseStore<Allocator>::operator=(const UnboundedSizeDenseStore &other) {
53+
54+
this->bins = other.bins;
55+
this->offset = other.offset;
56+
this->min_index = other.min_index;
57+
this->max_index = other.max_index;
58+
59+
60+
return *this;
61+
}
62+
63+
5164

5265
template<typename Allocator>
5366
void UnboundedSizeDenseStore<Allocator>::adjust(size_type new_min_index, size_type new_max_index) {
@@ -69,6 +82,21 @@ void UnboundedSizeDenseStore<Allocator>::merge(const UnboundedSizeDenseStore<All
6982
}
7083
}
7184

85+
template<typename Allocator>
86+
void UnboundedSizeDenseStore<Allocator>::serialize(std::ostream &os) const {
87+
this->serialize_common(os);
88+
}
89+
90+
template<typename Allocator>
91+
UnboundedSizeDenseStore<Allocator> UnboundedSizeDenseStore<Allocator>::deserialize(std::istream &is) {
92+
UnboundedSizeDenseStore<Allocator> store;
93+
UnboundedSizeDenseStore::deserialize_common(store, is);
94+
95+
return store;
96+
}
97+
98+
99+
72100

73101

74102
}

ddsketch/test/StoreTest.cpp

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
*/
1919

2020
#include <iostream>
21+
#include <sstream>
2122
#include <catch2/catch.hpp>
2223

2324
#include "collapsing_highest_dense_store.hpp"
@@ -543,5 +544,44 @@ TEST_CASE("merge test", "[mergetest]") {
543544

544545
//DDSketch<CollapsingHighestDenseStore<A>, LinearlyInterpolatedMapping> sketch4<1024>(0.99);
545546
}
547+
548+
TEMPLATE_TEST_CASE("dense store serialization test", "[serialization]",
549+
CollapsingLowestStoreTestCase<8>,
550+
CollapsingLowestStoreTestCase<128>,
551+
CollapsingLowestStoreTestCase<1024>,
552+
CollapsingHighestStoreTestCase<8>,
553+
CollapsingHighestStoreTestCase<128>,
554+
CollapsingHighestStoreTestCase<1024>,
555+
UnboundedStoreSizeTestCase,
556+
SparseStoreTestCase
557+
) {
558+
// Test empty store serialization
559+
auto store = *TestType::new_store();
560+
using StoreType = decltype(store);
561+
std::stringstream stream;
562+
563+
store.serialize(stream);
564+
StoreType deserialized_empty_store = StoreType::deserialize(stream);
565+
REQUIRE(store.is_empty());
566+
REQUIRE(deserialized_empty_store.is_empty());
567+
REQUIRE(stream.peek() == std::istream::traits_type::eof());
568+
REQUIRE(store == deserialized_empty_store);
569+
stream.clear();
570+
571+
std::vector<int> indexes{-1000, -1, 0, 1, 1000};
572+
std::vector<double> counts{0, 1, 2, 4, 5, 10, 20, 100, 1000, 10000};
573+
for (int idx: indexes) {
574+
for (double count: counts) {
575+
store.add(idx, count);
576+
}
577+
}
578+
579+
store.serialize(stream);
580+
auto deserialized_store = StoreType::deserialize(stream);
581+
REQUIRE_FALSE(store.is_empty());
582+
REQUIRE_FALSE(deserialized_store.is_empty());
583+
REQUIRE(stream.peek() == std::istream::traits_type::eof());
584+
REQUIRE(store == deserialized_store);
585+
}
546586
}
547587

0 commit comments

Comments
 (0)