RawSpeed
fast raw decoding library
Loading...
Searching...
No Matches
HuffmanCodeTest.cpp
Go to the documentation of this file.
1/*
2 RawSpeed - RAW file decoder.
3
4 Copyright (C) 2018 Roman Lebedev
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; withexpected even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19*/
20
21#include "codes/HuffmanCode.h"
22#include "adt/Array1DRef.h"
23#include "adt/Casts.h"
26#include "io/Buffer.h"
27#include <algorithm>
28#include <bitset>
29#include <cassert>
30#include <cstdint>
31#include <initializer_list>
32#include <ostream>
33#include <string>
34#include <tuple>
35#include <utility>
36#include <vector>
37#include <gtest/gtest.h>
38
39#ifndef NDEBUG
40#include <cstdlib>
41#endif
42
43namespace rawspeed {
44struct BaselineCodeTag;
45
46} // namespace rawspeed
47
48using rawspeed::BaselineCodeTag;
51using std::make_tuple;
52
53namespace rawspeed {
54
56
59 return !(lhs == rhs);
60}
61
62static ::std::ostream&
63operator<<(::std::ostream& os,
65 auto str = std::bitset<32>(s.code).to_string();
66
67 str = str.substr(str.size() - s.code_len);
68 return os << "0b" << str;
69}
70
73 return !(lhs == rhs);
74}
75
76} // namespace rawspeed
77
78namespace rawspeed_test {
79
80namespace {
81
82TEST(HuffmanCodeCodeSymbolTest, Equality) {
83#define s HuffmanCode<BaselineCodeTag>::CodeSymbol
84 ASSERT_EQ(s(0, 1), s(0, 1));
85 ASSERT_EQ(s(1, 1), s(1, 1));
86
87 ASSERT_NE(s(1, 1), s(0, 1));
88 ASSERT_NE(s(0, 1), s(1, 1));
89#undef s
90}
91
92#ifndef NDEBUG
93TEST(CodeSymbolDeathTest, CodeSymbolLength) {
94 ASSERT_DEATH(
95 { HuffmanCode<BaselineCodeTag>::CodeSymbol(0, 0); }, "code_len > 0");
96 ASSERT_DEATH(
97 { HuffmanCode<BaselineCodeTag>::CodeSymbol(1, 0); }, "code_len > 0");
98 ASSERT_DEATH(
100 "code_len <= Traits::MaxCodeLenghtBits");
101 ASSERT_DEATH(
103 "code_len <= Traits::MaxCodeLenghtBits");
104}
105
106using CodeSymbolType = std::tuple<int, int, bool>;
107class CodeSymbolDeathTest : public ::testing::TestWithParam<CodeSymbolType> {
108protected:
110 virtual void SetUp() {
111 auto p = GetParam();
112
113 val = std::get<0>(p);
114 len = std::get<1>(p);
115 die = std::get<2>(p);
116 }
117
118 int val;
119 int len;
120 bool die;
121};
123 // clang-format off
124 make_tuple(0b00, 1, false),
125 make_tuple(0b00, 2, false),
126 make_tuple(0b01, 1, false),
127 make_tuple(0b01, 2, false),
128 make_tuple(0b10, 1, true),
129 make_tuple(0b10, 2, false),
130 make_tuple(0b11, 1, true),
131 make_tuple(0b11, 2, false),
132 // clang-format on
133};
135 ::testing::ValuesIn(CodeSymbolData));
137 if (die) {
138 ASSERT_DEATH(
139 {
144 },
145 "code <= \\(\\(1U << code_len\\) - 1U\\)");
146 } else {
147 ASSERT_EXIT(
148 {
153 exit(0);
154 },
155 ::testing::ExitedWithCode(0), "");
156 }
157}
158#endif
159
160using CodeSymbolPrintDataType = std::tuple<int, int, std::string>;
162 : public ::testing::TestWithParam<CodeSymbolPrintDataType> {
163protected:
165 virtual void SetUp() {
166 auto p = GetParam();
167
168 val = std::get<0>(p);
169 len = std::get<1>(p);
170 str = std::get<2>(p);
171 }
172
173 int val;
174 int len;
175 std::string str;
176};
178 // clang-format off
179 make_tuple(0b00, 1, "0b0"),
180 make_tuple(0b00, 2, "0b00"),
181 make_tuple(0b01, 1, "0b1"),
182 make_tuple(0b01, 2, "0b01"),
183 make_tuple(0b10, 2, "0b10"),
184 make_tuple(0b11, 2, "0b11"),
185 // clang-format on
186};
188 ::testing::ValuesIn(CodeSymbolPrintData));
197
199 std::tuple<HuffmanCode<BaselineCodeTag>::CodeSymbol,
202 : public ::testing::TestWithParam<CodeSymbolHaveCommonPrefixDataType> {
203protected:
205 virtual void SetUp() {
206 auto p = GetParam();
207
208 symbol = std::get<0>(p);
209 partial = std::get<1>(p);
210 }
211
214};
215std::vector<HuffmanCode<BaselineCodeTag>::CodeSymbol>
217 // change those two together
218 static constexpr auto maxLen = 2U;
219 static constexpr auto expectedCnt = 2U + 4U;
220
221 std::vector<HuffmanCode<BaselineCodeTag>::CodeSymbol> allVariants;
222 allVariants.reserve(expectedCnt);
223 for (unsigned l = 1; l <= maxLen; l++) {
224 for (unsigned c = 0; c <= ((1U << l) - 1U); c++)
225 allVariants.emplace_back(c, l);
226 }
227 assert(allVariants.size() == expectedCnt);
228 return allVariants;
229}
233 ::testing::Combine(::testing::ValuesIn(allPossibleCodeSymbols),
234 ::testing::ValuesIn(allPossibleCodeSymbols)));
236 if (partial.code_len > symbol.code_len)
237 return;
238
239 auto symbol_str = ::testing::PrintToString(symbol);
240 auto partial_str = ::testing::PrintToString(partial);
241 const auto len = std::min(symbol_str.length(), partial_str.length());
242 // Trim them to the same length (cut end chars)
243 symbol_str.resize(len);
244 partial_str.resize(len);
246 partial),
247 symbol_str == partial_str)
248 << "Where symbol_str = " << symbol_str
249 << ", partial_str = " << partial_str;
250}
252 {
253 // Self-check for common prefix equals true
255 ASSERT_TRUE(
257 }
259 {0b0, 1}, {0b0, 1}));
261 {0b10, 2}, {0b1, 1}));
263 {0b10, 2}, {0b0, 1}));
265 {0b10, 2}, {0b01, 2}));
266}
267
268#ifndef NDEBUG
269TEST(CodeSymbolHaveCommonPrefixDeathTest, AsymmetricalDeathTest) {
270 ASSERT_DEATH(
271 {
273 {0b0, 2});
274 },
275 "partial.code_len <= symbol.code_len");
276 ASSERT_DEATH(
277 {
279 {0b010, 3});
280 },
281 "partial.code_len <= symbol.code_len");
282}
283#endif
284
285auto genHT = [](std::initializer_list<uint8_t>&& nCodesPerLength)
288 std::vector<uint8_t> v(nCodesPerLength.begin(), nCodesPerLength.end());
289 v.resize(16);
290 Buffer b(v.data(),
292 hc.setNCodesPerLength(b);
293
294 return hc;
295};
296
298 [](std::initializer_list<uint8_t>&& nCodesPerLength) -> uint32_t {
300 std::vector<uint8_t> v(nCodesPerLength.begin(), nCodesPerLength.end());
301 v.resize(16);
302 Buffer b(v.data(),
304 return hc.setNCodesPerLength(b);
305};
306
307auto genHTFull = [](std::initializer_list<uint8_t>&& nCodesPerLength,
308 std::initializer_list<uint8_t>&& codeValues)
310 auto hc = genHT(std::move(nCodesPerLength));
311 std::vector<uint8_t> v(codeValues.begin(), codeValues.end());
314 hc.setCodeValues(b);
315 return hc;
316};
317
318#ifndef NDEBUG
319TEST(HuffmanCodeDeathTest, setNCodesPerLengthRequires16Lengths) {
320 for (int i = 1; i < 32; i++) {
321 std::vector<uint8_t> v(i, 1);
322 ASSERT_EQ(v.size(), i);
323
324 Buffer b(v.data(),
326 ASSERT_EQ(b.getSize(),
328
330
331 if (b.getSize() != 16) {
332 ASSERT_DEATH(
333 { hc.setNCodesPerLength(b); },
334 "data.getSize\\(\\) == Traits::MaxCodeLenghtBits");
335 } else {
336 ASSERT_EXIT(
337 {
338 hc.setNCodesPerLength(b);
339
340 exit(0);
341 },
342 ::testing::ExitedWithCode(0), "");
343 }
344 }
345}
346#endif
347
348TEST(HuffmanCodeTest, setNCodesPerLengthEqualCompareAndTrimming) {
349 {
352
353 ASSERT_EQ(a, b);
354 }
355
356 ASSERT_EQ(genHT({1}), genHT({1}));
357 ASSERT_EQ(genHT({1}), genHT({1, 0}));
358 ASSERT_EQ(genHT({1, 0}), genHT({1}));
359 ASSERT_EQ(genHT({1, 0}), genHT({1, 0}));
360 ASSERT_EQ(genHT({0, 1}), genHT({0, 1}));
361 ASSERT_EQ(genHT({1, 1}), genHT({1, 1}));
362
363 ASSERT_NE(genHT({1, 0}), genHT({1, 1}));
364 ASSERT_NE(genHT({0, 1}), genHT({1}));
365 ASSERT_NE(genHT({0, 1}), genHT({1, 0}));
366 ASSERT_NE(genHT({0, 1}), genHT({1, 1}));
367 ASSERT_NE(genHT({1}), genHT({1, 1}));
368}
369
370TEST(HuffmanCodeTest, setNCodesPerLengthEmptyIsBad) {
371 ASSERT_THROW(genHT({}), rawspeed::RawDecoderException);
372 ASSERT_THROW(genHT({0}), rawspeed::RawDecoderException);
373 ASSERT_THROW(genHT({0, 0}), rawspeed::RawDecoderException);
374}
375
376TEST(HuffmanCodeTest, setNCodesPerLengthTooManyCodesTotal) {
377 ASSERT_NO_THROW(genHT({0, 0, 0, 0, 0, 0, 0, 162}));
378 ASSERT_THROW(genHT({0, 0, 0, 0, 0, 0, 0, 163}),
380}
381
382TEST(HuffmanCodeTest, setNCodesPerLengthTooManyCodesForLength) {
383 for (int len = 1; len < 8; len++) {
385 std::vector<uint8_t> v(16, 0);
386 Buffer b(v.data(),
388 for (auto i = 1U; i <= (1U << len); i++) {
389 v[len - 1] = rawspeed::implicit_cast<uint8_t>(i);
390 ASSERT_NO_THROW(ht.setNCodesPerLength(b););
391 }
392 v[len - 1]++;
394 }
395}
396
397TEST(HuffmanCodeTest, setNCodesPerLengthCodeSymbolOverflow) {
398 ASSERT_NO_THROW(genHT({1}));
399 ASSERT_NO_THROW(genHT({2}));
400 ASSERT_THROW(genHT({3}), rawspeed::RawDecoderException);
401 ASSERT_NO_THROW(genHT({1, 2}));
402 ASSERT_THROW(genHT({1, 3}), rawspeed::RawDecoderException);
403 ASSERT_THROW(genHT({2, 1}), rawspeed::RawDecoderException);
404 ASSERT_NO_THROW(genHT({0, 4}));
405 ASSERT_THROW(genHT({0, 5}), rawspeed::RawDecoderException);
406}
407
408TEST(HuffmanCodeTest, setNCodesPerLengthCounts) {
409 ASSERT_EQ(genHTCount({1}), 1);
410 ASSERT_EQ(genHTCount({1, 0}), 1);
411 ASSERT_EQ(genHTCount({0, 1}), 1);
412 ASSERT_EQ(genHTCount({0, 2}), 2);
413 ASSERT_EQ(genHTCount({0, 3}), 3);
414 ASSERT_EQ(genHTCount({1, 1}), 2);
415 ASSERT_EQ(genHTCount({1, 2}), 3);
416}
417
418#ifndef NDEBUG
419TEST(HuffmanCodeDeathTest, setCodeValuesRequiresCount) {
420 for (int len = 1; len < 8; len++) {
422 std::vector<uint8_t> l(16, 0);
423 Buffer bl(l.data(),
425 l[len - 1] = rawspeed::implicit_cast<uint8_t>((1U << len) - 1U);
426 const auto count = ht.setNCodesPerLength(bl);
427 std::vector<uint8_t> v;
428 v.reserve(count + 1);
429 for (auto cnt = count - 1; cnt <= count + 1; cnt++) {
430 v.resize(cnt);
432 v.data(),
434 if (cnt != count) {
435 ASSERT_DEATH(
436 { ht.setCodeValues(bv); }, "static_cast<unsigned>\\(data.size\\(\\)"
437 "\\) == maxCodesCount\\(\\)");
438 } else {
439 ASSERT_EXIT(
440 {
441 ht.setCodeValues(bv);
442 exit(0);
443 },
444 ::testing::ExitedWithCode(0), "");
445 }
446 }
447 }
448}
449
450TEST(HuffmanCodeDeathTest, setCodeValuesRequiresLessThan162) {
451 auto ht = genHT({0, 0, 0, 0, 0, 0, 0, 162});
452 std::vector<uint8_t> v(163, 0);
455 ASSERT_DEATH(
456 { ht.setCodeValues(bv); }, "data.size\\(\\) <= Traits::MaxNumCodeValues");
457}
458#endif
459
460TEST(HuffmanCodeTest, setCodeValuesValueLessThan16) {
461 auto ht = genHT({1});
462 std::vector<uint8_t> v(1);
463
464 for (int i = 0; i < 256; i++) {
467 v.data(),
469 ASSERT_NO_THROW(ht.setCodeValues(b););
470 }
471}
472
473TEST(HuffmanCodeTest, EqualCompareAndTrimming) {
474 ASSERT_EQ(genHTFull({1}, {0}), genHTFull({1}, {0}));
475 ASSERT_EQ(genHTFull({1}, {1}), genHTFull({1}, {1}));
476
477 ASSERT_EQ(genHTFull({1}, {0}), genHTFull({1, 0}, {0}));
478 ASSERT_EQ(genHTFull({1, 0}, {0}), genHTFull({1, 0}, {0}));
479 ASSERT_EQ(genHTFull({1, 0}, {0}), genHTFull({1}, {0}));
480
481 ASSERT_NE(genHTFull({1}, {0}), genHTFull({1}, {1}));
482 ASSERT_NE(genHTFull({1}, {1}), genHTFull({1}, {0}));
483
484 ASSERT_NE(genHTFull({1}, {0}), genHTFull({1, 0}, {1}));
485 ASSERT_NE(genHTFull({1, 0}, {0}), genHTFull({1, 0}, {1}));
486 ASSERT_NE(genHTFull({1, 0}, {0}), genHTFull({1}, {1}));
487}
488
489using SignExtendDataType = std::tuple<uint32_t, uint32_t, int>;
490class SignExtendTest : public ::testing::TestWithParam<SignExtendDataType> {
491protected:
492 SignExtendTest() = default;
493 virtual void SetUp() {
494 auto p = GetParam();
495
496 diff = std::get<0>(p);
497 len = std::get<1>(p);
498 value = std::get<2>(p);
499 }
500
503 int value;
504};
505
506auto zeroDiff = [](int len) { return make_tuple(0, len, -((1 << len) - 1)); };
507auto passthrough = [](int len) {
508 return make_tuple(((1 << len) - 1), len, ((1 << len) - 1));
509};
510auto one = [](int len) { return make_tuple((1 << len), len, 1); };
512 // clang-format off
513 zeroDiff(1),
514 zeroDiff(2),
515 zeroDiff(3),
516 zeroDiff(4),
517 zeroDiff(5),
518 zeroDiff(6),
519 zeroDiff(7),
520 zeroDiff(8),
521 zeroDiff(9),
522 zeroDiff(10),
523 zeroDiff(11),
524 zeroDiff(12),
525 zeroDiff(13),
526 zeroDiff(14),
527 zeroDiff(15),
528 zeroDiff(16),
529
530 passthrough(1),
531 passthrough(2),
532 passthrough(3),
533 passthrough(4),
534 passthrough(5),
535 passthrough(6),
536 passthrough(7),
537 passthrough(8),
538 passthrough(9),
539 passthrough(10),
540 passthrough(11),
541 passthrough(12),
542 passthrough(13),
543 passthrough(14),
544 passthrough(15),
545 passthrough(16),
546
547 one(1),
548 one(2),
549 one(3),
550 one(4),
551 one(5),
552 one(6),
553 one(7),
554 one(8),
555 one(9),
556 one(10),
557 one(11),
558 one(12),
559 one(13),
560 one(14),
561 one(15),
562 one(16),
563
564 make_tuple(0b00, 0b01, -0b001),
565 make_tuple(0b01, 0b01, 0b001),
566 make_tuple(0b10, 0b01, 0b001),
567 make_tuple(0b11, 0b01, 0b011),
568 make_tuple(0b00, 0b10, -0b011),
569 make_tuple(0b01, 0b10, -0b010),
570 make_tuple(0b10, 0b10, 0b010),
571 make_tuple(0b11, 0b10, 0b011),
572 make_tuple(0b00, 0b11, -0b111),
573 make_tuple(0b01, 0b11, -0b110),
574 make_tuple(0b10, 0b11, -0b101),
575 make_tuple(0b11, 0b11, -0b100),
576 // clang-format on
577};
579 ::testing::ValuesIn(signExtendData));
585
587 std::tuple<std::vector<uint8_t>,
588 std::vector<HuffmanCode<BaselineCodeTag>::CodeSymbol>>;
590 : public ::testing::TestWithParam<generateCodeSymbolsDataType> {
591protected:
593 virtual void SetUp() {
594 auto p = GetParam();
595
596 ncpl = std::get<0>(p);
597 ncpl.resize(16);
598 expectedSymbols = std::get<1>(p);
599 }
600
601 std::vector<uint8_t> ncpl;
602 std::vector<HuffmanCode<BaselineCodeTag>::CodeSymbol> expectedSymbols;
603};
605 make_tuple(std::vector<uint8_t>{1},
606 std::vector<HuffmanCode<BaselineCodeTag>::CodeSymbol>{{0b0, 1}}),
607
608 make_tuple(std::vector<uint8_t>{0, 1},
609 std::vector<HuffmanCode<BaselineCodeTag>::CodeSymbol>{
610 {0b00, 2},
611 }),
612 make_tuple(std::vector<uint8_t>{0, 2},
613 std::vector<HuffmanCode<BaselineCodeTag>::CodeSymbol>{
614 {0b00, 2},
615 {0b01, 2},
616 }),
617 make_tuple(std::vector<uint8_t>{0, 3},
618 std::vector<HuffmanCode<BaselineCodeTag>::CodeSymbol>{
619 {0b00, 2},
620 {0b01, 2},
621 {0b10, 2},
622 }),
623
624 make_tuple(std::vector<uint8_t>{1, 1},
625 std::vector<HuffmanCode<BaselineCodeTag>::CodeSymbol>{
626 {0b0, 1},
627 {0b10, 2},
628 }),
629 make_tuple(std::vector<uint8_t>{1, 2},
630 std::vector<HuffmanCode<BaselineCodeTag>::CodeSymbol>{
631 {0b0, 1},
632 {0b10, 2},
633 {0b11, 2},
634 }),
635
636};
638 ::testing::ValuesIn(generateCodeSymbolsData));
641 Buffer bl(ncpl.data(),
643 const auto cnt = hc.setNCodesPerLength(bl);
644 std::vector<uint8_t> cv(cnt, 0);
646 cv.data(),
648 hc.setCodeValues(bv);
649
650 ASSERT_EQ(hc.generateCodeSymbols(), expectedSymbols);
651}
652
653} // namespace
654
655} // namespace rawspeed_test
#define s
INSTANTIATE_TEST_SUITE_P(MD5Test, MD5Test, ::testing::ValuesIn(testCases))
TEST_P(MD5Test, CheckTestCaseSet)
Definition MD5Test.cpp:388
assert(dim.area() >=area)
static int RAWSPEED_READNONE extend(uint32_t diff, uint32_t len)
size_type RAWSPEED_READONLY getSize() const
Definition Buffer.h:115
uint32_t setNCodesPerLength(Buffer data)
Definition HuffmanCode.h:99
std::vector< CodeSymbol > generateCodeSymbols() const
Definition HuffmanCode.h:66
typename AbstractPrefixCode< CodeTag >::CodeSymbol CodeSymbol
Definition HuffmanCode.h:45
void setCodeValues(Array1DRef< const typename Traits::CodeValueTy > data)
std::vector< HuffmanCode< BaselineCodeTag >::CodeSymbol > expectedSymbols
std::tuple< std::vector< uint8_t >, std::vector< HuffmanCode< BaselineCodeTag >::CodeSymbol > > generateCodeSymbolsDataType
std::vector< HuffmanCode< BaselineCodeTag >::CodeSymbol > GenerateAllPossibleCodeSymbols()
std::tuple< int, int, std::string > CodeSymbolPrintDataType
const generateCodeSymbolsDataType generateCodeSymbolsData[]
std::tuple< HuffmanCode< BaselineCodeTag >::CodeSymbol, HuffmanCode< BaselineCodeTag >::CodeSymbol > CodeSymbolHaveCommonPrefixDataType
std::tuple< uint32_t, uint32_t, int > SignExtendDataType
constexpr RAWSPEED_READNONE Ttgt implicit_cast(Tsrc value)
Definition Casts.h:32
bool operator!=(const AlignedAllocator< T1, A1 > &, const AlignedAllocator< T2, A2 > &)
static inline ::std::ostream & operator<<(::std::ostream &os, const T &b)