HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
huffman.hpp
1// Copyright Take Vos 2020-2022.
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 "utility.hpp"
6#include "bits.hpp"
7#include "cast.hpp"
8#include <span>
9#include <vector>
10
11namespace hi::inline v1 {
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
29
30public:
31 huffman_tree() noexcept
32 {
33 tree.push_back(0);
34 tree.push_back(0);
35 }
36
40 void add(int symbol, int code, int code_length) noexcept
41 {
42 hi_axiom(code_length >= 1);
43
44 int offset = 0;
45 while (--code_length > 0) {
46 hilet select = (code >> code_length) & 1;
47 offset += select;
48
49 int value = tree[offset];
50
51 // value may not be a leaf.
52 hi_axiom(value <= 0);
53
54 if (value == 0) {
55 // Unused node entry. Point to the first of two new entries.
56 value = -(narrow_cast<int>(ssize(tree)) - offset);
57
58 tree[offset] = narrow_cast<T>(value);
59 tree.push_back(0);
60 tree.push_back(0);
61 }
62
63 // Go to the next entry.
64 offset -= value;
65 }
66
67 // place the symbol as a leaf.
68 hilet select = code & 1;
69 offset += select;
70
71 hi_axiom(tree[offset] == 0);
72 tree[offset] = narrow_cast<T>(symbol + 1);
73 }
74
75 [[nodiscard]] state_t start() const noexcept
76 {
77 return tree.data();
78 }
79
92 [[nodiscard]] int get(bool code_bit, state_t &state) const
93 {
94 state += static_cast<ptrdiff_t>(code_bit);
95
96 if (*state == 0) {
97 throw parse_error("Code not in huffman tree.");
98 }
99
100 auto value = *state;
101 state -= static_cast<ptrdiff_t>(value);
102 return value - 1;
103 }
104
105 [[nodiscard]] std::size_t get_symbol(std::span<std::byte const> bytes, std::size_t &bit_offset) const noexcept
106 {
107 auto state = start();
108 while (true) {
109 int symbol;
110 if ((symbol = get(get_bit(bytes, bit_offset), state)) >= 0) {
111 return static_cast<std::size_t>(symbol);
112 }
113 }
114 }
115
118 [[nodiscard]] static huffman_tree from_lengths(uint8_t const *lengths, std::size_t nr_symbols)
119 {
120 hi_assert_not_null(lengths);
122
123 struct symbol_length_t {
124 T symbol;
125 uint8_t length;
126
127 symbol_length_t(T symbol, uint8_t length) : symbol(symbol), length(length) {}
128 };
129
130 std::vector<symbol_length_t> symbol_lengths;
131 symbol_lengths.reserve(nr_symbols);
132
133 for (T symbol = T{0}; symbol != narrow_cast<T>(nr_symbols); ++symbol) {
134 symbol_lengths.emplace_back(symbol, lengths[symbol]);
135 }
136
137 // Sort the table based on the length of the code, followed by symbol
138 std::sort(symbol_lengths.begin(), symbol_lengths.end(), [](hilet &a, hilet &b) {
139 if (a.length == b.length) {
140 return a.symbol < b.symbol;
141 } else {
142 return a.length < b.length;
143 }
144 });
145
146 auto r = huffman_tree{};
147
148 int code = 0;
149 int prev_length = 0;
150 for (auto &&entry : symbol_lengths) {
151 if (entry.length != 0) {
152 code <<= (entry.length - prev_length);
153
154 r.add(entry.symbol, code, entry.length);
155 ++code;
156 }
157
158 prev_length = entry.length;
159 }
160
161 return r;
162 }
163
164 [[nodiscard]] static huffman_tree from_lengths(std::vector<uint8_t> const &lengths)
165 {
166 return from_lengths(lengths.data(), lengths.size());
167 }
168};
169
170} // namespace hi::inline v1
#define hi_axiom(expression,...)
Specify an axiom; an expression that is true.
Definition assert.hpp:133
#define hi_assert_not_null(x,...)
Assert if an expression is not nullptr.
Definition assert.hpp:118
Utilities used by the HikoGUI library itself.
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
DOXYGEN BUG.
Definition algorithm.hpp:15
bool get_bit(std::span< std::byte const > buffer, std::size_t &index) noexcept
Read a single bit from span of bytes Bits are ordered LSB first.
Definition bits.hpp:20
Definition huffman.hpp:14
int get(bool code_bit, state_t &state) const
Get a symbol from the huffman-tree.
Definition huffman.hpp:92
void add(int symbol, int code, int code_length) noexcept
Add a symbol to the huffman_tree.
Definition huffman.hpp:40
static huffman_tree from_lengths(uint8_t const *lengths, std::size_t nr_symbols)
Build a canonical-huffman table from a set of lengths.
Definition huffman.hpp:118
A tree container.
Definition tree.hpp:20
T begin(T... args)
T data(T... args)
T emplace_back(T... args)
T end(T... args)
T reserve(T... args)
T size(T... args)
T sort(T... args)