21namespace pixie::detail {
23inline constexpr std::size_t kByteAlphabetSize = 256;
24inline constexpr std::size_t kMaximumHuffmanNodes = 2 * kByteAlphabetSize - 1;
25inline constexpr std::size_t kMaximumHuffmanCodeLength = 11;
28 std::int16_t symbol = -1;
29 std::int16_t left = -1;
30 std::int16_t right = -1;
34 std::array<HuffmanBuildNode, kMaximumHuffmanNodes> tree{};
35 std::array<std::uint8_t, kMaximumHuffmanNodes> split_rank{};
36 std::array<std::uint8_t, kByteAlphabetSize> symbol_to_rank{};
37 std::int16_t root = -1;
38 std::size_t node_count = 0;
39 std::size_t symbol_count = 0;
42namespace huffman_build_detail {
45 std::size_t frequency;
51inline void sort_leaves_by_frequency(
52 std::span<FrequencyLeaf> leaves,
53 std::array<FrequencyLeaf, kByteAlphabetSize>& temporary) {
54 std::size_t maximum = 0;
56 maximum = std::max(maximum, leaf.frequency);
58 std::size_t byte_count = 0;
59 while (maximum != 0) {
64 FrequencyLeaf* source = leaves.data();
65 FrequencyLeaf* destination = temporary.data();
66 for (std::size_t
byte = 0;
byte < byte_count; ++byte) {
67 const std::size_t shift =
byte * 8;
68 std::array<std::size_t, 256> counts{};
69 for (std::size_t index = 0; index < leaves.size(); ++index) {
70 ++counts[(source[index].frequency >> shift) & 0xff];
72 std::size_t prefix = 0;
73 for (std::size_t& count : counts) {
74 const std::size_t current = count;
78 for (std::size_t index = 0; index < leaves.size(); ++index) {
79 const std::size_t bucket = (source[index].frequency >> shift) & 0xff;
80 destination[counts[bucket]++] = source[index];
82 std::swap(source, destination);
84 if (source != leaves.data()) {
85 std::copy_n(source, leaves.size(), leaves.data());
89inline std::array<std::uint8_t, kByteAlphabetSize> build_code_lengths(
90 std::span<const std::size_t> frequencies,
91 std::span<const std::uint16_t> used_symbols) {
92 std::array<std::uint8_t, kByteAlphabetSize> lengths{};
93 if (used_symbols.size() < 2) {
94 if (!used_symbols.empty()) {
95 lengths[used_symbols.front()] = 1;
100 std::array<FrequencyLeaf, kByteAlphabetSize> leaves{};
101 for (std::size_t index = 0; index < used_symbols.size(); ++index) {
102 const std::uint16_t symbol = used_symbols[index];
103 leaves[index] = {frequencies[symbol], symbol};
105 std::array<FrequencyLeaf, kByteAlphabetSize> sort_temporary{};
106 sort_leaves_by_frequency(std::span(leaves).first(used_symbols.size()),
109 std::array<std::size_t, kMaximumHuffmanNodes> node_frequency{};
110 std::array<std::uint16_t, kMaximumHuffmanNodes> parent{};
111 const std::size_t leaf_count = used_symbols.size();
112 for (std::size_t index = 0; index < leaf_count; ++index) {
113 node_frequency[index] = leaves[index].frequency;
116 std::size_t next_leaf = 0;
117 std::size_t internal_head = leaf_count;
118 std::size_t next_internal = leaf_count;
119 const auto take_minimum = [&]() {
120 if (next_leaf < leaf_count &&
121 (internal_head == next_internal ||
122 node_frequency[next_leaf] <= node_frequency[internal_head])) {
125 return internal_head++;
127 for (std::size_t remaining = leaf_count; remaining > 1; --remaining) {
128 const std::size_t left = take_minimum();
129 const std::size_t right = take_minimum();
130 node_frequency[next_internal] =
131 node_frequency[left] + node_frequency[right];
132 parent[left] =
static_cast<std::uint16_t
>(next_internal);
133 parent[right] =
static_cast<std::uint16_t
>(next_internal);
137 const std::size_t root = next_internal - 1;
138 std::array<std::uint8_t, kMaximumHuffmanNodes> depth{};
139 for (std::size_t index = root; index-- > 0;) {
140 depth[index] =
static_cast<std::uint8_t
>(depth[parent[index]] + 1);
142 for (std::size_t index = 0; index < leaf_count; ++index) {
143 lengths[leaves[index].symbol] = depth[index];
150inline void limit_code_lengths(
151 std::array<std::uint8_t, kByteAlphabetSize>& lengths) {
152 std::array<std::size_t, kByteAlphabetSize> length_counts{};
153 std::size_t maximum = 0;
154 for (
const std::uint8_t length : lengths) {
156 ++length_counts[length];
157 maximum = std::max(maximum,
static_cast<std::size_t
>(length));
160 if (maximum <= kMaximumHuffmanCodeLength) {
164 for (std::size_t length = maximum; length > kMaximumHuffmanCodeLength;
166 length_counts[kMaximumHuffmanCodeLength] += length_counts[length];
167 length_counts[length] = 0;
170 std::size_t kraft = 0;
171 for (std::size_t length = 1; length <= kMaximumHuffmanCodeLength; ++length) {
172 kraft += length_counts[length] << (kMaximumHuffmanCodeLength - length);
174 constexpr std::size_t kKraftTarget = std::size_t{1}
175 << kMaximumHuffmanCodeLength;
176 while (kraft > kKraftTarget) {
177 std::size_t best = kMaximumHuffmanCodeLength - 1;
178 while (best > 0 && length_counts[best] == 0) {
184 --length_counts[best];
185 ++length_counts[best + 1];
186 kraft -= std::size_t{1} << (kMaximumHuffmanCodeLength - best - 1);
188 while (kraft < kKraftTarget &&
189 length_counts[kMaximumHuffmanCodeLength] != 0) {
190 bool shortened =
false;
191 for (std::size_t length = kMaximumHuffmanCodeLength - 1; length > 0;
193 const std::size_t delta =
194 (std::size_t{1} << (kMaximumHuffmanCodeLength - length)) - 1;
195 if (kraft + delta <= kKraftTarget) {
196 --length_counts[kMaximumHuffmanCodeLength];
197 ++length_counts[length];
208 struct LengthSymbol {
212 std::array<LengthSymbol, kByteAlphabetSize> ordered{};
213 std::size_t count = 0;
214 for (std::size_t symbol = 0; symbol < lengths.size(); ++symbol) {
215 if (lengths[symbol] != 0) {
217 std::min<std::uint8_t>(lengths[symbol], kMaximumHuffmanCodeLength),
218 static_cast<std::uint8_t
>(symbol)};
221 for (std::size_t index = 1; index < count; ++index) {
222 const LengthSymbol current = ordered[index];
223 std::size_t position = index;
224 while (position != 0 && ordered[position - 1].length > current.length) {
225 ordered[position] = ordered[position - 1];
228 ordered[position] = current;
230 std::size_t ordered_index = 0;
231 for (std::size_t length = 1; length <= kMaximumHuffmanCodeLength; ++length) {
232 for (std::size_t index = 0; index < length_counts[length]; ++index) {
233 lengths[ordered[ordered_index++].symbol] =
234 static_cast<std::uint8_t
>(length);
242 std::uint16_t root_code;
243 std::size_t symbol_begin;
247 std::int16_t node_id,
248 std::uint16_t rank) {
250 if (node.symbol >= 0) {
251 table.symbol_to_rank[node.symbol] =
static_cast<std::uint8_t
>(rank);
252 return static_cast<std::uint16_t
>(rank + 1);
254 rank = assign_inorder_ranks(table, node.left, rank);
255 table.split_rank[node_id] =
static_cast<std::uint8_t
>(rank - 1);
256 return assign_inorder_ranks(table, node.right, rank);
259inline void build_canonical_tree(
260 const std::array<std::uint8_t, kByteAlphabetSize>& lengths,
261 HuffmanBuildTable& table) {
262 std::array<std::uint16_t, kMaximumHuffmanCodeLength + 1> length_counts{};
263 std::size_t maximum_length = 0;
264 for (
const std::uint8_t length : lengths) {
266 ++length_counts[length];
268 std::max(maximum_length,
static_cast<std::size_t
>(length));
272 std::array<std::uint8_t, kByteAlphabetSize> ordered_symbols{};
273 std::array<std::size_t, kMaximumHuffmanCodeLength + 2> length_begin{};
274 std::array<std::size_t, kMaximumHuffmanCodeLength + 2> cursor{};
275 std::size_t accumulated = 0;
276 for (std::size_t length = 1; length <= maximum_length; ++length) {
277 length_begin[length] = accumulated;
278 cursor[length] = accumulated;
279 accumulated += length_counts[length];
281 length_begin[maximum_length + 1] = accumulated;
282 for (std::size_t symbol = 0; symbol < lengths.size(); ++symbol) {
283 const std::uint8_t length = lengths[symbol];
285 ordered_symbols[cursor[length]++] =
static_cast<std::uint8_t
>(symbol);
289 std::array<CanonicalChunk, kByteAlphabetSize> chunks{};
290 std::size_t chunk_count = 0;
291 for (std::size_t length = 1; length <= maximum_length; ++length) {
292 std::size_t symbol = length_begin[length];
293 for (std::size_t pair = 0; pair < length_counts[length] / 2; ++pair) {
294 chunks[chunk_count++] = {
true,
static_cast<std::uint16_t
>(length - 1), 0,
298 if ((length_counts[length] & 1U) != 0) {
299 chunks[chunk_count++] = {
false,
static_cast<std::uint16_t
>(length), 0,
303 for (std::size_t index = 1; index < chunk_count; ++index) {
305 std::size_t position = index;
306 while (position != 0 && chunks[position - 1].depth > current.depth) {
307 chunks[position] = chunks[position - 1];
310 chunks[position] = current;
313 std::uint32_t code = 0;
314 std::size_t previous_depth = 0;
315 for (std::size_t index = 0; index < chunk_count; ++index) {
317 code <<= chunk.depth - previous_depth;
318 chunk.root_code =
static_cast<std::uint16_t
>(code);
320 previous_depth = chunk.depth;
324 table.node_count = 1;
325 for (std::size_t index = 0; index < chunk_count; ++index) {
327 std::int16_t node_id = table.root;
328 for (std::size_t bit = chunk.depth; bit-- > 0;) {
329 const bool right = ((chunk.root_code >> bit) & 1U) != 0;
330 std::int16_t& child =
331 right ? table.tree[node_id].right : table.tree[node_id].left;
333 child =
static_cast<std::int16_t
>(table.node_count++);
337 if (chunk.sibling_pair) {
338 HuffmanBuildNode& node = table.tree[node_id];
339 node.left =
static_cast<std::int16_t
>(table.node_count++);
340 table.tree[node.left].symbol = ordered_symbols[chunk.symbol_begin];
341 node.right =
static_cast<std::int16_t
>(table.node_count++);
342 table.tree[node.right].symbol = ordered_symbols[chunk.symbol_begin + 1];
344 table.tree[node_id].symbol = ordered_symbols[chunk.symbol_begin];
348 assign_inorder_ranks(table, table.root, 0);
359 std::span<const std::size_t> frequencies) {
361 std::array<std::uint16_t, kByteAlphabetSize> used_symbols{};
362 for (std::size_t symbol = 0; symbol < frequencies.size(); ++symbol) {
363 if (frequencies[symbol] != 0) {
364 used_symbols[table.symbol_count++] =
static_cast<std::uint16_t
>(symbol);
367 if (table.symbol_count == 0) {
370 if (table.symbol_count == 1) {
372 table.node_count = 1;
373 table.tree[0].symbol = used_symbols[0];
374 table.symbol_to_rank[used_symbols[0]] = 0;
378 auto lengths = huffman_build_detail::build_code_lengths(
379 frequencies, std::span(used_symbols).first(table.symbol_count));
380 huffman_build_detail::limit_code_lengths(lengths);
381 huffman_build_detail::build_canonical_tree(lengths, table);
HuffmanBuildTable build_huffman_table(std::span< const std::size_t > frequencies)
Build PivCo's sibling-paired, non-flat byte Huffman tree.
Definition huffman_build_table.h:358
Definition huffman_build_table.h:27
Definition huffman_build_table.h:33
Definition huffman_build_table.h:239
Definition huffman_build_table.h:44