Proc optimization

This commit is contained in:
aristocratos 2021-05-22 02:13:56 +02:00
parent 881b90f4f3
commit 81f2284a75
7 changed files with 191 additions and 172 deletions

View File

@ -26,6 +26,8 @@ tab-size = 4
#include <atomic> #include <atomic>
#include <numeric> #include <numeric>
#include <ranges> #include <ranges>
#include <filesystem>
#include <unistd.h>
#include <btop_globs.h> #include <btop_globs.h>
#include <btop_tools.h> #include <btop_tools.h>
@ -56,6 +58,7 @@ tab-size = 4
using std::string, std::vector, std::array, std::map, std::atomic, std::endl, std::cout, std::views::iota, std::list, std::accumulate; using std::string, std::vector, std::array, std::map, std::atomic, std::endl, std::cout, std::views::iota, std::list, std::accumulate;
using std::flush, std::endl, std::future, std::string_literals::operator""s, std::future_status; using std::flush, std::endl, std::future, std::string_literals::operator""s, std::future_status;
namespace fs = std::filesystem;
using namespace Tools; using namespace Tools;
@ -151,9 +154,9 @@ int main(int argc, char **argv){
#if defined(LINUX) #if defined(LINUX)
//? Linux init //? Linux init
Global::proc_path = (fs::is_directory(fs::path("/proc"))) ? fs::path("/proc") : Global::proc_path; Global::proc_path = (fs::is_directory(fs::path("/proc")) && access("/proc", R_OK) != -1) ? fs::path("/proc") : Global::proc_path;
if (Global::proc_path.empty()) { if (Global::proc_path.empty()) {
cout << "ERROR: Proc filesystem not detected!" << endl; cout << "ERROR: Proc filesystem not found/readable!" << endl;
exit(1); exit(1);
} }
#endif #endif
@ -161,7 +164,7 @@ int main(int argc, char **argv){
//? Initialize terminal and set options //? Initialize terminal and set options
if (!Term::init()) { if (!Term::init()) {
cout << "ERROR: No tty detected!" << endl; cout << "ERROR: No tty detected!" << endl;
cout << "Sorry, btop++ needs an interactive shell to run." << endl; cout << "btop++ needs an interactive shell to run." << endl;
exit(1); exit(1);
} }
@ -171,7 +174,7 @@ int main(int argc, char **argv){
auto thts = time_ms(); auto thts = time_ms();
//? Generate the theme //? Generate the theme
Theme::set(Global::Default_theme); Theme::set(Theme::Default_theme);
//? Create the btop++ banner //? Create the btop++ banner
Global::banner = createBanner(); Global::banner = createBanner();
@ -233,7 +236,6 @@ int main(int argc, char **argv){
} }
if (thread_test){ if (thread_test){
map<int, future<string>> runners; map<int, future<string>> runners;
@ -263,26 +265,17 @@ int main(int argc, char **argv){
//------>>>>>> //*------>>>>>> Proc testing
auto timestamp = time_ms(); auto timestamp = time_ms();
Proc::init(); Proc::init();
cout << "Total Processes init: " << time_ms() - timestamp << "ms" << endl;
cout << "Press any key to start!" << Mv::l(100) << flush;
// sleep_ms(1000);
// Input::wait();
// Input::clear();
// insert Processes call here
uint lc; uint lc;
string ostring; string ostring;
uint64_t tsl, timestamp2; uint64_t tsl, timestamp2, rcount = 0;
list<uint64_t> avgtimes; list<uint64_t> avgtimes;
uint timer = 1000; uint timer = 1000;
bool filtering = false; bool filtering = false;
@ -326,8 +319,8 @@ int main(int argc, char **argv){
avgtimes.push_front(timestamp); avgtimes.push_front(timestamp);
if (avgtimes.size() > 100) avgtimes.pop_back(); if (avgtimes.size() > 100) avgtimes.pop_back();
cout << pbox << ostring << Fx::reset << "\n" << endl; cout << pbox << ostring << Fx::reset << "\n" << endl;
cout << Mv::to(Term::height - 4, 1) << "Processes call took: " << timestamp << "ms. Average: " << accumulate(avgtimes.begin(), avgtimes.end(), 0) / avgtimes.size() << cout << Mv::to(Term::height - 4, 1) << "Processes call took: " << rjust(to_string(timestamp), 4) << "ms. Average: " << rjust(to_string(accumulate(avgtimes.begin(), avgtimes.end(), 0) / avgtimes.size()), 3) <<
"ms of " << avgtimes.size() << " samples. Drawing took: " << time_ms() - timestamp2 << "ms. " << endl; "ms of " << avgtimes.size() << " samples. Drawing took: " << time_ms() - timestamp2 << "ms. Number of processes: " << Proc::numpids << ". Run count: " << ++rcount << " " << endl;
while (time_ms() < tsl) { while (time_ms() < tsl) {
if (Input::poll(tsl - time_ms())) key = Input::get(); if (Input::poll(tsl - time_ms())) key = Input::get();
@ -353,7 +346,7 @@ int main(int argc, char **argv){
// cout << "Found " << plist.size() << " pids\n" << endl; // cout << "Found " << plist.size() << " pids\n" << endl;
//-----<<<<< //*-----<<<<<
//cout << pw->pw_name << "/" << gr->gr_name << endl; //cout << pw->pw_name << "/" << gr->gr_name << endl;

View File

@ -23,7 +23,6 @@ tab-size = 4
#include <map> #include <map>
#include <ranges> #include <ranges>
#include <btop_globs.h>
#include <btop_config.h> #include <btop_config.h>
#include <btop_tools.h> #include <btop_tools.h>

View File

@ -31,50 +31,7 @@ namespace Global {
atomic<bool> stop_all(false); atomic<bool> stop_all(false);
const unordered_map<string, string> Default_theme = {
{ "main_bg", "#00" },
{ "main_fg", "#cc" },
{ "title", "#ee" },
{ "hi_fg", "#969696" },
{ "selected_bg", "#7e2626" },
{ "selected_fg", "#ee" },
{ "inactive_fg", "#40" },
{ "graph_text", "#60" },
{ "meter_bg", "#40" },
{ "proc_misc", "#0de756" },
{ "cpu_box", "#3d7b46" },
{ "mem_box", "#8a882e" },
{ "net_box", "#423ba5" },
{ "proc_box", "#923535" },
{ "div_line", "#30" },
{ "temp_start", "#4897d4" },
{ "temp_mid", "#5474e8" },
{ "temp_end", "#ff40b6" },
{ "cpu_start", "#50f095" },
{ "cpu_mid", "#f2e266" },
{ "cpu_end", "#fa1e1e" },
{ "free_start", "#223014" },
{ "free_mid", "#b5e685" },
{ "free_end", "#dcff85" },
{ "cached_start", "#0b1a29" },
{ "cached_mid", "#74e6fc" },
{ "cached_end", "#26c5ff" },
{ "available_start", "#292107" },
{ "available_mid", "#ffd77a" },
{ "available_end", "#ffb814" },
{ "used_start", "#3b1f1c" },
{ "used_mid", "#d9626d" },
{ "used_end", "#ff4769" },
{ "download_start", "#231a63" },
{ "download_mid", "#4f43a3" },
{ "download_end", "#b0a9de" },
{ "upload_start", "#510554" },
{ "upload_mid", "#7d4180" },
{ "upload_end", "#dcafde" },
{ "process_start", "#80d0a3" },
{ "process_mid", "#dcd179" },
{ "process_end", "#d45454" }
};
const unordered_map<string, unordered_map<string, vector<string>>> Menus = { const unordered_map<string, unordered_map<string, vector<string>>> Menus = {
{ "options", { { "options", {
@ -115,25 +72,10 @@ namespace Global {
} } } }
}; };
//? Units for floating_humanizer function
const array<string, 11> Units_bit = {"bit", "Kib", "Mib", "Gib", "Tib", "Pib", "Eib", "Zib", "Yib", "Bib", "GEb"};
const array<string, 11> Units_byte = {"Byte", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB", "BiB", "GEB"};
} }
namespace Symbols {
const string h_line = "";
const string v_line = "";
const string left_up = "";
const string right_up = "";
const string left_down = "";
const string right_down = "";
const string title_left = "";
const string title_right = "";
const string div_up = "";
const string div_down = "";
const array<string, 10> superscript = { "", "¹", "²", "³", "", "", "", "", "", "" };
}
#endif #endif

View File

@ -23,12 +23,16 @@ tab-size = 4
#include <unordered_map> #include <unordered_map>
#include <iostream> #include <iostream>
#include <btop_globs.h>
#include <btop_tools.h> #include <btop_tools.h>
using std::string, std::unordered_map, std::cin; using std::string, std::unordered_map, std::cin;
using namespace Tools; using namespace Tools;
/* The input functions relies on the following std::cin options being set:
cin.sync_with_stdio(false);
cin.tie(NULL);
These will automatically be set when running Term::init() from btop_tools.h
*/
//* Functions and variables for handling keyboard and mouse input //* Functions and variables for handling keyboard and mouse input
namespace Input { namespace Input {

View File

@ -30,7 +30,6 @@ tab-size = 4
#include <ranges> #include <ranges>
#include <list> #include <list>
#include <unistd.h> #include <unistd.h>
#include <btop_config.h> #include <btop_config.h>
@ -39,12 +38,13 @@ tab-size = 4
using std::string, std::vector, std::array, std::ifstream, std::atomic, std::numeric_limits, std::streamsize, std::unordered_map, std::deque, std::list; using std::string, std::vector, std::array, std::ifstream, std::atomic, std::numeric_limits, std::streamsize, std::unordered_map, std::deque, std::list;
using std::cout, std::flush, std::endl;
namespace fs = std::filesystem; namespace fs = std::filesystem;
using namespace Tools; using namespace Tools;
const auto SSmax = std::numeric_limits<streamsize>::max();
namespace Global { namespace Global {
const string System = "linux";
fs::path proc_path; fs::path proc_path;
} }
@ -64,8 +64,6 @@ double system_uptime(){
namespace Proc { namespace Proc {
namespace { namespace {
uint64_t tstamp; uint64_t tstamp;
size_t numpids = 500;
long int clk_tck;
struct p_cache { struct p_cache {
string name, cmd, user; string name, cmd, user;
uint64_t cpu_t = 0, cpu_s = 0; uint64_t cpu_t = 0, cpu_s = 0;
@ -75,10 +73,12 @@ namespace Proc {
fs::path passwd_path; fs::path passwd_path;
fs::file_time_type passwd_time; fs::file_time_type passwd_time;
uint counter = 0; uint counter = 0;
long page_size = sysconf(_SC_PAGE_SIZE); auto page_size = sysconf(_SC_PAGE_SIZE);
auto clk_tck = sysconf(_SC_CLK_TCK);
} }
size_t numpids = 500;
atomic<bool> stop (false); atomic<bool> stop (false);
atomic<bool> running (false); atomic<bool> running (false);
array<string, 8> sort_array = { array<string, 8> sort_array = {
@ -116,15 +116,13 @@ namespace Proc {
bool new_cache; bool new_cache;
char state; char state;
int cpu_n, p_nice; int cpu_n, p_nice;
size_t threads; size_t threads, s_pos, c_pos, s_count;
ifstream pread; ifstream pread;
string pid_str, name, cmd, attr, user, instr, uid, status, tmpstr; string pid_str, name, cmd, attr, user, instr, uid, status, tmpstr;
auto since_last = time_ms() - tstamp; auto since_last = time_ms() - tstamp;
if (since_last < 1) since_last = 1; if (since_last < 1) since_last = 1;
auto uptime = system_uptime(); auto uptime = system_uptime();
auto sortint = (sort_map.contains(sorting)) ? sort_map[sorting] : 7; auto sortint = (sort_map.contains(sorting)) ? sort_map[sorting] : 7;
vector<string> pstat;
pstat.reserve(40);
vector<proc_info> procs; vector<proc_info> procs;
procs.reserve((numpids + 10)); procs.reserve((numpids + 10));
vector<uint> c_pids; vector<uint> c_pids;
@ -136,35 +134,35 @@ namespace Proc {
string r_uid, r_user; string r_uid, r_user;
passwd_time = fs::last_write_time(passwd_path); passwd_time = fs::last_write_time(passwd_path);
uid_user.clear(); uid_user.clear();
ifstream pread(passwd_path); pread.open(passwd_path);
if (pread.good()) { if (pread.good()) {
while (true){ while (true){
getline(pread, r_user, ':'); getline(pread, r_user, ':');
pread.ignore(numeric_limits<streamsize>::max(), ':'); pread.ignore(SSmax, ':');
getline(pread, r_uid, ':'); getline(pread, r_uid, ':');
uid_user[r_uid] = r_user; uid_user[r_uid] = r_user;
pread.ignore(numeric_limits<streamsize>::max(), '\n'); pread.ignore(SSmax, '\n');
if (pread.eof()) break; if (pread.eof()) break;
} }
} }
pread.close(); pread.close();
} }
//* Iterate over all pids in /proc and get relevant values //* Iterate over all pids in /proc and get relevant values
for (auto& d: fs::directory_iterator(Global::proc_path)){ for (auto& d: fs::directory_iterator(Global::proc_path)){
if (pread.is_open()) pread.close();
if (stop.load()) { if (stop.load()) {
procs.clear(); procs.clear();
running.store(false); running.store(false);
stop.store(false); stop.store(false);
return procs; return procs;
} }
numpids++; pid_str = d.path().filename();
pid_str = fs::path(d.path()).filename();
cpu = 0.0; cpu_s = 0.0; cpu_t = 0; cpu_n = 0; cpu = 0.0; cpu_s = 0.0; cpu_t = 0; cpu_n = 0;
rss_mem = 0; threads = 0; state = '0'; ppid = 0; p_nice = 0; rss_mem = 0; threads = 0; state = '0'; ppid = 0; p_nice = 0;
new_cache = false; new_cache = false;
if (d.is_directory() && isdigit(pid_str[0])) { if (d.is_directory() && isdigit(pid_str[0])) {
numpids++;
pid = stoul(pid_str); pid = stoul(pid_str);
c_pids.push_back(pid); c_pids.push_back(pid);
@ -172,76 +170,106 @@ namespace Proc {
if (!cache.contains(pid)) { if (!cache.contains(pid)) {
name.clear(); cmd.clear(); user.clear(); name.clear(); cmd.clear(); user.clear();
new_cache = true; new_cache = true;
if (fs::exists((string)d.path() + "/comm")) { pread.open(d.path() / "comm");
pread.clear(); name.clear(); if (pread.good()) {
ifstream pread((string)d.path() + "/comm"); getline(pread, name);
if (pread.good()) getline(pread, name);
pread.close(); pread.close();
} }
if (fs::exists((string)d.path() + "/cmdline")) { else continue;
pread.clear(); cmd.clear(); tmpstr.clear();
ifstream pread((string)d.path() + "/cmdline"); pread.open(d.path() / "cmdline");
if (pread.good()) while(getline(pread, tmpstr, '\0')) cmd += tmpstr + " "; if (pread.good()) {
tmpstr.clear();
while(getline(pread, tmpstr, '\0')) cmd += tmpstr + " ";
pread.close(); pread.close();
if (!cmd.empty()) cmd.pop_back(); if (!cmd.empty()) cmd.pop_back();
} }
if (fs::exists((string)d.path() + "/status")) { else continue;
pread.clear(); status.clear(); uid.clear();
ifstream pread((string)d.path() + "/status"); pread.open(d.path() / "status");
if (pread.good()) { if (pread.good()) {
while (!pread.eof()){ status.clear(); uid.clear();
getline(pread, status, ':'); while (!pread.eof()){
if (status == "Uid") { getline(pread, status, ':');
pread.ignore(); if (status == "Uid") {
getline(pread, uid, '\t'); pread.ignore();
break; getline(pread, uid, '\t');
} else { break;
pread.ignore(numeric_limits<streamsize>::max(), '\n'); } else {
} pread.ignore(SSmax, '\n');
} }
} }
pread.close(); pread.close();
user = (!uid.empty() && uid_user.contains(uid)) ? uid_user.at(uid) : uid; user = (!uid.empty() && uid_user.contains(uid)) ? uid_user.at(uid) : uid;
} }
else continue;
cache[pid] = p_cache(name, cmd, user); cache[pid] = p_cache(name, cmd, user);
} }
//* Match filter if defined
if (!filter.empty() && pid_str.find(filter) == string::npos &&
cache[pid].name.find(filter) == string::npos &&
cache[pid].cmd.find(filter) == string::npos &&
cache[pid].user.find(filter) == string::npos) {
if (new_cache) cache.erase(pid);
continue;
}
//* Get cpu usage, cpu cumulative and threads from /proc/[pid]/stat //* Get cpu usage, cpu cumulative and threads from /proc/[pid]/stat
if (fs::exists((string)d.path() + "/stat")) { pread.open(d.path() / "stat");
pread.clear(); instr.clear(); pstat.clear(); if (pread.good()) {
ifstream pread((string)d.path() + "/stat"); instr.clear(); s_pos = 0; c_pos = 0; s_count = 0;
if (pread.good() && getline(pread, instr)) { getline(pread, instr);
//? Skip pid and comm field and find comm fields closing ')' from right to avoid names with whitespace or parenthesis
pstat = ssplit(instr.substr(instr.rfind(')') + 1), " ", 37, true);
}
pread.close(); pread.close();
if (pstat.size() < 20) continue; //? Skip pid and comm field and find comm fields closing ')' to avoid names with whitespace or parenthesis
s_pos = instr.find_last_of(')') + 2;
//? Process state do {
state = pstat[0][0]; c_pos = instr.find(' ', s_pos);
if (c_pos == string::npos) break;
//? Process parent pid switch (s_count) {
ppid = stoul(pstat[1]); case 0: { //? Process state
state = instr[s_pos];
//? Process nice value break;
p_nice = stoi(pstat[16]); }
case 1: { //? Process parent pid
//? Process number of threads ppid = stoul(instr.substr(s_pos, c_pos - s_pos));
threads = stoul(pstat[17]); break;
}
//? Process utime + stime case 11: { //? Process utime
cpu_t = stoull(pstat[11]) + stoull(pstat[12]); cpu_t = stoull(instr.substr(s_pos, c_pos - s_pos));
break;
//? Cache cpu times and cpu seconds }
if (new_cache) { case 12: { //? Process stime
cache[pid].cpu_t = cpu_t; cpu_t += stoull(instr.substr(s_pos, c_pos - s_pos));
cache[pid].cpu_s = stoull(pstat[19]); break;
} }
case 16: { //? Process nice value
//? CPU number last executed on p_nice = stoi(instr.substr(s_pos, c_pos - s_pos));
if (pstat.size() > 36) cpu_n = stoi(pstat[36]); break;
}
case 17: { //? Process number of threads
threads = stoul(instr.substr(s_pos, c_pos - s_pos));
break;
}
case 19: { //? Cache cpu times and cpu seconds
if (new_cache) {
cache[pid].cpu_t = cpu_t;
cache[pid].cpu_s = stoull(instr.substr(s_pos, c_pos - s_pos));
};
break;
}
case 36: { //? CPU number last executed on
cpu_n = stoi(instr.substr(s_pos, c_pos - s_pos));
break;
}
}
s_pos = c_pos + 1;
} while (s_count++ < 36);
if (s_count < 20) continue;
//? Process cpu usage since last update, 100'000 because (100 percent * 1000 milliseconds) for correct conversion //? Process cpu usage since last update, 100'000 because (100 percent * 1000 milliseconds) for correct conversion
cpu = static_cast<double>(100000 * (cpu_t - cache[pid].cpu_t) / since_last) / clk_tck; cpu = static_cast<double>(100000 * (cpu_t - cache[pid].cpu_t) / since_last) / clk_tck;
@ -252,27 +280,17 @@ namespace Proc {
//? Update cache with latest cpu times //? Update cache with latest cpu times
cache[pid].cpu_t = cpu_t; cache[pid].cpu_t = cpu_t;
} }
else continue;
//* Get RSS memory in bytes from /proc/[pid]/statm //* Get RSS memory in bytes from /proc/[pid]/statm
if (fs::exists((string)d.path() + "/statm")) { pread.open(d.path() / "statm");
pread.clear(); if (pread.good()) {
ifstream pread((string)d.path() + "/statm"); pread.ignore(SSmax, ' ');
if (pread.good()) { pread >> rss_mem;
pread.ignore(numeric_limits<streamsize>::max(), ' '); rss_mem *= page_size;
pread >> rss_mem;
rss_mem *= page_size;
}
pread.close(); pread.close();
} }
//* Match filter if defined
if (!filter.empty() &&
pid_str.find(filter) == string::npos &&
cache[pid].name.find(filter) == string::npos &&
cache[pid].cmd.find(filter) == string::npos &&
cache[pid].user.find(filter) == string::npos
) continue;
//* Create proc_info //* Create proc_info
procs.push_back(proc_info(pid, cache[pid].name, cache[pid].cmd, threads, cache[pid].user, rss_mem, cpu, cpu_s, state, cpu_n, p_nice, ppid)); procs.push_back(proc_info(pid, cache[pid].name, cache[pid].cmd, threads, cache[pid].user, rss_mem, cpu, cpu_s, state, cpu_n, p_nice, ppid));
} }
@ -322,9 +340,8 @@ namespace Proc {
//* Initialize needed variables for collect //* Initialize needed variables for collect
void init(){ void init(){
clk_tck = sysconf(_SC_CLK_TCK);
tstamp = time_ms(); tstamp = time_ms();
passwd_path = (fs::exists(fs::path("/etc/passwd"))) ? fs::path("/etc/passwd") : passwd_path; passwd_path = (access("/etc/passwd", R_OK) != -1) ? fs::path("/etc/passwd") : passwd_path;
uint i = 0; uint i = 0;
for (auto& item : sort_array) sort_map[item] = i++; for (auto& item : sort_array) sort_map[item] = i++;
} }

View File

@ -35,6 +35,51 @@ using namespace Tools;
namespace Theme { namespace Theme {
const unordered_map<string, string> Default_theme = {
{ "main_bg", "#00" },
{ "main_fg", "#cc" },
{ "title", "#ee" },
{ "hi_fg", "#969696" },
{ "selected_bg", "#7e2626" },
{ "selected_fg", "#ee" },
{ "inactive_fg", "#40" },
{ "graph_text", "#60" },
{ "meter_bg", "#40" },
{ "proc_misc", "#0de756" },
{ "cpu_box", "#3d7b46" },
{ "mem_box", "#8a882e" },
{ "net_box", "#423ba5" },
{ "proc_box", "#923535" },
{ "div_line", "#30" },
{ "temp_start", "#4897d4" },
{ "temp_mid", "#5474e8" },
{ "temp_end", "#ff40b6" },
{ "cpu_start", "#50f095" },
{ "cpu_mid", "#f2e266" },
{ "cpu_end", "#fa1e1e" },
{ "free_start", "#223014" },
{ "free_mid", "#b5e685" },
{ "free_end", "#dcff85" },
{ "cached_start", "#0b1a29" },
{ "cached_mid", "#74e6fc" },
{ "cached_end", "#26c5ff" },
{ "available_start", "#292107" },
{ "available_mid", "#ffd77a" },
{ "available_end", "#ffb814" },
{ "used_start", "#3b1f1c" },
{ "used_mid", "#d9626d" },
{ "used_end", "#ff4769" },
{ "download_start", "#231a63" },
{ "download_mid", "#4f43a3" },
{ "download_end", "#b0a9de" },
{ "upload_start", "#510554" },
{ "upload_mid", "#7d4180" },
{ "upload_end", "#dcafde" },
{ "process_start", "#80d0a3" },
{ "process_mid", "#dcd179" },
{ "process_end", "#d45454" }
};
namespace { namespace {
//* Convert 24-bit colors to 256 colors using 6x6x6 color cube //* Convert 24-bit colors to 256 colors using 6x6x6 color cube
int truecolor_to_256(uint r, uint g, uint b){ int truecolor_to_256(uint r, uint g, uint b){
@ -146,7 +191,7 @@ namespace Theme {
vector<string> t_rgb; vector<string> t_rgb;
string depth; string depth;
colors.clear(); rgbs.clear(); colors.clear(); rgbs.clear();
for (auto& [name, color] : Global::Default_theme) { for (auto& [name, color] : Default_theme) {
depth = (name.ends_with("bg")) ? "bg" : "fg"; depth = (name.ends_with("bg")) ? "bg" : "fg";
if (source.contains(name)) { if (source.contains(name)) {
if (source.at(name)[0] == '#') { if (source.at(name)[0] == '#') {

View File

@ -37,6 +37,21 @@ using std::string, std::vector, std::regex, std::max, std::to_string, std::cin;
//? ------------------------------------------------- NAMESPACES ------------------------------------------------------ //? ------------------------------------------------- NAMESPACES ------------------------------------------------------
namespace Symbols {
const string h_line = "";
const string v_line = "";
const string left_up = "";
const string right_up = "";
const string left_down = "";
const string right_down = "";
const string title_left = "";
const string title_right = "";
const string div_up = "";
const string div_down = "";
const array<string, 10> superscript = { "", "¹", "²", "³", "", "", "", "", "", "" };
}
//* Collection of escape codes for text style and formatting //* Collection of escape codes for text style and formatting
namespace Fx { namespace Fx {
//* Escape sequence start //* Escape sequence start
@ -228,34 +243,34 @@ namespace Tools {
} }
//* Return current time since epoch in milliseconds //* Return current time since epoch in milliseconds
uint64_t time_ms(){ inline uint64_t time_ms(){
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count(); return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
} }
//* Check if a string is a valid bool value //* Check if a string is a valid bool value
bool isbool(string str){ inline bool isbool(string& str){
return (str == "true") || (str == "false") || (str == "True") || (str == "False"); return (str == "true") || (str == "false") || (str == "True") || (str == "False");
} }
//* Check if a string is a valid integer value //* Check if a string is a valid integer value
bool isint(string str){ inline bool isint(string& str){
return all_of(str.begin(), str.end(), ::isdigit); return all_of(str.begin(), str.end(), ::isdigit);
} }
//* Left-trim <t_str> from <str> and return string //* Left-trim <t_str> from <str> and return string
string ltrim(string str, string t_str = " "){ inline string ltrim(string str, string t_str = " "){
while (str.starts_with(t_str)) str.erase(0, t_str.size()); while (str.starts_with(t_str)) str.erase(0, t_str.size());
return str; return str;
} }
//* Right-trim <t_str> from <str> and return string //* Right-trim <t_str> from <str> and return string
string rtrim(string str, string t_str = " "){ inline string rtrim(string str, string t_str = " "){
while (str.ends_with(t_str)) str.resize(str.size() - t_str.size()); while (str.ends_with(t_str)) str.resize(str.size() - t_str.size());
return str; return str;
} }
//* Left-right-trim <t_str> from <str> and return string //* Left-right-trim <t_str> from <str> and return string
string trim(string str, string t_str = " "){ inline string trim(string str, string t_str = " "){
return ltrim(rtrim(str, t_str), t_str); return ltrim(rtrim(str, t_str), t_str);
} }
@ -352,6 +367,10 @@ namespace Tools {
return out; return out;
} }
//? Units for floating_humanizer function
const array<string, 11> Units_bit = {"bit", "Kib", "Mib", "Gib", "Tib", "Pib", "Eib", "Zib", "Yib", "Bib", "GEb"};
const array<string, 11> Units_byte = {"Byte", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB", "BiB", "GEB"};
//* Scales up in steps of 1024 to highest possible unit and returns string with unit suffixed //* Scales up in steps of 1024 to highest possible unit and returns string with unit suffixed
//* bit=True or defaults to bytes //* bit=True or defaults to bytes
//* start=int to set 1024 multiplier starting unit //* start=int to set 1024 multiplier starting unit
@ -359,7 +378,7 @@ namespace Tools {
string floating_humanizer(uint64_t value, bool shorten=false, uint start=0, bool bit=false, bool per_second=false){ string floating_humanizer(uint64_t value, bool shorten=false, uint start=0, bool bit=false, bool per_second=false){
string out; string out;
uint mult = (bit) ? 8 : 1; uint mult = (bit) ? 8 : 1;
auto& units = (bit) ? Global::Units_bit : Global::Units_byte; auto& units = (bit) ? Units_bit : Units_byte;
value *= 100 * mult; value *= 100 * mult;