2018-02-03 17:49:39 +01:00
|
|
|
//
|
2022-12-31 22:28:08 +01:00
|
|
|
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023
|
2018-02-03 17:49:39 +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)
|
|
|
|
//
|
|
|
|
#include "td/utils/misc.h"
|
|
|
|
|
2018-02-11 16:58:18 +01:00
|
|
|
#include "td/utils/port/thread_local.h"
|
2022-11-17 22:25:36 +01:00
|
|
|
#include "td/utils/utf8.h"
|
2018-02-11 16:58:18 +01:00
|
|
|
|
2018-02-03 17:49:39 +01:00
|
|
|
#include <algorithm>
|
|
|
|
#include <cstdlib>
|
2018-02-03 20:40:06 +01:00
|
|
|
#include <locale>
|
|
|
|
#include <sstream>
|
2018-02-03 17:49:39 +01:00
|
|
|
|
|
|
|
namespace td {
|
|
|
|
|
|
|
|
char *str_dup(Slice str) {
|
2021-10-18 18:26:14 +02:00
|
|
|
auto *res = static_cast<char *>(std::malloc(str.size() + 1));
|
2018-02-03 17:49:39 +01:00
|
|
|
if (res == nullptr) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
std::copy(str.begin(), str.end(), res);
|
|
|
|
res[str.size()] = '\0';
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2019-05-07 16:10:20 +02:00
|
|
|
string implode(const vector<string> &v, char delimiter) {
|
2018-02-03 17:49:39 +01:00
|
|
|
string result;
|
2020-04-21 18:08:01 +02:00
|
|
|
for (size_t i = 0; i < v.size(); i++) {
|
|
|
|
if (i != 0) {
|
2018-02-03 17:49:39 +01:00
|
|
|
result += delimiter;
|
|
|
|
}
|
2020-04-21 18:08:01 +02:00
|
|
|
result += v[i];
|
2018-02-03 17:49:39 +01:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2020-10-22 11:22:35 +02:00
|
|
|
string lpad(string str, size_t size, char c) {
|
2018-10-31 19:11:02 +01:00
|
|
|
if (str.size() >= size) {
|
|
|
|
return str;
|
|
|
|
}
|
2020-10-22 11:22:35 +02:00
|
|
|
return string(size - str.size(), c) + str;
|
|
|
|
}
|
|
|
|
|
|
|
|
string lpad0(string str, size_t size) {
|
|
|
|
return lpad(std::move(str), size, '0');
|
2018-10-31 19:11:02 +01:00
|
|
|
}
|
|
|
|
|
2020-10-30 14:21:13 +01:00
|
|
|
string rpad(string str, size_t size, char c) {
|
|
|
|
if (str.size() >= size) {
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
return str + string(size - str.size(), c);
|
|
|
|
}
|
|
|
|
|
2018-02-03 17:49:39 +01:00
|
|
|
string oneline(Slice str) {
|
|
|
|
string result;
|
|
|
|
result.reserve(str.size());
|
|
|
|
bool after_new_line = true;
|
|
|
|
for (auto c : str) {
|
2020-10-22 11:09:32 +02:00
|
|
|
if (c != '\n' && c != '\r') {
|
2018-02-03 17:49:39 +01:00
|
|
|
if (after_new_line) {
|
|
|
|
if (c == ' ') {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
after_new_line = false;
|
|
|
|
}
|
|
|
|
result += c;
|
2020-10-22 11:09:32 +02:00
|
|
|
} else if (!after_new_line) {
|
2018-02-03 17:49:39 +01:00
|
|
|
after_new_line = true;
|
|
|
|
result += ' ';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while (!result.empty() && result.back() == ' ') {
|
|
|
|
result.pop_back();
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2022-11-17 22:25:36 +01:00
|
|
|
namespace detail {
|
|
|
|
Status get_to_integer_safe_error(Slice str) {
|
|
|
|
auto status = Status::Error(PSLICE() << "Can't parse \"" << str << "\" as an integer");
|
|
|
|
if (!check_utf8(status.message())) {
|
|
|
|
status = Status::Error("Strings must be encoded in UTF-8");
|
|
|
|
}
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
} // namespace detail
|
|
|
|
|
2018-02-03 21:01:33 +01:00
|
|
|
double to_double(Slice str) {
|
2018-02-03 20:40:06 +01:00
|
|
|
static TD_THREAD_LOCAL std::stringstream *ss;
|
|
|
|
if (init_thread_local<std::stringstream>(ss)) {
|
2018-10-26 16:11:20 +02:00
|
|
|
auto previous_locale = ss->imbue(std::locale::classic());
|
2018-02-03 20:40:06 +01:00
|
|
|
} else {
|
|
|
|
ss->str(std::string());
|
|
|
|
ss->clear();
|
|
|
|
}
|
|
|
|
ss->write(str.begin(), narrow_cast<std::streamsize>(str.size()));
|
|
|
|
|
|
|
|
double result = 0.0;
|
|
|
|
*ss >> result;
|
|
|
|
return result;
|
2018-02-03 17:49:39 +01:00
|
|
|
}
|
|
|
|
|
2018-04-24 20:17:21 +02:00
|
|
|
Result<string> hex_decode(Slice hex) {
|
|
|
|
if (hex.size() % 2 != 0) {
|
|
|
|
return Status::Error("Wrong hex string length");
|
|
|
|
}
|
|
|
|
string result(hex.size() / 2, '\0');
|
|
|
|
for (size_t i = 0; i < result.size(); i++) {
|
|
|
|
int high = hex_to_int(hex[i + i]);
|
|
|
|
int low = hex_to_int(hex[i + i + 1]);
|
|
|
|
if (high == 16 || low == 16) {
|
|
|
|
return Status::Error("Wrong hex string");
|
|
|
|
}
|
|
|
|
result[i] = static_cast<char>(high * 16 + low); // TODO implementation-defined
|
|
|
|
}
|
|
|
|
return std::move(result);
|
|
|
|
}
|
|
|
|
|
2019-09-02 15:49:18 +02:00
|
|
|
string hex_encode(Slice data) {
|
|
|
|
const char *hex = "0123456789abcdef";
|
|
|
|
string res;
|
|
|
|
res.reserve(2 * data.size());
|
|
|
|
for (unsigned char c : data) {
|
|
|
|
res.push_back(hex[c >> 4]);
|
|
|
|
res.push_back(hex[c & 15]);
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2018-05-17 20:08:51 +02:00
|
|
|
static bool is_url_char(char c) {
|
|
|
|
return is_alnum(c) || c == '-' || c == '.' || c == '_' || c == '~';
|
|
|
|
}
|
|
|
|
|
2019-09-02 15:49:18 +02:00
|
|
|
string url_encode(Slice data) {
|
|
|
|
size_t length = 3 * data.size();
|
|
|
|
for (auto c : data) {
|
2018-05-17 20:08:51 +02:00
|
|
|
length -= 2 * is_url_char(c);
|
|
|
|
}
|
2019-09-02 15:49:18 +02:00
|
|
|
if (length == data.size()) {
|
|
|
|
return data.str();
|
2018-05-17 20:08:51 +02:00
|
|
|
}
|
|
|
|
string result;
|
|
|
|
result.reserve(length);
|
2019-09-02 15:49:18 +02:00
|
|
|
for (auto c : data) {
|
2018-05-17 20:08:51 +02:00
|
|
|
if (is_url_char(c)) {
|
|
|
|
result += c;
|
|
|
|
} else {
|
|
|
|
auto ch = static_cast<unsigned char>(c);
|
|
|
|
result += '%';
|
|
|
|
result += "0123456789ABCDEF"[ch / 16];
|
|
|
|
result += "0123456789ABCDEF"[ch % 16];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CHECK(result.size() == length);
|
|
|
|
return result;
|
|
|
|
}
|
2018-12-26 17:11:15 +01:00
|
|
|
|
2020-09-14 17:58:39 +02:00
|
|
|
size_t url_decode(Slice from, MutableSlice to, bool decode_plus_sign_as_space) {
|
|
|
|
size_t to_i = 0;
|
|
|
|
CHECK(to.size() >= from.size());
|
|
|
|
for (size_t from_i = 0, n = from.size(); from_i < n; from_i++) {
|
|
|
|
if (from[from_i] == '%' && from_i + 2 < n) {
|
|
|
|
int high = hex_to_int(from[from_i + 1]);
|
|
|
|
int low = hex_to_int(from[from_i + 2]);
|
|
|
|
if (high < 16 && low < 16) {
|
|
|
|
to[to_i++] = static_cast<char>(high * 16 + low);
|
|
|
|
from_i += 2;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
to[to_i++] = decode_plus_sign_as_space && from[from_i] == '+' ? ' ' : from[from_i];
|
|
|
|
}
|
|
|
|
return to_i;
|
|
|
|
}
|
|
|
|
|
2021-05-24 22:47:18 +02:00
|
|
|
string url_decode(Slice from, bool decode_plus_sign_as_space) {
|
|
|
|
string to;
|
|
|
|
to.resize(from.size());
|
|
|
|
to.resize(url_decode(from, to, decode_plus_sign_as_space));
|
|
|
|
return to;
|
|
|
|
}
|
|
|
|
|
2020-09-14 17:58:39 +02:00
|
|
|
MutableSlice url_decode_inplace(MutableSlice str, bool decode_plus_sign_as_space) {
|
|
|
|
size_t result_size = url_decode(str, str, decode_plus_sign_as_space);
|
|
|
|
str.truncate(result_size);
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
2019-09-02 15:49:18 +02:00
|
|
|
string buffer_to_hex(Slice buffer) {
|
|
|
|
const char *hex = "0123456789ABCDEF";
|
|
|
|
string res(2 * buffer.size(), '\0');
|
|
|
|
for (std::size_t i = 0; i < buffer.size(); i++) {
|
|
|
|
auto c = buffer.ubegin()[i];
|
|
|
|
res[2 * i] = hex[c & 15];
|
|
|
|
res[2 * i + 1] = hex[c >> 4];
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2018-12-26 17:11:15 +01:00
|
|
|
namespace {
|
|
|
|
|
2018-11-11 12:38:04 +01:00
|
|
|
template <class F>
|
|
|
|
string x_decode(Slice s, F &&f) {
|
|
|
|
string res;
|
|
|
|
for (size_t n = s.size(), i = 0; i < n; i++) {
|
|
|
|
if (i + 1 < n && f(s[i])) {
|
|
|
|
res.append(static_cast<unsigned char>(s[i + 1]), s[i]);
|
|
|
|
i++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
res.push_back(s[i]);
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
2018-12-26 17:11:15 +01:00
|
|
|
|
2018-11-11 12:38:04 +01:00
|
|
|
template <class F>
|
|
|
|
string x_encode(Slice s, F &&f) {
|
|
|
|
string res;
|
|
|
|
for (size_t n = s.size(), i = 0; i < n; i++) {
|
|
|
|
res.push_back(s[i]);
|
|
|
|
if (f(s[i])) {
|
|
|
|
unsigned char cnt = 1;
|
|
|
|
while (cnt < 250 && i + cnt < n && s[i + cnt] == s[i]) {
|
|
|
|
cnt++;
|
|
|
|
}
|
2018-12-26 17:11:15 +01:00
|
|
|
res.push_back(static_cast<char>(cnt));
|
2018-11-11 12:38:04 +01:00
|
|
|
i += cnt - 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
2018-12-26 17:11:15 +01:00
|
|
|
|
|
|
|
bool is_zero(unsigned char c) {
|
2018-11-11 12:38:04 +01:00
|
|
|
return c == 0;
|
|
|
|
}
|
2018-12-26 17:11:15 +01:00
|
|
|
|
|
|
|
bool is_zero_or_one(unsigned char c) {
|
2018-11-11 12:38:04 +01:00
|
|
|
return c == 0 || c == 0xff;
|
|
|
|
}
|
2018-12-26 17:11:15 +01:00
|
|
|
|
|
|
|
} // namespace
|
2018-11-11 12:38:04 +01:00
|
|
|
|
|
|
|
std::string zero_encode(Slice data) {
|
2018-12-26 17:11:15 +01:00
|
|
|
return x_encode(data, is_zero);
|
2018-11-11 12:38:04 +01:00
|
|
|
}
|
|
|
|
std::string zero_decode(Slice data) {
|
2018-12-26 17:11:15 +01:00
|
|
|
return x_decode(data, is_zero);
|
2018-11-11 12:38:04 +01:00
|
|
|
}
|
|
|
|
std::string zero_one_encode(Slice data) {
|
2018-12-26 17:11:15 +01:00
|
|
|
return x_encode(data, is_zero_or_one);
|
2018-11-11 12:38:04 +01:00
|
|
|
}
|
|
|
|
std::string zero_one_decode(Slice data) {
|
2018-12-26 17:11:15 +01:00
|
|
|
return x_decode(data, is_zero_or_one);
|
2018-11-11 12:38:04 +01:00
|
|
|
}
|
2018-05-17 20:08:51 +02:00
|
|
|
|
2018-02-03 17:49:39 +01:00
|
|
|
} // namespace td
|