performance optimizations
This commit is contained in:
parent
d6b39eb2c3
commit
a07e10344f
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
{
|
{
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -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 });
|
||||||
|
@ -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>
|
||||||
|
Loading…
Reference in New Issue
Block a user