HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
huffman.hpp
1
2#include "TTauri/Foundation/required.hpp"
3#include "TTauri/Foundation/bits.hpp"
4#include "TTauri/Foundation/numeric_cast.hpp"
5#include <nonstd/span>
6#include <vector>
7
8namespace tt {
9
10template<typename T>
12 static_assert(std::is_integral_v<T> && std::is_signed_v<T>);
13
14 using state_t = T const *;
15
25 std::vector<T> tree;
26
27public:
28 huffman_tree() {
29 tree.push_back(0);
30 tree.push_back(0);
31 }
32
36 void add(int symbol, int code, int code_length) noexcept {
37 tt_assume(code_length >= 1);
38
39 int offset = 0;
40 while (--code_length > 0) {
41 int select = (code >> code_length) & 1;
42 offset += select;
43
44 int value = tree[offset];
45
46 // value may not be a leaf.
47 tt_assume(value <= 0);
48
49 if (value == 0) {
50 // Unused node entry. Point to the first of two new entries.
51 value = -(static_cast<int>(ssize(tree)) - offset);
52
53 tree[offset] = numeric_cast<T>(value);
54 tree.push_back(0);
55 tree.push_back(0);
56 }
57
58 // Go to the next entry.
59 offset -= value;
60 }
61
62 // place the symbol as a leaf.
63 int select = code & 1;
64 offset += select;
65
66 tt_assume(tree[offset] == 0);
67 tree[offset] = numeric_cast<T>(symbol + 1);
68 }
69
70 [[nodiscard]] state_t start() const noexcept {
71 return tree.data();
72 }
73
86 [[nodiscard]] int get(bool code_bit, state_t &state) const {
87 state += static_cast<ptrdiff_t>(code_bit);
88
89 if (*state == 0) {
90 TTAURI_THROW(parse_error("Code not in huffman tree."));
91 }
92
93 auto value = *state;
94 state -= static_cast<ptrdiff_t>(value);
95 return value - 1;
96 }
97
98 [[nodiscard]] int get_symbol(nonstd::span<std::byte const> bytes, ssize_t &bit_offset) const noexcept {
99 auto state = start();
100 while (true) {
101 int symbol;
102 if ((symbol = get(get_bit(bytes, bit_offset), state)) >= 0) {
103 return symbol;
104 }
105 }
106 }
107
110 [[nodiscard]] static huffman_tree from_lengths(int const *lengths, ssize_t nr_symbols) {
111 struct symbol_length_t {
112 int symbol;
113 int length;
114
115 symbol_length_t(int symbol, int length) : symbol(symbol), length(length) {}
116 };
117
118 std::vector<symbol_length_t> symbol_lengths;
119 symbol_lengths.reserve(nr_symbols);
120
121 for (int symbol = 0; symbol != nr_symbols; ++symbol) {
122 symbol_lengths.emplace_back(symbol, lengths[symbol]);
123 }
124
125 // Sort the table based on the length of the code, followed by symbol
126 std::sort(symbol_lengths.begin(), symbol_lengths.end(), [](ttlet &a, ttlet &b) {
127 if (a.length == b.length) {
128 return a.symbol < b.symbol;
129 } else {
130 return a.length < b.length;
131 }
132 });
133
134 auto r = huffman_tree{};
135
136 int code = 0;
137 int prev_length = 0;
138 for (auto &&entry: symbol_lengths) {
139 if (entry.length != 0) {
140 code <<= (entry.length - prev_length);
141
142 r.add(entry.symbol, code, entry.length);
143 ++code;
144 }
145
146 prev_length = entry.length;
147 }
148
149 return r;
150 }
151
152 [[nodiscard]] static huffman_tree from_lengths(std::vector<int> const &lengths) {
153 return from_lengths(lengths.data(), ssize(lengths));
154 }
155};
156
157
158
159}
Definition exceptions.hpp:154
Definition huffman.hpp:11
void add(int symbol, int code, int code_length) noexcept
Add a symbol to the huffman_tree.
Definition huffman.hpp:36
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:110
int get(bool code_bit, state_t &state) const
Get a symbol from the huffman-tree.
Definition huffman.hpp:86
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)