1
0
mirror of synced 2025-01-31 03:53:44 +01:00

ux: Reduce file loading time to basically zero

This commit is contained in:
WerWolv 2022-09-19 16:09:22 +02:00
parent b11dbe4fe1
commit 7b61268f22
7 changed files with 143 additions and 106 deletions

View File

@ -19,7 +19,7 @@ namespace hex {
class Task {
public:
Task() = default;
Task(std::string unlocalizedName, u64 maxValue, std::function<void(Task &)> function);
Task(std::string unlocalizedName, u64 maxValue, bool background, std::function<void(Task &)> function);
Task(const Task&) = delete;
Task(Task &&other) noexcept;
@ -28,6 +28,7 @@ namespace hex {
void update(u64 value = 0);
void setMaxValue(u64 value);
[[nodiscard]] bool isBackgroundTask() const;
[[nodiscard]] bool isFinished() const;
[[nodiscard]] bool hadException() const;
[[nodiscard]] bool wasInterrupted() const;
@ -56,6 +57,7 @@ namespace hex {
std::function<void()> m_interruptCallback;
bool m_shouldInterrupt = false;
bool m_background = true;
bool m_interrupted = false;
bool m_finished = false;
@ -89,6 +91,7 @@ namespace hex {
constexpr static auto NoProgress = 0;
static TaskHolder createTask(std::string name, u64 maxValue, std::function<void(Task &)> function);
static TaskHolder createBackgroundTask(std::string name, std::function<void(Task &)> function);
static void collectGarbage();
static size_t getRunningTaskCount();

View File

@ -9,11 +9,11 @@ namespace hex {
std::mutex TaskManager::s_deferredCallsMutex;
std::list<std::shared_ptr<Task>> TaskManager::s_tasks;
std::list<std::shared_ptr<Task>> TaskManager::s_tasks, s_backgroundTasks;
std::list<std::function<void()>> TaskManager::s_deferredCalls;
Task::Task(std::string unlocalizedName, u64 maxValue, std::function<void(Task &)> function)
: m_unlocalizedName(std::move(unlocalizedName)), m_currValue(0), m_maxValue(maxValue) {
Task::Task(std::string unlocalizedName, u64 maxValue, bool background, std::function<void(Task &)> function)
: m_unlocalizedName(std::move(unlocalizedName)), m_currValue(0), m_maxValue(maxValue), m_background(background) {
this->m_thread = std::thread([this, func = std::move(function)] {
try {
func(*this);
@ -83,6 +83,12 @@ namespace hex {
this->m_interruptCallback = std::move(callback);
}
bool Task::isBackgroundTask() const {
std::scoped_lock lock(this->m_mutex);
return this->m_background;
}
bool Task::isFinished() const {
std::scoped_lock lock(this->m_mutex);
@ -164,13 +170,20 @@ namespace hex {
TaskHolder TaskManager::createTask(std::string name, u64 maxValue, std::function<void(Task &)> function) {
s_tasks.emplace_back(std::make_shared<Task>(std::move(name), maxValue, std::move(function)));
s_tasks.emplace_back(std::make_shared<Task>(std::move(name), maxValue, false, std::move(function)));
return TaskHolder(s_tasks.back());
}
TaskHolder TaskManager::createBackgroundTask(std::string name, std::function<void(Task &)> function) {
s_backgroundTasks.emplace_back(std::make_shared<Task>(std::move(name), 0, true, std::move(function)));
return TaskHolder(s_backgroundTasks.back());
}
void TaskManager::collectGarbage() {
std::erase_if(s_tasks, [](const auto &task) { return task->isFinished() && !task->hadException(); });
std::erase_if(s_backgroundTasks, [](const auto &task) { return task->isFinished(); });
}
std::list<std::shared_ptr<Task>> &TaskManager::getRunningTasks() {

View File

@ -52,6 +52,7 @@ namespace hex {
ImGui::Texture m_logoTexture = { nullptr };
std::mutex m_popupMutex;
std::list<std::string> m_popupsToOpen;
std::vector<int> m_pressedKeys;

View File

@ -144,6 +144,8 @@ namespace hex {
});
EventManager::subscribe<RequestOpenPopup>(this, [this](auto name) {
std::scoped_lock lock(this->m_popupMutex);
this->m_popupsToOpen.push_back(name);
});
@ -424,15 +426,17 @@ namespace hex {
ImGui::EndPopup();
}
}
{
std::scoped_lock lock(this->m_popupMutex);
this->m_popupsToOpen.remove_if([](const auto &name) {
if (ImGui::IsPopupOpen(name.c_str()))
return true;
else
ImGui::OpenPopup(name.c_str());
this->m_popupsToOpen.remove_if([](const auto &name) {
if (ImGui::IsPopupOpen(name.c_str()))
return true;
else
ImGui::OpenPopup(name.c_str());
return false;
});
return false;
});
}
TaskManager::runDeferredCalls();

View File

@ -106,19 +106,22 @@ namespace hex::plugin::builtin {
void ViewPatternData::drawContent() {
if (ImGui::Begin(View::toWindowName("hex.builtin.view.pattern_data.name").c_str(), &this->getWindowOpenState(), ImGuiWindowFlags_NoCollapse)) {
auto provider = ImHexApi::Provider::get();
if (ImHexApi::Provider::isValid() && provider->isReadable() && !ProviderExtraData::get(provider).patternLanguage.runtime->isRunning()) {
if (ImHexApi::Provider::isValid()) {
auto provider = ImHexApi::Provider::get();
auto &runtime = ProviderExtraData::get(provider).patternLanguage.runtime;
auto &sortedPatterns = this->m_sortedPatterns[ImHexApi::Provider::get()];
if (beginPatternTable(provider, ProviderExtraData::get(provider).patternLanguage.runtime->getAllPatterns(), sortedPatterns)) {
ImGui::TableHeadersRow();
if (!sortedPatterns.empty()) {
if (provider->isReadable() && runtime != nullptr && !runtime->isRunning()) {
auto &sortedPatterns = this->m_sortedPatterns[ImHexApi::Provider::get()];
if (beginPatternTable(provider, ProviderExtraData::get(provider).patternLanguage.runtime->getAllPatterns(), sortedPatterns)) {
ImGui::TableHeadersRow();
for (auto &patterns : sortedPatterns)
patterns->accept(this->m_patternDrawer);
if (!sortedPatterns.empty()) {
for (auto &patterns : sortedPatterns)
patterns->accept(this->m_patternDrawer);
}
ImGui::EndTable();
}
ImGui::EndTable();
}
}
}

View File

@ -114,58 +114,60 @@ namespace hex::plugin::builtin {
patternLanguageData.runtime = std::make_unique<pl::PatternLanguage>();
ContentRegistry::PatternLanguage::configureRuntime(*patternLanguageData.runtime, provider);
if (!this->m_autoLoadPatterns)
return;
TaskManager::createBackgroundTask("Analyzing file content", [this, provider, &data = patternLanguageData](auto &) {
if (!this->m_autoLoadPatterns)
return;
// Copy over current pattern source code to the new provider
if (!this->m_syncPatternSourceCode) {
patternLanguageData.sourceCode = this->m_textEditor.GetText();
}
auto &runtime = patternLanguageData.runtime;
auto mimeType = magic::getMIMEType(provider);
bool foundCorrectType = false;
runtime->addPragma("MIME", [&mimeType, &foundCorrectType](pl::PatternLanguage &runtime, const std::string &value) {
hex::unused(runtime);
if (value == mimeType) {
foundCorrectType = true;
return true;
// Copy over current pattern source code to the new provider
if (!this->m_syncPatternSourceCode) {
data.sourceCode = this->m_textEditor.GetText();
}
auto &runtime = data.runtime;
auto mimeType = magic::getMIMEType(provider);
bool foundCorrectType = false;
runtime->addPragma("MIME", [&mimeType, &foundCorrectType](pl::PatternLanguage &runtime, const std::string &value) {
hex::unused(runtime);
if (value == mimeType) {
foundCorrectType = true;
return true;
}
return !std::all_of(value.begin(), value.end(), isspace) && !value.ends_with('\n') && !value.ends_with('\r');
});
this->m_possiblePatternFiles.clear();
std::error_code errorCode;
for (const auto &dir : fs::getDefaultPaths(fs::ImHexPath::Patterns)) {
for (auto &entry : std::fs::recursive_directory_iterator(dir, errorCode)) {
foundCorrectType = false;
if (!entry.is_regular_file())
continue;
fs::File file(entry.path(), fs::File::Mode::Read);
if (!file.isValid())
continue;
runtime->getInternals().preprocessor->preprocess(*runtime, file.readString());
if (foundCorrectType)
this->m_possiblePatternFiles.push_back(entry.path());
runtime->reset();
}
}
runtime->addPragma("MIME", [](pl::PatternLanguage&, const std::string &value) { return !value.empty(); });
if (!this->m_possiblePatternFiles.empty()) {
this->m_selectedPatternFile = 0;
EventManager::post<RequestOpenPopup>("hex.builtin.view.pattern_editor.accept_pattern"_lang);
this->m_acceptPatternWindowOpen = true;
}
return !std::all_of(value.begin(), value.end(), isspace) && !value.ends_with('\n') && !value.ends_with('\r');
});
this->m_possiblePatternFiles.clear();
std::error_code errorCode;
for (const auto &dir : fs::getDefaultPaths(fs::ImHexPath::Patterns)) {
for (auto &entry : std::fs::recursive_directory_iterator(dir, errorCode)) {
foundCorrectType = false;
if (!entry.is_regular_file())
continue;
fs::File file(entry.path(), fs::File::Mode::Read);
if (!file.isValid())
continue;
runtime->getInternals().preprocessor->preprocess(*runtime, file.readString());
if (foundCorrectType)
this->m_possiblePatternFiles.push_back(entry.path());
runtime->reset();
}
}
runtime->addPragma("MIME", [](pl::PatternLanguage&, const std::string &value) { return !value.empty(); });
if (!this->m_possiblePatternFiles.empty()) {
this->m_selectedPatternFile = 0;
EventManager::post<RequestOpenPopup>("hex.builtin.view.pattern_editor.accept_pattern"_lang);
this->m_acceptPatternWindowOpen = true;
}
});
EventManager::subscribe<EventProviderChanged>(this, [this](prv::Provider *oldProvider, prv::Provider *newProvider) {

View File

@ -52,40 +52,49 @@ namespace hex::plugin::builtin {
};
static std::atomic<bool> s_recentProvidersUpdating = false;
static std::list<RecentProvider> s_recentProviders;
static void updateRecentProviders() {
s_recentProviders.clear();
TaskManager::createBackgroundTask("Updating recent files", [](auto&){
if (s_recentProvidersUpdating)
return;
// Query all recent providers
std::vector<std::fs::path> recentFilePaths;
for (const auto &folder : fs::getDefaultPaths(fs::ImHexPath::Recent)) {
for (const auto &entry : std::fs::directory_iterator(folder)) {
if (entry.is_regular_file())
recentFilePaths.push_back(entry.path());
s_recentProvidersUpdating = true;
ON_SCOPE_EXIT { s_recentProvidersUpdating = false; };
s_recentProviders.clear();
// Query all recent providers
std::vector<std::fs::path> recentFilePaths;
for (const auto &folder : fs::getDefaultPaths(fs::ImHexPath::Recent)) {
for (const auto &entry : std::fs::directory_iterator(folder)) {
if (entry.is_regular_file())
recentFilePaths.push_back(entry.path());
}
}
}
// Sort recent provider files by last modified time
std::sort(recentFilePaths.begin(), recentFilePaths.end(), [](const auto &a, const auto &b) {
return std::fs::last_write_time(a) > std::fs::last_write_time(b);
// Sort recent provider files by last modified time
std::sort(recentFilePaths.begin(), recentFilePaths.end(), [](const auto &a, const auto &b) {
return std::fs::last_write_time(a) > std::fs::last_write_time(b);
});
std::unordered_set<RecentProvider, RecentProvider::HashFunction> uniqueProviders;
for (u32 i = 0; i < recentFilePaths.size() && uniqueProviders.size() < 5; i++) {
auto &path = recentFilePaths[i];
try {
auto jsonData = nlohmann::json::parse(fs::File(path, fs::File::Mode::Read).readString());
uniqueProviders.insert(RecentProvider {
.displayName = jsonData["displayName"],
.type = jsonData["type"],
.filePath = path,
.data = jsonData
});
} catch (...) { }
}
std::copy(uniqueProviders.begin(), uniqueProviders.end(), std::front_inserter(s_recentProviders));
});
std::unordered_set<RecentProvider, RecentProvider::HashFunction> uniqueProviders;
for (u32 i = 0; i < recentFilePaths.size() && uniqueProviders.size() < 5; i++) {
auto &path = recentFilePaths[i];
try {
auto jsonData = nlohmann::json::parse(fs::File(path, fs::File::Mode::Read).readString());
uniqueProviders.insert(RecentProvider {
.displayName = jsonData["displayName"],
.type = jsonData["type"],
.filePath = path,
.data = jsonData
});
} catch (...) { }
}
std::copy(uniqueProviders.begin(), uniqueProviders.end(), std::front_inserter(s_recentProviders));
}
static void loadRecentProvider(const RecentProvider &recentProvider) {
@ -227,13 +236,15 @@ namespace hex::plugin::builtin {
ImGui::UnderlinedText("hex.builtin.welcome.start.recent"_lang);
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 5_scaled);
{
for (const auto &recentProvider : s_recentProviders) {
ImGui::PushID(&recentProvider);
ON_SCOPE_EXIT { ImGui::PopID(); };
if (!s_recentProvidersUpdating) {
for (const auto &recentProvider : s_recentProviders) {
ImGui::PushID(&recentProvider);
ON_SCOPE_EXIT { ImGui::PopID(); };
if (ImGui::BulletHyperlink(recentProvider.displayName.c_str())) {
loadRecentProvider(recentProvider);
break;
if (ImGui::BulletHyperlink(recentProvider.displayName.c_str())) {
loadRecentProvider(recentProvider);
break;
}
}
}
}
@ -500,7 +511,7 @@ namespace hex::plugin::builtin {
});
ContentRegistry::Interface::addMenuItem("hex.builtin.menu.file", 1075, [&] {
if (ImGui::BeginMenu("hex.builtin.menu.file.open_recent"_lang, !s_recentProviders.empty())) {
if (ImGui::BeginMenu("hex.builtin.menu.file.open_recent"_lang, !s_recentProvidersUpdating && !s_recentProviders.empty())) {
// Copy to avoid changing list while iteration
auto recentProviders = s_recentProviders;
for (auto &recentProvider : recentProviders) {