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#pragma once
6
7#include "../utility/utility.hpp"
8#include "../macros.hpp"
9#include <span>
10#include <vector>
11#include <algorithm>
12
13hi_export_module(hikogui.codec.huffman);
14
15hi_export namespace hi { inline namespace v1 {
16
17hi_export template<typename T>
19 static_assert(std::is_integral_v<T> && std::is_signed_v<T>);
20
21 using state_t = T const *;
22
32 std::vector<T> tree;
33
34public:
35 huffman_tree() noexcept
36 {
37 tree.push_back(0);
38 tree.push_back(0);
39 }
40
44 void add(int symbol, int code, int code_length) noexcept
45 {
46 hi_axiom(code_length >= 1);
47
48 int offset = 0;
49 while (--code_length > 0) {
50 auto const select = (code >> code_length) & 1;
51 offset += select;
52
53 int value = tree[offset];
54
55 // value may not be a leaf.
56 hi_axiom(value <= 0);
57
58 if (value == 0) {
59 // Unused node entry. Point to the first of two new entries.
60 value = -(narrow_cast<int>(ssize(tree)) - offset);
61
62 tree[offset] = narrow_cast<T>(value);
63 tree.push_back(0);
64 tree.push_back(0);
65 }
66
67 // Go to the next entry.
68 offset -= value;
69 }
70
71 // place the symbol as a leaf.
72 auto const select = code & 1;
73 offset += select;
74
75 hi_axiom(tree[offset] == 0);
76 tree[offset] = narrow_cast<T>(symbol + 1);
77 }
78
79 [[nodiscard]] state_t start() const noexcept
80 {
81 return tree.data();
82 }
83
96 [[nodiscard]] int get(bool code_bit, state_t& state) const
97 {
98 state += static_cast<ptrdiff_t>(code_bit);
99
100 if (*state == 0) {
101 throw parse_error("Code not in huffman tree.");
102 }
103
104 auto value = *state;
105 state -= static_cast<ptrdiff_t>(value);
106 return value - 1;
107 }
108
109 [[nodiscard]] std::size_t get_symbol(std::span<std::byte const> bytes, std::size_t& bit_offset) const noexcept
110 {
111 auto state = start();
112 while (true) {
113 int symbol;
114 if ((symbol = get(get_bit(bytes, bit_offset), state)) >= 0) {
115 return static_cast<std::size_t>(symbol);
116 }
117 }
118 }
119
123 {
124 hi_assert_not_null(lengths);
126
127 struct symbol_length_t {
128 T symbol;
129 uint8_t length;
130
131 symbol_length_t(T symbol, uint8_t length) : symbol(symbol), length(length) {}
132 };
133
136
137 for (T symbol = T{0}; symbol != narrow_cast<T>(nr_symbols); ++symbol) {
138 symbol_lengths.emplace_back(symbol, lengths[symbol]);
139 }
140
141 // Sort the table based on the length of the code, followed by symbol
142 std::sort(symbol_lengths.begin(), symbol_lengths.end(), [](auto const& a, auto const& b) {
143 if (a.length == b.length) {
144 return a.symbol < b.symbol;
145 } else {
146 return a.length < b.length;
147 }
148 });
149
150 auto r = huffman_tree{};
151
152 int code = 0;
153 int prev_length = 0;
154 for (auto&& entry : symbol_lengths) {
155 if (entry.length != 0) {
156 code <<= (entry.length - prev_length);
157
158 r.add(entry.symbol, code, entry.length);
159 ++code;
160 }
161
162 prev_length = entry.length;
163 }
164
165 return r;
166 }
167
168 [[nodiscard]] static huffman_tree from_lengths(std::vector<uint8_t> const& lengths)
169 {
170 return from_lengths(lengths.data(), lengths.size());
171 }
172};
173
174}} // namespace hi::v1
@ select
The widget is selectable.
The HikoGUI namespace.
Definition array_generic.hpp:20
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:27
DOXYGEN BUG.
Definition algorithm_misc.hpp:20
Definition huffman.hpp:18
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:122
void add(int symbol, int code, int code_length) noexcept
Add a symbol to the huffman_tree.
Definition huffman.hpp:44
int get(bool code_bit, state_t &state) const
Get a symbol from the huffman-tree.
Definition huffman.hpp:96
Exception thrown during parsing on an error.
Definition exception_intf.hpp:48
T data(T... args)
T push_back(T... args)
T reserve(T... args)
T size(T... args)
T sort(T... args)