Project
Loading...
Searching...
No Matches
test_HuffmanCodec.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
16
17#define BOOST_TEST_MODULE HuffmanCodec unit test
18#define BOOST_TEST_MAIN
19#define BOOST_TEST_DYN_LINK
20#include <boost/test/unit_test.hpp>
21#include <filesystem>
22#include <iostream>
23#include <iomanip>
24#include <sstream>
25#include <vector>
26#include <bitset>
27#include <thread>
28#include <stdexcept> // exeptions, runtime_error
29#include "../include/DataCompression/dc_primitives.h"
30#include "../include/DataCompression/HuffmanCodec.h"
32#include "DataGenerator.h"
33#include "Fifo.h"
34
35namespace o2
36{
37namespace data_compression
38{
39
40// a decoder process working on the FIFO of encoded data and comparing to
41// original data from the corresponding FIFO
42template <class RandvalStreamT, class EncodedStreamT, class CodecT>
43void decoderProcess(RandvalStreamT& fifoRandvals, EncodedStreamT& fifoEncoded, CodecT& codec)
44{
45 uint16_t decodedLen = 0;
46 typename CodecT::model_type::value_type decodedValue;
47 do {
48 } while (fifoEncoded.pull([&](typename EncodedStreamT::value_type c) {
49 codec.Decode(decodedValue, c, decodedLen);
50 return fifoRandvals.pull([&](typename RandvalStreamT::value_type v) {
51 if (decodedValue != v) {
52 throw std::runtime_error("decoding mismatch");
53 return false;
54 } else {
55 // std::cout << "decoded: "
56 // << std::setw(4) << decodedValue
57 // << " code: " << c
58 // << std::endl;
59 }
60 return true;
61 });
62 }));
63}
64
65template <typename CodecT, typename GeneratorT>
66void checkRandom(CodecT& codec, GeneratorT& generator, int nRolls = 1000000)
67{
68 using ValueT = typename CodecT::value_type;
69 using CodeT = typename CodecT::code_type;
70 auto const& huffmanmodel = codec.getCodingModel();
71
73 // test loop for random values
74 //
75
76 // FIFO for the random numbers
77 o2::test::Fifo<ValueT> fifoRandvals;
78
79 // FIFO for encoded values
80 using FifoBuffer_t = o2::test::Fifo<uint32_t>;
81 FifoBuffer_t fifoEncoded;
82
83 int n = nRolls;
84 std::cout << std::endl
85 << "Testing encoding-decoding with " << nRolls << " random value(s) ..." << std::endl;
86
87 std::thread decoderThread([&]() { decoderProcess(fifoRandvals, fifoEncoded, codec); });
88
89 while (n-- > 0) {
90 uint16_t codeLen = 0;
91 CodeT code;
92 ValueT value = generator();
93 codec.Encode(value, code, codeLen);
94 fifoRandvals.push(value);
95 if (huffmanmodel.OrderMSB) {
96 code <<= (code.size() - codeLen);
97 }
98 fifoEncoded.push(code.to_ulong(), n == 0);
99 // std::cout << "encoded: " << std::setw(4) << value << " code: " << code << std::endl;
100 }
101
102 decoderThread.join();
103
104 std::cout << "... done" << std::endl;
105}
106
108{
109 // defining a contiguous alphabet of integral 16 bit unsigned numbers
110 // in the range [-7, 10] including the upper bound
111 // the first definition is a data type, then an object of this type is
112 // defined
113 using TestDistribution_t = o2::test::normal_distribution<double>;
115 DataGenerator_t dg(-7, 10, 1, 0., 1.);
116 using SimpleRangeAlphabet_t = ContiguousAlphabet<DataGenerator_t::value_type, -7, 10>;
117 SimpleRangeAlphabet_t alphabet;
118
120 // Using the Huffman propability model for the alphabet
121 //
122 // HuffmanModel_t is a data type, huffmanmodel an object of this type
123 // the node type is defined to be HuffmanNode specialized to bitset<16>
124 // third template parameter determines whether code has to be decoded
125 // MSB to LSB (true) or LSB to MSB (false)
126 using HuffmanModel_t =
128 HuffmanModel_t huffmanmodel;
129
130 huffmanmodel.init(0.);
131 if (verbosity > 0) {
132 std::cout << std::endl
133 << "Huffman probability model after initialization: " << std::endl;
134 for (auto s : alphabet) {
135 std::cout << "val = " << std::setw(2) << s << " --> weight = " << huffmanmodel[s] << std::endl;
136 }
137 }
138
139 // add probabilities from data generator as weights for every symbol
140 for (auto s : alphabet) {
141 huffmanmodel.addWeight(s, dg.getProbability(s));
142 }
143
144 // normalizing the weight to the total weight thus having the probability
145 // for every symbol
146 huffmanmodel.normalize();
147 if (verbosity > 0) {
148 std::cout << std::endl
149 << "Probabilities from DataGenerator:" << std::endl;
150 for (auto i : huffmanmodel) {
151 std::cout << "val = " << std::setw(2) << i.first << " --> weight = " << i.second << std::endl;
152 }
153 }
154
156 // generate the Huffman tree in the Huffman model and create a Huffman
157 // codec operating on the probability table
158 huffmanmodel.GenerateHuffmanTree();
159 if (verbosity > 0) {
160 std::cout << std::endl
161 << "Generating binary tree and Huffman codes" << std::endl;
162 huffmanmodel.print();
163 }
164 return std::pair<HuffmanCodec<HuffmanModel_t>, DataGenerator_t>(huffmanmodel, dg);
165}
166
167BOOST_AUTO_TEST_CASE(test_HuffmanCodec_basic)
168{
169 auto setup = setupCodec();
170 auto& codec = setup.first;
171 auto& dg = setup.second;
172 auto const& huffmanmodel = codec.getCodingModel();
173 using ValueT = decltype(setup.first)::value_type;
174 using CodeT = decltype(setup.first)::code_type;
175
177 // print Huffman code summary and perform an encoding-decoding check for
178 // every symbol
179 std::cout << std::endl
180 << "Huffman code summary: " << (huffmanmodel.OrderMSB ? "MSB to LSB" : "LSB to MSB") << std::endl;
181 for (auto const& i : huffmanmodel) {
182 uint16_t codeLen = 0;
183 CodeT code;
184 codec.Encode(i.first, code, codeLen);
185 std::cout << "value: " << std::setw(4) << i.first << " code length: " << std::setw(3) << codeLen << " code: ";
186 if (not huffmanmodel.OrderMSB) {
187 std::cout << std::setw(code.size() - codeLen);
188 }
189 for (int k = 0; k < codeLen; k++) {
190 std::cout << code[codeLen - 1 - k];
191 }
192 std::cout << std::endl;
193 if (huffmanmodel.OrderMSB) {
194 code <<= (code.size() - codeLen);
195 }
196 uint16_t decodedLen = 0;
197 ValueT value;
198 codec.Decode(value, code, decodedLen);
199 if (codeLen != decodedLen || value != i.first) {
200 std::cout << "mismatch in decoded value: " << value << "(" << decodedLen << ")" << std::endl;
201 }
202 }
203
204 checkRandom(codec, dg);
205}
206
207BOOST_AUTO_TEST_CASE(test_HuffmanCodec_configuration)
208{
209 auto setup = setupCodec();
210 auto& codec = setup.first;
211 auto& dg = setup.second;
212 auto const& huffmanmodel = codec.getCodingModel();
213 using ValueT = decltype(setup.first)::value_type;
214 using CodeT = decltype(setup.first)::code_type;
215
216 // check writing and reading of the huffman configuration
217 std::stringstream filename;
218 filename << o2::utils::Str::create_unique_path(std::filesystem::temp_directory_path().native()) << "_testHuffmanCodec.zlib";
219
220 auto nNodes = codec.writeConfiguration(filename.str().c_str(), "zlib");
221 BOOST_CHECK(nNodes > 0);
222 auto result = codec.loadConfiguration(filename.str().c_str(), "zlib");
223 BOOST_CHECK(result == 0);
224 std::filesystem::remove(filename.str());
225
226 checkRandom(codec, dg);
227}
228
229} // namespace data_compression
230} // namespace o2
#define verbosity
A simple data generator.
Thread safe FIFO.
int32_t i
uint32_t c
Definition RawData.h:2
A simple data generator.
A thread safe FIFO.
Definition Fifo.h:60
void push(T something, bool isLast=false)
Definition Fifo.h:68
specialization of std::normal_distribution which implements also the analytic formula.
GLdouble n
Definition glcorearb.h:1982
GLuint64EXT * result
Definition glcorearb.h:5662
GLsizei const GLfloat * value
Definition glcorearb.h:819
void decoderProcess(RandvalStreamT &fifoRandvals, EncodedStreamT &fifoEncoded, CodecT &codec)
void checkRandom(CodecT &codec, GeneratorT &generator, int nRolls=1000000)
auto setupCodec(int verbosity=0)
a couple of static helper functions to create timestamp values for CCDB queries or override obsolete ...
BOOST_AUTO_TEST_CASE(FlatHisto)
std::string filename()
static std::string create_unique_path(const std::string_view prefix="", int length=16)
size_t nRolls
BOOST_CHECK(tree)