HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
huffman.hpp
1// Copyright Take Vos 2020.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#include "required.hpp"
6#include "bits.hpp"
7#include "cast.hpp"
8#include <span>
9#include <vector>
10
11namespace tt {
12
13template<typename T>
15 static_assert(std::is_integral_v<T> && std::is_signed_v<T>);
16
17 using state_t = T const *;
18
28 std::vector<T> tree;
29
30public:
31 huffman_tree() {
32 tree.push_back(0);
33 tree.push_back(0);
34 }
35
39 void add(int symbol, int code, int code_length) noexcept {
40 tt_axiom(code_length >= 1);
41
42 int offset = 0;
43 while (--code_length > 0) {
44 int select = (code >> code_length) & 1;
45 offset += select;
46
47 int value = tree[offset];
48
49 // value may not be a leaf.
50 tt_axiom(value <= 0);
51
52 if (value == 0) {
53 // Unused node entry. Point to the first of two new entries.
54 value = -(static_cast<int>(std::ssize(tree)) - offset);
55
56 tree[offset] = narrow_cast<T>(value);
57 tree.push_back(0);
58 tree.push_back(0);
59 }
60
61 // Go to the next entry.
62 offset -= value;
63 }
64
65 // place the symbol as a leaf.
66 int select = code & 1;
67 offset += select;
68
69 tt_axiom(tree[offset] == 0);
70 tree[offset] = narrow_cast<T>(symbol + 1);
71 }
72
73 [[nodiscard]] state_t start() const noexcept {
74 return tree.data();
75 }
76
89 [[nodiscard]] int get(bool code_bit, state_t &state) const {
90 state += static_cast<ptrdiff_t>(code_bit);
91
92 if (*state == 0) {
93 throw parse_error("Code not in huffman tree.");
94 }
95
96 auto value = *state;
97 state -= static_cast<ptrdiff_t>(value);
98 return value - 1;
99 }
100
101 [[nodiscard]] int get_symbol(std::span<std::byte const> bytes, ssize_t &bit_offset) const noexcept {
102 auto state = start();
103 while (true) {
104 int symbol;
105 if ((symbol = get(get_bit(bytes, bit_offset), state)) >= 0) {
106 return symbol;
107 }
108 }
109 }
110
113 [[nodiscard]] static huffman_tree from_lengths(int const *lengths, ssize_t nr_symbols) {
114 struct symbol_length_t {
115 int symbol;
116 int length;
117
118 symbol_length_t(int symbol, int length) : symbol(symbol), length(length) {}
119 };
120
121 std::vector<symbol_length_t> symbol_lengths;
122 symbol_lengths.reserve(nr_symbols);
123
124 for (int symbol = 0; symbol != nr_symbols; ++symbol) {
125 symbol_lengths.emplace_back(symbol, lengths[symbol]);
126 }
127
128 // Sort the table based on the length of the code, followed by symbol
129 std::sort(symbol_lengths.begin(), symbol_lengths.end(), [](ttlet &a, ttlet &b) {
130 if (a.length == b.length) {
131 return a.symbol < b.symbol;
132 } else {
133 return a.length < b.length;
134 }
135 });
136
137 auto r = huffman_tree{};
138
139 int code = 0;
140 int prev_length = 0;
141 for (auto &&entry: symbol_lengths) {
142 if (entry.length != 0) {
143 code <<= (entry.length - prev_length);
144
145 r.add(entry.symbol, code, entry.length);
146 ++code;
147 }
148
149 prev_length = entry.length;
150 }
151
152 return r;
153 }
154
155 [[nodiscard]] static huffman_tree from_lengths(std::vector<int> const &lengths) {
156 return from_lengths(lengths.data(), std::ssize(lengths));
157 }
158};
159
160
161
162}
Exception thrown during parsing on an error.
Definition exception.hpp:26
Definition huffman.hpp:14
void add(int symbol, int code, int code_length) noexcept
Add a symbol to the huffman_tree.
Definition huffman.hpp:39
static huffman_tree from_lengths(int const *lengths, ssize_t nr_symbols)
Build a canonical-huffman table from a set of lengths.
Definition huffman.hpp:113
int get(bool code_bit, state_t &state) const
Get a symbol from the huffman-tree.
Definition huffman.hpp:89
T begin(T... args)
T data(T... args)
T emplace_back(T... args)
T end(T... args)
T push_back(T... args)
T reserve(T... args)
T sort(T... args)