HashMgr::HashMgr(constchar* tpath, constchar* apath, constchar* key)
: flag_mode(FLAG_CHAR)
, complexprefixes(0)
, utf8(0)
, forbiddenword(FORBIDDENWORD) // forbidden word signing flag
, langnum(0)
, csconv(nullptr) {
load_config(apath, key); if (!csconv)
csconv = get_current_cs(SPELL_ENCODING); int ec = load_tables(tpath, key); if (ec) { /* error condition - what should we do here */
fprintf(stderr, "Hash Manager Error : %d\n", ec);
free_table(); //keep table size to 1 to fix possible division with zero
tableptr.resize(1, nullptr);
}
}
void HashMgr::free_table() { // now pass through hash table freeing up everything // go through column by column of the table for (auto ptr : tableptr) {
hentry* nt = nullptr; while (ptr) {
nt = ptr->next;
release_flags(ptr->astr, ptr->var & H_OPT_OWNFLAGS);
arena_free(ptr);
ptr = nt;
}
}
tableptr.clear();
}
HashMgr::~HashMgr() {
free_table();
static_assert(std::is_trivially_destructible<unsignedshort>::value, "arena_free replaces delete[]; aliasf elements must have trivial destructors"); for (auto& j : aliasf)
arena_free(j);
aliasf.clear();
static_assert(std::is_trivially_destructible<char>::value, "arena_free replaces delete[]; aliasm elements must have trivial destructors"); for (auto& j : aliasm)
arena_free(j);
aliasm.clear();
#ifdef MOZILLA_CLIENT delete[] csconv; #endif
}
// lookup a root word in the hashtable
struct hentry* HashMgr::lookup(constchar* word, size_t len) const { struct hentry* dp = tableptr[hash(word, len)]; if (!dp) return nullptr; for (; dp != nullptr; dp = dp->next) { if (strcmp(word, dp->word) == 0) return dp;
} return nullptr;
}
// add a word to the hash table (private) int HashMgr::add_word(const std::string& in_word, int wcl, unsignedshort* aff, int al, const std::string* in_desc, bool onlyupcase, int captype, bool own_aff) {
if (al > std::numeric_limits<short>::max()) {
HUNSPELL_WARNING(stderr, "error: affix len %d is over max limit\n", al);
release_flags(aff, own_aff); return1;
}
const std::string* word = &in_word; const std::string* desc = in_desc;
std::string* word_copy = nullptr;
std::string* desc_copy = nullptr; if ((!ignorechars.empty() && !has_no_ignored_chars(in_word, ignorechars)) || complexprefixes) {
word_copy = new std::string(in_word);
if (!ignorechars.empty()) { if (utf8) {
wcl = remove_ignored_chars_utf(*word_copy, ignorechars_utf16);
} else {
remove_ignored_chars(*word_copy, ignorechars);
}
}
if (complexprefixes) { if (utf8)
wcl = reverseword_utf(*word_copy); else
reverseword(*word_copy);
if (in_desc && aliasm.empty()) {
desc_copy = new std::string(*in_desc);
if (complexprefixes) { if (utf8)
reverseword_utf(*desc_copy); else
reverseword(*desc_copy);
}
desc = desc_copy;
}
}
word = word_copy;
}
// limit of hp->blen if (word->size() > std::numeric_limits<unsignedshort>::max()) {
HUNSPELL_WARNING(stderr, "error: word len %ld is over max limit\n", word->size()); delete desc_copy; delete word_copy;
release_flags(aff, own_aff); return1;
}
bool upcasehomonym = false; int descl = desc ? (!aliasm.empty() ? sizeof(char*) : desc->size() + 1) : 0; // variable-length hash record with word and optional fields auto hp =
(struct hentry*)arena_alloc(sizeof(struct hentry) + word->size() + descl,
alignof(struct hentry)); if (!hp) { delete desc_copy; delete word_copy;
release_flags(aff, own_aff); return1;
}
// store the description string or its pointer if (desc) {
hp->var |= H_OPT; if (!aliasm.empty()) {
hp->var |= H_OPT_ALIASM;
store_pointer(hpw + word->size() + 1, get_aliasm(atoi(desc->c_str())));
} else {
strcpy(hpw + word->size() + 1, desc->c_str());
} if (HENTRY_FIND(hp, MORPH_PHON)) {
hp->var |= H_OPT_PHON; // store ph: fields (pronounciation, misspellings, old orthography etc.) // of a morphological description in reptable to use in REP replacements.
size_t predicted = tableptr.size() / MORPH_PHON_RATIO; if (reptable.capacity() < predicted)
reptable.reserve(predicted);
std::string fields = HENTRY_DATA(hp);
std::string::const_iterator iter = fields.begin(), start_piece = mystrsep(fields, iter); while (start_piece != fields.end()) { if (std::string(start_piece, iter).find(MORPH_PHON) == 0) {
std::string ph = std::string(start_piece, iter).substr(sizeof MORPH_PHON - 1); if (!ph.empty()) {
std::vector<w_char> w;
size_t strippatt;
std::string wordpart; // dictionary based REP replacement, separated by "->" // for example "pretty ph:prity ph:priti->pretti" to handle // both prity -> pretty and pritier -> prettiest suggestions. if (((strippatt = ph.find("->")) != std::string::npos) &&
(strippatt > 0) && (strippatt < ph.size() - 2)) {
wordpart = ph.substr(strippatt + 2);
ph.erase(ph.begin() + strippatt, ph.end());
} else
wordpart = in_word; // when the ph: field ends with the character *, // strip last character of the pattern and the replacement // to match in REP suggestions also at character changes, // for example, "pretty ph:prity*" results "prit->prett" // REP replacement instead of "prity->pretty", to get // prity->pretty and pritiest->prettiest suggestions. if (ph.at(ph.size()-1) == '*') {
strippatt = 1;
size_t stripword = 0; if (utf8) { while ((strippatt < ph.size()) &&
((ph.at(ph.size()-strippatt-1) & 0xc0) == 0x80))
++strippatt; while ((stripword < wordpart.size()) &&
((wordpart.at(wordpart.size()-stripword-1) & 0xc0) == 0x80))
++stripword;
}
++strippatt;
++stripword; if ((ph.size() > strippatt) && (wordpart.size() > stripword)) {
ph.erase(ph.size()-strippatt, strippatt);
wordpart.erase(wordpart.size()-stripword, stripword);
}
} // capitalize lowercase pattern for capitalized words to support // good suggestions also for capitalized misspellings, eg. // Wednesday ph:wendsay // results wendsay -> Wednesday and Wendsay -> Wednesday, too. if (captype == INITCAP) {
std::string ph_capitalized; if (utf8) {
u8_u16(w, ph); if (get_captype_utf8(w, langnum) == NOCAP) {
mkinitcap_utf(w, langnum);
u16_u8(ph_capitalized, w);
}
} elseif (get_captype(ph, csconv) == NOCAP)
mkinitcap(ph_capitalized, csconv);
if (!ph_capitalized.empty()) { // add also lowercase word in the case of German or // Hungarian to support lowercase suggestions lowercased by // compound word generation or derivational suffixes // (for example by adjectival suffix "-i" of geographical // names in Hungarian: // Massachusetts ph:messzecsuzec // messzecsuzeci -> massachusettsi (adjective) // For lowercasing by conditional PFX rules, see // tests/germancompounding test example or the // Hungarian dictionary.) if (langnum == LANG_de || langnum == LANG_hu) {
std::string wordpart_lower(wordpart); if (utf8) {
u8_u16(w, wordpart_lower);
mkallsmall_utf(w, langnum);
u16_u8(wordpart_lower, w);
} else {
mkallsmall(wordpart_lower, csconv);
}
reptable.emplace_back();
reptable.back().pattern.assign(ph);
reptable.back().outstrings[0].assign(wordpart_lower);
}
reptable.emplace_back();
reptable.back().pattern.assign(ph_capitalized);
reptable.back().outstrings[0].assign(wordpart);
}
}
reptable.emplace_back();
reptable.back().pattern.assign(ph);
reptable.back().outstrings[0].assign(wordpart);
}
}
start_piece = mystrsep(fields, iter);
}
}
}
int HashMgr::add_hidden_capitalized_word(const std::string& word, int wcl, unsignedshort* flags, int flagslen, const std::string* dp, int captype) { if (flags == nullptr)
flagslen = 0;
// detect captype and modify word length for UTF-8 encoding int HashMgr::get_clen_and_captype(const std::string& word, int* captype, std::vector<w_char> &workbuf) { int len; if (utf8) {
len = u8_u16(workbuf, word);
*captype = get_captype_utf8(workbuf, langnum);
} else {
len = word.size();
*captype = get_captype(word, csconv);
} return len;
}
int HashMgr::get_clen_and_captype(const std::string& word, int* captype) {
std::vector<w_char> workbuf; return get_clen_and_captype(word, captype, workbuf);
}
// remove word (personal dictionary function for standalone applications) int HashMgr::remove(const std::string& word) { struct hentry* dp = lookup(word.c_str(), word.size()); while (dp) { if (dp->alen == 0 || !TESTAFF(dp->astr, forbiddenword, dp->alen)) { auto flags = newunsignedshort[dp->alen + 1]; for (int i = 0; i < dp->alen; i++)
flags[i] = dp->astr[i];
flags[dp->alen] = forbiddenword;
release_flags(dp->astr, dp->var & H_OPT_OWNFLAGS);
dp->astr = flags;
dp->alen++;
dp->var |= H_OPT_OWNFLAGS;
std::sort(flags, flags + dp->alen);
}
dp = dp->next_homonym;
} return0;
}
/* remove forbidden flag to add a personal word to the hash */ void HashMgr::remove_forbidden_flag(const std::string& word) { struct hentry* dp = lookup(word.c_str(), word.size()); if (!dp) return; while (dp) { if (dp->astr && TESTAFF(dp->astr, forbiddenword, dp->alen)) { if (dp->alen == 1) {
release_flags(dp->astr, dp->var & H_OPT_OWNFLAGS);
dp->astr = nullptr;
dp->alen = 0;
dp->var &= ~H_OPT_OWNFLAGS;
} else { auto newflags = newunsignedshort[dp->alen - 1]; int j = 0; for (int i = 0; i < dp->alen; i++) { if (dp->astr[i] != forbiddenword)
newflags[j++] = dp->astr[i];
}
release_flags(dp->astr, dp->var & H_OPT_OWNFLAGS);
dp->astr = newflags;
dp->alen = (short)j;
dp->var |= H_OPT_OWNFLAGS;
}
}
dp = dp->next_homonym;
}
}
// add a custom dic. word to the hash table (public) int HashMgr::add(const std::string& word) {
remove_forbidden_flag(word); int captype, al = 0; unsignedshort* flags = nullptr; int wcl = get_clen_and_captype(word, &captype); if (add_word(word, wcl, flags, al, nullptr, false, captype, true)) return1; return add_hidden_capitalized_word(word, wcl, flags, al, nullptr, captype);
}
int HashMgr::add_with_flags(const std::string& word, const std::string& flags, const std::string& desc) {
remove_forbidden_flag(word); int captype; unsignedshort *df; int al = decode_flags(&df, flags, nullptr); int wcl = get_clen_and_captype(word, &captype); if (add_word(word, wcl, df, al, &desc, false, captype, true)) return1; return add_hidden_capitalized_word(word, wcl, df, al, &desc, captype);
}
int HashMgr::add_with_affix(const std::string& word, const std::string& example) { // detect captype and modify word length for UTF-8 encoding struct hentry* dp = lookup(example.c_str(), example.size());
remove_forbidden_flag(word); if (dp && dp->astr) { int captype; int wcl = get_clen_and_captype(word, &captype); auto flags = newunsignedshort[dp->alen];
memcpy(flags, dp->astr, dp->alen * sizeof(unsignedshort)); if (add_word(word, wcl, flags, dp->alen, nullptr, false, captype, true)) return1; return add_hidden_capitalized_word(word, wcl, flags, dp->alen, nullptr, captype);
} return1;
}
// walk the hash table entry by entry - null at end // initialize: col=-1; hp = NULL; hp = walk_hashtable(&col, hp); struct hentry* HashMgr::walk_hashtable(int& col, struct hentry* hp) const { if (hp && hp->next != nullptr) return hp->next; for (col++; col < (int)tableptr.size(); ++col) { if (tableptr[col]) return tableptr[col];
} // null at end and reset to start
col = -1; return nullptr;
}
// load a munched word list and build a hash table on the fly int HashMgr::load_tables(constchar* tpath, constchar* key) { // open dictionary file
FileMgr* dict = new FileMgr(tpath, key);
// first read the first line of file to get hash table size
std::string ts; if (!dict->getline(ts)) {
fprintf(stderr, "error: empty dic file %s\n", tpath); delete dict; return2;
}
mychomp(ts);
/* remove byte order mark */ if (ts.compare(0, 3, "\xEF\xBB\xBF", 3) == 0) {
ts.erase(0, 3);
}
if (tablesize <= 0 || tablesize >= max_allowed) {
fprintf(stderr, "error: %s: line 1: missing or bad word count in the dic file\n",
tpath); delete dict; return4;
}
tablesize += nExtra; if ((tablesize & 1) == 0)
tablesize++;
// allocate the hash table
tableptr.resize(tablesize, nullptr);
// loop through all words on much list and add to hash // table and create word and affix strings
std::vector<w_char> workbuf;
int nLineCount(0); while (dict->getline(ts)) {
++nLineCount; #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION // limit words loaded to avoid O(n^2) hash chain walk timeout if (nLineCount >= tablesize) break; #endif
mychomp(ts); // split each line into word and morphological description
size_t dp_pos = 0; while ((dp_pos = ts.find(':', dp_pos)) != std::string::npos) { if ((dp_pos > 3) && (ts[dp_pos - 3] == ' ' || ts[dp_pos - 3] == '\t')) { for (dp_pos -= 3; dp_pos > 0 && (ts[dp_pos-1] == ' ' || ts[dp_pos-1] == '\t'); --dp_pos)
; if (dp_pos == 0) { // missing word
dp_pos = std::string::npos;
} else {
++dp_pos;
} break;
}
++dp_pos;
}
// tabulator is the old morphological field separator
size_t dp2_pos = ts.find('\t'); if (dp2_pos != std::string::npos && (dp_pos == std::string::npos || dp2_pos < dp_pos)) {
dp_pos = dp2_pos + 1;
}
// split each line into word and affix char strings // "\/" signs slash in words (not affix separator) // "/" at beginning of the line is word character (not affix separator)
size_t ap_pos = ts.find('/'); while (ap_pos != std::string::npos) { if (ap_pos == 0) {
++ap_pos; continue;
} elseif (ts[ap_pos - 1] != '\\') break; // replace "\/" with "/"
ts.erase(ap_pos - 1, 1);
ap_pos = ts.find('/', ap_pos);
}
unsignedshort* flags; int al; if (ap_pos != std::string::npos && ap_pos != ts.size()) {
std::string ap(ts.substr(ap_pos + 1));
ts.resize(ap_pos); if (!aliasf.empty()) { int index = atoi(ap.c_str());
al = get_aliasf(index, &flags, dict); if (!al) {
HUNSPELL_WARNING(stderr, "error: line %d: bad flag vector alias\n",
dict->getlinenum());
}
} else {
al = decode_flags(&flags, ap, dict, /* arena = */ true); if (al == -1) {
HUNSPELL_WARNING(stderr, "Can't allocate memory.\n"); delete dict; return6;
}
std::sort(flags, flags + al);
}
} else {
al = 0;
flags = nullptr;
}
int captype; int wcl = get_clen_and_captype(ts, &captype, workbuf); const std::string* dp_str = dp.empty() ? nullptr : &dp; // add the word and its index plus its capitalized form optionally // flags are arena-allocated, so own_aff must be false bool own = false; if (add_word(ts, wcl, flags, al, dp_str, false, captype, own) ||
add_hidden_capitalized_word(ts, wcl, flags, al, dp_str, captype)) { delete dict; return5;
}
}
int ret(0);
// reject ludicrous tablesizes if (tablesize > 8192 + nExtra && tablesize > nLineCount * 10 + nExtra) {
HUNSPELL_WARNING(stderr, ".dic initial approximate word count line value of %d is too large for %d lines\n", tablesize, nLineCount);
ret = 3;
}
delete dict; return ret;
}
// the hash function is a simple load and rotate // algorithm borrowed int HashMgr::hash(constchar* word, size_t len) const { unsignedlong hv = 0;
size_t i = 0; while (i < 4 && i < len)
hv = (hv << 8) | word[i++]; while (i < len) {
ROTATE(hv, ROTATE_LEN);
hv ^= word[i++];
} return (unsignedlong)hv % tableptr.size();
}
int HashMgr::decode_flags(unsignedshort** result, const std::string& flags, FileMgr* af) const { return decode_flags(result, flags, af, /* arena = */ false);
}
int HashMgr::decode_flags(unsignedshort** result, const std::string& flags, FileMgr* af, bool use_arena) const { auto alloc = [&](int n) -> unsignedshort* { return use_arena ? (unsignedshort*)this->arena_alloc(n * sizeof(unsignedshort),
alignof(unsignedshort))
: newunsignedshort[n];
}; int len; if (flags.empty()) {
*result = nullptr; return0;
} switch (flag_mode) { case FLAG_LONG: { // two-character flags (1x2yZz -> 1x 2y Zz)
len = flags.size(); if ((len & 1) == 1 && af != nullptr)
HUNSPELL_WARNING(stderr, "error: line %d: bad flagvector\n",
af->getlinenum());
len >>= 1;
*result = alloc(len); for (int i = 0; i < len; i++) { unsignedshort flag = ((unsignedshort)((unsignedchar)flags[i << 1]) << 8) |
((unsignedshort)((unsignedchar)flags[(i << 1) | 1]));
(*result)[i] = flag;
} break;
} case FLAG_NUM: { // decimal numbers separated by comma (4521,23,233 -> 4521 // 23 233)
len = int(1 + std::count_if(flags.begin(), flags.end(), [](char c) { return c == ','; }));
*result = alloc(len); unsignedshort* dest = *result; constchar* src = flags.c_str(); for (size_t p = 0; p < flags.size(); ++p) { if (flags[p] == ',') { int i = atoi(src); if ((i > std::numeric_limits<unsignedshort>::max() || i < 0) && af != nullptr) {
HUNSPELL_WARNING(
stderr, "error: line %d: flag id %d is out of range\n",
af->getlinenum(), i);
i = 0;
}
*dest = (unsignedshort)i; if (*dest == 0 && af != nullptr)
HUNSPELL_WARNING(stderr, "error: line %d: 0 is wrong flag id\n",
af->getlinenum());
src = flags.c_str() + p + 1;
dest++;
}
} int i = atoi(src); if ((i > std::numeric_limits<unsignedshort>::max() || i < 0) && af) {
HUNSPELL_WARNING(stderr, "error: line %d: flag id %d is out of range\n",
af->getlinenum(), i);
i = 0;
}
*dest = (unsignedshort)i; if (*dest == 0 && af)
HUNSPELL_WARNING(stderr, "error: line %d: 0 is wrong flag id\n",
af->getlinenum()); break;
} case FLAG_UNI: { // UTF-8 characters
std::vector<w_char> w;
u8_u16(w, flags);
len = w.size();
*result = alloc(len); #ifdefined(_WIN32) || (defined(__BYTE_ORDER__) && (__BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__)) || defined(__LITTLE_ENDIAN__)
memcpy(*result, w.data(), len * sizeof(unsignedshort)); #else unsignedshort* dest = *result; for (const w_char wc : w) {
*dest = (unsignedshort)wc;
dest++;
} #endif break;
} default: { // Ispell's one-character flags (erfg -> e r f g)
len = flags.size();
*result = alloc(len); unsignedshort* dest = *result; for (constchar flag : flags) {
*dest = (unsignedchar)flag;
dest++;
}
}
} return len;
}
bool HashMgr::decode_flags(std::vector<unsignedshort>& result, const std::string& flags, FileMgr* af) const { if (flags.empty()) { returnfalse;
} switch (flag_mode) { case FLAG_LONG: { // two-character flags (1x2yZz -> 1x 2y Zz)
size_t len = flags.size(); if ((len & 1) == 1)
HUNSPELL_WARNING(stderr, "error: line %d: bad flagvector\n",
af->getlinenum());
len >>= 1;
size_t origsize = result.size();
result.resize(origsize + len); for (size_t i = 0; i < len; ++i) {
result[origsize + i] = ((unsignedshort)((unsignedchar)flags[i << 1]) << 8) |
((unsignedshort)((unsignedchar)flags[(i << 1) | 1]));
} break;
} case FLAG_NUM: { // decimal numbers separated by comma (4521,23,233 -> 4521 // 23 233) constchar* src = flags.c_str(); for (constchar* p = src; *p; p++) { if (*p == ',') { int i = atoi(src); if (i > std::numeric_limits<unsignedshort>::max() || i < 0) {
HUNSPELL_WARNING(
stderr, "error: line %d: flag id %d is out of range\n",
af->getlinenum(), i);
i = 0;
}
result.push_back((unsignedshort)i); if (result.back() == 0)
HUNSPELL_WARNING(stderr, "error: line %d: 0 is wrong flag id\n",
af->getlinenum());
src = p + 1;
}
} int i = atoi(src); if (i > std::numeric_limits<unsignedshort>::max() || i < 0) {
HUNSPELL_WARNING(stderr, "error: line %d: flag id %d is out of range\n",
af->getlinenum(), i);
i = 0;
}
result.push_back((unsignedshort)i); if (result.back() == 0)
HUNSPELL_WARNING(stderr, "error: line %d: 0 is wrong flag id\n",
af->getlinenum()); break;
} case FLAG_UNI: { // UTF-8 characters
std::vector<w_char> w;
u8_u16(w, flags);
size_t len = w.size(), origsize = result.size();
result.resize(origsize + len); #ifdefined(_WIN32) || (defined(__BYTE_ORDER__) && (__BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__)) || defined(__LITTLE_ENDIAN__)
memcpy(result.data() + origsize, w.data(), len * sizeof(short)); #else for (size_t i = 0; i < len; ++i)
result[origsize + i] = (unsignedshort)w[i]; #endif break;
} default: { // Ispell's one-character flags (erfg -> e r f g)
result.reserve(flags.size()); for (constchar flag : flags) {
result.push_back((unsignedchar)flag);
}
}
} returntrue;
}
unsignedshort HashMgr::decode_flag(const std::string& f) const { unsignedshort s = 0; int i; switch (flag_mode) { case FLAG_LONG: if (f.size() >= 2)
s = ((unsignedshort)((unsignedchar)f[0]) << 8) | ((unsignedshort)((unsignedchar)f[1])); break; case FLAG_NUM:
i = atoi(f.c_str()); if (i > std::numeric_limits<unsignedshort>::max() || i < 0) {
HUNSPELL_WARNING(stderr, "error: flag id %d is out of range\n", i);
i = 0;
}
s = (unsignedshort)i; break; case FLAG_UNI: {
std::vector<w_char> w;
u8_u16(w, f); if (!w.empty())
s = (unsignedshort)w[0]; break;
} default: if (!f.empty())
s = (unsignedchar)f[0];
} if (s == 0)
HUNSPELL_WARNING(stderr, "error: 0 is wrong flag id\n"); return s;
}
void* HashMgr::arena_alloc(size_t num_bytes, size_t alignment) const { // Fixed-size 64KB chunks: small enough to avoid significant waste on small // dictionaries, large enough to amortize per-chunk malloc overhead on large // ones. make_unique throws std::bad_alloc on OOM. staticconst size_t MIN_CHUNK_SIZE = 65536; staticconst size_t MAX_ALIGNMENT = alignof(std::max_align_t); // Chunk sizes are rounded up to MAX_ALIGNMENT below; with this invariant, // any alignment that divides MAX_ALIGNMENT keeps aligned_offset within bounds.
assert(alignment > 0 && alignment <= MAX_ALIGNMENT); // Pad the offset up to the requested alignment before placing this allocation. // make_unique returns memory aligned for any scalar, so chunk-start is fine.
size_t aligned_offset = (current_chunk_offset + alignment - 1) & ~(alignment - 1); if (arena.empty() || current_chunk_size - aligned_offset < num_bytes) { // Round the new chunk's size up to a multiple of MAX_ALIGNMENT so that an // oversized num_bytes (>= MIN_CHUNK_SIZE) cannot leave a non-aligned // current_chunk_size that would later cause aligned_offset to overshoot. // Allocate before mutating current_chunk_size so a throwing make_unique // leaves the HashMgr in a consistent state.
size_t new_size = std::max(MIN_CHUNK_SIZE, num_bytes);
new_size = (new_size + MAX_ALIGNMENT - 1) & ~(MAX_ALIGNMENT - 1);
arena.push_back(std::make_unique<uint8_t[]>(new_size));
current_chunk_size = new_size;
aligned_offset = 0;
}
void HashMgr::arena_free(void*) const { // The arena vector owns all allocations and frees them in bulk at HashMgr // destruction, so this is a no-op for the memory itself. The counter is a // memory-safety check: more arena_free calls than arena_alloc calls would // indicate a double-free or use-after-free in Hunspell. Abort hard rather // than silently desynchronize tracking, even in release builds. if (outstanding_arena_allocations == 0) {
std::abort();
}
--outstanding_arena_allocations;
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.47 Sekunden
(vorverarbeitet am 2026-09-28)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.