Use ubrk_clone instead of ubrk_open to create UBreakIterator instances.

`ubrk_clone` is much faster, because the locale doesn't have to be parsed again.
This commit is contained in:
Lukas Tenbrink
2025-03-21 18:33:05 +01:00
parent 3d9b05ad4a
commit 09e7bef0e0
2 changed files with 24 additions and 14 deletions

View File

@ -5664,7 +5664,8 @@ bool TextServerAdvanced::_shaped_text_update_breaks(const RID &p_shaped) {
i++;
}
int r_end = sd->spans[i].end;
UBreakIterator *bi = sd->_get_break_iterator_for_locale(language, &err);
UBreakIterator *bi = _create_line_break_iterator_for_locale(language, &err);
if (!U_FAILURE(err) && bi) {
ubrk_setText(bi, data + _convert_pos_inv(sd, r_start), _convert_pos_inv(sd, r_end - r_start), &err);
}
@ -5697,6 +5698,7 @@ bool TextServerAdvanced::_shaped_text_update_breaks(const RID &p_shaped) {
sd->break_inserts++;
}
}
ubrk_close(bi);
}
i++;
}
@ -6145,13 +6147,22 @@ _FORCE_INLINE_ void TextServerAdvanced::_add_featuers(const Dictionary &p_source
}
}
UBreakIterator *TextServerAdvanced::ShapedTextDataAdvanced::_get_break_iterator_for_locale(const String &p_language, UErrorCode *r_err) {
HashMap<String, UBreakIterator *>::Iterator key_value = line_break_iterators_per_language.find(p_language);
UBreakIterator *TextServerAdvanced::_create_line_break_iterator_for_locale(const String &p_language, UErrorCode *r_err) const {
// Creating UBreakIterator (ubrk_open) is surprisingly costly.
// However, cloning (ubrk_clone) is cheaper, so we keep around blueprints to accelerate creating new ones.
const String language = p_language.is_empty() ? TranslationServer::get_singleton()->get_tool_locale() : p_language;
_THREAD_SAFE_METHOD_
const HashMap<String, UBreakIterator *>::Iterator key_value = line_break_iterators_per_language.find(language);
if (key_value) {
return key_value->value;
return ubrk_clone(key_value->value, r_err);
}
UBreakIterator *brk = ubrk_open(UBRK_LINE, p_language.is_empty() ? TranslationServer::get_singleton()->get_tool_locale().ascii().get_data() : p_language.ascii().get_data(), nullptr, 0, r_err);
return line_break_iterators_per_language.insert(p_language, brk)->value;
UBreakIterator *bi = ubrk_open(UBRK_LINE, language.ascii().get_data(), nullptr, 0, r_err);
if (U_FAILURE(*r_err) || !bi) {
return nullptr;
}
line_break_iterators_per_language.insert(language, bi);
return ubrk_clone(bi, r_err);
}
void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_start, int64_t p_end, hb_script_t p_script, hb_direction_t p_direction, TypedArray<RID> p_fonts, int64_t p_span, int64_t p_fb_index, int64_t p_prev_start, int64_t p_prev_end, RID p_prev_font) {
@ -7729,6 +7740,9 @@ TextServerAdvanced::~TextServerAdvanced() {
uset_close(allowed);
allowed = nullptr;
}
for (const KeyValue<String, UBreakIterator *> &bi : line_break_iterators_per_language) {
ubrk_close(bi.value);
}
std::atexit(u_cleanup);
}

View File

@ -176,6 +176,10 @@ class TextServerAdvanced : public TextServerExtension {
mutable USpoofChecker *sc_spoof = nullptr;
mutable USpoofChecker *sc_conf = nullptr;
mutable HashMap<String, UBreakIterator *> line_break_iterators_per_language;
UBreakIterator *_create_line_break_iterator_for_locale(const String &p_language, UErrorCode *r_err) const;
// Font cache data.
#ifdef MODULE_FREETYPE_ENABLED
@ -545,11 +549,6 @@ class TextServerAdvanced : public TextServerExtension {
bool js_ops_valid = false;
bool chars_valid = false;
HashMap<String, UBreakIterator *> line_break_iterators_per_language;
// Creating UBreakIterator is surprisingly costly. To improve efficiency, we cache them.
UBreakIterator *_get_break_iterator_for_locale(const String &p_language, UErrorCode *r_err);
~ShapedTextDataAdvanced() {
for (int i = 0; i < bidi_iter.size(); i++) {
if (bidi_iter[i]) {
@ -562,9 +561,6 @@ class TextServerAdvanced : public TextServerExtension {
if (hb_buffer) {
hb_buffer_destroy(hb_buffer);
}
for (const KeyValue<String, UBreakIterator *> &bi : line_break_iterators_per_language) {
ubrk_close(bi.value);
}
}
};