mirror of
https://github.com/Artikash/Textractor.git
synced 2024-12-23 08:54:12 +08:00
performance optimizations
This commit is contained in:
parent
d6b39eb2c3
commit
a07e10344f
@ -12,57 +12,53 @@
|
||||
|
||||
namespace
|
||||
{
|
||||
struct InfoForExtension
|
||||
|
||||
struct Extension
|
||||
{
|
||||
const char* name;
|
||||
int64_t value;
|
||||
InfoForExtension* next;
|
||||
~InfoForExtension() { if (next) delete next; };
|
||||
std::wstring name;
|
||||
wchar_t*(*callback)(wchar_t*, const InfoForExtension*);
|
||||
};
|
||||
|
||||
QHash<QString, wchar_t*(*)(wchar_t*, const InfoForExtension*)> extensions;
|
||||
QStringList extenNames;
|
||||
std::vector<Extension> extensions;
|
||||
std::shared_mutex extenMutex;
|
||||
|
||||
void Load(QString extenName)
|
||||
{
|
||||
if (extenName == ITH_DLL) return;
|
||||
// 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);
|
||||
extensions[extenName] = (wchar_t*(*)(wchar_t*, const InfoForExtension*))callback;
|
||||
extenNames.push_back(extenName);
|
||||
extensions.push_back({ S(extenName), callback });
|
||||
}
|
||||
}
|
||||
|
||||
void Unload(QString extenName)
|
||||
void Unload(int index)
|
||||
{
|
||||
std::scoped_lock writeLock(extenMutex);
|
||||
extenNames.erase(std::remove(extenNames.begin(), extenNames.end(), extenName), extenNames.end());
|
||||
FreeLibrary(GetModuleHandleW(S(extenName).c_str()));
|
||||
FreeLibrary(GetModuleHandleW(extensions.at(index).name.c_str()));
|
||||
extensions.erase(extensions.begin() + index);
|
||||
}
|
||||
|
||||
void Reorder(QStringList extenNames)
|
||||
{
|
||||
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));
|
||||
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);
|
||||
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 == nullptr) return false;
|
||||
sentenceBuffer = nextBuffer;
|
||||
@ -99,10 +95,10 @@ void ExtenWindow::Sync()
|
||||
ui->extenList->clear();
|
||||
QTextFile extenSaveFile(EXTEN_SAVE_FILE, QIODevice::WriteOnly | QIODevice::Truncate);
|
||||
std::shared_lock readLock(extenMutex);
|
||||
for (auto extenName : extenNames)
|
||||
for (auto extension : extensions)
|
||||
{
|
||||
ui->extenList->addItem(extenName);
|
||||
extenSaveFile.write((extenName + ">").toUtf8());
|
||||
ui->extenList->addItem(S(extension.name));
|
||||
extenSaveFile.write((S(extension.name) + ">").toUtf8());
|
||||
}
|
||||
}
|
||||
|
||||
@ -129,9 +125,9 @@ bool ExtenWindow::eventFilter(QObject* target, QEvent* 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();
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,13 @@ namespace Ui
|
||||
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
|
||||
{
|
||||
|
@ -151,7 +151,7 @@ namespace Host
|
||||
SetWindowsHookExW(WH_GETMESSAGE, [](int statusCode, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
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);
|
||||
}, NULL, GetCurrentThreadId());
|
||||
}
|
||||
@ -219,6 +219,6 @@ namespace Host
|
||||
|
||||
void AddConsoleOutput(std::wstring text)
|
||||
{
|
||||
GetThread(console).AddSentence(text);
|
||||
GetThread(console).AddSentence(std::move(text));
|
||||
}
|
||||
}
|
||||
|
@ -21,23 +21,23 @@ void TextThread::Stop()
|
||||
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);
|
||||
|
||||
BYTE doubleByteChar[2];
|
||||
if (len == 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 );
|
||||
else if (IsDBCSLeadByteEx(hp.codepage ? hp.codepage : Host::defaultCodepage, data[0])) std::tie(leadByte, len) = std::tuple(data[0], 0);
|
||||
if (length == 1) // doublebyte characters must be processed as pairs
|
||||
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, length) = std::tuple(data[0], 0);
|
||||
|
||||
if (hp.type & USING_UNICODE) buffer += std::wstring((wchar_t*)data, len / 2);
|
||||
else if (auto converted = Util::StringToWideString(std::string((char*)data, len), hp.codepage ? hp.codepage : Host::defaultCodepage)) buffer += converted.value();
|
||||
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, length), hp.codepage ? hp.codepage : Host::defaultCodepage)) buffer += converted.value();
|
||||
else Host::AddConsoleOutput(INVALID_CODEPAGE);
|
||||
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
|
||||
{
|
||||
repeatingChars = std::unordered_set(buffer.begin(), buffer.end());
|
||||
AddSentence(buffer);
|
||||
AddSentence(std::move(buffer));
|
||||
buffer.clear();
|
||||
}
|
||||
}
|
||||
@ -64,7 +64,7 @@ void TextThread::Flush()
|
||||
if (buffer.empty()) return;
|
||||
if (buffer.size() > maxBufferSize || GetTickCount() - lastPushTime > flushDelay)
|
||||
{
|
||||
AddSentence(buffer);
|
||||
AddSentence(std::move(buffer));
|
||||
buffer.clear();
|
||||
}
|
||||
}
|
||||
|
@ -17,8 +17,8 @@ public:
|
||||
|
||||
void Start();
|
||||
void Stop();
|
||||
void AddSentence(const std::wstring& sentence);
|
||||
void Push(const BYTE* data, int len);
|
||||
void AddSentence(std::wstring&& sentence);
|
||||
void Push(const BYTE* data, int length);
|
||||
|
||||
ThreadSafe<std::wstring> storage;
|
||||
const int64_t handle;
|
||||
|
@ -55,6 +55,7 @@ MainWindow::MainWindow(QWidget *parent) :
|
||||
[this](TextThread& thread) { ThreadRemoved(thread); },
|
||||
[this](TextThread& thread, std::wstring& output) { return SentenceReceived(thread, output); }
|
||||
);
|
||||
current = &Host::GetThread(Host::console);
|
||||
Host::AddConsoleOutput(ABOUT);
|
||||
|
||||
std::thread([]
|
||||
@ -144,22 +145,15 @@ void MainWindow::ThreadRemoved(TextThread& thread)
|
||||
|
||||
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";
|
||||
QString ttString = TextThreadString(thread);
|
||||
QMetaObject::invokeMethod(this, [this, ttString, sentence]
|
||||
{
|
||||
if (ui->ttCombo->currentText().startsWith(ttString))
|
||||
{
|
||||
ui->textOutput->moveCursor(QTextCursor::End);
|
||||
ui->textOutput->insertPlainText(S(sentence));
|
||||
ui->textOutput->moveCursor(QTextCursor::End);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
ui->textOutput->moveCursor(QTextCursor::End);
|
||||
ui->textOutput->insertPlainText(S(sentence));
|
||||
ui->textOutput->moveCursor(QTextCursor::End);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
QString MainWindow::TextThreadString(TextThread& thread)
|
||||
@ -184,17 +178,18 @@ DWORD MainWindow::GetSelectedProcessId()
|
||||
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
|
||||
{
|
||||
{ "current select", ui->ttCombo->currentText().startsWith(TextThreadString(thread)) },
|
||||
{ {
|
||||
{ "current select", &thread == current },
|
||||
{ "text number", thread.handle },
|
||||
{ "process id", thread.tp.processId },
|
||||
{ "hook address", thread.tp.addr },
|
||||
{ "hook address", (int64_t)thread.tp.addr },
|
||||
{ "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()
|
||||
@ -339,6 +334,6 @@ void MainWindow::Extensions()
|
||||
void MainWindow::ViewThread(int 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);
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "qtcommon.h"
|
||||
#include "extenwindow.h"
|
||||
#include "host/host.h"
|
||||
|
||||
namespace Ui
|
||||
@ -26,7 +27,7 @@ private:
|
||||
QString TextThreadString(TextThread& thread);
|
||||
ThreadParam ParseTextThreadString(QString ttString);
|
||||
DWORD GetSelectedProcessId();
|
||||
std::unordered_map<const char*, int64_t> GetMiscInfo(TextThread& thread);
|
||||
std::array<InfoForExtension, 10> GetMiscInfo(TextThread& thread);
|
||||
void AttachProcess();
|
||||
void LaunchProcess();
|
||||
void DetachProcess();
|
||||
@ -40,4 +41,5 @@ private:
|
||||
QWidget* extenWindow;
|
||||
std::pair<uint64_t, uint64_t> savedThreadCtx;
|
||||
wchar_t savedThreadCode[1000];
|
||||
std::atomic<TextThread*> current;
|
||||
};
|
||||
|
@ -6,16 +6,15 @@ struct InfoForExtension
|
||||
{
|
||||
const char* name;
|
||||
int64_t value;
|
||||
InfoForExtension* next;
|
||||
};
|
||||
|
||||
struct SentenceInfo
|
||||
{
|
||||
const InfoForExtension* list;
|
||||
// Traverse linked list to find info.
|
||||
const InfoForExtension* infoArray;
|
||||
// nullptr marks end of info array
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
@ -61,7 +61,7 @@ TEST(
|
||||
RemoveCyclicRepeats(cyclicRepeats);
|
||||
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. はい";
|
||||
ProcessSentence(empty, { &tester });
|
||||
ProcessSentence(one, { &tester });
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <Windows.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <functional>
|
||||
@ -14,5 +15,6 @@
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <cassert>
|
||||
|
Loading…
x
Reference in New Issue
Block a user