From 97e177e58d195f78ac103b3dd0b8ecedb1e07d4c Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 1 Jun 2012 17:51:06 +0200 Subject: Refactor the ICU code for QLocale Clean up the ICU code and make it thread-safe. Add a QIcuData structure to QLocalePrivate, that contains ICU specific data. Link against ICU directly, greatly simplifying the code. Also fix a bug in the locale specific case conversion code that would cause it to fail and fall back to the QString code if the output string was larger than the input. Change-Id: Ie67e5ea14fa204ebc5887d7aaeb1a4f3ecaf8697 Reviewed-by: Thiago Macieira --- src/corelib/tools/qlocale_icu.cpp | 205 +++++++------------------------------- 1 file changed, 35 insertions(+), 170 deletions(-) (limited to 'src/corelib/tools/qlocale_icu.cpp') diff --git a/src/corelib/tools/qlocale_icu.cpp b/src/corelib/tools/qlocale_icu.cpp index 42651aa745..97ce82108b 100644 --- a/src/corelib/tools/qlocale_icu.cpp +++ b/src/corelib/tools/qlocale_icu.cpp @@ -42,199 +42,56 @@ #include "qglobal.h" #include "qlibrary.h" #include "qdebug.h" +#include "qlocale_p.h" +#include "qmutex.h" #include "unicode/uversion.h" #include "unicode/ucol.h" +#include "unicode/uloc.h" +#include "unicode/ustring.h" QT_BEGIN_NAMESPACE -typedef UCollator *(*Ptr_ucol_open)(const char *loc, UErrorCode *status); -typedef void (*Ptr_ucol_close)(UCollator *coll); -typedef UCollationResult (*Ptr_ucol_strcoll)(const UCollator *coll, const UChar *source, int32_t sourceLength, const UChar *target, int32_t targetLength); typedef int32_t (*Ptr_u_strToCase)(UChar *dest, int32_t destCapacity, const UChar *src, int32_t srcLength, const char *locale, UErrorCode *pErrorCode); -static Ptr_ucol_open ptr_ucol_open = 0; -static Ptr_ucol_strcoll ptr_ucol_strcoll = 0; -static Ptr_ucol_close ptr_ucol_close = 0; -static Ptr_u_strToCase ptr_u_strToUpper = 0; -static Ptr_u_strToCase ptr_u_strToLower = 0; - -enum LibLoadStatus -{ - ErrorLoading = -1, - NotLoaded = 0, - Loaded = 1 -}; - -static LibLoadStatus status = NotLoaded; - -static UCollator *icuCollator = 0; - -namespace { -struct Libraries { - QLibrary libicui18n; - QLibrary libicuuc; - ~Libraries() - { - if (icuCollator) { - ptr_ucol_close(icuCollator); - icuCollator = 0; - } - - libicui18n.unload(); - libicuuc.unload(); - } -}; -} -Q_GLOBAL_STATIC(Libraries, icuLibraries) - -static bool loadIcuLibrary(QLibrary &lib, const QString &name) -{ -#ifdef Q_OS_WIN - // QLibrary on Windows does not use the version number, the libraries - // are named "icuin.dll", though. - lib.setFileName(name); -#else - // on Unix, we can use the version number - lib.setFileNameAndVersion(name, QStringLiteral(U_ICU_VERSION_SHORT)); -#endif - - // the ICU libraries appear to allocate global statics and not free them - // set the PreventUnloadHint so that we can unload the QLibrary object and - // delete it, but the libraries themselves remain in memory - lib.setLoadHints(QLibrary::PreventUnloadHint); - return lib.load(); -} - -// this function is NOT THREAD-SAFE! -bool qt_initIcu(const QString &localeString) -{ - if (status == ErrorLoading || !icuLibraries()) - return false; - - if (status == NotLoaded) { - - // resolve libicui18n -#ifdef Q_OS_WIN - // QLibrary on Windows does not use the version number, the libraries - // are named "icuin.dll", though. - // QStringLiteral should work here and will work when MSVC fully supports C++11 - // unfortunately, current versions have do not support proper string concatenation - QString libicui18nName = QLatin1String("icuin" U_ICU_VERSION_SHORT); - QString libicuucName = QLatin1String("icuuc" U_ICU_VERSION_SHORT); -#else - QString libicui18nName = QStringLiteral("icui18n"); - QString libicuucName = QStringLiteral("icuuc"); -#endif - QLibrary &lib = icuLibraries()->libicui18n; - if (!loadIcuLibrary(lib, libicui18nName)) { - qWarning("Unable to load library '%s' version " U_ICU_VERSION_SHORT ": %s", - qPrintable(libicui18nName), - qPrintable(lib.errorString())); - status = ErrorLoading; - return false; - } - - ptr_ucol_open = (Ptr_ucol_open)lib.resolve("ucol_open"); - ptr_ucol_close = (Ptr_ucol_close)lib.resolve("ucol_close"); - ptr_ucol_strcoll = (Ptr_ucol_strcoll)lib.resolve("ucol_strcoll"); - - if (!ptr_ucol_open || !ptr_ucol_close || !ptr_ucol_strcoll) { - // try again with decorated symbol names - ptr_ucol_open = (Ptr_ucol_open)lib.resolve("ucol_open" QT_STRINGIFY(U_ICU_VERSION_SUFFIX)); - ptr_ucol_close = (Ptr_ucol_close)lib.resolve("ucol_close" QT_STRINGIFY(U_ICU_VERSION_SUFFIX)); - ptr_ucol_strcoll = (Ptr_ucol_strcoll)lib.resolve("ucol_strcoll" QT_STRINGIFY(U_ICU_VERSION_SUFFIX)); - } - - if (!ptr_ucol_open || !ptr_ucol_close || !ptr_ucol_strcoll) { - ptr_ucol_open = 0; - ptr_ucol_close = 0; - ptr_ucol_strcoll = 0; - - qWarning("Unable to find symbols in '%s'.", qPrintable(libicui18nName)); - status = ErrorLoading; - return false; - } - - // resolve libicuuc - QLibrary &ucLib = icuLibraries()->libicuuc; - if (!loadIcuLibrary(ucLib, libicuucName)) { - qWarning("Unable to load library '%s' version " U_ICU_VERSION_SHORT ": %s", - qPrintable(libicuucName), - qPrintable(ucLib.errorString())); - status = ErrorLoading; - return false; - } - - ptr_u_strToUpper = (Ptr_u_strToCase)ucLib.resolve("u_strToUpper"); - ptr_u_strToLower = (Ptr_u_strToCase)ucLib.resolve("u_strToLower"); - - if (!ptr_u_strToUpper || !ptr_u_strToLower) { - ptr_u_strToUpper = (Ptr_u_strToCase)ucLib.resolve("u_strToUpper" QT_STRINGIFY(U_ICU_VERSION_SUFFIX)); - ptr_u_strToLower = (Ptr_u_strToCase)ucLib.resolve("u_strToLower" QT_STRINGIFY(U_ICU_VERSION_SUFFIX)); - } - - if (!ptr_u_strToUpper || !ptr_u_strToLower) { - ptr_u_strToUpper = 0; - ptr_u_strToLower = 0; - - qWarning("Unable to find symbols in '%s'", qPrintable(libicuucName)); - status = ErrorLoading; - return false; - } - - // success :) - status = Loaded; - } - - if (icuCollator) { - ptr_ucol_close(icuCollator); - icuCollator = 0; - } - - UErrorCode icuStatus = U_ZERO_ERROR; - icuCollator = ptr_ucol_open(localeString.toLatin1().constData(), &icuStatus); - - if (!icuCollator) { - qWarning("Unable to open locale %s in ICU, error code %d", qPrintable(localeString), icuStatus); - return false; - } - - return true; -} - -bool qt_ucol_strcoll(const QChar *source, int sourceLength, const QChar *target, int targetLength, int *result) +bool QIcu::strcoll(const QByteArray &localeID, + const QChar *source, int sourceLength, const QChar *target, int targetLength, int *result) { Q_ASSERT(result); Q_ASSERT(source); Q_ASSERT(target); - if (!icuCollator) + UErrorCode icuStatus = U_ZERO_ERROR; + UCollator *collator = ucol_open(localeID, &icuStatus); + + if (U_FAILURE((icuStatus))) return false; - *result = ptr_ucol_strcoll(icuCollator, reinterpret_cast(source), int32_t(sourceLength), - reinterpret_cast(target), int32_t(targetLength)); + *result = ucol_strcoll(collator, + reinterpret_cast(source), int32_t(sourceLength), + reinterpret_cast(target), int32_t(targetLength)); + + ucol_close(collator); return true; } // caseFunc can either be u_strToUpper or u_strToLower -static bool qt_u_strToCase(const QString &str, QString *out, const QLocale &locale, Ptr_u_strToCase caseFunc) +static bool qt_u_strToCase(const QString &str, QString *out, const char *localeID, Ptr_u_strToCase caseFunc) { Q_ASSERT(out); - if (!icuCollator) - return false; - - QString result(str.size(), Qt::Uninitialized); + int32_t size = str.size(); + size += size >> 2; // add 25% for possible expansions + QString result(size, Qt::Uninitialized); UErrorCode status = U_ZERO_ERROR; - int32_t size = caseFunc(reinterpret_cast(result.data()), result.size(), + size = caseFunc(reinterpret_cast(result.data()), result.size(), reinterpret_cast(str.constData()), str.size(), - locale.bcp47Name().toLatin1().constData(), &status); + localeID, &status); - if (U_FAILURE(status)) + if (U_FAILURE(status) && status != U_BUFFER_OVERFLOW_ERROR) return false; if (size < result.size()) { @@ -246,7 +103,7 @@ static bool qt_u_strToCase(const QString &str, QString *out, const QLocale &loca status = U_ZERO_ERROR; size = caseFunc(reinterpret_cast(result.data()), result.size(), reinterpret_cast(str.constData()), str.size(), - locale.bcp47Name().toLatin1().constData(), &status); + localeID, &status); if (U_FAILURE(status)) return false; @@ -260,14 +117,22 @@ static bool qt_u_strToCase(const QString &str, QString *out, const QLocale &loca return true; } -bool qt_u_strToUpper(const QString &str, QString *out, const QLocale &locale) +QString QIcu::toUpper(const QByteArray &localeID, const QString &str, bool *ok) { - return qt_u_strToCase(str, out, locale, ptr_u_strToUpper); + QString out; + bool err = qt_u_strToCase(str, &out, localeID, u_strToUpper); + if (ok) + *ok = err; + return out; } -bool qt_u_strToLower(const QString &str, QString *out, const QLocale &locale) +QString QIcu::toLower(const QByteArray &localeID, const QString &str, bool *ok) { - return qt_u_strToCase(str, out, locale, ptr_u_strToLower); + QString out; + bool err = qt_u_strToCase(str, &out, localeID, u_strToLower); + if (ok) + *ok = err; + return out; } QT_END_NAMESPACE -- cgit v1.2.3