QUnicodeTools: Use a global static to manage libthai state

Move all libthai symbol resolution and state management into a single
class. Create a single global static instance of this class. This allows
freeing of the state on program exit.

Task-number: QTBUG-105544
Change-Id: I2610863f85f49f88e83f1fdaa200ea277c88c0ef
Reviewed-by: Mikołaj Boc <Mikolaj.Boc@qt.io>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Reviewed-by: Marc Mutz <marc.mutz@qt.io>
(cherry picked from commit 5679f9f82c0dc19963edafe86d181dd9241fda2e)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Ievgenii Meshcheriakov 2023-01-12 15:12:11 +01:00 committed by Qt Cherry-pick Bot
parent 89672efd58
commit 2f96d72f4d

View File

@ -3,15 +3,12 @@
#include "qunicodetools_p.h"
#include "qmutex.h"
#include "qunicodetables_p.h"
#include "qvarlengtharray.h"
#if QT_CONFIG(library)
#include "qlibrary.h"
#endif
#include <mutex>
#include <limits.h>
#define FLAG(x) (1 << (x))
@ -1392,6 +1389,8 @@ static void indicAttributes(QChar::Script script, const char16_t *text, qsizetyp
}
#if QT_CONFIG(library)
#define LIBTHAI_MAJOR 0
/*
@ -1402,46 +1401,74 @@ struct thcell_t {
unsigned char hilo; /**< upper/lower vowel/diacritic */
unsigned char top; /**< top-level mark */
};
typedef struct _ThBrk ThBrk;
typedef ThBrk *(*th_brk_new_def)(const char *);
typedef int (*th_brk_find_breaks_def)(ThBrk *, const unsigned char *, int *, size_t);
typedef size_t (*th_next_cell_def) (const unsigned char *, size_t, struct thcell_t *, int);
using ThBrk = struct _ThBrk;
namespace {
class LibThai final
{
Q_DISABLE_COPY_MOVE(LibThai)
using th_brk_new_def = ThBrk *(*)(const char *);
using th_brk_delete_def = void (*)(ThBrk *);
using th_brk_find_breaks_def = int (*)(ThBrk *, const unsigned char *, int *, size_t);
using th_next_cell_def = size_t (*)(const unsigned char *, size_t, struct thcell_t *, int);
public:
LibThai() : m_library("thai"_L1, LIBTHAI_MAJOR)
{
m_th_brk_find_breaks =
reinterpret_cast<th_brk_find_breaks_def>(m_library.resolve("th_brk_find_breaks"));
m_th_next_cell = reinterpret_cast<th_next_cell_def>(m_library.resolve("th_next_cell"));
auto th_brk_new = reinterpret_cast<th_brk_new_def>(m_library.resolve("th_brk_new"));
if (th_brk_new) {
m_state = th_brk_new(nullptr);
m_th_brk_delete =
reinterpret_cast<th_brk_delete_def>(m_library.resolve("th_brk_delete"));
}
}
~LibThai()
{
if (m_state && m_th_brk_delete)
m_th_brk_delete(m_state);
m_library.unload();
}
bool isInitialized() const { return m_th_brk_find_breaks && m_th_next_cell && m_state; }
int brk_find_breaks(const unsigned char *s, int *pos, size_t pos_sz) const
{
Q_ASSERT(m_state);
Q_ASSERT(m_th_brk_find_breaks);
return m_th_brk_find_breaks(m_state, s, pos, pos_sz);
}
size_t next_cell(const unsigned char *s, size_t len, struct thcell_t *cell, int is_decomp_am)
{
Q_ASSERT(m_th_next_cell);
return m_th_next_cell(s, len, cell, is_decomp_am);
}
private:
QLibrary m_library;
// Global state for th_brk_find_breaks().
// Note: even if signature for th_brk_find_breaks() suggests otherwise, the
// state is read-only, and so it is safe to use it from multiple threads after
// initialization. This is also stated in the libthai documentation.
Q_CONSTINIT static ThBrk *th_state = nullptr;
ThBrk *m_state = nullptr;
/* libthai related function handles */
Q_CONSTINIT static th_brk_find_breaks_def th_brk_find_breaks = nullptr;
Q_CONSTINIT static th_next_cell_def th_next_cell = nullptr;
th_brk_find_breaks_def m_th_brk_find_breaks = nullptr;
th_next_cell_def m_th_next_cell = nullptr;
th_brk_delete_def m_th_brk_delete = nullptr;
};
static int init_libthai() {
#if QT_CONFIG(library)
Q_CONSTINIT static QBasicAtomicInt initialized = Q_BASIC_ATOMIC_INITIALIZER(false);
Q_CONSTINIT static QBasicMutex mutex;
if (!initialized.loadAcquire()) {
const auto locker = std::scoped_lock(mutex);
if (!initialized.loadAcquire()) {
th_brk_find_breaks = reinterpret_cast<th_brk_find_breaks_def>(
QLibrary::resolve("thai"_L1, LIBTHAI_MAJOR, "th_brk_find_breaks"));
th_next_cell = (th_next_cell_def)QLibrary::resolve("thai"_L1, LIBTHAI_MAJOR, "th_next_cell");
} // unnamed namespace
auto th_brk_new = reinterpret_cast<th_brk_new_def>(
QLibrary::resolve("thai"_L1, LIBTHAI_MAJOR, "th_brk_new"));
if (th_brk_new)
th_state = th_brk_new(nullptr);
initialized.storeRelease(true);
}
}
if (th_brk_find_breaks && th_next_cell && th_state)
return 1;
else
#endif
return 0;
}
Q_GLOBAL_STATIC(LibThai, g_libThai)
static void to_tis620(const char16_t *string, qsizetype len, char *cstr)
{
@ -1473,7 +1500,8 @@ static void thaiAssignAttributes(const char16_t *string, qsizetype len, QCharAtt
qsizetype numbreaks, i;
struct thcell_t tis_cell;
if (!init_libthai())
LibThai *libThai = g_libThai;
if (!libThai || !libThai->isInitialized())
return;
if (len >= 128)
@ -1502,7 +1530,7 @@ static void thaiAssignAttributes(const char16_t *string, qsizetype len, QCharAtt
attributes[0].wordBreak = true;
attributes[0].wordStart = true;
attributes[0].wordEnd = false;
numbreaks = th_brk_find_breaks(th_state, reinterpret_cast<const unsigned char *>(cstr),
numbreaks = libThai->brk_find_breaks(reinterpret_cast<const unsigned char *>(cstr),
break_positions, brp_size);
for (i = 0; i < numbreaks; ++i) {
attributes[break_positions[i]].wordBreak = true;
@ -1520,10 +1548,9 @@ static void thaiAssignAttributes(const char16_t *string, qsizetype len, QCharAtt
/* manage grapheme boundaries */
i = 0;
while (i < len) {
size_t cell_length = th_next_cell(reinterpret_cast<const unsigned char *>(cstr) + i,
size_t cell_length = libThai->next_cell(reinterpret_cast<const unsigned char *>(cstr) + i,
size_t(len - i), &tis_cell, true);
attributes[i].graphemeBoundary = true;
for (size_t j = 1; j < cell_length; ++j)
attributes[i + j].graphemeBoundary = false;
@ -1535,13 +1562,23 @@ static void thaiAssignAttributes(const char16_t *string, qsizetype len, QCharAtt
free(cstr);
}
#endif // QT_CONFIG(library)
static void thaiAttributes(QChar::Script script, const char16_t *text, qsizetype from, qsizetype len, QCharAttributes *attributes)
{
assert(script == QChar::Script_Thai);
#if QT_CONFIG(library)
const char16_t *uc = text + from;
attributes += from;
Q_UNUSED(script);
thaiAssignAttributes(uc, len, attributes);
#else
Q_UNUSED(script);
Q_UNUSED(text);
Q_UNUSED(from);
Q_UNUSED(len);
Q_UNUSED(attributes);
#endif
}
/*