blob: 41e39126bd2c58b2d53ce69b436b4e0ea7ee7a74 [file]
/*
* Copyright 2026 Rive
*/
#include "rive/text/line_break_dictionary.hpp"
#if defined(RIVE_LINE_BREAK_DICTIONARIES) && \
defined(WITH_RIVE_TEXT_DICT_COMPRESSED)
#include <cstring>
#include <unordered_set>
using namespace rive;
namespace
{
// A raw deflate (RFC 1951) decoder. Small rather than fast, it runs once per
// dictionary.
class Inflater
{
public:
Inflater(Span<const uint8_t> in, uint8_t* out, size_t outSize)
: m_in(in)
, m_out(out)
, m_outSize(outSize)
{}
// Whether the stream decoded to exactly outSize bytes.
bool run()
{
bool last = false;
while (!last && !m_failed)
{
last = bits(1) != 0;
bool ok = false;
switch (bits(2))
{
case 0:
ok = stored();
break;
case 1:
ok = fixed();
break;
case 2:
ok = dynamic();
break;
}
if (!ok)
{
return false;
}
}
return !m_failed && m_outPos == m_outSize;
}
private:
static constexpr int kMaxBits = 15;
// Canonical code: how many codes have each length, and the symbols
// ordered by code.
struct Huffman
{
uint16_t count[kMaxBits + 1];
uint16_t symbol[288];
};
// Reads n bits, least significant first. Past the end of the input it
// returns zeros and flags the failure.
uint32_t bits(int n)
{
while (m_bitCount < n)
{
if (m_inPos == m_in.size())
{
m_failed = true;
return 0;
}
m_bitBuffer |= (uint32_t)m_in[m_inPos++] << m_bitCount;
m_bitCount += 8;
}
uint32_t value = m_bitBuffer & ((1u << n) - 1);
m_bitBuffer >>= n;
m_bitCount -= n;
return value;
}
bool stored()
{
// Drop the rest of the current byte, then LEN and its complement.
m_bitBuffer = 0;
m_bitCount = 0;
if (m_in.size() - m_inPos < 4)
{
return false;
}
uint32_t length = m_in[m_inPos] | (m_in[m_inPos + 1] << 8);
uint32_t complement = m_in[m_inPos + 2] | (m_in[m_inPos + 3] << 8);
m_inPos += 4;
if (length != (~complement & 0xFFFF) || m_in.size() - m_inPos < length ||
m_outSize - m_outPos < length)
{
return false;
}
memcpy(m_out + m_outPos, m_in.data() + m_inPos, length);
m_inPos += length;
m_outPos += length;
return true;
}
// Fails on an over-subscribed set of lengths. Incomplete codes are fine,
// deflate uses them for single distance codes.
static bool construct(Huffman& huffman, const uint8_t* lengths, int count)
{
memset(huffman.count, 0, sizeof(huffman.count));
for (int i = 0; i < count; i++)
{
huffman.count[lengths[i]]++;
}
int left = 1;
for (int length = 1; length <= kMaxBits; length++)
{
left = (left << 1) - huffman.count[length];
if (left < 0)
{
return false;
}
}
uint16_t offsets[kMaxBits + 1];
offsets[1] = 0;
for (int length = 1; length < kMaxBits; length++)
{
offsets[length + 1] = offsets[length] + huffman.count[length];
}
for (int i = 0; i < count; i++)
{
if (lengths[i] != 0)
{
huffman.symbol[offsets[lengths[i]]++] = (uint16_t)i;
}
}
return true;
}
// Decodes one symbol a bit at a time, or returns -1.
int decode(const Huffman& huffman)
{
int code = 0;
int first = 0;
int index = 0;
for (int length = 1; length <= kMaxBits; length++)
{
code |= (int)bits(1);
int count = huffman.count[length];
if (code - count < first)
{
return huffman.symbol[index + (code - first)];
}
index += count;
first = (first + count) << 1;
code <<= 1;
}
return -1;
}
bool codes(const Huffman& lengthCode, const Huffman& distanceCode)
{
static const uint16_t kLengthBase[29] = {
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27,
31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258};
static const uint8_t kLengthExtra[29] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
1, 1, 2, 2, 2, 2, 3, 3, 3, 3,
4, 4, 4, 4, 5, 5, 5, 5, 0};
static const uint16_t kDistanceBase[30] = {
1, 2, 3, 4, 5, 7, 9, 13, 17, 25,
33, 49, 65, 97, 129, 193, 257, 385, 513, 769,
1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577};
static const uint8_t kDistanceExtra[30] = {
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6,
6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13};
while (!m_failed)
{
int symbol = decode(lengthCode);
if (symbol < 0)
{
return false;
}
if (symbol < 256)
{
if (m_outPos == m_outSize)
{
return false;
}
m_out[m_outPos++] = (uint8_t)symbol;
continue;
}
if (symbol == 256)
{
return !m_failed;
}
symbol -= 257;
if (symbol >= 29)
{
return false;
}
size_t length = kLengthBase[symbol] + bits(kLengthExtra[symbol]);
int distanceSymbol = decode(distanceCode);
if (distanceSymbol < 0 || distanceSymbol >= 30)
{
return false;
}
size_t distance = kDistanceBase[distanceSymbol] +
bits(kDistanceExtra[distanceSymbol]);
if (distance > m_outPos || length > m_outSize - m_outPos)
{
return false;
}
// Byte by byte, the copy may overlap what it writes.
for (size_t i = 0; i < length; i++, m_outPos++)
{
m_out[m_outPos] = m_out[m_outPos - distance];
}
}
return false;
}
bool fixed()
{
uint8_t lengths[288 + 30];
for (int i = 0; i < 288; i++)
{
lengths[i] = i < 144 ? 8 : i < 256 ? 9 : i < 280 ? 7 : 8;
}
for (int i = 0; i < 30; i++)
{
lengths[288 + i] = 5;
}
Huffman lengthCode;
Huffman distanceCode;
construct(lengthCode, lengths, 288);
construct(distanceCode, lengths + 288, 30);
return codes(lengthCode, distanceCode);
}
bool dynamic()
{
static const uint8_t kOrder[19] =
{16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
int lengthCount = (int)bits(5) + 257;
int distanceCount = (int)bits(5) + 1;
int codeCount = (int)bits(4) + 4;
if (lengthCount > 286 || distanceCount > 30)
{
return false;
}
uint8_t codeLengths[19] = {};
for (int i = 0; i < codeCount; i++)
{
codeLengths[kOrder[i]] = (uint8_t)bits(3);
}
Huffman codeCode;
if (!construct(codeCode, codeLengths, 19))
{
return false;
}
uint8_t lengths[286 + 30] = {};
int total = lengthCount + distanceCount;
for (int index = 0; index < total;)
{
int symbol = decode(codeCode);
if (symbol < 0 || m_failed)
{
return false;
}
if (symbol < 16)
{
lengths[index++] = (uint8_t)symbol;
continue;
}
uint8_t repeat = 0;
int times;
if (symbol == 16)
{
if (index == 0)
{
return false;
}
repeat = lengths[index - 1];
times = 3 + (int)bits(2);
}
else if (symbol == 17)
{
times = 3 + (int)bits(3);
}
else
{
times = 11 + (int)bits(7);
}
if (index + times > total)
{
return false;
}
while (times-- > 0)
{
lengths[index++] = repeat;
}
}
// The end of block code has to exist.
if (lengths[256] == 0)
{
return false;
}
Huffman lengthCode;
Huffman distanceCode;
if (!construct(lengthCode, lengths, lengthCount) ||
!construct(distanceCode, lengths + lengthCount, distanceCount))
{
return false;
}
return codes(lengthCode, distanceCode);
}
Span<const uint8_t> m_in;
size_t m_inPos = 0;
uint32_t m_bitBuffer = 0;
int m_bitCount = 0;
uint8_t* m_out;
size_t m_outSize;
size_t m_outPos = 0;
bool m_failed = false;
};
// Builds the minimized trie of words added in sorted order, following Daciuk
// et al., "Incremental construction of minimal acyclic finite-state
// automata". A word's end is marked on the edge that completes it, so a
// state is nothing but its outgoing edges.
class TrieBuilder
{
public:
TrieBuilder() : m_registry(1024, Hash{&m_states}, Equal{&m_states})
{
m_states.emplace_back(); // the root
m_path.push_back(0);
}
// Adds word[0, length), which shares its first shared symbols with the
// previous word.
void add(const uint8_t* word, size_t length, size_t shared)
{
minimize(shared);
m_path.resize(shared + 1);
for (size_t i = shared; i < length; i++)
{
uint32_t state = newState();
m_states[m_path.back()].push_back({word[i], i + 1 == length, state});
m_path.push_back(state);
}
}
// Lays the trie out breadth first from the root, as the generator does,
// so the edges come out identical to the ones it would compile in.
std::vector<uint32_t> finish()
{
minimize(0);
std::vector<uint32_t> firstEdge(m_states.size(), 0);
std::vector<bool> seen(m_states.size(), false);
std::vector<uint32_t> order = {0};
seen[0] = true;
uint32_t edgeCount = 0;
for (size_t i = 0; i < order.size(); i++)
{
const std::vector<Edge>& edges = m_states[order[i]];
if (edges.empty())
{
continue;
}
firstEdge[order[i]] = edgeCount;
edgeCount += (uint32_t)edges.size();
for (const Edge& edge : edges)
{
if (!seen[edge.target])
{
seen[edge.target] = true;
order.push_back(edge.target);
}
}
}
std::vector<uint32_t> result;
result.reserve(edgeCount);
for (uint32_t state : order)
{
const std::vector<Edge>& edges = m_states[state];
for (size_t i = 0; i < edges.size(); i++)
{
const Edge& edge = edges[i];
result.push_back(
edge.symbol |
(i + 1 == edges.size() ? LineBreakDictionary::lastEdge : 0) |
(edge.wordEnd ? LineBreakDictionary::wordEnd : 0) |
(firstEdge[edge.target] << LineBreakDictionary::targetShift));
}
}
return result;
}
private:
struct Edge
{
uint8_t symbol;
bool wordEnd;
uint32_t target;
bool operator==(const Edge& other) const
{
return symbol == other.symbol && wordEnd == other.wordEnd &&
target == other.target;
}
};
using States = std::vector<std::vector<Edge>>;
struct Hash
{
const States* states;
size_t operator()(uint32_t state) const
{
uint64_t hash = 14695981039346656037ull;
for (const Edge& edge : (*states)[state])
{
uint64_t value = edge.symbol | (edge.wordEnd ? 1u << 8 : 0) |
((uint64_t)edge.target << 9);
hash = (hash ^ value) * 1099511628211ull;
}
return (size_t)(hash ^ (hash >> 32));
}
};
struct Equal
{
const States* states;
bool operator()(uint32_t a, uint32_t b) const
{
return (*states)[a] == (*states)[b];
}
};
uint32_t newState()
{
if (!m_free.empty())
{
uint32_t state = m_free.back();
m_free.pop_back();
m_states[state].clear();
return state;
}
m_states.emplace_back();
return (uint32_t)(m_states.size() - 1);
}
// Replaces every state on the previous word's path deeper than depth with
// an equivalent registered one, or registers it, deepest first so its
// children are already final.
void minimize(size_t depth)
{
for (size_t d = m_path.size() - 1; d > depth; d--)
{
uint32_t state = m_path[d];
auto found = m_registry.find(state);
if (found != m_registry.end())
{
m_states[m_path[d - 1]].back().target = *found;
m_free.push_back(state);
}
else
{
m_registry.insert(state);
}
}
}
States m_states;
std::vector<uint32_t> m_free;
// States along the last word added, the root first.
std::vector<uint32_t> m_path;
std::unordered_set<uint32_t, Hash, Equal> m_registry;
};
} // namespace
std::vector<uint32_t> rive::buildLineBreakDictionaryEdges(
const LineBreakDictionary& dictionary)
{
std::vector<uint8_t> coded(dictionary.wordsSize);
if (!Inflater(dictionary.words, coded.data(), coded.size()).run())
{
return {};
}
// Each word is the length it shares with the previous one, its remaining
// symbols plus one, and a 0.
TrieBuilder builder;
uint8_t word[256];
size_t length = 0;
for (size_t i = 0; i < coded.size();)
{
size_t shared = coded[i++];
if (shared > length)
{
return {};
}
length = shared;
while (i < coded.size() && coded[i] != 0)
{
if (length == sizeof(word) || coded[i] > dictionary.alphabet.size())
{
return {};
}
word[length++] = coded[i++] - 1;
}
if (i == coded.size() || length == shared)
{
return {};
}
i++;
builder.add(word, length, shared);
}
std::vector<uint32_t> edges = builder.finish();
// Anything but the generator's trie would segment wrongly, so a mismatch
// leaves the script unsegmented instead.
uint32_t checksum = 2166136261u;
for (uint32_t edge : edges)
{
checksum = (checksum ^ edge) * 16777619u;
}
if (edges.size() != dictionary.edgeCount ||
checksum != dictionary.edgeChecksum)
{
return {};
}
return edges;
}
#endif