performance optimizations

This commit is contained in:
Akash Mozumdar 2019-02-09 00:30:38 -05:00
parent d6b39eb2c3
commit a07e10344f
10 changed files with 70 additions and 70 deletions

View File

@ -12,57 +12,53 @@
namespace namespace
{ {
struct InfoForExtension
struct Extension
{ {
const char* name; std::wstring name;
int64_t value; wchar_t*(*callback)(wchar_t*, const InfoForExtension*);
InfoForExtension* next;
~InfoForExtension() { if (next) delete next; };
}; };
QHash<QString, wchar_t*(*)(wchar_t*, const InfoForExtension*)> extensions; std::vector<Extension> extensions;
QStringList extenNames;
std::shared_mutex extenMutex; std::shared_mutex extenMutex;
void Load(QString extenName) void Load(QString extenName)
{ {
if (extenName == ITH_DLL) return; if (extenName == ITH_DLL) return;
// Extension is dll and exports "OnNewSentence" // Extension is dll and exports "OnNewSentence"
if (FARPROC callback = GetProcAddress(LoadLibraryOnce(S(extenName)), "OnNewSentence")) if (auto callback = (decltype(Extension::callback))GetProcAddress(LoadLibraryOnce(S(extenName)), "OnNewSentence"))
{ {
std::scoped_lock writeLock(extenMutex); std::scoped_lock writeLock(extenMutex);
extensions[extenName] = (wchar_t*(*)(wchar_t*, const InfoForExtension*))callback; extensions.push_back({ S(extenName), callback });
extenNames.push_back(extenName);
} }
} }
void Unload(QString extenName) void Unload(int index)
{ {
std::scoped_lock writeLock(extenMutex); std::scoped_lock writeLock(extenMutex);
extenNames.erase(std::remove(extenNames.begin(), extenNames.end(), extenName), extenNames.end()); FreeLibrary(GetModuleHandleW(extensions.at(index).name.c_str()));
FreeLibrary(GetModuleHandleW(S(extenName).c_str())); extensions.erase(extensions.begin() + index);
} }
void Reorder(QStringList extenNames) void Reorder(QStringList extenNames)
{ {
std::scoped_lock writeLock(extenMutex); std::scoped_lock writeLock(extenMutex);
::extenNames = extenNames; std::vector<Extension> extensions;
for (auto extenName : extenNames)
extensions.push_back(*std::find_if(::extensions.begin(), ::extensions.end(), [&](auto extension) { return extension.name == S(extenName); }));
::extensions = extensions;
} }
} }
bool DispatchSentenceToExtensions(std::wstring& sentence, std::unordered_map<const char*, int64_t> miscInfo) bool DispatchSentenceToExtensions(std::wstring& sentence, const InfoForExtension* miscInfo)
{ {
wchar_t* sentenceBuffer = (wchar_t*)HeapAlloc(GetProcessHeap(), 0, (sentence.size() + 1) * sizeof(wchar_t)); wchar_t* sentenceBuffer = (wchar_t*)HeapAlloc(GetProcessHeap(), 0, (sentence.size() + 1) * sizeof(wchar_t));
wcscpy_s(sentenceBuffer, sentence.size() + 1, sentence.c_str()); wcscpy_s(sentenceBuffer, sentence.size() + 1, sentence.c_str());
InfoForExtension miscInfoLinkedList{ "", 0, nullptr };
InfoForExtension* miscInfoTraverser = &miscInfoLinkedList;
for (auto[name, value] : miscInfo) miscInfoTraverser = miscInfoTraverser->next = new InfoForExtension{ name, value, nullptr };
std::shared_lock readLock(extenMutex); std::shared_lock readLock(extenMutex);
for (auto extenName : extenNames) for (const auto& extension : extensions)
{ {
wchar_t* nextBuffer = extensions[extenName](sentenceBuffer, &miscInfoLinkedList); wchar_t* nextBuffer = extension.callback(sentenceBuffer, miscInfo);
if (nextBuffer != sentenceBuffer) HeapFree(GetProcessHeap(), 0, sentenceBuffer); if (nextBuffer != sentenceBuffer) HeapFree(GetProcessHeap(), 0, sentenceBuffer);
if (nextBuffer == nullptr) return false; if (nextBuffer == nullptr) return false;
sentenceBuffer = nextBuffer; sentenceBuffer = nextBuffer;
@ -99,10 +95,10 @@ void ExtenWindow::Sync()
ui->extenList->clear(); ui->extenList->clear();
QTextFile extenSaveFile(EXTEN_SAVE_FILE, QIODevice::WriteOnly | QIODevice::Truncate); QTextFile extenSaveFile(EXTEN_SAVE_FILE, QIODevice::WriteOnly | QIODevice::Truncate);
std::shared_lock readLock(extenMutex); std::shared_lock readLock(extenMutex);
for (auto extenName : extenNames) for (auto extension : extensions)
{ {
ui->extenList->addItem(extenName); ui->extenList->addItem(S(extension.name));
extenSaveFile.write((extenName + ">").toUtf8()); extenSaveFile.write((S(extension.name) + ">").toUtf8());
} }
} }
@ -129,9 +125,9 @@ bool ExtenWindow::eventFilter(QObject* target, QEvent* event)
void ExtenWindow::keyPressEvent(QKeyEvent* event) void ExtenWindow::keyPressEvent(QKeyEvent* event)
{ {
if (event->key() == Qt::Key_Delete) if (auto extenName = ui->extenList->currentItem()) if (event->key() == Qt::Key_Delete && ui->extenList->currentItem() != nullptr)
{ {
Unload(extenName->text()); Unload(ui->extenList->currentIndex().row());
Sync(); Sync();
} }
} }

View File

@ -7,7 +7,13 @@ namespace Ui
class ExtenWindow; class ExtenWindow;
} }
bool DispatchSentenceToExtensions(std::wstring& sentence, std::unordered_map<const char*, int64_t> miscInfo); struct InfoForExtension
{
const char* name;
int64_t value;
};
bool DispatchSentenceToExtensions(std::wstring& sentence, const InfoForExtension* miscInfo);
class ExtenWindow : public QMainWindow class ExtenWindow : public QMainWindow
{ {

View File

@ -151,7 +151,7 @@ namespace Host
SetWindowsHookExW(WH_GETMESSAGE, [](int statusCode, WPARAM wParam, LPARAM lParam) SetWindowsHookExW(WH_GETMESSAGE, [](int statusCode, WPARAM wParam, LPARAM lParam)
{ {
if (statusCode == HC_ACTION && wParam == PM_REMOVE && ((MSG*)lParam)->message == WM_CLIPBOARDUPDATE) if (statusCode == HC_ACTION && wParam == PM_REMOVE && ((MSG*)lParam)->message == WM_CLIPBOARDUPDATE)
if (auto text = Util::GetClipboardText()) GetThread(clipboard).AddSentence(text.value()); if (auto text = Util::GetClipboardText()) GetThread(clipboard).AddSentence(std::move(text.value()));
return CallNextHookEx(NULL, statusCode, wParam, lParam); return CallNextHookEx(NULL, statusCode, wParam, lParam);
}, NULL, GetCurrentThreadId()); }, NULL, GetCurrentThreadId());
} }
@ -219,6 +219,6 @@ namespace Host
void AddConsoleOutput(std::wstring text) void AddConsoleOutput(std::wstring text)
{ {
GetThread(console).AddSentence(text); GetThread(console).AddSentence(std::move(text));
} }
} }

View File

@ -21,23 +21,23 @@ void TextThread::Stop()
timer = NULL; timer = NULL;
} }
void TextThread::AddSentence(const std::wstring& sentence) void TextThread::AddSentence(std::wstring&& sentence)
{ {
queuedSentences->push_back(sentence); queuedSentences->emplace_back(std::move(sentence));
} }
void TextThread::Push(const BYTE* data, int len) void TextThread::Push(const BYTE* data, int length)
{ {
if (len < 0) return; if (length < 0) return;
std::scoped_lock lock(bufferMutex); std::scoped_lock lock(bufferMutex);
BYTE doubleByteChar[2]; BYTE doubleByteChar[2];
if (len == 1) // doublebyte characters must be processed as pairs if (length == 1) // doublebyte characters must be processed as pairs
if (leadByte) std::tie(doubleByteChar[0], doubleByteChar[1], data, len, leadByte) = std::tuple(leadByte, data[0], doubleByteChar, 2, 0 ); if (leadByte) std::tie(doubleByteChar[0], doubleByteChar[1], data, length, leadByte) = std::tuple(leadByte, data[0], doubleByteChar, 2, 0 );
else if (IsDBCSLeadByteEx(hp.codepage ? hp.codepage : Host::defaultCodepage, data[0])) std::tie(leadByte, len) = std::tuple(data[0], 0); else if (IsDBCSLeadByteEx(hp.codepage ? hp.codepage : Host::defaultCodepage, data[0])) std::tie(leadByte, length) = std::tuple(data[0], 0);
if (hp.type & USING_UNICODE) buffer += std::wstring((wchar_t*)data, len / 2); if (hp.type & USING_UNICODE) buffer += std::wstring((wchar_t*)data, length / sizeof(wchar_t));
else if (auto converted = Util::StringToWideString(std::string((char*)data, len), hp.codepage ? hp.codepage : Host::defaultCodepage)) buffer += converted.value(); else if (auto converted = Util::StringToWideString(std::string((char*)data, length), hp.codepage ? hp.codepage : Host::defaultCodepage)) buffer += converted.value();
else Host::AddConsoleOutput(INVALID_CODEPAGE); else Host::AddConsoleOutput(INVALID_CODEPAGE);
lastPushTime = GetTickCount(); lastPushTime = GetTickCount();
@ -47,7 +47,7 @@ void TextThread::Push(const BYTE* data, int len)
if (Util::RemoveRepetition(buffer)) // sentence repetition detected, which means the entire sentence has already been received if (Util::RemoveRepetition(buffer)) // sentence repetition detected, which means the entire sentence has already been received
{ {
repeatingChars = std::unordered_set(buffer.begin(), buffer.end()); repeatingChars = std::unordered_set(buffer.begin(), buffer.end());
AddSentence(buffer); AddSentence(std::move(buffer));
buffer.clear(); buffer.clear();
} }
} }
@ -64,7 +64,7 @@ void TextThread::Flush()
if (buffer.empty()) return; if (buffer.empty()) return;
if (buffer.size() > maxBufferSize || GetTickCount() - lastPushTime > flushDelay) if (buffer.size() > maxBufferSize || GetTickCount() - lastPushTime > flushDelay)
{ {
AddSentence(buffer); AddSentence(std::move(buffer));
buffer.clear(); buffer.clear();
} }
} }

View File

@ -17,8 +17,8 @@ public:
void Start(); void Start();
void Stop(); void Stop();
void AddSentence(const std::wstring& sentence); void AddSentence(std::wstring&& sentence);
void Push(const BYTE* data, int len); void Push(const BYTE* data, int length);
ThreadSafe<std::wstring> storage; ThreadSafe<std::wstring> storage;
const int64_t handle; const int64_t handle;

View File

@ -55,6 +55,7 @@ MainWindow::MainWindow(QWidget *parent) :
[this](TextThread& thread) { ThreadRemoved(thread); }, [this](TextThread& thread) { ThreadRemoved(thread); },
[this](TextThread& thread, std::wstring& output) { return SentenceReceived(thread, output); } [this](TextThread& thread, std::wstring& output) { return SentenceReceived(thread, output); }
); );
current = &Host::GetThread(Host::console);
Host::AddConsoleOutput(ABOUT); Host::AddConsoleOutput(ABOUT);
std::thread([] std::thread([]
@ -144,22 +145,15 @@ void MainWindow::ThreadRemoved(TextThread& thread)
bool MainWindow::SentenceReceived(TextThread& thread, std::wstring& sentence) bool MainWindow::SentenceReceived(TextThread& thread, std::wstring& sentence)
{ {
if (DispatchSentenceToExtensions(sentence, GetMiscInfo(thread))) if (!DispatchSentenceToExtensions(sentence, GetMiscInfo(thread).data())) return false;
sentence += L'\n';
if (current == &thread) QMetaObject::invokeMethod(this, [this, sentence]
{ {
sentence += L"\n"; ui->textOutput->moveCursor(QTextCursor::End);
QString ttString = TextThreadString(thread); ui->textOutput->insertPlainText(S(sentence));
QMetaObject::invokeMethod(this, [this, ttString, sentence] ui->textOutput->moveCursor(QTextCursor::End);
{ });
if (ui->ttCombo->currentText().startsWith(ttString)) return true;
{
ui->textOutput->moveCursor(QTextCursor::End);
ui->textOutput->insertPlainText(S(sentence));
ui->textOutput->moveCursor(QTextCursor::End);
}
});
return true;
}
return false;
} }
QString MainWindow::TextThreadString(TextThread& thread) QString MainWindow::TextThreadString(TextThread& thread)
@ -184,17 +178,18 @@ DWORD MainWindow::GetSelectedProcessId()
return ui->processCombo->currentText().split(":")[0].toULong(nullptr, 16); return ui->processCombo->currentText().split(":")[0].toULong(nullptr, 16);
} }
std::unordered_map<const char*, int64_t> MainWindow::GetMiscInfo(TextThread& thread) std::array<InfoForExtension, 10> MainWindow::GetMiscInfo(TextThread& thread)
{ {
return return
{ { {
{ "current select", ui->ttCombo->currentText().startsWith(TextThreadString(thread)) }, { "current select", &thread == current },
{ "text number", thread.handle }, { "text number", thread.handle },
{ "process id", thread.tp.processId }, { "process id", thread.tp.processId },
{ "hook address", thread.tp.addr }, { "hook address", (int64_t)thread.tp.addr },
{ "text handle", thread.handle }, { "text handle", thread.handle },
{ "text name", (int64_t)thread.name.c_str() } { "text name", (int64_t)thread.name.c_str() },
}; { nullptr, 0 } // nullptr marks end of info array
} };
} }
void MainWindow::AttachProcess() void MainWindow::AttachProcess()
@ -339,6 +334,6 @@ void MainWindow::Extensions()
void MainWindow::ViewThread(int index) void MainWindow::ViewThread(int index)
{ {
ui->ttCombo->setCurrentIndex(index); ui->ttCombo->setCurrentIndex(index);
ui->textOutput->setPlainText(S(Host::GetThread(ParseTextThreadString(ui->ttCombo->itemText(index))).storage->c_str())); ui->textOutput->setPlainText(S((current = &Host::GetThread(ParseTextThreadString(ui->ttCombo->itemText(index))))->storage->c_str()));
ui->textOutput->moveCursor(QTextCursor::End); ui->textOutput->moveCursor(QTextCursor::End);
} }

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "qtcommon.h" #include "qtcommon.h"
#include "extenwindow.h"
#include "host/host.h" #include "host/host.h"
namespace Ui namespace Ui
@ -26,7 +27,7 @@ private:
QString TextThreadString(TextThread& thread); QString TextThreadString(TextThread& thread);
ThreadParam ParseTextThreadString(QString ttString); ThreadParam ParseTextThreadString(QString ttString);
DWORD GetSelectedProcessId(); DWORD GetSelectedProcessId();
std::unordered_map<const char*, int64_t> GetMiscInfo(TextThread& thread); std::array<InfoForExtension, 10> GetMiscInfo(TextThread& thread);
void AttachProcess(); void AttachProcess();
void LaunchProcess(); void LaunchProcess();
void DetachProcess(); void DetachProcess();
@ -40,4 +41,5 @@ private:
QWidget* extenWindow; QWidget* extenWindow;
std::pair<uint64_t, uint64_t> savedThreadCtx; std::pair<uint64_t, uint64_t> savedThreadCtx;
wchar_t savedThreadCode[1000]; wchar_t savedThreadCode[1000];
std::atomic<TextThread*> current;
}; };

View File

@ -6,16 +6,15 @@ struct InfoForExtension
{ {
const char* name; const char* name;
int64_t value; int64_t value;
InfoForExtension* next;
}; };
struct SentenceInfo struct SentenceInfo
{ {
const InfoForExtension* list; const InfoForExtension* infoArray;
// Traverse linked list to find info. // nullptr marks end of info array
int64_t operator[](std::string propertyName) int64_t operator[](std::string propertyName)
{ {
for (auto i = list; i != nullptr; i = i->next) if (propertyName == i->name) return i->value; for (auto info = infoArray; info->name != nullptr; ++info) if (propertyName == info->name) return info->value;
throw; throw;
} }
}; };

View File

@ -61,7 +61,7 @@ TEST(
RemoveCyclicRepeats(cyclicRepeats); RemoveCyclicRepeats(cyclicRepeats);
assert(cyclicRepeats == L"abcdefg"); assert(cyclicRepeats == L"abcdefg");
InfoForExtension tester{ "hook address", 0, nullptr }; InfoForExtension tester{ "hook address", 0 };
std::wstring empty = L"", one = L" ", normal = L"This is a normal sentence. はい"; std::wstring empty = L"", one = L" ", normal = L"This is a normal sentence. はい";
ProcessSentence(empty, { &tester }); ProcessSentence(empty, { &tester });
ProcessSentence(one, { &tester }); ProcessSentence(one, { &tester });

View File

@ -4,6 +4,7 @@
#include <Windows.h> #include <Windows.h>
#include <string> #include <string>
#include <vector> #include <vector>
#include <array>
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>
#include <functional> #include <functional>
@ -14,5 +15,6 @@
#include <thread> #include <thread>
#include <mutex> #include <mutex>
#include <shared_mutex> #include <shared_mutex>
#include <atomic>
#include <cstdint> #include <cstdint>
#include <cassert> #include <cassert>