1 /*
2 * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 #include "logging/rtc_event_log/encoder/delta_encoding.h"
12
13 #include <algorithm>
14 #include <limits>
15 #include <numeric>
16 #include <string>
17 #include <tuple>
18 #include <vector>
19
20 #include "absl/types/optional.h"
21 #include "rtc_base/arraysize.h"
22 #include "rtc_base/checks.h"
23 #include "rtc_base/random.h"
24 #include "test/gtest.h"
25
26 namespace webrtc {
27
28 void SetFixedLengthEncoderDeltaSignednessForTesting(bool signedness);
29 void UnsetFixedLengthEncoderDeltaSignednessForTesting();
30
31 namespace {
32
33 enum class DeltaSignedness { kNoOverride, kForceUnsigned, kForceSigned };
34
MaybeSetSignedness(DeltaSignedness signedness)35 void MaybeSetSignedness(DeltaSignedness signedness) {
36 switch (signedness) {
37 case DeltaSignedness::kNoOverride:
38 UnsetFixedLengthEncoderDeltaSignednessForTesting();
39 return;
40 case DeltaSignedness::kForceUnsigned:
41 SetFixedLengthEncoderDeltaSignednessForTesting(false);
42 return;
43 case DeltaSignedness::kForceSigned:
44 SetFixedLengthEncoderDeltaSignednessForTesting(true);
45 return;
46 }
47 RTC_DCHECK_NOTREACHED();
48 }
49
RandomWithMaxBitWidth(Random * prng,uint64_t max_width)50 uint64_t RandomWithMaxBitWidth(Random* prng, uint64_t max_width) {
51 RTC_DCHECK_GE(max_width, 1u);
52 RTC_DCHECK_LE(max_width, 64u);
53
54 const uint64_t low = prng->Rand(std::numeric_limits<uint32_t>::max());
55 const uint64_t high =
56 max_width > 32u ? prng->Rand(std::numeric_limits<uint32_t>::max()) : 0u;
57
58 const uint64_t random_before_mask = (high << 32) | low;
59
60 if (max_width < 64) {
61 return random_before_mask & ((static_cast<uint64_t>(1) << max_width) - 1);
62 } else {
63 return random_before_mask;
64 }
65 }
66
67 // Encodes `values` based on `base`, then decodes the result and makes sure
68 // that it is equal to the original input.
69 // If `encoded_string` is non-null, the encoded result will also be written
70 // into it.
TestEncodingAndDecoding(absl::optional<uint64_t> base,const std::vector<absl::optional<uint64_t>> & values,std::string * encoded_string=nullptr)71 void TestEncodingAndDecoding(
72 absl::optional<uint64_t> base,
73 const std::vector<absl::optional<uint64_t>>& values,
74 std::string* encoded_string = nullptr) {
75 const std::string encoded = EncodeDeltas(base, values);
76 if (encoded_string) {
77 *encoded_string = encoded;
78 }
79
80 const std::vector<absl::optional<uint64_t>> decoded =
81 DecodeDeltas(encoded, base, values.size());
82
83 EXPECT_EQ(decoded, values);
84 }
85
CreateSequenceByFirstValue(uint64_t first,size_t sequence_length)86 std::vector<absl::optional<uint64_t>> CreateSequenceByFirstValue(
87 uint64_t first,
88 size_t sequence_length) {
89 std::vector<absl::optional<uint64_t>> sequence(sequence_length);
90 std::iota(sequence.begin(), sequence.end(), first);
91 return sequence;
92 }
93
CreateSequenceByLastValue(uint64_t last,size_t num_values)94 std::vector<absl::optional<uint64_t>> CreateSequenceByLastValue(
95 uint64_t last,
96 size_t num_values) {
97 const uint64_t first = last - num_values + 1;
98 std::vector<absl::optional<uint64_t>> result(num_values);
99 std::iota(result.begin(), result.end(), first);
100 return result;
101 }
102
103 // If `sequence_length` is greater than the number of deltas, the sequence of
104 // deltas will wrap around.
CreateSequenceByOptionalDeltas(uint64_t first,const std::vector<absl::optional<uint64_t>> & deltas,size_t sequence_length)105 std::vector<absl::optional<uint64_t>> CreateSequenceByOptionalDeltas(
106 uint64_t first,
107 const std::vector<absl::optional<uint64_t>>& deltas,
108 size_t sequence_length) {
109 RTC_DCHECK_GE(sequence_length, 1);
110
111 std::vector<absl::optional<uint64_t>> sequence(sequence_length);
112
113 uint64_t previous = first;
114 for (size_t i = 0, next_delta_index = 0; i < sequence.size(); ++i) {
115 if (deltas[next_delta_index].has_value()) {
116 sequence[i] =
117 absl::optional<uint64_t>(previous + deltas[next_delta_index].value());
118 previous = sequence[i].value();
119 }
120 next_delta_index = (next_delta_index + 1) % deltas.size();
121 }
122
123 return sequence;
124 }
125
EncodingLengthUpperBound(size_t delta_max_bit_width,size_t num_of_deltas,DeltaSignedness signedness_override)126 size_t EncodingLengthUpperBound(size_t delta_max_bit_width,
127 size_t num_of_deltas,
128 DeltaSignedness signedness_override) {
129 absl::optional<size_t> smallest_header_size_bytes;
130 switch (signedness_override) {
131 case DeltaSignedness::kNoOverride:
132 case DeltaSignedness::kForceUnsigned:
133 smallest_header_size_bytes = 1;
134 break;
135 case DeltaSignedness::kForceSigned:
136 smallest_header_size_bytes = 2;
137 break;
138 }
139 RTC_DCHECK(smallest_header_size_bytes);
140
141 return delta_max_bit_width * num_of_deltas + *smallest_header_size_bytes;
142 }
143
144 // If `sequence_length` is greater than the number of deltas, the sequence of
145 // deltas will wrap around.
CreateSequenceByDeltas(uint64_t first,const std::vector<uint64_t> & deltas,size_t sequence_length)146 std::vector<absl::optional<uint64_t>> CreateSequenceByDeltas(
147 uint64_t first,
148 const std::vector<uint64_t>& deltas,
149 size_t sequence_length) {
150 RTC_DCHECK(!deltas.empty());
151 std::vector<absl::optional<uint64_t>> optional_deltas(deltas.size());
152 for (size_t i = 0; i < deltas.size(); ++i) {
153 optional_deltas[i] = absl::optional<uint64_t>(deltas[i]);
154 }
155 return CreateSequenceByOptionalDeltas(first, optional_deltas,
156 sequence_length);
157 }
158
159 // Tests of the delta encoding, parameterized by the number of values
160 // in the sequence created by the test.
161 class DeltaEncodingTest
162 : public ::testing::TestWithParam<
163 std::tuple<DeltaSignedness, size_t, bool, uint64_t>> {
164 public:
DeltaEncodingTest()165 DeltaEncodingTest()
166 : signedness_(std::get<0>(GetParam())),
167 num_of_values_(std::get<1>(GetParam())),
168 optional_values_(std::get<2>(GetParam())),
169 partial_random_seed_(std::get<3>(GetParam())) {
170 MaybeSetSignedness(signedness_);
171 }
172
173 ~DeltaEncodingTest() override = default;
174
175 // Running with the same seed for all variants would make all tests start
176 // with the same sequence; avoid this by making the seed different.
Seed() const177 uint64_t Seed() const {
178 // Multiply everything but by different primes to produce unique results.
179 return 2 * static_cast<uint64_t>(signedness_) + 3 * num_of_values_ +
180 5 * optional_values_ + 7 * partial_random_seed_;
181 }
182
183 const DeltaSignedness signedness_;
184 const uint64_t num_of_values_;
185 const bool optional_values_;
186 const uint64_t partial_random_seed_; // Explained where it's used.
187 };
188
TEST_P(DeltaEncodingTest,AllValuesEqualToExistentBaseValue)189 TEST_P(DeltaEncodingTest, AllValuesEqualToExistentBaseValue) {
190 const absl::optional<uint64_t> base(3432);
191 std::vector<absl::optional<uint64_t>> values(num_of_values_);
192 std::fill(values.begin(), values.end(), base);
193 std::string encoded;
194 TestEncodingAndDecoding(base, values, &encoded);
195
196 // Additional requirement - the encoding should be efficient in this
197 // case - the empty string will be used.
198 EXPECT_TRUE(encoded.empty());
199 }
200
TEST_P(DeltaEncodingTest,AllValuesEqualToNonExistentBaseValue)201 TEST_P(DeltaEncodingTest, AllValuesEqualToNonExistentBaseValue) {
202 if (!optional_values_) {
203 return; // Test irrelevant for this case.
204 }
205
206 const absl::optional<uint64_t> base;
207 std::vector<absl::optional<uint64_t>> values(num_of_values_);
208 std::fill(values.begin(), values.end(), base);
209 std::string encoded;
210 TestEncodingAndDecoding(base, values, &encoded);
211
212 // Additional requirement - the encoding should be efficient in this
213 // case - the empty string will be used.
214 EXPECT_TRUE(encoded.empty());
215 }
216
TEST_P(DeltaEncodingTest,BaseNonExistentButSomeOtherValuesExist)217 TEST_P(DeltaEncodingTest, BaseNonExistentButSomeOtherValuesExist) {
218 if (!optional_values_) {
219 return; // Test irrelevant for this case.
220 }
221
222 const absl::optional<uint64_t> base;
223 std::vector<absl::optional<uint64_t>> values(num_of_values_);
224
225 Random prng(Seed());
226
227 const uint64_t max_bit_width = 1 + prng.Rand(63); // [1, 64]
228
229 for (size_t i = 0; i < values.size();) {
230 // Leave a random number of values as non-existent.
231 const size_t non_existent_count = prng.Rand(values.size() - i - 1);
232 i += non_existent_count;
233
234 // Assign random values to a random number of values. (At least one, to
235 // prevent this iteration of the outer loop from being a no-op.)
236 const size_t existent_count =
237 std::max<size_t>(prng.Rand(values.size() - i - 1), 1);
238 for (size_t j = 0; j < existent_count; ++j) {
239 values[i + j] = RandomWithMaxBitWidth(&prng, max_bit_width);
240 }
241 i += existent_count;
242 }
243
244 TestEncodingAndDecoding(base, values);
245 }
246
TEST_P(DeltaEncodingTest,MinDeltaNoWrapAround)247 TEST_P(DeltaEncodingTest, MinDeltaNoWrapAround) {
248 const absl::optional<uint64_t> base(3432);
249
250 auto values = CreateSequenceByFirstValue(base.value() + 1, num_of_values_);
251 ASSERT_GT(values[values.size() - 1], base) << "Sanity; must not wrap around";
252
253 if (optional_values_) {
254 // Arbitrarily make one of the values non-existent, to force
255 // optional-supporting encoding.
256 values[0] = absl::optional<uint64_t>();
257 }
258
259 TestEncodingAndDecoding(base, values);
260 }
261
TEST_P(DeltaEncodingTest,BigDeltaNoWrapAround)262 TEST_P(DeltaEncodingTest, BigDeltaNoWrapAround) {
263 const uint64_t kBigDelta = 132828;
264 const absl::optional<uint64_t> base(3432);
265
266 auto values =
267 CreateSequenceByFirstValue(base.value() + kBigDelta, num_of_values_);
268 ASSERT_GT(values[values.size() - 1], base) << "Sanity; must not wrap around";
269
270 if (optional_values_) {
271 // Arbitrarily make one of the values non-existent, to force
272 // optional-supporting encoding.
273 values[0] = absl::optional<uint64_t>();
274 }
275
276 TestEncodingAndDecoding(base, values);
277 }
278
TEST_P(DeltaEncodingTest,MaxDeltaNoWrapAround)279 TEST_P(DeltaEncodingTest, MaxDeltaNoWrapAround) {
280 const absl::optional<uint64_t> base(3432);
281
282 auto values = CreateSequenceByLastValue(std::numeric_limits<uint64_t>::max(),
283 num_of_values_);
284 ASSERT_GT(values[values.size() - 1], base) << "Sanity; must not wrap around";
285
286 if (optional_values_) {
287 // Arbitrarily make one of the values non-existent, to force
288 // optional-supporting encoding.
289 values[0] = absl::optional<uint64_t>();
290 }
291
292 TestEncodingAndDecoding(base, values);
293 }
294
TEST_P(DeltaEncodingTest,SmallDeltaWithWrapAroundComparedToBase)295 TEST_P(DeltaEncodingTest, SmallDeltaWithWrapAroundComparedToBase) {
296 if (optional_values_ && num_of_values_ == 1) {
297 return; // Inapplicable
298 }
299
300 const absl::optional<uint64_t> base(std::numeric_limits<uint64_t>::max());
301
302 auto values = CreateSequenceByDeltas(*base, {1, 10, 3}, num_of_values_);
303 ASSERT_LT(values[0], base) << "Sanity; must wrap around";
304
305 if (optional_values_) {
306 // Arbitrarily make one of the values non-existent, to force
307 // optional-supporting encoding.
308 values[1] = absl::optional<uint64_t>();
309 }
310
311 TestEncodingAndDecoding(base, values);
312 }
313
TEST_P(DeltaEncodingTest,SmallDeltaWithWrapAroundInValueSequence)314 TEST_P(DeltaEncodingTest, SmallDeltaWithWrapAroundInValueSequence) {
315 if (num_of_values_ == 1 || (optional_values_ && num_of_values_ < 3)) {
316 return; // Inapplicable.
317 }
318
319 const absl::optional<uint64_t> base(std::numeric_limits<uint64_t>::max() - 2);
320
321 auto values = CreateSequenceByDeltas(*base, {1, 10, 3}, num_of_values_);
322 ASSERT_LT(values[values.size() - 1], values[0]) << "Sanity; must wrap around";
323
324 if (optional_values_) {
325 // Arbitrarily make one of the values non-existent, to force
326 // optional-supporting encoding.
327 RTC_DCHECK_GT(values.size() - 1, 1u); // Wrap around not cancelled.
328 values[1] = absl::optional<uint64_t>();
329 }
330
331 TestEncodingAndDecoding(base, values);
332 }
333
334 // Suppress "integral constant overflow" warning; this is the test's focus.
335 #ifdef _MSC_VER
336 #pragma warning(push)
337 #pragma warning(disable : 4307)
338 #endif
TEST_P(DeltaEncodingTest,BigDeltaWithWrapAroundComparedToBase)339 TEST_P(DeltaEncodingTest, BigDeltaWithWrapAroundComparedToBase) {
340 if (optional_values_ && num_of_values_ == 1) {
341 return; // Inapplicable
342 }
343
344 const uint64_t kBigDelta = 132828;
345 const absl::optional<uint64_t> base(std::numeric_limits<uint64_t>::max() -
346 kBigDelta + 3);
347
348 auto values =
349 CreateSequenceByFirstValue(base.value() + kBigDelta, num_of_values_);
350 ASSERT_LT(values[0], base.value()) << "Sanity; must wrap around";
351
352 if (optional_values_) {
353 // Arbitrarily make one of the values non-existent, to force
354 // optional-supporting encoding.
355 values[1] = absl::optional<uint64_t>();
356 }
357
358 TestEncodingAndDecoding(base, values);
359 }
360
TEST_P(DeltaEncodingTest,BigDeltaWithWrapAroundInValueSequence)361 TEST_P(DeltaEncodingTest, BigDeltaWithWrapAroundInValueSequence) {
362 if (num_of_values_ == 1 || (optional_values_ && num_of_values_ < 3)) {
363 return; // Inapplicable.
364 }
365
366 const uint64_t kBigDelta = 132828;
367 const absl::optional<uint64_t> base(std::numeric_limits<uint64_t>::max() -
368 kBigDelta + 3);
369
370 auto values = CreateSequenceByFirstValue(std::numeric_limits<uint64_t>::max(),
371 num_of_values_);
372 ASSERT_LT(values[values.size() - 1], values[0]) << "Sanity; must wrap around";
373
374 if (optional_values_) {
375 // Arbitrarily make one of the values non-existent, to force
376 // optional-supporting encoding.
377 RTC_DCHECK_GT(values.size() - 1, 1u); // Wrap around not cancelled.
378 values[1] = absl::optional<uint64_t>();
379 }
380
381 TestEncodingAndDecoding(base, values);
382 }
383 #ifdef _MSC_VER
384 #pragma warning(pop)
385 #endif
386
TEST_P(DeltaEncodingTest,MaxDeltaWithWrapAroundComparedToBase)387 TEST_P(DeltaEncodingTest, MaxDeltaWithWrapAroundComparedToBase) {
388 if (optional_values_ && num_of_values_ == 1) {
389 return; // Inapplicable
390 }
391
392 const absl::optional<uint64_t> base(3432);
393 auto values = CreateSequenceByFirstValue(*base - 1, num_of_values_);
394
395 if (optional_values_) {
396 // Arbitrarily make one of the values non-existent, to force
397 // optional-supporting encoding.
398 values[1] = absl::optional<uint64_t>();
399 }
400
401 TestEncodingAndDecoding(base, values);
402 }
403
TEST_P(DeltaEncodingTest,MaxDeltaWithWrapAroundInValueSequence)404 TEST_P(DeltaEncodingTest, MaxDeltaWithWrapAroundInValueSequence) {
405 if (num_of_values_ == 1 || (optional_values_ && num_of_values_ < 3)) {
406 return; // Inapplicable.
407 }
408
409 const absl::optional<uint64_t> base(3432);
410
411 auto values = CreateSequenceByDeltas(
412 *base, {0, std::numeric_limits<uint64_t>::max(), 3}, num_of_values_);
413 // Wraps around continuously by virtue of being max(); will not ASSERT.
414
415 if (optional_values_) {
416 // Arbitrarily make one of the values non-existent, to force
417 // optional-supporting encoding.
418 RTC_DCHECK_GT(values.size() - 1, 1u); // Wrap around not cancelled.
419 values[1] = absl::optional<uint64_t>();
420 }
421
422 TestEncodingAndDecoding(base, values);
423 }
424
425 // If num_of_values_ == 1, a zero delta will yield an empty string; that's
426 // already covered by AllValuesEqualToExistentBaseValue, but it doesn't hurt to
427 // test again. For all other cases, we have a new test.
TEST_P(DeltaEncodingTest,ZeroDelta)428 TEST_P(DeltaEncodingTest, ZeroDelta) {
429 const absl::optional<uint64_t> base(3432);
430
431 // Arbitrary sequence of deltas with intentional zero deltas, as well as
432 // consecutive zeros.
433 const std::vector<uint64_t> deltas = {0, 312, 11, 1, 1, 0, 0, 12,
434 400321, 3, 3, 12, 5, 0, 6};
435 auto values = CreateSequenceByDeltas(base.value(), deltas, num_of_values_);
436
437 if (optional_values_) {
438 // Arbitrarily make one of the values non-existent, to force
439 // optional-supporting encoding.
440 values[0] = absl::optional<uint64_t>();
441 }
442
443 TestEncodingAndDecoding(base, values);
444 }
445
446 INSTANTIATE_TEST_SUITE_P(
447 SignednessOverrideAndNumberOfValuesInSequence,
448 DeltaEncodingTest,
449 ::testing::Combine(::testing::Values(DeltaSignedness::kNoOverride,
450 DeltaSignedness::kForceUnsigned,
451 DeltaSignedness::kForceSigned),
452 ::testing::Values(1, 2, 100, 10000),
453 ::testing::Bool(),
454 ::testing::Values(10, 20, 30)));
455
456 // Tests over the quality of the compression (as opposed to its correctness).
457 // Not to be confused with tests of runtime efficiency.
458 class DeltaEncodingCompressionQualityTest
459 : public ::testing::TestWithParam<
460 std::tuple<DeltaSignedness, uint64_t, uint64_t, uint64_t>> {
461 public:
DeltaEncodingCompressionQualityTest()462 DeltaEncodingCompressionQualityTest()
463 : signedness_(std::get<0>(GetParam())),
464 delta_max_bit_width_(std::get<1>(GetParam())),
465 num_of_values_(std::get<2>(GetParam())),
466 partial_random_seed_(std::get<3>(GetParam())) {
467 MaybeSetSignedness(signedness_);
468 }
469
470 ~DeltaEncodingCompressionQualityTest() override = default;
471
472 // Running with the same seed for all variants would make all tests start
473 // with the same sequence; avoid this by making the seed different.
Seed() const474 uint64_t Seed() const {
475 // Multiply everything but by different primes to produce unique results.
476 return 2 * static_cast<uint64_t>(signedness_) + 3 * delta_max_bit_width_ +
477 5 * delta_max_bit_width_ + 7 * num_of_values_ +
478 11 * partial_random_seed_;
479 }
480
481 const DeltaSignedness signedness_;
482 const uint64_t delta_max_bit_width_;
483 const uint64_t num_of_values_;
484 const uint64_t partial_random_seed_; // Explained where it's used.
485 };
486
487 // If no wrap-around occurs in the stream, the width of the values does not
488 // matter to compression performance; only the deltas matter.
TEST_P(DeltaEncodingCompressionQualityTest,BaseDoesNotAffectEfficiencyIfNoWrapAround)489 TEST_P(DeltaEncodingCompressionQualityTest,
490 BaseDoesNotAffectEfficiencyIfNoWrapAround) {
491 // 1. Bases which will not produce a wrap-around.
492 // 2. The last base - 0xffffffffffffffff - does cause a wrap-around, but
493 // that still works, because the width is 64 anyway, and does not
494 // need to be conveyed explicitly in the encoding header.
495 const uint64_t bases[] = {0, 0x55, 0xffffffff,
496 std::numeric_limits<uint64_t>::max()};
497 const size_t kIntendedWrapAroundBaseIndex = arraysize(bases);
498
499 std::vector<uint64_t> deltas(num_of_values_);
500
501 // Allows us to make sure that the deltas do not produce a wrap-around.
502 uint64_t last_element[arraysize(bases)];
503 memcpy(last_element, bases, sizeof(bases));
504
505 // Avoid empty `deltas` due to first element causing wrap-around.
506 deltas[0] = 1;
507 for (size_t i = 0; i < arraysize(last_element); ++i) {
508 last_element[i] += 1;
509 }
510
511 Random prng(Seed());
512
513 for (size_t i = 1; i < deltas.size(); ++i) {
514 const uint64_t delta = RandomWithMaxBitWidth(&prng, delta_max_bit_width_);
515
516 bool wrap_around = false;
517 for (size_t j = 0; j < arraysize(last_element); ++j) {
518 if (j == kIntendedWrapAroundBaseIndex) {
519 continue;
520 }
521
522 last_element[j] += delta;
523 if (last_element[j] < bases[j]) {
524 wrap_around = true;
525 break;
526 }
527 }
528
529 if (wrap_around) {
530 deltas.resize(i);
531 break;
532 }
533
534 deltas[i] = delta;
535 }
536
537 std::string encodings[arraysize(bases)];
538
539 for (size_t i = 0; i < arraysize(bases); ++i) {
540 const auto values =
541 CreateSequenceByDeltas(bases[i], deltas, num_of_values_);
542 // Produce the encoding and write it to encodings[i].
543 // By using TestEncodingAndDecoding() to do this, we also sanity-test
544 // the encoding/decoding, though that is not the test's focus.
545 TestEncodingAndDecoding(bases[i], values, &encodings[i]);
546 EXPECT_LE(encodings[i].length(),
547 EncodingLengthUpperBound(delta_max_bit_width_, num_of_values_,
548 signedness_));
549 }
550
551 // Test focus - all of the encodings should be the same, as they are based
552 // on the same delta sequence, and do not contain a wrap-around.
553 for (size_t i = 1; i < arraysize(encodings); ++i) {
554 EXPECT_EQ(encodings[i], encodings[0]);
555 }
556 }
557
558 INSTANTIATE_TEST_SUITE_P(
559 SignednessOverrideAndDeltaMaxBitWidthAndNumberOfValuesInSequence,
560 DeltaEncodingCompressionQualityTest,
561 ::testing::Combine(
562 ::testing::Values(DeltaSignedness::kNoOverride,
563 DeltaSignedness::kForceUnsigned,
564 DeltaSignedness::kForceSigned),
565 ::testing::Values(1, 4, 8, 15, 16, 17, 31, 32, 33, 63, 64),
566 ::testing::Values(1, 2, 100, 10000),
567 ::testing::Values(11, 12, 13)));
568
569 // Similar to DeltaEncodingTest, but instead of semi-surgically producing
570 // specific cases, produce large amount of semi-realistic inputs.
571 class DeltaEncodingFuzzerLikeTest
572 : public ::testing::TestWithParam<
573 std::tuple<DeltaSignedness, uint64_t, uint64_t, bool, uint64_t>> {
574 public:
DeltaEncodingFuzzerLikeTest()575 DeltaEncodingFuzzerLikeTest()
576 : signedness_(std::get<0>(GetParam())),
577 delta_max_bit_width_(std::get<1>(GetParam())),
578 num_of_values_(std::get<2>(GetParam())),
579 optional_values_(std::get<3>(GetParam())),
580 partial_random_seed_(std::get<4>(GetParam())) {
581 MaybeSetSignedness(signedness_);
582 }
583
584 ~DeltaEncodingFuzzerLikeTest() override = default;
585
586 // Running with the same seed for all variants would make all tests start
587 // with the same sequence; avoid this by making the seed different.
Seed() const588 uint64_t Seed() const {
589 // Multiply everything but by different primes to produce unique results.
590 return 2 * static_cast<uint64_t>(signedness_) + 3 * delta_max_bit_width_ +
591 5 * delta_max_bit_width_ + 7 * num_of_values_ +
592 11 * static_cast<uint64_t>(optional_values_) +
593 13 * partial_random_seed_;
594 }
595
596 const DeltaSignedness signedness_;
597 const uint64_t delta_max_bit_width_;
598 const uint64_t num_of_values_;
599 const bool optional_values_;
600 const uint64_t partial_random_seed_; // Explained where it's used.
601 };
602
TEST_P(DeltaEncodingFuzzerLikeTest,Test)603 TEST_P(DeltaEncodingFuzzerLikeTest, Test) {
604 const absl::optional<uint64_t> base(3432);
605
606 Random prng(Seed());
607 std::vector<absl::optional<uint64_t>> deltas(num_of_values_);
608 for (size_t i = 0; i < deltas.size(); ++i) {
609 if (!optional_values_ || prng.Rand<bool>()) {
610 deltas[i] = RandomWithMaxBitWidth(&prng, delta_max_bit_width_);
611 }
612 }
613 const auto values =
614 CreateSequenceByOptionalDeltas(base.value(), deltas, num_of_values_);
615
616 TestEncodingAndDecoding(base, values);
617 }
618
619 INSTANTIATE_TEST_SUITE_P(
620 SignednessOverrideAndDeltaMaxBitWidthAndNumberOfValuesInSequence,
621 DeltaEncodingFuzzerLikeTest,
622 ::testing::Combine(
623 ::testing::Values(DeltaSignedness::kNoOverride,
624 DeltaSignedness::kForceUnsigned,
625 DeltaSignedness::kForceSigned),
626 ::testing::Values(1, 4, 8, 15, 16, 17, 31, 32, 33, 63, 64),
627 ::testing::Values(1, 2, 100, 10000),
628 ::testing::Bool(),
629 ::testing::Values(21, 22, 23)));
630
631 class DeltaEncodingSpecificEdgeCasesTest
632 : public ::testing::TestWithParam<
633 std::tuple<DeltaSignedness, uint64_t, bool>> {
634 public:
DeltaEncodingSpecificEdgeCasesTest()635 DeltaEncodingSpecificEdgeCasesTest() {
636 UnsetFixedLengthEncoderDeltaSignednessForTesting();
637 }
638
639 ~DeltaEncodingSpecificEdgeCasesTest() override = default;
640 };
641
642 // This case is special because it produces identical forward/backward deltas.
TEST_F(DeltaEncodingSpecificEdgeCasesTest,SignedDeltaWithOnlyTopBitOn)643 TEST_F(DeltaEncodingSpecificEdgeCasesTest, SignedDeltaWithOnlyTopBitOn) {
644 MaybeSetSignedness(DeltaSignedness::kForceSigned);
645
646 const absl::optional<uint64_t> base(3432);
647
648 const uint64_t delta = static_cast<uint64_t>(1) << 63;
649 const std::vector<absl::optional<uint64_t>> values = {base.value() + delta};
650
651 TestEncodingAndDecoding(base, values);
652 }
653
TEST_F(DeltaEncodingSpecificEdgeCasesTest,MaximumUnsignedDelta)654 TEST_F(DeltaEncodingSpecificEdgeCasesTest, MaximumUnsignedDelta) {
655 MaybeSetSignedness(DeltaSignedness::kForceUnsigned);
656
657 const absl::optional<uint64_t> base((static_cast<uint64_t>(1) << 63) + 0x123);
658
659 const std::vector<absl::optional<uint64_t>> values = {base.value() - 1};
660
661 TestEncodingAndDecoding(base, values);
662 }
663
664 // Check that, if all deltas are set to -1, things still work.
TEST_P(DeltaEncodingSpecificEdgeCasesTest,ReverseSequence)665 TEST_P(DeltaEncodingSpecificEdgeCasesTest, ReverseSequence) {
666 MaybeSetSignedness(std::get<0>(GetParam()));
667 const uint64_t width = std::get<1>(GetParam());
668 const bool wrap_around = std::get<2>(GetParam());
669
670 const uint64_t value_mask = (width == 64)
671 ? std::numeric_limits<uint64_t>::max()
672 : ((static_cast<uint64_t>(1) << width) - 1);
673
674 const uint64_t base = wrap_around ? 1u : (0xf82d3 & value_mask);
675 const std::vector<absl::optional<uint64_t>> values = {
676 (base - 1u) & value_mask, (base - 2u) & value_mask,
677 (base - 3u) & value_mask};
678
679 TestEncodingAndDecoding(base, values);
680 }
681
682 INSTANTIATE_TEST_SUITE_P(
683 _,
684 DeltaEncodingSpecificEdgeCasesTest,
685 ::testing::Combine(
686 ::testing::Values(DeltaSignedness::kNoOverride,
687 DeltaSignedness::kForceUnsigned,
688 DeltaSignedness::kForceSigned),
689 ::testing::Values(1, 4, 8, 15, 16, 17, 31, 32, 33, 63, 64),
690 ::testing::Bool()));
691
692 } // namespace
693 } // namespace webrtc
694