Use threads if more than one input file is searched.
Use 75% of the available threads (rounded up). Closes: #4
This commit is contained in:
parent
554caebcef
commit
fc0aa02bc9
|
@ -38,6 +38,7 @@ if(NOT termcolor_FOUND)
|
|||
message(FATAL_ERROR "Termcolor was not found.")
|
||||
endif()
|
||||
endif()
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
add_subdirectory(src)
|
||||
|
||||
|
|
|
@ -22,7 +22,9 @@ target_link_libraries(${PROJECT_NAME}_lib
|
|||
Boost::regex
|
||||
std::filesystem
|
||||
fmt::fmt
|
||||
termcolor::termcolor)
|
||||
termcolor::termcolor
|
||||
Threads::Threads
|
||||
m)
|
||||
|
||||
if(${CMAKE_VERSION} VERSION_LESS 3.17)
|
||||
target_link_libraries(${PROJECT_NAME}_lib
|
||||
|
|
199
src/main.cpp
199
src/main.cpp
|
@ -21,16 +21,21 @@
|
|||
#include <boost/locale/message.hpp>
|
||||
#include <boost/program_options/errors.hpp>
|
||||
#include <boost/program_options/variables_map.hpp>
|
||||
#include <fmt/format.h>
|
||||
#include <fmt/ostream.h> // For compatibility with fmt 4.
|
||||
#include <termcolor/termcolor.hpp>
|
||||
|
||||
#include <clocale>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <exception>
|
||||
#include <future>
|
||||
#include <iostream>
|
||||
#include <locale>
|
||||
#include <string>
|
||||
#include <typeinfo>
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
|
@ -39,8 +44,11 @@ int main(int argc, char *argv[])
|
|||
using namespace epubgrep;
|
||||
|
||||
using boost::locale::translate;
|
||||
using fmt::format;
|
||||
using std::cerr;
|
||||
using std::cout;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
// locale_generator("").name.c_str() returns "*" instead of "". That's why
|
||||
// the global C locale isn't changed. So we have to set it additionally.
|
||||
|
@ -74,84 +82,143 @@ int main(int argc, char *argv[])
|
|||
{
|
||||
cout << "NO INPUT FILE\n";
|
||||
// TODO: Read data from stdin.
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
else
|
||||
{
|
||||
search::options opts;
|
||||
if (vm.count("basic-regexp") > 0)
|
||||
{
|
||||
opts.regex = search::regex_kind::basic;
|
||||
}
|
||||
if (vm.count("extended-regexp") > 0)
|
||||
{
|
||||
opts.regex = search::regex_kind::extended;
|
||||
}
|
||||
if (vm.count("perl-regexp") > 0)
|
||||
{
|
||||
opts.regex = search::regex_kind::perl;
|
||||
}
|
||||
if (vm.count("grep") > 0)
|
||||
{
|
||||
opts.grep_like = true;
|
||||
}
|
||||
if (vm.count("ignore-case") > 0)
|
||||
{
|
||||
opts.ignore_case = true;
|
||||
}
|
||||
if (vm.count("raw") > 0)
|
||||
{
|
||||
opts.raw = true;
|
||||
}
|
||||
opts.context = vm["context"].as<std::uint64_t>();
|
||||
|
||||
for (const auto &filepath :
|
||||
vm["input-file"].as<std::vector<std::string>>())
|
||||
int return_code{EXIT_SUCCESS};
|
||||
|
||||
search::options opts;
|
||||
if (vm.count("basic-regexp") > 0)
|
||||
{
|
||||
opts.regex = search::regex_kind::basic;
|
||||
}
|
||||
if (vm.count("extended-regexp") > 0)
|
||||
{
|
||||
opts.regex = search::regex_kind::extended;
|
||||
}
|
||||
if (vm.count("perl-regexp") > 0)
|
||||
{
|
||||
opts.regex = search::regex_kind::perl;
|
||||
}
|
||||
if (vm.count("grep") > 0)
|
||||
{
|
||||
opts.grep_like = true;
|
||||
}
|
||||
if (vm.count("ignore-case") > 0)
|
||||
{
|
||||
opts.ignore_case = true;
|
||||
}
|
||||
if (vm.count("raw") > 0)
|
||||
{
|
||||
opts.raw = true;
|
||||
}
|
||||
opts.context = vm["context"].as<std::uint64_t>();
|
||||
|
||||
vector<vector<search::match>> matches_all;
|
||||
vector<std::future<int>> futurepool;
|
||||
|
||||
auto search_file{
|
||||
[&vm, &matches_all, &opts](std::string_view filepath)
|
||||
{
|
||||
for (const auto ®ex :
|
||||
vm["regexp"].as<std::vector<std::string>>())
|
||||
for (const auto ®ex : vm["regexp"].as<vector<string>>())
|
||||
{
|
||||
try
|
||||
{
|
||||
for (const auto &match :
|
||||
search::search(filepath, regex, opts))
|
||||
{
|
||||
if (vm.count("no-filename") == 0)
|
||||
{
|
||||
cout << match.filepath;
|
||||
}
|
||||
if (!match.headline.empty())
|
||||
{
|
||||
if (vm.count("no-filename") == 0)
|
||||
{
|
||||
cout << ", ";
|
||||
}
|
||||
cout << match.headline;
|
||||
}
|
||||
if (!match.page.empty())
|
||||
{
|
||||
cout << ", page " << match.page;
|
||||
}
|
||||
cout << ": " << match.context.first;
|
||||
if (vm.count("nocolor") == 0)
|
||||
{
|
||||
cout << termcolor::bright_magenta << match.text
|
||||
<< termcolor::reset;
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << match.text;
|
||||
}
|
||||
cout << match.context.second << '\n';
|
||||
}
|
||||
matches_all.emplace_back(
|
||||
search::search(filepath, regex, opts));
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{ // Unknown errors.
|
||||
cerr << '\n' << translate("ERROR: ") << e.what() << '\n';
|
||||
cerr << translate("Error while searching.") << '\n';
|
||||
// NOTE: Maybe we should continue with the next regex/file?
|
||||
cerr << format(translate("Error while searching {0:s}.")
|
||||
.str()
|
||||
.data(),
|
||||
filepath)
|
||||
<< '\n';
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}};
|
||||
|
||||
auto futures_cleanup{
|
||||
[&futurepool, &return_code](const bool wait = false)
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
for (auto it{futurepool.begin()}; it != futurepool.end();)
|
||||
{
|
||||
if (!wait && it->wait_for(100ms) != std::future_status::ready)
|
||||
{
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (int ret{}; (ret = it->get()) != EXIT_SUCCESS)
|
||||
{
|
||||
cerr << "ERROR\n";
|
||||
return_code = ret;
|
||||
}
|
||||
futurepool.erase(it);
|
||||
cerr << "ERASED\n";
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}};
|
||||
|
||||
const auto max_threads{
|
||||
[]
|
||||
{
|
||||
auto n{static_cast<double>(std::thread::hardware_concurrency())};
|
||||
return static_cast<std::uint32_t>(std::ceil(n / 2 + n / 4));
|
||||
}()};
|
||||
|
||||
for (const auto &filepath : vm["input-file"].as<vector<string>>())
|
||||
{
|
||||
if (futurepool.size() >= max_threads)
|
||||
{
|
||||
futures_cleanup();
|
||||
}
|
||||
futurepool.emplace_back(
|
||||
std::async(std::launch::async, search_file, filepath));
|
||||
cerr << "EMPLACED\n";
|
||||
}
|
||||
futures_cleanup(true);
|
||||
|
||||
for (const auto &matches_file : matches_all)
|
||||
{
|
||||
for (const auto &match : matches_file)
|
||||
{
|
||||
if (vm.count("no-filename") == 0)
|
||||
{
|
||||
cout << match.filepath;
|
||||
}
|
||||
if (!match.headline.empty())
|
||||
{
|
||||
if (vm.count("no-filename") == 0)
|
||||
{
|
||||
cout << ", ";
|
||||
}
|
||||
cout << match.headline;
|
||||
}
|
||||
if (!match.page.empty())
|
||||
{
|
||||
cout << ", page " << match.page;
|
||||
}
|
||||
cout << ": " << match.context.first;
|
||||
if (vm.count("nocolor") == 0)
|
||||
{
|
||||
cout << termcolor::bright_magenta << match.text
|
||||
<< termcolor::reset;
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << match.text;
|
||||
}
|
||||
cout << match.context.second << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
return return_code;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue