#include "Utils.h"
#include
#include
#include
#include
#include
#include
#include
#include
namespace Utils
{
void toLower(std::string &str) noexcept {
std::transform(str.begin(), str.end(), str.begin(), ::tolower);
}
std::string getLowerString(const std::string &str) {
std::string copy = str;
toLower(copy);
return copy;
}
void trim(std::string &str)
{
static const std::array whitespace { " \t\n\v\f\r" };
const size_t last = str.find_last_not_of(whitespace.data() );
if (std::string::npos == last) {
return str.clear();
}
str.assign(
str,
str.find_first_not_of(whitespace.data() ),
last + 1
);
}
std::string getTrimmedString(const std::string &str) {
std::string copy = str;
trim(copy);
return copy;
}
std::vector explode(const std::string &str, const char sep)
{
std::vector values;
for (size_t pos = 0; std::string::npos != pos;)
{
const size_t delimiter = str.find(sep, pos);
std::string value = str.substr(pos, delimiter - pos);
trim(value);
values.emplace_back(std::move(value) );
pos = delimiter;
if (std::string::npos != pos) {
++pos;
}
}
return values;
}
std::string encodeHtmlSymbols(const std::string &str)
{
std::string buf;
buf.reserve(str.length() );
for (size_t pos = 0; pos < str.length(); ++pos) {
switch (str[pos])
{
case '&': buf.append("&"); break;
case '\"': buf.append("""); break;
case '\'': buf.append("'"); break;
case '<': buf.append("<"); break;
case '>': buf.append(">"); break;
default: buf.push_back(str[pos]); break;
}
}
return buf;
}
std::string binToHexString(const void *binData, const size_t dataSize)
{
std::string str(dataSize * 2, 0);
const uint8_t *bin = reinterpret_cast(binData);
static const std::array hexDigits { "0123456789abcdef" };
for (size_t i = dataSize - 1; std::numeric_limits::max() != i; --i) {
str[i * 2 + 0] = hexDigits[bin[i] >> 4];
str[i * 2 + 1] = hexDigits[bin[i] & 0x0F];
}
return str;
}
static unsigned char hexStringToBinEncodeSymbol(const char c) noexcept
{
if (c >= '0' && c <= '9') {
return static_cast(c - 0x30);
}
else if (c >= 'a' && c <= 'f') {
return static_cast(c - 0x57);
}
else if (c >= 'A' && c <= 'F') {
return static_cast(c - 0x37);
}
return 0;
}
std::string hexStringToBin(const std::string &hexStr)
{
std::string bin(hexStr.length() / 2, 0);
for (size_t i = 0; i < bin.length(); ++i) {
const char a = hexStr[i * 2 + 0];
const char b = hexStr[i * 2 + 1];
bin[i] = char(
(hexStringToBinEncodeSymbol(a) << 4) | hexStringToBinEncodeSymbol(b)
);
}
return bin;
}
enum Endianness {
INIT = 0,
LITE = 1,
BIGE = 2
};
uint64_t hton64(const uint64_t host64) noexcept
{
static Endianness endian = Endianness::INIT;
union {
uint64_t ull;
unsigned char c[sizeof(uint64_t)];
} x;
if (endian == Endianness::INIT) {
x.ull = 0x01;
endian = (x.c[7] == 0x01ULL) ? Endianness::BIGE : Endianness::LITE;
}
if (endian == Endianness::BIGE) {
return host64;
}
x.ull = host64;
unsigned char c;
c = x.c[0]; x.c[0] = x.c[7]; x.c[7] = c;
c = x.c[1]; x.c[1] = x.c[6]; x.c[6] = c;
c = x.c[2]; x.c[2] = x.c[5]; x.c[5] = c;
c = x.c[3]; x.c[3] = x.c[4]; x.c[4] = c;
return x.ull;
}
uint64_t ntoh64(const uint64_t net64) noexcept {
return hton64(net64);
}
void hton24(void *dest, const uint32_t src) noexcept
{
static Endianness endian = Endianness::INIT;
union {
uint32_t ui;
uint8_t c[sizeof(uint32_t)];
} x;
if (endian == Endianness::INIT) {
x.ui = 0x01;
endian = (x.c[3] == 0x01) ? Endianness::BIGE : Endianness::LITE;
}
x.ui = src;
if (endian == Endianness::BIGE) {
x.ui <<= 8;
} else {
uint8_t c = x.c[0];
x.c[0] = x.c[2];
x.c[2] = c;
}
std::copy(x.c, x.c + 3, reinterpret_cast(dest) );
}
uint32_t ntoh24(const void *src24) noexcept
{
static Endianness endian = Endianness::INIT;
union {
uint32_t ui;
uint8_t c[sizeof(uint32_t)];
} x;
if (endian == Endianness::INIT) {
x.ui = 0x01;
endian = (x.c[3] == 0x01) ? Endianness::BIGE : Endianness::LITE;
}
if (endian == Endianness::BIGE) {
return *reinterpret_cast(src24) >> 8;
}
const uint8_t *addr = reinterpret_cast(src24);
x.ui = 0;
x.c[0] = addr[2];
x.c[1] = addr[1];
x.c[2] = addr[0];
return x.ui;
}
std::string getUniqueName() {
size_t time = size_t(
std::chrono::high_resolution_clock::now().time_since_epoch().count()
);
time = hton64(time);
return binToHexString(&time, sizeof(time) );
}
constexpr uint8_t PACK_NUMBER_SIZE_BYTE = 252;
constexpr uint8_t PACK_NUMBER_SIZE_16 = 253;
constexpr uint8_t PACK_NUMBER_SIZE_32 = 254;
constexpr uint8_t PACK_NUMBER_SIZE_MAX = 255;
size_t getPackNumberSize(const size_t number) noexcept
{
if (number <= PACK_NUMBER_SIZE_BYTE) {
return sizeof(uint8_t);
}
else if (number <= std::numeric_limits::max() ) {
return sizeof(uint8_t) + sizeof(uint16_t);
}
else if (number <= std::numeric_limits::max() ) {
return sizeof(uint8_t) + sizeof(uint32_t);
}
return sizeof(uint8_t) + sizeof(size_t);
}
size_t getPackStringSize(const std::string &str) noexcept {
return getPackNumberSize(str.length() ) + str.length();
}
uint8_t *packPointer(uint8_t *dest, void *pointer) noexcept {
*reinterpret_cast(dest) = pointer;
return dest + sizeof(void *);
}
uint8_t *packNumber(uint8_t *dest, const size_t number) noexcept
{
if (number <= PACK_NUMBER_SIZE_BYTE) {
*dest = static_cast(number);
dest += sizeof(uint8_t);
}
else if (number <= std::numeric_limits::max() ) {
*dest = PACK_NUMBER_SIZE_16;
dest += sizeof(uint8_t);
*reinterpret_cast(dest) = static_cast(number);
dest += sizeof(uint16_t);
}
else if (number <= std::numeric_limits::max() ) {
*dest = PACK_NUMBER_SIZE_32;
dest += sizeof(uint8_t);
*reinterpret_cast(dest) = static_cast(number);
dest += sizeof(uint32_t);
} else {
*dest = PACK_NUMBER_SIZE_MAX;
dest += sizeof(uint8_t);
*reinterpret_cast(dest) = number;
dest += sizeof(size_t);
}
return dest;
}
uint8_t *packString(uint8_t *dest, const std::string &str) noexcept {
dest = packNumber(dest, str.length() );
std::memcpy(dest, str.data(), str.length() );
return dest + str.length();
}
void packPointer(std::vector &buf, void *pointer) {
buf.resize(buf.size() + sizeof(void *) );
uint8_t *dest = reinterpret_cast(buf.data() + buf.size() - sizeof(void *) );
*reinterpret_cast(dest) = pointer;
}
void packNumber(std::vector &buf, const size_t number)
{
if (number <= PACK_NUMBER_SIZE_BYTE) {
buf.emplace_back(number);
}
else if (number <= std::numeric_limits::max() ) {
buf.emplace_back(PACK_NUMBER_SIZE_16);
buf.resize(buf.size() + sizeof(uint16_t) );
*reinterpret_cast(buf.data() + buf.size() - sizeof(uint16_t) ) = static_cast(number);
}
else if (number <= std::numeric_limits::max() ) {
buf.emplace_back(PACK_NUMBER_SIZE_32);
buf.resize(buf.size() + sizeof(uint32_t) );
*reinterpret_cast(buf.data() + buf.size() - sizeof(uint32_t) ) = static_cast(number);
} else {
buf.emplace_back(PACK_NUMBER_SIZE_MAX);
buf.resize(buf.size() + sizeof(size_t) );
*reinterpret_cast(buf.data() + buf.size() - sizeof(size_t) ) = number;
}
}
void packString(std::vector &buf, const std::string &str)
{
packNumber(buf, str.length() );
if (str.length() ) {
buf.insert(
buf.end(),
str.cbegin(),
str.cend()
);
}
}
const uint8_t *unpackPointer(void **pointer, const uint8_t *src) noexcept {
*pointer = *reinterpret_cast(
const_cast(
static_cast(src)
)
);
return src + sizeof(void *);
}
const uint8_t *unpackNumber(size_t *number, const uint8_t *src) noexcept
{
*number = *src;
src += sizeof(uint8_t);
if (*number <= PACK_NUMBER_SIZE_BYTE) {
}
else if (*number == PACK_NUMBER_SIZE_16) {
*number = *reinterpret_cast(src);
src += sizeof(uint16_t);
}
else if (*number == PACK_NUMBER_SIZE_32) {
*number = *reinterpret_cast(src);
src += sizeof(uint32_t);
} else {
*number = *reinterpret_cast(src);
src += sizeof(size_t);
}
return src;
}
const uint8_t *unpackString(std::string &str, const uint8_t *src)
{
size_t length;
src = unpackNumber(&length, src);
str.assign(src, src + length);
return src + length;
}
static const std::unordered_map map_days {
{"Mon", 0}, {"Tue", 1}, {"Wed", 2}, {"Thu", 3}, {"Fri", 4}, {"Sat", 5}, {"Sun", 6}
};
static const std::unordered_map map_months {
{"Jan", 0}, {"Feb", 1}, {"Mar", 2}, {"Apr", 3}, {"May", 4}, {"Jun", 5}, {"Jul", 6}, {"Aug", 7}, {"Sep", 8}, {"Oct", 9}, {"Nov", 10}, {"Dec", 11}
};
static const std::unordered_map map_zones {
{"GMT", 0}, {"UT", 0},
{"EST", -5 * 3600}, {"EDT", -4 * 3600}, {"CST", -6 * 3600}, {"CDT", -5 * 3600}, {"MST", -7 * 3600}, {"MDT", -6 * 3600}, {"PST", -8 * 3600}, {"PDT", -7 * 3600},
{"Z", 0}, {"A", -1 * 3600}, {"M", -12 * 3600}, {"N", 1 * 3600}, {"Y", 12 * 3600}
};
/**
* Parse RFC 882 (ddd, dd MMM yyyy HH:mm:ss K)
*/
time_t rfc822DatetimeToTimestamp(const std::string &strTime) {
std::tm tc {};
// Parse RFC 882 (ddd, dd MMM yyyy HH:mm:ss K)
size_t pos = strTime.find_first_not_of(' ');
size_t delimiter = strTime.find(',', pos);
if (std::string::npos == delimiter || delimiter - pos != 3) {
return ~0;
}
const std::string day = strTime.substr(pos, delimiter - pos);
auto const it_day = map_days.find(day);
if (map_days.cend() != it_day) {
tc.tm_wday = it_day->second;
} else {
return ~0;
}
pos = strTime.find_first_not_of(' ', delimiter + 1);
delimiter = strTime.find_first_of(' ', pos);
if (std::string::npos == delimiter) {
return ~0;
}
tc.tm_mday = std::atoi(strTime.data() + pos);
pos = strTime.find_first_not_of(' ', delimiter + 1);
delimiter = strTime.find_first_of(' ', pos);
if (std::string::npos == delimiter || delimiter - pos != 3) {
return ~0;
}
const std::string month = strTime.substr(pos, delimiter - pos);
auto const it_mon = map_months.find(month);
if (map_months.cend() != it_mon) {
tc.tm_mon = it_mon->second;
} else {
return ~0;
}
pos = strTime.find_first_not_of(' ', delimiter + 1);
delimiter = strTime.find_first_of(' ', pos);
if (std::string::npos == delimiter) {
return ~0;
}
tc.tm_year = std::atoi(strTime.data() + pos) - 1900;
pos = strTime.find_first_not_of(' ', delimiter + 1);
delimiter = strTime.find_first_of(':', pos);
if (std::string::npos == delimiter) {
return ~0;
}
tc.tm_hour = std::atoi(strTime.data() + pos);
pos = strTime.find_first_not_of(' ', delimiter + 1);
delimiter = strTime.find_first_of(':', pos);
if (std::string::npos == delimiter) {
return ~0;
}
tc.tm_min = std::atoi(strTime.data() + pos);
pos = strTime.find_first_not_of(' ', delimiter + 1);
delimiter = strTime.find_first_of(' ', pos);
if (std::string::npos == delimiter) {
return ~0;
}
tc.tm_sec = std::atoi(strTime.data() + pos);
pos = strTime.find_first_not_of(' ', delimiter + 1);
delimiter = strTime.find_first_of(' ', pos);
if (std::string::npos == delimiter) {
delimiter = strTime.length();
}
if (std::string::npos == pos || delimiter - pos > 5) {
return ~0;
}
const std::string zone = strTime.substr(pos, delimiter - pos);
auto const it_zone = map_zones.find(zone);
int timezone = 0;
if (map_zones.cend() != it_zone) {
timezone = it_zone->second;
}
else if (zone.length() == 5 && ('+' == zone.front() || '-' == zone.front() ) )
{
std::array hours;
std::array minutes;
zone.copy(hours.data(), 2, 1);
zone.copy(minutes.data(), 2, 3);
timezone = std::atoi(hours.data()) * 3600;
timezone += std::atoi(minutes.data()) * 60;
if (zone.front() == '-') {
timezone *= -1;
}
} else {
return ~0;
}
tc.tm_isdst = -1;
return std::mktime(&tc) - timezone;
}
static time_t localToGmt(const time_t timestamp)
{
#ifdef WIN32
std::tm stm {};
::gmtime_s(&stm, ×tamp);
return std::mktime(&stm);
#else
std::tm stm {};
::gmtime_r(×tamp, &stm);
return std::mktime(&stm);
#endif
}
/**
* Convert c-string (__DATE__ " " __TIME__) to std::time_t
*/
time_t predefinedDatetimeToTimestamp(const char *strTime)
{
std::tm tc {};
const char *ptrStr = std::strchr(strTime, ' ');
if (nullptr == ptrStr) {
return ~0;
}
const std::string month(strTime, ptrStr);
auto const it_mon = map_months.find(month);
if (map_months.cend() != it_mon) {
tc.tm_mon = it_mon->second;
} else {
return ~0;
}
++ptrStr;
// Fix for MS __DATE__
if (' ' == *ptrStr) {
++ptrStr;
}
strTime = std::strchr(ptrStr, ' ');
if (nullptr == strTime) {
return ~0;
}
tc.tm_mday = std::atoi(ptrStr);
++strTime;
ptrStr = std::strchr(strTime, ' ');
if (nullptr == ptrStr) {
return ~0;
}
tc.tm_year = std::atoi(strTime) - 1900;
++ptrStr;
strTime = std::strchr(ptrStr, ':');
if (nullptr == strTime) {
return ~0;
}
tc.tm_hour = std::atoi(ptrStr);
++strTime;
ptrStr = std::strchr(strTime, ':');
if (nullptr == ptrStr) {
return ~0;
}
tc.tm_min = std::atoi(strTime);
++ptrStr;
tc.tm_sec = std::atoi(ptrStr);
return localToGmt(std::mktime(&tc) );
}
/**
* Convert std::time_t to RFC822 std::string
*/
std::string getDatetimeAsString(time_t tTime, const bool isGmtTime)
{
std::array buf;
if (tTime == ~0) {
std::time(&tTime);
}
#ifdef WIN32
std::tm stm {};
isGmtTime
? ::localtime_s(&stm, &tTime)
: ::gmtime_s(&stm, &tTime);
auto const len = std::strftime(
buf.data(),
buf.size(),
"%a, %d %b %Y %H:%M:%S GMT", // RFC 822
&stm
);
#else
std::tm stm {};
isGmtTime
? ::localtime_r(&tTime, &stm)
: ::gmtime_r(&tTime, &stm);
auto const len = std::strftime(
buf.data(),
buf.size(),
"%a, %d %b %G %H:%M:%S GMT", // RFC 822
&stm
);
#endif
return std::string(buf.data(), buf.data() + len);
}
std::string predefinedDatetimeToRfc822(const char *strTime) {
const std::time_t time = predefinedDatetimeToTimestamp(strTime);
return getDatetimeAsString(time, false);
}
size_t getNumberLength(size_t number) noexcept
{
size_t length = 0;
do {
++length;
number /= 10;
}
while (number);
return length;
}
bool parseCookies(
const std::string &cookieHeader,
std::unordered_multimap &cookies
) {
if (cookieHeader.empty() ) {
return true;
}
for (
size_t cur_pos = 0, next_value;
std::string::npos != cur_pos;
cur_pos = next_value
) {
next_value = cookieHeader.find(';', cur_pos);
size_t delimiter = cookieHeader.find('=', cur_pos);
if (std::string::npos == delimiter || delimiter > next_value) {
return false;
}
std::string key = cookieHeader.substr(
cur_pos,
delimiter - cur_pos
);
trim(key);
key = urlDecode(key);
++delimiter;
std::string value = cookieHeader.substr(
delimiter,
std::string::npos != next_value
? next_value - delimiter
: next_value
);
trim(value);
value = urlDecode(value);
cookies.emplace(
std::move(key),
std::move(value)
);
if (std::string::npos != next_value) {
++next_value;
}
}
return true;
}
static inline bool isCharUrlAllowed(const char c) noexcept {
return c == '-' || c == '_' || c == '.' || c == '~';
}
std::string urlEncode(const std::string &str)
{
std::string encoded;
static const std::array hexDigits { "0123456789ABCDEF" };
for (size_t i = 0; i < str.length(); ++i)
{
const unsigned char c = static_cast(str[i]);
if (std::isalnum(c) || isCharUrlAllowed(char(c) ) ) {
encoded.push_back(char(c) );
}
else if (' ' == c) {
encoded.push_back('+');
} else {
const uint8_t a = c >> 4;
const uint8_t b = c & 0x0F;
encoded.push_back('%');
encoded.push_back(hexDigits[a]);
encoded.push_back(hexDigits[b]);
}
}
return encoded;
}
std::string urlDecode(const std::string &str)
{
std::string decoded;
for (size_t i = 0; i < str.length(); ++i)
{
unsigned char c = static_cast(str[i]);
if ('%' == c) {
if (i + 2 < str.length() ) {
const char a = str[++i];
const char b = str[++i];
c = static_cast(
(hexStringToBinEncodeSymbol(a) << 4) | hexStringToBinEncodeSymbol(b)
);
}
}
else if ('+' == c) {
c = ' ';
}
decoded.push_back(char(c) );
}
return decoded;
}
}