зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1739761 - arena-allocate strings in hunspell. r=glandium
This reduces the number of wasm pages allocated from 64 to 47 on en_us builds, corresponding to about 1 MB of reduced memory usage. Differential Revision: https://phabricator.services.mozilla.com/D132519
This commit is contained in:
Родитель
cf11e2e94d
Коммит
031f81a28e
|
@ -0,0 +1,322 @@
|
|||
diff --git a/extensions/spellcheck/hunspell/src/hashmgr.cxx b/extensions/spellcheck/hunspell/src/hashmgr.cxx
|
||||
index 7e843c3e76624..9536880c9050d 100644
|
||||
--- a/extensions/spellcheck/hunspell/src/hashmgr.cxx
|
||||
+++ b/extensions/spellcheck/hunspell/src/hashmgr.cxx
|
||||
@@ -68,6 +68,7 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
+#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
@@ -123,8 +124,8 @@ HashMgr::~HashMgr() {
|
||||
nt = pt->next;
|
||||
if (pt->astr &&
|
||||
(!aliasf || TESTAFF(pt->astr, ONLYUPCASEFLAG, pt->alen)))
|
||||
- free(pt->astr);
|
||||
- free(pt);
|
||||
+ arena_free(pt->astr);
|
||||
+ arena_free(pt);
|
||||
pt = nt;
|
||||
}
|
||||
}
|
||||
@@ -134,18 +135,18 @@ HashMgr::~HashMgr() {
|
||||
|
||||
if (aliasf) {
|
||||
for (int j = 0; j < (numaliasf); j++)
|
||||
- free(aliasf[j]);
|
||||
- free(aliasf);
|
||||
+ arena_free(aliasf[j]);
|
||||
+ arena_free(aliasf);
|
||||
aliasf = NULL;
|
||||
if (aliasflen) {
|
||||
- free(aliasflen);
|
||||
+ arena_free(aliasflen);
|
||||
aliasflen = NULL;
|
||||
}
|
||||
}
|
||||
if (aliasm) {
|
||||
for (int j = 0; j < (numaliasm); j++)
|
||||
- free(aliasm[j]);
|
||||
- free(aliasm);
|
||||
+ arena_free(aliasm[j]);
|
||||
+ arena_free(aliasm);
|
||||
aliasm = NULL;
|
||||
}
|
||||
|
||||
@@ -159,6 +160,8 @@ HashMgr::~HashMgr() {
|
||||
#ifdef MOZILLA_CLIENT
|
||||
delete[] csconv;
|
||||
#endif
|
||||
+
|
||||
+ assert(outstanding_arena_allocations == 0);
|
||||
}
|
||||
|
||||
// lookup a root word in the hashtable
|
||||
@@ -227,7 +230,7 @@ int HashMgr::add_word(const std::string& in_word,
|
||||
int descl = desc ? (aliasm ? sizeof(char*) : desc->size() + 1) : 0;
|
||||
// variable-length hash record with word and optional fields
|
||||
struct hentry* hp =
|
||||
- (struct hentry*)malloc(sizeof(struct hentry) + word->size() + descl);
|
||||
+ (struct hentry*)arena_alloc(sizeof(struct hentry) + word->size() + descl);
|
||||
if (!hp) {
|
||||
delete desc_copy;
|
||||
delete word_copy;
|
||||
@@ -371,10 +374,10 @@ int HashMgr::add_word(const std::string& in_word,
|
||||
// remove hidden onlyupcase homonym
|
||||
if (!onlyupcase) {
|
||||
if ((dp->astr) && TESTAFF(dp->astr, ONLYUPCASEFLAG, dp->alen)) {
|
||||
- free(dp->astr);
|
||||
+ arena_free(dp->astr);
|
||||
dp->astr = hp->astr;
|
||||
dp->alen = hp->alen;
|
||||
- free(hp);
|
||||
+ arena_free(hp);
|
||||
delete desc_copy;
|
||||
delete word_copy;
|
||||
return 0;
|
||||
@@ -391,10 +394,10 @@ int HashMgr::add_word(const std::string& in_word,
|
||||
// remove hidden onlyupcase homonym
|
||||
if (!onlyupcase) {
|
||||
if ((dp->astr) && TESTAFF(dp->astr, ONLYUPCASEFLAG, dp->alen)) {
|
||||
- free(dp->astr);
|
||||
+ arena_free(dp->astr);
|
||||
dp->astr = hp->astr;
|
||||
dp->alen = hp->alen;
|
||||
- free(hp);
|
||||
+ arena_free(hp);
|
||||
delete desc_copy;
|
||||
delete word_copy;
|
||||
return 0;
|
||||
@@ -410,8 +413,8 @@ int HashMgr::add_word(const std::string& in_word,
|
||||
} else {
|
||||
// remove hidden onlyupcase homonym
|
||||
if (hp->astr)
|
||||
- free(hp->astr);
|
||||
- free(hp);
|
||||
+ arena_free(hp->astr);
|
||||
+ arena_free(hp);
|
||||
}
|
||||
|
||||
delete desc_copy;
|
||||
@@ -435,7 +438,7 @@ int HashMgr::add_hidden_capitalized_word(const std::string& word,
|
||||
((captype == ALLCAP) && (flagslen != 0))) &&
|
||||
!((flagslen != 0) && TESTAFF(flags, forbiddenword, flagslen))) {
|
||||
unsigned short* flags2 =
|
||||
- (unsigned short*)malloc(sizeof(unsigned short) * (flagslen + 1));
|
||||
+ (unsigned short*)arena_alloc(sizeof(unsigned short) * (flagslen + 1));
|
||||
if (!flags2)
|
||||
return 1;
|
||||
if (flagslen)
|
||||
@@ -484,13 +487,13 @@ int HashMgr::remove(const std::string& word) {
|
||||
while (dp) {
|
||||
if (dp->alen == 0 || !TESTAFF(dp->astr, forbiddenword, dp->alen)) {
|
||||
unsigned short* flags =
|
||||
- (unsigned short*)malloc(sizeof(unsigned short) * (dp->alen + 1));
|
||||
+ (unsigned short*)arena_alloc(sizeof(unsigned short) * (dp->alen + 1));
|
||||
if (!flags)
|
||||
return 1;
|
||||
for (int i = 0; i < dp->alen; i++)
|
||||
flags[i] = dp->astr[i];
|
||||
flags[dp->alen] = forbiddenword;
|
||||
- free(dp->astr);
|
||||
+ arena_free(dp->astr);
|
||||
dp->astr = flags;
|
||||
dp->alen++;
|
||||
std::sort(flags, flags + dp->alen);
|
||||
@@ -538,7 +541,7 @@ int HashMgr::add_with_affix(const std::string& word, const std::string& example)
|
||||
add_word(word, wcl, dp->astr, dp->alen, NULL, false, captype);
|
||||
} else {
|
||||
unsigned short* flags =
|
||||
- (unsigned short*)malloc(dp->alen * sizeof(unsigned short));
|
||||
+ (unsigned short*) arena_alloc(dp->alen * sizeof(unsigned short));
|
||||
if (flags) {
|
||||
memcpy((void*)flags, (void*)dp->astr,
|
||||
dp->alen * sizeof(unsigned short));
|
||||
@@ -727,7 +730,7 @@ int HashMgr::decode_flags(unsigned short** result, const std::string& flags, Fil
|
||||
HUNSPELL_WARNING(stderr, "error: line %d: bad flagvector\n",
|
||||
af->getlinenum());
|
||||
len /= 2;
|
||||
- *result = (unsigned short*)malloc(len * sizeof(unsigned short));
|
||||
+ *result = (unsigned short*)arena_alloc(len * sizeof(unsigned short));
|
||||
if (!*result)
|
||||
return -1;
|
||||
for (int i = 0; i < len; i++) {
|
||||
@@ -744,7 +747,7 @@ int HashMgr::decode_flags(unsigned short** result, const std::string& flags, Fil
|
||||
if (flags[i] == ',')
|
||||
len++;
|
||||
}
|
||||
- *result = (unsigned short*)malloc(len * sizeof(unsigned short));
|
||||
+ *result = (unsigned short*)arena_alloc(len * sizeof(unsigned short));
|
||||
if (!*result)
|
||||
return -1;
|
||||
dest = *result;
|
||||
@@ -779,7 +782,7 @@ int HashMgr::decode_flags(unsigned short** result, const std::string& flags, Fil
|
||||
std::vector<w_char> w;
|
||||
u8_u16(w, flags);
|
||||
len = w.size();
|
||||
- *result = (unsigned short*)malloc(len * sizeof(unsigned short));
|
||||
+ *result = (unsigned short*)arena_alloc(len * sizeof(unsigned short));
|
||||
if (!*result)
|
||||
return -1;
|
||||
memcpy(*result, &w[0], len * sizeof(short));
|
||||
@@ -788,7 +791,7 @@ int HashMgr::decode_flags(unsigned short** result, const std::string& flags, Fil
|
||||
default: { // Ispell's one-character flags (erfg -> e r f g)
|
||||
unsigned short* dest;
|
||||
len = flags.size();
|
||||
- *result = (unsigned short*)malloc(len * sizeof(unsigned short));
|
||||
+ *result = (unsigned short*)arena_alloc(len * sizeof(unsigned short));
|
||||
if (!*result)
|
||||
return -1;
|
||||
dest = *result;
|
||||
@@ -1075,15 +1078,15 @@ bool HashMgr::parse_aliasf(const std::string& line, FileMgr* af) {
|
||||
return false;
|
||||
}
|
||||
aliasf =
|
||||
- (unsigned short**)malloc(numaliasf * sizeof(unsigned short*));
|
||||
+ (unsigned short**)arena_alloc(numaliasf * sizeof(unsigned short*));
|
||||
aliasflen =
|
||||
- (unsigned short*)malloc(numaliasf * sizeof(unsigned short));
|
||||
+ (unsigned short*)arena_alloc(numaliasf * sizeof(unsigned short));
|
||||
if (!aliasf || !aliasflen) {
|
||||
numaliasf = 0;
|
||||
if (aliasf)
|
||||
- free(aliasf);
|
||||
+ arena_free(aliasf);
|
||||
if (aliasflen)
|
||||
- free(aliasflen);
|
||||
+ arena_free(aliasflen);
|
||||
aliasf = NULL;
|
||||
aliasflen = NULL;
|
||||
return false;
|
||||
@@ -1099,8 +1102,8 @@ bool HashMgr::parse_aliasf(const std::string& line, FileMgr* af) {
|
||||
}
|
||||
if (np != 2) {
|
||||
numaliasf = 0;
|
||||
- free(aliasf);
|
||||
- free(aliasflen);
|
||||
+ arena_free(aliasf);
|
||||
+ arena_free(aliasflen);
|
||||
aliasf = NULL;
|
||||
aliasflen = NULL;
|
||||
HUNSPELL_WARNING(stderr, "error: line %d: missing data\n",
|
||||
@@ -1124,8 +1127,8 @@ bool HashMgr::parse_aliasf(const std::string& line, FileMgr* af) {
|
||||
case 0: {
|
||||
if (nl.compare(start_piece - nl.begin(), 2, "AF", 2) != 0) {
|
||||
numaliasf = 0;
|
||||
- free(aliasf);
|
||||
- free(aliasflen);
|
||||
+ arena_free(aliasf);
|
||||
+ arena_free(aliasflen);
|
||||
aliasf = NULL;
|
||||
aliasflen = NULL;
|
||||
HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n",
|
||||
@@ -1148,8 +1151,8 @@ bool HashMgr::parse_aliasf(const std::string& line, FileMgr* af) {
|
||||
start_piece = mystrsep(nl, iter);
|
||||
}
|
||||
if (!aliasf[j]) {
|
||||
- free(aliasf);
|
||||
- free(aliasflen);
|
||||
+ arena_free(aliasf);
|
||||
+ arena_free(aliasflen);
|
||||
aliasf = NULL;
|
||||
aliasflen = NULL;
|
||||
numaliasf = 0;
|
||||
@@ -1200,7 +1203,7 @@ bool HashMgr::parse_aliasm(const std::string& line, FileMgr* af) {
|
||||
af->getlinenum());
|
||||
return false;
|
||||
}
|
||||
- aliasm = (char**)malloc(numaliasm * sizeof(char*));
|
||||
+ aliasm = (char**)arena_alloc(numaliasm * sizeof(char*));
|
||||
if (!aliasm) {
|
||||
numaliasm = 0;
|
||||
return false;
|
||||
@@ -1216,7 +1219,7 @@ bool HashMgr::parse_aliasm(const std::string& line, FileMgr* af) {
|
||||
}
|
||||
if (np != 2) {
|
||||
numaliasm = 0;
|
||||
- free(aliasm);
|
||||
+ arena_free(aliasm);
|
||||
aliasm = NULL;
|
||||
HUNSPELL_WARNING(stderr, "error: line %d: missing data\n",
|
||||
af->getlinenum());
|
||||
@@ -1240,7 +1243,7 @@ bool HashMgr::parse_aliasm(const std::string& line, FileMgr* af) {
|
||||
HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n",
|
||||
af->getlinenum());
|
||||
numaliasm = 0;
|
||||
- free(aliasm);
|
||||
+ arena_free(aliasm);
|
||||
aliasm = NULL;
|
||||
return false;
|
||||
}
|
||||
@@ -1267,7 +1270,7 @@ bool HashMgr::parse_aliasm(const std::string& line, FileMgr* af) {
|
||||
}
|
||||
if (!aliasm[j]) {
|
||||
numaliasm = 0;
|
||||
- free(aliasm);
|
||||
+ arena_free(aliasm);
|
||||
aliasm = NULL;
|
||||
HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n",
|
||||
af->getlinenum());
|
||||
@@ -1387,3 +1390,25 @@ bool HashMgr::parse_reptable(const std::string& line, FileMgr* af) {
|
||||
const std::vector<replentry>& HashMgr::get_reptable() const {
|
||||
return reptable;
|
||||
}
|
||||
+
|
||||
+void* HashMgr::arena_alloc(int num_bytes) {
|
||||
+ if (num_bytes > CHUNK_SIZE) {
|
||||
+ assert(false);
|
||||
+ return nullptr;
|
||||
+ }
|
||||
+
|
||||
+ if (arena.empty() || (CHUNK_SIZE - current_chunk_offset < num_bytes)) {
|
||||
+ arena.push_back(std::make_unique<uint8_t[]>(CHUNK_SIZE));
|
||||
+ current_chunk_offset = 0;
|
||||
+ }
|
||||
+
|
||||
+ uint8_t* ptr = &arena.back()[current_chunk_offset];
|
||||
+ current_chunk_offset += num_bytes;
|
||||
+ outstanding_arena_allocations++;
|
||||
+ return ptr;
|
||||
+}
|
||||
+
|
||||
+void HashMgr::arena_free(void* ptr) {
|
||||
+ --outstanding_arena_allocations;
|
||||
+ assert(outstanding_arena_allocations >= 0);
|
||||
+}
|
||||
diff --git a/extensions/spellcheck/hunspell/src/hashmgr.hxx b/extensions/spellcheck/hunspell/src/hashmgr.hxx
|
||||
index b6eadddecc5b9..6919bc725b885 100644
|
||||
--- a/extensions/spellcheck/hunspell/src/hashmgr.hxx
|
||||
+++ b/extensions/spellcheck/hunspell/src/hashmgr.hxx
|
||||
@@ -72,6 +72,7 @@
|
||||
#define HASHMGR_HXX_
|
||||
|
||||
#include <stdio.h>
|
||||
+#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -153,6 +154,22 @@ class HashMgr {
|
||||
bool parse_aliasm(const std::string& line, FileMgr* af);
|
||||
bool parse_reptable(const std::string& line, FileMgr* af);
|
||||
int remove_forbidden_flag(const std::string& word);
|
||||
+
|
||||
+ // Our Mozilla fork uses a simple arena allocator for certain strings which
|
||||
+ // persist for the lifetime of the HashMgr in order to avoid heap fragmentation.
|
||||
+ // It's a simple bump-allocator, so we can't actually free() memory midway
|
||||
+ // through the lifecycle, but we have a dummy free() implementation to ensure
|
||||
+ // that our calls to arena_alloc() and arena_free() are balanced.
|
||||
+ void* arena_alloc(int num_bytes);
|
||||
+ void* arena_alloc(int num_bytes) const {
|
||||
+ return const_cast<HashMgr*>(this)->arena_alloc(num_bytes);
|
||||
+ }
|
||||
+ void arena_free(void* ptr);
|
||||
+
|
||||
+ static const int CHUNK_SIZE = 4096;
|
||||
+ std::vector<std::unique_ptr<uint8_t[]>> arena;
|
||||
+ int current_chunk_offset = 0;
|
||||
+ int outstanding_arena_allocations = 0;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -68,6 +68,7 @@
|
|||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
@ -123,8 +124,8 @@ HashMgr::~HashMgr() {
|
|||
nt = pt->next;
|
||||
if (pt->astr &&
|
||||
(!aliasf || TESTAFF(pt->astr, ONLYUPCASEFLAG, pt->alen)))
|
||||
free(pt->astr);
|
||||
free(pt);
|
||||
arena_free(pt->astr);
|
||||
arena_free(pt);
|
||||
pt = nt;
|
||||
}
|
||||
}
|
||||
|
@ -134,18 +135,18 @@ HashMgr::~HashMgr() {
|
|||
|
||||
if (aliasf) {
|
||||
for (int j = 0; j < (numaliasf); j++)
|
||||
free(aliasf[j]);
|
||||
free(aliasf);
|
||||
arena_free(aliasf[j]);
|
||||
arena_free(aliasf);
|
||||
aliasf = NULL;
|
||||
if (aliasflen) {
|
||||
free(aliasflen);
|
||||
arena_free(aliasflen);
|
||||
aliasflen = NULL;
|
||||
}
|
||||
}
|
||||
if (aliasm) {
|
||||
for (int j = 0; j < (numaliasm); j++)
|
||||
free(aliasm[j]);
|
||||
free(aliasm);
|
||||
arena_free(aliasm[j]);
|
||||
arena_free(aliasm);
|
||||
aliasm = NULL;
|
||||
}
|
||||
|
||||
|
@ -159,6 +160,8 @@ HashMgr::~HashMgr() {
|
|||
#ifdef MOZILLA_CLIENT
|
||||
delete[] csconv;
|
||||
#endif
|
||||
|
||||
assert(outstanding_arena_allocations == 0);
|
||||
}
|
||||
|
||||
// lookup a root word in the hashtable
|
||||
|
@ -227,7 +230,7 @@ int HashMgr::add_word(const std::string& in_word,
|
|||
int descl = desc ? (aliasm ? sizeof(char*) : desc->size() + 1) : 0;
|
||||
// variable-length hash record with word and optional fields
|
||||
struct hentry* hp =
|
||||
(struct hentry*)malloc(sizeof(struct hentry) + word->size() + descl);
|
||||
(struct hentry*)arena_alloc(sizeof(struct hentry) + word->size() + descl);
|
||||
if (!hp) {
|
||||
delete desc_copy;
|
||||
delete word_copy;
|
||||
|
@ -371,10 +374,10 @@ int HashMgr::add_word(const std::string& in_word,
|
|||
// remove hidden onlyupcase homonym
|
||||
if (!onlyupcase) {
|
||||
if ((dp->astr) && TESTAFF(dp->astr, ONLYUPCASEFLAG, dp->alen)) {
|
||||
free(dp->astr);
|
||||
arena_free(dp->astr);
|
||||
dp->astr = hp->astr;
|
||||
dp->alen = hp->alen;
|
||||
free(hp);
|
||||
arena_free(hp);
|
||||
delete desc_copy;
|
||||
delete word_copy;
|
||||
return 0;
|
||||
|
@ -391,10 +394,10 @@ int HashMgr::add_word(const std::string& in_word,
|
|||
// remove hidden onlyupcase homonym
|
||||
if (!onlyupcase) {
|
||||
if ((dp->astr) && TESTAFF(dp->astr, ONLYUPCASEFLAG, dp->alen)) {
|
||||
free(dp->astr);
|
||||
arena_free(dp->astr);
|
||||
dp->astr = hp->astr;
|
||||
dp->alen = hp->alen;
|
||||
free(hp);
|
||||
arena_free(hp);
|
||||
delete desc_copy;
|
||||
delete word_copy;
|
||||
return 0;
|
||||
|
@ -410,8 +413,8 @@ int HashMgr::add_word(const std::string& in_word,
|
|||
} else {
|
||||
// remove hidden onlyupcase homonym
|
||||
if (hp->astr)
|
||||
free(hp->astr);
|
||||
free(hp);
|
||||
arena_free(hp->astr);
|
||||
arena_free(hp);
|
||||
}
|
||||
|
||||
delete desc_copy;
|
||||
|
@ -435,7 +438,7 @@ int HashMgr::add_hidden_capitalized_word(const std::string& word,
|
|||
((captype == ALLCAP) && (flagslen != 0))) &&
|
||||
!((flagslen != 0) && TESTAFF(flags, forbiddenword, flagslen))) {
|
||||
unsigned short* flags2 =
|
||||
(unsigned short*)malloc(sizeof(unsigned short) * (flagslen + 1));
|
||||
(unsigned short*)arena_alloc(sizeof(unsigned short) * (flagslen + 1));
|
||||
if (!flags2)
|
||||
return 1;
|
||||
if (flagslen)
|
||||
|
@ -484,13 +487,13 @@ int HashMgr::remove(const std::string& word) {
|
|||
while (dp) {
|
||||
if (dp->alen == 0 || !TESTAFF(dp->astr, forbiddenword, dp->alen)) {
|
||||
unsigned short* flags =
|
||||
(unsigned short*)malloc(sizeof(unsigned short) * (dp->alen + 1));
|
||||
(unsigned short*)arena_alloc(sizeof(unsigned short) * (dp->alen + 1));
|
||||
if (!flags)
|
||||
return 1;
|
||||
for (int i = 0; i < dp->alen; i++)
|
||||
flags[i] = dp->astr[i];
|
||||
flags[dp->alen] = forbiddenword;
|
||||
free(dp->astr);
|
||||
arena_free(dp->astr);
|
||||
dp->astr = flags;
|
||||
dp->alen++;
|
||||
std::sort(flags, flags + dp->alen);
|
||||
|
@ -538,7 +541,7 @@ int HashMgr::add_with_affix(const std::string& word, const std::string& example)
|
|||
add_word(word, wcl, dp->astr, dp->alen, NULL, false, captype);
|
||||
} else {
|
||||
unsigned short* flags =
|
||||
(unsigned short*)malloc(dp->alen * sizeof(unsigned short));
|
||||
(unsigned short*) arena_alloc(dp->alen * sizeof(unsigned short));
|
||||
if (flags) {
|
||||
memcpy((void*)flags, (void*)dp->astr,
|
||||
dp->alen * sizeof(unsigned short));
|
||||
|
@ -727,7 +730,7 @@ int HashMgr::decode_flags(unsigned short** result, const std::string& flags, Fil
|
|||
HUNSPELL_WARNING(stderr, "error: line %d: bad flagvector\n",
|
||||
af->getlinenum());
|
||||
len /= 2;
|
||||
*result = (unsigned short*)malloc(len * sizeof(unsigned short));
|
||||
*result = (unsigned short*)arena_alloc(len * sizeof(unsigned short));
|
||||
if (!*result)
|
||||
return -1;
|
||||
for (int i = 0; i < len; i++) {
|
||||
|
@ -744,7 +747,7 @@ int HashMgr::decode_flags(unsigned short** result, const std::string& flags, Fil
|
|||
if (flags[i] == ',')
|
||||
len++;
|
||||
}
|
||||
*result = (unsigned short*)malloc(len * sizeof(unsigned short));
|
||||
*result = (unsigned short*)arena_alloc(len * sizeof(unsigned short));
|
||||
if (!*result)
|
||||
return -1;
|
||||
dest = *result;
|
||||
|
@ -779,7 +782,7 @@ int HashMgr::decode_flags(unsigned short** result, const std::string& flags, Fil
|
|||
std::vector<w_char> w;
|
||||
u8_u16(w, flags);
|
||||
len = w.size();
|
||||
*result = (unsigned short*)malloc(len * sizeof(unsigned short));
|
||||
*result = (unsigned short*)arena_alloc(len * sizeof(unsigned short));
|
||||
if (!*result)
|
||||
return -1;
|
||||
memcpy(*result, &w[0], len * sizeof(short));
|
||||
|
@ -788,7 +791,7 @@ int HashMgr::decode_flags(unsigned short** result, const std::string& flags, Fil
|
|||
default: { // Ispell's one-character flags (erfg -> e r f g)
|
||||
unsigned short* dest;
|
||||
len = flags.size();
|
||||
*result = (unsigned short*)malloc(len * sizeof(unsigned short));
|
||||
*result = (unsigned short*)arena_alloc(len * sizeof(unsigned short));
|
||||
if (!*result)
|
||||
return -1;
|
||||
dest = *result;
|
||||
|
@ -1075,15 +1078,15 @@ bool HashMgr::parse_aliasf(const std::string& line, FileMgr* af) {
|
|||
return false;
|
||||
}
|
||||
aliasf =
|
||||
(unsigned short**)malloc(numaliasf * sizeof(unsigned short*));
|
||||
(unsigned short**)arena_alloc(numaliasf * sizeof(unsigned short*));
|
||||
aliasflen =
|
||||
(unsigned short*)malloc(numaliasf * sizeof(unsigned short));
|
||||
(unsigned short*)arena_alloc(numaliasf * sizeof(unsigned short));
|
||||
if (!aliasf || !aliasflen) {
|
||||
numaliasf = 0;
|
||||
if (aliasf)
|
||||
free(aliasf);
|
||||
arena_free(aliasf);
|
||||
if (aliasflen)
|
||||
free(aliasflen);
|
||||
arena_free(aliasflen);
|
||||
aliasf = NULL;
|
||||
aliasflen = NULL;
|
||||
return false;
|
||||
|
@ -1099,8 +1102,8 @@ bool HashMgr::parse_aliasf(const std::string& line, FileMgr* af) {
|
|||
}
|
||||
if (np != 2) {
|
||||
numaliasf = 0;
|
||||
free(aliasf);
|
||||
free(aliasflen);
|
||||
arena_free(aliasf);
|
||||
arena_free(aliasflen);
|
||||
aliasf = NULL;
|
||||
aliasflen = NULL;
|
||||
HUNSPELL_WARNING(stderr, "error: line %d: missing data\n",
|
||||
|
@ -1124,8 +1127,8 @@ bool HashMgr::parse_aliasf(const std::string& line, FileMgr* af) {
|
|||
case 0: {
|
||||
if (nl.compare(start_piece - nl.begin(), 2, "AF", 2) != 0) {
|
||||
numaliasf = 0;
|
||||
free(aliasf);
|
||||
free(aliasflen);
|
||||
arena_free(aliasf);
|
||||
arena_free(aliasflen);
|
||||
aliasf = NULL;
|
||||
aliasflen = NULL;
|
||||
HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n",
|
||||
|
@ -1148,8 +1151,8 @@ bool HashMgr::parse_aliasf(const std::string& line, FileMgr* af) {
|
|||
start_piece = mystrsep(nl, iter);
|
||||
}
|
||||
if (!aliasf[j]) {
|
||||
free(aliasf);
|
||||
free(aliasflen);
|
||||
arena_free(aliasf);
|
||||
arena_free(aliasflen);
|
||||
aliasf = NULL;
|
||||
aliasflen = NULL;
|
||||
numaliasf = 0;
|
||||
|
@ -1200,7 +1203,7 @@ bool HashMgr::parse_aliasm(const std::string& line, FileMgr* af) {
|
|||
af->getlinenum());
|
||||
return false;
|
||||
}
|
||||
aliasm = (char**)malloc(numaliasm * sizeof(char*));
|
||||
aliasm = (char**)arena_alloc(numaliasm * sizeof(char*));
|
||||
if (!aliasm) {
|
||||
numaliasm = 0;
|
||||
return false;
|
||||
|
@ -1216,7 +1219,7 @@ bool HashMgr::parse_aliasm(const std::string& line, FileMgr* af) {
|
|||
}
|
||||
if (np != 2) {
|
||||
numaliasm = 0;
|
||||
free(aliasm);
|
||||
arena_free(aliasm);
|
||||
aliasm = NULL;
|
||||
HUNSPELL_WARNING(stderr, "error: line %d: missing data\n",
|
||||
af->getlinenum());
|
||||
|
@ -1240,7 +1243,7 @@ bool HashMgr::parse_aliasm(const std::string& line, FileMgr* af) {
|
|||
HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n",
|
||||
af->getlinenum());
|
||||
numaliasm = 0;
|
||||
free(aliasm);
|
||||
arena_free(aliasm);
|
||||
aliasm = NULL;
|
||||
return false;
|
||||
}
|
||||
|
@ -1267,7 +1270,7 @@ bool HashMgr::parse_aliasm(const std::string& line, FileMgr* af) {
|
|||
}
|
||||
if (!aliasm[j]) {
|
||||
numaliasm = 0;
|
||||
free(aliasm);
|
||||
arena_free(aliasm);
|
||||
aliasm = NULL;
|
||||
HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n",
|
||||
af->getlinenum());
|
||||
|
@ -1387,3 +1390,25 @@ bool HashMgr::parse_reptable(const std::string& line, FileMgr* af) {
|
|||
const std::vector<replentry>& HashMgr::get_reptable() const {
|
||||
return reptable;
|
||||
}
|
||||
|
||||
void* HashMgr::arena_alloc(int num_bytes) {
|
||||
if (num_bytes > CHUNK_SIZE) {
|
||||
assert(false);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (arena.empty() || (CHUNK_SIZE - current_chunk_offset < num_bytes)) {
|
||||
arena.push_back(std::make_unique<uint8_t[]>(CHUNK_SIZE));
|
||||
current_chunk_offset = 0;
|
||||
}
|
||||
|
||||
uint8_t* ptr = &arena.back()[current_chunk_offset];
|
||||
current_chunk_offset += num_bytes;
|
||||
outstanding_arena_allocations++;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void HashMgr::arena_free(void* ptr) {
|
||||
--outstanding_arena_allocations;
|
||||
assert(outstanding_arena_allocations >= 0);
|
||||
}
|
||||
|
|
|
@ -72,6 +72,8 @@
|
|||
#define HASHMGR_HXX_
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
@ -153,6 +155,22 @@ class HashMgr {
|
|||
bool parse_aliasm(const std::string& line, FileMgr* af);
|
||||
bool parse_reptable(const std::string& line, FileMgr* af);
|
||||
int remove_forbidden_flag(const std::string& word);
|
||||
|
||||
// Our Mozilla fork uses a simple arena allocator for certain strings which
|
||||
// persist for the lifetime of the HashMgr in order to avoid heap fragmentation.
|
||||
// It's a simple bump-allocator, so we can't actually free() memory midway
|
||||
// through the lifecycle, but we have a dummy free() implementation to ensure
|
||||
// that our calls to arena_alloc() and arena_free() are balanced.
|
||||
void* arena_alloc(int num_bytes);
|
||||
void* arena_alloc(int num_bytes) const {
|
||||
return const_cast<HashMgr*>(this)->arena_alloc(num_bytes);
|
||||
}
|
||||
void arena_free(void* ptr);
|
||||
|
||||
static const int CHUNK_SIZE = 4096;
|
||||
std::vector<std::unique_ptr<uint8_t[]>> arena;
|
||||
int current_chunk_offset = 0;
|
||||
int outstanding_arena_allocations = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -28,3 +28,4 @@ rm -rf ${tmpclonedir}
|
|||
|
||||
cd ${hunspell_dir}/src
|
||||
patch -p5 < ../patches/bug1410214.patch
|
||||
patch -p5 < ../patches/bug1739761.patch
|
||||
|
|
Загрузка…
Ссылка в новой задаче