Files
externals/linux/include/ccapi_cpp/ccapi_util_private.h
T
2022-09-08 11:16:15 -04:00

1051 lines
36 KiB
C++

#ifndef INCLUDE_CCAPI_CPP_CCAPI_UTIL_PRIVATE_H_
#define INCLUDE_CCAPI_CPP_CCAPI_UTIL_PRIVATE_H_
#include <unistd.h>
#include <algorithm>
#include <array>
#include <cmath>
#include <cstdint>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <map>
#include <numeric>
#include <random>
#include <regex>
#include <set>
#include <sstream>
#include <stdexcept>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "ccapi_cpp/ccapi_date.h"
#include "ccapi_cpp/ccapi_logger.h"
#include "ccapi_cpp/ccapi_util.h"
#include "openssl/evp.h"
namespace ccapi {
/**
* Utilities.
*/
class UtilString CCAPI_FINAL {
public:
static std::string roundInputBySignificantFigure(double input, int numSignificantFigure, int roundDirection) {
const auto& splitted = UtilString::split(UtilString::printDoubleScientific(input), 'e');
double a = std::stod(splitted.at(0)) * std::pow(10, numSignificantFigure - 1);
double b;
if (roundDirection > 0) {
b = std::ceil(a);
} else if (roundDirection < 0) {
b = std::floor(a);
} else {
b = std::round(a);
}
std::string c = std::to_string(static_cast<int>(b));
int exponent = std::stoi(splitted.at(1)) - (numSignificantFigure - 1);
std::string output;
if (exponent >= 0) {
output = c + std::string(exponent, '0');
} else if (-exponent <= c.size() - 1) {
output = c.substr(0, c.size() + exponent);
output += ".";
output += c.substr(c.size() + exponent);
} else {
output = std::string(-exponent - c.size() + 1, '0');
output += ".";
output += c;
}
return output;
}
static std::string replaceFirstOccurrence(std::string& s, const std::string& toReplace, const std::string& replaceWith) {
std::size_t pos = s.find(toReplace);
if (pos == std::string::npos) {
return s;
};
return s.replace(pos, toReplace.length(), replaceWith);
}
static bool endsWith(const std::string& mainStr, const std::string& toMatch) {
if (mainStr.size() >= toMatch.size() && mainStr.compare(mainStr.size() - toMatch.size(), toMatch.size(), toMatch) == 0) {
return true;
} else {
return false;
}
}
static std::string printDoubleScientific(double number, int precision = CCAPI_PRINT_DOUBLE_PRECISION_DEFAULT) {
std::stringstream ss;
ss << std::setprecision(precision) << std::scientific << number;
// ss << number;
return ss.str();
}
static bool isNumber(const std::string& s) {
return !s.empty() && std::find_if(s.begin(), s.end(), [](unsigned char c) { return !std::isdigit(c); }) == s.end();
}
// https://stackoverflow.com/questions/440133/how-do-i-create-a-random-alpha-numeric-string-in-c
static std::string generateRandomString(const size_t length) {
static const auto ch_set = std::vector<char>({'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'});
static std::default_random_engine rng(std::random_device{}());
static std::uniform_int_distribution<> dist(0, ch_set.size() - 1);
static auto randchar = []() { return ch_set[dist(rng)]; };
std::string str(length, 0);
std::generate_n(str.begin(), length, randchar);
return str;
}
static std::string generateUuidV4() {
static std::random_device rd;
static std::mt19937 gen(rd());
static std::uniform_int_distribution<> dis(0, 15);
static std::uniform_int_distribution<> dis2(8, 11);
std::stringstream ss;
int i;
ss << std::hex;
for (i = 0; i < 8; i++) {
ss << dis(gen);
}
ss << "-";
for (i = 0; i < 4; i++) {
ss << dis(gen);
}
ss << "-4";
for (i = 0; i < 3; i++) {
ss << dis(gen);
}
ss << "-";
ss << dis2(gen);
for (i = 0; i < 3; i++) {
ss << dis(gen);
}
ss << "-";
for (i = 0; i < 12; i++) {
ss << dis(gen);
};
return ss.str();
}
static std::vector<std::string> split(const std::string& in, char sep) {
std::vector<std::string> r;
r.reserve(std::count(in.begin(), in.end(), sep) + 1);
for (auto p = in.begin();; ++p) {
auto q = p;
p = std::find(p, in.end(), sep);
r.emplace_back(q, p);
if (p == in.end()) {
return r;
}
}
}
static std::vector<std::string> split(const std::string& original, const std::string& delimiter) {
std::string s = original;
std::vector<std::string> output;
size_t pos = 0;
std::string token;
while ((pos = s.find(delimiter)) != std::string::npos) {
token = s.substr(0, pos);
output.emplace_back(std::move(token));
s.erase(0, pos + delimiter.length());
}
output.emplace_back(std::move(s));
return output;
}
static std::set<std::string> splitToSet(const std::string& original, const std::string& delimiter) {
std::string s = original;
std::set<std::string> output;
size_t pos = 0;
std::string token;
while ((pos = s.find(delimiter)) != std::string::npos) {
token = s.substr(0, pos);
output.insert(std::move(token));
s.erase(0, pos + delimiter.length());
}
output.insert(std::move(s));
return output;
}
static std::string join(const std::vector<std::string>& strings, const std::string& delimiter) {
switch (strings.size()) {
case 0:
return "";
case 1:
return strings[0];
default:
std::ostringstream joined;
std::copy(strings.begin(), strings.end() - 1, std::ostream_iterator<std::string>(joined, delimiter.c_str()));
joined << *strings.rbegin();
return joined.str();
}
}
static std::string toUpper(const std::string& input) {
std::string output(input);
std::transform(output.begin(), output.end(), output.begin(), ::toupper);
return output;
}
static std::string toLower(const std::string& input) {
std::string output(input);
std::transform(output.begin(), output.end(), output.begin(), ::tolower);
return output;
}
static std::string ltrim(const std::string& original, const std::string& chars = "\t\n\v\f\r ") {
std::string str = original;
str.erase(0, str.find_first_not_of(chars));
return str;
}
static std::string ltrim(const std::string& original, char c) {
std::string str = original;
str.erase(0, str.find_first_not_of(c));
return str;
}
static void ltrimInPlace(std::string& str, const std::string& chars = "\t\n\v\f\r ") { str.erase(0, str.find_first_not_of(chars)); }
static void ltrimInPlace(std::string& str, char c) { str.erase(0, str.find_first_not_of(c)); }
static std::string rtrim(const std::string& original, const std::string& chars = "\t\n\v\f\r ") {
std::string str = original;
str.erase(str.find_last_not_of(chars) + 1);
return str;
}
static std::string rtrim(const std::string& original, char c) {
std::string str = original;
str.erase(str.find_last_not_of(c) + 1);
return str;
}
static void rtrimInPlace(std::string& str, const std::string& chars = "\t\n\v\f\r ") { str.erase(str.find_last_not_of(chars) + 1); }
static void rtrimInPlace(std::string& str, char c) { str.erase(str.find_last_not_of(c) + 1); }
static std::string trim(const std::string& original, const std::string& chars = "\t\n\v\f\r ") { return ltrim(rtrim(original, chars), chars); }
static std::string trim(const std::string& original, char c) { return ltrim(rtrim(original, c), c); }
static void trimInPlace(std::string& str, const std::string& chars = "\t\n\v\f\r ") {
rtrimInPlace(str, chars);
ltrimInPlace(str, chars);
}
static void trimInPlace(std::string& str, char c) {
rtrimInPlace(str, c);
ltrimInPlace(str, c);
}
static std::string firstNCharacter(const std::string& str, const size_t n) {
if (str.length() > n) {
return str.substr(0, n) + "...";
} else {
return str;
}
}
static std::string normalizeDecimalString(const std::string& original) {
if (original.find('.') != std::string::npos) {
std::string str(original);
rtrimInPlace(str, "0");
rtrimInPlace(str, ".");
return str;
} else {
return original;
}
}
static std::string normalizeDecimalString(const char* data) {
std::string str(data);
if (str.find('.') != std::string::npos) {
rtrimInPlace(str, "0");
rtrimInPlace(str, ".");
}
return str;
}
static std::string leftPadTo(const std::string& str, const size_t padToLength, const char paddingChar) {
std::string copy = str;
if (padToLength > copy.size()) {
copy.insert(0, padToLength - copy.size(), paddingChar);
}
return copy;
}
static std::string rightPadTo(const std::string& str, const size_t padToLength, const char paddingChar) {
std::string copy = str;
if (padToLength > copy.size()) {
copy.append(padToLength - copy.size(), paddingChar);
}
return copy;
}
};
class UtilTime CCAPI_FINAL {
public:
static std::string convertFIXTimeToISO(const std::string& fixTime) {
// convert 20200925-15:55:28.093490622 to 2020-09-25T15:55:28.093490622Z
std::string output;
output += fixTime.substr(0, 4);
output += "-";
output += fixTime.substr(4, 2);
output += "-";
output += fixTime.substr(6, 2);
output += "T";
output += fixTime.substr(9);
output += "Z";
return output;
}
static std::string convertTimePointToFIXTime(const TimePoint& tp) {
int year, month, day, hour, minute, second, millisecond;
timePointToParts(tp, year, month, day, hour, minute, second, millisecond);
std::string output;
output += std::to_string(year);
auto monthStr = std::to_string(month);
output += std::string(2 - monthStr.length(), '0');
output += monthStr;
auto dayStr = std::to_string(day);
output += std::string(2 - dayStr.length(), '0');
output += dayStr;
output += "-";
auto hourStr = std::to_string(hour);
output += std::string(2 - hourStr.length(), '0');
output += hourStr;
output += ":";
auto minuteStr = std::to_string(minute);
output += std::string(2 - minuteStr.length(), '0');
output += minuteStr;
output += ":";
auto secondStr = std::to_string(second);
output += std::string(2 - secondStr.length(), '0');
output += secondStr;
output += ".";
auto millisecondStr = std::to_string(millisecond);
output += std::string(3 - millisecondStr.length(), '0');
output += millisecondStr;
return output;
}
static void timePointToParts(TimePoint tp, int& year, int& month, int& day, int& hour, int& minute, int& second, int& millisecond) {
auto epoch_sec = std::chrono::time_point_cast<std::chrono::seconds>(tp).time_since_epoch().count();
auto day_sec = epoch_sec - (epoch_sec % 86400);
auto days_since_epoch = day_sec / 86400;
// see http://howardhinnant.github.io/date_algorithms.html
days_since_epoch += 719468;
const unsigned era = (days_since_epoch >= 0 ? days_since_epoch : days_since_epoch - 146096) / 146097;
const unsigned doe = static_cast<unsigned>(days_since_epoch - era * 146097);
const unsigned yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
year = static_cast<unsigned>(yoe) + era * 400;
const unsigned doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
const unsigned mp = (5 * doy + 2) / 153;
day = doy - (153 * mp + 2) / 5 + 1;
month = mp + (mp < 10 ? 3 : -9);
year += month <= 2;
auto in_day = tp - std::chrono::seconds(day_sec);
millisecond = std::chrono::time_point_cast<std::chrono::milliseconds>(in_day).time_since_epoch().count();
hour = millisecond / (60 * 60 * 1000);
millisecond -= hour * 60 * 60 * 1000;
minute = millisecond / (60 * 1000);
millisecond -= minute * 60 * 1000;
second = millisecond / 1000;
millisecond -= second * 1000;
}
static TimePoint now() {
auto now = std::chrono::system_clock::now();
return TimePoint(now);
}
static TimePoint parse(const std::string& s, const std::string& fmt = "%FT%TZ") {
TimePoint tp;
std::istringstream ss{s};
ss >> date::parse(fmt, tp);
if (ss.fail()) {
CCAPI_LOGGER_FATAL("unable to parse time string");
}
return tp;
}
static TimePoint makeTimePoint(const std::pair<long long, long long>& timePair) {
auto tp = TimePoint(std::chrono::duration<int64_t>(timePair.first));
tp += std::chrono::nanoseconds(timePair.second);
return tp;
}
static TimePoint makeTimePointMilli(const std::pair<long long, long long>& timePair) {
auto tp = TimePoint(std::chrono::milliseconds(timePair.first));
tp += std::chrono::nanoseconds(timePair.second);
return tp;
}
static std::pair<long long, long long> divide(const TimePoint& tp) {
auto then = tp.time_since_epoch();
auto s = std::chrono::duration_cast<std::chrono::seconds>(then);
then -= s;
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(then);
return std::make_pair(s.count(), ns.count());
}
static std::pair<long long, long long> divide(const std::string& seconds) {
if (seconds.find('.') != std::string::npos) {
std::string secondsCopy = seconds;
UtilString::rtrimInPlace(secondsCopy, '0');
UtilString::rtrimInPlace(secondsCopy, '.');
auto found = secondsCopy.find('.');
return std::make_pair(std::stoll(secondsCopy.substr(0, found)),
found != std::string::npos ? std::stoll(UtilString::rightPadTo(secondsCopy.substr(found + 1), 9, '0')) : 0);
} else {
return std::make_pair(std::stoll(seconds), 0);
}
}
static std::pair<long long, long long> divideMilli(const std::string& milliseconds) {
if (milliseconds.find('.') != std::string::npos) {
std::string millisecondsCopy = milliseconds;
UtilString::rtrimInPlace(millisecondsCopy, '0');
UtilString::rtrimInPlace(millisecondsCopy, '.');
auto found = millisecondsCopy.find('.');
return std::make_pair(std::stoll(millisecondsCopy.substr(0, found)),
found != std::string::npos ? std::stoll(UtilString::rightPadTo(millisecondsCopy.substr(found + 1), 6, '0')) : 0);
} else {
return std::make_pair(std::stoll(milliseconds), 0);
}
}
static std::pair<long long, long long> divideNanoWhole(const std::string& nanoseconds) {
return std::make_pair(std::stoll(nanoseconds.substr(0, nanoseconds.length() - 9)), std::stoll(nanoseconds.substr(nanoseconds.length() - 9)));
}
template <typename T = std::chrono::nanoseconds>
static std::string getISOTimestamp(const TimePoint& tp, const std::string& fmt = "%FT%TZ") {
return date::format(fmt.c_str(), date::floor<T>(tp));
}
static int getUnixTimestamp(const TimePoint& tp) {
auto then = tp.time_since_epoch();
auto s = std::chrono::duration_cast<std::chrono::seconds>(then);
return s.count();
}
static TimePoint makeTimePointFromMilliseconds(long long milliseconds) { return TimePoint(std::chrono::milliseconds(milliseconds)); }
static TimePoint makeTimePointFromSeconds(long seconds) { return TimePoint(std::chrono::seconds(seconds)); }
};
class UtilAlgorithm CCAPI_FINAL {
public:
enum class ShaVersion {
UNKNOWN,
SHA256,
SHA512,
};
public:
static std::string computeHash(const ShaVersion shaVersion, const std::string& unhashed, bool returnHex = false) {
EVP_MD_CTX* context = EVP_MD_CTX_new();
switch (shaVersion) {
case ShaVersion::SHA256:
EVP_DigestInit_ex(context, EVP_sha256(), NULL);
break;
case ShaVersion::SHA512:
EVP_DigestInit_ex(context, EVP_sha512(), NULL);
break;
default:
CCAPI_LOGGER_FATAL(CCAPI_UNSUPPORTED_VALUE);
}
EVP_DigestUpdate(context, unhashed.c_str(), unhashed.length());
unsigned char hash[EVP_MAX_MD_SIZE];
unsigned int lengthOfHash = 0;
EVP_DigestFinal_ex(context, hash, &lengthOfHash);
EVP_MD_CTX_free(context);
std::stringstream ss;
if (returnHex) {
for (unsigned int i = 0; i < lengthOfHash; ++i) {
ss << std::hex << std::setw(2) << std::setfill('0') << (int)hash[i];
}
} else {
for (unsigned int i = 0; i < lengthOfHash; ++i) {
ss << (char)hash[i];
}
}
return ss.str();
}
static std::string stringToHex(const std::string& input) {
static const char hex_digits[] = "0123456789abcdef";
std::string output;
output.reserve(input.length() * 2);
for (unsigned char c : input) {
output.push_back(hex_digits[c >> 4]);
output.push_back(hex_digits[c & 15]);
}
return output;
}
static int hexValue(unsigned char hex_digit) {
static const signed char hex_values[256] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
};
int value = hex_values[hex_digit];
if (value == -1) throw std::invalid_argument("invalid hex digit");
return value;
}
static std::string hexToString(const std::string& input) {
const auto len = input.length();
if (len & 1) throw std::invalid_argument("odd length");
std::string output;
output.reserve(len / 2);
for (auto it = input.begin(); it != input.end();) {
int hi = hexValue(*it++);
int lo = hexValue(*it++);
output.push_back(hi << 4 | lo);
}
return output;
}
// https://stackoverflow.com/questions/342409/how-do-i-base64-encode-decode-in-c
static std::string base64Encode(const std::string& input) {
static const unsigned char base64_table[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const unsigned char* src = reinterpret_cast<const unsigned char*>(input.c_str());
size_t len = input.length();
unsigned char *out, *pos;
const unsigned char *end, *in;
size_t olen;
olen = 4 * ((len + 2) / 3); /* 3-byte blocks to 4-byte */
if (olen < len) return std::string(); /* integer overflow */
std::string outStr;
outStr.resize(olen);
out = (unsigned char*)&outStr[0];
end = src + len;
in = src;
pos = out;
while (end - in >= 3) {
*pos++ = base64_table[in[0] >> 2];
*pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)];
*pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)];
*pos++ = base64_table[in[2] & 0x3f];
in += 3;
}
if (end - in) {
*pos++ = base64_table[in[0] >> 2];
if (end - in == 1) {
*pos++ = base64_table[(in[0] & 0x03) << 4];
*pos++ = '=';
} else {
*pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)];
*pos++ = base64_table[(in[1] & 0x0f) << 2];
}
*pos++ = '=';
}
return outStr;
}
static std::string base64Decode(const std::string& in) {
static const int B64index[256] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 63, 62, 62, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, 0,
0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0,
0, 0, 63, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51};
const void* data = in.c_str();
const size_t len = in.length();
unsigned char* p = (unsigned char*)data;
int pad = len > 0 && (len % 4 || p[len - 1] == '=');
const size_t L = ((len + 3) / 4 - pad) * 4;
std::string str(L / 4 * 3 + pad, '\0');
for (size_t i = 0, j = 0; i < L; i += 4) {
int n = B64index[p[i]] << 18 | B64index[p[i + 1]] << 12 | B64index[p[i + 2]] << 6 | B64index[p[i + 3]];
str[j++] = n >> 16;
str[j++] = n >> 8 & 0xFF;
str[j++] = n & 0xFF;
}
if (pad) {
int n = B64index[p[L]] << 18 | B64index[p[L + 1]] << 12;
str[str.size() - 1] = n >> 16;
if (len > L + 2 && p[L + 2] != '=') {
n |= B64index[p[L + 2]] << 6;
str.push_back(n >> 8 & 0xFF);
}
}
return str;
}
// https://github.com/brianloveswords/base64url
static std::string base64UrlFromBase64(const std::string& base64) {
return std::regex_replace(std::regex_replace(std::regex_replace(base64, std::regex("="), ""), std::regex("\\+"), "-"), std::regex("\\/"), "_");
}
static std::string base64FromBase64Url(const std::string& base64Url) {
auto segmentLength = 4;
auto stringLength = base64Url.size();
auto diff = stringLength % segmentLength;
if (!diff) {
return base64Url;
}
auto padLength = segmentLength - diff;
std::string paddedBase64Url(base64Url);
paddedBase64Url += std::string(padLength, '=');
return std::regex_replace(std::regex_replace(paddedBase64Url, std::regex("\\-"), "+"), std::regex("_"), "/");
}
static std::string base64UrlEncode(const std::string& in) { return base64UrlFromBase64(base64Encode(in)); }
static std::string base64UrlDecode(const std::string& in) { return base64Decode(base64FromBase64Url(in)); }
static double exponentialBackoff(double initial, double multiplier, double base, double exponent) { return initial + multiplier * (pow(base, exponent) - 1); }
template <typename InputIterator>
static uint_fast32_t crc(InputIterator first, InputIterator last);
};
template <typename InputIterator>
inline uint_fast32_t UtilAlgorithm::crc(InputIterator first, InputIterator last) {
static auto const table = []() {
auto const reversed_polynomial = uint_fast32_t{0xEDB88320uL};
// This is a function object that calculates the checksum for a value,
// then increments the value, starting from zero.
struct byte_checksum {
uint_fast32_t operator()() noexcept {
auto checksum = static_cast<uint_fast32_t>(n++);
for (auto i = 0; i < 8; ++i) checksum = (checksum >> 1) ^ ((checksum & 0x1u) ? reversed_polynomial : 0);
return checksum;
}
unsigned n = 0;
};
auto table = std::array<uint_fast32_t, 256>{};
std::generate(table.begin(), table.end(), byte_checksum{});
return table;
}();
// Calculate the checksum - make sure to clip to 32 bits, for systems that don't
// have a true (fast) 32-bit type.
return uint_fast32_t{0xFFFFFFFFuL} &
~std::accumulate(first, last, ~uint_fast32_t{0} & uint_fast32_t{0xFFFFFFFFuL},
[](uint_fast32_t checksum, std::uint_fast8_t value) { return table[(checksum ^ value) & 0xFFu] ^ (checksum >> 8); });
}
class UtilSystem CCAPI_FINAL {
public:
static bool getEnvAsBool(const std::string variableName, const bool defaultValue = false) {
const char* env_p = std::getenv(variableName.c_str());
if (env_p) {
return UtilString::toLower(std::string(env_p)) == "true";
} else {
return defaultValue;
}
}
static std::string getEnvAsString(const std::string variableName, const std::string defaultValue = "") {
const char* env_p = std::getenv(variableName.c_str());
if (env_p) {
return std::string(env_p);
} else {
return defaultValue;
}
}
static int getEnvAsInt(const std::string variableName, const int defaultValue = 0) {
const char* env_p = std::getenv(variableName.c_str());
if (env_p) {
return std::stoi(std::string(env_p));
} else {
return defaultValue;
}
}
static long getEnvAsLong(const std::string variableName, const long defaultValue = 0) {
const char* env_p = std::getenv(variableName.c_str());
if (env_p) {
return std::stol(std::string(env_p));
} else {
return defaultValue;
}
}
static float getEnvAsFloat(const std::string variableName, const float defaultValue = 0) {
const char* env_p = std::getenv(variableName.c_str());
if (env_p) {
return std::stof(std::string(env_p));
} else {
return defaultValue;
}
}
static double getEnvAsDouble(const std::string variableName, const double defaultValue = 0) {
const char* env_p = std::getenv(variableName.c_str());
if (env_p) {
return std::stod(std::string(env_p));
} else {
return defaultValue;
}
}
static bool checkEnvExist(const std::string& variableName) {
const char* env_p = std::getenv(variableName.c_str());
if (env_p) {
return true;
} else {
return false;
}
}
};
inline std::string size_tToString(const size_t& t) {
std::stringstream ss;
ss << t;
return ss.str();
}
template <typename T>
std::string intToHex(T i) {
std::stringstream stream;
stream << std::hex << i;
return stream.str();
}
template <typename T>
int ceilSearch(const std::vector<T>& c, int low, int high, T x) {
int i = 0;
if (x <= c[low]) {
return low;
}
for (i = low; i < high; i++) {
if (c[i] == x) {
return i;
}
if (c[i] < x && c[i + 1] >= x) {
return i + 1;
}
}
return -1;
}
template <typename T>
struct reversion_wrapper {
T& iterable;
};
template <typename T>
auto begin(reversion_wrapper<T> w) {
return std::rbegin(w.iterable);
}
template <typename T>
auto end(reversion_wrapper<T> w) {
return std::rend(w.iterable);
}
template <typename T>
reversion_wrapper<T> reverse(T&& iterable) {
return {iterable};
}
template <typename K, typename V>
bool firstNSame(const std::map<K, V>& c1, const std::map<K, V>& c2, size_t n) {
if (c1.empty() || c2.empty()) {
return c1.empty() && c2.empty();
}
size_t i = 0;
for (auto i1 = c1.begin(), i2 = c2.begin(); i1 != c1.end() && i2 != c2.end(); ++i1, ++i2) {
if (i >= n) {
return true;
}
if (i1 == c1.end() || i2 == c2.end()) {
return false;
}
if (i1->first != i2->first || i1->second != i2->second) {
return false;
}
++i;
}
return true;
}
template <typename K, typename V>
bool lastNSame(const std::map<K, V>& c1, const std::map<K, V>& c2, size_t n) {
if (c1.empty() || c2.empty()) {
return c1.empty() && c2.empty();
}
size_t i = 0;
for (auto i1 = c1.rbegin(), i2 = c2.rbegin(); i1 != c1.rend() || i2 != c2.rend(); ++i1, ++i2) {
if (i >= n) {
return true;
}
if (i1 == c1.rend() || i2 == c2.rend()) {
return false;
}
if (i1->first != i2->first || i1->second != i2->second) {
return false;
}
++i;
}
return true;
}
template <typename K, typename V>
void keepFirstN(std::map<K, V>& c, size_t n) {
if (!c.empty()) {
auto it = c.begin();
std::advance(it, std::min(n, c.size()));
c.erase(it, c.end());
}
}
template <typename K, typename V>
void keepLastN(std::map<K, V>& c, size_t n) {
if (!c.empty()) {
auto it = c.end();
std::advance(it, -std::min(n, c.size()));
c.erase(c.begin(), it);
}
}
template <typename T>
typename std::enable_if<std::is_same<decltype(std::declval<const T&>().toString()), std::string>::value, std::string>::type toString(const T& t) {
return t.toString();
}
template <typename T>
typename std::enable_if<std::is_same<decltype(std::declval<const T&>().toStringPretty()), std::string>::value, std::string>::type toStringPretty(
const T& t, const int space = 2, const int leftToIndent = 0, const bool indentFirstLine = true) {
return t.toStringPretty(space, leftToIndent, indentFirstLine);
}
template <typename T>
typename std::enable_if<std::is_integral<T>::value, std::string>::type toString(const T& t) {
return std::to_string(t);
}
template <typename T>
typename std::enable_if<std::is_same<decltype(std::to_string(std::declval<T&>())), std::string>::value, std::string>::type toStringPretty(
const T& t, const int space = 2, const int leftToIndent = 0, const bool indentFirstLine = true) {
std::string sl(leftToIndent, ' ');
std::string output = (indentFirstLine ? sl : "") + std::to_string(t);
return output;
}
template <typename T>
typename std::enable_if<std::is_floating_point<T>::value, std::string>::type toString(const T& t) {
std::stringstream ss;
ss << std::fixed;
ss << std::setprecision(CCAPI_PRINT_DOUBLE_PRECISION_DEFAULT);
ss << t;
return ss.str();
}
template <typename T>
typename std::enable_if<std::is_same<T, std::string>::value, std::string>::type toString(const T& t) {
return t;
}
template <typename T>
typename std::enable_if<std::is_same<T, std::string>::value, std::string>::type toStringPretty(const T& t, const int space = 2, const int leftToIndent = 0,
const bool indentFirstLine = true) {
std::string sl(leftToIndent, ' ');
std::string output = (indentFirstLine ? sl : "") + t;
return output;
}
template <typename T>
typename std::enable_if<std::is_same<T, TimePoint>::value, std::string>::type toString(const T& t) {
auto timePair = UtilTime::divide(t);
return "(" + std::to_string(timePair.first) + "," + std::to_string(timePair.second) + ")";
}
template <typename T>
typename std::string toString(const T* a, const size_t n) {
std::string output = "[";
for (int i = 0; i < n; i++) {
output += toString(a[i]);
if (i < n - 1) {
output += ", ";
}
}
output += "]";
return output;
}
template <typename T, typename... Args>
std::string toString(const std::unordered_set<T, Args...>& c);
template <typename T, typename... Args>
std::string toString(const std::set<T, Args...>& c);
template <typename K, typename V>
std::string toString(const std::map<K, V>& c);
template <typename K, typename V, typename... Args>
std::string toString(const std::unordered_map<K, V, Args...>& c);
template <typename K, typename V>
std::string firstNToString(const std::map<K, V>& c, const size_t n);
template <typename K, typename V>
std::string lastNToString(const std::map<K, V>& c, const size_t n);
template <typename T>
std::string toString(const std::vector<T>& c);
template <typename T>
std::string firstNToString(const std::vector<T>& c, const size_t n);
template <typename U, typename V>
std::string toString(const std::pair<U, V>& c) {
std::string output = "(";
output += toString(c.first);
output += ",";
output += toString(c.second);
output += ")";
return output;
}
template <typename T, typename... Args>
inline std::string toString(const std::unordered_set<T, Args...>& c) {
std::string output = "[";
auto size = c.size();
auto i = 0;
for (const auto& elem : c) {
output += toString(elem);
if (i < size - 1) {
output += ", ";
}
++i;
}
output += "]";
return output;
}
template <typename T, typename... Args>
inline std::string toString(const std::set<T, Args...>& c) {
std::string output = "[";
auto size = c.size();
auto i = 0;
for (const auto& elem : c) {
output += toString(elem);
if (i < size - 1) {
output += ", ";
}
++i;
}
output += "]";
return output;
}
template <typename K, typename V>
inline std::string toString(const std::map<K, V>& c) {
std::string output = "{";
auto size = c.size();
auto i = 0;
for (const auto& elem : c) {
output += toString(elem.first);
output += "=";
output += toString(elem.second);
if (i < size - 1) {
output += ", ";
}
++i;
}
output += "}";
return output;
}
template <typename K, typename V>
std::string toStringPretty(const std::map<K, V>& c, const int space = 2, const int leftToIndent = 0, const bool indentFirstLine = true) {
std::string sl(leftToIndent, ' ');
std::string output = (indentFirstLine ? sl : "") + "{\n";
auto size = c.size();
auto i = 0;
for (const auto& elem : c) {
output += toStringPretty(elem.first, space, space + leftToIndent, true);
output += " = ";
output += toStringPretty(elem.second, space, space + leftToIndent, false);
if (i < size - 1) {
output += ",\n";
}
++i;
}
output += "\n" + sl + "}";
return output;
}
template <typename K, typename V, typename... Args>
inline std::string toString(const std::unordered_map<K, V, Args...>& c) {
std::string output = "{";
auto size = c.size();
auto i = 0;
for (const auto& elem : c) {
output += toString(elem.first);
output += "=";
output += toString(elem.second);
if (i < size - 1) {
output += ", ";
}
++i;
}
output += "}";
return output;
}
template <typename K, typename V>
inline std::string firstNToString(const std::map<K, V>& c, const size_t n) {
std::string output = "{";
auto size = c.size();
auto i = 0;
for (const auto& elem : c) {
if (i >= n) {
break;
}
output += toString(elem.first);
output += "=";
output += toString(elem.second);
if (i < size - 1) {
output += ", ";
}
++i;
}
if (i < size - 1 && i > 0) {
output += "...";
}
output += "}";
return output;
}
template <typename K, typename V>
inline std::string lastNToString(const std::map<K, V>& c, const size_t n) {
std::string output = "{";
auto size = c.size();
auto i = 0;
for (const auto& elem : reverse(c)) {
if (i >= n) {
break;
}
output += toString(elem.first);
output += "=";
output += toString(elem.second);
if (i < size - 1) {
output += ", ";
}
++i;
}
if (i < size - 1 && i > 0) {
output += "...";
}
output += "}";
return output;
}
template <typename T>
inline std::string toString(const std::vector<T>& c) {
std::string output = "[ ";
auto size = c.size();
auto i = 0;
for (const auto& elem : c) {
output += toString(elem);
if (i < size - 1) {
output += ", ";
}
++i;
}
output += " ]";
return output;
}
template <typename T>
std::string toStringPretty(const std::vector<T>& c, const int space = 2, const int leftToIndent = 0, const bool indentFirstLine = true) {
std::string sl(leftToIndent, ' ');
std::string output = (indentFirstLine ? sl : "") + "[\n";
auto size = c.size();
auto i = 0;
for (const auto& elem : c) {
output += toStringPretty(elem, space, space + leftToIndent, true);
if (i < size - 1) {
output += ",\n";
}
++i;
}
output += "\n" + sl + "]";
return output;
}
template <typename T>
inline std::string firstNToString(const std::vector<T>& c, const size_t n) {
std::string output = "[ ";
auto size = c.size();
auto i = 0;
for (const auto& elem : c) {
if (i >= n) {
break;
}
output += toString(elem);
if (i < size - 1) {
output += ", ";
}
++i;
}
if (i < size - 1 && i > 0) {
output += "...";
}
output += " ]";
return output;
}
template <typename T>
std::string firstNToStringPretty(const std::vector<T>& c, const size_t n, const int space = 2, const int leftToIndent = 0, const bool indentFirstLine = true) {
std::string sl(leftToIndent, ' ');
std::string output = (indentFirstLine ? sl : "") + "[\n";
auto size = c.size();
auto i = 0;
for (const auto& elem : c) {
if (i >= n) {
break;
}
output += toStringPretty(elem, space, space + leftToIndent, true);
if (i < size - 1) {
output += ",\n";
}
++i;
}
if (i < size - 1 && i > 0) {
output += "...";
}
output += "\n" + sl + "]";
return output;
}
template <typename K, typename V>
std::map<V, std::vector<K> > invertMapMulti(const std::map<K, V>& c) {
std::map<V, std::vector<K> > output;
for (const auto& elem : c) {
output[elem.second].push_back(elem.first);
}
return output;
}
template <typename K, typename V>
std::map<V, K> invertMap(const std::map<K, V>& c) {
std::map<V, K> output;
for (const auto& elem : c) {
output.insert(std::make_pair(elem.second, elem.first));
}
return output;
}
template <template <class, class, class...> class C, typename K, typename V, typename... Args>
V mapGetWithDefault(const C<K, V, Args...>& m, const K& key, const V defaultValue = {}) {
typename C<K, V, Args...>::const_iterator it = m.find(key);
if (it == m.end()) {
return defaultValue;
}
return it->second;
}
} /* namespace ccapi */
#endif // INCLUDE_CCAPI_CPP_CCAPI_UTIL_PRIVATE_H_