banan-os/userspace/libraries/LibDEFLATE/HuffmanTree.cpp

142 lines
3.5 KiB
C++

#include <LibDEFLATE/HuffmanTree.h>
namespace LibDEFLATE
{
HuffmanTree& HuffmanTree::operator=(HuffmanTree&& other)
{
m_instant_bits = other.m_instant_bits;
m_min_bits = other.m_min_bits;
m_max_bits = other.m_max_bits;
m_instant = BAN::move(other.m_instant);
m_min_code = BAN::move(other.m_min_code);
m_slow_table = BAN::move(other.m_slow_table);
return *this;
}
BAN::ErrorOr<HuffmanTree> HuffmanTree::create(BAN::Span<const uint8_t> bit_lengths)
{
HuffmanTree result;
TRY(result.initialize(bit_lengths));
return result;
}
BAN::ErrorOr<void> HuffmanTree::initialize(BAN::Span<const uint8_t> bit_lengths)
{
m_max_bits = 0;
m_min_bits = MAX_BITS;
uint16_t max_sym = 0;
uint16_t bl_count[MAX_BITS + 1] {};
for (size_t sym = 0; sym < bit_lengths.size(); sym++)
{
if (bit_lengths[sym] == 0)
continue;
m_max_bits = BAN::Math::max(bit_lengths[sym], m_max_bits);
m_min_bits = BAN::Math::min(bit_lengths[sym], m_min_bits);
bl_count[bit_lengths[sym]]++;
max_sym = sym;
}
uint16_t next_code[MAX_BITS + 1] {};
uint16_t code = 0;
for (uint8_t bits = 1; bits <= MAX_BITS; bits++)
{
code = (code + bl_count[bits - 1]) << 1;
next_code[bits] = code;
m_min_code[bits] = code;
}
BAN::Vector<Leaf> tree;
TRY(tree.resize(max_sym + 1, { .code = 0, .len = 0 }));
for (uint16_t sym = 0; sym <= max_sym; sym++)
{
tree[sym].len = bit_lengths[sym];
if (const uint8_t len = tree[sym].len)
tree[sym].code = next_code[len]++;
}
TRY(build_instant_table(tree.span()));
TRY(build_slow_table(tree.span()));
return {};
}
BAN::ErrorOr<void> HuffmanTree::build_instant_table(BAN::Span<const Leaf> tree)
{
m_instant_bits = BAN::Math::min<uint8_t>(9, m_max_bits);
TRY(m_instant.resize(1 << m_instant_bits, {}));
for (uint16_t sym = 0; sym < tree.size(); sym++)
{
if (tree[sym].len == 0 || tree[sym].len > m_instant_bits)
continue;
const uint16_t code = tree[sym].code;
const uint16_t shift = m_instant_bits - tree[sym].len;
for (uint16_t j = code << shift; j < (code + 1) << shift; j++)
m_instant[j] = { sym, tree[sym].len };
}
return {};
}
BAN::ErrorOr<void> HuffmanTree::build_slow_table(BAN::Span<const Leaf> tree)
{
TRY(m_slow_table.resize(MAX_BITS + 1));
for (uint16_t sym = 0; sym < tree.size(); sym++)
{
const auto leaf = tree[sym];
if (leaf.len == 0)
continue;
const size_t offset = leaf.code - m_min_code[leaf.len];
if (offset >= m_slow_table[leaf.len].size())
TRY(m_slow_table[leaf.len].resize(offset + 1));
m_slow_table[leaf.len][offset] = sym;
}
return {};
}
BAN::ErrorOr<HuffmanTree> HuffmanTree::fixed_tree()
{
struct BitLengths
{
consteval BitLengths()
{
size_t i = 0;
for (; i <= 143; i++) values[i] = 8;
for (; i <= 255; i++) values[i] = 9;
for (; i <= 279; i++) values[i] = 7;
for (; i <= 287; i++) values[i] = 8;
}
BAN::Array<uint8_t, 288> values;
};
static constexpr BitLengths bit_lengths;
return TRY(HuffmanTree::create(bit_lengths.values.span()));
}
BAN::Optional<HuffmanTree::Instant> HuffmanTree::get_symbol_instant(uint16_t code) const
{
ASSERT(code < m_instant.size());
if (const auto entry = m_instant[code]; entry.len)
return entry;
return {};
}
BAN::Optional<uint16_t> HuffmanTree::get_symbol(uint16_t code, uint8_t len) const
{
ASSERT(len <= m_max_bits);
const auto& symbols = m_slow_table[len];
const size_t offset = code - m_min_code[len];
if (symbols.size() <= offset)
return {};
return symbols[offset];
}
}