mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
pow-testgiver support
This commit is contained in:
parent
dbde9c1c40
commit
f064b1047a
257 changed files with 6665 additions and 2608 deletions
|
@ -31,7 +31,7 @@ namespace td {
|
|||
class AesCtrByteFlow : public ByteFlowInplaceBase {
|
||||
public:
|
||||
void init(const UInt256 &key, const UInt128 &iv) {
|
||||
state_.init(key, iv);
|
||||
state_.init(as_slice(key), as_slice(iv));
|
||||
}
|
||||
void init(AesCtrState &&state) {
|
||||
state_ = std::move(state);
|
||||
|
|
|
@ -1,30 +1,22 @@
|
|||
/*
|
||||
This file is part of TON Blockchain source code.
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain is distributed in the hope that it will be useful,
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with TON Blockchain. If not, see <http://www.gnu.org/licenses/>.
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
You must obey the GNU General Public License in all respects for all
|
||||
of the code used other than OpenSSL. If you modify file(s) with this
|
||||
exception, you may extend this exception to your version of the file(s),
|
||||
but you are not obligated to do so. If you do not wish to do so, delete this
|
||||
exception statement from your version. If you delete this exception statement
|
||||
from all source files in the program, then also delete it here.
|
||||
|
||||
Copyright 2019-2020 Telegram Systems LLP
|
||||
Copyright 2017-2020 Telegram Systems LLP
|
||||
*/
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "td/utils/common.h"
|
||||
|
|
|
@ -28,6 +28,8 @@ char disable_linker_warning_about_empty_file_bignum_cpp TD_UNUSED;
|
|||
#include <openssl/bn.h>
|
||||
#include <openssl/crypto.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace td {
|
||||
|
||||
class BigNumContext::Impl {
|
||||
|
@ -103,8 +105,9 @@ BigNum BigNum::from_le_binary(Slice str) {
|
|||
#elif OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
|
||||
return BigNum(make_unique<Impl>(BN_lebin2bn(str.ubegin(), narrow_cast<int>(str.size()), nullptr)));
|
||||
#else
|
||||
LOG(FATAL) << "Unsupported from_le_binary";
|
||||
return BigNum();
|
||||
string str_copy = str.str();
|
||||
std::reverse(str_copy.begin(), str_copy.end());
|
||||
return from_binary(str_copy);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -133,14 +136,6 @@ BigNum BigNum::from_raw(void *openssl_big_num) {
|
|||
BigNum::BigNum(unique_ptr<Impl> &&impl) : impl_(std::move(impl)) {
|
||||
}
|
||||
|
||||
void BigNum::ensure_const_time() {
|
||||
#if !defined(OPENSSL_IS_BORINGSSL)
|
||||
BN_set_flags(impl_->big_num, BN_FLG_CONSTTIME);
|
||||
#else
|
||||
LOG(FATAL) << "Unsupported BN_FLG_CONSTTIME";
|
||||
#endif
|
||||
}
|
||||
|
||||
int BigNum::get_num_bits() const {
|
||||
return BN_num_bits(impl_->big_num);
|
||||
}
|
||||
|
@ -238,8 +233,9 @@ string BigNum::to_le_binary(int exact_size) const {
|
|||
#endif
|
||||
return res;
|
||||
#else
|
||||
LOG(FATAL) << "Unsupported to_le_binary";
|
||||
return "";
|
||||
string result = to_binary(exact_size);
|
||||
std::reverse(result.begin(), result.end());
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -55,7 +55,6 @@ class BigNum {
|
|||
|
||||
static BigNum from_binary(Slice str);
|
||||
|
||||
// Available only if OpenSSL >= 1.1.0
|
||||
static BigNum from_le_binary(Slice str);
|
||||
|
||||
static Result<BigNum> from_decimal(CSlice str);
|
||||
|
@ -66,8 +65,6 @@ class BigNum {
|
|||
|
||||
void set_value(uint32 new_value);
|
||||
|
||||
void ensure_const_time();
|
||||
|
||||
int get_num_bits() const;
|
||||
|
||||
int get_num_bytes() const;
|
||||
|
@ -84,7 +81,6 @@ class BigNum {
|
|||
|
||||
string to_binary(int exact_size = -1) const;
|
||||
|
||||
// Available only if OpenSSL >= 1.1.0
|
||||
string to_le_binary(int exact_size = -1) const;
|
||||
|
||||
string to_decimal() const;
|
||||
|
|
|
@ -127,12 +127,12 @@ Result<size_t> BufferedFdBase<FdT>::flush_write() {
|
|||
write_->sync_with_writer();
|
||||
size_t result = 0;
|
||||
while (!write_->empty() && ::td::can_write(*this)) {
|
||||
constexpr size_t buf_size = 20;
|
||||
IoSlice buf[buf_size];
|
||||
constexpr size_t BUF_SIZE = 20;
|
||||
IoSlice buf[BUF_SIZE];
|
||||
|
||||
auto it = write_->clone();
|
||||
size_t buf_i;
|
||||
for (buf_i = 0; buf_i < buf_size; buf_i++) {
|
||||
for (buf_i = 0; buf_i < BUF_SIZE; buf_i++) {
|
||||
Slice slice = it.prepare_read();
|
||||
if (slice.empty()) {
|
||||
break;
|
||||
|
|
|
@ -76,7 +76,8 @@ class UdpReaderHelper {
|
|||
}
|
||||
|
||||
private:
|
||||
enum : size_t { MAX_PACKET_SIZE = 2048, RESERVED_SIZE = MAX_PACKET_SIZE * 8 };
|
||||
static constexpr size_t MAX_PACKET_SIZE = 2048;
|
||||
static constexpr size_t RESERVED_SIZE = MAX_PACKET_SIZE * 8;
|
||||
UdpMessage message_;
|
||||
BufferSlice buffer_;
|
||||
};
|
||||
|
@ -110,7 +111,7 @@ class UdpReader {
|
|||
}
|
||||
|
||||
private:
|
||||
enum : size_t { BUFFER_SIZE = 16 };
|
||||
static constexpr size_t BUFFER_SIZE = 16;
|
||||
std::array<UdpSocketFd::InboundMessage, BUFFER_SIZE> messages_;
|
||||
std::array<UdpReaderHelper, BUFFER_SIZE> helpers_;
|
||||
};
|
||||
|
|
|
@ -121,7 +121,7 @@ class ConcurrentHashMap {
|
|||
ConcurrentHashMap(ConcurrentHashMap &&) = delete;
|
||||
ConcurrentHashMap &operator=(ConcurrentHashMap &&) = delete;
|
||||
~ConcurrentHashMap() {
|
||||
unique_ptr<HashMap>(hash_map_.load());
|
||||
unique_ptr<HashMap>(hash_map_.load()).reset();
|
||||
}
|
||||
|
||||
static std::string get_name() {
|
||||
|
|
|
@ -192,7 +192,7 @@ class DecTree {
|
|||
}
|
||||
}
|
||||
void insert(KeyType key, ValueType value) {
|
||||
root_ = insert_node(std::move(root_), std::move(key), std::move(value), td::Random::fast_uint32());
|
||||
root_ = insert_node(std::move(root_), std::move(key), std::move(value), Random::fast_uint32());
|
||||
}
|
||||
void remove(const KeyType &key) {
|
||||
root_ = remove_node(std::move(root_), key);
|
||||
|
@ -207,7 +207,7 @@ class DecTree {
|
|||
if (size() == 0) {
|
||||
return nullptr;
|
||||
} else {
|
||||
return get_node_by_idx(root_, td::Random::fast_uint32() % size());
|
||||
return get_node_by_idx(root_, Random::fast_uint32() % size());
|
||||
}
|
||||
}
|
||||
const ValueType *get(const KeyType &key) const {
|
||||
|
@ -217,7 +217,7 @@ class DecTree {
|
|||
if (size() == 0) {
|
||||
return nullptr;
|
||||
} else {
|
||||
return get_node_by_idx(root_, td::Random::fast_uint32() % size());
|
||||
return get_node_by_idx(root_, Random::fast_uint32() % size());
|
||||
}
|
||||
}
|
||||
bool exists(const KeyType &key) const {
|
||||
|
|
|
@ -19,8 +19,8 @@
|
|||
#pragma once
|
||||
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/misc.h"
|
||||
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <tuple>
|
||||
|
||||
|
@ -32,7 +32,8 @@ class Enumerator {
|
|||
using Key = int32;
|
||||
|
||||
Key add(ValueT v) {
|
||||
int32 next_id = narrow_cast<int32>(arr_.size() + 1);
|
||||
CHECK(arr_.size() < static_cast<size_t>(std::numeric_limits<int32>::max() - 1));
|
||||
int32 next_id = static_cast<int32>(arr_.size() + 1);
|
||||
bool was_inserted;
|
||||
decltype(map_.begin()) it;
|
||||
std::tie(it, was_inserted) = map_.emplace(std::move(v), next_id);
|
||||
|
|
|
@ -107,13 +107,13 @@ class EpochBasedMemoryReclamation {
|
|||
static constexpr size_t MAX_BAGS = 3;
|
||||
struct ThreadData {
|
||||
std::atomic<int64> epoch{1};
|
||||
char pad[TD_CONCURRENCY_PAD - sizeof(epoch)];
|
||||
char pad[TD_CONCURRENCY_PAD - sizeof(std::atomic<int64>)];
|
||||
|
||||
size_t to_skip{0};
|
||||
size_t checked_thread_i{0};
|
||||
size_t bag_i{0};
|
||||
std::vector<unique_ptr<T>> to_delete[MAX_BAGS];
|
||||
char pad2[TD_CONCURRENCY_PAD - sizeof(to_delete)];
|
||||
char pad2[TD_CONCURRENCY_PAD - sizeof(std::vector<unique_ptr<T>>) * MAX_BAGS];
|
||||
|
||||
void rotate_bags() {
|
||||
bag_i = (bag_i + 1) % MAX_BAGS;
|
||||
|
@ -143,10 +143,10 @@ class EpochBasedMemoryReclamation {
|
|||
}
|
||||
};
|
||||
std::vector<ThreadData> threads_;
|
||||
char pad[TD_CONCURRENCY_PAD - sizeof(threads_)];
|
||||
char pad[TD_CONCURRENCY_PAD - sizeof(std::vector<ThreadData>)];
|
||||
|
||||
std::atomic<int64> epoch_{1};
|
||||
char pad2[TD_CONCURRENCY_PAD - sizeof(epoch_)];
|
||||
char pad2[TD_CONCURRENCY_PAD - sizeof(std::atomic<int64>)];
|
||||
|
||||
void lock(size_t thread_id) {
|
||||
auto &data = threads_[thread_id];
|
||||
|
|
|
@ -30,13 +30,13 @@
|
|||
namespace td {
|
||||
|
||||
Status FileLog::init(string path, int64 rotate_threshold, bool redirect_stderr) {
|
||||
if (path.empty()) {
|
||||
return Status::Error("Log file path can't be empty");
|
||||
}
|
||||
if (path == path_) {
|
||||
set_rotate_threshold(rotate_threshold);
|
||||
return Status::OK();
|
||||
}
|
||||
if (path.empty()) {
|
||||
return Status::Error("Log file path can't be empty");
|
||||
}
|
||||
|
||||
TRY_RESULT(fd, FileFd::open(path, FileFd::Create | FileFd::Write | FileFd::Append));
|
||||
|
||||
|
@ -52,8 +52,7 @@ Status FileLog::init(string path, int64 rotate_threshold, bool redirect_stderr)
|
|||
} else {
|
||||
path_ = r_path.move_as_ok();
|
||||
}
|
||||
TRY_RESULT(size, fd_.get_size());
|
||||
size_ = size;
|
||||
TRY_RESULT_ASSIGN(size_, fd_.get_size());
|
||||
rotate_threshold_ = rotate_threshold;
|
||||
redirect_stderr_ = redirect_stderr;
|
||||
return Status::OK();
|
||||
|
|
|
@ -53,7 +53,7 @@ class FileLog : public LogInterface {
|
|||
string path_;
|
||||
int64 size_ = 0;
|
||||
int64 rotate_threshold_ = 0;
|
||||
bool redirect_stderr_;
|
||||
bool redirect_stderr_ = false;
|
||||
std::atomic<bool> want_rotate_{};
|
||||
|
||||
void do_rotate();
|
||||
|
|
|
@ -45,9 +45,9 @@ class Gzip::Impl {
|
|||
};
|
||||
|
||||
Status Gzip::init_encode() {
|
||||
CHECK(mode_ == Empty);
|
||||
CHECK(mode_ == Mode::Empty);
|
||||
init_common();
|
||||
mode_ = Encode;
|
||||
mode_ = Mode::Encode;
|
||||
int ret = deflateInit2(&impl_->stream_, 6, Z_DEFLATED, 15, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY);
|
||||
if (ret != Z_OK) {
|
||||
return Status::Error(PSLICE() << "zlib deflate init failed: " << ret);
|
||||
|
@ -56,9 +56,9 @@ Status Gzip::init_encode() {
|
|||
}
|
||||
|
||||
Status Gzip::init_decode() {
|
||||
CHECK(mode_ == Empty);
|
||||
CHECK(mode_ == Mode::Empty);
|
||||
init_common();
|
||||
mode_ = Decode;
|
||||
mode_ = Mode::Decode;
|
||||
int ret = inflateInit2(&impl_->stream_, MAX_WBITS + 32);
|
||||
if (ret != Z_OK) {
|
||||
return Status::Error(PSLICE() << "zlib inflate init failed: " << ret);
|
||||
|
@ -88,19 +88,19 @@ void Gzip::set_output(MutableSlice output) {
|
|||
Result<Gzip::State> Gzip::run() {
|
||||
while (true) {
|
||||
int ret;
|
||||
if (mode_ == Decode) {
|
||||
if (mode_ == Mode::Decode) {
|
||||
ret = inflate(&impl_->stream_, Z_NO_FLUSH);
|
||||
} else {
|
||||
ret = deflate(&impl_->stream_, close_input_flag_ ? Z_FINISH : Z_NO_FLUSH);
|
||||
}
|
||||
|
||||
if (ret == Z_OK) {
|
||||
return Running;
|
||||
return State::Running;
|
||||
}
|
||||
if (ret == Z_STREAM_END) {
|
||||
// TODO(now): fail if input is not empty;
|
||||
clear();
|
||||
return Done;
|
||||
return State::Done;
|
||||
}
|
||||
clear();
|
||||
return Status::Error(PSLICE() << "zlib error " << ret);
|
||||
|
@ -131,12 +131,12 @@ void Gzip::init_common() {
|
|||
}
|
||||
|
||||
void Gzip::clear() {
|
||||
if (mode_ == Decode) {
|
||||
if (mode_ == Mode::Decode) {
|
||||
inflateEnd(&impl_->stream_);
|
||||
} else if (mode_ == Encode) {
|
||||
} else if (mode_ == Mode::Encode) {
|
||||
deflateEnd(&impl_->stream_);
|
||||
}
|
||||
mode_ = Empty;
|
||||
mode_ = Mode::Empty;
|
||||
}
|
||||
|
||||
Gzip::Gzip() : impl_(make_unique<Impl>()) {
|
||||
|
@ -180,7 +180,7 @@ BufferSlice gzdecode(Slice s) {
|
|||
return BufferSlice();
|
||||
}
|
||||
auto state = r_state.ok();
|
||||
if (state == Gzip::Done) {
|
||||
if (state == Gzip::State::Done) {
|
||||
message.confirm_append(gzip.flush_output());
|
||||
break;
|
||||
}
|
||||
|
@ -196,12 +196,12 @@ BufferSlice gzdecode(Slice s) {
|
|||
return message.extract_reader().move_as_buffer_slice();
|
||||
}
|
||||
|
||||
BufferSlice gzencode(Slice s, double k) {
|
||||
BufferSlice gzencode(Slice s, double max_compression_ratio) {
|
||||
Gzip gzip;
|
||||
gzip.init_encode().ensure();
|
||||
gzip.set_input(s);
|
||||
gzip.close_input();
|
||||
size_t max_size = static_cast<size_t>(static_cast<double>(s.size()) * k);
|
||||
size_t max_size = static_cast<size_t>(static_cast<double>(s.size()) * max_compression_ratio);
|
||||
BufferWriter message{max_size};
|
||||
gzip.set_output(message.prepare_append());
|
||||
auto r_state = gzip.run();
|
||||
|
@ -209,7 +209,7 @@ BufferSlice gzencode(Slice s, double k) {
|
|||
return BufferSlice();
|
||||
}
|
||||
auto state = r_state.ok();
|
||||
if (state != Gzip::Done) {
|
||||
if (state != Gzip::State::Done) {
|
||||
return BufferSlice();
|
||||
}
|
||||
message.confirm_append(gzip.flush_output());
|
||||
|
|
|
@ -36,11 +36,11 @@ class Gzip {
|
|||
Gzip &operator=(Gzip &&other);
|
||||
~Gzip();
|
||||
|
||||
enum Mode { Empty, Encode, Decode };
|
||||
enum class Mode { Empty, Encode, Decode };
|
||||
Status init(Mode mode) TD_WARN_UNUSED_RESULT {
|
||||
if (mode == Encode) {
|
||||
if (mode == Mode::Encode) {
|
||||
return init_encode();
|
||||
} else if (mode == Decode) {
|
||||
} else if (mode == Mode::Decode) {
|
||||
return init_decode();
|
||||
}
|
||||
clear();
|
||||
|
@ -91,7 +91,7 @@ class Gzip {
|
|||
return res;
|
||||
}
|
||||
|
||||
enum State { Running, Done };
|
||||
enum class State { Running, Done };
|
||||
Result<State> run() TD_WARN_UNUSED_RESULT;
|
||||
|
||||
private:
|
||||
|
@ -101,7 +101,7 @@ class Gzip {
|
|||
size_t input_size_ = 0;
|
||||
size_t output_size_ = 0;
|
||||
bool close_input_flag_ = false;
|
||||
Mode mode_ = Empty;
|
||||
Mode mode_ = Mode::Empty;
|
||||
|
||||
void init_common();
|
||||
void clear();
|
||||
|
@ -111,7 +111,7 @@ class Gzip {
|
|||
|
||||
BufferSlice gzdecode(Slice s);
|
||||
|
||||
BufferSlice gzencode(Slice s, double k = 0.9);
|
||||
BufferSlice gzencode(Slice s, double max_compression_ratio);
|
||||
|
||||
} // namespace td
|
||||
|
||||
|
|
|
@ -64,7 +64,7 @@ void GzipByteFlow::loop() {
|
|||
return finish(r_state.move_as_error());
|
||||
}
|
||||
auto state = r_state.ok();
|
||||
if (state == Gzip::Done) {
|
||||
if (state == Gzip::State::Done) {
|
||||
on_output_updated();
|
||||
return consume_input();
|
||||
}
|
||||
|
|
|
@ -109,14 +109,14 @@ class HazardPointers {
|
|||
private:
|
||||
struct ThreadData {
|
||||
std::array<std::atomic<T *>, MaxPointersN> hazard_;
|
||||
char pad[TD_CONCURRENCY_PAD - sizeof(hazard_)];
|
||||
char pad[TD_CONCURRENCY_PAD - sizeof(std::array<std::atomic<T *>, MaxPointersN>)];
|
||||
|
||||
// stupid gc
|
||||
std::vector<std::unique_ptr<T, Deleter>> to_delete_;
|
||||
char pad2[TD_CONCURRENCY_PAD - sizeof(to_delete_)];
|
||||
char pad2[TD_CONCURRENCY_PAD - sizeof(std::vector<std::unique_ptr<T, Deleter>>)];
|
||||
};
|
||||
std::vector<ThreadData> threads_;
|
||||
char pad2[TD_CONCURRENCY_PAD - sizeof(threads_)];
|
||||
char pad2[TD_CONCURRENCY_PAD - sizeof(std::vector<ThreadData>)];
|
||||
|
||||
template <class S>
|
||||
static S *do_protect(std::atomic<T *> &hazard_ptr, std::atomic<S *> &to_protect) {
|
||||
|
|
|
@ -80,7 +80,7 @@ vector<string> Hints::get_words(Slice name, bool is_search) {
|
|||
|
||||
void Hints::add_word(const string &word, KeyT key, std::map<string, vector<KeyT>> &word_to_keys) {
|
||||
vector<KeyT> &keys = word_to_keys[word];
|
||||
CHECK(std::find(keys.begin(), keys.end(), key) == keys.end());
|
||||
CHECK(!td::contains(keys, key));
|
||||
keys.push_back(key);
|
||||
}
|
||||
|
||||
|
|
|
@ -22,16 +22,17 @@
|
|||
#include "td/utils/logging.h"
|
||||
#include "td/utils/misc.h"
|
||||
#include "td/utils/Parser.h"
|
||||
#include "td/utils/port/IPAddress.h"
|
||||
|
||||
namespace td {
|
||||
|
||||
string HttpUrl::get_url() const {
|
||||
string result;
|
||||
switch (protocol_) {
|
||||
case Protocol::HTTP:
|
||||
case Protocol::Http:
|
||||
result += "http://";
|
||||
break;
|
||||
case Protocol::HTTPS:
|
||||
case Protocol::Https:
|
||||
result += "https://";
|
||||
break;
|
||||
default:
|
||||
|
@ -41,13 +42,7 @@ string HttpUrl::get_url() const {
|
|||
result += userinfo_;
|
||||
result += '@';
|
||||
}
|
||||
if (is_ipv6_) {
|
||||
result += '[';
|
||||
}
|
||||
result += host_;
|
||||
if (is_ipv6_) {
|
||||
result += ']';
|
||||
}
|
||||
if (specified_port_ > 0) {
|
||||
result += ':';
|
||||
result += to_string(specified_port_);
|
||||
|
@ -60,15 +55,15 @@ string HttpUrl::get_url() const {
|
|||
Result<HttpUrl> parse_url(Slice url, HttpUrl::Protocol default_protocol) {
|
||||
// url == [https?://][userinfo@]host[:port]
|
||||
ConstParser parser(url);
|
||||
string protocol_str = to_lower(parser.read_till_nofail(':'));
|
||||
string protocol_str = to_lower(parser.read_till_nofail(":/?#@[]"));
|
||||
|
||||
HttpUrl::Protocol protocol;
|
||||
if (parser.start_with("://")) {
|
||||
parser.advance(3);
|
||||
if (protocol_str == "http") {
|
||||
protocol = HttpUrl::Protocol::HTTP;
|
||||
protocol = HttpUrl::Protocol::Http;
|
||||
} else if (protocol_str == "https") {
|
||||
protocol = HttpUrl::Protocol::HTTPS;
|
||||
protocol = HttpUrl::Protocol::Https;
|
||||
} else {
|
||||
return Status::Error("Unsupported URL protocol");
|
||||
}
|
||||
|
@ -100,8 +95,11 @@ Result<HttpUrl> parse_url(Slice url, HttpUrl::Protocol default_protocol) {
|
|||
|
||||
bool is_ipv6 = false;
|
||||
if (!host.empty() && host[0] == '[' && host.back() == ']') {
|
||||
host.remove_prefix(1);
|
||||
host.remove_suffix(1);
|
||||
IPAddress ip_address;
|
||||
if (ip_address.init_ipv6_port(host.str(), 1).is_error()) {
|
||||
return Status::Error("Wrong IPv6 address specified in the URL");
|
||||
}
|
||||
CHECK(ip_address.is_ipv6());
|
||||
is_ipv6 = true;
|
||||
}
|
||||
if (host.empty()) {
|
||||
|
@ -113,10 +111,10 @@ Result<HttpUrl> parse_url(Slice url, HttpUrl::Protocol default_protocol) {
|
|||
|
||||
int specified_port = port;
|
||||
if (port == 0) {
|
||||
if (protocol == HttpUrl::Protocol::HTTP) {
|
||||
if (protocol == HttpUrl::Protocol::Http) {
|
||||
port = 80;
|
||||
} else {
|
||||
CHECK(protocol == HttpUrl::Protocol::HTTPS);
|
||||
CHECK(protocol == HttpUrl::Protocol::Https);
|
||||
port = 443;
|
||||
}
|
||||
}
|
||||
|
@ -183,7 +181,7 @@ Result<HttpUrl> parse_url(Slice url, HttpUrl::Protocol default_protocol) {
|
|||
}
|
||||
|
||||
StringBuilder &operator<<(StringBuilder &sb, const HttpUrl &url) {
|
||||
sb << tag("protocol", url.protocol_ == HttpUrl::Protocol::HTTP ? "HTTP" : "HTTPS") << tag("userinfo", url.userinfo_)
|
||||
sb << tag("protocol", url.protocol_ == HttpUrl::Protocol::Http ? "HTTP" : "HTTPS") << tag("userinfo", url.userinfo_)
|
||||
<< tag("host", url.host_) << tag("port", url.port_) << tag("query", url.query_);
|
||||
return sb;
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ namespace td {
|
|||
|
||||
class HttpUrl {
|
||||
public:
|
||||
enum class Protocol { HTTP, HTTPS } protocol_ = Protocol::HTTP;
|
||||
enum class Protocol { Http, Https } protocol_ = Protocol::Http;
|
||||
string userinfo_;
|
||||
string host_;
|
||||
bool is_ipv6_ = false;
|
||||
|
@ -49,7 +49,7 @@ class HttpUrl {
|
|||
};
|
||||
|
||||
Result<HttpUrl> parse_url(Slice url,
|
||||
HttpUrl::Protocol default_protocol = HttpUrl::Protocol::HTTP) TD_WARN_UNUSED_RESULT;
|
||||
HttpUrl::Protocol default_protocol = HttpUrl::Protocol::Http) TD_WARN_UNUSED_RESULT;
|
||||
|
||||
StringBuilder &operator<<(StringBuilder &sb, const HttpUrl &url);
|
||||
|
||||
|
|
|
@ -605,6 +605,15 @@ bool has_json_object_field(const JsonObject &object, Slice name) {
|
|||
return false;
|
||||
}
|
||||
|
||||
JsonValue get_json_object_field_force(JsonObject &object, Slice name) {
|
||||
for (auto &field_value : object) {
|
||||
if (field_value.first == name) {
|
||||
return std::move(field_value.second);
|
||||
}
|
||||
}
|
||||
return JsonValue();
|
||||
}
|
||||
|
||||
Result<JsonValue> get_json_object_field(JsonObject &object, Slice name, JsonValue::Type type, bool is_optional) {
|
||||
for (auto &field_value : object) {
|
||||
if (field_value.first == name) {
|
||||
|
|
|
@ -27,20 +27,11 @@
|
|||
#include "td/utils/StringBuilder.h"
|
||||
|
||||
#include <new>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace td {
|
||||
|
||||
template <class... Args>
|
||||
std::tuple<const Args &...> ctie(const Args &... args) TD_WARN_UNUSED_RESULT;
|
||||
|
||||
template <class... Args>
|
||||
std::tuple<const Args &...> ctie(const Args &... args) {
|
||||
return std::tie(args...);
|
||||
}
|
||||
|
||||
class JsonTrue {
|
||||
public:
|
||||
friend StringBuilder &operator<<(StringBuilder &sb, const JsonTrue &val) {
|
||||
|
@ -210,8 +201,11 @@ class JsonBuilder {
|
|||
return offset_ >= 0;
|
||||
}
|
||||
void print_offset() {
|
||||
for (int x = 0; x < offset_; x++) {
|
||||
sb_ << " ";
|
||||
if (offset_ >= 0) {
|
||||
sb_ << '\n';
|
||||
for (int x = 0; x < offset_; x++) {
|
||||
sb_ << " ";
|
||||
}
|
||||
}
|
||||
}
|
||||
void dec_offset() {
|
||||
|
@ -307,9 +301,7 @@ class JsonScope {
|
|||
*sb_ << x;
|
||||
return *this;
|
||||
}
|
||||
JsonScope &operator<<(bool x) {
|
||||
return *this << JsonBool(x);
|
||||
}
|
||||
JsonScope &operator<<(bool x) = delete;
|
||||
JsonScope &operator<<(int32 x) {
|
||||
return *this << JsonInt(x);
|
||||
}
|
||||
|
@ -319,8 +311,6 @@ class JsonScope {
|
|||
JsonScope &operator<<(double x) {
|
||||
return *this << JsonFloat(x);
|
||||
}
|
||||
template <class T>
|
||||
JsonScope &operator<<(const T *x); // not implemented
|
||||
template <size_t N>
|
||||
JsonScope &operator<<(const char (&x)[N]) {
|
||||
return *this << JsonString(Slice(x));
|
||||
|
@ -328,9 +318,6 @@ class JsonScope {
|
|||
JsonScope &operator<<(const char *x) {
|
||||
return *this << JsonString(Slice(x));
|
||||
}
|
||||
JsonScope &operator<<(const string &x) {
|
||||
return *this << JsonString(Slice(x));
|
||||
}
|
||||
JsonScope &operator<<(Slice x) {
|
||||
return *this << JsonString(x);
|
||||
}
|
||||
|
@ -428,16 +415,8 @@ class JsonObjectScope : public JsonScope {
|
|||
}
|
||||
*sb_ << "}";
|
||||
}
|
||||
template <class S, class T>
|
||||
JsonObjectScope &operator<<(std::tuple<S, T> key_value) {
|
||||
return (*this)(std::get<0>(key_value), std::get<1>(key_value));
|
||||
}
|
||||
template <class S, class T>
|
||||
JsonObjectScope &operator<<(std::pair<S, T> key_value) {
|
||||
return (*this)(key_value.first, key_value.second);
|
||||
}
|
||||
template <class S, class T>
|
||||
JsonObjectScope &operator()(S &&key, T &&value) {
|
||||
template <class T>
|
||||
JsonObjectScope &operator()(Slice key, T &&value) {
|
||||
CHECK(is_active());
|
||||
if (is_first_) {
|
||||
*sb_ << ",";
|
||||
|
@ -624,7 +603,7 @@ class JsonValue : public Jsonable {
|
|||
case Type::Object: {
|
||||
auto object = scope->enter_object();
|
||||
for (auto &key_value : get_object()) {
|
||||
object << ctie(JsonString(key_value.first), key_value.second);
|
||||
object(key_value.first, key_value.second);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -846,6 +825,7 @@ class JsonObjectImpl : Jsonable {
|
|||
private:
|
||||
F f_;
|
||||
};
|
||||
|
||||
template <class F>
|
||||
auto json_object(F &&f) {
|
||||
return JsonObjectImpl<F>(std::forward<F>(f));
|
||||
|
@ -881,6 +861,8 @@ auto json_array(const A &a, F &&f) {
|
|||
|
||||
bool has_json_object_field(const JsonObject &object, Slice name);
|
||||
|
||||
JsonValue get_json_object_field_force(JsonObject &object, Slice name) TD_WARN_UNUSED_RESULT;
|
||||
|
||||
Result<JsonValue> get_json_object_field(JsonObject &object, Slice name, JsonValue::Type type,
|
||||
bool is_optional = true) TD_WARN_UNUSED_RESULT;
|
||||
|
||||
|
|
|
@ -40,19 +40,19 @@ struct ListNode {
|
|||
if (other.empty()) {
|
||||
clear();
|
||||
} else {
|
||||
ListNode *head = other.prev;
|
||||
other.remove();
|
||||
head->put(this);
|
||||
init_from(std::move(other));
|
||||
}
|
||||
}
|
||||
|
||||
ListNode &operator=(ListNode &&other) {
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
this->remove();
|
||||
|
||||
if (!other.empty()) {
|
||||
ListNode *head = other.prev;
|
||||
other.remove();
|
||||
head->put(this);
|
||||
init_from(std::move(other));
|
||||
}
|
||||
|
||||
return *this;
|
||||
|
@ -70,11 +70,12 @@ struct ListNode {
|
|||
}
|
||||
|
||||
void put(ListNode *other) {
|
||||
other->connect(next);
|
||||
this->connect(other);
|
||||
DCHECK(other->empty());
|
||||
put_unsafe(other);
|
||||
}
|
||||
|
||||
void put_back(ListNode *other) {
|
||||
DCHECK(other->empty());
|
||||
prev->connect(other);
|
||||
other->connect(this);
|
||||
}
|
||||
|
@ -94,11 +95,35 @@ struct ListNode {
|
|||
return next == this;
|
||||
}
|
||||
|
||||
private:
|
||||
ListNode *begin() {
|
||||
return next;
|
||||
}
|
||||
ListNode *end() {
|
||||
return this;
|
||||
}
|
||||
ListNode *get_next() {
|
||||
return next;
|
||||
}
|
||||
ListNode *get_prev() {
|
||||
return prev;
|
||||
}
|
||||
|
||||
protected:
|
||||
void clear() {
|
||||
next = this;
|
||||
prev = this;
|
||||
}
|
||||
|
||||
void init_from(ListNode &&other) {
|
||||
ListNode *head = other.prev;
|
||||
other.remove();
|
||||
head->put_unsafe(this);
|
||||
}
|
||||
|
||||
void put_unsafe(ListNode *other) {
|
||||
other->connect(next);
|
||||
this->connect(other);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace td
|
||||
|
|
|
@ -46,30 +46,30 @@ class MemoryLog : public LogInterface {
|
|||
size_t slice_size = slice.size();
|
||||
CHECK(slice_size * 3 < buffer_size);
|
||||
size_t pad_size = ((slice_size + 15) & ~15) - slice_size;
|
||||
constexpr size_t magic_size = 16;
|
||||
uint32 total_size = static_cast<uint32>(slice_size + pad_size + magic_size);
|
||||
constexpr size_t MAGIC_SIZE = 16;
|
||||
uint32 total_size = static_cast<uint32>(slice_size + pad_size + MAGIC_SIZE);
|
||||
uint32 real_pos = pos_.fetch_add(total_size, std::memory_order_relaxed);
|
||||
CHECK((total_size & 15) == 0);
|
||||
|
||||
uint32 start_pos = real_pos & (buffer_size - 1);
|
||||
uint32 end_pos = start_pos + total_size;
|
||||
if (likely(end_pos <= buffer_size)) {
|
||||
std::memcpy(&buffer_[start_pos + magic_size], slice.data(), slice_size);
|
||||
std::memcpy(&buffer_[start_pos + magic_size + slice_size], " ", pad_size);
|
||||
std::memcpy(&buffer_[start_pos + MAGIC_SIZE], slice.data(), slice_size);
|
||||
std::memcpy(&buffer_[start_pos + MAGIC_SIZE + slice_size], " ", pad_size);
|
||||
} else {
|
||||
size_t first = buffer_size - start_pos - magic_size;
|
||||
size_t first = buffer_size - start_pos - MAGIC_SIZE;
|
||||
size_t second = slice_size - first;
|
||||
std::memcpy(&buffer_[start_pos + magic_size], slice.data(), first);
|
||||
std::memcpy(&buffer_[start_pos + MAGIC_SIZE], slice.data(), first);
|
||||
std::memcpy(&buffer_[0], slice.data() + first, second);
|
||||
std::memcpy(&buffer_[second], " ", pad_size);
|
||||
}
|
||||
|
||||
CHECK((start_pos & 15) == 0);
|
||||
CHECK(start_pos <= buffer_size - magic_size);
|
||||
CHECK(start_pos <= buffer_size - MAGIC_SIZE);
|
||||
buffer_[start_pos] = '\n';
|
||||
size_t printed = std::snprintf(&buffer_[start_pos + 1], magic_size - 1, "LOG:%08x: ", real_pos);
|
||||
CHECK(printed == magic_size - 2);
|
||||
buffer_[start_pos + magic_size - 1] = ' ';
|
||||
size_t printed = std::snprintf(&buffer_[start_pos + 1], MAGIC_SIZE - 1, "LOG:%08x: ", real_pos);
|
||||
CHECK(printed == MAGIC_SIZE - 2);
|
||||
buffer_[start_pos + MAGIC_SIZE - 1] = ' ';
|
||||
|
||||
if (log_level == VERBOSITY_NAME(FATAL)) {
|
||||
process_fatal_error(new_slice);
|
||||
|
|
|
@ -30,8 +30,10 @@ class MovableValue {
|
|||
other.clear();
|
||||
}
|
||||
MovableValue &operator=(MovableValue &&other) {
|
||||
val_ = other.val_;
|
||||
other.clear();
|
||||
if (this != &other) {
|
||||
val_ = other.val_;
|
||||
other.clear();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
MovableValue(const MovableValue &) = delete;
|
||||
|
|
|
@ -166,7 +166,7 @@ class MpscLinkQueueUniquePtrNode {
|
|||
MpscLinkQueueImpl::Node *to_mpsc_link_queue_node() {
|
||||
return ptr_.release()->to_mpsc_link_queue_node();
|
||||
}
|
||||
static MpscLinkQueueUniquePtrNode<Value> from_mpsc_link_queue_node(td::MpscLinkQueueImpl::Node *node) {
|
||||
static MpscLinkQueueUniquePtrNode<Value> from_mpsc_link_queue_node(MpscLinkQueueImpl::Node *node) {
|
||||
return MpscLinkQueueUniquePtrNode<Value>(unique_ptr<Value>(Value::from_mpsc_link_queue_node(node)));
|
||||
}
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ class ObjectPool {
|
|||
// It is not very usual case of acquire/release use.
|
||||
// Instead of publishing an object via some flag we do the opposite.
|
||||
// We publish new generation via destruction of the data.
|
||||
// In usual case if we see a flag then we are able to use an object.
|
||||
// In usual case if we see a flag, then we are able to use an object.
|
||||
// In our case if we have used an object and it is already invalid, then generation will mismatch
|
||||
bool is_alive() const {
|
||||
if (!storage_) {
|
||||
|
|
231
tdutils/td/utils/OptionParser.cpp
Normal file
231
tdutils/td/utils/OptionParser.cpp
Normal file
|
@ -0,0 +1,231 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2020 Telegram Systems LLP
|
||||
*/
|
||||
|
||||
#include "td/utils/OptionParser.h"
|
||||
|
||||
#include "td/utils/misc.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace td {
|
||||
|
||||
void OptionParser::set_description(string description) {
|
||||
description_ = std::move(description);
|
||||
}
|
||||
|
||||
void OptionParser::add_option(Option::Type type, char short_key, Slice long_key, Slice description,
|
||||
std::function<Status(Slice)> callback) {
|
||||
for (auto &option : options_) {
|
||||
if (option.short_key == short_key || (!long_key.empty() && long_key == option.long_key)) {
|
||||
LOG(ERROR) << "Ignore duplicated option '" << short_key << "' '" << long_key << "'";
|
||||
}
|
||||
}
|
||||
options_.push_back(Option{type, short_key, long_key.str(), description.str(), std::move(callback)});
|
||||
}
|
||||
|
||||
void OptionParser::add_checked_option(char short_key, Slice long_key, Slice description,
|
||||
std::function<Status(Slice)> callback) {
|
||||
add_option(Option::Type::Arg, short_key, long_key, description, std::move(callback));
|
||||
}
|
||||
|
||||
void OptionParser::add_checked_option(char short_key, Slice long_key, Slice description,
|
||||
std::function<Status(void)> callback) {
|
||||
add_option(Option::Type::NoArg, short_key, long_key, description,
|
||||
[callback = std::move(callback)](Slice) { return callback(); });
|
||||
}
|
||||
|
||||
void OptionParser::add_option(char short_key, Slice long_key, Slice description, std::function<void(Slice)> callback) {
|
||||
add_option(Option::Type::Arg, short_key, long_key, description, [callback = std::move(callback)](Slice parameter) {
|
||||
callback(parameter);
|
||||
return Status::OK();
|
||||
});
|
||||
}
|
||||
|
||||
void OptionParser::add_option(char short_key, Slice long_key, Slice description, std::function<void(void)> callback) {
|
||||
add_option(Option::Type::NoArg, short_key, long_key, description, [callback = std::move(callback)](Slice) {
|
||||
callback();
|
||||
return Status::OK();
|
||||
});
|
||||
}
|
||||
|
||||
void OptionParser::add_check(std::function<Status()> check) {
|
||||
checks_.push_back(std::move(check));
|
||||
}
|
||||
|
||||
Result<vector<char *>> OptionParser::run(int argc, char *argv[], int expected_non_option_count) {
|
||||
std::unordered_map<char, const Option *> short_options;
|
||||
std::unordered_map<string, const Option *> long_options;
|
||||
for (auto &opt : options_) {
|
||||
if (opt.short_key != '\0') {
|
||||
short_options[opt.short_key] = &opt;
|
||||
}
|
||||
if (!opt.long_key.empty()) {
|
||||
long_options[opt.long_key] = &opt;
|
||||
}
|
||||
}
|
||||
|
||||
vector<char *> non_options;
|
||||
for (int arg_pos = 1; arg_pos < argc; arg_pos++) {
|
||||
const char *arg = argv[arg_pos];
|
||||
if (arg[0] != '-' || arg[1] == '\0') {
|
||||
non_options.push_back(argv[arg_pos]);
|
||||
continue;
|
||||
}
|
||||
if (arg[1] == '-' && arg[2] == '\0') {
|
||||
// "--"; after it everything is non-option
|
||||
while (++arg_pos < argc) {
|
||||
non_options.push_back(argv[arg_pos]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (arg[1] == '-') {
|
||||
// long option
|
||||
Slice long_arg(arg + 2, std::strlen(arg + 2));
|
||||
Slice parameter;
|
||||
auto equal_pos = long_arg.find('=');
|
||||
bool has_equal = equal_pos != Slice::npos;
|
||||
if (has_equal) {
|
||||
parameter = long_arg.substr(equal_pos + 1);
|
||||
long_arg = long_arg.substr(0, equal_pos);
|
||||
}
|
||||
|
||||
auto it = long_options.find(long_arg.str());
|
||||
if (it == long_options.end()) {
|
||||
return Status::Error(PSLICE() << "Option " << long_arg << " was unrecognized");
|
||||
}
|
||||
|
||||
auto option = it->second;
|
||||
switch (option->type) {
|
||||
case Option::Type::NoArg:
|
||||
if (has_equal) {
|
||||
return Status::Error(PSLICE() << "Option " << long_arg << " must not have argument");
|
||||
}
|
||||
break;
|
||||
case Option::Type::Arg:
|
||||
if (!has_equal) {
|
||||
if (++arg_pos == argc) {
|
||||
return Status::Error(PSLICE() << "Option " << long_arg << " must have argument");
|
||||
}
|
||||
parameter = Slice(argv[arg_pos], std::strlen(argv[arg_pos]));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
TRY_STATUS(option->arg_callback(parameter));
|
||||
continue;
|
||||
}
|
||||
|
||||
for (size_t opt_pos = 1; arg[opt_pos] != '\0'; opt_pos++) {
|
||||
auto it = short_options.find(arg[opt_pos]);
|
||||
if (it == short_options.end()) {
|
||||
return Status::Error(PSLICE() << "Option " << arg[opt_pos] << " was unrecognized");
|
||||
}
|
||||
|
||||
auto option = it->second;
|
||||
Slice parameter;
|
||||
switch (option->type) {
|
||||
case Option::Type::NoArg:
|
||||
// nothing to do
|
||||
break;
|
||||
case Option::Type::Arg:
|
||||
if (arg[opt_pos + 1] == '\0') {
|
||||
if (++arg_pos == argc) {
|
||||
return Status::Error(PSLICE() << "Option " << arg[opt_pos] << " must have argument");
|
||||
}
|
||||
parameter = Slice(argv[arg_pos], std::strlen(argv[arg_pos]));
|
||||
} else {
|
||||
parameter = Slice(arg + opt_pos + 1, std::strlen(arg + opt_pos + 1));
|
||||
opt_pos += parameter.size();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
TRY_STATUS(option->arg_callback(parameter));
|
||||
}
|
||||
}
|
||||
if (expected_non_option_count >= 0 && non_options.size() != static_cast<size_t>(expected_non_option_count)) {
|
||||
if (expected_non_option_count == 0) {
|
||||
return Status::Error("Unexpected non-option parameters specified");
|
||||
}
|
||||
if (non_options.size() > static_cast<size_t>(expected_non_option_count)) {
|
||||
return Status::Error("Too much non-option parameters specified");
|
||||
} else {
|
||||
return Status::Error("Too few non-option parameters specified");
|
||||
}
|
||||
}
|
||||
for (auto &check : checks_) {
|
||||
TRY_STATUS(check());
|
||||
}
|
||||
|
||||
return std::move(non_options);
|
||||
}
|
||||
|
||||
StringBuilder &operator<<(StringBuilder &sb, const OptionParser &o) {
|
||||
if (!o.description_.empty()) {
|
||||
sb << o.description_ << ". ";
|
||||
}
|
||||
sb << "Options:\n";
|
||||
|
||||
size_t max_length = 0;
|
||||
for (auto &opt : o.options_) {
|
||||
bool has_short_key = opt.short_key != '\0';
|
||||
bool has_long_key = !opt.long_key.empty();
|
||||
size_t length = (has_short_key ? 2 : 0) + (has_long_key ? 2 + opt.long_key.size() + 2 * has_short_key : 0);
|
||||
if (opt.type != OptionParser::Option::Type::NoArg) {
|
||||
length += 5;
|
||||
}
|
||||
if (length > max_length) {
|
||||
max_length = length;
|
||||
}
|
||||
}
|
||||
max_length++;
|
||||
|
||||
for (auto &opt : o.options_) {
|
||||
bool has_short_key = opt.short_key != '\0';
|
||||
sb << " ";
|
||||
size_t length = max_length;
|
||||
if (has_short_key) {
|
||||
sb << '-' << opt.short_key;
|
||||
length -= 2;
|
||||
}
|
||||
if (!opt.long_key.empty()) {
|
||||
if (has_short_key) {
|
||||
sb << ", ";
|
||||
length -= 2;
|
||||
}
|
||||
sb << "--" << opt.long_key;
|
||||
length -= 2 + opt.long_key.size();
|
||||
}
|
||||
if (opt.type != OptionParser::Option::Type::NoArg) {
|
||||
sb << "<arg>";
|
||||
length -= 5;
|
||||
}
|
||||
sb << string(length, ' ') << opt.description;
|
||||
sb << '\n';
|
||||
}
|
||||
return sb;
|
||||
}
|
||||
|
||||
} // namespace td
|
72
tdutils/td/utils/OptionParser.h
Normal file
72
tdutils/td/utils/OptionParser.h
Normal file
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2020 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/Slice.h"
|
||||
#include "td/utils/Status.h"
|
||||
#include "td/utils/StringBuilder.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace td {
|
||||
|
||||
class OptionParser {
|
||||
class Option {
|
||||
public:
|
||||
enum class Type { NoArg, Arg };
|
||||
Type type;
|
||||
char short_key;
|
||||
string long_key;
|
||||
string description;
|
||||
std::function<Status(Slice)> arg_callback;
|
||||
};
|
||||
|
||||
void add_option(Option::Type type, char short_key, Slice long_key, Slice description,
|
||||
std::function<Status(Slice)> callback);
|
||||
|
||||
public:
|
||||
void set_description(string description);
|
||||
|
||||
void add_checked_option(char short_key, Slice long_key, Slice description, std::function<Status(Slice)> callback);
|
||||
|
||||
void add_checked_option(char short_key, Slice long_key, Slice description, std::function<Status(void)> callback);
|
||||
|
||||
void add_option(char short_key, Slice long_key, Slice description, std::function<Status(Slice)> callback) = delete;
|
||||
|
||||
void add_option(char short_key, Slice long_key, Slice description, std::function<Status(void)> callback) = delete;
|
||||
|
||||
void add_option(char short_key, Slice long_key, Slice description, std::function<void(Slice)> callback);
|
||||
|
||||
void add_option(char short_key, Slice long_key, Slice description, std::function<void(void)> callback);
|
||||
|
||||
void add_check(std::function<Status()> check);
|
||||
|
||||
// returns found non-option parameters
|
||||
Result<vector<char *>> run(int argc, char *argv[], int expected_non_option_count = -1) TD_WARN_UNUSED_RESULT;
|
||||
|
||||
friend StringBuilder &operator<<(StringBuilder &sb, const OptionParser &o);
|
||||
|
||||
private:
|
||||
vector<Option> options_;
|
||||
vector<std::function<Status()>> checks_;
|
||||
string description_;
|
||||
};
|
||||
|
||||
} // namespace td
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
Copyright 2017-2020 Telegram Systems LLP
|
||||
*/
|
||||
#include "td/utils/OptionsParser.h"
|
||||
#include "td/utils/OptionParser.h"
|
||||
|
||||
#if TD_HAVE_GETOPT
|
||||
#include "getopt.h"
|
||||
|
@ -29,11 +29,11 @@
|
|||
|
||||
namespace td {
|
||||
|
||||
void OptionsParser::set_description(std::string description) {
|
||||
void OptionParser::set_description(std::string description) {
|
||||
description_ = std::move(description);
|
||||
}
|
||||
|
||||
void OptionsParser::add_option(Option::Type type, char short_key, Slice long_key, Slice description,
|
||||
void OptionParser::add_option(Option::Type type, char short_key, Slice long_key, Slice description,
|
||||
std::function<Status(Slice)> callback) {
|
||||
for (auto &option : options_) {
|
||||
if (option.short_key == short_key || (!long_key.empty() && long_key == option.long_key)) {
|
||||
|
@ -43,12 +43,12 @@ void OptionsParser::add_option(Option::Type type, char short_key, Slice long_key
|
|||
options_.push_back(Option{type, short_key, long_key.str(), description.str(), std::move(callback)});
|
||||
}
|
||||
|
||||
void OptionsParser::add_option(char short_key, Slice long_key, Slice description,
|
||||
void OptionParser::add_option(char short_key, Slice long_key, Slice description,
|
||||
std::function<Status(Slice)> callback) {
|
||||
add_option(Option::Type::Arg, short_key, long_key, description, std::move(callback));
|
||||
}
|
||||
|
||||
void OptionsParser::add_option(char short_key, Slice long_key, Slice description,
|
||||
void OptionParser::add_option(char short_key, Slice long_key, Slice description,
|
||||
std::function<Status(void)> callback) {
|
||||
// Ouch. There must be some better way
|
||||
add_option(Option::Type::NoArg, short_key, long_key, description,
|
||||
|
@ -56,7 +56,7 @@ void OptionsParser::add_option(char short_key, Slice long_key, Slice description
|
|||
std::placeholders::_1));
|
||||
}
|
||||
|
||||
Result<int> OptionsParser::run(int argc, char *argv[]) {
|
||||
Result<int> OptionParser::run(int argc, char *argv[]) {
|
||||
#if TD_HAVE_GETOPT
|
||||
char buff[1024];
|
||||
StringBuilder sb(MutableSlice{buff, sizeof(buff)});
|
||||
|
@ -122,20 +122,20 @@ Result<int> OptionsParser::run(int argc, char *argv[]) {
|
|||
#endif
|
||||
}
|
||||
|
||||
StringBuilder &operator<<(StringBuilder &sb, const OptionsParser &o) {
|
||||
StringBuilder &operator<<(StringBuilder &sb, const OptionParser &o) {
|
||||
sb << o.description_ << "\n";
|
||||
for (auto &opt : o.options_) {
|
||||
sb << "-" << opt.short_key;
|
||||
if (!opt.long_key.empty()) {
|
||||
sb << "|--" << opt.long_key;
|
||||
}
|
||||
if (opt.type == OptionsParser::Option::OptionalArg) {
|
||||
if (opt.type == OptionParser::Option::OptionalArg) {
|
||||
sb << "[";
|
||||
}
|
||||
if (opt.type != OptionsParser::Option::NoArg) {
|
||||
if (opt.type != OptionParser::Option::NoArg) {
|
||||
sb << "<arg>";
|
||||
}
|
||||
if (opt.type == OptionsParser::Option::OptionalArg) {
|
||||
if (opt.type == OptionParser::Option::OptionalArg) {
|
||||
sb << "]";
|
||||
}
|
||||
sb << "\t" << opt.description;
|
||||
|
|
|
@ -57,7 +57,7 @@ class ParserImpl {
|
|||
return ptr_ == end_;
|
||||
}
|
||||
void clear() {
|
||||
ptr_ = nullptr;
|
||||
ptr_ = SliceT().begin();
|
||||
end_ = ptr_;
|
||||
status_ = Status::OK();
|
||||
}
|
||||
|
|
70
tdutils/td/utils/PathView.cpp
Normal file
70
tdutils/td/utils/PathView.cpp
Normal file
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2020 Telegram Systems LLP
|
||||
*/
|
||||
|
||||
#include "td/utils/PathView.h"
|
||||
|
||||
#include "td/utils/misc.h"
|
||||
|
||||
namespace td {
|
||||
|
||||
PathView::PathView(Slice path) : path_(path) {
|
||||
last_slash_ = narrow_cast<int32>(path_.size()) - 1;
|
||||
while (last_slash_ >= 0 && !is_slash(path_[last_slash_])) {
|
||||
last_slash_--;
|
||||
}
|
||||
|
||||
last_dot_ = static_cast<int32>(path_.size());
|
||||
for (auto i = last_dot_ - 1; i > last_slash_ + 1; i--) {
|
||||
if (path_[i] == '.') {
|
||||
last_dot_ = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Slice PathView::relative(Slice path, Slice dir, bool force) {
|
||||
if (begins_with(path, dir)) {
|
||||
path.remove_prefix(dir.size());
|
||||
return path;
|
||||
}
|
||||
if (force) {
|
||||
return Slice();
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
Slice PathView::dir_and_file(Slice path) {
|
||||
auto last_slash = static_cast<int32>(path.size()) - 1;
|
||||
while (last_slash >= 0 && !is_slash(path[last_slash])) {
|
||||
last_slash--;
|
||||
}
|
||||
if (last_slash < 0) {
|
||||
return Slice();
|
||||
}
|
||||
last_slash--;
|
||||
while (last_slash >= 0 && !is_slash(path[last_slash])) {
|
||||
last_slash--;
|
||||
}
|
||||
if (last_slash < 0) {
|
||||
return Slice();
|
||||
}
|
||||
return path.substr(last_slash + 1);
|
||||
}
|
||||
|
||||
} // namespace td
|
|
@ -18,27 +18,13 @@
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
#include "td/utils/misc.h"
|
||||
#include "td/utils/Slice.h"
|
||||
|
||||
namespace td {
|
||||
|
||||
class PathView {
|
||||
public:
|
||||
explicit PathView(Slice path) : path_(path) {
|
||||
last_slash_ = narrow_cast<int32>(path_.size()) - 1;
|
||||
while (last_slash_ >= 0 && !is_slash(path_[last_slash_])) {
|
||||
last_slash_--;
|
||||
}
|
||||
|
||||
last_dot_ = static_cast<int32>(path_.size());
|
||||
for (auto i = last_dot_ - 1; i > last_slash_ + 1; i--) {
|
||||
if (path_[i] == '.') {
|
||||
last_dot_ = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
explicit PathView(Slice path);
|
||||
|
||||
bool empty() const {
|
||||
return path_.empty();
|
||||
|
@ -89,34 +75,8 @@ class PathView {
|
|||
return !is_absolute();
|
||||
}
|
||||
|
||||
static Slice relative(Slice path, Slice dir, bool force = false) {
|
||||
if (begins_with(path, dir)) {
|
||||
path.remove_prefix(dir.size());
|
||||
return path;
|
||||
}
|
||||
if (force) {
|
||||
return Slice();
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
static Slice dir_and_file(Slice path) {
|
||||
auto last_slash = static_cast<int32>(path.size()) - 1;
|
||||
while (last_slash >= 0 && !is_slash(path[last_slash])) {
|
||||
last_slash--;
|
||||
}
|
||||
if (last_slash < 0) {
|
||||
return Slice();
|
||||
}
|
||||
last_slash--;
|
||||
while (last_slash >= 0 && !is_slash(path[last_slash])) {
|
||||
last_slash--;
|
||||
}
|
||||
if (last_slash < 0) {
|
||||
return Slice();
|
||||
}
|
||||
return path.substr(last_slash + 1);
|
||||
}
|
||||
static Slice relative(Slice path, Slice dir, bool force = false);
|
||||
static Slice dir_and_file(Slice path);
|
||||
|
||||
private:
|
||||
static bool is_slash(char c) {
|
||||
|
|
|
@ -43,25 +43,25 @@ void Random::secure_bytes(MutableSlice dest) {
|
|||
}
|
||||
|
||||
void Random::secure_bytes(unsigned char *ptr, size_t size) {
|
||||
constexpr size_t buf_size = 512;
|
||||
constexpr size_t BUF_SIZE = 512;
|
||||
static TD_THREAD_LOCAL unsigned char *buf; // static zero-initialized
|
||||
static TD_THREAD_LOCAL size_t buf_pos;
|
||||
static TD_THREAD_LOCAL int64 generation;
|
||||
if (init_thread_local<unsigned char[]>(buf, buf_size)) {
|
||||
buf_pos = buf_size;
|
||||
if (init_thread_local<unsigned char[]>(buf, BUF_SIZE)) {
|
||||
buf_pos = BUF_SIZE;
|
||||
generation = 0;
|
||||
}
|
||||
if (ptr == nullptr) {
|
||||
td::MutableSlice(buf, buf_size).fill_zero_secure();
|
||||
buf_pos = buf_size;
|
||||
td::MutableSlice(buf, BUF_SIZE).fill_zero_secure();
|
||||
buf_pos = BUF_SIZE;
|
||||
return;
|
||||
}
|
||||
if (generation != random_seed_generation.load(std::memory_order_relaxed)) {
|
||||
generation = random_seed_generation.load(std::memory_order_acquire);
|
||||
buf_pos = buf_size;
|
||||
buf_pos = BUF_SIZE;
|
||||
}
|
||||
|
||||
auto ready = min(size, buf_size - buf_pos);
|
||||
auto ready = min(size, BUF_SIZE - buf_pos);
|
||||
if (ready != 0) {
|
||||
std::memcpy(ptr, buf + buf_pos, ready);
|
||||
buf_pos += ready;
|
||||
|
@ -71,8 +71,8 @@ void Random::secure_bytes(unsigned char *ptr, size_t size) {
|
|||
return;
|
||||
}
|
||||
}
|
||||
if (size < buf_size) {
|
||||
int err = RAND_bytes(buf, static_cast<int>(buf_size));
|
||||
if (size < BUF_SIZE) {
|
||||
int err = RAND_bytes(buf, static_cast<int>(BUF_SIZE));
|
||||
// TODO: it CAN fail
|
||||
LOG_IF(FATAL, err != 1);
|
||||
buf_pos = size;
|
||||
|
@ -164,7 +164,7 @@ double Random::fast(double min, double max) {
|
|||
}
|
||||
|
||||
Random::Xorshift128plus::Xorshift128plus(uint64 seed) {
|
||||
auto next = [&]() {
|
||||
auto next = [&] {
|
||||
// splitmix64
|
||||
seed += static_cast<uint64>(0x9E3779B97F4A7C15ull);
|
||||
uint64 z = seed;
|
||||
|
@ -189,6 +189,7 @@ uint64 Random::Xorshift128plus::operator()() {
|
|||
seed_[1] = x ^ y ^ (x >> 17) ^ (y >> 26);
|
||||
return seed_[1] + y;
|
||||
}
|
||||
|
||||
int Random::Xorshift128plus::fast(int min, int max) {
|
||||
return static_cast<int>((*this)() % (max - min + 1) + min);
|
||||
}
|
||||
|
@ -196,4 +197,18 @@ int64 Random::Xorshift128plus::fast64(int64 min, int64 max) {
|
|||
return static_cast<int64>((*this)() % (max - min + 1) + min);
|
||||
}
|
||||
|
||||
void Random::Xorshift128plus::bytes(MutableSlice dest) {
|
||||
int cnt = 0;
|
||||
uint64 buf = 0;
|
||||
for (auto &c : dest) {
|
||||
if (cnt == 0) {
|
||||
buf = operator()();
|
||||
cnt = 8;
|
||||
}
|
||||
cnt--;
|
||||
c = static_cast<char>(buf & 255);
|
||||
buf >>= 8;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace td
|
||||
|
|
|
@ -59,6 +59,7 @@ class Random {
|
|||
uint64 operator()();
|
||||
int fast(int min, int max);
|
||||
int64 fast64(int64 min, int64 max);
|
||||
void bytes(MutableSlice dest);
|
||||
|
||||
private:
|
||||
uint64 seed_[2];
|
||||
|
|
|
@ -86,4 +86,4 @@ auto operator+(ScopeExit, FunctionT &&func) {
|
|||
|
||||
} // namespace td
|
||||
|
||||
#define SCOPE_EXIT auto TD_CONCAT(SCOPE_EXIT_VAR_, __LINE__) = ::td::ScopeExit() + [&]()
|
||||
#define SCOPE_EXIT auto TD_CONCAT(SCOPE_EXIT_VAR_, __LINE__) = ::td::ScopeExit() + [&]
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
Copyright 2017-2020 Telegram Systems LLP
|
||||
*/
|
||||
|
||||
#include "td/utils/SharedSlice.h"
|
||||
|
||||
#include "td/utils/buffer.h"
|
||||
|
@ -23,7 +24,7 @@
|
|||
namespace td {
|
||||
|
||||
BufferSlice SharedSlice::clone_as_buffer_slice() const {
|
||||
return BufferSlice{as_slice().str()};
|
||||
return BufferSlice{as_slice()};
|
||||
}
|
||||
|
||||
} // namespace td
|
||||
|
|
|
@ -16,18 +16,21 @@
|
|||
|
||||
Copyright 2017-2020 Telegram Systems LLP
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "td/utils/Slice.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <new>
|
||||
#include <type_traits>
|
||||
|
||||
namespace td {
|
||||
|
||||
namespace detail {
|
||||
struct SharedSliceHeader {
|
||||
explicit SharedSliceHeader(size_t size) : refcnt_{1}, size_{size} {
|
||||
explicit SharedSliceHeader(size_t size) : size_{size} {
|
||||
}
|
||||
|
||||
void inc() {
|
||||
|
@ -49,7 +52,7 @@ struct SharedSliceHeader {
|
|||
}
|
||||
|
||||
private:
|
||||
std::atomic<uint64> refcnt_;
|
||||
std::atomic<uint64> refcnt_{1};
|
||||
size_t size_;
|
||||
};
|
||||
|
||||
|
@ -122,9 +125,9 @@ class UnsafeSharedSlice {
|
|||
|
||||
static UnsafeSharedSlice create(size_t size) {
|
||||
static_assert(std::is_standard_layout<HeaderT>::value, "HeaderT must have statdard layout");
|
||||
auto ptr = std::make_unique<char[]>(size + sizeof(HeaderT));
|
||||
auto ptr = std::make_unique<char[]>(sizeof(HeaderT) + size);
|
||||
auto header_ptr = new (ptr.get()) HeaderT(size);
|
||||
CHECK(reinterpret_cast<char *>(header_ptr) == ptr.get());
|
||||
CHECK(header_ptr == reinterpret_cast<HeaderT *>(ptr.get()));
|
||||
|
||||
return UnsafeSharedSlice(std::move(ptr));
|
||||
}
|
||||
|
@ -149,7 +152,7 @@ class UnsafeSharedSlice {
|
|||
return reinterpret_cast<HeaderT *>(ptr_.get());
|
||||
}
|
||||
|
||||
struct Destructor {
|
||||
struct SharedSliceDestructor {
|
||||
void operator()(char *ptr) {
|
||||
auto header = reinterpret_cast<HeaderT *>(ptr);
|
||||
if (header->dec()) {
|
||||
|
@ -161,7 +164,7 @@ class UnsafeSharedSlice {
|
|||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<char[], Destructor> ptr_;
|
||||
std::unique_ptr<char[], SharedSliceDestructor> ptr_;
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
|
@ -175,8 +178,7 @@ class SharedSlice {
|
|||
public:
|
||||
SharedSlice() = default;
|
||||
|
||||
explicit SharedSlice(Slice slice) : impl_(Impl::create(slice.size())) {
|
||||
impl_.as_mutable_slice().copy_from(slice);
|
||||
explicit SharedSlice(Slice slice) : impl_(Impl::create(slice)) {
|
||||
}
|
||||
|
||||
explicit SharedSlice(UniqueSharedSlice from);
|
||||
|
@ -316,7 +318,7 @@ class UniqueSliceImpl {
|
|||
explicit UniqueSliceImpl(size_t size) : impl_(Impl::create(size)) {
|
||||
}
|
||||
UniqueSliceImpl(size_t size, char c) : impl_(Impl::create(size)) {
|
||||
std::memset(as_mutable_slice().data(), c, size);
|
||||
as_mutable_slice().fill(c);
|
||||
}
|
||||
explicit UniqueSliceImpl(Slice slice) : impl_(Impl::create(slice)) {
|
||||
}
|
||||
|
|
|
@ -73,7 +73,7 @@ class MutableSlice {
|
|||
char &back();
|
||||
char &operator[](size_t i);
|
||||
|
||||
static const size_t npos = string::npos;
|
||||
static const size_t npos = static_cast<size_t>(-1);
|
||||
};
|
||||
|
||||
class Slice {
|
||||
|
@ -138,7 +138,7 @@ class Slice {
|
|||
char back() const;
|
||||
char operator[](size_t i) const;
|
||||
|
||||
static const size_t npos = string::npos;
|
||||
static const size_t npos = static_cast<size_t>(-1);
|
||||
};
|
||||
|
||||
bool operator==(const Slice &a, const Slice &b);
|
||||
|
|
|
@ -16,7 +16,9 @@
|
|||
|
||||
Copyright 2017-2020 Telegram Systems LLP
|
||||
*/
|
||||
|
||||
#include "td/utils/Slice.h"
|
||||
|
||||
#if TD_HAVE_OPENSSL
|
||||
#include <openssl/crypto.h>
|
||||
#endif
|
||||
|
@ -26,16 +28,18 @@ namespace td {
|
|||
void MutableSlice::fill(char c) {
|
||||
std::memset(data(), c, size());
|
||||
}
|
||||
|
||||
void MutableSlice::fill_zero() {
|
||||
fill(0);
|
||||
fill('\0');
|
||||
}
|
||||
|
||||
void MutableSlice::fill_zero_secure() {
|
||||
#if TD_HAVE_OPENSSL
|
||||
OPENSSL_cleanse(begin(), size());
|
||||
#else
|
||||
volatile char *ptr = begin();
|
||||
for (size_t i = 0; i < size(); i++) {
|
||||
ptr[i] = 0;
|
||||
ptr[i] = '\0';
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "td/utils/common.h"
|
||||
|
||||
#include <array>
|
||||
#include <iterator>
|
||||
|
||||
namespace td {
|
||||
|
||||
|
@ -109,18 +110,20 @@ class SpanImpl {
|
|||
InnerT *data() const {
|
||||
return data_;
|
||||
}
|
||||
|
||||
InnerT *begin() const {
|
||||
return data_;
|
||||
}
|
||||
InnerT *end() const {
|
||||
return data_ + size_;
|
||||
}
|
||||
auto rbegin() const {
|
||||
std::reverse_iterator<InnerT *> rbegin() const {
|
||||
return std::reverse_iterator<InnerT *>(end());
|
||||
}
|
||||
auto rend() const {
|
||||
std::reverse_iterator<InnerT *> rend() const {
|
||||
return std::reverse_iterator<InnerT *>(begin());
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
return size_;
|
||||
}
|
||||
|
|
|
@ -71,7 +71,7 @@
|
|||
#define TRY_RESULT_PROMISE(promise_name, name, result) \
|
||||
TRY_RESULT_PROMISE_IMPL(promise_name, TD_CONCAT(TD_CONCAT(r_, name), __LINE__), auto name, result)
|
||||
|
||||
#define TRY_RESULT_ASSIGN(name, result) TRY_RESULT_IMPL(TD_CONCAT(TD_CONCAT(r_, name), __LINE__), name, result)
|
||||
#define TRY_RESULT_ASSIGN(name, result) TRY_RESULT_IMPL(TD_CONCAT(r_response, __LINE__), name, result)
|
||||
|
||||
#define TRY_RESULT_PROMISE_ASSIGN(promise_name, name, result) \
|
||||
TRY_RESULT_PROMISE_IMPL(promise_name, TD_CONCAT(TD_CONCAT(r_, name), __LINE__), name, result)
|
||||
|
@ -133,19 +133,19 @@
|
|||
|
||||
#if TD_PORT_POSIX
|
||||
#define OS_ERROR(message) \
|
||||
[&]() { \
|
||||
[&] { \
|
||||
auto saved_errno = errno; \
|
||||
return ::td::Status::PosixError(saved_errno, (message)); \
|
||||
}()
|
||||
#define OS_SOCKET_ERROR(message) OS_ERROR(message)
|
||||
#elif TD_PORT_WINDOWS
|
||||
#define OS_ERROR(message) \
|
||||
[&]() { \
|
||||
[&] { \
|
||||
auto saved_error = ::GetLastError(); \
|
||||
return ::td::Status::WindowsError(saved_error, (message)); \
|
||||
}()
|
||||
#define OS_SOCKET_ERROR(message) \
|
||||
[&]() { \
|
||||
[&] { \
|
||||
auto saved_error = ::WSAGetLastError(); \
|
||||
return ::td::Status::WindowsError(saved_error, (message)); \
|
||||
}()
|
||||
|
@ -162,7 +162,7 @@ string winerror_to_string(int code);
|
|||
#endif
|
||||
|
||||
class Status {
|
||||
enum class ErrorType : int8 { general, os };
|
||||
enum class ErrorType : int8 { General, Os };
|
||||
|
||||
public:
|
||||
Status() = default;
|
||||
|
@ -187,7 +187,7 @@ class Status {
|
|||
}
|
||||
|
||||
static Status Error(int err, Slice message = Slice()) TD_WARN_UNUSED_RESULT {
|
||||
return Status(false, ErrorType::general, err, message);
|
||||
return Status(false, ErrorType::General, err, message);
|
||||
}
|
||||
|
||||
static Status Error(Slice message) TD_WARN_UNUSED_RESULT {
|
||||
|
@ -196,13 +196,13 @@ class Status {
|
|||
|
||||
#if TD_PORT_WINDOWS
|
||||
static Status WindowsError(int saved_error, Slice message) TD_WARN_UNUSED_RESULT {
|
||||
return Status(false, ErrorType::os, saved_error, message);
|
||||
return Status(false, ErrorType::Os, saved_error, message);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if TD_PORT_POSIX
|
||||
static Status PosixError(int32 saved_errno, Slice message) TD_WARN_UNUSED_RESULT {
|
||||
return Status(false, ErrorType::os, saved_errno, message);
|
||||
return Status(false, ErrorType::Os, saved_errno, message);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -212,7 +212,7 @@ class Status {
|
|||
|
||||
template <int Code>
|
||||
static Status Error() {
|
||||
static Status status(true, ErrorType::general, Code, "");
|
||||
static Status status(true, ErrorType::General, Code, "");
|
||||
return status.clone_static();
|
||||
}
|
||||
|
||||
|
@ -222,10 +222,10 @@ class Status {
|
|||
}
|
||||
Info info = get_info();
|
||||
switch (info.error_type) {
|
||||
case ErrorType::general:
|
||||
case ErrorType::General:
|
||||
sb << "[Error";
|
||||
break;
|
||||
case ErrorType::os:
|
||||
case ErrorType::Os:
|
||||
#if TD_PORT_POSIX
|
||||
sb << "[PosixError : " << strerror_safe(info.error_code);
|
||||
#elif TD_PORT_WINDOWS
|
||||
|
@ -304,9 +304,9 @@ class Status {
|
|||
}
|
||||
Info info = get_info();
|
||||
switch (info.error_type) {
|
||||
case ErrorType::general:
|
||||
case ErrorType::General:
|
||||
return message().str();
|
||||
case ErrorType::os:
|
||||
case ErrorType::Os:
|
||||
#if TD_PORT_POSIX
|
||||
return strerror_safe(info.error_code).str();
|
||||
#elif TD_PORT_WINDOWS
|
||||
|
@ -343,10 +343,10 @@ class Status {
|
|||
CHECK(is_error());
|
||||
Info info = get_info();
|
||||
switch (info.error_type) {
|
||||
case ErrorType::general:
|
||||
return Error(code(), PSLICE() << prefix << " " << message());
|
||||
case ErrorType::os:
|
||||
return Status(false, ErrorType::os, code(), PSLICE() << prefix << " " << message());
|
||||
case ErrorType::General:
|
||||
return Error(code(), PSLICE() << prefix << message());
|
||||
case ErrorType::Os:
|
||||
return Status(false, ErrorType::Os, code(), PSLICE() << prefix << message());
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return {};
|
||||
|
@ -356,10 +356,10 @@ class Status {
|
|||
CHECK(is_error());
|
||||
Info info = get_info();
|
||||
switch (info.error_type) {
|
||||
case ErrorType::general:
|
||||
return Error(code(), PSLICE() << message() << " " << suffix);
|
||||
case ErrorType::os:
|
||||
return Status(false, ErrorType::os, code(), PSLICE() << message() << " " << suffix);
|
||||
case ErrorType::General:
|
||||
return Error(code(), PSLICE() << message() << suffix);
|
||||
case ErrorType::Os:
|
||||
return Status(false, ErrorType::Os, code(), PSLICE() << message() << suffix);
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return {};
|
||||
|
|
|
@ -34,14 +34,14 @@ namespace td {
|
|||
|
||||
StringBuilder::StringBuilder(MutableSlice slice, bool use_buffer)
|
||||
: begin_ptr_(slice.begin()), current_ptr_(begin_ptr_), use_buffer_(use_buffer) {
|
||||
if (slice.size() <= reserved_size) {
|
||||
auto buffer_size = reserved_size + 100;
|
||||
if (slice.size() <= RESERVED_SIZE) {
|
||||
auto buffer_size = RESERVED_SIZE + 100;
|
||||
buffer_ = std::make_unique<char[]>(buffer_size);
|
||||
begin_ptr_ = buffer_.get();
|
||||
current_ptr_ = begin_ptr_;
|
||||
end_ptr_ = begin_ptr_ + buffer_size - reserved_size;
|
||||
end_ptr_ = begin_ptr_ + buffer_size - RESERVED_SIZE;
|
||||
} else {
|
||||
end_ptr_ = slice.end() - reserved_size;
|
||||
end_ptr_ = slice.end() - RESERVED_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,7 @@ StringBuilder &StringBuilder::operator<<(Slice slice) {
|
|||
if (end_ptr_ < current_ptr_) {
|
||||
return on_error();
|
||||
}
|
||||
auto available_size = static_cast<size_t>(end_ptr_ + reserved_size - 1 - current_ptr_);
|
||||
auto available_size = static_cast<size_t>(end_ptr_ + RESERVED_SIZE - 1 - current_ptr_);
|
||||
if (size > available_size) {
|
||||
error_flag_ = true;
|
||||
size = available_size;
|
||||
|
@ -113,12 +113,12 @@ bool StringBuilder::reserve_inner(size_t size) {
|
|||
}
|
||||
|
||||
size_t old_data_size = current_ptr_ - begin_ptr_;
|
||||
if (size >= std::numeric_limits<size_t>::max() - reserved_size - old_data_size - 1) {
|
||||
if (size >= std::numeric_limits<size_t>::max() - RESERVED_SIZE - old_data_size - 1) {
|
||||
return false;
|
||||
}
|
||||
size_t need_data_size = old_data_size + size;
|
||||
size_t old_buffer_size = end_ptr_ - begin_ptr_;
|
||||
if (old_buffer_size >= (std::numeric_limits<size_t>::max() - reserved_size) / 2 - 2) {
|
||||
if (old_buffer_size >= (std::numeric_limits<size_t>::max() - RESERVED_SIZE) / 2 - 2) {
|
||||
return false;
|
||||
}
|
||||
size_t new_buffer_size = (old_buffer_size + 1) * 2;
|
||||
|
@ -128,13 +128,13 @@ bool StringBuilder::reserve_inner(size_t size) {
|
|||
if (new_buffer_size < 100) {
|
||||
new_buffer_size = 100;
|
||||
}
|
||||
new_buffer_size += reserved_size;
|
||||
new_buffer_size += RESERVED_SIZE;
|
||||
auto new_buffer = std::make_unique<char[]>(new_buffer_size);
|
||||
std::memcpy(new_buffer.get(), begin_ptr_, old_data_size);
|
||||
buffer_ = std::move(new_buffer);
|
||||
begin_ptr_ = buffer_.get();
|
||||
current_ptr_ = begin_ptr_ + old_data_size;
|
||||
end_ptr_ = begin_ptr_ + new_buffer_size - reserved_size;
|
||||
end_ptr_ = begin_ptr_ + new_buffer_size - RESERVED_SIZE;
|
||||
CHECK(end_ptr_ > current_ptr_);
|
||||
CHECK(static_cast<size_t>(end_ptr_ - current_ptr_) >= size);
|
||||
return true;
|
||||
|
@ -205,7 +205,7 @@ StringBuilder &StringBuilder::operator<<(FixedDouble x) {
|
|||
*ss << x.d;
|
||||
|
||||
int len = narrow_cast<int>(static_cast<std::streamoff>(ss->tellp()));
|
||||
auto left = end_ptr_ + reserved_size - current_ptr_;
|
||||
auto left = end_ptr_ + RESERVED_SIZE - current_ptr_;
|
||||
if (unlikely(len >= left)) {
|
||||
error_flag_ = true;
|
||||
len = left ? narrow_cast<int>(left - 1) : 0;
|
||||
|
@ -219,7 +219,7 @@ StringBuilder &StringBuilder::operator<<(const void *ptr) {
|
|||
if (unlikely(!reserve())) {
|
||||
return on_error();
|
||||
}
|
||||
current_ptr_ += std::snprintf(current_ptr_, reserved_size, "%p", ptr);
|
||||
current_ptr_ += std::snprintf(current_ptr_, RESERVED_SIZE, "%p", ptr);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ class StringBuilder {
|
|||
}
|
||||
|
||||
MutableCSlice as_cslice() {
|
||||
if (current_ptr_ >= end_ptr_ + reserved_size) {
|
||||
if (current_ptr_ >= end_ptr_ + RESERVED_SIZE) {
|
||||
std::abort(); // shouldn't happen
|
||||
}
|
||||
*current_ptr_ = 0;
|
||||
|
@ -118,7 +118,7 @@ class StringBuilder {
|
|||
bool error_flag_ = false;
|
||||
bool use_buffer_ = false;
|
||||
std::unique_ptr<char[]> buffer_;
|
||||
static constexpr size_t reserved_size = 30;
|
||||
static constexpr size_t RESERVED_SIZE = 30;
|
||||
|
||||
StringBuilder &on_error() {
|
||||
error_flag_ = true;
|
||||
|
@ -129,7 +129,7 @@ class StringBuilder {
|
|||
if (end_ptr_ > current_ptr_) {
|
||||
return true;
|
||||
}
|
||||
return reserve_inner(reserved_size);
|
||||
return reserve_inner(RESERVED_SIZE);
|
||||
}
|
||||
bool reserve(size_t size) {
|
||||
if (end_ptr_ > current_ptr_ && static_cast<size_t>(end_ptr_ - current_ptr_) >= size) {
|
||||
|
|
|
@ -16,34 +16,34 @@
|
|||
|
||||
Copyright 2017-2020 Telegram Systems LLP
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "td/utils/port/thread.h"
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/port/thread_local.h"
|
||||
#include "td/utils/int_types.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
|
||||
namespace td {
|
||||
template <class T>
|
||||
class ThreadLocalStorage {
|
||||
public:
|
||||
T& get() {
|
||||
T &get() {
|
||||
return thread_local_node().value;
|
||||
}
|
||||
|
||||
template <class F>
|
||||
void for_each(F&& f) {
|
||||
int n = max_thread_id_.load();
|
||||
for (int i = 0; i < n; i++) {
|
||||
void for_each(F &&f) {
|
||||
int32 n = max_thread_id_.load();
|
||||
for (int32 i = 0; i < n; i++) {
|
||||
f(nodes_[i].value);
|
||||
}
|
||||
}
|
||||
template <class F>
|
||||
void for_each(F&& f) const {
|
||||
int n = max_thread_id_.load();
|
||||
for (int i = 0; i < n; i++) {
|
||||
void for_each(F &&f) const {
|
||||
int32 n = max_thread_id_.load();
|
||||
for (int32 i = 0; i < n; i++) {
|
||||
f(nodes_[i].value);
|
||||
}
|
||||
}
|
||||
|
@ -51,13 +51,13 @@ class ThreadLocalStorage {
|
|||
private:
|
||||
struct Node {
|
||||
T value{};
|
||||
char padding[128];
|
||||
char padding[TD_CONCURRENCY_PAD];
|
||||
};
|
||||
static constexpr int MAX_THREAD_ID = 128;
|
||||
std::atomic<int> max_thread_id_{MAX_THREAD_ID};
|
||||
static constexpr int32 MAX_THREAD_ID = 128;
|
||||
std::atomic<int32> max_thread_id_{MAX_THREAD_ID};
|
||||
std::array<Node, MAX_THREAD_ID> nodes_;
|
||||
|
||||
Node& thread_local_node() {
|
||||
Node &thread_local_node() {
|
||||
auto thread_id = get_thread_id();
|
||||
CHECK(0 <= thread_id && static_cast<size_t>(thread_id) < nodes_.size());
|
||||
return nodes_[thread_id];
|
||||
|
|
|
@ -16,11 +16,16 @@
|
|||
|
||||
Copyright 2017-2020 Telegram Systems LLP
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "td/utils/ThreadLocalStorage.h"
|
||||
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/Slice.h"
|
||||
#include "td/utils/StringBuilder.h"
|
||||
#include "td/utils/ThreadLocalStorage.h"
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
|
||||
namespace td {
|
||||
|
@ -35,7 +40,7 @@ class ThreadSafeMultiCounter {
|
|||
int64 sum(size_t index) const {
|
||||
CHECK(index < N);
|
||||
int64 res = 0;
|
||||
tls_.for_each([&](auto &value) { res += value[index].load(); });
|
||||
tls_.for_each([&](auto &value) { res += value[index].load(std::memory_order_relaxed); });
|
||||
return res;
|
||||
}
|
||||
void clear() {
|
||||
|
@ -94,7 +99,7 @@ class NamedThreadSafeCounter {
|
|||
}
|
||||
}
|
||||
CHECK(names_.size() < N);
|
||||
names_.push_back(name.str());
|
||||
names_.emplace_back(name.begin(), name.size());
|
||||
return get_counter_ref(names_.size() - 1);
|
||||
}
|
||||
|
||||
|
|
|
@ -71,7 +71,7 @@ class Timestamp {
|
|||
return Timestamp{timeout};
|
||||
}
|
||||
static Timestamp at_unix(double timeout) {
|
||||
return Timestamp{timeout - td::Clocks::system() + Time::now()};
|
||||
return Timestamp{timeout - Clocks::system() + Time::now()};
|
||||
}
|
||||
|
||||
static Timestamp in(double timeout, td::Timestamp now = td::Timestamp::now_cached()) {
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
|
||||
#include "td/utils/format.h"
|
||||
#include "td/utils/logging.h"
|
||||
//#include "td/utils/Slice.h" // TODO move StringBuilder implementation to cpp, remove header
|
||||
#include "td/utils/Time.h"
|
||||
|
||||
namespace td {
|
||||
|
|
|
@ -16,8 +16,16 @@
|
|||
|
||||
Copyright 2017-2020 Telegram Systems LLP
|
||||
*/
|
||||
#include "TsFileLog.h"
|
||||
|
||||
#include "td/utils/TsFileLog.h"
|
||||
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/FileLog.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/port/thread_local.h"
|
||||
#include "td/utils/Slice.h"
|
||||
|
||||
#include <array>
|
||||
#include <limits>
|
||||
|
||||
namespace td {
|
||||
|
|
|
@ -16,9 +16,12 @@
|
|||
|
||||
Copyright 2017-2020 Telegram Systems LLP
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "td/utils/FileLog.h"
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/Status.h"
|
||||
|
||||
namespace td {
|
||||
class TsFileLog {
|
||||
|
|
213
tdutils/td/utils/TsList.h
Normal file
213
tdutils/td/utils/TsList.h
Normal file
|
@ -0,0 +1,213 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2020 Telegram Systems LLP
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/List.h"
|
||||
|
||||
#include <mutex>
|
||||
|
||||
namespace td {
|
||||
|
||||
template <class DataT>
|
||||
class TsList;
|
||||
|
||||
template <class DataT>
|
||||
class TsListNode : protected ListNode {
|
||||
public:
|
||||
TsListNode() {
|
||||
clear();
|
||||
}
|
||||
explicit TsListNode(DataT &&data) : data_(std::move(data)) {
|
||||
clear();
|
||||
}
|
||||
|
||||
~TsListNode() {
|
||||
remove();
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> lock() TD_WARN_UNUSED_RESULT;
|
||||
|
||||
TsListNode(const TsListNode &) = delete;
|
||||
TsListNode &operator=(const TsListNode &) = delete;
|
||||
|
||||
TsListNode(TsListNode &&other) {
|
||||
other.validate();
|
||||
if (other.empty()) {
|
||||
data_ = std::move(other.data_);
|
||||
clear();
|
||||
} else {
|
||||
auto guard = other.lock();
|
||||
init_from(std::move(other));
|
||||
}
|
||||
validate();
|
||||
other.validate();
|
||||
}
|
||||
|
||||
TsListNode &operator=(TsListNode &&other) {
|
||||
validate();
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
other.validate();
|
||||
remove();
|
||||
|
||||
if (other.empty()) {
|
||||
data_ = std::move(other.data_);
|
||||
} else {
|
||||
auto guard = other.lock();
|
||||
init_from(std::move(other));
|
||||
}
|
||||
|
||||
validate();
|
||||
other.validate();
|
||||
return *this;
|
||||
}
|
||||
|
||||
void validate() {
|
||||
CHECK(empty() || !ListNode::empty() || is_root);
|
||||
CHECK(!empty() || ListNode::empty());
|
||||
}
|
||||
|
||||
void remove() {
|
||||
validate();
|
||||
if (is_root) {
|
||||
CHECK(ListNode::empty());
|
||||
return;
|
||||
}
|
||||
if (empty()) {
|
||||
CHECK(ListNode::empty());
|
||||
return;
|
||||
}
|
||||
{
|
||||
auto guard = lock();
|
||||
ListNode::remove();
|
||||
if (!is_root) {
|
||||
parent = nullptr;
|
||||
}
|
||||
}
|
||||
validate();
|
||||
}
|
||||
|
||||
void put(TsListNode *other) {
|
||||
validate();
|
||||
other->validate();
|
||||
DCHECK(other->empty());
|
||||
DCHECK(!empty());
|
||||
DCHECK(!other->is_root);
|
||||
{
|
||||
auto guard = lock();
|
||||
ListNode::put(other);
|
||||
other->parent = parent;
|
||||
}
|
||||
validate();
|
||||
other->validate();
|
||||
}
|
||||
|
||||
void put_back(TsListNode *other) {
|
||||
DCHECK(other->empty());
|
||||
DCHECK(!empty());
|
||||
DCHECK(!other->is_root);
|
||||
auto guard = lock();
|
||||
ListNode::put_back(other);
|
||||
other->parent = parent;
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
return parent == nullptr;
|
||||
}
|
||||
|
||||
TsListNode *get_next() {
|
||||
return static_cast<TsListNode *>(next);
|
||||
}
|
||||
TsListNode *get_prev() {
|
||||
return static_cast<TsListNode *>(prev);
|
||||
}
|
||||
|
||||
DataT &get_data_unsafe() {
|
||||
return data_;
|
||||
}
|
||||
|
||||
private:
|
||||
TsList<DataT> *parent;
|
||||
bool is_root{false};
|
||||
DataT data_;
|
||||
|
||||
friend class TsList<DataT>;
|
||||
|
||||
void clear() {
|
||||
ListNode::clear();
|
||||
if (!is_root) {
|
||||
parent = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void init_from(TsListNode &&other) {
|
||||
ListNode::init_from(std::move(other));
|
||||
parent = other.parent;
|
||||
other.parent = nullptr;
|
||||
data_ = std::move(other.data_);
|
||||
}
|
||||
};
|
||||
|
||||
template <class DataT>
|
||||
class TsList : public TsListNode<DataT> {
|
||||
public:
|
||||
TsList() {
|
||||
this->parent = this;
|
||||
this->is_root = true;
|
||||
}
|
||||
TsList(const TsList &) = delete;
|
||||
TsList &operator=(const TsList &) = delete;
|
||||
TsList(TsList &&) = delete;
|
||||
TsList &operator=(TsList &&) = delete;
|
||||
~TsList() {
|
||||
CHECK(ListNode::empty());
|
||||
this->parent = nullptr;
|
||||
}
|
||||
std::unique_lock<std::mutex> lock() TD_WARN_UNUSED_RESULT {
|
||||
return std::unique_lock<std::mutex>(mutex_);
|
||||
}
|
||||
TsListNode<DataT> *begin() {
|
||||
return this->get_next();
|
||||
}
|
||||
TsListNode<DataT> *end() {
|
||||
return this;
|
||||
}
|
||||
TsListNode<DataT> *get() {
|
||||
auto guard = lock();
|
||||
auto res = static_cast<TsListNode<DataT> *>(ListNode::get());
|
||||
if (res) {
|
||||
res->parent = nullptr;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex mutex_;
|
||||
};
|
||||
|
||||
template <class DataT>
|
||||
std::unique_lock<std::mutex> TsListNode<DataT>::lock() {
|
||||
CHECK(parent != nullptr);
|
||||
return parent->lock();
|
||||
}
|
||||
|
||||
} // namespace td
|
|
@ -21,8 +21,6 @@
|
|||
#include "td/utils/common.h"
|
||||
#include "td/utils/Slice.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace td {
|
||||
|
||||
template <size_t size>
|
||||
|
@ -69,8 +67,8 @@ bool operator!=(const UInt<size> &a, const UInt<size> &b) {
|
|||
}
|
||||
|
||||
template <size_t size>
|
||||
td::UInt<size> operator^(const UInt<size> &a, const UInt<size> &b) {
|
||||
td::UInt<size> res;
|
||||
UInt<size> operator^(const UInt<size> &a, const UInt<size> &b) {
|
||||
UInt<size> res;
|
||||
for (size_t i = 0; i < size / 8; i++) {
|
||||
res.raw[i] = static_cast<uint8>(a.raw[i] ^ b.raw[i]);
|
||||
}
|
||||
|
|
|
@ -18,10 +18,10 @@
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/Span.h"
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace td {
|
||||
|
||||
|
@ -30,7 +30,7 @@ class VectorQueue {
|
|||
public:
|
||||
template <class S>
|
||||
void push(S &&s) {
|
||||
vector_.push_back(std::forward<S>(s));
|
||||
vector_.emplace_back(std::forward<S>(s));
|
||||
}
|
||||
template <class... Args>
|
||||
void emplace(Args &&... args) {
|
||||
|
@ -62,21 +62,21 @@ class VectorQueue {
|
|||
size_t size() const {
|
||||
return vector_.size() - read_pos_;
|
||||
}
|
||||
T *data() {
|
||||
const T *data() const {
|
||||
return vector_.data() + read_pos_;
|
||||
}
|
||||
const T *data() const {
|
||||
T *data() {
|
||||
return vector_.data() + read_pos_;
|
||||
}
|
||||
Span<T> as_span() const {
|
||||
return {data(), size()};
|
||||
}
|
||||
MutableSpan<T> as_mutable_span() {
|
||||
return {data(), size()};
|
||||
return {vector_.data() + read_pos_, size()};
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<T> vector_;
|
||||
vector<T> vector_;
|
||||
size_t read_pos_{0};
|
||||
|
||||
void try_shrink() {
|
||||
|
|
|
@ -16,19 +16,238 @@
|
|||
|
||||
Copyright 2017-2020 Telegram Systems LLP
|
||||
*/
|
||||
|
||||
#include "td/utils/base64.h"
|
||||
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/Slice.h"
|
||||
#include "td/utils/Status.h"
|
||||
|
||||
#include "td/utils/format.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
namespace td {
|
||||
//TODO: fix copypaste
|
||||
|
||||
template <bool is_url>
|
||||
static const char *get_characters() {
|
||||
return is_url ? "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
|
||||
: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
}
|
||||
|
||||
template <bool is_url>
|
||||
static const unsigned char *get_character_table() {
|
||||
static unsigned char char_to_value[256];
|
||||
static bool is_inited = [] {
|
||||
auto characters = get_characters<is_url>();
|
||||
std::fill(std::begin(char_to_value), std::end(char_to_value), static_cast<unsigned char>(64));
|
||||
for (unsigned char i = 0; i < 64; i++) {
|
||||
char_to_value[static_cast<size_t>(characters[i])] = i;
|
||||
}
|
||||
return true;
|
||||
}();
|
||||
CHECK(is_inited);
|
||||
return char_to_value;
|
||||
}
|
||||
|
||||
template <bool is_url>
|
||||
string base64_encode_impl(Slice input) {
|
||||
auto characters = get_characters<is_url>();
|
||||
string base64;
|
||||
base64.reserve((input.size() + 2) / 3 * 4);
|
||||
for (size_t i = 0; i < input.size();) {
|
||||
size_t left = min(input.size() - i, static_cast<size_t>(3));
|
||||
int c = input.ubegin()[i++] << 16;
|
||||
base64 += characters[c >> 18];
|
||||
if (left != 1) {
|
||||
c |= input.ubegin()[i++] << 8;
|
||||
}
|
||||
base64 += characters[(c >> 12) & 63];
|
||||
if (left == 3) {
|
||||
c |= input.ubegin()[i++];
|
||||
}
|
||||
if (left != 1) {
|
||||
base64 += characters[(c >> 6) & 63];
|
||||
} else if (!is_url) {
|
||||
base64 += '=';
|
||||
}
|
||||
if (left == 3) {
|
||||
base64 += characters[c & 63];
|
||||
} else if (!is_url) {
|
||||
base64 += '=';
|
||||
}
|
||||
}
|
||||
return base64;
|
||||
}
|
||||
|
||||
string base64_encode(Slice input) {
|
||||
return base64_encode_impl<false>(input);
|
||||
}
|
||||
|
||||
string base64url_encode(Slice input) {
|
||||
return base64_encode_impl<true>(input);
|
||||
}
|
||||
|
||||
template <bool is_url>
|
||||
Result<Slice> base64_drop_padding(Slice base64) {
|
||||
size_t padding_length = 0;
|
||||
while (!base64.empty() && base64.back() == '=') {
|
||||
base64.remove_suffix(1);
|
||||
padding_length++;
|
||||
}
|
||||
if (padding_length >= 3) {
|
||||
return Status::Error("Wrong string padding");
|
||||
}
|
||||
if ((!is_url || padding_length > 0) && ((base64.size() + padding_length) & 3) != 0) {
|
||||
return Status::Error("Wrong padding length");
|
||||
}
|
||||
if (is_url && (base64.size() & 3) == 1) {
|
||||
return Status::Error("Wrong string length");
|
||||
}
|
||||
return base64;
|
||||
}
|
||||
|
||||
static Status do_base64_decode_impl(Slice base64, const unsigned char *table, char *ptr) {
|
||||
for (size_t i = 0; i < base64.size();) {
|
||||
size_t left = min(base64.size() - i, static_cast<size_t>(4));
|
||||
int c = 0;
|
||||
for (size_t t = 0; t < left; t++) {
|
||||
auto value = table[base64.ubegin()[i++]];
|
||||
if (value == 64) {
|
||||
return Status::Error("Wrong character in the string");
|
||||
}
|
||||
c |= value << ((3 - t) * 6);
|
||||
}
|
||||
*ptr++ = static_cast<char>(static_cast<unsigned char>(c >> 16)); // implementation-defined
|
||||
if (left == 2) {
|
||||
if ((c & ((1 << 16) - 1)) != 0) {
|
||||
return Status::Error("Wrong padding in the string");
|
||||
}
|
||||
} else {
|
||||
*ptr++ = static_cast<char>(static_cast<unsigned char>(c >> 8)); // implementation-defined
|
||||
if (left == 3) {
|
||||
if ((c & ((1 << 8) - 1)) != 0) {
|
||||
return Status::Error("Wrong padding in the string");
|
||||
}
|
||||
} else {
|
||||
*ptr++ = static_cast<char>(static_cast<unsigned char>(c)); // implementation-defined
|
||||
}
|
||||
}
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static T create_empty(size_t size);
|
||||
|
||||
template <>
|
||||
string create_empty<string>(size_t size) {
|
||||
return string(size, '\0');
|
||||
}
|
||||
|
||||
template <>
|
||||
SecureString create_empty<SecureString>(size_t size) {
|
||||
return SecureString{size};
|
||||
}
|
||||
|
||||
template <bool is_url, class T>
|
||||
static Result<T> base64_decode_impl(Slice base64) {
|
||||
TRY_RESULT_ASSIGN(base64, base64_drop_padding<is_url>(base64));
|
||||
|
||||
T result = create_empty<T>(base64.size() / 4 * 3 + ((base64.size() & 3) + 1) / 2);
|
||||
TRY_STATUS(do_base64_decode_impl(base64, get_character_table<is_url>(), as_mutable_slice(result).begin()));
|
||||
return std::move(result);
|
||||
}
|
||||
|
||||
Result<string> base64_decode(Slice base64) {
|
||||
return base64_decode_impl<false, string>(base64);
|
||||
}
|
||||
|
||||
Result<SecureString> base64_decode_secure(Slice base64) {
|
||||
return base64_decode_impl<false, SecureString>(base64);
|
||||
}
|
||||
|
||||
Result<string> base64url_decode(Slice base64) {
|
||||
return base64_decode_impl<true, string>(base64);
|
||||
}
|
||||
|
||||
template <bool is_url>
|
||||
static bool is_base64_impl(Slice input) {
|
||||
size_t padding_length = 0;
|
||||
while (!input.empty() && input.back() == '=') {
|
||||
input.remove_suffix(1);
|
||||
padding_length++;
|
||||
}
|
||||
if (padding_length >= 3) {
|
||||
return false;
|
||||
}
|
||||
if ((!is_url || padding_length > 0) && ((input.size() + padding_length) & 3) != 0) {
|
||||
return false;
|
||||
}
|
||||
if (is_url && (input.size() & 3) == 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto table = get_character_table<is_url>();
|
||||
for (auto c : input) {
|
||||
if (table[static_cast<unsigned char>(c)] == 64) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((input.size() & 3) == 2) {
|
||||
auto value = table[static_cast<int>(input.back())];
|
||||
if ((value & 15) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if ((input.size() & 3) == 3) {
|
||||
auto value = table[static_cast<int>(input.back())];
|
||||
if ((value & 3) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool is_base64(Slice input) {
|
||||
return is_base64_impl<false>(input);
|
||||
}
|
||||
|
||||
bool is_base64url(Slice input) {
|
||||
return is_base64_impl<true>(input);
|
||||
}
|
||||
|
||||
template <bool is_url>
|
||||
static bool is_base64_characters_impl(Slice input) {
|
||||
auto table = get_character_table<is_url>();
|
||||
for (auto c : input) {
|
||||
if (table[static_cast<unsigned char>(c)] == 64) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool is_base64_characters(Slice input) {
|
||||
return is_base64_characters_impl<false>(input);
|
||||
}
|
||||
|
||||
bool is_base64url_characters(Slice input) {
|
||||
return is_base64_characters_impl<true>(input);
|
||||
}
|
||||
|
||||
string base64_filter(Slice input) {
|
||||
auto table = get_character_table<false>();
|
||||
string res;
|
||||
res.reserve(input.size());
|
||||
for (auto c : input) {
|
||||
if (table[static_cast<unsigned char>(c)] != 64 || c == '=') {
|
||||
res += c;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static const char *const symbols32_lc = "abcdefghijklmnopqrstuvwxyz234567";
|
||||
static const char *const symbols32_uc = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
||||
|
@ -91,274 +310,4 @@ Result<string> base32_decode(Slice base32) {
|
|||
//TODO: check padding
|
||||
return res;
|
||||
}
|
||||
|
||||
static const char *const symbols64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
string base64_encode(Slice input) {
|
||||
string base64;
|
||||
base64.reserve((input.size() + 2) / 3 * 4);
|
||||
for (size_t i = 0; i < input.size();) {
|
||||
size_t left = min(input.size() - i, static_cast<size_t>(3));
|
||||
int c = input.ubegin()[i++] << 16;
|
||||
base64 += symbols64[c >> 18];
|
||||
if (left != 1) {
|
||||
c |= input.ubegin()[i++] << 8;
|
||||
}
|
||||
base64 += symbols64[(c >> 12) & 63];
|
||||
if (left == 3) {
|
||||
c |= input.ubegin()[i++];
|
||||
}
|
||||
if (left != 1) {
|
||||
base64 += symbols64[(c >> 6) & 63];
|
||||
} else {
|
||||
base64 += '=';
|
||||
}
|
||||
if (left == 3) {
|
||||
base64 += symbols64[c & 63];
|
||||
} else {
|
||||
base64 += '=';
|
||||
}
|
||||
}
|
||||
return base64;
|
||||
}
|
||||
|
||||
static unsigned char char_to_value[256];
|
||||
static void init_base64_table() {
|
||||
static bool is_inited = [] {
|
||||
std::fill(std::begin(char_to_value), std::end(char_to_value), static_cast<unsigned char>(64));
|
||||
for (unsigned char i = 0; i < 64; i++) {
|
||||
char_to_value[static_cast<size_t>(symbols64[i])] = i;
|
||||
}
|
||||
return true;
|
||||
}();
|
||||
CHECK(is_inited);
|
||||
}
|
||||
|
||||
Result<Slice> base64_drop_padding(Slice base64) {
|
||||
if ((base64.size() & 3) != 0) {
|
||||
return Status::Error("Wrong string length");
|
||||
}
|
||||
|
||||
size_t padding_length = 0;
|
||||
while (!base64.empty() && base64.back() == '=') {
|
||||
base64.remove_suffix(1);
|
||||
padding_length++;
|
||||
}
|
||||
if (padding_length >= 3) {
|
||||
return Status::Error("Wrong string padding");
|
||||
}
|
||||
return base64;
|
||||
}
|
||||
|
||||
template <class F>
|
||||
Status base64_do_decode(Slice base64, F &&append) {
|
||||
for (size_t i = 0; i < base64.size();) {
|
||||
size_t left = min(base64.size() - i, static_cast<size_t>(4));
|
||||
int c = 0;
|
||||
for (size_t t = 0; t < left; t++) {
|
||||
auto value = char_to_value[base64.ubegin()[i++]];
|
||||
if (value == 64) {
|
||||
return Status::Error("Wrong character in the string");
|
||||
}
|
||||
c |= value << ((3 - t) * 6);
|
||||
}
|
||||
append(static_cast<char>(static_cast<unsigned char>(c >> 16))); // implementation-defined
|
||||
if (left == 2) {
|
||||
if ((c & ((1 << 16) - 1)) != 0) {
|
||||
return Status::Error("Wrong padding in the string");
|
||||
}
|
||||
} else {
|
||||
append(static_cast<char>(static_cast<unsigned char>(c >> 8))); // implementation-defined
|
||||
if (left == 3) {
|
||||
if ((c & ((1 << 8) - 1)) != 0) {
|
||||
return Status::Error("Wrong padding in the string");
|
||||
}
|
||||
} else {
|
||||
append(static_cast<char>(static_cast<unsigned char>(c))); // implementation-defined
|
||||
}
|
||||
}
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Result<string> base64_decode(Slice base64) {
|
||||
init_base64_table();
|
||||
|
||||
TRY_RESULT(tmp, base64_drop_padding(base64));
|
||||
base64 = tmp;
|
||||
|
||||
string output;
|
||||
output.reserve(((base64.size() + 3) >> 2) * 3);
|
||||
TRY_STATUS(base64_do_decode(base64, [&output](char c) { output += c; }));
|
||||
return output;
|
||||
}
|
||||
|
||||
Result<SecureString> base64_decode_secure(Slice base64) {
|
||||
init_base64_table();
|
||||
|
||||
TRY_RESULT(tmp, base64_drop_padding(base64));
|
||||
base64 = tmp;
|
||||
|
||||
SecureString output(((base64.size() + 3) >> 2) * 3);
|
||||
char *ptr = output.as_mutable_slice().begin();
|
||||
TRY_STATUS(base64_do_decode(base64, [&ptr](char c) { *ptr++ = c; }));
|
||||
size_t size = ptr - output.as_mutable_slice().begin();
|
||||
if (size == output.size()) {
|
||||
return std::move(output);
|
||||
}
|
||||
return SecureString(output.as_slice().substr(0, size));
|
||||
}
|
||||
|
||||
static const char *const url_symbols64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
||||
|
||||
string base64url_encode(Slice input) {
|
||||
string base64;
|
||||
base64.reserve((input.size() + 2) / 3 * 4);
|
||||
for (size_t i = 0; i < input.size();) {
|
||||
size_t left = min(input.size() - i, static_cast<size_t>(3));
|
||||
int c = input.ubegin()[i++] << 16;
|
||||
base64 += url_symbols64[c >> 18];
|
||||
if (left != 1) {
|
||||
c |= input.ubegin()[i++] << 8;
|
||||
}
|
||||
base64 += url_symbols64[(c >> 12) & 63];
|
||||
if (left == 3) {
|
||||
c |= input.ubegin()[i++];
|
||||
}
|
||||
if (left != 1) {
|
||||
base64 += url_symbols64[(c >> 6) & 63];
|
||||
}
|
||||
if (left == 3) {
|
||||
base64 += url_symbols64[c & 63];
|
||||
}
|
||||
}
|
||||
return base64;
|
||||
}
|
||||
|
||||
static unsigned char url_char_to_value[256];
|
||||
static void init_base64url_table() {
|
||||
static bool is_inited = [] {
|
||||
std::fill(std::begin(url_char_to_value), std::end(url_char_to_value), static_cast<unsigned char>(64));
|
||||
for (unsigned char i = 0; i < 64; i++) {
|
||||
url_char_to_value[static_cast<size_t>(url_symbols64[i])] = i;
|
||||
}
|
||||
return true;
|
||||
}();
|
||||
CHECK(is_inited);
|
||||
}
|
||||
|
||||
Result<string> base64url_decode(Slice base64) {
|
||||
init_base64url_table();
|
||||
|
||||
size_t padding_length = 0;
|
||||
while (!base64.empty() && base64.back() == '=') {
|
||||
base64.remove_suffix(1);
|
||||
padding_length++;
|
||||
}
|
||||
if (padding_length >= 3 || (padding_length > 0 && ((base64.size() + padding_length) & 3) != 0)) {
|
||||
return Status::Error("Wrong string padding");
|
||||
}
|
||||
|
||||
if ((base64.size() & 3) == 1) {
|
||||
return Status::Error("Wrong string length");
|
||||
}
|
||||
|
||||
string output;
|
||||
output.reserve(((base64.size() + 3) >> 2) * 3);
|
||||
for (size_t i = 0; i < base64.size();) {
|
||||
size_t left = min(base64.size() - i, static_cast<size_t>(4));
|
||||
int c = 0;
|
||||
for (size_t t = 0; t < left; t++) {
|
||||
auto value = url_char_to_value[base64.ubegin()[i++]];
|
||||
if (value == 64) {
|
||||
return Status::Error("Wrong character in the string");
|
||||
}
|
||||
c |= value << ((3 - t) * 6);
|
||||
}
|
||||
output += static_cast<char>(static_cast<unsigned char>(c >> 16)); // implementation-defined
|
||||
if (left == 2) {
|
||||
if ((c & ((1 << 16) - 1)) != 0) {
|
||||
return Status::Error("Wrong padding in the string");
|
||||
}
|
||||
} else {
|
||||
output += static_cast<char>(static_cast<unsigned char>(c >> 8)); // implementation-defined
|
||||
if (left == 3) {
|
||||
if ((c & ((1 << 8) - 1)) != 0) {
|
||||
return Status::Error("Wrong padding in the string");
|
||||
}
|
||||
} else {
|
||||
output += static_cast<char>(static_cast<unsigned char>(c)); // implementation-defined
|
||||
}
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
template <bool is_url>
|
||||
static bool is_base64_impl(Slice input) {
|
||||
size_t padding_length = 0;
|
||||
while (!input.empty() && input.back() == '=') {
|
||||
input.remove_suffix(1);
|
||||
padding_length++;
|
||||
}
|
||||
if (padding_length >= 3) {
|
||||
return false;
|
||||
}
|
||||
if ((!is_url || padding_length > 0) && ((input.size() + padding_length) & 3) != 0) {
|
||||
return false;
|
||||
}
|
||||
if (is_url && (input.size() & 3) == 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned char *table;
|
||||
if (is_url) {
|
||||
init_base64url_table();
|
||||
table = url_char_to_value;
|
||||
} else {
|
||||
init_base64_table();
|
||||
table = char_to_value;
|
||||
}
|
||||
for (auto c : input) {
|
||||
if (table[static_cast<unsigned char>(c)] == 64) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((input.size() & 3) == 2) {
|
||||
auto value = table[static_cast<int>(input.back())];
|
||||
if ((value & 15) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if ((input.size() & 3) == 3) {
|
||||
auto value = table[static_cast<int>(input.back())];
|
||||
if ((value & 3) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool is_base64(Slice input) {
|
||||
return is_base64_impl<false>(input);
|
||||
}
|
||||
|
||||
bool is_base64url(Slice input) {
|
||||
return is_base64_impl<true>(input);
|
||||
}
|
||||
|
||||
string base64_filter(Slice input) {
|
||||
string res;
|
||||
res.reserve(input.size());
|
||||
init_base64_table();
|
||||
for (auto c : input) {
|
||||
if (char_to_value[static_cast<unsigned char>(c)] != 64 || c == '=') {
|
||||
res += c;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
} // namespace td
|
||||
|
|
|
@ -16,11 +16,12 @@
|
|||
|
||||
Copyright 2017-2020 Telegram Systems LLP
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/Slice.h"
|
||||
#include "td/utils/SharedSlice.h"
|
||||
#include "td/utils/Slice.h"
|
||||
#include "td/utils/Status.h"
|
||||
|
||||
namespace td {
|
||||
|
@ -35,9 +36,10 @@ Result<string> base64url_decode(Slice base64);
|
|||
bool is_base64(Slice input);
|
||||
bool is_base64url(Slice input);
|
||||
|
||||
string base64_filter(Slice input);
|
||||
bool is_base64_characters(Slice input);
|
||||
bool is_base64url_characters(Slice input);
|
||||
|
||||
string base64_filter(Slice input);
|
||||
string base32_encode(Slice input, bool upper_case = false);
|
||||
Result<string> base32_decode(Slice base32);
|
||||
|
||||
} // namespace td
|
||||
|
|
|
@ -59,6 +59,15 @@ inline uint64 lower_bit64(uint64 x) {
|
|||
return x & bits_negate64(x);
|
||||
}
|
||||
|
||||
inline uint64 host_to_big_endian64(uint64 x) {
|
||||
// NB: works only for little-endian systems
|
||||
return bswap64(x);
|
||||
}
|
||||
inline uint64 big_endian_to_host64(uint64 x) {
|
||||
// NB: works only for little-endian systems
|
||||
return bswap64(x);
|
||||
}
|
||||
|
||||
//TODO: optimize
|
||||
inline int32 count_leading_zeroes_non_zero32(uint32 x) {
|
||||
DCHECK(x != 0);
|
||||
|
@ -145,7 +154,6 @@ inline int32 count_bits32(uint32 x) {
|
|||
x = (x + (x >> 4)) & 0x0F0F0F0F;
|
||||
x += x >> 8;
|
||||
return (x + (x >> 16)) & 0x3F;
|
||||
//return __popcnt(x);
|
||||
}
|
||||
|
||||
inline int32 count_bits64(uint64 x) {
|
||||
|
@ -215,11 +223,15 @@ inline uint64 bswap64(uint64 x) {
|
|||
}
|
||||
|
||||
inline int32 count_bits32(uint32 x) {
|
||||
return _popcnt32(static_cast<int>(x));
|
||||
x -= (x >> 1) & 0x55555555;
|
||||
x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
|
||||
x = (x + (x >> 4)) & 0x0F0F0F0F;
|
||||
x += x >> 8;
|
||||
return (x + (x >> 16)) & 0x3F;
|
||||
}
|
||||
|
||||
inline int32 count_bits64(uint64 x) {
|
||||
return _popcnt64(static_cast<__int64>(x));
|
||||
return count_bits32(static_cast<uint32>(x >> 32)) + count_bits32(static_cast<uint32>(x));
|
||||
}
|
||||
|
||||
#else
|
||||
|
|
|
@ -19,8 +19,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/misc.h"
|
||||
#include "td/utils/port/thread_local.h"
|
||||
#include "td/utils/Slice.h"
|
||||
|
||||
|
@ -617,7 +615,7 @@ class ChainBufferReader {
|
|||
}
|
||||
|
||||
ChainBufferReader cut_head(size_t offset) TD_WARN_UNUSED_RESULT {
|
||||
LOG_CHECK(offset <= size()) << offset << " " << size();
|
||||
CHECK(offset <= size());
|
||||
auto it = begin_.clone();
|
||||
it.advance(offset);
|
||||
return cut_head(std::move(it));
|
||||
|
@ -765,8 +763,8 @@ class BufferBuilder {
|
|||
|
||||
template <class F>
|
||||
void for_each(F &&f) const & {
|
||||
for (auto &slice : reversed(to_prepend_)) {
|
||||
f(slice.as_slice());
|
||||
for (auto i = to_prepend_.size(); i > 0; i--) {
|
||||
f(to_prepend_[i - 1].as_slice());
|
||||
}
|
||||
if (!buffer_writer_.empty()) {
|
||||
f(buffer_writer_.as_slice());
|
||||
|
@ -777,8 +775,8 @@ class BufferBuilder {
|
|||
}
|
||||
template <class F>
|
||||
void for_each(F &&f) && {
|
||||
for (auto &slice : reversed(to_prepend_)) {
|
||||
f(std::move(slice));
|
||||
for (auto i = to_prepend_.size(); i > 0; i--) {
|
||||
f(std::move(to_prepend_[i - 1]));
|
||||
}
|
||||
if (!buffer_writer_.empty()) {
|
||||
f(buffer_writer_.as_buffer_slice());
|
||||
|
|
|
@ -5,5 +5,4 @@
|
|||
#cmakedefine01 TD_HAVE_CRC32C
|
||||
#cmakedefine01 TD_HAVE_COROUTINES
|
||||
#cmakedefine01 TD_HAVE_ABSL
|
||||
#cmakedefine01 TD_HAVE_GETOPT
|
||||
#cmakedefine01 TD_FD_DEBUG
|
||||
|
|
|
@ -20,17 +20,23 @@
|
|||
|
||||
#include "td/utils/as.h"
|
||||
#include "td/utils/BigNum.h"
|
||||
#include "td/utils/bits.h"
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/misc.h"
|
||||
#include "td/utils/port/RwMutex.h"
|
||||
#include "td/utils/port/thread_local.h"
|
||||
#include "td/utils/Random.h"
|
||||
#include "td/utils/ScopeGuard.h"
|
||||
#include "td/utils/SharedSlice.h"
|
||||
#include "td/utils/StackAllocator.h"
|
||||
#include "td/utils/StringBuilder.h"
|
||||
|
||||
#if TD_HAVE_OPENSSL
|
||||
#include <openssl/aes.h>
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/crypto.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/hmac.h>
|
||||
#include <openssl/md5.h>
|
||||
|
@ -48,6 +54,8 @@
|
|||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
#include <utility>
|
||||
|
||||
|
@ -144,11 +152,13 @@ uint64 pq_factorize(uint64 pq) {
|
|||
void init_crypto() {
|
||||
static bool is_inited = [] {
|
||||
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
||||
return OPENSSL_init_crypto(0, nullptr) != 0;
|
||||
bool result = OPENSSL_init_crypto(0, nullptr) != 0;
|
||||
#else
|
||||
OpenSSL_add_all_algorithms();
|
||||
return true;
|
||||
bool result = true;
|
||||
#endif
|
||||
clear_openssl_errors("Init crypto");
|
||||
return result;
|
||||
}();
|
||||
CHECK(is_inited);
|
||||
}
|
||||
|
@ -253,34 +263,336 @@ int pq_factorize(Slice pq_str, string *p_str, string *q_str) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef OPENSSL_IS_BORINGSSL
|
||||
extern "C" {
|
||||
void AES_ige_encrypt(const unsigned char *in_ptr, unsigned char *out, size_t length, const AES_KEY *key,
|
||||
unsigned char *ivec, const int enc);
|
||||
}
|
||||
#endif
|
||||
struct AesBlock {
|
||||
uint64 hi;
|
||||
uint64 lo;
|
||||
|
||||
static void aes_ige_xcrypt(Slice aes_key, MutableSlice aes_iv, Slice from, MutableSlice to, bool encrypt_flag) {
|
||||
CHECK(aes_key.size() == 32);
|
||||
CHECK(aes_iv.size() == 16);
|
||||
AES_KEY key;
|
||||
int err;
|
||||
if (encrypt_flag) {
|
||||
err = AES_set_encrypt_key(aes_key.ubegin(), 256, &key);
|
||||
} else {
|
||||
err = AES_set_decrypt_key(aes_key.ubegin(), 256, &key);
|
||||
uint8 *raw() {
|
||||
return reinterpret_cast<uint8 *>(this);
|
||||
}
|
||||
LOG_IF(FATAL, err != 0);
|
||||
CHECK(from.size() <= to.size());
|
||||
AES_ige_encrypt(from.ubegin(), to.ubegin(), from.size(), &key, aes_iv.ubegin(), encrypt_flag);
|
||||
const uint8 *raw() const {
|
||||
return reinterpret_cast<const uint8 *>(this);
|
||||
}
|
||||
Slice as_slice() const {
|
||||
return Slice(raw(), AES_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
AesBlock operator^(const AesBlock &b) const {
|
||||
AesBlock res;
|
||||
res.hi = hi ^ b.hi;
|
||||
res.lo = lo ^ b.lo;
|
||||
return res;
|
||||
}
|
||||
void operator^=(const AesBlock &b) {
|
||||
hi ^= b.hi;
|
||||
lo ^= b.lo;
|
||||
}
|
||||
|
||||
void load(const uint8 *from) {
|
||||
*this = as<AesBlock>(from);
|
||||
}
|
||||
void store(uint8 *to) {
|
||||
as<AesBlock>(to) = *this;
|
||||
}
|
||||
|
||||
AesBlock inc() const {
|
||||
#if SIZE_MAX == UINT64_MAX
|
||||
AesBlock res;
|
||||
res.lo = host_to_big_endian64(big_endian_to_host64(lo) + 1);
|
||||
if (res.lo == 0) {
|
||||
res.hi = host_to_big_endian64(big_endian_to_host64(hi) + 1);
|
||||
} else {
|
||||
res.hi = hi;
|
||||
}
|
||||
return res;
|
||||
#else
|
||||
AesBlock res = *this;
|
||||
auto ptr = res.raw();
|
||||
if (++ptr[15] == 0) {
|
||||
for (int i = 14; i >= 0; i--) {
|
||||
if (++ptr[i] != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(AesBlock) == 16, "");
|
||||
static_assert(sizeof(AesBlock) == AES_BLOCK_SIZE, "");
|
||||
|
||||
class XorBytes {
|
||||
public:
|
||||
static void run(const uint8 *a, const uint8 *b, uint8 *c, size_t n) {
|
||||
static constexpr int BLOCK_SIZE = 16;
|
||||
auto block_cnt = n / BLOCK_SIZE;
|
||||
n -= block_cnt * BLOCK_SIZE;
|
||||
while (block_cnt-- > 0) {
|
||||
Block<BLOCK_SIZE> a_big = as<Block<BLOCK_SIZE>>(a);
|
||||
Block<BLOCK_SIZE> b_big = as<Block<BLOCK_SIZE>>(b);
|
||||
as<Block<BLOCK_SIZE>>(c) = a_big ^ b_big;
|
||||
a += BLOCK_SIZE;
|
||||
b += BLOCK_SIZE;
|
||||
c += BLOCK_SIZE;
|
||||
}
|
||||
while (n-- > 0) {
|
||||
c[n] = a[n] ^ b[n];
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
template <size_t N>
|
||||
struct alignas(N) Block {
|
||||
uint8 data[N];
|
||||
Block operator^(const Block &b) const & {
|
||||
Block res;
|
||||
for (size_t i = 0; i < N; i++) {
|
||||
res.data[i] = data[i] ^ b.data[i];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
struct AesCtrCounterPack {
|
||||
static constexpr size_t BLOCK_COUNT = 32;
|
||||
AesBlock blocks[BLOCK_COUNT];
|
||||
uint8 *raw() {
|
||||
return reinterpret_cast<uint8 *>(this);
|
||||
}
|
||||
const uint8 *raw() const {
|
||||
return reinterpret_cast<const uint8 *>(this);
|
||||
}
|
||||
|
||||
static size_t size() {
|
||||
return sizeof(blocks);
|
||||
}
|
||||
|
||||
void init(AesBlock block) {
|
||||
blocks[0] = block;
|
||||
for (size_t i = 1; i < BLOCK_COUNT; i++) {
|
||||
blocks[i] = blocks[i - 1].inc();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class Evp {
|
||||
public:
|
||||
Evp() {
|
||||
ctx_ = EVP_CIPHER_CTX_new();
|
||||
LOG_IF(FATAL, ctx_ == nullptr);
|
||||
}
|
||||
Evp(const Evp &from) = delete;
|
||||
Evp &operator=(const Evp &from) = delete;
|
||||
Evp(Evp &&from) = delete;
|
||||
Evp &operator=(Evp &&from) = delete;
|
||||
~Evp() {
|
||||
CHECK(ctx_ != nullptr);
|
||||
EVP_CIPHER_CTX_free(ctx_);
|
||||
}
|
||||
|
||||
void init_encrypt_ecb(Slice key) {
|
||||
init(Type::Ecb, true, EVP_aes_256_ecb(), key);
|
||||
}
|
||||
|
||||
void init_decrypt_ecb(Slice key) {
|
||||
init(Type::Ecb, false, EVP_aes_256_ecb(), key);
|
||||
}
|
||||
|
||||
void init_encrypt_cbc(Slice key) {
|
||||
init(Type::Cbc, true, EVP_aes_256_cbc(), key);
|
||||
}
|
||||
|
||||
void init_decrypt_cbc(Slice key) {
|
||||
init(Type::Cbc, false, EVP_aes_256_cbc(), key);
|
||||
}
|
||||
|
||||
void init_iv(Slice iv) {
|
||||
int res = EVP_CipherInit_ex(ctx_, nullptr, nullptr, nullptr, iv.ubegin(), -1);
|
||||
LOG_IF(FATAL, res != 1);
|
||||
}
|
||||
|
||||
void encrypt(const uint8 *src, uint8 *dst, int size) {
|
||||
// CHECK(type_ != Type::Empty && is_encrypt_);
|
||||
CHECK(size % AES_BLOCK_SIZE == 0);
|
||||
int len;
|
||||
int res = EVP_EncryptUpdate(ctx_, dst, &len, src, size);
|
||||
LOG_IF(FATAL, res != 1);
|
||||
CHECK(len == size);
|
||||
}
|
||||
|
||||
void decrypt(const uint8 *src, uint8 *dst, int size) {
|
||||
// CHECK(type_ != Type::Empty && !is_encrypt_);
|
||||
CHECK(size % AES_BLOCK_SIZE == 0);
|
||||
int len;
|
||||
int res = EVP_DecryptUpdate(ctx_, dst, &len, src, size);
|
||||
LOG_IF(FATAL, res != 1);
|
||||
CHECK(len == size);
|
||||
}
|
||||
|
||||
private:
|
||||
EVP_CIPHER_CTX *ctx_{nullptr};
|
||||
enum class Type : int8 { Empty, Ecb, Cbc };
|
||||
// Type type_{Type::Empty};
|
||||
// bool is_encrypt_ = false;
|
||||
|
||||
void init(Type type, bool is_encrypt, const EVP_CIPHER *cipher, Slice key) {
|
||||
// type_ = type;
|
||||
// is_encrypt_ = is_encrypt;
|
||||
int res = EVP_CipherInit_ex(ctx_, cipher, nullptr, key.ubegin(), nullptr, is_encrypt ? 1 : 0);
|
||||
LOG_IF(FATAL, res != 1);
|
||||
EVP_CIPHER_CTX_set_padding(ctx_, 0);
|
||||
}
|
||||
};
|
||||
|
||||
struct AesState::Impl {
|
||||
Evp evp;
|
||||
};
|
||||
|
||||
AesState::AesState() = default;
|
||||
AesState::AesState(AesState &&from) = default;
|
||||
AesState &AesState::operator=(AesState &&from) = default;
|
||||
AesState::~AesState() = default;
|
||||
|
||||
void AesState::init(Slice key, bool encrypt) {
|
||||
CHECK(key.size() == 32);
|
||||
if (!impl_) {
|
||||
impl_ = make_unique<Impl>();
|
||||
}
|
||||
if (encrypt) {
|
||||
impl_->evp.init_encrypt_ecb(key);
|
||||
} else {
|
||||
impl_->evp.init_decrypt_ecb(key);
|
||||
}
|
||||
}
|
||||
|
||||
void AesState::encrypt(const uint8 *src, uint8 *dst, int size) {
|
||||
CHECK(impl_);
|
||||
impl_->evp.encrypt(src, dst, size);
|
||||
}
|
||||
|
||||
void AesState::decrypt(const uint8 *src, uint8 *dst, int size) {
|
||||
CHECK(impl_);
|
||||
impl_->evp.decrypt(src, dst, size);
|
||||
}
|
||||
|
||||
class AesIgeStateImpl {
|
||||
public:
|
||||
void init(Slice key, Slice iv, bool encrypt) {
|
||||
CHECK(key.size() == 32);
|
||||
CHECK(iv.size() == 32);
|
||||
if (encrypt) {
|
||||
evp_.init_encrypt_cbc(key);
|
||||
} else {
|
||||
evp_.init_decrypt_ecb(key);
|
||||
}
|
||||
|
||||
encrypted_iv_.load(iv.ubegin());
|
||||
plaintext_iv_.load(iv.ubegin() + AES_BLOCK_SIZE);
|
||||
}
|
||||
void encrypt(Slice from, MutableSlice to) {
|
||||
CHECK(from.size() % AES_BLOCK_SIZE == 0);
|
||||
CHECK(to.size() >= from.size());
|
||||
auto len = to.size() / AES_BLOCK_SIZE;
|
||||
auto in = from.ubegin();
|
||||
auto out = to.ubegin();
|
||||
|
||||
static constexpr size_t BLOCK_COUNT = 31;
|
||||
while (len != 0) {
|
||||
AesBlock data[BLOCK_COUNT];
|
||||
AesBlock data_xored[BLOCK_COUNT];
|
||||
|
||||
auto count = td::min(BLOCK_COUNT, len);
|
||||
std::memcpy(data, in, AES_BLOCK_SIZE * count);
|
||||
data_xored[0] = data[0];
|
||||
if (count > 1) {
|
||||
data_xored[1] = plaintext_iv_ ^ data[1];
|
||||
for (size_t i = 2; i < count; i++) {
|
||||
data_xored[i] = data[i - 2] ^ data[i];
|
||||
}
|
||||
}
|
||||
|
||||
evp_.init_iv(encrypted_iv_.as_slice());
|
||||
int inlen = static_cast<int>(AES_BLOCK_SIZE * count);
|
||||
evp_.encrypt(data_xored[0].raw(), data_xored[0].raw(), inlen);
|
||||
|
||||
data_xored[0] ^= plaintext_iv_;
|
||||
for (size_t i = 1; i < count; i++) {
|
||||
data_xored[i] ^= data[i - 1];
|
||||
}
|
||||
plaintext_iv_ = data[count - 1];
|
||||
encrypted_iv_ = data_xored[count - 1];
|
||||
|
||||
std::memcpy(out, data_xored, AES_BLOCK_SIZE * count);
|
||||
len -= count;
|
||||
in += AES_BLOCK_SIZE * count;
|
||||
out += AES_BLOCK_SIZE * count;
|
||||
}
|
||||
}
|
||||
|
||||
void decrypt(Slice from, MutableSlice to) {
|
||||
CHECK(from.size() % AES_BLOCK_SIZE == 0);
|
||||
CHECK(to.size() >= from.size());
|
||||
auto len = to.size() / AES_BLOCK_SIZE;
|
||||
auto in = from.ubegin();
|
||||
auto out = to.ubegin();
|
||||
|
||||
AesBlock encrypted;
|
||||
|
||||
while (len) {
|
||||
encrypted.load(in);
|
||||
|
||||
plaintext_iv_ ^= encrypted;
|
||||
evp_.decrypt(plaintext_iv_.raw(), plaintext_iv_.raw(), AES_BLOCK_SIZE);
|
||||
plaintext_iv_ ^= encrypted_iv_;
|
||||
|
||||
plaintext_iv_.store(out);
|
||||
encrypted_iv_ = encrypted;
|
||||
|
||||
--len;
|
||||
in += AES_BLOCK_SIZE;
|
||||
out += AES_BLOCK_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Evp evp_;
|
||||
AesBlock encrypted_iv_;
|
||||
AesBlock plaintext_iv_;
|
||||
};
|
||||
|
||||
AesIgeState::AesIgeState() = default;
|
||||
AesIgeState::AesIgeState(AesIgeState &&from) = default;
|
||||
AesIgeState &AesIgeState::operator=(AesIgeState &&from) = default;
|
||||
AesIgeState::~AesIgeState() = default;
|
||||
|
||||
void AesIgeState::init(Slice key, Slice iv, bool encrypt) {
|
||||
if (!impl_) {
|
||||
impl_ = make_unique<AesIgeStateImpl>();
|
||||
}
|
||||
|
||||
impl_->init(key, iv, encrypt);
|
||||
}
|
||||
|
||||
void AesIgeState::encrypt(Slice from, MutableSlice to) {
|
||||
impl_->encrypt(from, to);
|
||||
}
|
||||
|
||||
void AesIgeState::decrypt(Slice from, MutableSlice to) {
|
||||
impl_->decrypt(from, to);
|
||||
}
|
||||
|
||||
void aes_ige_encrypt(Slice aes_key, MutableSlice aes_iv, Slice from, MutableSlice to) {
|
||||
aes_ige_xcrypt(aes_key, aes_iv, from, to, true);
|
||||
AesIgeStateImpl state;
|
||||
state.init(aes_key, aes_iv, true);
|
||||
state.encrypt(from, to);
|
||||
}
|
||||
|
||||
void aes_ige_decrypt(Slice aes_key, MutableSlice aes_iv, Slice from, MutableSlice to) {
|
||||
aes_ige_xcrypt(aes_key, aes_iv, from, to, false);
|
||||
AesIgeStateImpl state;
|
||||
state.init(aes_key, aes_iv, false);
|
||||
state.decrypt(from, to);
|
||||
}
|
||||
|
||||
static void aes_cbc_xcrypt(Slice aes_key, MutableSlice aes_iv, Slice from, MutableSlice to, bool encrypt_flag) {
|
||||
|
@ -324,35 +636,43 @@ class AesCtrState::Impl {
|
|||
CHECK(key.size() == 32);
|
||||
CHECK(iv.size() == 16);
|
||||
static_assert(AES_BLOCK_SIZE == 16, "");
|
||||
if (AES_set_encrypt_key(key.ubegin(), 256, &aes_key) < 0) {
|
||||
LOG(FATAL) << "Failed to set encrypt key";
|
||||
}
|
||||
counter.as_mutable_slice().copy_from(as_slice(iv));
|
||||
current_pos = 0;
|
||||
evp_.init_encrypt_ecb(key);
|
||||
counter_.load(iv.ubegin());
|
||||
fill();
|
||||
}
|
||||
|
||||
void encrypt(Slice from, MutableSlice to) {
|
||||
CHECK(to.size() >= from.size());
|
||||
for (size_t i = 0; i < from.size(); i++) {
|
||||
if (current_pos == 0) {
|
||||
AES_encrypt(counter.as_slice().ubegin(), encrypted_counter.as_mutable_slice().ubegin(), &aes_key);
|
||||
uint8 *ptr = counter.as_mutable_slice().ubegin();
|
||||
for (int j = 15; j >= 0; j--) {
|
||||
if (++ptr[j] != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
auto *src = from.ubegin();
|
||||
auto *dst = to.ubegin();
|
||||
auto n = from.size();
|
||||
while (n != 0) {
|
||||
size_t left = encrypted_counter_.raw() + AesCtrCounterPack::size() - current_;
|
||||
if (left == 0) {
|
||||
fill();
|
||||
left = AesCtrCounterPack::size();
|
||||
}
|
||||
to[i] = static_cast<char>(from[i] ^ encrypted_counter[current_pos]);
|
||||
current_pos = (current_pos + 1) & 15;
|
||||
size_t min_n = td::min(n, left);
|
||||
XorBytes::run(src, current_, dst, min_n);
|
||||
src += min_n;
|
||||
dst += min_n;
|
||||
n -= min_n;
|
||||
current_ += min_n;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
AES_KEY aes_key;
|
||||
SecureString counter{AES_BLOCK_SIZE};
|
||||
SecureString encrypted_counter{AES_BLOCK_SIZE};
|
||||
uint8 current_pos;
|
||||
Evp evp_;
|
||||
|
||||
uint8 *current_;
|
||||
AesBlock counter_;
|
||||
AesCtrCounterPack encrypted_counter_;
|
||||
|
||||
void fill() {
|
||||
encrypted_counter_.init(counter_);
|
||||
counter_ = encrypted_counter_.blocks[AesCtrCounterPack::BLOCK_COUNT - 1].inc();
|
||||
current_ = encrypted_counter_.raw();
|
||||
evp_.encrypt(current_, current_, static_cast<int>(AesCtrCounterPack::size()));
|
||||
}
|
||||
};
|
||||
|
||||
AesCtrState::AesCtrState() = default;
|
||||
|
@ -689,6 +1009,35 @@ void init_openssl_threads() {
|
|||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Status create_openssl_error(int code, Slice message) {
|
||||
const int max_result_size = 1 << 12;
|
||||
auto result = StackAllocator::alloc(max_result_size);
|
||||
StringBuilder sb(result.as_slice());
|
||||
|
||||
sb << message;
|
||||
while (unsigned long error_code = ERR_get_error()) {
|
||||
char error_buf[1024];
|
||||
ERR_error_string_n(error_code, error_buf, sizeof(error_buf));
|
||||
Slice error(error_buf, std::strlen(error_buf));
|
||||
sb << "{" << error << "}";
|
||||
}
|
||||
LOG_IF(ERROR, sb.is_error()) << "OpenSSL error buffer overflow";
|
||||
LOG(DEBUG) << sb.as_cslice();
|
||||
return Status::Error(code, sb.as_cslice());
|
||||
}
|
||||
|
||||
void clear_openssl_errors(Slice source) {
|
||||
if (ERR_peek_error() != 0) {
|
||||
LOG(ERROR) << source << ": " << create_openssl_error(0, "Unprocessed OPENSSL_ERROR");
|
||||
}
|
||||
#if TD_PORT_WINDOWS
|
||||
WSASetLastError(0);
|
||||
#else
|
||||
errno = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if TD_HAVE_ZLIB
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include "td/utils/buffer.h"
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/SharedSlice.h"
|
||||
#include "td/utils/Slice.h"
|
||||
#include "td/utils/SharedSlice.h"
|
||||
#include "td/utils/Status.h"
|
||||
|
@ -33,9 +34,50 @@ void init_crypto();
|
|||
|
||||
int pq_factorize(Slice pq_str, string *p_str, string *q_str);
|
||||
|
||||
class AesState {
|
||||
public:
|
||||
AesState();
|
||||
AesState(const AesState &from) = delete;
|
||||
AesState &operator=(const AesState &from) = delete;
|
||||
AesState(AesState &&from);
|
||||
AesState &operator=(AesState &&from);
|
||||
~AesState();
|
||||
|
||||
void init(Slice key, bool encrypt);
|
||||
|
||||
void encrypt(const uint8 *src, uint8 *dst, int size);
|
||||
|
||||
void decrypt(const uint8 *src, uint8 *dst, int size);
|
||||
|
||||
private:
|
||||
struct Impl;
|
||||
unique_ptr<Impl> impl_;
|
||||
};
|
||||
|
||||
void aes_ige_encrypt(Slice aes_key, MutableSlice aes_iv, Slice from, MutableSlice to);
|
||||
void aes_ige_decrypt(Slice aes_key, MutableSlice aes_iv, Slice from, MutableSlice to);
|
||||
|
||||
class AesIgeStateImpl;
|
||||
|
||||
class AesIgeState {
|
||||
public:
|
||||
AesIgeState();
|
||||
AesIgeState(const AesIgeState &from) = delete;
|
||||
AesIgeState &operator=(const AesIgeState &from) = delete;
|
||||
AesIgeState(AesIgeState &&from);
|
||||
AesIgeState &operator=(AesIgeState &&from);
|
||||
~AesIgeState();
|
||||
|
||||
void init(Slice key, Slice iv, bool encrypt);
|
||||
|
||||
void encrypt(Slice from, MutableSlice to);
|
||||
|
||||
void decrypt(Slice from, MutableSlice to);
|
||||
|
||||
private:
|
||||
unique_ptr<AesIgeStateImpl> impl_;
|
||||
};
|
||||
|
||||
void aes_cbc_encrypt(Slice aes_key, MutableSlice aes_iv, Slice from, MutableSlice to);
|
||||
void aes_cbc_decrypt(Slice aes_key, MutableSlice aes_iv, Slice from, MutableSlice to);
|
||||
|
||||
|
@ -122,6 +164,10 @@ Result<BufferSlice> rsa_encrypt_pkcs1_oaep(Slice public_key, Slice data);
|
|||
Result<BufferSlice> rsa_decrypt_pkcs1_oaep(Slice private_key, Slice data);
|
||||
|
||||
void init_openssl_threads();
|
||||
|
||||
Status create_openssl_error(int code, Slice message);
|
||||
|
||||
void clear_openssl_errors(Slice source);
|
||||
#endif
|
||||
|
||||
#if TD_HAVE_ZLIB
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "td/utils/buffer.h"
|
||||
#include "td/utils/SharedSlice.h"
|
||||
#include "td/utils/Slice.h"
|
||||
#include "td/utils/SharedSlice.h"
|
||||
#include "td/utils/Status.h"
|
||||
|
|
|
@ -205,7 +205,7 @@ inline StringBuilder &operator<<(StringBuilder &logger, Size t) {
|
|||
uint64 value;
|
||||
};
|
||||
|
||||
static constexpr NamedValue sizes[] = {{"B", 1}, {"kB", 1 << 10}, {"MB", 1 << 20}, {"GB", 1 << 30}};
|
||||
static constexpr NamedValue sizes[] = {{"B", 1}, {"KB", 1 << 10}, {"MB", 1 << 20}, {"GB", 1 << 30}};
|
||||
static constexpr size_t sizes_n = sizeof(sizes) / sizeof(NamedValue);
|
||||
|
||||
size_t i = 0;
|
||||
|
@ -240,6 +240,19 @@ StringBuilder &operator<<(StringBuilder &stream, const Array<ArrayT> &array) {
|
|||
return stream << Slice("}");
|
||||
}
|
||||
|
||||
inline StringBuilder &operator<<(StringBuilder &stream, const Array<vector<bool>> &array) {
|
||||
bool first = true;
|
||||
stream << Slice("{");
|
||||
for (bool x : array.ref) {
|
||||
if (!first) {
|
||||
stream << Slice(", ");
|
||||
}
|
||||
stream << x;
|
||||
first = false;
|
||||
}
|
||||
return stream << Slice("}");
|
||||
}
|
||||
|
||||
template <class ArrayT>
|
||||
Array<ArrayT> as_array(const ArrayT &array) {
|
||||
return Array<ArrayT>{array};
|
||||
|
|
|
@ -58,6 +58,9 @@ TD_THREAD_LOCAL const char *Logger::tag2_ = nullptr;
|
|||
Logger::Logger(LogInterface &log, const LogOptions &options, int log_level, Slice file_name, int line_num,
|
||||
Slice comment)
|
||||
: Logger(log, options, log_level) {
|
||||
if (log_level == VERBOSITY_NAME(PLAIN) && &options == &log_options) {
|
||||
return;
|
||||
}
|
||||
if (!options_.add_info) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -39,11 +39,11 @@ char *str_dup(Slice str) {
|
|||
|
||||
string implode(const vector<string> &v, char delimiter) {
|
||||
string result;
|
||||
for (auto &str : v) {
|
||||
if (!result.empty()) {
|
||||
for (size_t i = 0; i < v.size(); i++) {
|
||||
if (i != 0) {
|
||||
result += delimiter;
|
||||
}
|
||||
result += str;
|
||||
result += v[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -110,21 +110,32 @@ Result<string> hex_decode(Slice hex) {
|
|||
return std::move(result);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static bool is_url_char(char c) {
|
||||
return is_alnum(c) || c == '-' || c == '.' || c == '_' || c == '~';
|
||||
}
|
||||
|
||||
string url_encode(Slice str) {
|
||||
size_t length = 3 * str.size();
|
||||
for (auto c : str) {
|
||||
string url_encode(Slice data) {
|
||||
size_t length = 3 * data.size();
|
||||
for (auto c : data) {
|
||||
length -= 2 * is_url_char(c);
|
||||
}
|
||||
if (length == str.size()) {
|
||||
return str.str();
|
||||
if (length == data.size()) {
|
||||
return data.str();
|
||||
}
|
||||
string result;
|
||||
result.reserve(length);
|
||||
for (auto c : str) {
|
||||
for (auto c : data) {
|
||||
if (is_url_char(c)) {
|
||||
result += c;
|
||||
} else {
|
||||
|
@ -138,6 +149,17 @@ string url_encode(Slice str) {
|
|||
return result;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
template <class F>
|
||||
|
@ -181,17 +203,6 @@ bool is_zero_or_one(unsigned char c) {
|
|||
|
||||
} // namespace
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
std::string zero_encode(Slice data) {
|
||||
return x_encode(data, is_zero);
|
||||
}
|
||||
|
|
|
@ -67,10 +67,10 @@ string implode(const vector<string> &v, char delimiter = ' ');
|
|||
|
||||
namespace detail {
|
||||
|
||||
template <typename T>
|
||||
template <typename V>
|
||||
struct transform_helper {
|
||||
template <class Func>
|
||||
auto transform(const T &v, const Func &f) {
|
||||
auto transform(const V &v, const Func &f) {
|
||||
vector<decltype(f(*v.begin()))> result;
|
||||
result.reserve(v.size());
|
||||
for (auto &x : v) {
|
||||
|
@ -80,7 +80,7 @@ struct transform_helper {
|
|||
}
|
||||
|
||||
template <class Func>
|
||||
auto transform(T &&v, const Func &f) {
|
||||
auto transform(V &&v, const Func &f) {
|
||||
vector<decltype(f(std::move(*v.begin())))> result;
|
||||
result.reserve(v.size());
|
||||
for (auto &x : v) {
|
||||
|
@ -92,9 +92,58 @@ struct transform_helper {
|
|||
|
||||
} // 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 V, class Func>
|
||||
auto transform(V &&v, const Func &f) {
|
||||
return detail::transform_helper<std::decay_t<V>>().transform(std::forward<V>(v), f);
|
||||
}
|
||||
|
||||
template <class V, class Func>
|
||||
void remove_if(V &v, const Func &f) {
|
||||
size_t i = 0;
|
||||
while (i != v.size() && !f(v[i])) {
|
||||
i++;
|
||||
}
|
||||
if (i == v.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t j = i;
|
||||
while (++i != v.size()) {
|
||||
if (!f(v[i])) {
|
||||
v[j++] = std::move(v[i]);
|
||||
}
|
||||
}
|
||||
v.erase(v.begin() + j, v.end());
|
||||
}
|
||||
|
||||
template <class V, class T>
|
||||
bool remove(V &v, const T &value) {
|
||||
size_t i = 0;
|
||||
while (i != v.size() && v[i] != value) {
|
||||
i++;
|
||||
}
|
||||
if (i == v.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t j = i;
|
||||
while (++i != v.size()) {
|
||||
if (v[i] != value) {
|
||||
v[j++] = std::move(v[i]);
|
||||
}
|
||||
}
|
||||
v.erase(v.begin() + j, v.end());
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class V, class T>
|
||||
bool contains(const V &v, const T &value) {
|
||||
for (auto &x : v) {
|
||||
if (x == value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
|
@ -132,6 +181,9 @@ void combine(vector<T> &destination, vector<T> &&source) {
|
|||
if (destination.size() < source.size()) {
|
||||
destination.swap(source);
|
||||
}
|
||||
if (source.empty()) {
|
||||
return;
|
||||
}
|
||||
destination.reserve(destination.size() + source.size());
|
||||
for (auto &elem : source) {
|
||||
destination.push_back(std::move(elem));
|
||||
|
@ -332,7 +384,9 @@ T clamp(T value, T min_value, T max_value) {
|
|||
|
||||
Result<string> hex_decode(Slice hex);
|
||||
|
||||
string url_encode(Slice str);
|
||||
string hex_encode(Slice data);
|
||||
|
||||
string url_encode(Slice data);
|
||||
|
||||
// run-time checked narrowing cast (type conversion):
|
||||
|
||||
|
|
|
@ -19,17 +19,55 @@
|
|||
#include "td/utils/port/Clocks.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
|
||||
namespace td {
|
||||
|
||||
ClocksDefault::Duration ClocksDefault::monotonic() {
|
||||
double Clocks::monotonic() {
|
||||
// TODO write system specific functions, because std::chrono::steady_clock is steady only under Windows
|
||||
auto duration = std::chrono::steady_clock::now().time_since_epoch();
|
||||
return static_cast<double>(std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count()) * 1e-9;
|
||||
}
|
||||
|
||||
ClocksDefault::Duration ClocksDefault::system() {
|
||||
double Clocks::system() {
|
||||
auto duration = std::chrono::system_clock::now().time_since_epoch();
|
||||
return static_cast<double>(std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count()) * 1e-9;
|
||||
}
|
||||
|
||||
int Clocks::tz_offset() {
|
||||
// not thread-safe on POSIX, so calculate the offset only once
|
||||
static int offset = [] {
|
||||
auto now = std::time(nullptr);
|
||||
|
||||
auto time_ptr = std::localtime(&now);
|
||||
if (time_ptr == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
auto local_time = *time_ptr;
|
||||
|
||||
time_ptr = std::gmtime(&now);
|
||||
if (time_ptr == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
auto utc_time = *time_ptr;
|
||||
|
||||
int minute_offset = local_time.tm_min - utc_time.tm_min;
|
||||
int hour_offset = local_time.tm_hour - utc_time.tm_hour;
|
||||
int day_offset = local_time.tm_mday - utc_time.tm_mday;
|
||||
if (day_offset >= 20) {
|
||||
day_offset = -1;
|
||||
} else if (day_offset <= -20) {
|
||||
day_offset = 1;
|
||||
}
|
||||
int sec_offset = day_offset * 86400 + hour_offset * 3600 + minute_offset * 60;
|
||||
if (sec_offset >= 15 * 3600 || sec_offset <= -15 * 3600) {
|
||||
return 0;
|
||||
}
|
||||
return sec_offset / 900 * 900; // round to 900 just in case
|
||||
}();
|
||||
return offset;
|
||||
}
|
||||
|
||||
static int init_tz_offset = Clocks::tz_offset();
|
||||
|
||||
} // namespace td
|
||||
|
|
|
@ -20,21 +20,12 @@
|
|||
|
||||
namespace td {
|
||||
|
||||
class ClocksBase {
|
||||
public:
|
||||
using Duration = double;
|
||||
struct Clocks {
|
||||
static double monotonic();
|
||||
|
||||
static double system();
|
||||
|
||||
static int tz_offset();
|
||||
};
|
||||
|
||||
// TODO: (maybe) write system specific functions.
|
||||
class ClocksDefault {
|
||||
public:
|
||||
using Duration = ClocksBase::Duration;
|
||||
|
||||
static Duration monotonic();
|
||||
|
||||
static Duration system();
|
||||
};
|
||||
|
||||
using Clocks = ClocksDefault;
|
||||
|
||||
} // namespace td
|
||||
|
|
|
@ -18,20 +18,27 @@
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
#pragma managed(push, off)
|
||||
#include "td/utils/port/config.h"
|
||||
#pragma managed(pop)
|
||||
|
||||
#include "td/utils/common.h"
|
||||
#undef small
|
||||
|
||||
#if TD_WINRT
|
||||
|
||||
#pragma managed(push, off)
|
||||
#include "td/utils/port/wstring_convert.h"
|
||||
#pragma managed(pop)
|
||||
|
||||
#include "collection.h"
|
||||
|
||||
#pragma managed(push, off)
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#pragma managed(pop)
|
||||
|
||||
#undef small
|
||||
|
||||
#define REF_NEW ref new
|
||||
#define CLRCALL
|
||||
|
@ -102,6 +109,8 @@ inline String^ string_from_unmanaged(const std::string &from) {
|
|||
|
||||
#elif TD_CLI
|
||||
|
||||
#undef small
|
||||
|
||||
#define REF_NEW gcnew
|
||||
#define CLRCALL __clrcall
|
||||
#define DEPRECATED_ATTRIBUTE(message) System::ObsoleteAttribute(message)
|
||||
|
|
|
@ -23,9 +23,11 @@
|
|||
#include "td/utils/port/wstring_convert.h"
|
||||
#endif
|
||||
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/misc.h"
|
||||
#include "td/utils/port/detail/PollableFd.h"
|
||||
#include "td/utils/port/detail/skip_eintr.h"
|
||||
#include "td/utils/port/PollFlags.h"
|
||||
#include "td/utils/port/sleep.h"
|
||||
#include "td/utils/ScopeGuard.h"
|
||||
|
@ -34,8 +36,11 @@
|
|||
#include <cstring>
|
||||
#include <mutex>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
|
||||
#if TD_PORT_POSIX
|
||||
#include <cerrno>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/types.h>
|
||||
|
@ -43,6 +48,10 @@
|
|||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if TD_PORT_WINDOWS && defined(WIN32_LEAN_AND_MEAN)
|
||||
#include <winioctl.h>
|
||||
#endif
|
||||
|
||||
namespace td {
|
||||
|
||||
namespace {
|
||||
|
@ -218,6 +227,12 @@ Result<FileFd> FileFd::open(CSlice filepath, int32 flags, int32 mode) {
|
|||
if (handle == INVALID_HANDLE_VALUE) {
|
||||
return OS_ERROR(PSLICE() << "File \"" << filepath << "\" can't be " << PrintFlags{flags});
|
||||
}
|
||||
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
|
||||
if (flags & Write) {
|
||||
DWORD bytes_returned = 0;
|
||||
DeviceIoControl(handle, FSCTL_SET_SPARSE, nullptr, 0, nullptr, 0, &bytes_returned, nullptr);
|
||||
}
|
||||
#endif
|
||||
auto native_fd = NativeFd(handle);
|
||||
if (flags & Append) {
|
||||
LARGE_INTEGER offset;
|
||||
|
@ -492,18 +507,58 @@ NativeFd FileFd::move_as_native_fd() {
|
|||
return res;
|
||||
}
|
||||
|
||||
Result<int64> FileFd::get_size() const {
|
||||
TRY_RESULT(s, stat());
|
||||
return s.size_;
|
||||
}
|
||||
|
||||
#if TD_PORT_WINDOWS
|
||||
static uint64 filetime_to_unix_time_nsec(LONGLONG filetime) {
|
||||
namespace {
|
||||
|
||||
uint64 filetime_to_unix_time_nsec(LONGLONG filetime) {
|
||||
const auto FILETIME_UNIX_TIME_DIFF = 116444736000000000ll;
|
||||
return static_cast<uint64>((filetime - FILETIME_UNIX_TIME_DIFF) * 100);
|
||||
}
|
||||
|
||||
struct FileSize {
|
||||
int64 size_;
|
||||
int64 real_size_;
|
||||
};
|
||||
|
||||
Result<FileSize> get_file_size(const FileFd &file_fd) {
|
||||
FILE_STANDARD_INFO standard_info;
|
||||
if (!GetFileInformationByHandleEx(file_fd.get_native_fd().fd(), FileStandardInfo, &standard_info,
|
||||
sizeof(standard_info))) {
|
||||
return OS_ERROR("Get FileStandardInfo failed");
|
||||
}
|
||||
FileSize res;
|
||||
res.size_ = standard_info.EndOfFile.QuadPart;
|
||||
res.real_size_ = standard_info.AllocationSize.QuadPart;
|
||||
|
||||
if (res.size_ > 0 && res.real_size_ <= 0) { // just in case
|
||||
LOG(ERROR) << "Fix real file size from " << res.real_size_ << " to " << res.size_;
|
||||
res.real_size_ = res.size_;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
#endif
|
||||
|
||||
Result<int64> FileFd::get_size() const {
|
||||
#if TD_PORT_POSIX
|
||||
TRY_RESULT(s, stat());
|
||||
#elif TD_PORT_WINDOWS
|
||||
TRY_RESULT(s, get_file_size(*this));
|
||||
#endif
|
||||
return s.size_;
|
||||
}
|
||||
|
||||
Result<int64> FileFd::get_real_size() const {
|
||||
#if TD_PORT_POSIX
|
||||
TRY_RESULT(s, stat());
|
||||
#elif TD_PORT_WINDOWS
|
||||
TRY_RESULT(s, get_file_size(*this));
|
||||
#endif
|
||||
return s.real_size_;
|
||||
}
|
||||
|
||||
Result<Stat> FileFd::stat() const {
|
||||
CHECK(!empty());
|
||||
#if TD_PORT_POSIX
|
||||
|
@ -521,12 +576,9 @@ Result<Stat> FileFd::stat() const {
|
|||
res.is_dir_ = (basic_info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
||||
res.is_reg_ = !res.is_dir_; // TODO this is still wrong
|
||||
|
||||
FILE_STANDARD_INFO standard_info;
|
||||
status = GetFileInformationByHandleEx(get_native_fd().fd(), FileStandardInfo, &standard_info, sizeof(standard_info));
|
||||
if (!status) {
|
||||
return OS_ERROR("Get FileStandardInfo failed");
|
||||
}
|
||||
res.size_ = standard_info.EndOfFile.QuadPart;
|
||||
TRY_RESULT(file_size, get_file_size(*this));
|
||||
res.size_ = file_size.size_;
|
||||
res.real_size_ = file_size.real_size_;
|
||||
|
||||
return res;
|
||||
#endif
|
||||
|
|
|
@ -62,11 +62,15 @@ class FileFd {
|
|||
|
||||
PollableFdInfo &get_poll_info();
|
||||
const PollableFdInfo &get_poll_info() const;
|
||||
|
||||
void close();
|
||||
|
||||
bool empty() const;
|
||||
|
||||
Result<int64> get_size() const;
|
||||
|
||||
Result<int64> get_real_size() const;
|
||||
|
||||
Result<Stat> stat() const;
|
||||
|
||||
Status sync() TD_WARN_UNUSED_RESULT;
|
||||
|
|
|
@ -279,11 +279,11 @@ uint32 IPAddress::get_ipv4() const {
|
|||
return htonl(ipv4_addr_.sin_addr.s_addr);
|
||||
}
|
||||
|
||||
Slice IPAddress::get_ipv6() const {
|
||||
string IPAddress::get_ipv6() const {
|
||||
static_assert(sizeof(ipv6_addr_.sin6_addr) == 16, "ipv6 size == 16");
|
||||
CHECK(is_valid());
|
||||
CHECK(!is_ipv4());
|
||||
return Slice(ipv6_addr_.sin6_addr.s6_addr, 16);
|
||||
return Slice(ipv6_addr_.sin6_addr.s6_addr, 16).str();
|
||||
}
|
||||
|
||||
IPAddress IPAddress::get_any_addr() const {
|
||||
|
@ -320,7 +320,12 @@ void IPAddress::init_ipv6_any() {
|
|||
Status IPAddress::init_ipv6_port(CSlice ipv6, int port) {
|
||||
is_valid_ = false;
|
||||
if (port <= 0 || port >= (1 << 16)) {
|
||||
return Status::Error(PSLICE() << "Invalid [port=" << port << "]");
|
||||
return Status::Error(PSLICE() << "Invalid [IPv6 address port=" << port << "]");
|
||||
}
|
||||
string ipv6_plain;
|
||||
if (ipv6.size() > 2 && ipv6[0] == '[' && ipv6.back() == ']') {
|
||||
ipv6_plain.assign(ipv6.begin() + 1, ipv6.size() - 2);
|
||||
ipv6 = ipv6_plain;
|
||||
}
|
||||
std::memset(&ipv6_addr_, 0, sizeof(ipv6_addr_));
|
||||
ipv6_addr_.sin6_family = AF_INET6;
|
||||
|
@ -342,7 +347,7 @@ Status IPAddress::init_ipv6_as_ipv4_port(CSlice ipv4, int port) {
|
|||
Status IPAddress::init_ipv4_port(CSlice ipv4, int port) {
|
||||
is_valid_ = false;
|
||||
if (port <= 0 || port >= (1 << 16)) {
|
||||
return Status::Error(PSLICE() << "Invalid [port=" << port << "]");
|
||||
return Status::Error(PSLICE() << "Invalid [IPv4 address port=" << port << "]");
|
||||
}
|
||||
std::memset(&ipv4_addr_, 0, sizeof(ipv4_addr_));
|
||||
ipv4_addr_.sin_family = AF_INET;
|
||||
|
@ -357,6 +362,18 @@ Status IPAddress::init_ipv4_port(CSlice ipv4, int port) {
|
|||
return Status::OK();
|
||||
}
|
||||
|
||||
Result<IPAddress> IPAddress::get_ip_address(CSlice host) {
|
||||
auto r_address = get_ipv4_address(host);
|
||||
if (r_address.is_ok()) {
|
||||
return r_address.move_as_ok();
|
||||
}
|
||||
r_address = get_ipv6_address(host);
|
||||
if (r_address.is_ok()) {
|
||||
return r_address.move_as_ok();
|
||||
}
|
||||
return Status::Error("Not a valid IP address");
|
||||
}
|
||||
|
||||
Result<IPAddress> IPAddress::get_ipv4_address(CSlice host) {
|
||||
// sometimes inet_addr allows much more valid IPv4 hosts than inet_pton,
|
||||
// like 0x12.0x34.0x56.0x78, or 0x12345678, or 0x7f.001
|
||||
|
@ -384,6 +401,10 @@ Result<IPAddress> IPAddress::get_ipv6_address(CSlice host) {
|
|||
}
|
||||
|
||||
Status IPAddress::init_host_port(CSlice host, int port, bool prefer_ipv6) {
|
||||
if (host.size() > 2 && host[0] == '[' && host.back() == ']') {
|
||||
return init_ipv6_port(host, port == 0 ? 1 : port);
|
||||
}
|
||||
|
||||
return init_host_port(host, PSLICE() << port, prefer_ipv6);
|
||||
}
|
||||
|
||||
|
@ -398,7 +419,12 @@ Status IPAddress::init_host_port(CSlice host, CSlice port, bool prefer_ipv6) {
|
|||
}
|
||||
#endif
|
||||
TRY_RESULT(ascii_host, idn_to_ascii(host));
|
||||
host = ascii_host;
|
||||
host = ascii_host; // assign string to CSlice
|
||||
|
||||
if (host[0] == '[' && host.back() == ']') {
|
||||
auto port_int = to_integer<int>(port);
|
||||
return init_ipv6_port(host, port_int == 0 ? 1 : port_int);
|
||||
}
|
||||
|
||||
// some getaddrinfo implementations use inet_pton instead of inet_aton and support only decimal-dotted IPv4 form,
|
||||
// and so doesn't recognize 0x12.0x34.0x56.0x78, or 0x12345678, or 0x7f.001 as valid IPv4 addresses
|
||||
|
@ -413,7 +439,7 @@ Status IPAddress::init_host_port(CSlice host, CSlice port, bool prefer_ipv6) {
|
|||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
LOG(DEBUG + 10) << "Try to init IP address of " << host << " with port " << port;
|
||||
LOG(DEBUG + 10) << "Trying to init IP address of " << host << " with port " << port;
|
||||
auto err = getaddrinfo(host.c_str(), port.c_str(), &hints, &info);
|
||||
if (err != 0) {
|
||||
#if TD_WINDOWS
|
||||
|
@ -506,19 +532,19 @@ Status IPAddress::init_peer_address(const SocketFd &socket_fd) {
|
|||
return Status::OK();
|
||||
}
|
||||
|
||||
CSlice IPAddress::ipv4_to_str(uint32 ipv4) {
|
||||
string IPAddress::ipv4_to_str(uint32 ipv4) {
|
||||
ipv4 = ntohl(ipv4);
|
||||
return ::td::get_ip_str(AF_INET, &ipv4);
|
||||
return ::td::get_ip_str(AF_INET, &ipv4).str();
|
||||
}
|
||||
|
||||
CSlice IPAddress::ipv6_to_str(Slice ipv6) {
|
||||
string IPAddress::ipv6_to_str(Slice ipv6) {
|
||||
CHECK(ipv6.size() == 16);
|
||||
return ::td::get_ip_str(AF_INET6, ipv6.ubegin());
|
||||
return ::td::get_ip_str(AF_INET6, ipv6.ubegin()).str();
|
||||
}
|
||||
|
||||
Slice IPAddress::get_ip_str() const {
|
||||
CSlice IPAddress::get_ip_str() const {
|
||||
if (!is_valid()) {
|
||||
return Slice("0.0.0.0");
|
||||
return CSlice("0.0.0.0");
|
||||
}
|
||||
|
||||
switch (get_address_family()) {
|
||||
|
@ -528,7 +554,23 @@ Slice IPAddress::get_ip_str() const {
|
|||
return ::td::get_ip_str(AF_INET, &ipv4_addr_.sin_addr);
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return Slice();
|
||||
return CSlice();
|
||||
}
|
||||
}
|
||||
|
||||
string IPAddress::get_ip_host() const {
|
||||
if (!is_valid()) {
|
||||
return "0.0.0.0";
|
||||
}
|
||||
|
||||
switch (get_address_family()) {
|
||||
case AF_INET6:
|
||||
return PSTRING() << '[' << ::td::get_ip_str(AF_INET6, &ipv6_addr_.sin6_addr) << ']';
|
||||
case AF_INET:
|
||||
return ::td::get_ip_str(AF_INET, &ipv4_addr_.sin_addr).str();
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return string();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -611,12 +653,7 @@ StringBuilder &operator<<(StringBuilder &builder, const IPAddress &address) {
|
|||
if (!address.is_valid()) {
|
||||
return builder << "[invalid]";
|
||||
}
|
||||
if (address.get_address_family() == AF_INET) {
|
||||
return builder << "[" << address.get_ip_str() << ":" << address.get_port() << "]";
|
||||
} else {
|
||||
CHECK(address.get_address_family() == AF_INET6);
|
||||
return builder << "[[" << address.get_ip_str() << "]:" << address.get_port() << "]";
|
||||
}
|
||||
return builder << "[" << address.get_ip_host() << ":" << address.get_port() << "]";
|
||||
}
|
||||
|
||||
} // namespace td
|
||||
|
|
|
@ -51,11 +51,20 @@ class IPAddress {
|
|||
void set_port(int port);
|
||||
|
||||
uint32 get_ipv4() const;
|
||||
Slice get_ipv6() const;
|
||||
Slice get_ip_str() const;
|
||||
string get_ipv6() const;
|
||||
|
||||
// returns result in a static thread-local buffer, which may be overwritten by any subsequent method call
|
||||
CSlice get_ip_str() const;
|
||||
|
||||
// returns IP address as a host, i.e. IPv4 or [IPv6]
|
||||
string get_ip_host() const;
|
||||
|
||||
static string ipv4_to_str(uint32 ipv4);
|
||||
static string ipv6_to_str(Slice ipv6);
|
||||
|
||||
IPAddress get_any_addr() const;
|
||||
|
||||
static Result<IPAddress> get_ip_address(CSlice host); // host must be any IPv4 or IPv6
|
||||
static Result<IPAddress> get_ipv4_address(CSlice host);
|
||||
static Result<IPAddress> get_ipv6_address(CSlice host);
|
||||
|
||||
|
@ -75,8 +84,6 @@ class IPAddress {
|
|||
const sockaddr *get_sockaddr() const;
|
||||
size_t get_sockaddr_len() const;
|
||||
int get_address_family() const;
|
||||
static CSlice ipv4_to_str(uint32 ipv4);
|
||||
static CSlice ipv6_to_str(Slice ipv6);
|
||||
Status init_sockaddr(sockaddr *addr);
|
||||
Status init_sockaddr(sockaddr *addr, socklen_t len) TD_WARN_UNUSED_RESULT;
|
||||
|
||||
|
|
|
@ -22,10 +22,12 @@
|
|||
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/port/detail/skip_eintr.h"
|
||||
#include "td/utils/port/IPAddress.h"
|
||||
#include "td/utils/port/PollFlags.h"
|
||||
|
||||
#if TD_PORT_POSIX
|
||||
#include <cerrno>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <fcntl.h>
|
||||
|
@ -34,7 +36,6 @@
|
|||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#endif
|
||||
|
||||
#if TD_PORT_WINDOWS
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "td/utils/format.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/misc.h"
|
||||
#include "td/utils/port/detail/skip_eintr.h"
|
||||
#include "td/utils/port/PollFlags.h"
|
||||
|
||||
#if TD_PORT_WINDOWS
|
||||
|
@ -32,6 +33,8 @@
|
|||
#endif
|
||||
|
||||
#if TD_PORT_POSIX
|
||||
#include <cerrno>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <fcntl.h>
|
||||
#include <netinet/in.h>
|
||||
|
@ -227,11 +230,11 @@ class SocketFdImpl : private Iocp::Callback {
|
|||
return;
|
||||
}
|
||||
std::memset(&write_overlapped_, 0, sizeof(write_overlapped_));
|
||||
constexpr size_t buf_size = 20;
|
||||
WSABUF buf[buf_size];
|
||||
constexpr size_t BUF_SIZE = 20;
|
||||
WSABUF buf[BUF_SIZE];
|
||||
auto it = output_reader_.clone();
|
||||
size_t buf_i;
|
||||
for (buf_i = 0; buf_i < buf_size; buf_i++) {
|
||||
for (buf_i = 0; buf_i < BUF_SIZE; buf_i++) {
|
||||
auto src = it.prepare_read();
|
||||
if (src.empty()) {
|
||||
break;
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
*/
|
||||
#include "td/utils/port/Stat.h"
|
||||
|
||||
#include "td/utils/port/detail/PollableFd.h"
|
||||
#include "td/utils/port/FileFd.h"
|
||||
|
||||
#if TD_PORT_POSIX
|
||||
|
@ -27,6 +26,7 @@
|
|||
#include "td/utils/logging.h"
|
||||
#include "td/utils/misc.h"
|
||||
#include "td/utils/port/Clocks.h"
|
||||
#include "td/utils/port/detail/skip_eintr.h"
|
||||
#include "td/utils/ScopeGuard.h"
|
||||
|
||||
#include <utility>
|
||||
|
@ -50,8 +50,20 @@
|
|||
#include <sys/syscall.h>
|
||||
#endif
|
||||
|
||||
#elif TD_PORT_WINDOWS
|
||||
|
||||
#include "td/utils/port/thread.h"
|
||||
|
||||
#ifndef PSAPI_VERSION
|
||||
#define PSAPI_VERSION 1
|
||||
#endif
|
||||
#include <psapi.h>
|
||||
|
||||
#endif
|
||||
|
||||
namespace td {
|
||||
|
||||
#if TD_PORT_POSIX
|
||||
namespace detail {
|
||||
|
||||
template <class...>
|
||||
|
@ -118,6 +130,7 @@ Stat from_native_stat(const struct ::stat &buf) {
|
|||
res.atime_nsec_ = static_cast<uint64>(buf.st_atime) * 1000000000 + time_nsec.first;
|
||||
res.mtime_nsec_ = static_cast<uint64>(buf.st_mtime) * 1000000000 + time_nsec.second / 1000 * 1000;
|
||||
res.size_ = buf.st_size;
|
||||
res.real_size_ = buf.st_blocks * 512;
|
||||
res.is_dir_ = (buf.st_mode & S_IFMT) == S_IFDIR;
|
||||
res.is_reg_ = (buf.st_mode & S_IFMT) == S_IFREG;
|
||||
return res;
|
||||
|
@ -187,14 +200,20 @@ Status update_atime(CSlice path) {
|
|||
};
|
||||
return detail::update_atime(file.get_native_fd().fd());
|
||||
}
|
||||
#endif
|
||||
|
||||
Result<Stat> stat(CSlice path) {
|
||||
#if TD_PORT_POSIX
|
||||
struct ::stat buf;
|
||||
int err = detail::skip_eintr([&] { return ::stat(path.c_str(), &buf); });
|
||||
if (err < 0) {
|
||||
return OS_ERROR(PSLICE() << "Stat for file \"" << path << "\" failed");
|
||||
}
|
||||
return detail::from_native_stat(buf);
|
||||
#elif TD_PORT_WINDOWS
|
||||
TRY_RESULT(fd, FileFd::open(path, FileFd::Flags::Read | FileFd::PrivateFlags::WinStat));
|
||||
return fd.stat();
|
||||
#endif
|
||||
}
|
||||
|
||||
Result<MemStat> mem_stat() {
|
||||
|
@ -204,7 +223,7 @@ Result<MemStat> mem_stat() {
|
|||
|
||||
if (KERN_SUCCESS !=
|
||||
task_info(mach_task_self(), TASK_BASIC_INFO, reinterpret_cast<task_info_t>(&t_info), &t_info_count)) {
|
||||
return Status::Error("task_info failed");
|
||||
return Status::Error("Call to task_info failed");
|
||||
}
|
||||
MemStat res;
|
||||
res.resident_size_ = t_info.resident_size;
|
||||
|
@ -262,7 +281,7 @@ Result<MemStat> mem_stat() {
|
|||
LOG(ERROR) << "Failed to parse memory stats " << tag("name", name) << tag("value", value);
|
||||
*x = static_cast<uint64>(-1);
|
||||
} else {
|
||||
*x = r_mem.ok() * 1024; // memory is in kB
|
||||
*x = r_mem.ok() * 1024; // memory is in KB
|
||||
}
|
||||
}
|
||||
if (*s == 0) {
|
||||
|
@ -271,6 +290,19 @@ Result<MemStat> mem_stat() {
|
|||
s++;
|
||||
}
|
||||
|
||||
return res;
|
||||
#elif TD_WINDOWS
|
||||
PROCESS_MEMORY_COUNTERS_EX counters;
|
||||
if (!GetProcessMemoryInfo(GetCurrentProcess(), reinterpret_cast<PROCESS_MEMORY_COUNTERS *>(&counters),
|
||||
sizeof(counters))) {
|
||||
return Status::Error("Call to GetProcessMemoryInfo failed");
|
||||
}
|
||||
|
||||
MemStat res;
|
||||
res.resident_size_ = counters.WorkingSetSize;
|
||||
res.resident_size_peak_ = counters.PeakWorkingSetSize;
|
||||
res.virtual_size_ = counters.PrivateUsage;
|
||||
res.virtual_size_peak_ = counters.PeakPagefileUsage;
|
||||
return res;
|
||||
#else
|
||||
return Status::Error("Not supported");
|
||||
|
@ -287,7 +319,9 @@ Status cpu_stat_self(CpuStat &stat) {
|
|||
constexpr int TMEM_SIZE = 10000;
|
||||
char mem[TMEM_SIZE];
|
||||
TRY_RESULT(size, fd.read(MutableSlice(mem, TMEM_SIZE - 1)));
|
||||
CHECK(size < TMEM_SIZE - 1);
|
||||
if (size >= TMEM_SIZE - 1) {
|
||||
return Status::Error("Failed for read /proc/self/stat");
|
||||
}
|
||||
mem[size] = 0;
|
||||
|
||||
char *s = mem;
|
||||
|
@ -296,10 +330,10 @@ Status cpu_stat_self(CpuStat &stat) {
|
|||
|
||||
while (pass_cnt < 15) {
|
||||
if (pass_cnt == 13) {
|
||||
stat.process_user_ticks = to_integer<uint64>(Slice(s, t));
|
||||
stat.process_user_ticks_ = to_integer<uint64>(Slice(s, t));
|
||||
}
|
||||
if (pass_cnt == 14) {
|
||||
stat.process_system_ticks = to_integer<uint64>(Slice(s, t));
|
||||
stat.process_system_ticks_ = to_integer<uint64>(Slice(s, t));
|
||||
}
|
||||
while (*s && *s != ' ') {
|
||||
s++;
|
||||
|
@ -308,11 +342,12 @@ Status cpu_stat_self(CpuStat &stat) {
|
|||
s++;
|
||||
pass_cnt++;
|
||||
} else {
|
||||
return Status::Error("unexpected end of proc file");
|
||||
return Status::Error("Unexpected end of proc file");
|
||||
}
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status cpu_stat_total(CpuStat &stat) {
|
||||
TRY_RESULT(fd, FileFd::open("/proc/stat", FileFd::Read));
|
||||
SCOPE_EXIT {
|
||||
|
@ -322,14 +357,17 @@ Status cpu_stat_total(CpuStat &stat) {
|
|||
constexpr int TMEM_SIZE = 10000;
|
||||
char mem[TMEM_SIZE];
|
||||
TRY_RESULT(size, fd.read(MutableSlice(mem, TMEM_SIZE - 1)));
|
||||
CHECK(size < TMEM_SIZE - 1);
|
||||
if (size >= TMEM_SIZE - 1) {
|
||||
return Status::Error("Failed for read /proc/stat");
|
||||
}
|
||||
mem[size] = 0;
|
||||
|
||||
uint64 sum = 0, cur = 0;
|
||||
uint64 sum = 0;
|
||||
uint64 cur = 0;
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
int c = mem[i];
|
||||
char c = mem[i];
|
||||
if (c >= '0' && c <= '9') {
|
||||
cur = cur * 10 + (uint64)c - '0';
|
||||
cur = cur * 10 + static_cast<uint64>(c) - '0';
|
||||
} else {
|
||||
sum += cur;
|
||||
cur = 0;
|
||||
|
@ -339,7 +377,7 @@ Status cpu_stat_total(CpuStat &stat) {
|
|||
}
|
||||
}
|
||||
|
||||
stat.total_ticks = sum;
|
||||
stat.total_ticks_ = sum;
|
||||
return Status::OK();
|
||||
}
|
||||
#endif
|
||||
|
@ -349,25 +387,28 @@ Result<CpuStat> cpu_stat() {
|
|||
CpuStat stat;
|
||||
TRY_STATUS(cpu_stat_self(stat));
|
||||
TRY_STATUS(cpu_stat_total(stat));
|
||||
return stat;
|
||||
#elif TD_WINDOWS
|
||||
CpuStat stat;
|
||||
stat.total_ticks_ = static_cast<uint64>(GetTickCount64()) * 10000;
|
||||
auto hardware_concurrency = thread::hardware_concurrency();
|
||||
if (hardware_concurrency != 0) {
|
||||
stat.total_ticks_ *= hardware_concurrency;
|
||||
}
|
||||
|
||||
FILETIME ignored_time;
|
||||
FILETIME kernel_time;
|
||||
FILETIME user_time;
|
||||
if (!GetProcessTimes(GetCurrentProcess(), &ignored_time, &ignored_time, &kernel_time, &user_time)) {
|
||||
return Status::Error("Failed to call GetProcessTimes");
|
||||
}
|
||||
stat.process_system_ticks_ = kernel_time.dwLowDateTime + (static_cast<uint64>(kernel_time.dwHighDateTime) << 32);
|
||||
stat.process_user_ticks_ = user_time.dwLowDateTime + (static_cast<uint64>(user_time.dwHighDateTime) << 32);
|
||||
|
||||
return stat;
|
||||
#else
|
||||
return Status::Error("Not supported");
|
||||
#endif
|
||||
}
|
||||
} // namespace td
|
||||
#endif
|
||||
|
||||
#if TD_PORT_WINDOWS
|
||||
namespace td {
|
||||
|
||||
Result<Stat> stat(CSlice path) {
|
||||
TRY_RESULT(fd, FileFd::open(path, FileFd::Flags::Read | FileFd::PrivateFlags::WinStat));
|
||||
return fd.stat();
|
||||
}
|
||||
|
||||
Result<CpuStat> cpu_stat() {
|
||||
return Status::Error("Not supported");
|
||||
}
|
||||
|
||||
} // namespace td
|
||||
#endif
|
||||
|
|
|
@ -30,6 +30,7 @@ struct Stat {
|
|||
bool is_dir_;
|
||||
bool is_reg_;
|
||||
int64 size_;
|
||||
int64 real_size_;
|
||||
uint64 atime_nsec_;
|
||||
uint64 mtime_nsec_;
|
||||
};
|
||||
|
@ -37,20 +38,13 @@ struct Stat {
|
|||
Result<Stat> stat(CSlice path) TD_WARN_UNUSED_RESULT;
|
||||
|
||||
struct CpuStat {
|
||||
uint64 total_ticks{0};
|
||||
uint64 process_user_ticks{0};
|
||||
uint64 process_system_ticks{0};
|
||||
uint64 total_ticks_{0};
|
||||
uint64 process_user_ticks_{0};
|
||||
uint64 process_system_ticks_{0};
|
||||
};
|
||||
|
||||
Result<CpuStat> cpu_stat() TD_WARN_UNUSED_RESULT;
|
||||
|
||||
#if TD_PORT_POSIX
|
||||
|
||||
namespace detail {
|
||||
Result<Stat> fstat(int native_fd);
|
||||
} // namespace detail
|
||||
|
||||
Status update_atime(CSlice path) TD_WARN_UNUSED_RESULT;
|
||||
|
||||
struct MemStat {
|
||||
uint64 resident_size_ = 0;
|
||||
uint64 resident_size_peak_ = 0;
|
||||
|
@ -60,6 +54,14 @@ struct MemStat {
|
|||
|
||||
Result<MemStat> mem_stat() TD_WARN_UNUSED_RESULT;
|
||||
|
||||
#if TD_PORT_POSIX
|
||||
|
||||
namespace detail {
|
||||
Result<Stat> fstat(int native_fd);
|
||||
} // namespace detail
|
||||
|
||||
Status update_atime(CSlice path) TD_WARN_UNUSED_RESULT;
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace td
|
||||
|
|
|
@ -35,8 +35,7 @@ namespace td {
|
|||
template <int id>
|
||||
static FileFd &get_file_fd() {
|
||||
static FileFd result = FileFd::from_native_fd(NativeFd(id, true));
|
||||
static auto guard = td::ScopeExit() + [&] { result.move_as_native_fd().release(); };
|
||||
assert(!result.empty());
|
||||
static auto guard = ScopeExit() + [&] { result.move_as_native_fd().release(); };
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -56,7 +55,7 @@ static FileFd &get_file_fd() {
|
|||
static auto handle = GetStdHandle(id);
|
||||
LOG_IF(FATAL, handle == INVALID_HANDLE_VALUE) << "Failed to GetStdHandle " << id;
|
||||
static FileFd result = FileFd::from_native_fd(NativeFd(handle, true));
|
||||
static auto guard = td::ScopeExit() + [&] { result.move_as_native_fd().release(); };
|
||||
static auto guard = ScopeExit() + [&] { result.move_as_native_fd().release(); };
|
||||
#else
|
||||
static FileFd result;
|
||||
#endif
|
||||
|
@ -81,7 +80,7 @@ class BufferedStdinImpl : public Iocp::Callback {
|
|||
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
|
||||
BufferedStdinImpl() : info_(NativeFd(GetStdHandle(STD_INPUT_HANDLE), true)) {
|
||||
iocp_ref_ = Iocp::get()->get_ref();
|
||||
read_thread_ = td::thread([this] { this->read_loop(); });
|
||||
read_thread_ = thread([this] { this->read_loop(); });
|
||||
}
|
||||
#else
|
||||
BufferedStdinImpl() {
|
||||
|
@ -121,7 +120,7 @@ class BufferedStdinImpl : public Iocp::Callback {
|
|||
PollableFdInfo info_;
|
||||
ChainBufferWriter writer_;
|
||||
ChainBufferReader reader_ = writer_.extract_reader();
|
||||
td::thread read_thread_;
|
||||
thread read_thread_;
|
||||
std::atomic<bool> close_flag_{false};
|
||||
IocpRef iocp_ref_;
|
||||
std::atomic<int> refcnt_{1};
|
||||
|
@ -144,7 +143,7 @@ class BufferedStdinImpl : public Iocp::Callback {
|
|||
}
|
||||
}
|
||||
void on_iocp(Result<size_t> r_size, WSAOVERLAPPED *overlapped) override {
|
||||
info_.add_flags_from_poll(td::PollFlags::Read());
|
||||
info_.add_flags_from_poll(PollFlags::Read());
|
||||
dec_refcnt();
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "td/utils/format.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/misc.h"
|
||||
#include "td/utils/port/detail/skip_eintr.h"
|
||||
#include "td/utils/port/PollFlags.h"
|
||||
#include "td/utils/port/SocketFd.h"
|
||||
#include "td/utils/VectorQueue.h"
|
||||
|
@ -32,6 +33,8 @@
|
|||
#endif
|
||||
|
||||
#if TD_PORT_POSIX
|
||||
#include <cerrno>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <fcntl.h>
|
||||
#include <netinet/in.h>
|
||||
|
@ -73,7 +76,7 @@ class UdpSocketReceiveHelper {
|
|||
message.error = Status::OK();
|
||||
|
||||
if ((message_header.dwFlags & (MSG_TRUNC | MSG_CTRUNC)) != 0) {
|
||||
message.error = Status::Error(501, "message too long");
|
||||
message.error = Status::Error(501, "Message too long");
|
||||
message.data = BufferSlice();
|
||||
return;
|
||||
}
|
||||
|
@ -179,7 +182,8 @@ class UdpSocketFdImpl : private Iocp::Callback {
|
|||
UdpMessage to_receive_;
|
||||
WSAMSG receive_message_;
|
||||
UdpSocketReceiveHelper receive_helper_;
|
||||
enum : size_t { MAX_PACKET_SIZE = 2048, RESERVED_SIZE = MAX_PACKET_SIZE * 8 };
|
||||
static constexpr size_t MAX_PACKET_SIZE = 2048;
|
||||
static constexpr size_t RESERVED_SIZE = MAX_PACKET_SIZE * 8;
|
||||
BufferSlice receive_buffer_;
|
||||
|
||||
UdpMessage to_send_;
|
||||
|
@ -434,7 +438,7 @@ class UdpSocketReceiveHelper {
|
|||
}
|
||||
if (message_header.msg_flags & MSG_TRUNC) {
|
||||
if (message.error) {
|
||||
*message.error = Status::Error(501, "message too long");
|
||||
*message.error = Status::Error(501, "Message too long");
|
||||
}
|
||||
message.data.truncate(0);
|
||||
return;
|
||||
|
@ -822,11 +826,11 @@ static Result<uint32> maximize_buffer(int socket_fd, int optname, uint32 max) {
|
|||
}
|
||||
|
||||
Result<uint32> UdpSocketFd::maximize_snd_buffer(uint32 max) {
|
||||
return maximize_buffer(get_native_fd().fd(), SO_SNDBUF, max == 0 ? default_udp_max_snd_buffer_size : max);
|
||||
return maximize_buffer(get_native_fd().fd(), SO_SNDBUF, max == 0 ? DEFAULT_UDP_MAX_SND_BUFFER_SIZE : max);
|
||||
}
|
||||
|
||||
Result<uint32> UdpSocketFd::maximize_rcv_buffer(uint32 max) {
|
||||
return maximize_buffer(get_native_fd().fd(), SO_RCVBUF, max == 0 ? default_udp_max_rcv_buffer_size : max);
|
||||
return maximize_buffer(get_native_fd().fd(), SO_RCVBUF, max == 0 ? DEFAULT_UDP_MAX_RCV_BUFFER_SIZE : max);
|
||||
}
|
||||
#else
|
||||
Result<uint32> UdpSocketFd::maximize_snd_buffer(uint32 max) {
|
||||
|
|
|
@ -96,8 +96,8 @@ class UdpSocketFd {
|
|||
#endif
|
||||
|
||||
private:
|
||||
static constexpr uint32 default_udp_max_snd_buffer_size = (1 << 24);
|
||||
static constexpr uint32 default_udp_max_rcv_buffer_size = (1 << 24);
|
||||
static constexpr uint32 DEFAULT_UDP_MAX_SND_BUFFER_SIZE = (1 << 24);
|
||||
static constexpr uint32 DEFAULT_UDP_MAX_RCV_BUFFER_SIZE = (1 << 24);
|
||||
std::unique_ptr<detail::UdpSocketFdImpl, detail::UdpSocketFdImplDeleter> impl_;
|
||||
explicit UdpSocketFd(unique_ptr<detail::UdpSocketFdImpl> impl);
|
||||
};
|
||||
|
|
|
@ -26,6 +26,8 @@ char disable_linker_warning_about_empty_file_epoll_cpp TD_UNUSED;
|
|||
#include "td/utils/logging.h"
|
||||
#include "td/utils/Status.h"
|
||||
|
||||
#include <cerrno>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
namespace td {
|
||||
|
@ -82,7 +84,8 @@ void Epoll::unsubscribe(PollableFdRef fd_ref) {
|
|||
int err = epoll_ctl(epoll_fd_.fd(), EPOLL_CTL_DEL, native_fd, nullptr);
|
||||
auto epoll_ctl_errno = errno;
|
||||
LOG_IF(FATAL, err == -1) << Status::PosixError(epoll_ctl_errno, "epoll_ctl DEL failed")
|
||||
<< ", epoll_fd = " << epoll_fd_.fd() << ", fd = " << native_fd << fd.native_fd().validate();
|
||||
<< ", epoll_fd = " << epoll_fd_.fd() << ", fd = " << native_fd
|
||||
<< ", status = " << fd.native_fd().validate();
|
||||
}
|
||||
|
||||
void Epoll::unsubscribe_before_close(PollableFdRef fd) {
|
||||
|
|
|
@ -28,6 +28,8 @@ char disable_linker_warning_about_empty_file_event_fd_bsd_cpp TD_UNUSED;
|
|||
#include "td/utils/port/SocketFd.h"
|
||||
#include "td/utils/Slice.h"
|
||||
|
||||
#include <cerrno>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <sys/socket.h>
|
||||
|
|
|
@ -25,10 +25,13 @@ char disable_linker_warning_about_empty_file_event_fd_linux_cpp TD_UNUSED;
|
|||
#include "td/utils/logging.h"
|
||||
#include "td/utils/misc.h"
|
||||
#include "td/utils/port/detail/NativeFd.h"
|
||||
#include "td/utils/port/detail/skip_eintr.h"
|
||||
#include "td/utils/port/PollFlags.h"
|
||||
#include "td/utils/ScopeGuard.h"
|
||||
#include "td/utils/Slice.h"
|
||||
|
||||
#include <cerrno>
|
||||
|
||||
#include <poll.h>
|
||||
#include <sys/eventfd.h>
|
||||
#include <unistd.h>
|
||||
|
|
|
@ -25,6 +25,7 @@ char disable_linker_warning_about_empty_file_kqueue_cpp TD_UNUSED;
|
|||
#include "td/utils/logging.h"
|
||||
#include "td/utils/Status.h"
|
||||
|
||||
#include <cerrno>
|
||||
#include <utility>
|
||||
|
||||
#include <sys/time.h>
|
||||
|
|
|
@ -38,7 +38,9 @@ namespace td {
|
|||
class FdSet {
|
||||
public:
|
||||
void on_create_fd(NativeFd::Fd fd) {
|
||||
CHECK(is_valid(fd));
|
||||
if (!is_valid(fd)) {
|
||||
return;
|
||||
}
|
||||
if (is_stdio(fd)) {
|
||||
return;
|
||||
}
|
||||
|
@ -64,7 +66,9 @@ class FdSet {
|
|||
}
|
||||
|
||||
void on_close_fd(NativeFd::Fd fd) {
|
||||
CHECK(is_valid(fd));
|
||||
if (!is_valid(fd)) {
|
||||
return;
|
||||
}
|
||||
if (is_stdio(fd)) {
|
||||
return;
|
||||
}
|
||||
|
@ -112,7 +116,7 @@ FdSet &get_fd_set() {
|
|||
|
||||
Status NativeFd::validate() const {
|
||||
#if TD_FD_DEBUG
|
||||
return get_fd_set().validate(fd_.get());
|
||||
return get_fd_set().validate(fd_);
|
||||
#else
|
||||
return Status::OK();
|
||||
#endif
|
||||
|
@ -121,13 +125,13 @@ Status NativeFd::validate() const {
|
|||
NativeFd::NativeFd(Fd fd) : fd_(fd) {
|
||||
VLOG(fd) << *this << " create";
|
||||
#if TD_FD_DEBUG
|
||||
get_fd_set().on_create_fd(fd_.get());
|
||||
get_fd_set().on_create_fd(fd_);
|
||||
#endif
|
||||
}
|
||||
|
||||
NativeFd::NativeFd(Fd fd, bool nolog) : fd_(fd) {
|
||||
#if TD_FD_DEBUG
|
||||
get_fd_set().on_create_fd(fd_.get());
|
||||
get_fd_set().on_create_fd(fd_);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -135,18 +139,26 @@ NativeFd::NativeFd(Fd fd, bool nolog) : fd_(fd) {
|
|||
NativeFd::NativeFd(Socket socket) : fd_(reinterpret_cast<Fd>(socket)), is_socket_(true) {
|
||||
VLOG(fd) << *this << " create";
|
||||
#if TD_FD_DEBUG
|
||||
get_fd_set().on_create_fd(fd_.get());
|
||||
get_fd_set().on_create_fd(fd_);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
NativeFd &NativeFd::operator=(NativeFd &&from) {
|
||||
CHECK(this != &from);
|
||||
close();
|
||||
fd_ = std::move(from.fd_);
|
||||
NativeFd::NativeFd(NativeFd &&other) : fd_(other.fd_) {
|
||||
#if TD_PORT_WINDOWS
|
||||
is_socket_ = from.is_socket_;
|
||||
is_socket_ = other.is_socket_;
|
||||
#endif
|
||||
other.fd_ = empty_fd();
|
||||
}
|
||||
|
||||
NativeFd &NativeFd::operator=(NativeFd &&other) {
|
||||
CHECK(this != &other);
|
||||
close();
|
||||
fd_ = other.fd_;
|
||||
#if TD_PORT_WINDOWS
|
||||
is_socket_ = other.is_socket_;
|
||||
#endif
|
||||
other.fd_ = empty_fd();
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
@ -155,7 +167,7 @@ NativeFd::~NativeFd() {
|
|||
}
|
||||
|
||||
NativeFd::operator bool() const {
|
||||
return fd_.get() != empty_fd();
|
||||
return fd_ != empty_fd();
|
||||
}
|
||||
|
||||
NativeFd::Fd NativeFd::empty_fd() {
|
||||
|
@ -167,7 +179,7 @@ NativeFd::Fd NativeFd::empty_fd() {
|
|||
}
|
||||
|
||||
NativeFd::Fd NativeFd::fd() const {
|
||||
return fd_.get();
|
||||
return fd_;
|
||||
}
|
||||
|
||||
NativeFd::Socket NativeFd::socket() const {
|
||||
|
@ -175,7 +187,7 @@ NativeFd::Socket NativeFd::socket() const {
|
|||
return fd();
|
||||
#elif TD_PORT_WINDOWS
|
||||
CHECK(is_socket_);
|
||||
return reinterpret_cast<Socket>(fd_.get());
|
||||
return reinterpret_cast<Socket>(fd_);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -239,13 +251,13 @@ void NativeFd::close() {
|
|||
auto error = OS_ERROR("Close fd");
|
||||
LOG(ERROR) << error;
|
||||
}
|
||||
fd_ = {};
|
||||
fd_ = empty_fd();
|
||||
}
|
||||
|
||||
NativeFd::Fd NativeFd::release() {
|
||||
VLOG(fd) << *this << " release";
|
||||
auto res = fd_.get();
|
||||
fd_ = {};
|
||||
auto res = fd_;
|
||||
fd_ = empty_fd();
|
||||
#if TD_FD_DEBUG
|
||||
get_fd_set().on_close_fd(res);
|
||||
#endif
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
#include "td/utils/port/config.h"
|
||||
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/MovableValue.h"
|
||||
#include "td/utils/Status.h"
|
||||
#include "td/utils/StringBuilder.h"
|
||||
|
||||
|
@ -37,8 +36,6 @@ class NativeFd {
|
|||
using Socket = SOCKET;
|
||||
#endif
|
||||
NativeFd() = default;
|
||||
NativeFd(NativeFd &&) = default;
|
||||
NativeFd &operator=(NativeFd &&);
|
||||
explicit NativeFd(Fd fd);
|
||||
NativeFd(Fd fd, bool nolog);
|
||||
#if TD_PORT_WINDOWS
|
||||
|
@ -46,12 +43,12 @@ class NativeFd {
|
|||
#endif
|
||||
NativeFd(const NativeFd &) = delete;
|
||||
NativeFd &operator=(const NativeFd &) = delete;
|
||||
NativeFd(NativeFd &&other);
|
||||
NativeFd &operator=(NativeFd &&other);
|
||||
~NativeFd();
|
||||
|
||||
explicit operator bool() const;
|
||||
|
||||
static Fd empty_fd();
|
||||
|
||||
Fd fd() const;
|
||||
Socket socket() const;
|
||||
|
||||
|
@ -67,10 +64,10 @@ class NativeFd {
|
|||
Status validate() const;
|
||||
|
||||
private:
|
||||
#if TD_PORT_POSIX
|
||||
MovableValue<Fd, -1> fd_;
|
||||
#elif TD_PORT_WINDOWS
|
||||
MovableValue<Fd, INVALID_HANDLE_VALUE> fd_;
|
||||
static Fd empty_fd();
|
||||
|
||||
Fd fd_ = empty_fd();
|
||||
#if TD_PORT_WINDOWS
|
||||
bool is_socket_{false};
|
||||
#endif
|
||||
};
|
||||
|
|
|
@ -28,6 +28,8 @@ char disable_linker_warning_about_empty_file_poll_cpp TD_UNUSED;
|
|||
#include "td/utils/ScopeGuard.h"
|
||||
#include "td/utils/Status.h"
|
||||
|
||||
#include <cerrno>
|
||||
|
||||
namespace td {
|
||||
namespace detail {
|
||||
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
namespace td {
|
||||
|
||||
|
@ -220,30 +219,6 @@ inline const NativeFd &PollableFd::native_fd() const {
|
|||
return fd_info_->native_fd();
|
||||
}
|
||||
|
||||
#if TD_PORT_POSIX
|
||||
namespace detail {
|
||||
template <class F>
|
||||
auto skip_eintr(F &&f) {
|
||||
decltype(f()) res;
|
||||
static_assert(std::is_integral<decltype(res)>::value, "integral type expected");
|
||||
do {
|
||||
errno = 0; // just in case
|
||||
res = f();
|
||||
} while (res < 0 && errno == EINTR);
|
||||
return res;
|
||||
}
|
||||
template <class F>
|
||||
auto skip_eintr_cstr(F &&f) {
|
||||
char *res;
|
||||
do {
|
||||
errno = 0; // just in case
|
||||
res = f();
|
||||
} while (res == nullptr && errno == EINTR);
|
||||
return res;
|
||||
}
|
||||
} // namespace detail
|
||||
#endif
|
||||
|
||||
template <class FdT>
|
||||
bool can_read(const FdT &fd) {
|
||||
return fd.get_poll_info().get_flags().can_read() || fd.get_poll_info().get_flags().has_pending_error();
|
||||
|
|
114
tdutils/td/utils/port/detail/ThreadPthread.cpp
Normal file
114
tdutils/td/utils/port/detail/ThreadPthread.cpp
Normal file
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2020 Telegram Systems LLP
|
||||
*/
|
||||
|
||||
#include "td/utils/port/detail/ThreadPthread.h"
|
||||
|
||||
char disable_linker_warning_about_empty_file_thread_pthread_cpp TD_UNUSED;
|
||||
|
||||
#if TD_THREAD_PTHREAD
|
||||
|
||||
#include "td/utils/misc.h"
|
||||
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#if TD_FREEBSD || TD_OPENBSD || TD_NETBSD
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
|
||||
namespace td {
|
||||
namespace detail {
|
||||
unsigned ThreadPthread::hardware_concurrency() {
|
||||
// Linux and macOS
|
||||
#if defined(_SC_NPROCESSORS_ONLN)
|
||||
{
|
||||
auto res = sysconf(_SC_NPROCESSORS_ONLN);
|
||||
if (res > 0) {
|
||||
return narrow_cast<unsigned>(res);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if TD_FREEBSD || TD_OPENBSD || TD_NETBSD
|
||||
#if defined(HW_AVAILCPU) && defined(CTL_HW)
|
||||
{
|
||||
int mib[2] = {CTL_HW, HW_AVAILCPU};
|
||||
int res{0};
|
||||
size_t len = sizeof(res);
|
||||
if (sysctl(mib, 2, &res, &len, nullptr, 0) == 0 && res != 0) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HW_NCPU) && defined(CTL_HW)
|
||||
{
|
||||
int mib[2] = {CTL_HW, HW_NCPU};
|
||||
int res{0};
|
||||
size_t len = sizeof(res);
|
||||
if (sysctl(mib, 2, &res, &len, nullptr, 0) == 0 && res != 0) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Just in case
|
||||
return 8;
|
||||
}
|
||||
|
||||
void ThreadPthread::set_name(CSlice name) {
|
||||
#if defined(_GNU_SOURCE) && defined(__GLIBC_PREREQ)
|
||||
#if __GLIBC_PREREQ(2, 12)
|
||||
pthread_setname_np(thread_, name.c_str());
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void ThreadPthread::join() {
|
||||
if (is_inited_.get()) {
|
||||
is_inited_ = false;
|
||||
pthread_join(thread_, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadPthread::detach() {
|
||||
if (is_inited_.get()) {
|
||||
is_inited_ = false;
|
||||
pthread_detach(thread_);
|
||||
}
|
||||
}
|
||||
|
||||
int ThreadPthread::do_pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *),
|
||||
void *arg) {
|
||||
return pthread_create(thread, attr, start_routine, arg);
|
||||
}
|
||||
|
||||
namespace this_thread_pthread {
|
||||
void yield() {
|
||||
sched_yield();
|
||||
}
|
||||
ThreadPthread::id get_id() {
|
||||
return pthread_self();
|
||||
}
|
||||
} // namespace this_thread_pthread
|
||||
|
||||
} // namespace detail
|
||||
} // namespace td
|
||||
#endif
|
|
@ -34,8 +34,7 @@
|
|||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
namespace td {
|
||||
namespace detail {
|
||||
|
@ -44,7 +43,8 @@ class ThreadPthread {
|
|||
ThreadPthread() = default;
|
||||
ThreadPthread(const ThreadPthread &other) = delete;
|
||||
ThreadPthread &operator=(const ThreadPthread &other) = delete;
|
||||
ThreadPthread(ThreadPthread &&) = default;
|
||||
ThreadPthread(ThreadPthread &&other) noexcept : is_inited_(std::move(other.is_inited_)), thread_(other.thread_) {
|
||||
}
|
||||
ThreadPthread &operator=(ThreadPthread &&other) {
|
||||
join();
|
||||
is_inited_ = std::move(other.is_inited_);
|
||||
|
@ -58,36 +58,20 @@ class ThreadPthread {
|
|||
invoke_tuple(std::move(args));
|
||||
clear_thread_locals();
|
||||
});
|
||||
pthread_create(&thread_, nullptr, run_thread, func.release());
|
||||
do_pthread_create(&thread_, nullptr, run_thread, func.release());
|
||||
is_inited_ = true;
|
||||
}
|
||||
void set_name(CSlice name) {
|
||||
#if defined(_GNU_SOURCE) && defined(__GLIBC_PREREQ)
|
||||
#if __GLIBC_PREREQ(2, 12)
|
||||
pthread_setname_np(thread_, name.c_str());
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
void join() {
|
||||
if (is_inited_.get()) {
|
||||
is_inited_ = false;
|
||||
pthread_join(thread_, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void detach() {
|
||||
if (is_inited_.get()) {
|
||||
is_inited_ = false;
|
||||
pthread_detach(thread_);
|
||||
}
|
||||
}
|
||||
~ThreadPthread() {
|
||||
join();
|
||||
}
|
||||
|
||||
static unsigned hardware_concurrency() {
|
||||
return 8;
|
||||
}
|
||||
void set_name(CSlice name);
|
||||
|
||||
void join();
|
||||
|
||||
void detach();
|
||||
|
||||
static unsigned hardware_concurrency();
|
||||
|
||||
using id = pthread_t;
|
||||
|
||||
|
@ -100,6 +84,8 @@ class ThreadPthread {
|
|||
return std::forward<T>(v);
|
||||
}
|
||||
|
||||
int do_pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg);
|
||||
|
||||
static void *run_thread(void *ptr) {
|
||||
ThreadIdGuard thread_id_guard;
|
||||
auto func = unique_ptr<Destructor>(static_cast<Destructor *>(ptr));
|
||||
|
@ -108,12 +94,8 @@ class ThreadPthread {
|
|||
};
|
||||
|
||||
namespace this_thread_pthread {
|
||||
inline void yield() {
|
||||
sched_yield();
|
||||
}
|
||||
inline ThreadPthread::id get_id() {
|
||||
return pthread_self();
|
||||
}
|
||||
void yield();
|
||||
ThreadPthread::id get_id();
|
||||
} // namespace this_thread_pthread
|
||||
} // namespace detail
|
||||
} // namespace td
|
||||
|
|
52
tdutils/td/utils/port/detail/skip_eintr.h
Normal file
52
tdutils/td/utils/port/detail/skip_eintr.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2020 Telegram Systems LLP
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cerrno>
|
||||
#include <type_traits>
|
||||
|
||||
namespace td {
|
||||
|
||||
#if TD_PORT_POSIX
|
||||
namespace detail {
|
||||
template <class F>
|
||||
auto skip_eintr(F &&f) {
|
||||
decltype(f()) res;
|
||||
static_assert(std::is_integral<decltype(res)>::value, "integral type expected");
|
||||
do {
|
||||
errno = 0; // just in case
|
||||
res = f();
|
||||
} while (res < 0 && errno == EINTR);
|
||||
return res;
|
||||
}
|
||||
|
||||
template <class F>
|
||||
auto skip_eintr_cstr(F &&f) {
|
||||
char *res;
|
||||
do {
|
||||
errno = 0; // just in case
|
||||
res = f();
|
||||
} while (res == nullptr && errno == EINTR);
|
||||
return res;
|
||||
}
|
||||
} // namespace detail
|
||||
#endif
|
||||
|
||||
} // namespace td
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
#include "td/utils/format.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/port/detail/PollableFd.h"
|
||||
#include "td/utils/port/detail/skip_eintr.h"
|
||||
#include "td/utils/ScopeGuard.h"
|
||||
|
||||
#if TD_PORT_WINDOWS
|
||||
|
@ -55,6 +55,7 @@
|
|||
#include <sys/syslimits.h>
|
||||
#endif
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
|
||||
|
@ -68,8 +69,7 @@ Status set_temporary_dir(CSlice dir) {
|
|||
input_dir += TD_DIR_SLASH;
|
||||
}
|
||||
TRY_STATUS(mkpath(input_dir, 0750));
|
||||
TRY_RESULT(real_dir, realpath(input_dir));
|
||||
temporary_dir = std::move(real_dir);
|
||||
TRY_RESULT_ASSIGN(temporary_dir, realpath(input_dir));
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
|
@ -431,8 +431,7 @@ Result<string> realpath(CSlice slice, bool ignore_access_denied) {
|
|||
return OS_ERROR(PSLICE() << "GetFullPathNameW failed for \"" << slice << '"');
|
||||
}
|
||||
} else {
|
||||
TRY_RESULT(t_res, from_wstring(buf));
|
||||
res = std::move(t_res);
|
||||
TRY_RESULT_ASSIGN(res, from_wstring(buf));
|
||||
}
|
||||
if (res.empty()) {
|
||||
return Status::Error("Empty path");
|
||||
|
|
|
@ -14,14 +14,19 @@
|
|||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2019-2020 Telegram Systems LLP
|
||||
Copyright 2017-2020 Telegram Systems LLP
|
||||
*/
|
||||
#include "rlimit.h"
|
||||
#if TD_LINUX || TD_ANDROID
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "td/utils/port/rlimit.h"
|
||||
|
||||
#include "td/utils/port/config.h"
|
||||
|
||||
#include "td/utils/misc.h"
|
||||
|
||||
#if TD_PORT_POSIX
|
||||
#include <sys/resource.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
namespace td {
|
||||
|
|
|
@ -16,10 +16,10 @@
|
|||
|
||||
Copyright 2017-2020 Telegram Systems LLP
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "td/utils/port/config.h"
|
||||
#include "td/utils/port/platform.h"
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/Status.h"
|
||||
|
||||
namespace td {
|
||||
|
@ -28,5 +28,4 @@ enum class RlimitType { nofile, rss };
|
|||
|
||||
td::Status change_rlimit(RlimitType rlim_type, td::uint64 value, td::uint64 cap = 0);
|
||||
td::Status change_maximize_rlimit(RlimitType rlim, td::uint64 value);
|
||||
|
||||
} // namespace td
|
||||
|
|
|
@ -301,7 +301,7 @@ void signal_safe_write_pointer(void *p, bool add_header) {
|
|||
char *ptr = end;
|
||||
*--ptr = '\n';
|
||||
do {
|
||||
*--ptr = td::format::hex_digit(addr % 16);
|
||||
*--ptr = format::hex_digit(addr % 16);
|
||||
addr /= 16;
|
||||
} while (addr != 0);
|
||||
*--ptr = 'x';
|
||||
|
@ -318,6 +318,7 @@ static void block_stdin() {
|
|||
}
|
||||
|
||||
static void default_failure_signal_handler(int sig) {
|
||||
Stacktrace::init();
|
||||
signal_safe_write_signal_number(sig);
|
||||
|
||||
Stacktrace::PrintOptions options;
|
||||
|
|
|
@ -65,10 +65,12 @@ void print_backtrace_gdb(void) {
|
|||
name_buf[res] = 0;
|
||||
|
||||
#if TD_LINUX
|
||||
#if defined(PR_SET_DUMPABLE)
|
||||
if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) < 0) {
|
||||
signal_safe_write("Can't set dumpable\n");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#if defined(PR_SET_PTRACER)
|
||||
// We can't use event fd because we are in a signal handler
|
||||
int fds[2];
|
||||
|
@ -120,10 +122,18 @@ void print_backtrace_gdb(void) {
|
|||
} // namespace
|
||||
|
||||
void Stacktrace::print_to_stderr(const PrintOptions &options) {
|
||||
print_backtrace();
|
||||
if (options.use_gdb) {
|
||||
print_backtrace_gdb();
|
||||
}
|
||||
print_backtrace();
|
||||
}
|
||||
|
||||
void Stacktrace::init() {
|
||||
#if __GLIBC__
|
||||
// backtrace needs to be called once to ensure that next calls are async-signal-safe
|
||||
void *buffer[1];
|
||||
backtrace(buffer, 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace td
|
||||
|
|
|
@ -28,6 +28,8 @@ class Stacktrace {
|
|||
}
|
||||
};
|
||||
static void print_to_stderr(const PrintOptions &options = PrintOptions());
|
||||
|
||||
static void init();
|
||||
};
|
||||
|
||||
} // namespace td
|
||||
|
|
172
tdutils/td/utils/port/uname.cpp
Normal file
172
tdutils/td/utils/port/uname.cpp
Normal file
|
@ -0,0 +1,172 @@
|
|||
#include "td/utils/port/uname.h"
|
||||
|
||||
#include "td/utils/port/config.h"
|
||||
|
||||
#include "td/utils/common.h"
|
||||
#include "td/utils/filesystem.h"
|
||||
#include "td/utils/logging.h"
|
||||
#include "td/utils/misc.h"
|
||||
#include "td/utils/port/Stat.h"
|
||||
|
||||
#if TD_PORT_POSIX
|
||||
|
||||
#if TD_ANDROID
|
||||
#include <sys/system_properties.h>
|
||||
#else
|
||||
#if TD_DARWIN
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
#include <sys/utsname.h>
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
namespace td {
|
||||
|
||||
#if TD_DARWIN || TD_LINUX
|
||||
static string read_os_name(CSlice os_version_file_path, CSlice prefix, CSlice suffix) {
|
||||
auto r_stat = stat(os_version_file_path);
|
||||
if (r_stat.is_ok() && r_stat.ok().is_reg_ && r_stat.ok().size_ < (1 << 16)) {
|
||||
auto r_file = read_file_str(os_version_file_path, r_stat.ok().size_);
|
||||
if (r_file.is_ok()) {
|
||||
auto begin_pos = r_file.ok().find(prefix.c_str());
|
||||
if (begin_pos != string::npos) {
|
||||
begin_pos += prefix.size();
|
||||
auto end_pos = r_file.ok().find(suffix.c_str(), begin_pos);
|
||||
if (end_pos != string::npos) {
|
||||
auto os_version = trim(r_file.ok().substr(begin_pos, end_pos - begin_pos));
|
||||
if (os_version.find("\n") == string::npos) {
|
||||
return os_version;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return string();
|
||||
}
|
||||
#endif
|
||||
|
||||
Slice get_operating_system_version() {
|
||||
static string result = []() -> string {
|
||||
#if TD_DARWIN
|
||||
char version[256];
|
||||
size_t size = sizeof(version);
|
||||
string os_version;
|
||||
if (sysctlbyname("kern.osproductversion", version, &size, nullptr, 0) == 0) {
|
||||
os_version = trim(string(version, size));
|
||||
}
|
||||
if (os_version.empty()) {
|
||||
os_version = read_os_name("/System/Library/CoreServices/SystemVersion.plist",
|
||||
"<key>ProductUserVisibleVersion</key>\n\t<string>", "</string>\n");
|
||||
}
|
||||
if (!os_version.empty()) {
|
||||
os_version = " " + os_version;
|
||||
}
|
||||
|
||||
#if TD_DARWIN_IOS
|
||||
return "iOS" + os_version;
|
||||
#elif TD_DARWIN_TV_OS
|
||||
return "tvOS" + os_version;
|
||||
#elif TD_DARWIN_WATCH_OS
|
||||
return "watchOS" + os_version;
|
||||
#elif TD_DARWIN_MAC
|
||||
return "macOS" + os_version;
|
||||
#else
|
||||
return "Darwin" + os_version;
|
||||
#endif
|
||||
#elif TD_PORT_POSIX
|
||||
#if TD_ANDROID
|
||||
char version[PROP_VALUE_MAX + 1];
|
||||
int length = __system_property_get("ro.build.version.release", version);
|
||||
if (length > 0) {
|
||||
return "Android " + string(version, length);
|
||||
}
|
||||
#else
|
||||
#if TD_LINUX
|
||||
auto os_name = read_os_name("/etc/os-release", "PRETTY_NAME=\"", "\"\n");
|
||||
if (!os_name.empty()) {
|
||||
return os_name;
|
||||
}
|
||||
#endif
|
||||
|
||||
utsname name;
|
||||
int err = uname(&name);
|
||||
if (err == 0) {
|
||||
auto os_name = trim(PSTRING() << name.sysname << " " << name.release);
|
||||
if (!os_name.empty()) {
|
||||
return os_name;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
LOG(ERROR) << "Failed to identify OS name; use generic one";
|
||||
|
||||
#if TD_ANDROID
|
||||
return "Android";
|
||||
#elif TD_TIZEN
|
||||
return "Tizen";
|
||||
#elif TD_LINUX
|
||||
return "Linux";
|
||||
#elif TD_FREEBSD
|
||||
return "FreeBSD";
|
||||
#elif TD_OPENBSD
|
||||
return "OpenBSD";
|
||||
#elif TD_NETBSD
|
||||
return "NetBSD";
|
||||
#elif TD_CYGWIN
|
||||
return "Cygwin";
|
||||
#elif TD_EMSCRIPTEN
|
||||
return "Emscripten";
|
||||
#else
|
||||
return "Unix";
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM)
|
||||
auto handle = GetModuleHandle(L"ntdll.dll");
|
||||
if (handle != nullptr) {
|
||||
using RtlGetVersionPtr = LONG(WINAPI *)(PRTL_OSVERSIONINFOEXW);
|
||||
RtlGetVersionPtr RtlGetVersion = reinterpret_cast<RtlGetVersionPtr>(GetProcAddress(handle, "RtlGetVersion"));
|
||||
if (RtlGetVersion != nullptr) {
|
||||
RTL_OSVERSIONINFOEXW os_version_info = {};
|
||||
os_version_info.dwOSVersionInfoSize = sizeof(os_version_info);
|
||||
if (RtlGetVersion(&os_version_info) == 0) {
|
||||
auto major = os_version_info.dwMajorVersion;
|
||||
auto minor = os_version_info.dwMinorVersion;
|
||||
bool is_server = os_version_info.wProductType != VER_NT_WORKSTATION;
|
||||
|
||||
if (major == 10 && minor >= 0) {
|
||||
if (is_server) {
|
||||
return os_version_info.dwBuildNumber >= 17623 ? "Windows Server 2019" : "Windows Server 2016";
|
||||
}
|
||||
return "Windows 10";
|
||||
}
|
||||
if (major == 6 && minor == 3) {
|
||||
return is_server ? "Windows Server 2012 R2" : "Windows 8.1";
|
||||
}
|
||||
if (major == 6 && minor == 2) {
|
||||
return is_server ? "Windows Server 2012" : "Windows 8";
|
||||
}
|
||||
if (major == 6 && minor == 1) {
|
||||
return is_server ? "Windows Server 2008 R2" : "Windows 7";
|
||||
}
|
||||
if (major == 6 && minor == 0) {
|
||||
return is_server ? "Windows Server 2008" : "Windows Vista";
|
||||
}
|
||||
return is_server ? "Windows Server" : "Windows";
|
||||
}
|
||||
}
|
||||
}
|
||||
#elif TD_WINRT
|
||||
return "Windows 10";
|
||||
#endif
|
||||
|
||||
LOG(ERROR) << "Failed to identify OS name; use generic one";
|
||||
return "Windows";
|
||||
#endif
|
||||
}();
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace td
|
9
tdutils/td/utils/port/uname.h
Normal file
9
tdutils/td/utils/port/uname.h
Normal file
|
@ -0,0 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include "td/utils/Slice.h"
|
||||
|
||||
namespace td {
|
||||
|
||||
Slice get_operating_system_version();
|
||||
|
||||
}
|
|
@ -16,44 +16,53 @@
|
|||
|
||||
Copyright 2017-2020 Telegram Systems LLP
|
||||
*/
|
||||
#include "user.h"
|
||||
#if TD_LINUX
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <pwd.h>
|
||||
|
||||
#include "td/utils/port/user.h"
|
||||
|
||||
#include "td/utils/port/config.h"
|
||||
|
||||
#if TD_PORT_POSIX
|
||||
#include <grp.h>
|
||||
#include <pwd.h>
|
||||
#if TD_DARWIN || TD_FREEBSD || TD_NETBSD
|
||||
#include <sys/param.h>
|
||||
#endif
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
namespace td {
|
||||
|
||||
#if TD_LINUX
|
||||
td::Status change_user(td::Slice user) {
|
||||
struct passwd *pw;
|
||||
if (getuid() != 0 || geteuid() != 0) {
|
||||
return td::Status::PosixError(errno, "cannot setuid() as not root");
|
||||
}
|
||||
if ((pw = getpwnam(user.str().c_str())) == 0) {
|
||||
return td::Status::PosixError(errno, PSTRING() << "bad user '" << user << "'");
|
||||
Status change_user(CSlice username, CSlice groupname) {
|
||||
#if TD_PORT_POSIX
|
||||
passwd *pw = getpwnam(username.c_str());
|
||||
if (pw == nullptr) {
|
||||
return OS_ERROR(PSTRING() << "Can't find the user '" << username << "' to switch to");
|
||||
}
|
||||
uid_t uid = pw->pw_uid;
|
||||
gid_t gid = pw->pw_gid;
|
||||
if (setgroups(1, &gid) < 0) {
|
||||
return td::Status::PosixError(errno, "failed to clear supplementary groups list");
|
||||
if (setgroups(1, &gid) == -1) {
|
||||
return OS_ERROR("Failed to clear supplementary group list");
|
||||
}
|
||||
if (initgroups(user.str().c_str(), gid) != 0) {
|
||||
return td::Status::PosixError(errno, "failed to load groups of user");
|
||||
if (!groupname.empty()) {
|
||||
group *g = getgrnam(groupname.c_str());
|
||||
if (g == nullptr) {
|
||||
return OS_ERROR("Can't find the group to switch to");
|
||||
}
|
||||
gid = g->gr_gid;
|
||||
} else if (initgroups(username.c_str(), gid) == -1) {
|
||||
return OS_ERROR("Failed to load groups of user");
|
||||
}
|
||||
if (setgid(pw->pw_gid) < 0) {
|
||||
return td::Status::PosixError(errno, "failed to setgid()");
|
||||
if (setgid(gid) == -1) {
|
||||
return OS_ERROR("failed to set effective group ID");
|
||||
}
|
||||
if (setuid(pw->pw_uid) < 0) {
|
||||
return td::Status::PosixError(errno, "failed to setuid()");
|
||||
if (setuid(uid) == -1) {
|
||||
return OS_ERROR("failed to set effective user ID");
|
||||
}
|
||||
return td::Status::OK();
|
||||
}
|
||||
return Status::OK();
|
||||
#else
|
||||
td::Status change_user(td::Slice username) {
|
||||
return td::Status::Error("not implemented");
|
||||
}
|
||||
return Status::Error("Changing effective user is not supported");
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace td
|
||||
|
|
|
@ -16,14 +16,12 @@
|
|||
|
||||
Copyright 2017-2020 Telegram Systems LLP
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "td/utils/port/config.h"
|
||||
#include "td/utils/port/platform.h"
|
||||
#include "td/utils/Slice.h"
|
||||
#include "td/utils/Status.h"
|
||||
|
||||
namespace td {
|
||||
|
||||
td::Status change_user(td::Slice username);
|
||||
|
||||
Status change_user(CSlice username, CSlice groupname = CSlice());
|
||||
}
|
||||
|
|
|
@ -33,10 +33,7 @@ Result<std::wstring> to_wstring(CSlice slice) {
|
|||
return Status::Error("Wrong encoding");
|
||||
}
|
||||
|
||||
size_t wstring_len = 0;
|
||||
for (auto c : slice) {
|
||||
wstring_len += ((c & 0xc0) != 0x80) + ((c & 0xf8) == 0xf0);
|
||||
}
|
||||
size_t wstring_len = utf8_utf16_length(slice);
|
||||
|
||||
std::wstring result(wstring_len, static_cast<wchar_t>(0));
|
||||
if (wstring_len) {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue