Mercurial > minori
view dep/animia/src/player.cc @ 187:9613d72b097e
*: multiple performance improvements
like marking `static const` when it makes sense...
date: change old stupid heap-based method to a structure which should
make copying the thing actually make a copy.
also many performance-based changes, like removing the std::tie
dependency and forward-declaring nlohmann json
*: replace every instance of QString::fromUtf8 to Strings::ToQString.
the main difference is that our function will always convert exactly
what is in the string, while some other times it would only convert
up to the nearest NUL byte
author | Paper <mrpapersonic@gmail.com> |
---|---|
date | Wed, 06 Dec 2023 13:43:54 -0500 |
parents | cdf79282d647 |
children |
line wrap: on
line source
#include "animia/player.h" #include "animia/util.h" #include <map> #include <sstream> #include <string> #include <vector> namespace animia { namespace internal::parser { enum class State { ExpectPlayerName, ExpectSection, ExpectWindow, ExpectExecutable, ExpectStrategy, ExpectType, ExpectWindowTitle, }; size_t GetIndentation(const std::string& line) { return line.find_first_not_of('\t'); } bool HandleIndentation(const size_t current, const std::vector<Player>& players, State& state) { // Each state has a definitive expected indentation const auto expected = [&state]() -> size_t { switch (state) { default: case State::ExpectPlayerName: return 0; case State::ExpectSection: return 1; case State::ExpectWindow: case State::ExpectExecutable: case State::ExpectStrategy: case State::ExpectType: return 2; case State::ExpectWindowTitle: return 3; } }(); if (current > expected) return false; // Disallow excessive indentation if (current < expected) { auto fix_state = [&]() { state = !current ? State::ExpectPlayerName : State::ExpectSection; }; switch (state) { case State::ExpectWindow: if (players.back().windows.empty()) return false; fix_state(); break; case State::ExpectExecutable: if (players.back().executables.empty()) return false; fix_state(); break; case State::ExpectStrategy: if (players.back().strategies.empty()) return false; fix_state(); break; case State::ExpectType: fix_state(); break; case State::ExpectWindowTitle: return false; } } return true; } bool HandleState(std::string& line, std::vector<Player>& players, State& state) { switch (state) { case State::ExpectPlayerName: players.push_back(Player()); players.back().name = line; state = State::ExpectSection; break; case State::ExpectSection: { static const std::map<std::string, State> sections = { {"windows", State::ExpectWindow }, {"executables", State::ExpectExecutable}, {"strategies", State::ExpectStrategy }, {"type", State::ExpectType }, }; util::TrimRight(line, ":"); const auto it = sections.find(line); if (it == sections.end()) return false; state = it->second; break; } case State::ExpectWindow: players.back().windows.push_back(line); break; case State::ExpectExecutable: players.back().executables.push_back(line); break; case State::ExpectStrategy: { static const std::map<std::string, Strategy> strategies = { {"window_title", Strategy::WindowTitle }, {"open_files", Strategy::OpenFiles }, {"ui_automation", Strategy::UiAutomation}, }; util::TrimRight(line, ":"); const auto it = strategies.find(line); if (it == strategies.end()) return false; const auto strategy = it->second; players.back().strategies.push_back(strategy); switch (strategy) { case Strategy::WindowTitle: state = State::ExpectWindowTitle; break; } break; } case State::ExpectType: { static const std::map<std::string, PlayerType> types = { {"default", PlayerType::Default }, {"web_browser", PlayerType::WebBrowser}, }; const auto it = types.find(line); if (it == types.end()) return false; players.back().type = it->second; break; } case State::ExpectWindowTitle: players.back().window_title_format = line; state = State::ExpectStrategy; break; } return true; } } // namespace internal::parser //////////////////////////////////////////////////////////////////////////////// bool ParsePlayersData(const std::string& data, std::vector<Player>& players) { if (data.empty()) return false; std::istringstream stream(data); std::string line; size_t indentation = 0; auto state = internal::parser::State::ExpectPlayerName; while (std::getline(stream, line, '\n')) { if (line.empty()) continue; // Ignore empty lines indentation = internal::parser::GetIndentation(line); internal::util::TrimLeft(line, "\t"); internal::util::TrimRight(line, "\n\r"); if (line.empty() || line.front() == '#') continue; // Ignore empty lines and comments if (!internal::parser::HandleIndentation(indentation, players, state)) return false; if (!internal::parser::HandleState(line, players, state)) return false; } return !players.empty(); } bool ParsePlayersFile(const std::string& path, std::vector<Player>& players) { std::string data; if (!internal::util::ReadFile(path, data)) return false; return ParsePlayersData(data, players); } } // namespace animia