2018-12-31 20:04:05 +01:00
|
|
|
//
|
2018-12-31 23:02:34 +01:00
|
|
|
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019
|
2018-12-31 20:04:05 +01:00
|
|
|
//
|
|
|
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
|
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
|
|
//
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include "td/utils/common.h"
|
|
|
|
#include "td/utils/logging.h"
|
|
|
|
#include "td/utils/Slice.h"
|
|
|
|
#include "td/utils/Status.h"
|
|
|
|
#include "td/utils/StringBuilder.h"
|
|
|
|
|
|
|
|
#include <cstdint>
|
|
|
|
#include <limits>
|
|
|
|
#include <type_traits>
|
|
|
|
#include <utility>
|
|
|
|
|
|
|
|
namespace td {
|
|
|
|
|
2018-02-03 17:49:39 +01:00
|
|
|
char *str_dup(Slice str);
|
2018-12-31 20:04:05 +01:00
|
|
|
|
|
|
|
template <class T>
|
|
|
|
std::pair<T, T> split(T s, char delimiter = ' ') {
|
|
|
|
auto delimiter_pos = s.find(delimiter);
|
|
|
|
if (delimiter_pos == string::npos) {
|
|
|
|
return {std::move(s), T()};
|
|
|
|
} else {
|
|
|
|
return {s.substr(0, delimiter_pos), s.substr(delimiter_pos + 1)};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class T>
|
|
|
|
vector<T> full_split(T s, char delimiter = ' ') {
|
|
|
|
vector<T> result;
|
2018-11-15 22:11:03 +01:00
|
|
|
if (s.empty()) {
|
|
|
|
return result;
|
|
|
|
}
|
2018-09-22 13:24:02 +02:00
|
|
|
while (true) {
|
|
|
|
auto delimiter_pos = s.find(delimiter);
|
|
|
|
if (delimiter_pos == string::npos) {
|
|
|
|
result.push_back(std::move(s));
|
|
|
|
return result;
|
|
|
|
} else {
|
|
|
|
result.push_back(s.substr(0, delimiter_pos));
|
|
|
|
s = s.substr(delimiter_pos + 1);
|
|
|
|
}
|
2018-12-31 20:04:05 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-03 17:49:39 +01:00
|
|
|
string implode(vector<string> v, char delimiter = ' ');
|
2018-12-31 20:04:05 +01:00
|
|
|
|
|
|
|
namespace detail {
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
struct transform_helper {
|
|
|
|
template <class Func>
|
|
|
|
auto transform(const T &v, const Func &f) {
|
|
|
|
vector<decltype(f(*v.begin()))> result;
|
|
|
|
result.reserve(v.size());
|
|
|
|
for (auto &x : v) {
|
|
|
|
result.push_back(f(x));
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class Func>
|
|
|
|
auto transform(T &&v, const Func &f) {
|
|
|
|
vector<decltype(f(std::move(*v.begin())))> result;
|
|
|
|
result.reserve(v.size());
|
|
|
|
for (auto &x : v) {
|
|
|
|
result.push_back(f(std::move(x)));
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace detail
|
|
|
|
|
|
|
|
template <class T, class Func>
|
|
|
|
auto transform(T &&v, const Func &f) {
|
|
|
|
return detail::transform_helper<std::decay_t<T>>().transform(std::forward<T>(v), f);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class T>
|
|
|
|
void reset_to_empty(T &value) {
|
|
|
|
using std::swap;
|
|
|
|
std::decay_t<T> tmp;
|
|
|
|
swap(tmp, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class T>
|
2019-01-19 02:09:58 +01:00
|
|
|
void append(vector<T> &destination, const vector<T> &source) {
|
2018-12-31 20:04:05 +01:00
|
|
|
destination.insert(destination.end(), source.begin(), source.end());
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class T>
|
2019-01-19 02:09:58 +01:00
|
|
|
void append(vector<T> &destination, vector<T> &&source) {
|
2018-12-31 20:04:05 +01:00
|
|
|
if (destination.empty()) {
|
|
|
|
destination.swap(source);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
destination.reserve(destination.size() + source.size());
|
2018-02-12 11:37:54 +01:00
|
|
|
for (auto &elem : source) {
|
2019-01-19 02:09:58 +01:00
|
|
|
destination.push_back(std::move(elem));
|
|
|
|
}
|
|
|
|
reset_to_empty(source);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class T>
|
|
|
|
void combine(vector<T> &destination, const vector<T> &source) {
|
|
|
|
append(destination, source);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class T>
|
|
|
|
void combine(vector<T> &destination, vector<T> &&source) {
|
|
|
|
if (destination.size() < source.size()) {
|
|
|
|
destination.swap(source);
|
|
|
|
}
|
|
|
|
destination.reserve(destination.size() + source.size());
|
|
|
|
for (auto &elem : source) {
|
2018-02-12 09:40:52 +01:00
|
|
|
destination.push_back(std::move(elem));
|
|
|
|
}
|
2018-12-31 20:04:05 +01:00
|
|
|
reset_to_empty(source);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool begins_with(Slice str, Slice prefix) {
|
|
|
|
return prefix.size() <= str.size() && prefix == Slice(str.data(), prefix.size());
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool ends_with(Slice str, Slice suffix) {
|
|
|
|
return suffix.size() <= str.size() && suffix == Slice(str.data() + str.size() - suffix.size(), suffix.size());
|
|
|
|
}
|
|
|
|
|
|
|
|
inline char to_lower(char c) {
|
|
|
|
if ('A' <= c && c <= 'Z') {
|
|
|
|
return static_cast<char>(c - 'A' + 'a');
|
|
|
|
}
|
|
|
|
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void to_lower_inplace(MutableSlice slice) {
|
|
|
|
for (auto &c : slice) {
|
|
|
|
c = to_lower(c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inline string to_lower(Slice slice) {
|
|
|
|
auto result = slice.str();
|
|
|
|
to_lower_inplace(result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline char to_upper(char c) {
|
|
|
|
if ('a' <= c && c <= 'z') {
|
|
|
|
return static_cast<char>(c - 'a' + 'A');
|
|
|
|
}
|
|
|
|
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void to_upper_inplace(MutableSlice slice) {
|
|
|
|
for (auto &c : slice) {
|
|
|
|
c = to_upper(c);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inline string to_upper(Slice slice) {
|
|
|
|
auto result = slice.str();
|
|
|
|
to_upper_inplace(result);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool is_space(char c) {
|
|
|
|
return c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\0' || c == '\v';
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool is_alpha(char c) {
|
|
|
|
c |= 0x20;
|
|
|
|
return 'a' <= c && c <= 'z';
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool is_digit(char c) {
|
|
|
|
return '0' <= c && c <= '9';
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool is_alnum(char c) {
|
|
|
|
return is_alpha(c) || is_digit(c);
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool is_hex_digit(char c) {
|
|
|
|
if (is_digit(c)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
c |= 0x20;
|
|
|
|
return 'a' <= c && c <= 'f';
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class T>
|
|
|
|
T trim(T str) {
|
|
|
|
auto begin = str.data();
|
|
|
|
auto end = begin + str.size();
|
|
|
|
while (begin < end && is_space(*begin)) {
|
|
|
|
begin++;
|
|
|
|
}
|
|
|
|
while (begin < end && is_space(end[-1])) {
|
|
|
|
end--;
|
|
|
|
}
|
|
|
|
if (static_cast<size_t>(end - begin) == str.size()) {
|
|
|
|
return std::move(str);
|
|
|
|
}
|
|
|
|
return T(begin, end);
|
|
|
|
}
|
|
|
|
|
2018-10-31 19:11:02 +01:00
|
|
|
string lpad0(string str, size_t size);
|
|
|
|
|
2018-02-03 17:49:39 +01:00
|
|
|
string oneline(Slice str);
|
2018-12-31 20:04:05 +01:00
|
|
|
|
|
|
|
template <class T>
|
|
|
|
std::enable_if_t<std::is_signed<T>::value, T> to_integer(Slice str) {
|
|
|
|
using unsigned_T = typename std::make_unsigned<T>::type;
|
|
|
|
unsigned_T integer_value = 0;
|
|
|
|
auto begin = str.begin();
|
|
|
|
auto end = str.end();
|
|
|
|
bool is_negative = false;
|
|
|
|
if (begin != end && *begin == '-') {
|
|
|
|
is_negative = true;
|
|
|
|
begin++;
|
|
|
|
}
|
|
|
|
while (begin != end && is_digit(*begin)) {
|
2018-10-25 20:29:10 +02:00
|
|
|
integer_value = static_cast<unsigned_T>(integer_value * 10 + static_cast<unsigned_T>(*begin++ - '0'));
|
2018-12-31 20:04:05 +01:00
|
|
|
}
|
|
|
|
if (integer_value > static_cast<unsigned_T>(std::numeric_limits<T>::max())) {
|
|
|
|
static_assert(~0 + 1 == 0, "Two's complement");
|
|
|
|
// Use ~x + 1 instead of -x to suppress Visual Studio warning.
|
|
|
|
integer_value = static_cast<unsigned_T>(~integer_value + 1);
|
|
|
|
is_negative = !is_negative;
|
|
|
|
|
|
|
|
if (integer_value > static_cast<unsigned_T>(std::numeric_limits<T>::max())) {
|
|
|
|
return std::numeric_limits<T>::min();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return is_negative ? static_cast<T>(-static_cast<T>(integer_value)) : static_cast<T>(integer_value);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class T>
|
|
|
|
std::enable_if_t<std::is_unsigned<T>::value, T> to_integer(Slice str) {
|
|
|
|
T integer_value = 0;
|
|
|
|
auto begin = str.begin();
|
|
|
|
auto end = str.end();
|
|
|
|
while (begin != end && is_digit(*begin)) {
|
2018-10-25 20:29:10 +02:00
|
|
|
integer_value = static_cast<T>(integer_value * 10 + static_cast<T>(*begin++ - '0'));
|
2018-12-31 20:04:05 +01:00
|
|
|
}
|
|
|
|
return integer_value;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class T>
|
|
|
|
Result<T> to_integer_safe(Slice str) {
|
|
|
|
auto res = to_integer<T>(str);
|
2018-07-08 01:47:46 +02:00
|
|
|
if ((PSLICE() << res) != str) {
|
2018-12-31 20:04:05 +01:00
|
|
|
return Status::Error(PSLICE() << "Can't parse \"" << str << "\" as number");
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline int hex_to_int(char c) {
|
|
|
|
if (is_digit(c)) {
|
|
|
|
return c - '0';
|
|
|
|
}
|
|
|
|
c |= 0x20;
|
|
|
|
if ('a' <= c && c <= 'f') {
|
|
|
|
return c - 'a' + 10;
|
|
|
|
}
|
|
|
|
return 16;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class T>
|
|
|
|
typename std::enable_if<std::is_unsigned<T>::value, T>::type hex_to_integer(Slice str) {
|
|
|
|
T integer_value = 0;
|
|
|
|
auto begin = str.begin();
|
|
|
|
auto end = str.end();
|
|
|
|
while (begin != end && is_hex_digit(*begin)) {
|
|
|
|
integer_value = static_cast<T>(integer_value * 16 + hex_to_int(*begin++));
|
|
|
|
}
|
|
|
|
return integer_value;
|
|
|
|
}
|
|
|
|
|
2018-02-03 21:01:33 +01:00
|
|
|
double to_double(Slice str);
|
2018-12-31 20:04:05 +01:00
|
|
|
|
|
|
|
template <class T>
|
|
|
|
T clamp(T value, T min_value, T max_value) {
|
|
|
|
if (value < min_value) {
|
|
|
|
return min_value;
|
|
|
|
}
|
|
|
|
if (value > max_value) {
|
|
|
|
return max_value;
|
|
|
|
}
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
2018-04-24 20:17:21 +02:00
|
|
|
Result<string> hex_decode(Slice hex);
|
2018-05-17 20:08:51 +02:00
|
|
|
|
|
|
|
string url_encode(Slice str);
|
2018-04-24 20:17:21 +02:00
|
|
|
|
2018-12-31 20:04:05 +01:00
|
|
|
// run-time checked narrowing cast (type conversion):
|
|
|
|
|
|
|
|
namespace detail {
|
|
|
|
template <class T, class U>
|
2018-09-07 02:41:21 +02:00
|
|
|
struct is_same_signedness
|
|
|
|
: public std::integral_constant<bool, std::is_signed<T>::value == std::is_signed<U>::value> {};
|
2018-12-31 20:04:05 +01:00
|
|
|
|
|
|
|
template <class T, class Enable = void>
|
|
|
|
struct safe_undeflying_type {
|
|
|
|
using type = T;
|
|
|
|
};
|
|
|
|
|
|
|
|
template <class T>
|
|
|
|
struct safe_undeflying_type<T, std::enable_if_t<std::is_enum<T>::value>> {
|
|
|
|
using type = std::underlying_type_t<T>;
|
|
|
|
};
|
|
|
|
|
2018-07-22 00:09:10 +02:00
|
|
|
class NarrowCast {
|
|
|
|
const char *file_;
|
|
|
|
int line_;
|
2018-12-31 20:04:05 +01:00
|
|
|
|
2018-07-22 00:09:10 +02:00
|
|
|
public:
|
|
|
|
NarrowCast(const char *file, int line) : file_(file), line_(line) {
|
|
|
|
}
|
2018-12-31 20:04:05 +01:00
|
|
|
|
2018-07-22 00:09:10 +02:00
|
|
|
template <class R, class A>
|
|
|
|
R cast(const A &a) {
|
|
|
|
using RT = typename safe_undeflying_type<R>::type;
|
|
|
|
using AT = typename safe_undeflying_type<A>::type;
|
2018-12-31 20:04:05 +01:00
|
|
|
|
2018-07-22 00:09:10 +02:00
|
|
|
static_assert(std::is_integral<RT>::value, "expected integral type to cast to");
|
|
|
|
static_assert(std::is_integral<AT>::value, "expected integral type to cast from");
|
|
|
|
|
|
|
|
auto r = R(a);
|
|
|
|
CHECK(A(r) == a) << static_cast<AT>(a) << " " << static_cast<RT>(r) << " " << file_ << " " << line_;
|
|
|
|
CHECK((is_same_signedness<RT, AT>::value) || ((static_cast<RT>(r) < RT{}) == (static_cast<AT>(a) < AT{})))
|
|
|
|
<< static_cast<AT>(a) << " " << static_cast<RT>(r) << " " << file_ << " " << line_;
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
} // namespace detail
|
|
|
|
|
|
|
|
#define narrow_cast detail::NarrowCast(__FILE__, __LINE__).cast
|
2018-12-31 20:04:05 +01:00
|
|
|
|
|
|
|
template <class R, class A>
|
|
|
|
Result<R> narrow_cast_safe(const A &a) {
|
|
|
|
using RT = typename detail::safe_undeflying_type<R>::type;
|
|
|
|
using AT = typename detail::safe_undeflying_type<A>::type;
|
|
|
|
|
|
|
|
static_assert(std::is_integral<RT>::value, "expected integral type to cast to");
|
|
|
|
static_assert(std::is_integral<AT>::value, "expected integral type to cast from");
|
|
|
|
|
|
|
|
auto r = R(a);
|
|
|
|
if (!(A(r) == a)) {
|
|
|
|
return Status::Error("Narrow cast failed");
|
|
|
|
}
|
|
|
|
if (!((detail::is_same_signedness<RT, AT>::value) || ((static_cast<RT>(r) < RT{}) == (static_cast<AT>(a) < AT{})))) {
|
|
|
|
return Status::Error("Narrow cast failed");
|
|
|
|
}
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <int Alignment, class T>
|
|
|
|
bool is_aligned_pointer(const T *pointer) {
|
|
|
|
static_assert(Alignment > 0 && (Alignment & (Alignment - 1)) == 0, "Wrong alignment");
|
|
|
|
return (reinterpret_cast<std::uintptr_t>(static_cast<const void *>(pointer)) & (Alignment - 1)) == 0;
|
|
|
|
}
|
|
|
|
|
2018-09-07 02:41:21 +02:00
|
|
|
namespace detail {
|
2018-08-13 19:15:09 +02:00
|
|
|
template <typename T>
|
|
|
|
struct reversion_wrapper {
|
|
|
|
T &iterable;
|
|
|
|
};
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
auto begin(reversion_wrapper<T> w) {
|
2018-09-21 19:57:20 +02:00
|
|
|
return w.iterable.rbegin();
|
2018-08-13 19:15:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
auto end(reversion_wrapper<T> w) {
|
2018-09-21 19:57:20 +02:00
|
|
|
return w.iterable.rend();
|
2018-08-13 19:15:09 +02:00
|
|
|
}
|
2018-09-07 02:41:21 +02:00
|
|
|
} // namespace detail
|
2018-08-13 19:15:09 +02:00
|
|
|
|
|
|
|
template <typename T>
|
2018-09-07 02:41:21 +02:00
|
|
|
detail::reversion_wrapper<T> reversed(T &iterable) {
|
2018-08-13 19:15:09 +02:00
|
|
|
return {iterable};
|
|
|
|
}
|
|
|
|
|
2018-12-26 17:11:15 +01:00
|
|
|
string zero_encode(Slice data);
|
|
|
|
|
|
|
|
string zero_decode(Slice data);
|
|
|
|
|
|
|
|
string zero_one_encode(Slice data);
|
|
|
|
|
|
|
|
string zero_one_decode(Slice data);
|
|
|
|
|
2018-12-31 20:04:05 +01:00
|
|
|
} // namespace td
|