diff options
Diffstat (limited to 'coding/transliteration.cpp')
-rw-r--r-- | coding/transliteration.cpp | 56 |
1 files changed, 43 insertions, 13 deletions
diff --git a/coding/transliteration.cpp b/coding/transliteration.cpp index ca2f84e643..618c602471 100644 --- a/coding/transliteration.cpp +++ b/coding/transliteration.cpp @@ -3,21 +3,35 @@ #include "base/logging.hpp" -#include <cstring> - #include "3party/icu/common/unicode/uclean.h" #include "3party/icu/common/unicode/unistr.h" #include "3party/icu/common/unicode/utypes.h" #include "3party/icu/i18n/unicode/translit.h" #include "3party/icu/i18n/unicode/utrans.h" +#include "std/unique_ptr.hpp" + +#include <atomic> +#include <cstring> +#include <mutex> + +struct Transliteration::TransliteratorInfo +{ + TransliteratorInfo() + : m_initialized(false) + {} + + std::atomic<bool> m_initialized; + std::mutex m_mutex; + std::unique_ptr<Transliterator> m_transliterator; +}; + Transliteration::~Transliteration() { // The use of u_cleanup() just before an application terminates is optional, // but it should be called only once for performance reasons. // The primary benefit is to eliminate reports of memory or resource leaks originating // in ICU code from the results generated by heap analysis tools. - // http://www.icu-project.org/apiref/icu4c/uclean_8h.html#a93f27d0ddc7c196a1da864763f2d8920 m_transliterators.clear(); u_cleanup(); } @@ -30,6 +44,9 @@ Transliteration & Transliteration::Instance() void Transliteration::Init(std::string const & icuDataDir) { + // This function should be called at most once in a process, + // before the first ICU operation that will require the loading of an ICU data file. + // This function is not thread-safe. Use it before calling ICU APIs from multiple threads. u_setDataDirectory(icuDataDir.c_str()); for (auto const & lang : StringUtf8Multilang::GetSupportedLanguages()) @@ -37,14 +54,7 @@ void Transliteration::Init(std::string const & icuDataDir) if (strlen(lang.m_transliteratorId) == 0 || m_transliterators.count(lang.m_transliteratorId) != 0) continue; - UErrorCode status = U_ZERO_ERROR; - std::unique_ptr<Transliterator> transliterator( - Transliterator::createInstance(lang.m_transliteratorId, UTRANS_FORWARD, status)); - - if (transliterator != nullptr) - m_transliterators.emplace(lang.m_transliteratorId, std::move(transliterator)); - else - LOG(LWARNING, ("Cannot create transliterator \"", lang.m_transliteratorId, "\", icu error =", status)); + m_transliterators.emplace(lang.m_transliteratorId, make_unique<TransliteratorInfo>()); } } @@ -58,15 +68,35 @@ bool Transliteration::Transliterate(std::string const & str, int8_t langCode, st if (transliteratorId.empty()) return false; - auto const & it = m_transliterators.find(transliteratorId); + auto it = m_transliterators.find(transliteratorId); if (it == m_transliterators.end()) { LOG(LWARNING, ("Transliteration failed, unknown transliterator \"", transliteratorId, "\"")); return false; } + if (!it->second->m_initialized) + { + std::lock_guard<std::mutex> lock(it->second->m_mutex); + if (!it->second->m_initialized) + { + UErrorCode status = U_ZERO_ERROR; + UnicodeString translitId(it->first.c_str()); + + it->second->m_transliterator.reset(Transliterator::createInstance(translitId, UTRANS_FORWARD, status)); + + if (it->second->m_transliterator == nullptr) + LOG(LWARNING, ("Cannot create transliterator \"", it->first, "\", icu error =", status)); + + it->second->m_initialized = true; + } + } + + if (it->second->m_transliterator == nullptr) + return false; + UnicodeString ustr(str.c_str()); - it->second->transliterate(ustr); + it->second->m_transliterator->transliterate(ustr); if (ustr.isEmpty()) return false; |