Skip to content

Commit

Permalink
QUnicodeTools: Use a global static to manage libthai state
Browse files Browse the repository at this point in the history
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
Pick-to: 6.5
Change-Id: I2610863f85f49f88e83f1fdaa200ea277c88c0ef
Reviewed-by: Mikołaj Boc <[email protected]>
Reviewed-by: Thiago Macieira <[email protected]>
Reviewed-by: Marc Mutz <[email protected]>
  • Loading branch information
eugmes committed Jan 17, 2023
1 parent 800ebd8 commit 5679f9f
Showing 1 changed file with 85 additions and 48 deletions.
133 changes: 85 additions & 48 deletions src/corelib/text/qunicodetools.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -1392,6 +1389,8 @@ static void indicAttributes(QChar::Script script, const char16_t *text, qsizetyp

}

#if QT_CONFIG(library)

#define LIBTHAI_MAJOR 0

/*
Expand All @@ -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);

// 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;

/* 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;

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");

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);

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"));
}
}
if (th_brk_find_breaks && th_next_cell && th_state)
return 1;
else
#endif
return 0;
}

~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.
ThBrk *m_state = 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;
};

} // unnamed namespace

Q_GLOBAL_STATIC(LibThai, g_libThai)

static void to_tis620(const char16_t *string, qsizetype len, char *cstr)
{
Expand Down Expand Up @@ -1473,8 +1500,9 @@ static void thaiAssignAttributes(const char16_t *string, qsizetype len, QCharAtt
qsizetype numbreaks, i;
struct thcell_t tis_cell;

if (!init_libthai())
return ;
LibThai *libThai = g_libThai;
if (!libThai || !libThai->isInitialized())
return;

if (len >= 128)
cstr = static_cast<char *>(malloc (len * sizeof(char) + 1));
Expand Down Expand Up @@ -1502,8 +1530,8 @@ 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),
break_positions, brp_size);
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;
attributes[break_positions[i]].wordStart = true;
Expand All @@ -1520,9 +1548,8 @@ 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(len - i), &tis_cell, true);

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)
Expand All @@ -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
}

/*
Expand Down

0 comments on commit 5679f9f

Please sign in to comment.