Clean up state handling for ICU and iconv based codecs

Get rid of the hack for the FreeFunction and instead add a proper
function pointer to clear the data to the ConverterState struct.

Change-Id: I104aae1a4381c69f1a254713ec76e1aeaa862cdc
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
This commit is contained in:
Lars Knoll 2020-04-16 12:56:58 +02:00
parent a77b19a911
commit babcabfbc8
6 changed files with 39 additions and 40 deletions

View File

@ -121,7 +121,7 @@ void QIconvCodec::IconvState::saveChars(const char *c, int count)
static void qIconvCodecStateFree(QTextCodec::ConverterState *state)
{
delete reinterpret_cast<QIconvCodec::IconvState *>(state->d);
delete reinterpret_cast<QIconvCodec::IconvState *>(state->d[0]);
}
Q_GLOBAL_STATIC(QThreadStorage<QIconvCodec::IconvState *>, toUnicodeState)
@ -139,15 +139,14 @@ QString QIconvCodec::convertToUnicode(const char* chars, int len, ConverterState
if (convState) {
// stateful conversion
pstate = reinterpret_cast<IconvState **>(&convState->d);
if (convState->d) {
pstate = reinterpret_cast<IconvState **>(&convState->d[0]);
if (convState->d[0]) {
// restore state
remainingCount = convState->remainingChars;
remainingBuffer = (*pstate)->buffer;
} else {
// first time
convState->flags |= FreeFunction;
QTextCodecUnalignedPointer::encode(convState->state_data, qIconvCodecStateFree);
convState->clearFn = qIconvCodecStateFree;
}
} else {
QThreadStorage<QIconvCodec::IconvState *> *ts = toUnicodeState();

View File

@ -60,7 +60,7 @@ typedef QList<QByteArray>::ConstIterator ByteArrayListConstIt;
static void qIcuCodecStateFree(QTextCodec::ConverterState *state)
{
ucnv_close(static_cast<UConverter *>(state->d));
ucnv_close(static_cast<UConverter *>(state->d[0]));
}
bool qTextCodecNameMatch(const char *n, const char *h)
@ -569,18 +569,17 @@ UConverter *QIcuCodec::getConverter(QTextCodec::ConverterState *state) const
{
UConverter *conv = nullptr;
if (state) {
if (!state->d) {
if (!state->d[0]) {
// first time
state->flags |= QTextCodec::FreeFunction;
QTextCodecUnalignedPointer::encode(state->state_data, qIcuCodecStateFree);
state->clearFn = qIcuCodecStateFree;
UErrorCode error = U_ZERO_ERROR;
state->d = ucnv_open(m_name, &error);
ucnv_setSubstChars(static_cast<UConverter *>(state->d),
state->d[0] = ucnv_open(m_name, &error);
ucnv_setSubstChars(static_cast<UConverter *>(state->d[0]),
state->flags & QTextCodec::ConvertInvalidToNull ? "\0" : "?", 1, &error);
if (U_FAILURE(error))
qDebug("getConverter(state) ucnv_open failed %s %s", m_name, u_errorName(error));
}
conv = static_cast<UConverter *>(state->d);
conv = static_cast<UConverter *>(state->d[0]);
}
if (!conv) {
// stateless conversion

View File

@ -343,10 +343,19 @@ static void setup() {}
*/
QTextCodec::ConverterState::~ConverterState()
{
if (flags & FreeFunction)
(QTextCodecUnalignedPointer::decode(state_data))(this);
else if (d)
free(d);
clear();
}
void QTextCodec::ConverterState::clear()
{
if (clearFn)
clearFn(this);
remainingChars = 0;
invalidChars = 0;
state_data[0] = 0;
state_data[1] = 0;
state_data[2] = 0;
state_data[3] = 0;
}
/*!

View File

@ -92,20 +92,25 @@ public:
enum ConversionFlag {
DefaultConversion,
ConvertInvalidToNull = 0x80000000,
IgnoreHeader = 0x1,
FreeFunction = 0x2
IgnoreHeader = 0x1
};
Q_DECLARE_FLAGS(ConversionFlags, ConversionFlag)
struct Q_CORE_EXPORT ConverterState {
ConverterState(ConversionFlags f = DefaultConversion)
: flags(f), remainingChars(0), invalidChars(0), d(nullptr) { state_data[0] = state_data[1] = state_data[2] = 0; }
: flags(f), state_data{0, 0, 0, 0} {}
~ConverterState();
ConversionFlags flags;
int remainingChars;
int invalidChars;
uint state_data[3];
void *d;
int remainingChars = 0;
int invalidChars = 0;
union {
uint state_data[4];
void *d[2];
};
void clear();
using ClearDataFn = void (*)(ConverterState *);
ClearDataFn clearFn = nullptr;
private:
Q_DISABLE_COPY(ConverterState)
};

View File

@ -66,21 +66,6 @@ QT_BEGIN_NAMESPACE
typedef void (*QTextCodecStateFreeFunction)(QTextCodec::ConverterState*);
struct QTextCodecUnalignedPointer
{
static inline QTextCodecStateFreeFunction decode(const uint *src)
{
quintptr data;
memcpy(&data, src, sizeof(data));
return reinterpret_cast<QTextCodecStateFreeFunction>(data);
}
static inline void encode(uint *dst, QTextCodecStateFreeFunction fn)
{
quintptr data = reinterpret_cast<quintptr>(fn);
memcpy(dst, &data, sizeof(data));
}
};
bool qTextCodecNameMatch(const char *a, const char *b);
#else // without textcodec:

View File

@ -364,12 +364,14 @@ static void copyConverterStateHelper(QTextCodec::ConverterState *dest,
{
// ### QTextCodec::ConverterState's copy constructors and assignments are
// private. This function copies the structure manually.
Q_ASSERT(!src->d);
Q_ASSERT(!src->clearFn);
dest->flags = src->flags;
dest->remainingChars = src->remainingChars;
dest->invalidChars = src->invalidChars;
dest->state_data[0] = src->state_data[0];
dest->state_data[1] = src->state_data[1];
dest->state_data[2] = src->state_data[2];
dest->state_data[3] = src->state_data[3];
}
#endif
@ -787,7 +789,7 @@ inline void QTextStreamPrivate::consume(int size)
inline void QTextStreamPrivate::saveConverterState(qint64 newPos)
{
#if QT_CONFIG(textcodec)
if (readConverterState.d) {
if (readConverterState.clearFn) {
// converter cannot be copied, so don't save anything
// don't update readBufferStartDevicePos either
return;