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 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
28 std::vector<T> tree;
29
30public:
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 int 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 = -(static_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 int 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_axiom(nr_symbols < std::numeric_limits<T>::min());
121
122 struct symbol_length_t {
123 T symbol;
124 uint8_t length;
125
126 symbol_length_t(T symbol, uint8_t length) : symbol(symbol), length(length) {}
127 };
128
129 std::vector<symbol_length_t> symbol_lengths;
130 symbol_lengths.reserve(nr_symbols);
131
132 for (T symbol = T{0}; symbol != static_cast<T>(nr_symbols); ++symbol) {
133 symbol_lengths.emplace_back(symbol, lengths[symbol]);
134 }
135
136 // Sort the table based on the length of the code, followed by symbol
137 std::sort(symbol_lengths.begin(), symbol_lengths.end(), [](hilet &a, hilet &b) {
138 if (a.length == b.length) {
139 return a.symbol < b.symbol;
140 } else {
141 return a.length < b.length;
142 }
143 });
144
145 auto r = huffman_tree{};
146
147 int code = 0;
148 int prev_length = 0;
149 for (auto &&entry : symbol_lengths) {
150 if (entry.length != 0) {
151 code <<= (entry.length - prev_length);
152
153 r.add(entry.symbol, code, entry.length);
154 ++code;
155 }
156
157 prev_length = entry.length;
158 }
159
160 return r;
161 }
162
163 [[nodiscard]] static huffman_tree from_lengths(std::vector<uint8_t> const &lengths)
164 {
165 return from_lengths(lengths.data(), lengths.size());
166 }
167};
168
169} // namespace hi::inline v1
This file includes required definitions.
#define hilet
Invariant should be the default for variables.
Definition required.hpp:23
Exception thrown during parsing on an error.
Definition exception.hpp:25
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
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 size(T... args)
T sort(T... args)