-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathhuffmanmodel.h
240 lines (193 loc) · 6.82 KB
/
huffmanmodel.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
#ifndef JWUTIL_HUFFMANMODEL_H
#define JWUTIL_HUFFMANMODEL_H
#include <assert.h>
#include <array>
#include <queue>
#include <limits.h>
#include "fastmath.h"
namespace jw_util
{
static constexpr unsigned int HuffmanModelDynamic = static_cast<unsigned int>(-1);
template <unsigned int outputs>
class HuffmanModel
{
public:
class Builder
{
public:
void add_output(unsigned int value, std::uint64_t freq)
{
nodes.push(new Node(freq, value));
}
void compile(HuffmanModel &model)
{
assert(!nodes.empty());
alloc_data(model.read_tree, nodes.size() * 2 - 1);
alloc_data(model.write_list, nodes.size());
while (nodes.size() >= 2)
{
const Node *first = nodes.top();
nodes.pop();
const Node *second = nodes.top();
nodes.pop();
std::uint64_t freq_sum = first->freq + second->freq;
// Catch possible overflow
assert(freq_sum >= first->freq);
assert(freq_sum >= second->freq);
nodes.push(new Node(freq_sum, first, second));
}
unsigned int read_tree_pos = 0;
descend(model, read_tree_pos, 0, 0, nodes.top());
assert_at_end(model.read_tree, read_tree_pos);
nodes.pop();
assert(nodes.empty());
}
static void decompile(HuffmanModel &model)
{
free_data(model.read_tree);
free_data(model.write_list);
}
private:
struct Node
{
Node(std::uint64_t freq, unsigned int value)
: freq(freq)
, value(value)
, children{0, 0}
{}
Node(std::uint64_t freq, const Node *child1, const Node *child2)
: freq(freq)
, children{child1, child2}
{}
std::uint64_t freq;
unsigned int value;
const Node *children[2];
struct Comparator
{
bool operator() (const Node *a, const Node *b) const
{
if (a->children[0] == 0 && b->children[0] == 0) {
assert(a->value != b->value);
}
if (a->freq != b->freq) {return a->freq > b->freq;}
else {return a->value > b->value;}
}
};
};
std::priority_queue<Node*, std::vector<Node*>, typename Node::Comparator> nodes;
static void descend(HuffmanModel &model, unsigned int &read_tree_pos, unsigned int path, unsigned int path_bits, const Node *node)
{
unsigned int prev_read_tree_pos = read_tree_pos;
assert_inside(model.read_tree, prev_read_tree_pos);
read_tree_pos++;
if (node->children[0])
{
// Branch
assert(node->children[1]);
descend(model, read_tree_pos, path | (0 << path_bits), path_bits + 1, node->children[0]);
model.read_tree[prev_read_tree_pos] = read_tree_pos - prev_read_tree_pos;
descend(model, read_tree_pos, path | (1 << path_bits), path_bits + 1, node->children[1]);
}
else
{
// Leaf
assert(!node->children[1]);
assert_inside(model.write_list, node->value);
assert(path_bits < sizeof(unsigned int) * CHAR_BIT);
model.read_tree[prev_read_tree_pos] = -node->value;
model.write_list[node->value] = path | (1 << path_bits);
}
delete node;
}
};
class Reader
{
public:
Reader()
{}
Reader(const HuffmanModel &model)
: ptr(get_data(model.read_tree))
{}
bool needs_bit() const
{
return *ptr > 0;
}
unsigned int get_result() const
{
assert(!needs_bit());
return -*ptr;
}
void recv_bit(bool bit)
{
assert(needs_bit());
ptr += bit ? *ptr : 1;
}
private:
const signed int *ptr;
};
class Writer
{
public:
Writer()
{}
Writer(const HuffmanModel &model, unsigned int value)
: path(model.write_list[value])
{}
bool has_bit() const
{
assert(path >= 1);
return path != 1;
}
bool get_bit() const
{
assert(has_bit());
return path & 1;
}
void next_bit()
{
assert(has_bit());
path >>= 1;
}
unsigned int num_bits_left() const
{
return FastMath::log2(path);
}
unsigned int get_remaining_bits() const
{
return path;
}
private:
unsigned int path;
};
private:
typedef typename std::conditional<outputs == HuffmanModelDynamic, signed int*, std::array<signed int, outputs * 2 - 1>>::type ReadTreeType;
typedef typename std::conditional<outputs == HuffmanModelDynamic, unsigned int*, std::array<unsigned int, outputs>>::type WriteListType;
ReadTreeType read_tree;
WriteListType write_list;
template <typename DataType>
static void alloc_data(DataType *&res, unsigned int count) {res = new DataType[count];}
template <typename DataType, std::size_t size>
static void alloc_data(std::array<DataType, size> &res, unsigned int count) {assert(count == size);}
template <typename DataType>
static DataType *get_data(DataType *res) {return res;}
template <typename DataType, std::size_t size>
static DataType *get_data(std::array<DataType, size> &res) {return res.data();}
template <typename DataType>
static const DataType *get_data(const DataType *res) {return res;}
template <typename DataType, std::size_t size>
static const DataType *get_data(const std::array<DataType, size> &res) {return res.data();}
template <typename DataType>
static void free_data(DataType *res) {delete[] res;}
template <typename DataType, std::size_t size>
static void free_data(std::array<DataType, size> &res) {}
template <typename DataType>
static void assert_inside(const DataType *res, unsigned int index) {}
template <typename DataType, std::size_t size>
static void assert_inside(const std::array<DataType, size> &res, unsigned int index) {assert(index < res.size());}
template <typename DataType>
static void assert_at_end(const DataType *res, unsigned int index) {}
template <typename DataType, std::size_t size>
static void assert_at_end(const std::array<DataType, size> &res, unsigned int index) {assert(index == res.size());}
};
}
#endif // JWUTIL_HUFFMANMODEL_H