20
20
#include < iostream>
21
21
#include < random>
22
22
#include < type_traits>
23
+ #include < typeindex>
23
24
#include < vector>
24
25
25
26
#include < folly/FBString.h>
@@ -48,6 +49,7 @@ struct populator_opts {
48
49
range<> str_len = range<>(0 , 0xFF );
49
50
// Probability to use for populating optional fields.
50
51
float optional_field_prob = 0.0 ;
52
+ size_t recursion_limit = 0 ;
51
53
};
52
54
53
55
namespace detail {
@@ -144,6 +146,44 @@ struct deref<PtrType, enable_if_smart_pointer<PtrType>> {
144
146
template <typename T>
145
147
using infer_tag = type::infer_tag<T, true /* GuessStringTag */ >;
146
148
149
+ template <typename Rng>
150
+ struct State {
151
+ Rng& rng;
152
+ const populator_opts& opts;
153
+ std::map<std::type_index, size_t > tag_counts;
154
+ };
155
+
156
+ // Using the type_info of the tags to check if we are populating recursively
157
+ template <typename Tag>
158
+ class RecursionGuard {
159
+ private:
160
+ size_t & cnt_;
161
+ const bool can_recurse_;
162
+
163
+ public:
164
+ template <typename Rng>
165
+ explicit RecursionGuard (State<Rng>& state)
166
+ : cnt_(state.tag_counts[typeid (Tag)]),
167
+ can_recurse_(cnt_ <= state.opts.recursion_limit) {
168
+ if (can_recurse_) {
169
+ cnt_++;
170
+ }
171
+ }
172
+
173
+ ~RecursionGuard () {
174
+ if (can_recurse_) {
175
+ cnt_--;
176
+ }
177
+ }
178
+
179
+ operator bool () const { return can_recurse_; }
180
+
181
+ RecursionGuard (const RecursionGuard&) = delete ;
182
+ RecursionGuard (RecursionGuard&&) = delete ;
183
+ RecursionGuard& operator =(const RecursionGuard&) = delete ;
184
+ RecursionGuard& operator =(RecursionGuard&&) = delete ;
185
+ };
186
+
147
187
} // namespace detail
148
188
149
189
template <typename Tag, typename Type, typename Enable = void >
@@ -166,13 +206,13 @@ struct populator_methods<
166
206
// Special overload to work with lists of booleans.
167
207
template <typename Rng>
168
208
static void populate (
169
- Rng& rng, const populator_opts& , std::vector<bool >::reference out) {
170
- out = next_value (rng);
209
+ detail::State< Rng>& state , std::vector<bool >::reference out) {
210
+ out = next_value (state. rng );
171
211
}
172
212
173
213
template <typename Rng>
174
- static void populate (Rng& rng, const populator_opts& , Int& out) {
175
- out = next_value (rng);
214
+ static void populate (detail::State< Rng>& state , Int& out) {
215
+ out = next_value (state. rng );
176
216
}
177
217
};
178
218
@@ -183,28 +223,29 @@ struct populator_methods<
183
223
std::enable_if_t <
184
224
type::is_a_v<detail::infer_tag<Fp>, type::floating_point_c>>> {
185
225
template <typename Rng>
186
- static void populate (Rng& rng, const populator_opts& , Fp& out) {
226
+ static void populate (detail::State< Rng>& state , Fp& out) {
187
227
std::uniform_real_distribution<Fp> gen;
188
- out = gen (rng);
228
+ out = gen (state. rng );
189
229
DVLOG (4 ) << " generated real: " << out;
190
230
}
191
231
};
192
232
193
233
template <>
194
234
struct populator_methods <type::string_t , std::string> {
195
235
template <typename Rng>
196
- static void populate (Rng& rng, const populator_opts& opts , std::string& str) {
236
+ static void populate (detail::State< Rng>& state , std::string& str) {
197
237
using larger_char =
198
238
std::conditional_t <std::numeric_limits<char >::is_signed, int , unsigned >;
199
239
200
240
// all printable chars (see `man ascii`)
201
241
std::uniform_int_distribution<larger_char> char_gen (0x20 , 0x7E );
202
242
203
- const std::size_t length = detail::rand_in_range (rng, opts.str_len );
243
+ const std::size_t length =
244
+ detail::rand_in_range (state.rng , state.opts .str_len );
204
245
205
246
str = std::string (length, 0 );
206
247
std::generate_n (str.begin (), length, [&]() {
207
- return static_cast <char >(char_gen (rng));
248
+ return static_cast <char >(char_gen (state. rng ));
208
249
});
209
250
210
251
DVLOG (4 ) << " generated string of len" << length;
@@ -216,10 +257,9 @@ struct populator_methods<
216
257
type::cpp_type<folly::fbstring, type::string_t >,
217
258
folly::fbstring> {
218
259
template <typename Rng>
219
- static void populate (
220
- Rng& rng, const populator_opts& opts, folly::fbstring& bin) {
260
+ static void populate (detail::State<Rng>& state, folly::fbstring& bin) {
221
261
std::string t;
222
- populator_methods<type::string_t , std::string>::populate (rng, opts , t);
262
+ populator_methods<type::string_t , std::string>::populate (state , t);
223
263
bin = folly::fbstring (std::move (t));
224
264
}
225
265
};
@@ -237,11 +277,11 @@ void generate_bytes(
237
277
template <>
238
278
struct populator_methods <type::binary_t , std::string> {
239
279
template <typename Rng>
240
- static void populate (Rng& rng, const populator_opts& opts , std::string& bin) {
241
- const auto length = detail::rand_in_range (rng, opts.bin_len );
280
+ static void populate (detail::State< Rng>& state , std::string& bin) {
281
+ const auto length = detail::rand_in_range (state. rng , state. opts .bin_len );
242
282
bin = std::string (length, 0 );
243
283
auto iter = bin.begin ();
244
- generate_bytes (rng, bin, length, [&](uint8_t c) { *iter++ = c; });
284
+ generate_bytes (state. rng , bin, length, [&](uint8_t c) { *iter++ = c; });
245
285
}
246
286
};
247
287
@@ -250,10 +290,9 @@ struct populator_methods<
250
290
type::cpp_type<folly::fbstring, type::binary_t >,
251
291
folly::fbstring> {
252
292
template <typename Rng>
253
- static void populate (
254
- Rng& rng, const populator_opts& opts, folly::fbstring& bin) {
293
+ static void populate (detail::State<Rng>& state, folly::fbstring& bin) {
255
294
std::string t;
256
- populator_methods<type::binary_t , std::string>::populate (rng, opts , t);
295
+ populator_methods<type::binary_t , std::string>::populate (state , t);
257
296
bin = folly::fbstring (std::move (t));
258
297
}
259
298
};
@@ -263,14 +302,13 @@ struct populator_methods<
263
302
type::cpp_type<folly::IOBuf, type::binary_t >,
264
303
folly::IOBuf> {
265
304
template <typename Rng>
266
- static void populate (
267
- Rng& rng, const populator_opts& opts, folly::IOBuf& bin) {
268
- const auto length = detail::rand_in_range (rng, opts.bin_len );
305
+ static void populate (detail::State<Rng>& state, folly::IOBuf& bin) {
306
+ const auto length = detail::rand_in_range (state.rng , state.opts .bin_len );
269
307
bin = folly::IOBuf (folly::IOBuf::CREATE, length);
270
308
bin.append (length);
271
309
folly::io::RWUnshareCursor range (&bin);
272
310
generate_bytes (
273
- rng, range, length, [&](uint8_t c) { range.write <uint8_t >(c); });
311
+ state. rng , range, length, [&](uint8_t c) { range.write <uint8_t >(c); });
274
312
}
275
313
};
276
314
@@ -280,13 +318,11 @@ struct populator_methods<
280
318
std::unique_ptr<folly::IOBuf>> {
281
319
template <typename Rng>
282
320
static void populate (
283
- Rng& rng,
284
- const populator_opts& opts,
285
- std::unique_ptr<folly::IOBuf>& bin) {
321
+ detail::State<Rng>& state, std::unique_ptr<folly::IOBuf>& bin) {
286
322
bin = std::make_unique<folly::IOBuf>();
287
323
return populator_methods<
288
324
type::cpp_type<folly::IOBuf, type::binary_t >,
289
- folly::IOBuf>::populate (rng, opts , *bin);
325
+ folly::IOBuf>::populate (state , *bin);
290
326
}
291
327
};
292
328
@@ -300,8 +336,8 @@ struct populator_methods<
300
336
using type_methods = populator_methods<Tag, element_type>;
301
337
302
338
template <typename Rng>
303
- static void populate (Rng& rng, const populator_opts& opts , PtrType& out) {
304
- return type_methods::populate (rng, opts , *out);
339
+ static void populate (detail::State< Rng>& state , PtrType& out) {
340
+ return type_methods::populate (state , *out);
305
341
}
306
342
};
307
343
@@ -315,9 +351,9 @@ struct populator_methods<
315
351
using int_methods = populator_methods<detail::infer_tag<int_type>, int_type>;
316
352
317
353
template <typename Rng>
318
- static void populate (Rng& rng, const populator_opts& opts , Type& out) {
354
+ static void populate (detail::State< Rng>& state , Type& out) {
319
355
int_type tmp;
320
- int_methods::populate (rng, opts , tmp);
356
+ int_methods::populate (state , tmp);
321
357
out = static_cast <Type>(tmp);
322
358
}
323
359
};
@@ -329,15 +365,21 @@ struct populator_methods<type::list<ElemTag>, Type> {
329
365
using elem_methods = populator_methods<ElemTag, elem_type>;
330
366
331
367
template <typename Rng>
332
- static void populate (Rng& rng, const populator_opts& opts, Type& out) {
333
- std::uint32_t list_size = detail::rand_in_range (rng, opts.list_len );
368
+ static void populate (detail::State<Rng>& state, Type& out) {
369
+ auto recursion_guard = detail::RecursionGuard<type::list<ElemTag>>(state);
370
+ if (!recursion_guard) {
371
+ return ;
372
+ }
373
+
374
+ std::uint32_t list_size =
375
+ detail::rand_in_range (state.rng , state.opts .list_len );
334
376
out = Type ();
335
377
336
378
DVLOG (3 ) << " populating list size " << list_size;
337
379
338
380
out.resize (list_size);
339
381
for (decltype (list_size) i = 0 ; i < list_size; i++) {
340
- elem_methods::populate (rng, opts , out[i]);
382
+ elem_methods::populate (state , out[i]);
341
383
}
342
384
}
343
385
};
@@ -351,15 +393,21 @@ struct populator_methods<type::set<ElemTag>, Type> {
351
393
using elem_methods = populator_methods<ElemTag, elem_type>;
352
394
353
395
template <typename Rng>
354
- static void populate (Rng& rng, const populator_opts& opts, Type& out) {
355
- std::uint32_t set_size = detail::rand_in_range (rng, opts.set_len );
396
+ static void populate (detail::State<Rng>& state, Type& out) {
397
+ auto recursion_guard = detail::RecursionGuard<type::set<ElemTag>>(state);
398
+ if (!recursion_guard) {
399
+ return ;
400
+ }
401
+
402
+ std::uint32_t set_size =
403
+ detail::rand_in_range (state.rng , state.opts .set_len );
356
404
357
405
DVLOG (3 ) << " populating set size " << set_size;
358
406
out = Type ();
359
407
360
408
for (decltype (set_size) i = 0 ; i < set_size; i++) {
361
409
elem_type tmp;
362
- elem_methods::populate (rng, opts , tmp);
410
+ elem_methods::populate (state , tmp);
363
411
out.insert (std::move (tmp));
364
412
}
365
413
}
@@ -375,16 +423,23 @@ struct populator_methods<type::map<KeyTag, MappedTag>, Type> {
375
423
using mapped_methods = populator_methods<MappedTag, mapped_type>;
376
424
377
425
template <typename Rng>
378
- static void populate (Rng& rng, const populator_opts& opts, Type& out) {
379
- std::uint32_t map_size = detail::rand_in_range (rng, opts.map_len );
426
+ static void populate (detail::State<Rng>& state, Type& out) {
427
+ auto recursion_guard =
428
+ detail::RecursionGuard<type::map<KeyTag, MappedTag>>(state);
429
+ if (!recursion_guard) {
430
+ return ;
431
+ }
432
+
433
+ std::uint32_t map_size =
434
+ detail::rand_in_range (state.rng , state.opts .map_len );
380
435
381
436
DVLOG (3 ) << " populating map size " << map_size;
382
437
out = Type ();
383
438
384
439
for (decltype (map_size) i = 0 ; i < map_size; i++) {
385
440
key_type key_tmp;
386
- key_methods::populate (rng, opts , key_tmp);
387
- mapped_methods::populate (rng, opts , out[std::move (key_tmp)]);
441
+ key_methods::populate (state , key_tmp);
442
+ mapped_methods::populate (state , out[std::move (key_tmp)]);
388
443
}
389
444
}
390
445
};
@@ -393,13 +448,13 @@ struct populator_methods<type::map<KeyTag, MappedTag>, Type> {
393
448
template <typename Union>
394
449
struct populator_methods <type::union_t <Union>, Union> {
395
450
template <typename Rng>
396
- static void populate (Rng& rng, const populator_opts& opts , Union& out) {
451
+ static void populate (detail::State< Rng>& state , Union& out) {
397
452
DVLOG (3 ) << " begin writing union: "
398
453
<< op::get_class_name_v<Union> << " , type: "
399
454
<< folly::to_underlying (out.getType ());
400
455
401
456
const auto selected = static_cast <type::Ordinal>(detail::rand_in_range (
402
- rng, populator_opts::range<size_t >{0 , op::size_v<Union> - 1 }));
457
+ state. rng , populator_opts::range<size_t >{0 , op::size_v<Union> - 1 }));
403
458
404
459
op::for_each_ordinal<Union>([&](auto ord) {
405
460
using Ord = decltype (ord);
@@ -414,7 +469,7 @@ struct populator_methods<type::union_t<Union>, Union> {
414
469
<< op::get_name_v<Union, Ord> << " , fid: "
415
470
<< folly::to_underlying (op::get_field_id_v<Union, Ord>);
416
471
417
- methods::populate (rng, opts , op::get<Ord>(out).ensure ());
472
+ methods::populate (state , op::get<Ord>(out).ensure ());
418
473
});
419
474
420
475
DVLOG (3 ) << " end writing union" ;
@@ -428,7 +483,7 @@ struct populator_methods<type::struct_t<Struct>, Struct> {
428
483
class member_populator {
429
484
public:
430
485
template <typename Ord, typename Rng>
431
- void operator ()(Ord, Rng& rng, const populator_opts& opts , Struct& out) {
486
+ void operator ()(Ord, detail::State< Rng>& state , Struct& out) {
432
487
using methods = populator_methods<
433
488
op::get_type_tag<Struct, Ord>,
434
489
op::get_native_type<Struct, Ord>>;
@@ -439,7 +494,7 @@ struct populator_methods<type::struct_t<Struct>, Struct> {
439
494
// Popualate optional fields with `optional_field_prob` probability.
440
495
const auto skip = //
441
496
::apache::thrift::detail::is_optional_field_ref_v<field_ref_type> &&
442
- !detail::get_bernoulli (rng, opts.optional_field_prob );
497
+ !detail::get_bernoulli (state. rng , state. opts .optional_field_prob );
443
498
if (skip) {
444
499
return ;
445
500
}
@@ -448,15 +503,20 @@ struct populator_methods<type::struct_t<Struct>, Struct> {
448
503
449
504
op::ensure<Ord>(out);
450
505
methods::populate (
451
- rng, opts , detail::deref<field_ref_type>::clear_and_get (got));
506
+ state , detail::deref<field_ref_type>::clear_and_get (got));
452
507
}
453
508
};
454
509
455
510
public:
456
511
template <typename Rng>
457
- static void populate (Rng& rng, const populator_opts& opts, Struct& out) {
512
+ static void populate (detail::State<Rng>& state, Struct& out) {
513
+ auto recursion_guard =
514
+ detail::RecursionGuard<type::struct_t <Struct>>(state);
515
+ if (!recursion_guard) {
516
+ return ;
517
+ }
458
518
op::for_each_ordinal<Struct>(
459
- [&](auto ord) { member_populator ()(ord, rng, opts , out); });
519
+ [&](auto ord) { member_populator ()(ord, state , out); });
460
520
}
461
521
};
462
522
template <typename Exn>
@@ -470,9 +530,9 @@ struct populator_methods<type::adapted<Adapter, InnerTag>, T> {
470
530
using inner_methods = populator_methods<InnerTag, inner_type>;
471
531
472
532
template <typename Rng>
473
- static void populate (Rng& rng, const populator_opts& opts , T& out) {
533
+ static void populate (detail::State< Rng>& state , T& out) {
474
534
inner_type tmp;
475
- inner_methods::populate (rng, opts , tmp);
535
+ inner_methods::populate (state , tmp);
476
536
out = Adapter::fromThrift (std::move (tmp));
477
537
}
478
538
};
@@ -500,7 +560,8 @@ struct populator_methods<
500
560
501
561
template <typename Type, typename Rng, typename Tag = detail::infer_tag<Type>>
502
562
void populate (Type& out, const populator_opts& opts, Rng& rng) {
503
- return populator_methods<Tag, Type>::populate (rng, opts, out);
563
+ detail::State<Rng> state{rng, opts, {}};
564
+ return populator_methods<Tag, Type>::populate (state, out);
504
565
}
505
566
506
567
} // namespace apache::thrift::populator
0 commit comments