Skip to content

Commit 9381dcd

Browse files
committed
fix: empty string handling
1 parent a1e24c8 commit 9381dcd

5 files changed

Lines changed: 334 additions & 172 deletions

tuple/include/array_of_strings_sketch.hpp

Lines changed: 53 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,11 @@ class default_array_of_strings_update_policy {
4949
// serializer/deserializer for an array of strings
5050
// Requirements: all strings must be valid UTF-8 and array size must be <= 127.
5151
template<typename Allocator = std::allocator<std::string>>
52-
struct array_of_strings_serde {
52+
struct default_array_of_strings_serde {
5353
using array_of_strings = array<std::string, Allocator>;
54+
using summary_allocator = typename std::allocator_traits<Allocator>::template rebind_alloc<array_of_strings>;
55+
56+
explicit default_array_of_strings_serde(const Allocator& allocator = Allocator());
5457

5558
void serialize(std::ostream& os, const array_of_strings* items, unsigned num) const;
5659
void deserialize(std::istream& is, array_of_strings* items, unsigned num) const;
@@ -59,6 +62,8 @@ struct array_of_strings_serde {
5962
size_t size_of_item(const array_of_strings& item) const;
6063

6164
private:
65+
Allocator allocator_;
66+
summary_allocator summary_allocator_;
6267
static void check_num_nodes(uint8_t num_nodes);
6368
static uint32_t compute_total_bytes(const array_of_strings& item);
6469
static void check_utf8(const std::string& value);
@@ -79,38 +84,62 @@ class compact_array_of_strings_tuple_sketch:
7984
using summary_allocator = typename std::allocator_traits<Allocator>::template rebind_alloc<array_of_strings>;
8085
using Base = compact_tuple_sketch<array_of_strings, summary_allocator>;
8186
using vector_bytes = typename Base::vector_bytes;
82-
87+
using Base::serialize;
88+
89+
/**
90+
* Copy constructor.
91+
* Constructs a compact sketch from another sketch (update or compact)
92+
* @param other sketch to be constructed from
93+
* @param ordered if true make the resulting sketch ordered
94+
*/
8395
template<typename Sketch>
8496
compact_array_of_strings_tuple_sketch(const Sketch& sketch, bool ordered = true);
8597

86-
void serialize(std::ostream& os) const;
87-
vector_bytes serialize(unsigned header_size_bytes = 0) const;
88-
98+
/**
99+
* This method deserializes a sketch from a given stream.
100+
* @param is input stream
101+
* @param seed the seed for the hash function that was used to create the sketch
102+
* @param sd instance of a SerDe
103+
* @param allocator instance of an Allocator
104+
* @return an instance of the sketch
105+
*/
106+
template<typename SerDe = default_array_of_strings_serde<Allocator>>
89107
static compact_array_of_strings_tuple_sketch deserialize(std::istream& is, uint64_t seed = DEFAULT_SEED,
90-
const Allocator& allocator = Allocator());
108+
const SerDe& sd = SerDe(), const Allocator& allocator = Allocator());
109+
110+
/**
111+
* This method deserializes a sketch from a given array of bytes.
112+
* @param bytes pointer to the array of bytes
113+
* @param size the size of the array
114+
* @param seed the seed for the hash function that was used to create the sketch
115+
* @param sd instance of a SerDe
116+
* @param allocator instance of an Allocator
117+
* @return an instance of the sketch
118+
*/
119+
template<typename SerDe = default_array_of_strings_serde<Allocator>>
91120
static compact_array_of_strings_tuple_sketch deserialize(const void* bytes, size_t size, uint64_t seed = DEFAULT_SEED,
92-
const Allocator& allocator = Allocator());
121+
const SerDe& sd = SerDe(), const Allocator& allocator = Allocator());
93122

94123
private:
95124
explicit compact_array_of_strings_tuple_sketch(Base&& base);
96125
};
97126

98127
/**
99128
* Extended class of update_tuple_sketch for array of strings
100-
* Requirements: all strings must be valid UTF-8 and array size must be <= 127.
101129
*/
102-
template<typename Allocator = std::allocator<std::string>>
130+
template<template<typename> class Policy = default_array_of_strings_update_policy,
131+
typename Allocator = std::allocator<std::string>>
103132
class update_array_of_strings_tuple_sketch:
104133
public update_tuple_sketch<
105134
array<std::string, Allocator>,
106135
array<std::string, Allocator>,
107-
default_array_of_strings_update_policy<Allocator>,
136+
Policy<Allocator>,
108137
typename std::allocator_traits<Allocator>::template rebind_alloc<array<std::string, Allocator>>
109138
> {
110139
public:
111140
using array_of_strings = array<std::string, Allocator>;
112141
using summary_allocator = typename std::allocator_traits<Allocator>::template rebind_alloc<array_of_strings>;
113-
using policy_type = default_array_of_strings_update_policy<Allocator>;
142+
using policy_type = Policy<Allocator>;
114143
using Base = update_tuple_sketch<
115144
array_of_strings,
116145
array_of_strings,
@@ -121,7 +150,18 @@ class update_array_of_strings_tuple_sketch:
121150
class builder;
122151
using Base::update;
123152

153+
/**
154+
* Updates the sketch with string array for both key and value.
155+
* @param key the given string array key
156+
* @param value the given string array value
157+
*/
124158
void update(const array_of_strings& key, const array_of_strings& value);
159+
160+
/**
161+
* Converts this sketch to a compact sketch (ordered or unordered).
162+
* @param ordered optional flag to specify if an ordered sketch should be produced
163+
* @return compact array of strings sketch
164+
*/
125165
compact_array_of_strings_tuple_sketch<Allocator> compact(bool ordered = true) const;
126166

127167
private:
@@ -134,8 +174,8 @@ class update_array_of_strings_tuple_sketch:
134174
static uint64_t hash_key(const array_of_strings& key);
135175
};
136176

137-
template<typename Allocator>
138-
class update_array_of_strings_tuple_sketch<Allocator>::builder:
177+
template<template<typename> class Policy, typename Allocator>
178+
class update_array_of_strings_tuple_sketch<Policy, Allocator>::builder:
139179
public tuple_base_builder<builder, policy_type, summary_allocator> {
140180
public:
141181
builder(const policy_type& policy = policy_type(), const summary_allocator& allocator = summary_allocator());

tuple/include/array_of_strings_sketch_impl.hpp

Lines changed: 54 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ template<typename Allocator>
4040
void default_array_of_strings_update_policy<Allocator>::update(
4141
array_of_strings& array, const array_of_strings& input
4242
) const {
43-
const auto length = input.size();
44-
array = array_of_strings(length, "", allocator_);
45-
for (uint8_t i = 0; i < length; ++i) array[i] = input[i];
43+
const auto length = static_cast<size_t>(input.size());
44+
array = array_of_strings(static_cast<uint8_t>(length), "", allocator_);
45+
for (size_t i = 0; i < length; ++i) array[i] = input[i];
4646
}
4747

4848
template<typename Allocator>
@@ -53,53 +53,53 @@ void default_array_of_strings_update_policy<Allocator>::update(
5353
array = array_of_strings(0, "", allocator_);
5454
return;
5555
}
56-
const auto length = input->size();
57-
array = array_of_strings(length, "", allocator_);
58-
for (uint8_t i = 0; i < length; ++i) array[i] = (*input)[i];
56+
const auto length = static_cast<size_t>(input->size());
57+
array = array_of_strings(static_cast<uint8_t>(length), "", allocator_);
58+
for (size_t i = 0; i < length; ++i) array[i] = (*input)[i];
5959
}
6060

61-
template<typename Allocator>
62-
update_array_of_strings_tuple_sketch<Allocator>::update_array_of_strings_tuple_sketch(
61+
template<template<typename> class Policy, typename Allocator>
62+
update_array_of_strings_tuple_sketch<Policy, Allocator>::update_array_of_strings_tuple_sketch(
6363
uint8_t lg_cur_size, uint8_t lg_nom_size, resize_factor rf, float p, uint64_t theta,
6464
uint64_t seed, const policy_type& policy, const summary_allocator& allocator
6565
):
6666
Base(lg_cur_size, lg_nom_size, rf, p, theta, seed, policy, allocator) {}
6767

68-
template<typename Allocator>
69-
void update_array_of_strings_tuple_sketch<Allocator>::update(
68+
template<template<typename> class Policy, typename Allocator>
69+
void update_array_of_strings_tuple_sketch<Policy, Allocator>::update(
7070
const array_of_strings& key, const array_of_strings& value
7171
) {
7272
const uint64_t hash = hash_key(key);
7373
Base::update(hash, value);
7474
}
7575

76-
template<typename Allocator>
77-
uint64_t update_array_of_strings_tuple_sketch<Allocator>::hash_key(const array_of_strings& key) {
76+
template<template<typename> class Policy, typename Allocator>
77+
uint64_t update_array_of_strings_tuple_sketch<Policy, Allocator>::hash_key(const array_of_strings& key) {
7878
XXHash64 hasher(STRING_ARR_HASH_SEED);
7979
const auto size = static_cast<size_t>(key.size());
8080
for (size_t i = 0; i < size; ++i) {
81-
const auto& entry = key[static_cast<uint8_t>(i)];
81+
const auto& entry = key[i];
8282
hasher.add(entry.data(), entry.size());
8383
if (i + 1 < size) hasher.add(",", 1);
8484
}
8585
return hasher.hash();
8686
}
8787

88-
template<typename Allocator>
89-
compact_array_of_strings_tuple_sketch<Allocator> update_array_of_strings_tuple_sketch<Allocator>::compact(bool ordered) const {
88+
template<template<typename> class Policy, typename Allocator>
89+
compact_array_of_strings_tuple_sketch<Allocator> update_array_of_strings_tuple_sketch<Policy, Allocator>::compact(bool ordered) const {
9090
return compact_array_of_strings_tuple_sketch<Allocator>(*this, ordered);
9191
}
9292

9393
// builder
9494

95-
template<typename Allocator>
96-
update_array_of_strings_tuple_sketch<Allocator>::builder::builder(
95+
template<template<typename> class Policy, typename Allocator>
96+
update_array_of_strings_tuple_sketch<Policy, Allocator>::builder::builder(
9797
const policy_type& policy, const summary_allocator& allocator
9898
):
9999
tuple_base_builder<builder, policy_type, summary_allocator>(policy, allocator) {}
100100

101-
template<typename Allocator>
102-
auto update_array_of_strings_tuple_sketch<Allocator>::builder::build() const -> update_array_of_strings_tuple_sketch {
101+
template<template<typename> class Policy, typename Allocator>
102+
auto update_array_of_strings_tuple_sketch<Policy, Allocator>::builder::build() const -> update_array_of_strings_tuple_sketch {
103103
return update_array_of_strings_tuple_sketch(
104104
this->starting_lg_size(),
105105
this->lg_k_,
@@ -124,35 +124,32 @@ compact_array_of_strings_tuple_sketch<Allocator>::compact_array_of_strings_tuple
124124
): Base(std::move(base)) {}
125125

126126
template<typename Allocator>
127-
void compact_array_of_strings_tuple_sketch<Allocator>::serialize(std::ostream& os) const {
128-
Base::serialize(os, array_of_strings_serde<Allocator>());
129-
}
130-
131-
template<typename Allocator>
132-
auto compact_array_of_strings_tuple_sketch<Allocator>::serialize(unsigned header_size_bytes) const -> vector_bytes {
133-
return Base::serialize(header_size_bytes, array_of_strings_serde<Allocator>());
134-
}
135-
136-
template<typename Allocator>
127+
template<typename SerDe>
137128
auto compact_array_of_strings_tuple_sketch<Allocator>::deserialize(
138-
std::istream& is, uint64_t seed, const Allocator& allocator
129+
std::istream& is, uint64_t seed, const SerDe& sd, const Allocator& allocator
139130
) -> compact_array_of_strings_tuple_sketch {
140131
summary_allocator alloc(allocator);
141-
auto base = Base::deserialize(is, seed, array_of_strings_serde<Allocator>(), alloc);
132+
auto base = Base::deserialize(is, seed, sd, alloc);
142133
return compact_array_of_strings_tuple_sketch(std::move(base));
143134
}
144135

145136
template<typename Allocator>
137+
template<typename SerDe>
146138
auto compact_array_of_strings_tuple_sketch<Allocator>::deserialize(
147-
const void* bytes, size_t size, uint64_t seed, const Allocator& allocator
139+
const void* bytes, size_t size, uint64_t seed, const SerDe& sd, const Allocator& allocator
148140
) -> compact_array_of_strings_tuple_sketch {
149141
summary_allocator alloc(allocator);
150-
auto base = Base::deserialize(bytes, size, seed, array_of_strings_serde<Allocator>(), alloc);
142+
auto base = Base::deserialize(bytes, size, seed, sd, alloc);
151143
return compact_array_of_strings_tuple_sketch(std::move(base));
152144
}
153145

154146
template<typename Allocator>
155-
void array_of_strings_serde<Allocator>::serialize(
147+
default_array_of_strings_serde<Allocator>::default_array_of_strings_serde(const Allocator& allocator):
148+
allocator_(allocator),
149+
summary_allocator_(allocator) {}
150+
151+
template<typename Allocator>
152+
void default_array_of_strings_serde<Allocator>::serialize(
156153
std::ostream& os, const array_of_strings* items, unsigned num
157154
) const {
158155
for (unsigned i = 0; i < num; ++i) {
@@ -171,27 +168,34 @@ void array_of_strings_serde<Allocator>::serialize(
171168
}
172169

173170
template<typename Allocator>
174-
void array_of_strings_serde<Allocator>::deserialize(
171+
void default_array_of_strings_serde<Allocator>::deserialize(
175172
std::istream& is, array_of_strings* items, unsigned num
176173
) const {
177174
for (unsigned i = 0; i < num; ++i) {
178175
read<uint32_t>(is); // total_bytes
176+
if (!is) throw std::runtime_error("array_of_strings stream read failed");
179177
const uint8_t num_nodes = read<uint8_t>(is);
178+
if (!is) throw std::runtime_error("array_of_strings stream read failed");
180179
check_num_nodes(num_nodes);
181-
array_of_strings array(num_nodes, "", Allocator());
180+
array_of_strings array(num_nodes, "", allocator_);
182181
for (uint8_t j = 0; j < num_nodes; ++j) {
183182
const uint32_t length = read<uint32_t>(is);
183+
if (!is) throw std::runtime_error("array_of_strings stream read failed");
184184
std::string value(length, '\0');
185-
is.read(&value[0], length);
185+
if (length != 0) {
186+
is.read(value.data(), length);
187+
if (!is) throw std::runtime_error("array_of_strings stream read failed");
188+
}
186189
check_utf8(value);
187190
array[j] = std::move(value);
188191
}
189-
new (&items[i]) array_of_strings(std::move(array));
192+
summary_allocator alloc(summary_allocator_);
193+
std::allocator_traits<summary_allocator>::construct(alloc, &items[i], std::move(array));
190194
}
191195
}
192196

193197
template<typename Allocator>
194-
size_t array_of_strings_serde<Allocator>::serialize(
198+
size_t default_array_of_strings_serde<Allocator>::serialize(
195199
void* ptr, size_t capacity, const array_of_strings* items, unsigned num
196200
) const {
197201
uint8_t* ptr8 = static_cast<uint8_t*>(ptr);
@@ -216,7 +220,7 @@ size_t array_of_strings_serde<Allocator>::serialize(
216220
}
217221

218222
template<typename Allocator>
219-
size_t array_of_strings_serde<Allocator>::deserialize(
223+
size_t default_array_of_strings_serde<Allocator>::deserialize(
220224
const void* ptr, size_t capacity, array_of_strings* items, unsigned num
221225
) const {
222226
const uint8_t* ptr8 = static_cast<const uint8_t*>(ptr);
@@ -231,34 +235,37 @@ size_t array_of_strings_serde<Allocator>::deserialize(
231235
uint8_t num_nodes;
232236
bytes_read += copy_from_mem(ptr8 + bytes_read, num_nodes);
233237
check_num_nodes(num_nodes);
234-
array_of_strings array(num_nodes, "", Allocator());
238+
array_of_strings array(num_nodes, "", allocator_);
235239
for (uint8_t j = 0; j < num_nodes; ++j) {
236240
uint32_t length;
237241
bytes_read += copy_from_mem(ptr8 + bytes_read, length);
238242
std::string value(length, '\0');
239-
bytes_read += copy_from_mem(ptr8 + bytes_read, &value[0], length);
243+
if (length != 0) {
244+
bytes_read += copy_from_mem(ptr8 + bytes_read, value.data(), length);
245+
}
240246
check_utf8(value);
241247
array[j] = std::move(value);
242248
}
243-
new (&items[i]) array_of_strings(std::move(array));
249+
summary_allocator alloc(summary_allocator_);
250+
std::allocator_traits<summary_allocator>::construct(alloc, &items[i], std::move(array));
244251
}
245252
return bytes_read;
246253
}
247254

248255
template<typename Allocator>
249-
size_t array_of_strings_serde<Allocator>::size_of_item(const array_of_strings& item) const {
256+
size_t default_array_of_strings_serde<Allocator>::size_of_item(const array_of_strings& item) const {
250257
return compute_total_bytes(item);
251258
}
252259

253260
template<typename Allocator>
254-
void array_of_strings_serde<Allocator>::check_num_nodes(uint8_t num_nodes) {
261+
void default_array_of_strings_serde<Allocator>::check_num_nodes(uint8_t num_nodes) {
255262
if (num_nodes > 127) {
256263
throw std::runtime_error("array_of_strings size exceeds 127");
257264
}
258265
}
259266

260267
template<typename Allocator>
261-
uint32_t array_of_strings_serde<Allocator>::compute_total_bytes(const array_of_strings& item) {
268+
uint32_t default_array_of_strings_serde<Allocator>::compute_total_bytes(const array_of_strings& item) {
262269
const auto count = item.size();
263270
check_num_nodes(static_cast<uint8_t>(count));
264271
size_t total = sizeof(uint32_t) + sizeof(uint8_t) + count * sizeof(uint32_t);
@@ -273,7 +280,7 @@ uint32_t array_of_strings_serde<Allocator>::compute_total_bytes(const array_of_s
273280
}
274281

275282
template<typename Allocator>
276-
void array_of_strings_serde<Allocator>::check_utf8(const std::string& value) {
283+
void default_array_of_strings_serde<Allocator>::check_utf8(const std::string& value) {
277284
if (!utf8::is_valid(value.begin(), value.end())) {
278285
throw std::runtime_error("array_of_strings contains invalid UTF-8");
279286
}

0 commit comments

Comments
 (0)