Textractor_test/vnr/texthook/textthread_p.cc

377 lines
10 KiB
C++
Raw Normal View History

// textthread_p.cc
// 6/6/2012 jichi
#include "texthook/textthread_p.h"
#include "texthook/texthook_p.h"
#include "winmutex/winmutex.h"
#include "wintimer/wintimer.h"
#include "host/textthread.h"
#include <QtCore/QRegExp>
//#define DEBUG "textthread_p.cc"
#include "sakurakit/skdebug.h"
enum { ITH_THREAD_NAME_CAPACITY = 0x200 }; // used internally by ITH
#define REPEAT_RX_1 "(.{2,})\\1+$" // The pattern has at least 2 bytes, and repeats at least once
/** Private class */
#define D_LOCK win_mutex_lock<D::mutex_type> d_lock(D::globalMutex) // Synchronized scope for accessing private data
class TextThreadDelegatePrivate
{
SK_CLASS(TextThreadDelegatePrivate)
SK_DISABLE_COPY(TextThreadDelegatePrivate)
public:
typedef win_mutex<CRITICAL_SECTION> mutex_type;
static mutex_type globalMutex; // Used only in public class. Because ITH is running in another single thread
static int globalCapacity; // maximum text size
static bool globalRemovesRepeat;
static bool globalKeepsSpace;
static bool globalWideCharacter;
TextThread *t;
WinTimer flushTimer; // as QTimer does not work with windows remote thread, use native WM_TIMER instead
ulong signature; // buffered
char sourceBuffer[ITH_THREAD_NAME_CAPACITY]; // buffered
QString source; // buffered
int bufferSize;
int bufferCapacity;
char *buffer;
bool removesRepeat;
QByteArray spaceBuffer;
int spaceCount;
struct Repeat
{
QRegExp rx; // cached
char *buffer; // repeated string
int size;
int pos; // >= 0, current pos of repeating string
int offset; // offset of repeated string
Repeat() : rx(REPEAT_RX_1), buffer(nullptr), size(0), pos(0), offset(-1) {}
~Repeat() { if (buffer) delete[] buffer; }
void clear()
{
size = pos = 0;
offset = -1;
}
bool isRepeating(const char *data, int len) const
{
if (!size || !buffer)
return false;
switch (len) {
case 1: return pos < size && buffer[pos] == *data;
case 2: return pos < size + 1 && buffer[pos] == data[0] && buffer[pos +1] == data[1];
default:
if (pos + len >= size)
return false;
for (int i = 0; i < len; i++)
if (buffer[pos + i] != data[i])
return false;
return true;
}
}
} repeat;
// - Construction -
public:
explicit TextThreadDelegatePrivate(TextThread *thread) : t(thread),
bufferSize(0), bufferCapacity(globalCapacity), buffer(new char[globalCapacity]),
spaceCount(0),
removesRepeat(false)
{
signature = signatureOf(t);
//size_t size =
t->GetThreadString(sourceBuffer, ITH_THREAD_NAME_CAPACITY);
source = sourceBuffer;
}
~TextThreadDelegatePrivate() { delete[] buffer; }
// - Properties -
public:
//QString text() const { return QString::fromLocal8Bit(buffer); }
//ulong context() const { return t->GetThreadParameter()->retn; }
//ulong subcontext() const { return t->GetThreadParameter()->spl; }
//ulong processId() const { return t->PID(); }
// - Actions -
public:
void flush()
{
if (flushTimer.isActive())
flushTimer.stop();
if (bufferSize) {
send();
bufferSize = 0;
}
if (!spaceBuffer.isEmpty())
spaceBuffer.clear();
spaceCount = 0;
}
void syncGlobal()
{
if (bufferCapacity < globalCapacity) {
delete[] buffer;
bufferCapacity = globalCapacity;
buffer = new char[bufferCapacity];
if (repeat.buffer) {
delete[] repeat.buffer;
if (!removesRepeat)
repeat.buffer = nullptr;
else {
char *largerBuffer = new char[bufferCapacity];
if (repeat.size)
qMemCopy(largerBuffer, repeat.buffer, repeat.size);
repeat.buffer = largerBuffer;
}
}
//bufferSize = repeatOffset = 0; // already reset in flush
//if (removesRepeat)
// repeat.reset();
}
if (removesRepeat != globalRemovesRepeat) {
removesRepeat = globalRemovesRepeat;
if (removesRepeat)
repeat.clear();
}
}
void appendSpace()
{
flushTimer.start();
spaceCount++;
if (spaceBuffer.isEmpty())
spaceBuffer.append(buffer, bufferSize);
spaceBuffer.append(' ');
if (globalWideCharacter)
spaceBuffer.append('\0'); // L' ' = {'\x20', '\0'};
}
void append(const char *data, int len)
{
flushTimer.start();
if (bufferSize < qMin(bufferCapacity, globalCapacity))
switch (len) {
case 1: buffer[bufferSize++] = *data; break;
case 2: buffer[bufferSize++] = *data;
if (bufferSize < bufferCapacity)
buffer[bufferSize++] = data[1];
break;
default:
{
int diff = qMin(len, bufferCapacity - bufferSize);
qMemCopy(buffer + bufferSize, data, diff);
bufferSize += diff;
}
}
if (!spaceBuffer.isEmpty())
spaceBuffer.append(data, len);
}
void appendRepeat(const char *data, int len)
{
if (bufferSize + len >= qMin(bufferCapacity, globalCapacity)) // overflow
return;
if (repeat.isRepeating(data, len)) {
repeat.pos += len;
if (repeat.pos >= repeat.size)
repeat.pos = 0;
return;
}
repeat.clear();
append(data, len);
if (bufferSize >= 6) { // at least 2 characters
// Use fromLatin1 to prevent the data from being decoded
QString t = QString::fromLatin1(buffer, bufferSize);
repeat.offset = repeat.rx.indexIn(t);
if (repeat.offset >= 0) {
repeat.size = repeat.rx.cap(1).size();
if (!repeat.buffer)
repeat.buffer = new char[bufferCapacity];
qMemCopy(repeat.buffer, buffer + repeat.offset, repeat.size);
//bufferSize = repeat.offset repeat.size;
}
}
}
private:
void send()
{
int size;
if (removesRepeat && repeat.offset >= 0 && repeat.size)
size = repeat.offset + repeat.size;
else
size = bufferSize;
if (!spaceBuffer.isEmpty() && spaceBuffer.size() != size)
spaceBuffer.truncate(size + spaceCount);
TextHookPrivate::sendData(
QByteArray(buffer, size), spaceBuffer,
signature, source);
}
static qint32 signatureOf(TextThread *t)
{
qint32 ret =
(t->GetThreadParameter()->retn & 0xffff) | // context
(t->GetThreadParameter()->spl & 0xffff) << 16; // subcontext
return ret ? ret : t->Addr();
}
//static QString sourceOf(TextThread *t);
public:
static ulong contextOf(TextThread *t)
{ return t->GetThreadParameter()->retn; }
static ulong subcontextOf(TextThread *t)
{ return t->GetThreadParameter()->spl; }
};
TextThreadDelegatePrivate::mutex_type TextThreadDelegatePrivate::globalMutex;
int TextThreadDelegatePrivate::globalCapacity = 512;
bool TextThreadDelegatePrivate::globalRemovesRepeat = false;
bool TextThreadDelegatePrivate::globalKeepsSpace = false;
bool TextThreadDelegatePrivate::globalWideCharacter = false;
//QString TextThreadDelegatePrivate::sourceOf(TextThread *t)
//{
// Q_ASSERT(t);
// QString ret;
// enum { buf_size = 0x200 }; // 0x200 is used by ITH internally
// wchar_t buf[buf_size];
// ulong len = t->GetThreadString(buf, buf_size);
// if (len)
// ret = QString::fromWCharArray(buf, len);
// return ret;
//}
/** Public class */
// - Constructions -
TextThreadDelegate::TextThreadDelegate(TextThread *t)
: d_(new D(t))
{
d_->flushTimer.setMethod(this, &Self::flush);
d_->flushTimer.setSingleShot(true);
}
TextThreadDelegate::~TextThreadDelegate()
{
if (d_->flushTimer.isActive())
d_->flushTimer.stop();
delete d_;
}
bool TextThreadDelegate::delegateOf(const Self *that) const
{
Q_ASSERT(t);
// Both have no context, and my subcontext is smaller
return that
&& !D::contextOf(that->d_->t) && !D::contextOf(d_->t)
&& D::subcontextOf(that->d_->t) >= D::subcontextOf(d_->t)
&& ::strcmp(d_->sourceBuffer, that->d_->sourceBuffer) == 0
&& nameEquals("Malie");
}
// - Properties -
//TextThread *TextThreadDelegate::t() const { return d_->t; }
int TextThreadDelegate::threadNumber() const
{ return d_->t->Number(); }
qint32 TextThreadDelegate::signature() const
{ return d_->signature; }
QString TextThreadDelegate::name() const
{ return d_->source; }
bool TextThreadDelegate::nameEquals(const char *that) const
{ return !::strcmp(d_->sourceBuffer, that); }
int TextThreadDelegate::capacity() { return D::globalCapacity; }
void TextThreadDelegate::setCapacity(int value) { D::globalCapacity = value; }
bool TextThreadDelegate::removesRepeat() { return D::globalRemovesRepeat; }
void TextThreadDelegate::setRemovesRepeat(bool value) { D::globalRemovesRepeat = value; }
bool TextThreadDelegate::wideCharacter() { return D::globalWideCharacter; }
void TextThreadDelegate::setWideCharacter(bool value) { D::globalWideCharacter = value; }
bool TextThreadDelegate::keepsSpace() { return D::globalKeepsSpace; }
void TextThreadDelegate::setKeepsSpace(bool value) { D::globalKeepsSpace = value; }
void TextThreadDelegate::setInterval(int msecs)
{ d_->flushTimer.setInterval(msecs); }
void TextThreadDelegate::setParentWindow(WId winId)
{ d_->flushTimer.setParentWindow(winId); }
// - Actions -
void TextThreadDelegate::flush()
{
D_LOCK;
d_->flush();
d_->syncGlobal();
}
void TextThreadDelegate::touch()
{
D_LOCK;
d_->flushTimer.start();
}
void TextThreadDelegate::append(const char *data, int len, bool space)
{
D_LOCK;
if (space && D::globalKeepsSpace)
d_->appendSpace();
if (data && len) {
if (d_->removesRepeat)
d_->appendRepeat(data, len);
else
d_->append(data, len);
}
}
// EOF
/*
void TextThreadDelegate::append(const QByteArray &data)
{
D::mutex_lock_type locker(D::mutex);
d_->flushTimer.start();
if (d_->buffer.size() <= D::capacity)
d_->buffer.append(data);
}
void TextThreadDelegatePrivate::send()
{
#ifdef DEBUG
qDebug()<< source()
<< t->Number()
<< t->PID()
<< QString::number(t->Addr(), 16)
<< QString::number(t->GetThreadParameter()->retn, 16)
<< QString::number(t->GetThreadParameter()->spl, 16)
<< QTextCodec::codecForName("SHIFT-JIS")->makeDecoder()->toUnicode(buffer);
#endif // DEBUG
}
*/