1
0
Fork 0
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:
ton 2020-07-06 17:07:20 +03:00
parent dbde9c1c40
commit f064b1047a
257 changed files with 6665 additions and 2608 deletions

View file

@ -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);

View file

@ -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"

View file

@ -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
}

View file

@ -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;

View file

@ -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;

View file

@ -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_;
};

View file

@ -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() {

View file

@ -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 {

View file

@ -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);

View file

@ -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];

View file

@ -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();

View file

@ -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();

View file

@ -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());

View file

@ -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

View file

@ -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();
}

View file

@ -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) {

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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);

View file

@ -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) {

View file

@ -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;

View file

@ -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

View file

@ -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);

View file

@ -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;

View file

@ -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)));
}

View file

@ -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_) {

View 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

View 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

View file

@ -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;

View file

@ -57,7 +57,7 @@ class ParserImpl {
return ptr_ == end_;
}
void clear() {
ptr_ = nullptr;
ptr_ = SliceT().begin();
end_ = ptr_;
status_ = Status::OK();
}

View 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

View file

@ -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) {

View file

@ -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

View file

@ -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];

View file

@ -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() + [&]

View file

@ -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

View file

@ -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)) {
}

View file

@ -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);

View file

@ -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
}

View file

@ -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_;
}

View file

@ -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 {};

View file

@ -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;
}

View file

@ -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) {

View file

@ -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];

View file

@ -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);
}

View file

@ -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()) {

View file

@ -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 {

View file

@ -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 {

View file

@ -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
View 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

View file

@ -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]);
}

View file

@ -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() {

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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());

View file

@ -5,5 +5,4 @@
#cmakedefine01 TD_HAVE_CRC32C
#cmakedefine01 TD_HAVE_COROUTINES
#cmakedefine01 TD_HAVE_ABSL
#cmakedefine01 TD_HAVE_GETOPT
#cmakedefine01 TD_FD_DEBUG

View file

@ -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

View file

@ -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

View file

@ -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"

View file

@ -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};

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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):

View file

@ -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

View file

@ -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

View file

@ -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)

View file

@ -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

View file

@ -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;

View file

@ -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

View file

@ -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;

View file

@ -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

View file

@ -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;

View file

@ -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

View file

@ -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

View file

@ -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();
}

View file

@ -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) {

View file

@ -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);
};

View file

@ -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) {

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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

View file

@ -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
};

View file

@ -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 {

View file

@ -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();

View 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

View file

@ -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

View 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

View file

@ -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");

View file

@ -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 {

View file

@ -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

View file

@ -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;

View file

@ -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

View file

@ -28,6 +28,8 @@ class Stacktrace {
}
};
static void print_to_stderr(const PrintOptions &options = PrintOptions());
static void init();
};
} // namespace td

View 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

View file

@ -0,0 +1,9 @@
#pragma once
#include "td/utils/Slice.h"
namespace td {
Slice get_operating_system_version();
}

View file

@ -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

View file

@ -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());
}

View file

@ -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