1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-03-09 15:40:10 +00:00

initial commit

This commit is contained in:
initial commit 2019-09-07 14:03:22 +04:00 committed by vvaltman
commit c2da007f40
1610 changed files with 398047 additions and 0 deletions

View file

@ -0,0 +1,68 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/ByteFlow.h"
#include "td/utils/common.h"
#include "td/utils/crypto.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
#include "td/utils/UInt.h"
namespace td {
#if TD_HAVE_OPENSSL
class AesCtrByteFlow : public ByteFlowInplaceBase {
public:
void init(const UInt256 &key, const UInt128 &iv) {
state_.init(key, iv);
}
void init(AesCtrState &&state) {
state_ = std::move(state);
}
AesCtrState move_aes_ctr_state() {
return std::move(state_);
}
void loop() override {
bool was_updated = false;
while (true) {
auto ready = input_->prepare_read();
if (ready.empty()) {
break;
}
state_.encrypt(ready, MutableSlice(const_cast<char *>(ready.data()), ready.size()));
input_->confirm_read(ready.size());
output_.advance_end(ready.size());
was_updated = true;
}
if (was_updated) {
on_output_updated();
}
if (!is_input_active_) {
finish(Status::OK()); // End of input stream.
}
set_need_size(1);
}
private:
AesCtrState state_;
};
#endif
} // namespace td

321
tdutils/td/utils/BigNum.cpp Normal file
View file

@ -0,0 +1,321 @@
/*
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-2019 Telegram Systems LLP
*/
#include "td/utils/BigNum.h"
char disable_linker_warning_about_empty_file_bignum_cpp TD_UNUSED;
#if TD_HAVE_OPENSSL
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include <openssl/bn.h>
#include <openssl/crypto.h>
namespace td {
class BigNumContext::Impl {
public:
BN_CTX *big_num_context;
Impl() : big_num_context(BN_CTX_new()) {
LOG_IF(FATAL, big_num_context == nullptr);
}
Impl(const Impl &other) = delete;
Impl &operator=(const Impl &other) = delete;
Impl(Impl &&other) = delete;
Impl &operator=(Impl &&other) = delete;
~Impl() {
BN_CTX_free(big_num_context);
}
};
BigNumContext::BigNumContext() : impl_(make_unique<Impl>()) {
}
BigNumContext::BigNumContext(BigNumContext &&other) = default;
BigNumContext &BigNumContext::operator=(BigNumContext &&other) = default;
BigNumContext::~BigNumContext() = default;
class BigNum::Impl {
public:
BIGNUM *big_num;
Impl() : Impl(BN_new()) {
}
explicit Impl(BIGNUM *big_num) : big_num(big_num) {
LOG_IF(FATAL, big_num == nullptr);
}
Impl(const Impl &other) = delete;
Impl &operator=(const Impl &other) = delete;
Impl(Impl &&other) = delete;
Impl &operator=(Impl &&other) = delete;
~Impl() {
BN_clear_free(big_num);
}
};
BigNum::BigNum() : impl_(make_unique<Impl>()) {
}
BigNum::BigNum(const BigNum &other) : BigNum() {
*this = other;
}
BigNum &BigNum::operator=(const BigNum &other) {
CHECK(impl_ != nullptr);
CHECK(other.impl_ != nullptr);
BIGNUM *result = BN_copy(impl_->big_num, other.impl_->big_num);
LOG_IF(FATAL, result == nullptr);
return *this;
}
BigNum::BigNum(BigNum &&other) = default;
BigNum &BigNum::operator=(BigNum &&other) = default;
BigNum::~BigNum() = default;
BigNum BigNum::from_binary(Slice str) {
return BigNum(make_unique<Impl>(BN_bin2bn(str.ubegin(), narrow_cast<int>(str.size()), nullptr)));
}
BigNum BigNum::from_le_binary(Slice str) {
#if 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();
#endif
}
Result<BigNum> BigNum::from_decimal(CSlice str) {
BigNum result;
int res = BN_dec2bn(&result.impl_->big_num, str.c_str());
if (res == 0 || static_cast<size_t>(res) != str.size()) {
return Status::Error(PSLICE() << "Failed to parse \"" << str << "\" as BigNum");
}
return result;
}
Result<BigNum> BigNum::from_hex(CSlice str) {
BigNum result;
int res = BN_hex2bn(&result.impl_->big_num, str.c_str());
if (res == 0 || static_cast<size_t>(res) != str.size()) {
return Status::Error(PSLICE() << "Failed to parse \"" << str << "\" as hexadecimal BigNum");
}
return result;
}
BigNum BigNum::from_raw(void *openssl_big_num) {
return BigNum(make_unique<Impl>(static_cast<BIGNUM *>(openssl_big_num)));
}
BigNum::BigNum(unique_ptr<Impl> &&impl) : impl_(std::move(impl)) {
}
void BigNum::ensure_const_time() {
BN_set_flags(impl_->big_num, BN_FLG_CONSTTIME);
}
int BigNum::get_num_bits() const {
return BN_num_bits(impl_->big_num);
}
int BigNum::get_num_bytes() const {
return BN_num_bytes(impl_->big_num);
}
void BigNum::set_bit(int num) {
int result = BN_set_bit(impl_->big_num, num);
LOG_IF(FATAL, result != 1);
}
void BigNum::clear_bit(int num) {
int result = BN_clear_bit(impl_->big_num, num);
LOG_IF(FATAL, result != 1);
}
bool BigNum::is_bit_set(int num) const {
return BN_is_bit_set(impl_->big_num, num) != 0;
}
bool BigNum::is_prime(BigNumContext &context) const {
int result = BN_is_prime_ex(impl_->big_num, BN_prime_checks, context.impl_->big_num_context, nullptr);
LOG_IF(FATAL, result == -1);
return result == 1;
}
void BigNum::operator+=(uint32 value) {
int result = BN_add_word(impl_->big_num, value);
LOG_IF(FATAL, result != 1);
}
void BigNum::operator-=(uint32 value) {
int result = BN_sub_word(impl_->big_num, value);
LOG_IF(FATAL, result != 1);
}
void BigNum::operator*=(uint32 value) {
int result = BN_mul_word(impl_->big_num, value);
LOG_IF(FATAL, result != 1);
}
void BigNum::operator/=(uint32 value) {
BN_ULONG result = BN_div_word(impl_->big_num, value);
LOG_IF(FATAL, result == static_cast<BN_ULONG>(-1));
}
uint32 BigNum::operator%(uint32 value) const {
BN_ULONG result = BN_mod_word(impl_->big_num, value);
LOG_IF(FATAL, result == static_cast<BN_ULONG>(-1));
return narrow_cast<uint32>(result);
}
void BigNum::set_value(uint32 new_value) {
if (new_value == 0) {
BN_zero(impl_->big_num);
} else {
int result = BN_set_word(impl_->big_num, new_value);
LOG_IF(FATAL, result != 1);
}
}
BigNum BigNum::clone() const {
BIGNUM *result = BN_dup(impl_->big_num);
LOG_IF(FATAL, result == nullptr);
return BigNum(make_unique<Impl>(result));
}
string BigNum::to_binary(int exact_size) const {
int num_size = get_num_bytes();
if (exact_size == -1) {
exact_size = num_size;
} else {
CHECK(exact_size >= num_size);
}
string res(exact_size, '\0');
BN_bn2bin(impl_->big_num, MutableSlice(res).ubegin() + (exact_size - num_size));
return res;
}
string BigNum::to_le_binary(int exact_size) const {
#if OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined(LIBRESSL_VERSION_NUMBER)
int num_size = get_num_bytes();
if (exact_size == -1) {
exact_size = num_size;
} else {
CHECK(exact_size >= num_size);
}
string res(exact_size, '\0');
BN_bn2lebinpad(impl_->big_num, MutableSlice(res).ubegin(), exact_size);
return res;
#else
LOG(FATAL) << "Unsupported to_le_binary";
return "";
#endif
}
string BigNum::to_decimal() const {
char *result = BN_bn2dec(impl_->big_num);
CHECK(result != nullptr);
string res(result);
OPENSSL_free(result);
return res;
}
void BigNum::random(BigNum &r, int bits, int top, int bottom) {
int result = BN_rand(r.impl_->big_num, bits, top, bottom);
LOG_IF(FATAL, result != 1);
}
void BigNum::add(BigNum &r, const BigNum &a, const BigNum &b) {
int result = BN_add(r.impl_->big_num, a.impl_->big_num, b.impl_->big_num);
LOG_IF(FATAL, result != 1);
}
void BigNum::sub(BigNum &r, const BigNum &a, const BigNum &b) {
CHECK(r.impl_->big_num != a.impl_->big_num);
CHECK(r.impl_->big_num != b.impl_->big_num);
int result = BN_sub(r.impl_->big_num, a.impl_->big_num, b.impl_->big_num);
LOG_IF(FATAL, result != 1);
}
void BigNum::mul(BigNum &r, BigNum &a, BigNum &b, BigNumContext &context) {
int result = BN_mul(r.impl_->big_num, a.impl_->big_num, b.impl_->big_num, context.impl_->big_num_context);
LOG_IF(FATAL, result != 1);
}
void BigNum::mod_add(BigNum &r, BigNum &a, BigNum &b, const BigNum &m, BigNumContext &context) {
int result = BN_mod_add(r.impl_->big_num, a.impl_->big_num, b.impl_->big_num, m.impl_->big_num,
context.impl_->big_num_context);
LOG_IF(FATAL, result != 1);
}
void BigNum::mod_sub(BigNum &r, BigNum &a, BigNum &b, const BigNum &m, BigNumContext &context) {
int result = BN_mod_sub(r.impl_->big_num, a.impl_->big_num, b.impl_->big_num, m.impl_->big_num,
context.impl_->big_num_context);
LOG_IF(FATAL, result != 1);
}
void BigNum::mod_mul(BigNum &r, BigNum &a, BigNum &b, const BigNum &m, BigNumContext &context) {
int result = BN_mod_mul(r.impl_->big_num, a.impl_->big_num, b.impl_->big_num, m.impl_->big_num,
context.impl_->big_num_context);
LOG_IF(FATAL, result != 1);
}
void BigNum::mod_inverse(BigNum &r, BigNum &a, const BigNum &m, BigNumContext &context) {
auto result = BN_mod_inverse(r.impl_->big_num, a.impl_->big_num, m.impl_->big_num, context.impl_->big_num_context);
LOG_IF(FATAL, result != r.impl_->big_num);
}
void BigNum::div(BigNum *quotient, BigNum *remainder, const BigNum &dividend, const BigNum &divisor,
BigNumContext &context) {
auto q = quotient == nullptr ? nullptr : quotient->impl_->big_num;
auto r = remainder == nullptr ? nullptr : remainder->impl_->big_num;
if (q == nullptr && r == nullptr) {
return;
}
auto result = BN_div(q, r, dividend.impl_->big_num, divisor.impl_->big_num, context.impl_->big_num_context);
LOG_IF(FATAL, result != 1);
}
void BigNum::mod_exp(BigNum &r, const BigNum &a, const BigNum &p, const BigNum &m, BigNumContext &context) {
int result = BN_mod_exp(r.impl_->big_num, a.impl_->big_num, p.impl_->big_num, m.impl_->big_num,
context.impl_->big_num_context);
LOG_IF(FATAL, result != 1);
}
void BigNum::gcd(BigNum &r, BigNum &a, BigNum &b, BigNumContext &context) {
int result = BN_gcd(r.impl_->big_num, a.impl_->big_num, b.impl_->big_num, context.impl_->big_num_context);
LOG_IF(FATAL, result != 1);
}
int BigNum::compare(const BigNum &a, const BigNum &b) {
return BN_cmp(a.impl_->big_num, b.impl_->big_num);
}
StringBuilder &operator<<(StringBuilder &sb, const BigNum &bn) {
return sb << bn.to_decimal();
}
} // namespace td
#endif

138
tdutils/td/utils/BigNum.h Normal file
View file

@ -0,0 +1,138 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#if TD_HAVE_OPENSSL
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
#include "td/utils/StringBuilder.h"
namespace td {
class BigNumContext {
public:
BigNumContext();
BigNumContext(const BigNumContext &other) = delete;
BigNumContext &operator=(const BigNumContext &other) = delete;
BigNumContext(BigNumContext &&other);
BigNumContext &operator=(BigNumContext &&other);
~BigNumContext();
private:
class Impl;
unique_ptr<Impl> impl_;
friend class BigNum;
};
class BigNum {
public:
BigNum();
BigNum(const BigNum &other);
BigNum &operator=(const BigNum &other);
BigNum(BigNum &&other);
BigNum &operator=(BigNum &&other);
~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);
static Result<BigNum> from_hex(CSlice str);
static BigNum from_raw(void *openssl_big_num);
void set_value(uint32 new_value);
void ensure_const_time();
int get_num_bits() const;
int get_num_bytes() const;
void set_bit(int num);
void clear_bit(int num);
bool is_bit_set(int num) const;
bool is_prime(BigNumContext &context) const;
BigNum clone() const;
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;
void operator+=(uint32 value);
void operator-=(uint32 value);
void operator*=(uint32 value);
void operator/=(uint32 value);
uint32 operator%(uint32 value) const;
static void random(BigNum &r, int bits, int top, int bottom);
static void add(BigNum &r, const BigNum &a, const BigNum &b);
static void sub(BigNum &r, const BigNum &a, const BigNum &b);
static void mul(BigNum &r, BigNum &a, BigNum &b, BigNumContext &context);
static void mod_add(BigNum &r, BigNum &a, BigNum &b, const BigNum &m, BigNumContext &context);
static void mod_sub(BigNum &r, BigNum &a, BigNum &b, const BigNum &m, BigNumContext &context);
static void mod_mul(BigNum &r, BigNum &a, BigNum &b, const BigNum &m, BigNumContext &context);
static void mod_inverse(BigNum &r, BigNum &a, const BigNum &m, BigNumContext &context);
static void div(BigNum *quotient, BigNum *remainder, const BigNum &dividend, const BigNum &divisor,
BigNumContext &context);
static void mod_exp(BigNum &r, const BigNum &a, const BigNum &p, const BigNum &m, BigNumContext &context);
static void gcd(BigNum &r, BigNum &a, BigNum &b, BigNumContext &context);
static int compare(const BigNum &a, const BigNum &b);
private:
class Impl;
unique_ptr<Impl> impl_;
explicit BigNum(unique_ptr<Impl> &&impl);
};
StringBuilder &operator<<(StringBuilder &sb, const BigNum &bn);
} // namespace td
#endif

View file

@ -0,0 +1,226 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/buffer.h"
#include "td/utils/common.h"
#include "td/utils/format.h"
#include "td/utils/logging.h"
#include "td/utils/port/detail/PollableFd.h"
#include "td/utils/port/IoSlice.h"
#include "td/utils/Slice.h"
#include "td/utils/Span.h"
#include "td/utils/Status.h"
#include <limits>
namespace td {
// just reads from given reader and writes to given writer
template <class FdT>
class BufferedFdBase : public FdT {
public:
BufferedFdBase() = default;
explicit BufferedFdBase(FdT &&fd_);
// TODO: make move constructor and move assignment safer
Result<size_t> flush_read(size_t max_read = std::numeric_limits<size_t>::max()) TD_WARN_UNUSED_RESULT;
Result<size_t> flush_write() TD_WARN_UNUSED_RESULT;
bool need_flush_write(size_t at_least = 0) {
CHECK(write_);
write_->sync_with_writer();
return write_->size() > at_least;
}
size_t ready_for_flush_write() {
CHECK(write_);
write_->sync_with_writer();
return write_->size();
}
void set_input_writer(ChainBufferWriter *read) {
read_ = read;
}
void set_output_reader(ChainBufferReader *write) {
write_ = write;
}
private:
ChainBufferWriter *read_ = nullptr;
ChainBufferReader *write_ = nullptr;
};
template <class FdT>
class BufferedFd : public BufferedFdBase<FdT> {
using Parent = BufferedFdBase<FdT>;
ChainBufferWriter input_writer_;
ChainBufferReader input_reader_;
ChainBufferWriter output_writer_;
ChainBufferReader output_reader_;
void init();
void init_ptr();
public:
BufferedFd();
explicit BufferedFd(FdT &&fd_);
BufferedFd(BufferedFd &&);
BufferedFd &operator=(BufferedFd &&);
BufferedFd(const BufferedFd &) = delete;
BufferedFd &operator=(const BufferedFd &) = delete;
~BufferedFd();
void close();
Result<size_t> flush_read(size_t max_read = std::numeric_limits<size_t>::max()) TD_WARN_UNUSED_RESULT;
Result<size_t> flush_write() TD_WARN_UNUSED_RESULT;
// Yep, direct access to buffers. It is IO interface too.
ChainBufferReader &input_buffer();
ChainBufferWriter &output_buffer();
};
// IMPLEMENTATION
/*** BufferedFd ***/
template <class FdT>
BufferedFdBase<FdT>::BufferedFdBase(FdT &&fd_) : FdT(std::move(fd_)) {
}
template <class FdT>
Result<size_t> BufferedFdBase<FdT>::flush_read(size_t max_read) {
CHECK(read_);
size_t result = 0;
while (::td::can_read(*this) && max_read) {
MutableSlice slice = read_->prepare_append().truncate(max_read);
TRY_RESULT(x, FdT::read(slice));
slice.truncate(x);
read_->confirm_append(x);
result += x;
max_read -= x;
}
return result;
}
template <class FdT>
Result<size_t> BufferedFdBase<FdT>::flush_write() {
// TODO: sync on demand
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];
auto it = write_->clone();
size_t buf_i;
for (buf_i = 0; buf_i < buf_size; buf_i++) {
Slice slice = it.prepare_read();
if (slice.empty()) {
break;
}
buf[buf_i] = as_io_slice(slice);
it.confirm_read(slice.size());
}
TRY_RESULT(x, FdT::writev(Span<IoSlice>(buf, buf_i)));
write_->advance(x);
result += x;
}
return result;
}
/*** BufferedFd ***/
template <class FdT>
void BufferedFd<FdT>::init() {
input_reader_ = input_writer_.extract_reader();
output_reader_ = output_writer_.extract_reader();
init_ptr();
}
template <class FdT>
void BufferedFd<FdT>::init_ptr() {
this->set_input_writer(&input_writer_);
this->set_output_reader(&output_reader_);
}
template <class FdT>
BufferedFd<FdT>::BufferedFd() {
init();
}
template <class FdT>
BufferedFd<FdT>::BufferedFd(FdT &&fd_) : Parent(std::move(fd_)) {
init();
}
template <class FdT>
BufferedFd<FdT>::BufferedFd(BufferedFd &&from) {
*this = std::move(from);
}
template <class FdT>
BufferedFd<FdT> &BufferedFd<FdT>::operator=(BufferedFd &&from) {
FdT::operator=(std::move(static_cast<FdT &>(from)));
input_reader_ = std::move(from.input_reader_);
input_writer_ = std::move(from.input_writer_);
output_reader_ = std::move(from.output_reader_);
output_writer_ = std::move(from.output_writer_);
init_ptr();
return *this;
}
template <class FdT>
BufferedFd<FdT>::~BufferedFd() {
close();
}
template <class FdT>
void BufferedFd<FdT>::close() {
FdT::close();
// TODO: clear buffers
}
template <class FdT>
Result<size_t> BufferedFd<FdT>::flush_read(size_t max_read) {
TRY_RESULT(result, Parent::flush_read(max_read));
if (result) {
// TODO: faster sync is possible if you owns writer.
input_reader_.sync_with_writer();
LOG(DEBUG) << "Flush read: +" << format::as_size(result) << tag("total", format::as_size(input_reader_.size()));
}
return result;
}
template <class FdT>
Result<size_t> BufferedFd<FdT>::flush_write() {
TRY_RESULT(result, Parent::flush_write());
if (result) {
LOG(DEBUG) << "Flush write: +" << format::as_size(result) << tag("left", format::as_size(output_reader_.size()));
}
return result;
}
// Yep, direct access to buffers. It is IO interface too.
template <class FdT>
ChainBufferReader &BufferedFd<FdT>::input_buffer() {
return input_reader_;
}
template <class FdT>
ChainBufferWriter &BufferedFd<FdT>::output_buffer() {
return output_writer_;
}
} // namespace td

View file

@ -0,0 +1,73 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include "td/utils/port/FileFd.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
namespace td {
class BufferedReader {
public:
explicit BufferedReader(FileFd &file, size_t buff_size = 8152)
: file_(file), buff_(buff_size), begin_pos_(0), end_pos_(0) {
}
Result<size_t> read(MutableSlice slice) TD_WARN_UNUSED_RESULT;
private:
FileFd &file_;
vector<char> buff_;
size_t begin_pos_;
size_t end_pos_;
};
inline Result<size_t> BufferedReader::read(MutableSlice slice) {
size_t available = end_pos_ - begin_pos_;
if (available >= slice.size()) {
// have enough data in buffer
slice.copy_from({&buff_[begin_pos_], slice.size()});
begin_pos_ += slice.size();
return slice.size();
}
if (available) {
slice.copy_from({&buff_[begin_pos_], available});
begin_pos_ += available;
slice.remove_prefix(available);
}
if (slice.size() > buff_.size() / 2) {
TRY_RESULT(result, file_.read(slice));
return result + available;
}
TRY_RESULT(result, file_.read({&buff_[0], buff_.size()}));
begin_pos_ = 0;
end_pos_ = result;
size_t left = min(end_pos_, slice.size());
slice.copy_from({&buff_[0], left});
begin_pos_ = left;
return left + available;
}
} // namespace td

View file

@ -0,0 +1,29 @@
/*
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-2019 Telegram Systems LLP
*/
#include "td/utils/BufferedUdp.h"
char disable_linker_warning_about_empty_file_buffered_udp_cpp TD_UNUSED;
namespace td {
#if TD_PORT_POSIX
TD_THREAD_LOCAL detail::UdpReader *BufferedUdp::udp_reader_;
#endif
} // namespace td

View file

@ -0,0 +1,184 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/buffer.h"
#include "td/utils/common.h"
#include "td/utils/logging.h"
#include "td/utils/optional.h"
#include "td/utils/port/detail/PollableFd.h"
#include "td/utils/port/thread_local.h"
#include "td/utils/port/UdpSocketFd.h"
#include "td/utils/Span.h"
#include "td/utils/Status.h"
#include "td/utils/VectorQueue.h"
#include <array>
namespace td {
#if TD_PORT_POSIX
namespace detail {
class UdpWriter {
public:
static Status write_once(UdpSocketFd &fd, VectorQueue<UdpMessage> &queue) TD_WARN_UNUSED_RESULT {
std::array<UdpSocketFd::OutboundMessage, 16> messages;
auto to_send = queue.as_span();
size_t to_send_n = td::min(messages.size(), to_send.size());
to_send.truncate(to_send_n);
for (size_t i = 0; i < to_send_n; i++) {
messages[i].to = &to_send[i].address;
messages[i].data = to_send[i].data.as_slice();
}
size_t cnt;
auto status = fd.send_messages(::td::Span<UdpSocketFd::OutboundMessage>(messages).truncate(to_send_n), cnt);
queue.pop_n(cnt);
return status;
}
};
class UdpReaderHelper {
public:
void init_inbound_message(UdpSocketFd::InboundMessage &message) {
message.from = &message_.address;
message.error = &message_.error;
if (buffer_.size() < MAX_PACKET_SIZE) {
buffer_ = BufferSlice(RESERVED_SIZE);
}
CHECK(buffer_.size() >= MAX_PACKET_SIZE);
message.data = buffer_.as_slice().truncate(MAX_PACKET_SIZE);
}
UdpMessage extract_udp_message(UdpSocketFd::InboundMessage &message) {
message_.data = buffer_.from_slice(message.data);
auto size = message_.data.size();
size = (size + 7) & ~7;
CHECK(size <= MAX_PACKET_SIZE);
buffer_.confirm_read(size);
return std::move(message_);
}
private:
enum : size_t { MAX_PACKET_SIZE = 2048, RESERVED_SIZE = MAX_PACKET_SIZE * 8 };
UdpMessage message_;
BufferSlice buffer_;
};
// One for thread is enough
class UdpReader {
public:
UdpReader() {
for (size_t i = 0; i < messages_.size(); i++) {
helpers_[i].init_inbound_message(messages_[i]);
}
}
Status read_once(UdpSocketFd &fd, VectorQueue<UdpMessage> &queue) TD_WARN_UNUSED_RESULT {
for (size_t i = 0; i < messages_.size(); i++) {
CHECK(messages_[i].data.size() == 2048);
}
size_t cnt = 0;
auto status = fd.receive_messages(messages_, cnt);
for (size_t i = 0; i < cnt; i++) {
queue.push(helpers_[i].extract_udp_message(messages_[i]));
helpers_[i].init_inbound_message(messages_[i]);
}
for (size_t i = cnt; i < messages_.size(); i++) {
LOG_CHECK(messages_[i].data.size() == 2048)
<< " cnt = " << cnt << " i = " << i << " size = " << messages_[i].data.size() << " status = " << status;
}
if (status.is_error() && !UdpSocketFd::is_critical_read_error(status)) {
queue.push(UdpMessage{{}, {}, std::move(status)});
}
return status;
}
private:
enum : size_t { BUFFER_SIZE = 16 };
std::array<UdpSocketFd::InboundMessage, BUFFER_SIZE> messages_;
std::array<UdpReaderHelper, BUFFER_SIZE> helpers_;
};
} // namespace detail
#endif
class BufferedUdp : public UdpSocketFd {
public:
explicit BufferedUdp(UdpSocketFd fd) : UdpSocketFd(std::move(fd)) {
}
#if TD_PORT_POSIX
Result<optional<UdpMessage>> receive() {
if (input_.empty() && can_read(*this)) {
TRY_STATUS(flush_read_once());
}
if (input_.empty()) {
return optional<UdpMessage>();
}
return input_.pop();
}
void send(UdpMessage message) {
output_.push(std::move(message));
}
Status flush_send() {
Status status;
while (status.is_ok() && can_write(*this) && !output_.empty()) {
status = flush_send_once();
}
return status;
}
#endif
UdpSocketFd move_as_udp_socket_fd() {
return std::move(as_fd());
}
UdpSocketFd &as_fd() {
return *static_cast<UdpSocketFd *>(this);
}
private:
#if TD_PORT_POSIX
VectorQueue<UdpMessage> input_;
VectorQueue<UdpMessage> output_;
VectorQueue<UdpMessage> &input() {
return input_;
}
VectorQueue<UdpMessage> &output() {
return output_;
}
Status flush_send_once() TD_WARN_UNUSED_RESULT {
return detail::UdpWriter::write_once(as_fd(), output_);
}
Status flush_read_once() TD_WARN_UNUSED_RESULT {
init_thread_local<detail::UdpReader>(udp_reader_);
return udp_reader_->read_once(as_fd(), input_);
}
static TD_THREAD_LOCAL detail::UdpReader *udp_reader_;
#endif
};
} // namespace td

303
tdutils/td/utils/ByteFlow.h Normal file
View file

@ -0,0 +1,303 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/buffer.h"
#include "td/utils/common.h"
#include "td/utils/Status.h"
namespace td {
class ByteFlowInterface {
public:
virtual void close_input(Status status) = 0;
virtual void wakeup() = 0;
virtual void set_parent(ByteFlowInterface &other) = 0;
virtual void set_input(ChainBufferReader *input) = 0;
virtual size_t get_need_size() = 0;
ByteFlowInterface() = default;
ByteFlowInterface(const ByteFlowInterface &) = delete;
ByteFlowInterface &operator=(const ByteFlowInterface &) = delete;
ByteFlowInterface(ByteFlowInterface &&) = default;
ByteFlowInterface &operator=(ByteFlowInterface &&) = default;
virtual ~ByteFlowInterface() = default;
};
class ByteFlowBaseCommon : public ByteFlowInterface {
public:
ByteFlowBaseCommon() = default;
void close_input(Status status) final {
if (status.is_error()) {
finish(std::move(status));
} else {
is_input_active_ = false;
wakeup();
}
}
void wakeup() final {
if (stop_flag_) {
return;
}
input_->sync_with_writer();
if (waiting_flag_) {
if (!is_input_active_) {
finish(Status::OK());
}
return;
}
if (is_input_active_) {
if (need_size_ != 0 && input_->size() < need_size_) {
return;
}
}
need_size_ = 0;
loop();
}
size_t get_need_size() final {
return need_size_;
}
virtual void loop() = 0;
protected:
bool waiting_flag_ = false;
ChainBufferReader *input_ = nullptr;
bool is_input_active_ = true;
size_t need_size_ = 0;
void finish(Status status) {
stop_flag_ = true;
need_size_ = 0;
if (parent_) {
parent_->close_input(std::move(status));
parent_ = nullptr;
}
}
void set_need_size(size_t need_size) {
need_size_ = need_size;
}
void on_output_updated() {
if (parent_) {
parent_->wakeup();
}
}
void consume_input() {
waiting_flag_ = true;
if (!is_input_active_) {
finish(Status::OK());
}
}
private:
ByteFlowInterface *parent_ = nullptr;
bool stop_flag_ = false;
friend class ByteFlowBase;
friend class ByteFlowInplaceBase;
};
class ByteFlowBase : public ByteFlowBaseCommon {
public:
ByteFlowBase() = default;
void set_input(ChainBufferReader *input) final {
input_ = input;
}
void set_parent(ByteFlowInterface &other) final {
parent_ = &other;
parent_->set_input(&output_reader_);
}
void loop() override = 0;
// ChainBufferWriter &get_output() {
// return output_;
//}
protected:
ChainBufferWriter output_;
ChainBufferReader output_reader_ = output_.extract_reader();
};
class ByteFlowInplaceBase : public ByteFlowBaseCommon {
public:
ByteFlowInplaceBase() = default;
void set_input(ChainBufferReader *input) final {
input_ = input;
output_ = ChainBufferReader(input_->begin().clone(), input_->begin().clone(), false);
}
void set_parent(ByteFlowInterface &other) final {
parent_ = &other;
parent_->set_input(&output_);
}
void loop() override = 0;
ChainBufferReader &get_output() {
return output_;
}
protected:
ChainBufferReader output_;
};
inline ByteFlowInterface &operator>>(ByteFlowInterface &from, ByteFlowInterface &to) {
from.set_parent(to);
return to;
}
class ByteFlowSource : public ByteFlowInterface {
public:
ByteFlowSource() = default;
explicit ByteFlowSource(ChainBufferReader *buffer) : buffer_(buffer) {
}
ByteFlowSource(ByteFlowSource &&other) : buffer_(other.buffer_), parent_(other.parent_) {
other.buffer_ = nullptr;
other.parent_ = nullptr;
}
ByteFlowSource &operator=(ByteFlowSource &&other) {
buffer_ = other.buffer_;
parent_ = other.parent_;
other.buffer_ = nullptr;
other.parent_ = nullptr;
return *this;
}
ByteFlowSource(const ByteFlowSource &) = delete;
ByteFlowSource &operator=(const ByteFlowSource &) = delete;
~ByteFlowSource() override = default;
void set_input(ChainBufferReader *) final {
UNREACHABLE();
}
void set_parent(ByteFlowInterface &parent) final {
CHECK(parent_ == nullptr);
parent_ = &parent;
parent_->set_input(buffer_);
}
void close_input(Status status) final {
CHECK(parent_);
parent_->close_input(std::move(status));
parent_ = nullptr;
}
void wakeup() final {
CHECK(parent_);
parent_->wakeup();
}
size_t get_need_size() final {
if (parent_ == nullptr) {
return 0;
}
return parent_->get_need_size();
}
private:
ChainBufferReader *buffer_ = nullptr;
ByteFlowInterface *parent_ = nullptr;
};
class ByteFlowSink : public ByteFlowInterface {
public:
void set_input(ChainBufferReader *input) final {
CHECK(buffer_ == nullptr);
buffer_ = input;
}
void set_parent(ByteFlowInterface & /*parent*/) final {
UNREACHABLE();
}
void close_input(Status status) final {
CHECK(active_);
active_ = false;
status_ = std::move(status);
buffer_->sync_with_writer();
}
void wakeup() final {
buffer_->sync_with_writer();
}
size_t get_need_size() final {
UNREACHABLE();
return 0;
}
bool is_ready() {
return !active_;
}
Status &status() {
return status_;
}
ChainBufferReader *result() {
CHECK(is_ready() && status().is_ok());
return buffer_;
}
ChainBufferReader *get_output() {
return buffer_;
}
private:
bool active_ = true;
Status status_;
ChainBufferReader *buffer_ = nullptr;
};
class ByteFlowMoveSink : public ByteFlowInterface {
public:
ByteFlowMoveSink() = default;
explicit ByteFlowMoveSink(ChainBufferWriter *output) {
set_output(output);
}
void set_input(ChainBufferReader *input) final {
CHECK(!input_);
input_ = input;
}
void set_parent(ByteFlowInterface & /*parent*/) final {
UNREACHABLE();
}
void close_input(Status status) final {
CHECK(active_);
active_ = false;
status_ = std::move(status);
wakeup();
}
void wakeup() final {
input_->sync_with_writer();
output_->append(*input_);
}
size_t get_need_size() final {
UNREACHABLE();
return 0;
}
void set_output(ChainBufferWriter *output) {
CHECK(!output_);
output_ = output;
}
bool is_ready() {
return !active_;
}
Status &status() {
return status_;
}
private:
bool active_ = true;
Status status_;
ChainBufferReader *input_ = nullptr;
ChainBufferWriter *output_ = nullptr;
};
} // namespace td

View file

@ -0,0 +1,78 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include <atomic>
#include <memory>
namespace td {
namespace detail {
struct RawCancellationToken {
std::atomic<bool> is_cancelled_{false};
};
} // namespace detail
class CancellationToken {
public:
explicit operator bool() const {
return token_->is_cancelled_.load(std::memory_order_acquire);
}
explicit CancellationToken(std::shared_ptr<detail::RawCancellationToken> token) : token_(std::move(token)) {
}
private:
std::shared_ptr<detail::RawCancellationToken> token_;
};
class CancellationTokenSource {
public:
CancellationTokenSource() = default;
CancellationTokenSource(CancellationTokenSource &&other) : token_(std::move(other.token_)) {
}
CancellationTokenSource &operator=(CancellationTokenSource &&other) {
cancel();
token_ = std::move(other.token_);
return *this;
}
CancellationTokenSource(const CancellationTokenSource &other) = delete;
CancellationTokenSource &operator=(const CancellationTokenSource &other) = delete;
~CancellationTokenSource() {
cancel();
}
CancellationToken get_cancellation_token() {
if (!token_) {
token_ = std::make_shared<detail::RawCancellationToken>();
}
return CancellationToken(token_);
}
void cancel() {
if (!token_) {
return;
}
token_->is_cancelled_.store(true, std::memory_order_release);
token_.reset();
}
private:
std::shared_ptr<detail::RawCancellationToken> token_;
};
} // namespace td

View file

@ -0,0 +1,73 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include <utility>
namespace td {
// Process changes after they are finished in order of addition
template <class DataT>
class ChangesProcessor {
public:
using Id = uint64;
void clear() {
offset_ += data_array_.size();
ready_i_ = 0;
data_array_.clear();
}
template <class FromDataT>
Id add(FromDataT &&data) {
auto res = offset_ + data_array_.size();
data_array_.emplace_back(std::forward<DataT>(data), false);
return static_cast<Id>(res);
}
template <class F>
void finish(Id token, F &&func) {
size_t pos = static_cast<size_t>(token) - offset_;
if (pos >= data_array_.size()) {
return;
}
data_array_[pos].second = true;
while (ready_i_ < data_array_.size() && data_array_[ready_i_].second == true) {
func(std::move(data_array_[ready_i_].first));
ready_i_++;
}
try_compactify();
}
private:
size_t offset_ = 1;
size_t ready_i_ = 0;
std::vector<std::pair<DataT, bool>> data_array_;
void try_compactify() {
if (ready_i_ > 5 && ready_i_ * 2 > data_array_.size()) {
data_array_.erase(data_array_.begin(), data_array_.begin() + ready_i_);
offset_ += ready_i_;
ready_i_ = 0;
}
}
};
} // namespace td

180
tdutils/td/utils/Closure.h Normal file
View file

@ -0,0 +1,180 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include "td/utils/invoke.h"
#include "td/utils/logging.h"
#include <cstdlib>
#include <tuple>
#include <type_traits>
#include <utility>
//
// Essentially we have:
// (ActorT::func, arg1, arg2, ..., argn)
// We want to call:
// actor->func(arg1, arg2, ..., argn)
// And in some cases we would like to delay this call.
//
// First attempt would be
// [a1=arg1, a2=arg2, ..., an=argn](ActorT *actor) {
// actor->func(a1, a2, ..., an)
// }
//
// But there are some difficulties with elimitation on unnecessary copies.
// We want to use move constructor when it is possible
//
// We may pass
// Tmp. Temporary / rvalue reference
// Var. Variable / reference
// CnstRef. const reference
//
//
// Function may expect
// Val. Value
// CnstRef. const reference
// Ref. rvalue reverence / reference
//
// TODO:
// Immediate call / Delayed call
// Tmp->Val move / move->move
// Tmp->CnstRef + / move->+
// Tmp->Ref + / move->+
// Var->Val copy / copy->move
// Var->CnstRef + / copy->
// Var->Ref + / copy->+ // khm. It will complile, but won't work
//
// So I will use common idiom: forward references
// If delay is needed, just std::forward data to temporary storage, and std::move them when call is executed.
//
//
// create_immediate_closure(&ActorT::func, arg1, arg2, ..., argn).run(actor)
// to_delayed_closure(std::move(immediate)).run(actor)
namespace td {
template <class ActorT, class FunctionT, class... ArgsT>
class DelayedClosure;
template <class ActorT, class FunctionT, class... ArgsT>
class ImmediateClosure {
public:
using Delayed = DelayedClosure<ActorT, FunctionT, ArgsT...>;
friend Delayed;
using ActorType = ActorT;
// no &&. just save references as references.
explicit ImmediateClosure(FunctionT func, ArgsT... args) : args(func, std::forward<ArgsT>(args)...) {
}
private:
std::tuple<FunctionT, ArgsT...> args;
public:
auto run(ActorT *actor) -> decltype(mem_call_tuple(actor, std::move(args))) {
return mem_call_tuple(actor, std::move(args));
}
};
template <class ActorT, class ResultT, class... DestArgsT, class... SrcArgsT>
ImmediateClosure<ActorT, ResultT (ActorT::*)(DestArgsT...), SrcArgsT &&...> create_immediate_closure(
ResultT (ActorT::*func)(DestArgsT...), SrcArgsT &&... args) {
return ImmediateClosure<ActorT, ResultT (ActorT::*)(DestArgsT...), SrcArgsT &&...>(func,
std::forward<SrcArgsT>(args)...);
}
template <class ActorT, class FunctionT, class... ArgsT>
class DelayedClosure {
public:
using ActorType = ActorT;
using Delayed = DelayedClosure<ActorT, FunctionT, ArgsT...>;
DelayedClosure clone() const {
return do_clone(*this);
}
explicit DelayedClosure(ImmediateClosure<ActorT, FunctionT, ArgsT...> &&other) : args(std::move(other.args)) {
}
explicit DelayedClosure(FunctionT func, ArgsT... args) : args(func, std::forward<ArgsT>(args)...) {
}
template <class F>
void for_each(const F &f) {
tuple_for_each(args, f);
}
private:
using ArgsStorageT = std::tuple<FunctionT, typename std::decay<ArgsT>::type...>;
ArgsStorageT args;
template <class FromActorT, class FromFunctionT, class... FromArgsT>
explicit DelayedClosure(const DelayedClosure<FromActorT, FromFunctionT, FromArgsT...> &other,
std::enable_if_t<LogicAnd<std::is_copy_constructible<FromArgsT>::value...>::value, int> = 0)
: args(other.args) {
}
template <class FromActorT, class FromFunctionT, class... FromArgsT>
explicit DelayedClosure(
const DelayedClosure<FromActorT, FromFunctionT, FromArgsT...> &other,
std::enable_if_t<!LogicAnd<std::is_copy_constructible<FromArgsT>::value...>::value, int> = 0) {
LOG(FATAL) << "Deleted constructor";
std::abort();
}
template <class FromActorT, class FromFunctionT, class... FromArgsT>
std::enable_if_t<!LogicAnd<std::is_copy_constructible<FromArgsT>::value...>::value,
DelayedClosure<FromActorT, FromFunctionT, FromArgsT...>>
do_clone(const DelayedClosure<FromActorT, FromFunctionT, FromArgsT...> &value) const {
LOG(FATAL) << "Trying to clone DelayedClosure that contains noncopyable elements";
std::abort();
}
template <class FromActorT, class FromFunctionT, class... FromArgsT>
std::enable_if_t<LogicAnd<std::is_copy_constructible<FromArgsT>::value...>::value,
DelayedClosure<FromActorT, FromFunctionT, FromArgsT...>>
do_clone(const DelayedClosure<FromActorT, FromFunctionT, FromArgsT...> &value) const {
return DelayedClosure<FromActorT, FromFunctionT, FromArgsT...>(value);
}
public:
auto run(ActorT *actor) -> decltype(mem_call_tuple(actor, std::move(args))) {
return mem_call_tuple(actor, std::move(args));
}
};
template <class... ArgsT>
typename ImmediateClosure<ArgsT...>::Delayed to_delayed_closure(ImmediateClosure<ArgsT...> &&other) {
return typename ImmediateClosure<ArgsT...>::Delayed(std::move(other));
}
template <class... ArgsT>
DelayedClosure<ArgsT...> to_delayed_closure(DelayedClosure<ArgsT...> &&other) {
return std::move(other);
}
template <class ActorT, class ResultT, class... DestArgsT, class... SrcArgsT>
auto create_delayed_closure(ResultT (ActorT::*func)(DestArgsT...), SrcArgsT &&... args) {
return DelayedClosure<ActorT, ResultT (ActorT::*)(DestArgsT...), SrcArgsT &&...>(func,
std::forward<SrcArgsT>(args)...);
}
} // namespace td

View file

@ -0,0 +1,334 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include "td/utils/HazardPointers.h"
#include "td/utils/logging.h"
#include "td/utils/port/thread_local.h"
#include <atomic>
#include <condition_variable>
#include <mutex>
namespace td {
// AtomicHashArray<KeyT, ValueT>
// Building block for other concurrent hash maps
//
// Support one operation:
// template <class F>
// bool with_value(KeyT key, bool should_create, F &&func);
//
// Finds slot for key, and call func(value)
// Creates slot if should_create is true.
// Returns true if func was called.
//
// Concurrent calls with the same key may result in concurrent calls to func(value)
// It is responsibility of the caller to handle such races.
//
// Key should already be random
// It is responsibility of the caller to provide unique random key.
// One may use injective hash function, or handle collisions in some other way.
template <class KeyT, class ValueT>
class AtomicHashArray {
public:
explicit AtomicHashArray(size_t n) : nodes_(n) {
}
struct Node {
std::atomic<KeyT> key{KeyT{}};
ValueT value{};
};
size_t size() const {
return nodes_.size();
}
Node &node_at(size_t i) {
return nodes_[i];
}
static KeyT empty_key() {
return KeyT{};
}
template <class F>
bool with_value(KeyT key, bool should_create, F &&f) {
DCHECK(key != empty_key());
size_t pos = static_cast<size_t>(key) % nodes_.size();
size_t n = td::min(td::max(static_cast<size_t>(300), nodes_.size() / 16 + 2), nodes_.size());
for (size_t i = 0; i < n; i++) {
pos++;
if (pos >= nodes_.size()) {
pos = 0;
}
auto &node = nodes_[pos];
while (true) {
auto node_key = node.key.load(std::memory_order_acquire);
if (node_key == empty_key()) {
if (!should_create) {
return false;
}
KeyT expected_key = empty_key();
if (node.key.compare_exchange_strong(expected_key, key, std::memory_order_relaxed,
std::memory_order_relaxed)) {
f(node.value);
return true;
}
} else if (node_key == key) {
f(node.value);
return true;
} else {
break;
}
}
}
return false;
}
private:
std::vector<Node> nodes_;
};
// Simple concurrent hash map with multiple limitations
template <class KeyT, class ValueT>
class ConcurrentHashMap {
using HashMap = AtomicHashArray<KeyT, std::atomic<ValueT>>;
static HazardPointers<HashMap> hp_;
public:
explicit ConcurrentHashMap(size_t n = 32) {
n = 1;
hash_map_.store(make_unique<HashMap>(n).release());
}
ConcurrentHashMap(const ConcurrentHashMap &) = delete;
ConcurrentHashMap &operator=(const ConcurrentHashMap &) = delete;
ConcurrentHashMap(ConcurrentHashMap &&) = delete;
ConcurrentHashMap &operator=(ConcurrentHashMap &&) = delete;
~ConcurrentHashMap() {
unique_ptr<HashMap>(hash_map_.load());
}
static std::string get_name() {
return "ConcurrrentHashMap";
}
static KeyT empty_key() {
return KeyT{};
}
static ValueT empty_value() {
return ValueT{};
}
static ValueT migrate_value() {
return (ValueT)(1); // c-style conversion because reinterpret_cast<int>(1) is CE in MSVC
}
ValueT insert(KeyT key, ValueT value) {
CHECK(key != empty_key());
CHECK(value != migrate_value());
typename HazardPointers<HashMap>::Holder holder(hp_, get_thread_id(), 0);
while (true) {
auto hash_map = holder.protect(hash_map_);
if (!hash_map) {
do_migrate(nullptr);
continue;
}
bool ok = false;
ValueT inserted_value;
hash_map->with_value(key, true, [&](auto &node_value) {
ValueT expected_value = this->empty_value();
if (node_value.compare_exchange_strong(expected_value, value, std::memory_order_release,
std::memory_order_acquire)) {
ok = true;
inserted_value = value;
} else {
if (expected_value == this->migrate_value()) {
ok = false;
} else {
ok = true;
inserted_value = expected_value;
}
}
});
if (ok) {
return inserted_value;
}
do_migrate(hash_map);
}
}
ValueT find(KeyT key, ValueT value) {
typename HazardPointers<HashMap>::Holder holder(hp_, get_thread_id(), 0);
while (true) {
auto hash_map = holder.protect(hash_map_);
if (!hash_map) {
do_migrate(nullptr);
continue;
}
bool has_value = hash_map->with_value(
key, false, [&](auto &node_value) { value = node_value.load(std::memory_order_acquire); });
if (!has_value || value != migrate_value()) {
return value;
}
do_migrate(hash_map);
}
}
template <class F>
void for_each(F &&f) {
auto hash_map = hash_map_.load();
CHECK(hash_map);
auto size = hash_map->size();
for (size_t i = 0; i < size; i++) {
auto &node = hash_map->node_at(i);
auto key = node.key.load(std::memory_order_relaxed);
auto value = node.value.load(std::memory_order_relaxed);
if (key != empty_key()) {
CHECK(value != migrate_value());
if (value != empty_value()) {
f(key, value);
}
}
}
}
private:
// use no padding intentionally
std::atomic<HashMap *> hash_map_{nullptr};
std::mutex migrate_mutex_;
std::condition_variable migrate_cv_;
int migrate_cnt_{0};
int migrate_generation_{0};
HashMap *migrate_from_hash_map_{nullptr};
HashMap *migrate_to_hash_map_{nullptr};
struct Task {
size_t begin;
size_t end;
bool empty() const {
return begin >= end;
}
size_t size() const {
if (empty()) {
return 0;
}
return end - begin;
}
};
struct TaskCreator {
size_t chunk_size;
size_t size;
std::atomic<size_t> pos{0};
Task create() {
auto i = pos++;
auto begin = i * chunk_size;
auto end = begin + chunk_size;
if (end > size) {
end = size;
}
return {begin, end};
}
};
TaskCreator task_creator;
void do_migrate(HashMap *ptr) {
//LOG(ERROR) << "In do_migrate: " << ptr;
std::unique_lock<std::mutex> lock(migrate_mutex_);
if (hash_map_.load() != ptr) {
return;
}
init_migrate();
CHECK(!ptr || migrate_from_hash_map_ == ptr);
migrate_cnt_++;
auto migrate_generation = migrate_generation_;
lock.unlock();
run_migrate();
lock.lock();
migrate_cnt_--;
if (migrate_cnt_ == 0) {
finish_migrate();
}
migrate_cv_.wait(lock, [&] { return migrate_generation_ != migrate_generation; });
}
void finish_migrate() {
//LOG(ERROR) << "In finish_migrate";
hash_map_.store(migrate_to_hash_map_);
hp_.retire(get_thread_id(), migrate_from_hash_map_);
migrate_from_hash_map_ = nullptr;
migrate_to_hash_map_ = nullptr;
migrate_generation_++;
migrate_cv_.notify_all();
}
void init_migrate() {
if (migrate_from_hash_map_ != nullptr) {
return;
}
//LOG(ERROR) << "In init_migrate";
CHECK(migrate_cnt_ == 0);
migrate_generation_++;
migrate_from_hash_map_ = hash_map_.exchange(nullptr);
auto new_size = migrate_from_hash_map_->size() * 2;
migrate_to_hash_map_ = make_unique<HashMap>(new_size).release();
task_creator.chunk_size = 100;
task_creator.size = migrate_from_hash_map_->size();
task_creator.pos = 0;
}
void run_migrate() {
//LOG(ERROR) << "In run_migrate";
size_t cnt = 0;
while (true) {
auto task = task_creator.create();
cnt += task.size();
if (task.empty()) {
break;
}
run_task(task);
}
//LOG(ERROR) << "In run_migrate " << cnt;
}
void run_task(Task task) {
for (auto i = task.begin; i < task.end; i++) {
auto &node = migrate_from_hash_map_->node_at(i);
auto old_value = node.value.exchange(migrate_value(), std::memory_order_acq_rel);
if (old_value == 0) {
continue;
}
auto node_key = node.key.load(std::memory_order_relaxed);
//LOG(ERROR) << node_key << " " << node_key;
auto ok = migrate_to_hash_map_->with_value(
node_key, true, [&](auto &node_value) { node_value.store(old_value, std::memory_order_relaxed); });
LOG_CHECK(ok) << "Migration overflow";
}
}
};
template <class KeyT, class ValueT>
td::HazardPointers<typename ConcurrentHashMap<KeyT, ValueT>::HashMap> ConcurrentHashMap<KeyT, ValueT>::hp_(64);
} // namespace td

View file

@ -0,0 +1,160 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include <limits>
namespace td {
// 1. Allocates all objects in vector. (but vector never shrinks)
// 2. Id is safe way to reach this object.
// 3. All ids are unique.
// 4. All ids are non-zero.
template <class DataT>
class Container {
public:
using Id = uint64;
DataT *get(Id id) {
int32 slot_id = decode_id(id);
if (slot_id == -1) {
return nullptr;
}
return &slots_[slot_id].data;
}
void erase(Id id) {
int32 slot_id = decode_id(id);
if (slot_id == -1) {
return;
}
release(slot_id);
}
DataT extract(Id id) {
int32 slot_id = decode_id(id);
CHECK(slot_id != -1);
auto res = std::move(slots_[slot_id].data);
release(slot_id);
return res;
}
Id create(DataT &&data = DataT(), uint8 type = 0) {
int32 id = store(std::move(data), type);
return encode_id(id);
}
Id reset_id(Id id) {
int32 slot_id = decode_id(id);
CHECK(slot_id != -1);
inc_generation(slot_id);
return encode_id(slot_id);
}
static uint8 type_from_id(Id id) {
return static_cast<uint8>(id);
}
vector<Id> ids() {
vector<bool> is_bad(slots_.size(), false);
for (auto id : empty_slots_) {
is_bad[id] = true;
}
vector<Id> res;
for (size_t i = 0, n = slots_.size(); i < n; i++) {
if (!is_bad[i]) {
res.push_back(encode_id(static_cast<int32>(i)));
}
}
return res;
}
template <class F>
void for_each(const F &f) {
auto ids = this->ids();
for (auto id : ids) {
f(id, *get(id));
}
}
size_t size() const {
CHECK(empty_slots_.size() <= slots_.size());
return slots_.size() - empty_slots_.size();
}
bool empty() const {
return size() == 0;
}
void clear() {
*this = Container<DataT>();
}
private:
static constexpr uint32 GENERATION_STEP = 1 << 8;
static constexpr uint32 TYPE_MASK = (1 << 8) - 1;
struct Slot {
uint32 generation;
DataT data;
};
vector<Slot> slots_;
vector<int32> empty_slots_;
Id encode_id(int32 id) const {
return (static_cast<uint64>(id) << 32) | slots_[id].generation;
}
int32 decode_id(Id id) const {
int32 slot_id = static_cast<int32>(id >> 32);
uint32 generation = static_cast<uint32>(id);
if (slot_id < 0 || slot_id >= static_cast<int32>(slots_.size())) {
return -1;
}
if (generation != slots_[slot_id].generation) {
return -1;
}
return slot_id;
}
int32 store(DataT &&data, uint8 type) {
int32 pos;
if (!empty_slots_.empty()) {
pos = empty_slots_.back();
empty_slots_.pop_back();
slots_[pos].data = std::move(data);
slots_[pos].generation ^= (slots_[pos].generation & TYPE_MASK) ^ type;
} else {
CHECK(slots_.size() <= static_cast<size_t>(std::numeric_limits<int32>::max()));
pos = static_cast<int32>(slots_.size());
slots_.push_back(Slot{GENERATION_STEP + type, std::move(data)});
}
return pos;
}
void release(int32 id) {
inc_generation(id);
slots_[id].data = DataT();
if (slots_[id].generation & ~TYPE_MASK) { // generation overflow. Can't use this id anymore
empty_slots_.push_back(id);
}
}
void inc_generation(int32 id) {
slots_[id].generation += GENERATION_STEP;
}
};
} // namespace td

View file

@ -0,0 +1,56 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/port/thread_local.h"
namespace td {
template <class Impl>
class Context {
public:
static Impl *get() {
return context_;
}
class Guard {
public:
explicit Guard(Impl *new_context) {
old_context_ = context_;
context_ = new_context;
}
~Guard() {
context_ = old_context_;
}
Guard(const Guard &) = delete;
Guard &operator=(const Guard &) = delete;
Guard(Guard &&) = delete;
Guard &operator=(Guard &&) = delete;
private:
Impl *old_context_;
};
private:
static TD_THREAD_LOCAL Impl *context_;
};
template <class Impl>
TD_THREAD_LOCAL Impl *Context<Impl>::context_;
} // namespace td

228
tdutils/td/utils/DecTree.h Normal file
View file

@ -0,0 +1,228 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include "td/utils/Random.h"
#include <functional>
#include <utility>
namespace td {
template <typename KeyType, typename ValueType, typename Compare = std::less<KeyType>>
class DecTree {
struct Node {
unique_ptr<Node> left_;
unique_ptr<Node> right_;
size_t size_;
KeyType key_;
ValueType value_;
uint32 y_;
void relax() {
size_ = 1;
if (left_ != nullptr) {
size_ += left_->size_;
}
if (right_ != nullptr) {
size_ += right_->size_;
}
}
Node(KeyType key, ValueType value, uint32 y) : size_(1), key_(std::move(key)), value_(std::move(value)), y_(y) {
}
};
unique_ptr<Node> root_;
static unique_ptr<Node> create_node(KeyType key, ValueType value, uint32 y) {
return make_unique<Node>(std::move(key), std::move(value), y);
}
static unique_ptr<Node> insert_node(unique_ptr<Node> Tree, KeyType key, ValueType value, uint32 y) {
if (Tree == nullptr) {
return create_node(std::move(key), std::move(value), y);
}
if (Tree->y_ < y) {
auto P = split_node(std::move(Tree), key);
auto T = create_node(std::move(key), std::move(value), y);
T->left_ = std::move(P.first);
T->right_ = std::move(P.second);
T->relax();
return T;
}
if (Compare()(key, Tree->key_)) {
Tree->left_ = insert_node(std::move(Tree->left_), std::move(key), std::move(value), y);
} else if (Compare()(Tree->key_, key)) {
Tree->right_ = insert_node(std::move(Tree->right_), std::move(key), std::move(value), y);
} else {
// ?? assert
}
Tree->relax();
return Tree;
}
static unique_ptr<Node> remove_node(unique_ptr<Node> Tree, const KeyType &key) {
if (Tree == nullptr) {
// ?? assert
return nullptr;
}
if (Compare()(key, Tree->key_)) {
Tree->left_ = remove_node(std::move(Tree->left_), key);
} else if (Compare()(Tree->key_, key)) {
Tree->right_ = remove_node(std::move(Tree->right_), key);
} else {
Tree = merge_node(std::move(Tree->left_), std::move(Tree->right_));
}
if (Tree != nullptr) {
Tree->relax();
}
return Tree;
}
static ValueType *get_node(unique_ptr<Node> &Tree, const KeyType &key) {
if (Tree == nullptr) {
return nullptr;
}
if (Compare()(key, Tree->key_)) {
return get_node(Tree->left_, key);
} else if (Compare()(Tree->key_, key)) {
return get_node(Tree->right_, key);
} else {
return &Tree->value_;
}
}
static ValueType *get_node_by_idx(unique_ptr<Node> &Tree, size_t idx) {
CHECK(Tree != nullptr);
auto s = (Tree->left_ != nullptr) ? Tree->left_->size_ : 0;
if (idx < s) {
return get_node_by_idx(Tree->left_, idx);
} else if (idx == s) {
return &Tree->value_;
} else {
return get_node_by_idx(Tree->right_, idx - s - 1);
}
}
static const ValueType *get_node(const unique_ptr<Node> &Tree, const KeyType &key) {
if (Tree == nullptr) {
return nullptr;
}
if (Compare()(key, Tree->key_)) {
return get_node(Tree->left_, key);
} else if (Compare()(Tree->key_, key)) {
return get_node(Tree->right_, key);
} else {
return &Tree->value_;
}
}
static const ValueType *get_node_by_idx(const unique_ptr<Node> &Tree, size_t idx) {
CHECK(Tree != nullptr);
auto s = (Tree->left_ != nullptr) ? Tree->left_->size_ : 0;
if (idx < s) {
return get_node_by_idx(Tree->left_, idx);
} else if (idx == s) {
return &Tree->value_;
} else {
return get_node_by_idx(Tree->right_, idx - s - 1);
}
}
static std::pair<unique_ptr<Node>, unique_ptr<Node>> split_node(unique_ptr<Node> Tree, const KeyType &key) {
if (Tree == nullptr) {
return {nullptr, nullptr};
}
if (Compare()(key, Tree->key_)) {
auto P = split_node(std::move(Tree->left_), key);
Tree->left_ = std::move(P.second);
Tree->relax();
P.second = std::move(Tree);
return P;
} else {
auto P = split_node(std::move(Tree->right_), key);
Tree->right_ = std::move(P.first);
Tree->relax();
P.first = std::move(Tree);
return P;
}
}
static unique_ptr<Node> merge_node(unique_ptr<Node> left, unique_ptr<Node> right) {
if (left == nullptr) {
return right;
}
if (right == nullptr) {
return left;
}
if (left->y_ < right->y_) {
right->left_ = merge_node(std::move(left), std::move(right->left_));
right->relax();
return right;
} else {
left->right_ = merge_node(std::move(left->right_), std::move(right));
left->relax();
return left;
}
}
public:
size_t size() const {
if (root_ == nullptr) {
return 0;
} else {
return root_->size_;
}
}
void insert(KeyType key, ValueType value) {
root_ = insert_node(std::move(root_), std::move(key), std::move(value), td::Random::fast_uint32());
}
void remove(const KeyType &key) {
root_ = remove_node(std::move(root_), key);
}
void reset() {
root_ = nullptr;
}
ValueType *get(const KeyType &key) {
return get_node(root_, key);
}
ValueType *get_random() {
if (size() == 0) {
return nullptr;
} else {
return get_node_by_idx(root_, td::Random::fast_uint32() % size());
}
}
const ValueType *get(const KeyType &key) const {
return get_node(root_, key);
}
const ValueType *get_random() const {
if (size() == 0) {
return nullptr;
} else {
return get_node_by_idx(root_, td::Random::fast_uint32() % size());
}
}
bool exists(const KeyType &key) const {
return get_node(root_, key) != nullptr;
}
};
} // namespace td

View file

@ -0,0 +1,64 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include <memory>
#include <utility>
namespace td {
class Destructor {
public:
Destructor() = default;
Destructor(const Destructor &other) = delete;
Destructor &operator=(const Destructor &other) = delete;
Destructor(Destructor &&other) = default;
Destructor &operator=(Destructor &&other) = default;
virtual ~Destructor() = default;
};
template <class F>
class LambdaDestructor : public Destructor {
public:
explicit LambdaDestructor(F &&f) : f_(std::move(f)) {
}
LambdaDestructor(const LambdaDestructor &other) = delete;
LambdaDestructor &operator=(const LambdaDestructor &other) = delete;
LambdaDestructor(LambdaDestructor &&other) = default;
LambdaDestructor &operator=(LambdaDestructor &&other) = default;
~LambdaDestructor() override {
f_();
}
private:
F f_;
};
template <class F>
auto create_destructor(F &&f) {
return make_unique<LambdaDestructor<F>>(std::forward<F>(f));
}
template <class F>
auto create_shared_destructor(F &&f) {
return std::make_shared<LambdaDestructor<F>>(std::forward<F>(f));
}
} // namespace td

View file

@ -0,0 +1,56 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include "td/utils/misc.h"
#include <map>
#include <tuple>
namespace td {
template <class ValueT>
class Enumerator {
public:
using Key = int32;
Key add(ValueT v) {
int32 next_id = narrow_cast<int32>(arr_.size() + 1);
bool was_inserted;
decltype(map_.begin()) it;
std::tie(it, was_inserted) = map_.emplace(std::move(v), next_id);
if (was_inserted) {
arr_.push_back(&it->first);
}
return it->second;
}
const ValueT &get(Key key) const {
auto pos = static_cast<size_t>(key - 1);
CHECK(pos < arr_.size());
return *arr_[pos];
}
private:
std::map<ValueT, int32> map_;
std::vector<const ValueT *> arr_;
};
} // namespace td

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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include "td/utils/logging.h"
#include "td/utils/port/sleep.h"
#include <atomic>
#include <memory>
namespace td {
template <class T>
class EpochBasedMemoryReclamation {
public:
EpochBasedMemoryReclamation(const EpochBasedMemoryReclamation &other) = delete;
EpochBasedMemoryReclamation &operator=(const EpochBasedMemoryReclamation &other) = delete;
EpochBasedMemoryReclamation(EpochBasedMemoryReclamation &&other) = delete;
EpochBasedMemoryReclamation &operator=(EpochBasedMemoryReclamation &&other) = delete;
~EpochBasedMemoryReclamation() = default;
class Locker {
public:
Locker(size_t thread_id, EpochBasedMemoryReclamation *ebmr) : thread_id_(thread_id), ebmr_(ebmr) {
}
Locker(const Locker &other) = delete;
Locker &operator=(const Locker &other) = delete;
Locker(Locker &&other) = default;
Locker &operator=(Locker &&other) = delete;
~Locker() {
if (ebmr_) {
retire_sync();
unlock();
ebmr_.release();
}
}
void lock() {
DCHECK(ebmr_);
ebmr_->lock(thread_id_);
}
void unlock() {
DCHECK(ebmr_);
ebmr_->unlock(thread_id_);
}
void retire_sync() {
ebmr_->retire_sync(thread_id_);
}
void retire() {
ebmr_->retire(thread_id_);
}
void retire(T *ptr) {
ebmr_->retire(thread_id_, ptr);
}
private:
size_t thread_id_;
struct Never {
template <class S>
void operator()(S *) const {
UNREACHABLE();
}
};
std::unique_ptr<EpochBasedMemoryReclamation, Never> ebmr_;
};
explicit EpochBasedMemoryReclamation(size_t threads_n) : threads_(threads_n) {
}
Locker get_locker(size_t thread_id) {
return Locker{thread_id, this};
}
size_t to_delete_size_unsafe() const {
size_t res = 0;
for (auto &thread_data : threads_) {
// LOG(ERROR) << "---" << thread_data.epoch.load() / 2;
for (size_t i = 0; i < MAX_BAGS; i++) {
res += thread_data.to_delete[i].size();
// LOG(ERROR) << thread_data.to_delete[i].size();
}
}
return res;
}
private:
static constexpr size_t MAX_BAGS = 3;
struct ThreadData {
std::atomic<int64> epoch{1};
char pad[TD_CONCURRENCY_PAD - sizeof(epoch)];
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)];
void rotate_bags() {
bag_i = (bag_i + 1) % MAX_BAGS;
to_delete[bag_i].clear();
}
void set_epoch(int64 new_epoch) {
//LOG(ERROR) << new_epoch;
if (epoch.load(std::memory_order_relaxed) / 2 != new_epoch) {
checked_thread_i = 0;
to_skip = 0;
rotate_bags();
}
epoch = new_epoch * 2;
}
void idle() {
epoch.store(epoch.load(std::memory_order_relaxed) | 1);
}
size_t undeleted() const {
size_t res = 0;
for (size_t i = 0; i < MAX_BAGS; i++) {
res += to_delete[i].size();
}
return res;
}
};
std::vector<ThreadData> threads_;
char pad[TD_CONCURRENCY_PAD - sizeof(threads_)];
std::atomic<int64> epoch_{1};
char pad2[TD_CONCURRENCY_PAD - sizeof(epoch_)];
void lock(size_t thread_id) {
auto &data = threads_[thread_id];
auto epoch = epoch_.load();
data.set_epoch(epoch);
if (data.to_skip == 0) {
data.to_skip = 30;
step_check(data);
} else {
data.to_skip--;
}
}
void unlock(size_t thread_id) {
//LOG(ERROR) << "UNLOCK";
auto &data = threads_[thread_id];
data.idle();
}
bool step_check(ThreadData &data) {
auto epoch = data.epoch.load(std::memory_order_relaxed) / 2;
auto checked_thread_epoch = threads_[data.checked_thread_i].epoch.load();
if (checked_thread_epoch % 2 == 1 || checked_thread_epoch / 2 == epoch) {
data.checked_thread_i++;
if (data.checked_thread_i == threads_.size()) {
if (epoch_.compare_exchange_strong(epoch, epoch + 1)) {
data.set_epoch(epoch + 1);
} else {
data.set_epoch(epoch);
}
}
return true;
}
return false;
}
void retire_sync(size_t thread_id) {
auto &data = threads_[thread_id];
while (true) {
retire(thread_id);
data.idle();
if (data.undeleted() == 0) {
break;
}
usleep_for(1000);
}
}
void retire(size_t thread_id) {
auto &data = threads_[thread_id];
data.set_epoch(epoch_.load());
while (step_check(data) && data.undeleted() != 0) {
}
}
void retire(size_t thread_id, T *ptr) {
auto &data = threads_[thread_id];
data.to_delete[data.bag_i].push_back(unique_ptr<T>{ptr});
}
};
} // namespace td

View file

@ -0,0 +1,131 @@
/*
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-2019 Telegram Systems LLP
*/
#include "td/utils/FileLog.h"
#include "td/utils/common.h"
#include "td/utils/logging.h"
#include "td/utils/port/FileFd.h"
#include "td/utils/port/path.h"
#include "td/utils/port/StdStreams.h"
#include "td/utils/Slice.h"
#include <limits>
namespace td {
Status FileLog::init(string path, int64 rotate_threshold, bool redirect_stderr) {
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));
fd_.close();
fd_ = std::move(fd);
if (!Stderr().empty() && redirect_stderr) {
fd_.get_native_fd().duplicate(Stderr().get_native_fd()).ignore();
}
auto r_path = realpath(path, true);
if (r_path.is_error()) {
path_ = std::move(path);
} else {
path_ = r_path.move_as_ok();
}
TRY_RESULT(size, fd_.get_size());
size_ = size;
rotate_threshold_ = rotate_threshold;
redirect_stderr_ = redirect_stderr;
return Status::OK();
}
Slice FileLog::get_path() const {
return path_;
}
vector<string> FileLog::get_file_paths() {
vector<string> result;
if (!path_.empty()) {
result.push_back(path_);
result.push_back(PSTRING() << path_ << ".old");
}
return result;
}
void FileLog::set_rotate_threshold(int64 rotate_threshold) {
rotate_threshold_ = rotate_threshold;
}
int64 FileLog::get_rotate_threshold() const {
return rotate_threshold_;
}
void FileLog::append(CSlice cslice, int log_level) {
Slice slice = cslice;
while (!slice.empty()) {
auto r_size = fd_.write(slice);
if (r_size.is_error()) {
process_fatal_error(PSLICE() << r_size.error() << " in " << __FILE__ << " at " << __LINE__);
}
auto written = r_size.ok();
size_ += static_cast<int64>(written);
slice.remove_prefix(written);
}
if (log_level == VERBOSITY_NAME(FATAL)) {
process_fatal_error(cslice);
}
if (size_ > rotate_threshold_) {
auto status = rename(path_, PSLICE() << path_ << ".old");
if (status.is_error()) {
process_fatal_error(PSLICE() << status.error() << " in " << __FILE__ << " at " << __LINE__);
}
do_rotate();
}
}
void FileLog::rotate() {
if (path_.empty()) {
return;
}
do_rotate();
}
void FileLog::do_rotate() {
auto current_verbosity_level = GET_VERBOSITY_LEVEL();
SET_VERBOSITY_LEVEL(std::numeric_limits<int>::min()); // to ensure that nothing will be printed to the closed log
CHECK(!path_.empty());
fd_.close();
auto r_fd = FileFd::open(path_, FileFd::Create | FileFd::Truncate | FileFd::Write);
if (r_fd.is_error()) {
process_fatal_error(PSLICE() << r_fd.error() << " in " << __FILE__ << " at " << __LINE__);
}
fd_ = r_fd.move_as_ok();
if (!Stderr().empty() && redirect_stderr_) {
fd_.get_native_fd().duplicate(Stderr().get_native_fd()).ignore();
}
size_ = 0;
SET_VERBOSITY_LEVEL(current_verbosity_level);
}
} // namespace td

View file

@ -0,0 +1,57 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include "td/utils/logging.h"
#include "td/utils/port/FileFd.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
namespace td {
class FileLog : public LogInterface {
static constexpr int64 DEFAULT_ROTATE_THRESHOLD = 10 * (1 << 20);
public:
Status init(string path, int64 rotate_threshold = DEFAULT_ROTATE_THRESHOLD, bool redirect_stderr = true);
Slice get_path() const;
vector<string> get_file_paths() override;
void set_rotate_threshold(int64 rotate_threshold);
int64 get_rotate_threshold() const;
void append(CSlice cslice, int log_level) override;
void rotate() override;
private:
FileFd fd_;
string path_;
int64 size_ = 0;
int64 rotate_threshold_ = 0;
bool redirect_stderr_;
void do_rotate();
};
} // namespace td

View file

@ -0,0 +1,74 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include "td/utils/TimedStat.h"
namespace td {
class FloodControlFast {
public:
uint32 add_event(int32 now) {
for (auto &limit : limits_) {
limit.stat_.add_event(CounterStat::Event(), now);
if (limit.stat_.get_stat(now).count_ > limit.count_) {
wakeup_at_ = max(wakeup_at_, now + limit.duration_ * 2);
}
}
return wakeup_at_;
}
uint32 get_wakeup_at() {
return wakeup_at_;
}
void add_limit(uint32 duration, int32 count) {
limits_.push_back({TimedStat<CounterStat>(duration, 0), duration, count});
}
void clear_events() {
for (auto &limit : limits_) {
limit.stat_.clear_events();
}
wakeup_at_ = 0;
}
private:
class CounterStat {
public:
struct Event {};
int32 count_ = 0;
void on_event(Event e) {
count_++;
}
void clear() {
count_ = 0;
}
};
uint32 wakeup_at_ = 0;
struct Limit {
TimedStat<CounterStat> stat_;
uint32 duration_;
int32 count_;
};
std::vector<Limit> limits_;
};
} // namespace td

View file

@ -0,0 +1,108 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include <limits>
namespace td {
// More strict implementaions of flood control than FloodControlFast.
// Should be just fine for small counters.
class FloodControlStrict {
public:
int32 add_event(int32 now) {
events_.push_back(Event{now});
if (without_update_ > 0) {
without_update_--;
} else {
update(now);
}
return wakeup_at_;
}
// no more than count in each duration.
void add_limit(int32 duration, int32 count) {
limits_.push_back(Limit{duration, count, 0});
}
int32 get_wakeup_at() {
return wakeup_at_;
}
void clear_events() {
events_.clear();
for (auto &limit : limits_) {
limit.pos_ = 0;
}
without_update_ = 0;
wakeup_at_ = 0;
}
int32 update(int32 now) {
size_t min_pos = events_.size();
without_update_ = std::numeric_limits<size_t>::max();
for (auto &limit : limits_) {
if (limit.pos_ + limit.count_ < events_.size()) {
limit.pos_ = events_.size() - limit.count_;
}
// binary-search? :D
while (limit.pos_ < events_.size() && events_[limit.pos_].timestamp_ + limit.duration_ < now) {
limit.pos_++;
}
if (limit.count_ + limit.pos_ <= events_.size()) {
CHECK(limit.count_ + limit.pos_ == events_.size());
wakeup_at_ = max(wakeup_at_, events_[limit.pos_].timestamp_ + limit.duration_);
without_update_ = 0;
} else {
without_update_ = min(without_update_, limit.count_ + limit.pos_ - events_.size());
}
min_pos = min(min_pos, limit.pos_);
}
if (min_pos * 2 > events_.size()) {
for (auto &limit : limits_) {
limit.pos_ -= min_pos;
}
events_.erase(events_.begin(), events_.begin() + min_pos);
}
return wakeup_at_;
}
private:
int32 wakeup_at_ = 0;
struct Event {
int32 timestamp_;
};
struct Limit {
int32 duration_;
int32 count_;
size_t pos_;
};
size_t without_update_ = 0;
std::vector<Event> events_;
std::vector<Limit> limits_;
};
} // namespace td

View file

@ -0,0 +1,32 @@
/*
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-2019 Telegram Systems LLP
*/
#include "td/utils/GitInfo.h"
#include "auto/git_info.h"
namespace td {
CSlice GitInfo::commit() {
return GIT_COMMIT;
}
bool GitInfo::is_dirty() {
return GIT_DIRTY;
}
} // namespace td

View file

@ -0,0 +1,31 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/Slice.h"
namespace td {
class GitInfo {
public:
static CSlice commit();
static bool is_dirty();
};
} // namespace td

220
tdutils/td/utils/Gzip.cpp Normal file
View file

@ -0,0 +1,220 @@
/*
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-2019 Telegram Systems LLP
*/
#include "td/utils/Gzip.h"
char disable_linker_warning_about_empty_file_gzip_cpp TD_UNUSED;
#if TD_HAVE_ZLIB
#include "td/utils/logging.h"
#include <cstring>
#include <limits>
#include <utility>
#include <zlib.h>
namespace td {
class Gzip::Impl {
public:
z_stream stream_;
// z_stream is not copyable nor movable
Impl() = default;
Impl(const Impl &other) = delete;
Impl &operator=(const Impl &other) = delete;
Impl(Impl &&other) = delete;
Impl &operator=(Impl &&other) = delete;
~Impl() = default;
};
Status Gzip::init_encode() {
CHECK(mode_ == Empty);
init_common();
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);
}
return Status::OK();
}
Status Gzip::init_decode() {
CHECK(mode_ == Empty);
init_common();
mode_ = Decode;
int ret = inflateInit2(&impl_->stream_, MAX_WBITS + 32);
if (ret != Z_OK) {
return Status::Error(PSLICE() << "zlib inflate init failed: " << ret);
}
return Status::OK();
}
void Gzip::set_input(Slice input) {
CHECK(input_size_ == 0);
CHECK(!close_input_flag_);
CHECK(input.size() <= std::numeric_limits<uInt>::max());
CHECK(impl_->stream_.avail_in == 0);
input_size_ = input.size();
impl_->stream_.avail_in = static_cast<uInt>(input.size());
impl_->stream_.next_in = reinterpret_cast<Bytef *>(const_cast<char *>(input.data()));
}
void Gzip::set_output(MutableSlice output) {
CHECK(output_size_ == 0);
CHECK(output.size() <= std::numeric_limits<uInt>::max());
CHECK(impl_->stream_.avail_out == 0);
output_size_ = output.size();
impl_->stream_.avail_out = static_cast<uInt>(output.size());
impl_->stream_.next_out = reinterpret_cast<Bytef *>(output.data());
}
Result<Gzip::State> Gzip::run() {
while (true) {
int ret;
if (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;
}
if (ret == Z_STREAM_END) {
// TODO(now): fail if input is not empty;
clear();
return Done;
}
clear();
return Status::Error(PSLICE() << "zlib error " << ret);
}
}
size_t Gzip::left_input() const {
return impl_->stream_.avail_in;
}
size_t Gzip::left_output() const {
return impl_->stream_.avail_out;
}
void Gzip::init_common() {
std::memset(&impl_->stream_, 0, sizeof(impl_->stream_));
impl_->stream_.zalloc = Z_NULL;
impl_->stream_.zfree = Z_NULL;
impl_->stream_.opaque = Z_NULL;
impl_->stream_.avail_in = 0;
impl_->stream_.next_in = nullptr;
impl_->stream_.avail_out = 0;
impl_->stream_.next_out = nullptr;
input_size_ = 0;
output_size_ = 0;
close_input_flag_ = false;
}
void Gzip::clear() {
if (mode_ == Decode) {
inflateEnd(&impl_->stream_);
} else if (mode_ == Encode) {
deflateEnd(&impl_->stream_);
}
mode_ = Empty;
}
Gzip::Gzip() : impl_(make_unique<Impl>()) {
}
Gzip::Gzip(Gzip &&other) : Gzip() {
swap(other);
}
Gzip &Gzip::operator=(Gzip &&other) {
CHECK(this != &other);
clear();
swap(other);
return *this;
}
void Gzip::swap(Gzip &other) {
using std::swap;
swap(impl_, other.impl_);
swap(input_size_, other.input_size_);
swap(output_size_, other.output_size_);
swap(close_input_flag_, other.close_input_flag_);
swap(mode_, other.mode_);
}
Gzip::~Gzip() {
clear();
}
BufferSlice gzdecode(Slice s) {
Gzip gzip;
gzip.init_decode().ensure();
ChainBufferWriter message;
gzip.set_input(s);
gzip.close_input();
double k = 2;
gzip.set_output(message.prepare_append(static_cast<size_t>(static_cast<double>(s.size()) * k)));
while (true) {
auto r_state = gzip.run();
if (r_state.is_error()) {
return BufferSlice();
}
auto state = r_state.ok();
if (state == Gzip::Done) {
message.confirm_append(gzip.flush_output());
break;
}
if (gzip.need_input()) {
return BufferSlice();
}
if (gzip.need_output()) {
message.confirm_append(gzip.flush_output());
k *= 1.5;
gzip.set_output(message.prepare_append(static_cast<size_t>(static_cast<double>(gzip.left_input()) * k)));
}
}
return message.extract_reader().move_as_buffer_slice();
}
BufferSlice gzencode(Slice s, double k) {
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);
BufferWriter message{max_size};
gzip.set_output(message.prepare_append());
auto r_state = gzip.run();
if (r_state.is_error()) {
return BufferSlice();
}
auto state = r_state.ok();
if (state != Gzip::Done) {
return BufferSlice();
}
message.confirm_append(gzip.flush_output());
return message.as_buffer_slice();
}
} // namespace td
#endif

118
tdutils/td/utils/Gzip.h Normal file
View file

@ -0,0 +1,118 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#if TD_HAVE_ZLIB
#include "td/utils/buffer.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
namespace td {
class Gzip {
public:
Gzip();
Gzip(const Gzip &) = delete;
Gzip &operator=(const Gzip &) = delete;
Gzip(Gzip &&other);
Gzip &operator=(Gzip &&other);
~Gzip();
enum Mode { Empty, Encode, Decode };
Status init(Mode mode) TD_WARN_UNUSED_RESULT {
if (mode == Encode) {
return init_encode();
} else if (mode == Decode) {
return init_decode();
}
clear();
return Status::OK();
}
Status init_encode() TD_WARN_UNUSED_RESULT;
Status init_decode() TD_WARN_UNUSED_RESULT;
void set_input(Slice input);
void set_output(MutableSlice output);
void close_input() {
close_input_flag_ = true;
}
bool need_input() const {
return left_input() == 0;
}
bool need_output() const {
return left_output() == 0;
}
size_t left_input() const;
size_t left_output() const;
size_t used_input() const {
return input_size_ - left_input();
}
size_t used_output() const {
return output_size_ - left_output();
}
size_t flush_input() {
auto res = used_input();
input_size_ = left_input();
return res;
}
size_t flush_output() {
auto res = used_output();
output_size_ = left_output();
return res;
}
enum State { Running, Done };
Result<State> run() TD_WARN_UNUSED_RESULT;
private:
class Impl;
unique_ptr<Impl> impl_;
size_t input_size_ = 0;
size_t output_size_ = 0;
bool close_input_flag_ = false;
Mode mode_ = Empty;
void init_common();
void clear();
void swap(Gzip &other);
};
BufferSlice gzdecode(Slice s);
BufferSlice gzencode(Slice s, double k = 0.9);
} // namespace td
#endif

View file

@ -0,0 +1,82 @@
/*
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-2019 Telegram Systems LLP
*/
#include "td/utils/GzipByteFlow.h"
char disable_linker_warning_about_empty_file_gzipbyteflow_cpp TD_UNUSED;
#if TD_HAVE_ZLIB
#include "td/utils/common.h"
#include "td/utils/Status.h"
namespace td {
void GzipByteFlow::loop() {
while (true) {
if (gzip_.need_input()) {
auto slice = input_->prepare_read();
if (slice.empty()) {
if (!is_input_active_) {
gzip_.close_input();
} else {
break;
}
} else {
gzip_.set_input(input_->prepare_read());
}
}
if (gzip_.need_output()) {
auto slice = output_.prepare_append();
CHECK(!slice.empty());
gzip_.set_output(slice);
}
auto r_state = gzip_.run();
auto output_size = gzip_.flush_output();
if (output_size) {
uncommited_size_ += output_size;
total_output_size_ += output_size;
if (total_output_size_ > max_output_size_) {
return finish(Status::Error("Max output size limit exceeded"));
}
output_.confirm_append(output_size);
}
auto input_size = gzip_.flush_input();
if (input_size) {
input_->confirm_read(input_size);
}
if (r_state.is_error()) {
return finish(r_state.move_as_error());
}
auto state = r_state.ok();
if (state == Gzip::Done) {
on_output_updated();
return consume_input();
}
}
if (uncommited_size_ >= MIN_UPDATE_SIZE) {
uncommited_size_ = 0;
on_output_updated();
}
}
constexpr size_t GzipByteFlow::MIN_UPDATE_SIZE;
} // namespace td
#endif

View file

@ -0,0 +1,60 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/ByteFlow.h"
#include "td/utils/Gzip.h"
#include <limits>
namespace td {
#if TD_HAVE_ZLIB
class GzipByteFlow final : public ByteFlowBase {
public:
GzipByteFlow() = default;
explicit GzipByteFlow(Gzip::Mode mode) {
gzip_.init(mode).ensure();
}
void init_decode() {
gzip_.init_decode().ensure();
}
void init_encode() {
gzip_.init_encode().ensure();
}
void set_max_output_size(size_t max_output_size) {
max_output_size_ = max_output_size;
}
void loop() override;
private:
Gzip gzip_;
size_t uncommited_size_ = 0;
size_t total_output_size_ = 0;
size_t max_output_size_ = std::numeric_limits<size_t>::max();
static constexpr size_t MIN_UPDATE_SIZE = 1 << 14;
};
#endif
} // namespace td

87
tdutils/td/utils/Hash.h Normal file
View file

@ -0,0 +1,87 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#if TD_HAVE_ABSL
#include <absl/hash/hash.h>
#endif
#include <utility>
namespace td {
// A simple wrapper for absl::flat_hash_map, std::unordered_map and probably some our implementaion of hash map in
// the future
// We will introduce out own Hashing utility like an absl one.
class Hasher {
public:
Hasher() = default;
explicit Hasher(size_t init_value) : hash_(init_value) {
}
std::size_t finalize() const {
return hash_;
}
static Hasher combine(Hasher hasher, size_t value) {
hasher.hash_ ^= value;
return hasher;
}
template <class A, class B>
static Hasher combine(Hasher hasher, const std::pair<A, B> &value) {
hasher = AbslHashValue(std::move(hasher), value.first);
hasher = AbslHashValue(std::move(hasher), value.second);
return hasher;
}
private:
std::size_t hash_{0};
};
template <class IgnoreT>
class TdHash {
public:
template <class T>
std::size_t operator()(const T &value) const noexcept {
return AbslHashValue(Hasher(), value).finalize();
}
};
#if TD_HAVE_ABSL
template <class T>
using AbslHash = absl::Hash<T>;
#endif
// default hash implementations
template <class H, class T>
decltype(H::combine(std::declval<H>(), std::declval<T>())) AbslHashValue(H hasher, const T &value) {
return H::combine(std::move(hasher), value);
}
#if TD_HAVE_ABSL
template <class T>
using Hash = AbslHash<T>;
#else
template <class T>
using Hash = TdHash<T>;
#endif
} // namespace td

View file

@ -0,0 +1,39 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/Hash.h"
#if TD_HAVE_ABSL
#include <absl/container/flat_hash_map.h>
#else
#include <unordered_map>
#endif
namespace td {
#if TD_HAVE_ABSL
template <class Key, class Value, class H = Hash<Key>>
using HashMap = absl::flat_hash_map<Key, Value, H>;
#else
template <class Key, class Value, class H = Hash<Key>>
using HashMap = std::unordered_map<Key, Value, H>;
#endif
} // namespace td

View file

@ -0,0 +1,39 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/Hash.h"
#if TD_HAVE_ABSL
#include <absl/container/flat_hash_set.h>
#else
#include <unordered_set>
#endif
namespace td {
#if TD_HAVE_ABSL
template <class Key, class H = Hash<Key>>
using HashSet = absl::flat_hash_set<Key, H>;
#else
template <class Key, class H = Hash<Key>>
using HashSet = std::unordered_set<Key, H>;
#endif
} // namespace td

View file

@ -0,0 +1,153 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include <array>
#include <atomic>
#include <memory>
namespace td {
template <class T, int MaxPointersN = 1, class Deleter = std::default_delete<T>>
class HazardPointers {
public:
explicit HazardPointers(size_t threads_n) : threads_(threads_n) {
for (auto &data : threads_) {
for (auto &ptr : data.hazard_) {
// workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64658
#if TD_GCC && GCC_VERSION <= 40902
ptr = nullptr;
#else
std::atomic_init(&ptr, static_cast<T *>(nullptr));
#endif
}
}
}
HazardPointers(const HazardPointers &other) = delete;
HazardPointers &operator=(const HazardPointers &other) = delete;
HazardPointers(HazardPointers &&other) = delete;
HazardPointers &operator=(HazardPointers &&other) = delete;
class Holder {
public:
template <class S>
S *protect(std::atomic<S *> &to_protect) {
return do_protect(hazard_ptr_, to_protect);
}
Holder(HazardPointers &hp, size_t thread_id, size_t pos) : Holder(hp.get_hazard_ptr(thread_id, pos)) {
CHECK(hazard_ptr_.load() == 0);
hazard_ptr_.store(reinterpret_cast<T *>(1));
}
Holder(const Holder &other) = delete;
Holder &operator=(const Holder &other) = delete;
Holder(Holder &&other) = delete;
Holder &operator=(Holder &&other) = delete;
~Holder() {
clear();
}
void clear() {
hazard_ptr_.store(nullptr, std::memory_order_release);
}
private:
friend class HazardPointers;
explicit Holder(std::atomic<T *> &ptr) : hazard_ptr_(ptr) {
}
std::atomic<T *> &hazard_ptr_;
};
void retire(size_t thread_id, T *ptr = nullptr) {
CHECK(thread_id < threads_.size());
auto &data = threads_[thread_id];
if (ptr) {
data.to_delete_.push_back(std::unique_ptr<T, Deleter>(ptr));
}
for (auto it = data.to_delete_.begin(); it != data.to_delete_.end();) {
if (!is_protected(it->get())) {
it->reset();
it = data.to_delete_.erase(it);
} else {
++it;
}
}
}
// old inteface
T *protect(size_t thread_id, size_t pos, std::atomic<T *> &ptr) {
return do_protect(get_hazard_ptr(thread_id, pos), ptr);
}
void clear(size_t thread_id, size_t pos) {
do_clear(get_hazard_ptr(thread_id, pos));
}
size_t to_delete_size_unsafe() const {
size_t res = 0;
for (auto &thread : threads_) {
res += thread.to_delete_.size();
}
return res;
}
private:
struct ThreadData {
std::array<std::atomic<T *>, MaxPointersN> hazard_;
char pad[TD_CONCURRENCY_PAD - sizeof(hazard_)];
// stupid gc
std::vector<std::unique_ptr<T, Deleter>> to_delete_;
char pad2[TD_CONCURRENCY_PAD - sizeof(to_delete_)];
};
std::vector<ThreadData> threads_;
char pad2[TD_CONCURRENCY_PAD - sizeof(threads_)];
template <class S>
static S *do_protect(std::atomic<T *> &hazard_ptr, std::atomic<S *> &to_protect) {
T *saved = nullptr;
T *to_save;
while ((to_save = to_protect.load()) != saved) {
hazard_ptr.store(to_save);
saved = to_save;
}
return static_cast<S *>(saved);
}
static void do_clear(std::atomic<T *> &hazard_ptr) {
hazard_ptr.store(nullptr, std::memory_order_release);
}
bool is_protected(T *ptr) {
for (auto &thread : threads_) {
for (auto &hazard_ptr : thread.hazard_) {
if (hazard_ptr.load() == ptr) {
return true;
}
}
}
return false;
}
std::atomic<T *> &get_hazard_ptr(size_t thread_id, size_t pos) {
CHECK(thread_id < threads_.size());
return threads_[thread_id].hazard_[pos];
}
};
} // namespace td

170
tdutils/td/utils/Heap.h Normal file
View file

@ -0,0 +1,170 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
namespace td {
struct HeapNode {
bool in_heap() const {
return pos_ != -1;
}
bool is_top() const {
return pos_ == 0;
}
void remove() {
pos_ = -1;
}
int pos_ = -1;
};
template <class KeyT, int K = 4>
class KHeap {
public:
bool empty() const {
return array_.empty();
}
size_t size() const {
return array_.size();
}
KeyT top_key() const {
return array_[0].key_;
}
HeapNode *pop() {
CHECK(!empty());
HeapNode *result = array_[0].node_;
result->remove();
erase(0);
return result;
}
void insert(KeyT key, HeapNode *node) {
CHECK(!node->in_heap());
array_.push_back({key, node});
fix_up(static_cast<int>(array_.size()) - 1);
}
void fix(KeyT key, HeapNode *node) {
CHECK(node->in_heap());
int pos = node->pos_;
KeyT old_key = array_[pos].key_;
array_[pos].key_ = key;
if (key < old_key) {
fix_up(pos);
} else {
fix_down(pos);
}
}
void erase(HeapNode *node) {
CHECK(node->in_heap());
int pos = node->pos_;
node->remove();
erase(pos);
}
template <class F>
void for_each(F &&f) const {
for (auto &it : array_) {
f(it.key_, it.node_);
}
}
template <class F>
void for_each(F &&f) {
for (auto &it : array_) {
f(it.key_, it.node_);
}
}
void check() const {
for (size_t i = 0; i < array_.size(); i++) {
for (size_t j = i * K + 1; j < i * K + 1 + K && j < array_.size(); j++) {
CHECK(array_[i].key_ <= array_[j].key_);
}
}
}
private:
struct Item {
KeyT key_;
HeapNode *node_;
};
vector<Item> array_;
void fix_up(int pos) {
auto item = array_[pos];
while (pos) {
int parent_pos = (pos - 1) / K;
auto parent_item = array_[parent_pos];
if (parent_item.key_ < item.key_) {
break;
}
parent_item.node_->pos_ = pos;
array_[pos] = parent_item;
pos = parent_pos;
}
item.node_->pos_ = pos;
array_[pos] = item;
}
void fix_down(int pos) {
auto item = array_[pos];
while (true) {
int left_pos = pos * K + 1;
int right_pos = min(left_pos + K, static_cast<int>(array_.size()));
int next_pos = pos;
KeyT next_key = item.key_;
for (int i = left_pos; i < right_pos; i++) {
KeyT i_key = array_[i].key_;
if (i_key < next_key) {
next_key = i_key;
next_pos = i;
}
}
if (next_pos == pos) {
break;
}
array_[pos] = array_[next_pos];
array_[pos].node_->pos_ = pos;
pos = next_pos;
}
item.node_->pos_ = pos;
array_[pos] = item;
}
void erase(int pos) {
array_[pos] = array_.back();
array_.pop_back();
if (pos < static_cast<int>(array_.size())) {
fix_down(pos);
fix_up(pos);
}
}
};
} // namespace td

245
tdutils/td/utils/Hints.cpp Normal file
View file

@ -0,0 +1,245 @@
/*
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-2019 Telegram Systems LLP
*/
#include "td/utils/Hints.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/Slice.h"
#include "td/utils/translit.h"
#include "td/utils/unicode.h"
#include "td/utils/utf8.h"
#include <algorithm>
namespace td {
vector<string> Hints::fix_words(vector<string> words) {
std::sort(words.begin(), words.end());
size_t new_words_size = 0;
for (size_t i = 0; i != words.size(); i++) {
if (i == words.size() - 1 || !begins_with(words[i + 1], words[i])) {
if (i != new_words_size) {
words[new_words_size] = std::move(words[i]);
}
new_words_size++;
}
}
words.resize(new_words_size);
return words;
}
vector<string> Hints::get_words(Slice name, bool is_search) {
bool in_word = false;
string word;
vector<string> words;
auto pos = name.ubegin();
auto end = name.uend();
while (pos != end) {
uint32 code;
pos = next_utf8_unsafe(pos, &code, is_search ? "get_words_search" : "get_words_add");
code = prepare_search_character(code);
if (code == 0) {
continue;
}
if (code == ' ') {
if (in_word) {
words.push_back(std::move(word));
word.clear();
in_word = false;
}
} else {
in_word = true;
code = remove_diacritics(code);
append_utf8_character(word, code);
}
}
if (in_word) {
words.push_back(std::move(word));
}
return fix_words(std::move(words));
}
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());
keys.push_back(key);
}
void Hints::delete_word(const string &word, KeyT key, std::map<string, vector<KeyT>> &word_to_keys) {
vector<KeyT> &keys = word_to_keys[word];
auto key_it = std::find(keys.begin(), keys.end(), key);
CHECK(key_it != keys.end());
if (keys.size() == 1) {
word_to_keys.erase(word);
} else {
CHECK(keys.size() > 1);
*key_it = keys.back();
keys.pop_back();
}
}
void Hints::add(KeyT key, Slice name) {
// LOG(ERROR) << "Add " << key << ": " << name;
auto it = key_to_name_.find(key);
if (it != key_to_name_.end()) {
if (it->second == name) {
return;
}
vector<string> old_transliterations;
for (auto &old_word : get_words(it->second, false)) {
delete_word(old_word, key, word_to_keys_);
for (auto &w : get_word_transliterations(old_word, false)) {
if (w != old_word) {
old_transliterations.push_back(std::move(w));
}
}
}
for (auto &word : fix_words(old_transliterations)) {
delete_word(word, key, translit_word_to_keys_);
}
}
if (name.empty()) {
if (it != key_to_name_.end()) {
key_to_name_.erase(it);
}
key_to_rating_.erase(key);
return;
}
vector<string> transliterations;
for (auto &word : get_words(name, false)) {
add_word(word, key, word_to_keys_);
for (auto &w : get_word_transliterations(word, false)) {
if (w != word) {
transliterations.push_back(std::move(w));
}
}
}
for (auto &word : fix_words(transliterations)) {
add_word(word, key, translit_word_to_keys_);
}
key_to_name_[key] = name.str();
}
void Hints::set_rating(KeyT key, RatingT rating) {
// LOG(ERROR) << "Set rating " << key << ": " << rating;
key_to_rating_[key] = rating;
}
void Hints::add_search_results(vector<KeyT> &results, const string &word,
const std::map<string, vector<KeyT>> &word_to_keys) {
LOG(DEBUG) << "Search for word " << word;
auto it = word_to_keys.lower_bound(word);
while (it != word_to_keys.end() && begins_with(it->first, word)) {
results.insert(results.end(), it->second.begin(), it->second.end());
++it;
}
}
vector<Hints::KeyT> Hints::search_word(const string &word) const {
vector<KeyT> results;
add_search_results(results, word, translit_word_to_keys_);
for (auto w : get_word_transliterations(word, true)) {
add_search_results(results, w, word_to_keys_);
}
std::sort(results.begin(), results.end());
results.erase(std::unique(results.begin(), results.end()), results.end());
return results;
}
std::pair<size_t, vector<Hints::KeyT>> Hints::search(Slice query, int32 limit, bool return_all_for_empty_query) const {
// LOG(ERROR) << "Search " << query;
vector<KeyT> results;
if (limit < 0) {
return {key_to_name_.size(), std::move(results)};
}
auto words = get_words(query, true);
if (return_all_for_empty_query && words.empty()) {
results.reserve(key_to_name_.size());
for (auto &it : key_to_name_) {
results.push_back(it.first);
}
}
for (size_t i = 0; i < words.size(); i++) {
vector<KeyT> keys = search_word(words[i]);
if (i == 0) {
results = std::move(keys);
continue;
}
// now need to intersect two lists
size_t results_pos = 0;
size_t keys_pos = 0;
size_t new_results_size = 0;
while (results_pos != results.size() && keys_pos != keys.size()) {
if (results[results_pos] < keys[keys_pos]) {
results_pos++;
} else if (results[results_pos] > keys[keys_pos]) {
keys_pos++;
} else {
results[new_results_size++] = results[results_pos];
results_pos++;
keys_pos++;
}
}
results.resize(new_results_size);
}
auto total_size = results.size();
if (total_size < static_cast<size_t>(limit)) {
std::sort(results.begin(), results.end(), CompareByRating(key_to_rating_));
} else {
std::partial_sort(results.begin(), results.begin() + limit, results.end(), CompareByRating(key_to_rating_));
results.resize(limit);
}
return {total_size, std::move(results)};
}
bool Hints::has_key(KeyT key) const {
return key_to_name_.find(key) != key_to_name_.end();
}
string Hints::key_to_string(KeyT key) const {
auto it = key_to_name_.find(key);
if (it == key_to_name_.end()) {
return string();
}
return it->second;
}
std::pair<size_t, vector<Hints::KeyT>> Hints::search_empty(int32 limit) const {
return search(Slice(), limit, true);
}
size_t Hints::size() const {
return key_to_name_.size();
}
} // namespace td

97
tdutils/td/utils/Hints.h Normal file
View file

@ -0,0 +1,97 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include "td/utils/Slice.h"
#include <map>
#include <unordered_map>
#include <utility>
namespace td {
// TODO template KeyT
class Hints {
using KeyT = int64;
using RatingT = int64;
public:
void add(KeyT key, Slice name);
void remove(KeyT key) {
add(key, "");
}
void set_rating(KeyT key, RatingT rating);
std::pair<size_t, vector<KeyT>> search(
Slice query, int32 limit,
bool return_all_for_empty_query = false) const; // TODO sort by name instead of sort by rating
bool has_key(KeyT key) const;
string key_to_string(KeyT key) const;
std::pair<size_t, vector<KeyT>> search_empty(int32 limit) const; // == search("", limit, true)
size_t size() const;
private:
std::map<string, vector<KeyT>> word_to_keys_;
std::map<string, vector<KeyT>> translit_word_to_keys_;
std::unordered_map<KeyT, string> key_to_name_;
std::unordered_map<KeyT, RatingT> key_to_rating_;
static void add_word(const string &word, KeyT key, std::map<string, vector<KeyT>> &word_to_keys);
static void delete_word(const string &word, KeyT key, std::map<string, vector<KeyT>> &word_to_keys);
static vector<string> fix_words(vector<string> words);
static vector<string> get_words(Slice name, bool is_search);
static void add_search_results(vector<KeyT> &results, const string &word,
const std::map<string, vector<KeyT>> &word_to_keys);
vector<KeyT> search_word(const string &word) const;
class CompareByRating {
const std::unordered_map<KeyT, RatingT> &key_to_rating_;
RatingT get_rating(const KeyT &key) const {
auto it = key_to_rating_.find(key);
if (it == key_to_rating_.end()) {
return RatingT();
}
return it->second;
}
public:
explicit CompareByRating(const std::unordered_map<KeyT, RatingT> &key_to_rating) : key_to_rating_(key_to_rating) {
}
bool operator()(const KeyT &lhs, const KeyT &rhs) const {
auto lhs_rating = get_rating(lhs);
auto rhs_rating = get_rating(rhs);
return lhs_rating < rhs_rating || (lhs_rating == rhs_rating && lhs < rhs);
}
};
};
} // namespace td

View file

@ -0,0 +1,211 @@
/*
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-2019 Telegram Systems LLP
*/
#include "td/utils/HttpUrl.h"
#include "td/utils/format.h"
#include "td/utils/logging.h"
#include "td/utils/misc.h"
#include "td/utils/Parser.h"
namespace td {
string HttpUrl::get_url() const {
string result;
switch (protocol_) {
case Protocol::HTTP:
result += "http://";
break;
case Protocol::HTTPS:
result += "https://";
break;
default:
UNREACHABLE();
}
if (!userinfo_.empty()) {
result += userinfo_;
result += '@';
}
if (is_ipv6_) {
result += '[';
}
result += host_;
if (is_ipv6_) {
result += ']';
}
if (specified_port_ > 0) {
result += ':';
result += to_string(specified_port_);
}
LOG_CHECK(!query_.empty() && query_[0] == '/') << query_;
result += query_;
return result;
}
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(':'));
HttpUrl::Protocol protocol;
if (parser.start_with("://")) {
parser.advance(3);
if (protocol_str == "http") {
protocol = HttpUrl::Protocol::HTTP;
} else if (protocol_str == "https") {
protocol = HttpUrl::Protocol::HTTPS;
} else {
return Status::Error("Unsupported URL protocol");
}
} else {
parser = ConstParser(url);
protocol = default_protocol;
}
Slice userinfo_host_port = parser.read_till_nofail("/?#");
int port = 0;
const char *colon = userinfo_host_port.end() - 1;
while (colon > userinfo_host_port.begin() && *colon != ':' && *colon != ']' && *colon != '@') {
colon--;
}
Slice userinfo_host;
if (colon > userinfo_host_port.begin() && *colon == ':') {
port = to_integer<int>(Slice(colon + 1, userinfo_host_port.end()));
userinfo_host = Slice(userinfo_host_port.begin(), colon);
} else {
userinfo_host = userinfo_host_port;
}
if (port < 0 || port > 65535) {
return Status::Error("Wrong port number specified in the URL");
}
auto at_pos = userinfo_host.rfind('@');
Slice userinfo = at_pos == static_cast<size_t>(-1) ? "" : userinfo_host.substr(0, at_pos);
Slice host = userinfo_host.substr(at_pos + 1);
bool is_ipv6 = false;
if (!host.empty() && host[0] == '[' && host.back() == ']') {
host.remove_prefix(1);
host.remove_suffix(1);
is_ipv6 = true;
}
if (host.empty()) {
return Status::Error("URL host is empty");
}
if (host == ".") {
return Status::Error("Host is invalid");
}
int specified_port = port;
if (port == 0) {
if (protocol == HttpUrl::Protocol::HTTP) {
port = 80;
} else {
CHECK(protocol == HttpUrl::Protocol::HTTPS);
port = 443;
}
}
Slice query = parser.read_all();
while (!query.empty() && is_space(query.back())) {
query.remove_suffix(1);
}
if (query.empty()) {
query = Slice("/");
}
string query_str;
if (query[0] != '/') {
query_str = '/';
}
for (auto c : query) {
if (static_cast<unsigned char>(c) <= 0x20) {
query_str += '%';
query_str += "0123456789ABCDEF"[c / 16];
query_str += "0123456789ABCDEF"[c % 16];
} else {
query_str += c;
}
}
string host_str = to_lower(host);
for (size_t i = 0; i < host_str.size(); i++) {
char c = host_str[i];
if (is_ipv6) {
if (c == ':' || ('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || c == '.') {
continue;
}
return Status::Error("Wrong IPv6 URL host");
}
if (('a' <= c && c <= 'z') || c == '.' || ('0' <= c && c <= '9') || c == '-' || c == '_' || c == '!' || c == '$' ||
c == ',' || c == '~' || c == '*' || c == '\'' || c == '(' || c == ')' || c == ';' || c == '&' || c == '+' ||
c == '=') {
// symbols allowed by RFC 7230 and RFC 3986
continue;
}
if (c == '%') {
c = host_str[++i];
if (('a' <= c && c <= 'f') || ('0' <= c && c <= '9')) {
c = host_str[++i];
if (('a' <= c && c <= 'f') || ('0' <= c && c <= '9')) {
// percent encoded symbol as allowed by RFC 7230 and RFC 3986
continue;
}
}
return Status::Error("Wrong percent-encoded symbol in URL host");
}
// all other symbols aren't allowed
unsigned char uc = static_cast<unsigned char>(c);
if (uc >= 128) {
// but we allow plain UTF-8 symbols
continue;
}
return Status::Error("Wrong URL host");
}
return HttpUrl{protocol, userinfo.str(), std::move(host_str), is_ipv6, specified_port, port, std::move(query_str)};
}
StringBuilder &operator<<(StringBuilder &sb, const HttpUrl &url) {
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;
}
string get_url_query_file_name(const string &query) {
Slice query_slice = query;
query_slice.truncate(query.find_first_of("?#"));
auto slash_pos = query_slice.rfind('/');
if (slash_pos < query_slice.size()) {
return query_slice.substr(slash_pos + 1).str();
}
return query_slice.str();
}
string get_url_file_name(Slice url) {
auto r_http_url = parse_url(url);
if (r_http_url.is_error()) {
LOG(WARNING) << "Receive wrong URL \"" << url << '"';
return string();
}
return get_url_query_file_name(r_http_url.ok().query_);
}
} // namespace td

View file

@ -0,0 +1,60 @@
/*
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-2019 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"
namespace td {
class HttpUrl {
public:
enum class Protocol { HTTP, HTTPS } protocol_ = Protocol::HTTP;
string userinfo_;
string host_;
bool is_ipv6_ = false;
int specified_port_ = 0;
int port_ = 0;
string query_;
string get_url() const;
HttpUrl(Protocol protocol, string userinfo, string host, bool is_ipv6, int specified_port, int port, string query)
: protocol_(protocol)
, userinfo_(std::move(userinfo))
, host_(std::move(host))
, is_ipv6_(is_ipv6)
, specified_port_(specified_port)
, port_(port)
, query_(std::move(query)) {
}
};
Result<HttpUrl> parse_url(Slice url,
HttpUrl::Protocol default_protocol = HttpUrl::Protocol::HTTP) TD_WARN_UNUSED_RESULT;
StringBuilder &operator<<(StringBuilder &sb, const HttpUrl &url);
string get_url_query_file_name(const string &query);
string get_url_file_name(Slice url);
} // namespace td

View file

@ -0,0 +1,698 @@
/*
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-2019 Telegram Systems LLP
*/
#include "td/utils/JsonBuilder.h"
#include "td/utils/misc.h"
#include "td/utils/ScopeGuard.h"
#include <cstring>
namespace td {
StringBuilder &operator<<(StringBuilder &sb, const JsonRawString &val) {
sb << '"';
SCOPE_EXIT {
sb << '"';
};
auto *s = val.value_.begin();
auto len = val.value_.size();
for (size_t pos = 0; pos < len; pos++) {
auto ch = static_cast<unsigned char>(s[pos]);
switch (ch) {
case '"':
sb << '\\' << '"';
break;
case '\\':
sb << '\\' << '\\';
break;
case '\b':
sb << '\\' << 'b';
break;
case '\f':
sb << '\\' << 'f';
break;
case '\n':
sb << '\\' << 'n';
break;
case '\r':
sb << '\\' << 'r';
break;
case '\t':
sb << '\\' << 't';
break;
default:
if (ch <= 31) {
sb << JsonOneChar(s[pos]);
break;
}
sb << s[pos];
break;
}
}
return sb;
}
StringBuilder &operator<<(StringBuilder &sb, const JsonString &val) {
sb << '"';
SCOPE_EXIT {
sb << '"';
};
auto *s = val.str_.begin();
auto len = val.str_.size();
for (size_t pos = 0; pos < len; pos++) {
auto ch = static_cast<unsigned char>(s[pos]);
switch (ch) {
case '"':
sb << '\\' << '"';
break;
case '\\':
sb << '\\' << '\\';
break;
case '\b':
sb << '\\' << 'b';
break;
case '\f':
sb << '\\' << 'f';
break;
case '\n':
sb << '\\' << 'n';
break;
case '\r':
sb << '\\' << 'r';
break;
case '\t':
sb << '\\' << 't';
break;
default:
if (ch <= 31) {
sb << JsonOneChar(s[pos]);
break;
}
if (128 <= ch) {
int a = s[pos];
CHECK((a & 0x40) != 0);
CHECK(pos + 1 < len);
int b = s[++pos];
CHECK((b & 0xc0) == 0x80);
if ((a & 0x20) == 0) {
CHECK((a & 0x1e) > 0);
sb << JsonChar(((a & 0x1f) << 6) | (b & 0x3f));
break;
}
CHECK(pos + 1 < len);
int c = s[++pos];
CHECK((c & 0xc0) == 0x80);
if ((a & 0x10) == 0) {
CHECK(((a & 0x0f) | (b & 0x20)) > 0);
sb << JsonChar(((a & 0x0f) << 12) | ((b & 0x3f) << 6) | (c & 0x3f));
break;
}
CHECK(pos + 1 < len);
int d = s[++pos];
CHECK((d & 0xc0) == 0x80);
if ((a & 0x08) == 0) {
CHECK(((a & 0x07) | (b & 0x30)) > 0);
sb << JsonChar(((a & 0x07) << 18) | ((b & 0x3f) << 12) | ((c & 0x3f) << 6) | (d & 0x3f));
break;
}
UNREACHABLE();
break;
}
sb << s[pos];
break;
}
}
return sb;
}
Result<MutableSlice> json_string_decode(Parser &parser) {
if (!parser.try_skip('"')) {
return Status::Error("Opening '\"' expected");
}
auto *cur_src = parser.data().data();
auto *end_src = parser.data().end();
auto *end = cur_src;
while (end < end_src && end[0] != '"') {
if (end[0] == '\\') {
end++;
}
end++;
}
if (end >= end_src) {
return Status::Error("Closing '\"' not found");
}
parser.advance(end + 1 - cur_src);
end_src = end;
auto *cur_dest = cur_src;
auto *begin_dest = cur_src;
while (cur_src != end_src) {
auto *slash = static_cast<char *>(std::memchr(cur_src, '\\', end_src - cur_src));
if (slash == nullptr) {
slash = end_src;
}
std::memmove(cur_dest, cur_src, slash - cur_src);
cur_dest += slash - cur_src;
cur_src = slash;
if (cur_src != end_src) {
cur_src++;
if (cur_src == end_src) {
// TODO UNREACHABLE();
return Status::Error("Unexpected end of string");
}
switch (*cur_src) {
case '"':
case '\\':
case '/':
*cur_dest++ = *cur_src++;
break;
case 'b':
*cur_dest++ = '\b';
cur_src++;
break;
case 'f':
*cur_dest++ = '\f';
cur_src++;
break;
case 'n':
*cur_dest++ = '\n';
cur_src++;
break;
case 'r':
*cur_dest++ = '\r';
cur_src++;
break;
case 't':
*cur_dest++ = '\t';
cur_src++;
break;
case 'u': {
cur_src++;
if (cur_src + 4 > end_src) {
return Status::Error("\\u has less than 4 symbols");
}
int num = 0;
for (int i = 0; i < 4; i++, cur_src++) {
int d = hex_to_int(*cur_src);
if (d == 16) {
return Status::Error("Invalid \\u -- not hex digit");
}
num = num * 16 + d;
}
if (0xD7FF < num && num < 0xE000) {
if (cur_src + 6 <= end_src && cur_src[0] == '\\' && cur_src[1] == 'u') {
cur_src += 2;
int new_num = 0;
for (int i = 0; i < 4; i++, cur_src++) {
int d = hex_to_int(*cur_src);
if (d == 16) {
return Status::Error("Invalid \\u -- not hex digit");
}
new_num = new_num * 16 + d;
}
if (0xD7FF < new_num && new_num < 0xE000) {
num = (((num & 0x3FF) << 10) | (new_num & 0x3FF)) + 0x10000;
} else {
cur_src -= 6;
}
}
}
if (num < 128) {
*cur_dest++ = static_cast<char>(num);
} else if (num < 0x800) {
*cur_dest++ = static_cast<char>(0xc0 + (num >> 6));
*cur_dest++ = static_cast<char>(0x80 + (num & 63));
} else if (num <= 0xffff) {
*cur_dest++ = static_cast<char>(0xe0 + (num >> 12));
*cur_dest++ = static_cast<char>(0x80 + ((num >> 6) & 63));
*cur_dest++ = static_cast<char>(0x80 + (num & 63));
} else {
*cur_dest++ = static_cast<char>(0xf0 + (num >> 18));
*cur_dest++ = static_cast<char>(0x80 + ((num >> 12) & 63));
*cur_dest++ = static_cast<char>(0x80 + ((num >> 6) & 63));
*cur_dest++ = static_cast<char>(0x80 + (num & 63));
}
break;
}
}
}
}
CHECK(cur_dest <= end_src);
return MutableSlice(begin_dest, cur_dest);
}
Status json_string_skip(Parser &parser) {
if (!parser.try_skip('"')) {
return Status::Error("Opening '\"' expected");
}
auto *begin_src = parser.data().data();
auto *cur_src = begin_src;
auto *end_src = parser.data().end();
auto *end = cur_src;
while (end < end_src && *end != '"') {
if (*end == '\\') {
end++;
}
end++;
}
if (end >= end_src) {
return Status::Error("Closing '\"' not found");
}
parser.advance(end + 1 - cur_src);
end_src = end;
while (cur_src != end_src) {
auto *slash = static_cast<char *>(std::memchr(cur_src, '\\', end_src - cur_src));
if (slash == nullptr) {
slash = end_src;
}
cur_src = slash;
if (cur_src != end_src) {
cur_src++;
if (cur_src == end_src) {
// TODO UNREACHABLE();
return Status::Error("Unexpected end of string");
}
switch (*cur_src) {
case '"':
case '\\':
case '/':
case 'b':
case 'f':
case 'n':
case 'r':
case 't':
cur_src++;
break;
case 'u': {
cur_src++;
if (cur_src + 4 > end_src) {
return Status::Error("\\u has less than 4 symbols");
}
int num = 0;
for (int i = 0; i < 4; i++, cur_src++) {
int d = hex_to_int(*cur_src);
if (d == 16) {
return Status::Error("Invalid \\u -- not hex digit");
}
num = num * 16 + d;
}
if (0xD7FF < num && num < 0xE000) {
if (cur_src + 6 <= end_src && cur_src[0] == '\\' && cur_src[1] == 'u') {
cur_src += 2;
int new_num = 0;
for (int i = 0; i < 4; i++, cur_src++) {
int d = hex_to_int(*cur_src);
if (d == 16) {
return Status::Error("Invalid \\u -- not hex digit");
}
new_num = new_num * 16 + d;
}
if (0xD7FF < new_num && new_num < 0xE000) {
// num = (((num & 0x3FF) << 10) | (new_num & 0x3FF)) + 0x10000;
} else {
cur_src -= 6;
}
}
}
break;
}
}
}
}
return Status::OK();
}
Result<JsonValue> do_json_decode(Parser &parser, int32 max_depth) {
if (max_depth < 0) {
return Status::Error("Too big object depth");
}
parser.skip_whitespaces();
switch (parser.peek_char()) {
case 'f':
if (parser.skip_start_with("false")) {
return JsonValue::create_boolean(false);
}
return Status::Error("Token starts with 'f' -- false expected");
case 't':
if (parser.skip_start_with("true")) {
return JsonValue::create_boolean(true);
}
return Status::Error("Token starts with 't' -- true expected");
case 'n':
if (parser.skip_start_with("null")) {
return JsonValue();
}
return Status::Error("Token starts with 'n' -- null expected");
case '"': {
TRY_RESULT(slice, json_string_decode(parser));
return JsonValue::create_string(slice);
}
case '[': {
parser.skip('[');
parser.skip_whitespaces();
std::vector<JsonValue> res;
if (parser.try_skip(']')) {
return JsonValue::create_array(std::move(res));
}
while (true) {
if (parser.empty()) {
return Status::Error("Unexpected string end");
}
TRY_RESULT(value, do_json_decode(parser, max_depth - 1));
res.emplace_back(std::move(value));
parser.skip_whitespaces();
if (parser.try_skip(']')) {
break;
}
if (parser.try_skip(',')) {
parser.skip_whitespaces();
continue;
}
if (parser.empty()) {
return Status::Error("Unexpected string end");
}
return Status::Error("Unexpected symbol while parsing JSON Array");
}
return JsonValue::create_array(std::move(res));
}
case '{': {
parser.skip('{');
parser.skip_whitespaces();
std::vector<std::pair<MutableSlice, JsonValue> > res;
if (parser.try_skip('}')) {
return JsonValue::make_object(std::move(res));
}
while (true) {
if (parser.empty()) {
return Status::Error("Unexpected string end");
}
TRY_RESULT(key, json_string_decode(parser));
parser.skip_whitespaces();
if (!parser.try_skip(':')) {
return Status::Error("':' expected");
}
TRY_RESULT(value, do_json_decode(parser, max_depth - 1));
res.emplace_back(std::move(key), std::move(value));
parser.skip_whitespaces();
if (parser.try_skip('}')) {
break;
}
if (parser.try_skip(',')) {
parser.skip_whitespaces();
continue;
}
if (parser.empty()) {
return Status::Error("Unexpected string end");
}
return Status::Error("Unexpected symbol while parsing JSON Object");
}
return JsonValue::make_object(std::move(res));
}
case '-':
case '+':
case '.':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9': {
auto num = parser.read_while(
[](char c) { return c == '-' || ('0' <= c && c <= '9') || c == 'e' || c == 'E' || c == '+' || c == '.'; });
return JsonValue::create_number(num);
}
case 0:
return Status::Error("Unexpected string end");
default: {
char next = parser.peek_char();
if (0 < next && next < 127) {
return Status::Error(PSLICE() << "Unexpected symbol '" << parser.peek_char() << "'");
} else {
return Status::Error("Unexpected symbol");
}
}
}
UNREACHABLE();
}
Status do_json_skip(Parser &parser, int32 max_depth) {
if (max_depth < 0) {
return Status::Error("Too big object depth");
}
parser.skip_whitespaces();
switch (parser.peek_char()) {
case 'f':
if (parser.skip_start_with("false")) {
return Status::OK();
}
return Status::Error("Starts with 'f' -- false expected");
case 't':
if (parser.skip_start_with("true")) {
return Status::OK();
}
return Status::Error("Starts with 't' -- true expected");
case 'n':
if (parser.skip_start_with("null")) {
return Status::OK();
}
return Status::Error("Starts with 'n' -- null expected");
case '"': {
return json_string_skip(parser);
}
case '[': {
parser.skip('[');
parser.skip_whitespaces();
if (parser.try_skip(']')) {
return Status::OK();
}
while (true) {
if (parser.empty()) {
return Status::Error("Unexpected end");
}
TRY_STATUS(do_json_skip(parser, max_depth - 1));
parser.skip_whitespaces();
if (parser.try_skip(']')) {
break;
}
if (parser.try_skip(',')) {
parser.skip_whitespaces();
continue;
}
return Status::Error("Unexpected symbol");
}
return Status::OK();
}
case '{': {
parser.skip('{');
parser.skip_whitespaces();
if (parser.try_skip('}')) {
return Status::OK();
}
while (true) {
if (parser.empty()) {
return Status::Error("Unexpected end");
}
TRY_STATUS(json_string_skip(parser));
parser.skip_whitespaces();
if (!parser.try_skip(':')) {
return Status::Error("':' expected");
}
TRY_STATUS(do_json_skip(parser, max_depth - 1));
parser.skip_whitespaces();
if (parser.try_skip('}')) {
break;
}
if (parser.try_skip(',')) {
parser.skip_whitespaces();
continue;
}
return Status::Error("Unexpected symbol");
}
return Status::OK();
}
case '-':
case '+':
case '.':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9': {
parser.read_while(
[](char c) { return c == '-' || ('0' <= c && c <= '9') || c == 'e' || c == 'E' || c == '+' || c == '.'; });
return Status::OK();
}
case 0:
return Status::Error("Unexpected end");
default: {
char next = parser.peek_char();
if (0 < next && next < 127) {
return Status::Error(PSLICE() << "Unexpected symbol '" << parser.peek_char() << "'");
} else {
return Status::Error("Unexpected symbol");
}
}
}
return Status::Error("Can't parse");
}
Slice JsonValue::get_type_name(Type type) {
switch (type) {
case Type::Null:
return Slice("Null");
case Type::Number:
return Slice("Number");
case Type::Boolean:
return Slice("Boolean");
case Type::String:
return Slice("String");
case Type::Array:
return Slice("Array");
case Type::Object:
return Slice("Object");
default:
UNREACHABLE();
return Slice("Unknown");
}
}
bool has_json_object_field(const JsonObject &object, Slice name) {
for (auto &field_value : object) {
if (field_value.first == name) {
return true;
}
}
return false;
}
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) {
if (type != JsonValue::Type::Null && field_value.second.type() != type) {
return Status::Error(400, PSLICE()
<< "Field \"" << name << "\" must be of type " << JsonValue::get_type_name(type));
}
return std::move(field_value.second);
}
}
if (!is_optional) {
return Status::Error(400, PSLICE() << "Can't find field \"" << name << "\"");
}
return JsonValue();
}
Result<bool> get_json_object_bool_field(JsonObject &object, Slice name, bool is_optional, bool default_value) {
TRY_RESULT(value, get_json_object_field(object, name, JsonValue::Type::Boolean, is_optional));
if (value.type() == JsonValue::Type::Null) {
return default_value;
}
return value.get_boolean();
}
Result<int32> get_json_object_int_field(JsonObject &object, Slice name, bool is_optional, int32 default_value) {
for (auto &field_value : object) {
if (field_value.first == name) {
if (field_value.second.type() == JsonValue::Type::String) {
return to_integer_safe<int32>(field_value.second.get_string());
}
if (field_value.second.type() == JsonValue::Type::Number) {
return to_integer_safe<int32>(field_value.second.get_number());
}
return Status::Error(400, PSLICE() << "Field \"" << name << "\" must be of type Number");
}
}
if (is_optional) {
return default_value;
}
return Status::Error(400, PSLICE() << "Can't find field \"" << name << "\"");
}
Result<int64> get_json_object_long_field(JsonObject &object, Slice name, bool is_optional, int64 default_value) {
for (auto &field_value : object) {
if (field_value.first == name) {
if (field_value.second.type() == JsonValue::Type::String) {
return to_integer_safe<int64>(field_value.second.get_string());
}
if (field_value.second.type() == JsonValue::Type::Number) {
return to_integer_safe<int64>(field_value.second.get_number());
}
return Status::Error(400, PSLICE() << "Field \"" << name << "\" must be a Number");
}
}
if (is_optional) {
return default_value;
}
return Status::Error(400, PSLICE() << "Can't find field \"" << name << "\"");
}
Result<double> get_json_object_double_field(JsonObject &object, Slice name, bool is_optional, double default_value) {
TRY_RESULT(value, get_json_object_field(object, name, JsonValue::Type::Number, is_optional));
if (value.type() == JsonValue::Type::Null) {
return default_value;
}
return to_double(value.get_number());
}
Result<string> get_json_object_string_field(JsonObject &object, Slice name, bool is_optional, string default_value) {
for (auto &field_value : object) {
if (field_value.first == name) {
if (field_value.second.type() == JsonValue::Type::String) {
return field_value.second.get_string().str();
}
if (field_value.second.type() == JsonValue::Type::Number) {
return field_value.second.get_number().str();
}
return Status::Error(400, PSLICE() << "Field \"" << name << "\" must be of type String");
}
}
if (is_optional) {
return default_value;
}
return Status::Error(400, PSLICE() << "Can't find field \"" << name << "\"");
}
} // namespace td

View file

@ -0,0 +1,902 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include "td/utils/logging.h"
#include "td/utils/Parser.h"
#include "td/utils/Slice.h"
#include "td/utils/StackAllocator.h"
#include "td/utils/Status.h"
#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) {
return sb << "true";
}
};
class JsonFalse {
public:
friend StringBuilder &operator<<(StringBuilder &sb, const JsonFalse &val) {
return sb << "false";
}
};
class JsonNull {
public:
friend StringBuilder &operator<<(StringBuilder &sb, JsonNull val) {
return sb << "null";
}
};
class JsonBool {
public:
explicit JsonBool(bool value) : value_(value) {
}
friend StringBuilder &operator<<(StringBuilder &sb, const JsonBool &val) {
if (val.value_) {
return sb << JsonTrue();
} else {
return sb << JsonFalse();
}
}
private:
bool value_;
};
class JsonInt {
public:
explicit JsonInt(int32 value) : value_(value) {
}
friend StringBuilder &operator<<(StringBuilder &sb, const JsonInt &val) {
return sb << val.value_;
}
private:
int32 value_;
};
class JsonLong {
public:
explicit JsonLong(int64 value) : value_(value) {
}
friend StringBuilder &operator<<(StringBuilder &sb, const JsonLong &val) {
return sb << val.value_;
}
private:
int64 value_;
};
class JsonFloat {
public:
explicit JsonFloat(double value) : value_(value) {
}
friend StringBuilder &operator<<(StringBuilder &sb, const JsonFloat &val) {
return sb << val.value_;
}
private:
double value_;
};
class JsonOneChar {
public:
explicit JsonOneChar(unsigned int c) : c_(c) {
}
friend StringBuilder &operator<<(StringBuilder &sb, const JsonOneChar &val) {
auto c = val.c_;
return sb << '\\' << 'u' << "0123456789abcdef"[c >> 12] << "0123456789abcdef"[(c >> 8) & 15]
<< "0123456789abcdef"[(c >> 4) & 15] << "0123456789abcdef"[c & 15];
}
private:
unsigned int c_;
};
class JsonChar {
public:
explicit JsonChar(unsigned int c) : c_(c) {
}
friend StringBuilder &operator<<(StringBuilder &sb, const JsonChar &val) {
auto c = val.c_;
if (c < 0x10000) {
if (0xD7FF < c && c < 0xE000) {
// UTF-8 correctness has already been checked
UNREACHABLE();
}
return sb << JsonOneChar(c);
} else if (c <= 0x10ffff) {
return sb << JsonOneChar(0xD7C0 + (c >> 10)) << JsonOneChar(0xDC00 + (c & 0x3FF));
} else {
// UTF-8 correctness has already been checked
UNREACHABLE();
}
}
private:
unsigned int c_;
};
class JsonRaw {
public:
explicit JsonRaw(Slice value) : value_(value) {
}
friend StringBuilder &operator<<(StringBuilder &sb, const JsonRaw &val) {
return sb << val.value_;
}
private:
Slice value_;
};
class JsonRawString {
public:
explicit JsonRawString(Slice value) : value_(value) {
}
friend StringBuilder &operator<<(StringBuilder &sb, const JsonRawString &val);
private:
Slice value_;
};
class JsonString {
public:
explicit JsonString(Slice str) : str_(str) {
}
friend StringBuilder &operator<<(StringBuilder &sb, const JsonString &val);
private:
Slice str_;
};
class JsonScope;
class JsonValueScope;
class JsonArrayScope;
class JsonObjectScope;
class JsonBuilder {
public:
explicit JsonBuilder(StringBuilder &&sb, int32 offset = -1) : sb_(std::move(sb)), offset_(offset) {
}
StringBuilder &string_builder() {
return sb_;
}
friend class JsonScope;
JsonValueScope enter_value() TD_WARN_UNUSED_RESULT;
JsonArrayScope enter_array() TD_WARN_UNUSED_RESULT;
JsonObjectScope enter_object() TD_WARN_UNUSED_RESULT;
int32 offset() const {
return offset_;
}
bool is_pretty() const {
return offset_ >= 0;
}
void print_offset() {
for (int x = 0; x < offset_; x++) {
sb_ << " ";
}
}
void dec_offset() {
if (offset_ >= 0) {
CHECK(offset_ > 0);
offset_--;
}
}
void inc_offset() {
if (offset_ >= 0) {
offset_++;
}
}
private:
StringBuilder sb_;
JsonScope *scope_ = nullptr;
int32 offset_;
};
class Jsonable {};
class JsonScope {
public:
explicit JsonScope(JsonBuilder *jb) : sb_(&jb->sb_), jb_(jb), save_scope_(jb->scope_) {
jb_->scope_ = this;
CHECK(is_active());
}
JsonScope(const JsonScope &other) = delete;
JsonScope(JsonScope &&other) : sb_(other.sb_), jb_(other.jb_), save_scope_(other.save_scope_) {
other.jb_ = nullptr;
}
JsonScope &operator=(const JsonScope &) = delete;
JsonScope &operator=(JsonScope &&) = delete;
~JsonScope() {
if (jb_) {
leave();
}
}
void leave() {
CHECK(is_active());
jb_->scope_ = save_scope_;
}
protected:
StringBuilder *sb_;
// For CHECK
JsonBuilder *jb_;
JsonScope *save_scope_;
bool is_active() const {
return jb_ && jb_->scope_ == this;
}
JsonScope &operator<<(JsonTrue x) {
*sb_ << x;
return *this;
}
JsonScope &operator<<(JsonFalse x) {
*sb_ << x;
return *this;
}
JsonScope &operator<<(JsonNull x) {
*sb_ << x;
return *this;
}
JsonScope &operator<<(const JsonBool &x) {
*sb_ << x;
return *this;
}
JsonScope &operator<<(const JsonInt &x) {
*sb_ << x;
return *this;
}
JsonScope &operator<<(const JsonLong &x) {
*sb_ << x;
return *this;
}
JsonScope &operator<<(const JsonFloat &x) {
*sb_ << x;
return *this;
}
JsonScope &operator<<(const JsonString &x) {
*sb_ << x;
return *this;
}
JsonScope &operator<<(const JsonRawString &x) {
*sb_ << x;
return *this;
}
JsonScope &operator<<(const JsonRaw &x) {
*sb_ << x;
return *this;
}
JsonScope &operator<<(bool x) {
return *this << JsonBool(x);
}
JsonScope &operator<<(int32 x) {
return *this << JsonInt(x);
}
JsonScope &operator<<(int64 x) {
return *this << JsonLong(x);
}
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));
}
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);
}
};
class JsonValueScope : public JsonScope {
public:
using JsonScope::JsonScope;
template <class T>
std::enable_if_t<std::is_base_of<Jsonable, typename std::decay<T>::type>::value, JsonValueScope &> operator<<(
const T &x) {
x.store(this);
return *this;
}
template <class T>
std::enable_if_t<!std::is_base_of<Jsonable, typename std::decay<T>::type>::value, JsonValueScope &> operator<<(
const T &x) {
CHECK(!was_);
was_ = true;
JsonScope::operator<<(x);
return *this;
}
JsonArrayScope enter_array() TD_WARN_UNUSED_RESULT;
JsonObjectScope enter_object() TD_WARN_UNUSED_RESULT;
private:
bool was_ = false;
};
class JsonArrayScope : public JsonScope {
public:
explicit JsonArrayScope(JsonBuilder *jb) : JsonScope(jb) {
jb->inc_offset();
*sb_ << "[";
}
JsonArrayScope(JsonArrayScope &&other) = default;
~JsonArrayScope() {
if (jb_) {
leave();
}
}
void leave() {
jb_->dec_offset();
if (jb_->is_pretty()) {
*sb_ << "\n";
jb_->print_offset();
}
*sb_ << "]";
}
template <class T>
JsonArrayScope &operator<<(const T &x) {
return (*this)(x);
}
template <class T>
JsonArrayScope &operator()(const T &x) {
enter_value() << x;
return *this;
}
JsonValueScope enter_value() {
CHECK(is_active());
if (is_first_) {
*sb_ << ",";
} else {
is_first_ = true;
}
if (jb_->is_pretty()) {
*sb_ << "\n";
jb_->print_offset();
}
return jb_->enter_value();
}
private:
bool is_first_ = false;
};
class JsonObjectScope : public JsonScope {
public:
explicit JsonObjectScope(JsonBuilder *jb) : JsonScope(jb) {
jb->inc_offset();
*sb_ << "{";
}
JsonObjectScope(JsonObjectScope &&other) = default;
~JsonObjectScope() {
if (jb_) {
leave();
}
}
void leave() {
jb_->dec_offset();
if (jb_->is_pretty()) {
*sb_ << "\n";
jb_->print_offset();
}
*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) {
CHECK(is_active());
if (is_first_) {
*sb_ << ",";
} else {
is_first_ = true;
}
if (jb_->is_pretty()) {
*sb_ << "\n";
jb_->print_offset();
}
jb_->enter_value() << key;
if (jb_->is_pretty()) {
*sb_ << " : ";
} else {
*sb_ << ":";
}
jb_->enter_value() << value;
return *this;
}
JsonObjectScope &operator<<(const JsonRaw &key_value) {
CHECK(is_active());
is_first_ = true;
jb_->enter_value() << key_value;
return *this;
}
private:
bool is_first_ = false;
};
inline JsonArrayScope JsonValueScope::enter_array() {
CHECK(!was_);
was_ = true;
return JsonArrayScope(jb_);
}
inline JsonObjectScope JsonValueScope::enter_object() {
CHECK(!was_);
was_ = true;
return JsonObjectScope(jb_);
}
inline JsonValueScope JsonBuilder::enter_value() {
return JsonValueScope(this);
}
inline JsonObjectScope JsonBuilder::enter_object() {
return JsonObjectScope(this);
}
inline JsonArrayScope JsonBuilder::enter_array() {
return JsonArrayScope(this);
}
class JsonValue;
using JsonObject = vector<std::pair<MutableSlice, JsonValue>>;
using JsonArray = vector<JsonValue>;
class JsonValue : public Jsonable {
public:
enum class Type { Null, Number, Boolean, String, Array, Object };
static Slice get_type_name(Type type);
JsonValue() {
}
~JsonValue() {
destroy();
}
JsonValue(JsonValue &&other) : JsonValue() {
init(std::move(other));
}
JsonValue &operator=(JsonValue &&other) {
if (&other == this) {
return *this;
}
destroy();
init(std::move(other));
return *this;
}
JsonValue(const JsonValue &other) = delete;
JsonValue &operator=(const JsonValue &other) = delete;
Type type() const {
return type_;
}
MutableSlice &get_string() {
CHECK(type_ == Type::String);
return string_;
}
const MutableSlice &get_string() const {
CHECK(type_ == Type::String);
return string_;
}
bool &get_boolean() {
CHECK(type_ == Type::Boolean);
return boolean_;
}
const bool &get_boolean() const {
CHECK(type_ == Type::Boolean);
return boolean_;
}
MutableSlice &get_number() {
CHECK(type_ == Type::Number);
return number_;
}
const MutableSlice &get_number() const {
CHECK(type_ == Type::Number);
return number_;
}
JsonArray &get_array() {
CHECK(type_ == Type::Array);
return array_;
}
const JsonArray &get_array() const {
CHECK(type_ == Type::Array);
return array_;
}
JsonObject &get_object() {
CHECK(type_ == Type::Object);
return object_;
}
const JsonObject &get_object() const {
CHECK(type_ == Type::Object);
return object_;
}
static JsonValue create_boolean(bool val) {
JsonValue res;
res.init_boolean(val);
return res;
}
static JsonValue create_number(MutableSlice number) {
JsonValue res;
res.init_number(number);
return res;
}
static JsonValue create_string(MutableSlice str) {
JsonValue res;
res.init_string(str);
return res;
}
static JsonValue create_array(JsonArray v) {
JsonValue res;
res.init_array(std::move(v));
return res;
}
static JsonValue make_object(JsonObject c) {
JsonValue res;
res.init_object(std::move(c));
return res;
}
void store(JsonValueScope *scope) const {
switch (type_) {
case Type::Null:
*scope << JsonRaw("null");
break;
case Type::Boolean:
if (get_boolean()) {
*scope << JsonRaw("true");
} else {
*scope << JsonRaw("false");
}
break;
case Type::Number:
*scope << JsonRaw(get_number());
break;
case Type::String:
*scope << JsonString(get_string());
break;
case Type::Array: {
auto arr = scope->enter_array();
for (auto &val : get_array()) {
arr << val;
}
break;
}
case Type::Object: {
auto object = scope->enter_object();
for (auto &key_value : get_object()) {
object << ctie(JsonString(key_value.first), key_value.second);
}
break;
}
}
};
private:
Type type_{Type::Null};
union {
MutableSlice number_;
bool boolean_;
MutableSlice string_;
JsonArray array_;
JsonObject object_;
};
void init_null() {
type_ = Type::Null;
}
void init_number(MutableSlice number) {
type_ = Type::Number;
new (&number_) MutableSlice(number);
}
void init_boolean(bool boolean) {
type_ = Type::Boolean;
boolean_ = boolean;
}
void init_string(MutableSlice slice) {
type_ = Type::String;
new (&string_) MutableSlice(slice);
}
void init_array(JsonArray array) {
type_ = Type::Array;
new (&array_) JsonArray(std::move(array));
}
void init_object(JsonObject object) {
type_ = Type::Object;
new (&object_) JsonObject(std::move(object));
}
void init(JsonValue &&other) {
switch (other.type_) {
case Type::Null:
break;
case Type::Number:
init_number(other.number_);
break;
case Type::Boolean:
init_boolean(other.boolean_);
break;
case Type::String:
init_string(other.string_);
break;
case Type::Array:
init_array(std::move(other.array_));
break;
case Type::Object:
init_object(std::move(other.object_));
break;
}
other.destroy();
}
void destroy() {
switch (type_) {
case Type::Null:
case Type::Boolean:
break;
case Type::Number:
number_.~MutableSlice();
break;
case Type::String:
string_.~MutableSlice();
break;
case Type::Array:
array_.~vector<JsonValue>();
break;
case Type::Object:
object_.~vector<std::pair<MutableSlice, JsonValue>>();
break;
}
type_ = Type::Null;
}
};
inline StringBuilder &operator<<(StringBuilder &sb, JsonValue::Type type) {
switch (type) {
case JsonValue::Type::Null:
return sb << "Null";
case JsonValue::Type::Number:
return sb << "Number";
case JsonValue::Type::Boolean:
return sb << "Boolean";
case JsonValue::Type::String:
return sb << "String";
case JsonValue::Type::Array:
return sb << "Array";
case JsonValue::Type::Object:
return sb << "Object";
default:
UNREACHABLE();
return sb;
}
}
class VirtuallyJsonable : public Jsonable {
public:
virtual void store(JsonValueScope *scope) const = 0;
VirtuallyJsonable() = default;
VirtuallyJsonable(const VirtuallyJsonable &) = delete;
VirtuallyJsonable &operator=(const VirtuallyJsonable &) = delete;
VirtuallyJsonable(VirtuallyJsonable &&) = default;
VirtuallyJsonable &operator=(VirtuallyJsonable &&) = default;
virtual ~VirtuallyJsonable() = default;
};
class VirtuallyJsonableInt : public VirtuallyJsonable {
public:
explicit VirtuallyJsonableInt(int32 value) : value_(value) {
}
void store(JsonValueScope *scope) const override {
*scope << JsonInt(value_);
}
private:
int32 value_;
};
class VirtuallyJsonableLong : public VirtuallyJsonable {
public:
explicit VirtuallyJsonableLong(int64 value) : value_(value) {
}
void store(JsonValueScope *scope) const override {
*scope << JsonLong(value_);
}
private:
int64 value_;
};
class VirtuallyJsonableString : public VirtuallyJsonable {
public:
explicit VirtuallyJsonableString(Slice value) : value_(value) {
}
void store(JsonValueScope *scope) const override {
*scope << JsonString(value_);
}
private:
Slice value_;
};
Result<MutableSlice> json_string_decode(Parser &parser) TD_WARN_UNUSED_RESULT;
Status json_string_skip(Parser &parser) TD_WARN_UNUSED_RESULT;
Result<JsonValue> do_json_decode(Parser &parser, int32 max_depth) TD_WARN_UNUSED_RESULT;
Status do_json_skip(Parser &parser, int32 max_depth) TD_WARN_UNUSED_RESULT;
inline Result<JsonValue> json_decode(MutableSlice json) {
Parser parser(json);
const int32 DEFAULT_MAX_DEPTH = 100;
auto result = do_json_decode(parser, DEFAULT_MAX_DEPTH);
if (result.is_ok()) {
parser.skip_whitespaces();
if (!parser.empty()) {
return Status::Error("Expected string end");
}
}
return result;
}
template <class StrT, class ValT>
StrT json_encode(const ValT &val, bool pretty = false) {
auto buf_len = 1 << 18;
auto buf = StackAllocator::alloc(buf_len);
JsonBuilder jb(StringBuilder(buf.as_slice(), true), pretty ? 0 : -1);
jb.enter_value() << val;
if (pretty) {
jb.string_builder() << "\n";
}
LOG_IF(ERROR, jb.string_builder().is_error()) << "JSON buffer overflow";
auto slice = jb.string_builder().as_cslice();
return StrT(slice.begin(), slice.size());
}
template <class T>
class ToJsonImpl : public Jsonable {
public:
explicit ToJsonImpl(const T &value) : value_(value) {
}
void store(JsonValueScope *scope) const {
to_json(*scope, value_);
}
private:
const T &value_;
};
template <class T>
auto ToJson(const T &value) {
return ToJsonImpl<T>(value);
}
template <class T>
void to_json(JsonValueScope &jv, const T &value) {
jv << value;
}
template <class F>
class JsonObjectImpl : Jsonable {
public:
explicit JsonObjectImpl(F &&f) : f_(std::forward<F>(f)) {
}
void store(JsonValueScope *scope) const {
auto object = scope->enter_object();
f_(object);
}
private:
F f_;
};
template <class F>
auto json_object(F &&f) {
return JsonObjectImpl<F>(std::forward<F>(f));
}
template <class F>
class JsonArrayImpl : Jsonable {
public:
explicit JsonArrayImpl(F &&f) : f_(std::forward<F>(f)) {
}
void store(JsonValueScope *scope) const {
auto array = scope->enter_array();
f_(array);
}
private:
F f_;
};
template <class F>
auto json_array(F &&f) {
return JsonArrayImpl<F>(std::forward<F>(f));
}
template <class A, class F>
auto json_array(const A &a, F &&f) {
return json_array([&a, &f](auto &arr) {
for (auto &x : a) {
arr(f(x));
}
});
}
bool has_json_object_field(const JsonObject &object, Slice name);
Result<JsonValue> get_json_object_field(JsonObject &object, Slice name, JsonValue::Type type,
bool is_optional = true) TD_WARN_UNUSED_RESULT;
Result<bool> get_json_object_bool_field(JsonObject &object, Slice name, bool is_optional = true,
bool default_value = false) TD_WARN_UNUSED_RESULT;
Result<int32> get_json_object_int_field(JsonObject &object, Slice name, bool is_optional = true,
int32 default_value = 0) TD_WARN_UNUSED_RESULT;
Result<int64> get_json_object_long_field(JsonObject &object, Slice name, bool is_optional = true,
int64 default_value = 0) TD_WARN_UNUSED_RESULT;
Result<double> get_json_object_double_field(JsonObject &object, Slice name, bool is_optional = true,
double default_value = 0.0) TD_WARN_UNUSED_RESULT;
Result<string> get_json_object_string_field(JsonObject &object, Slice name, bool is_optional = true,
string default_value = "") TD_WARN_UNUSED_RESULT;
} // namespace td

104
tdutils/td/utils/List.h Normal file
View file

@ -0,0 +1,104 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
namespace td {
struct ListNode {
ListNode *next;
ListNode *prev;
ListNode() {
clear();
}
~ListNode() {
remove();
}
ListNode(const ListNode &) = delete;
ListNode &operator=(const ListNode &) = delete;
ListNode(ListNode &&other) {
if (other.empty()) {
clear();
} else {
ListNode *head = other.prev;
other.remove();
head->put(this);
}
}
ListNode &operator=(ListNode &&other) {
this->remove();
if (!other.empty()) {
ListNode *head = other.prev;
other.remove();
head->put(this);
}
return *this;
}
void connect(ListNode *to) {
CHECK(to != nullptr);
next = to;
to->prev = this;
}
void remove() {
prev->connect(next);
clear();
}
void put(ListNode *other) {
other->connect(next);
this->connect(other);
}
void put_back(ListNode *other) {
prev->connect(other);
other->connect(this);
}
ListNode *get() {
ListNode *result = prev;
if (result == this) {
return nullptr;
}
result->prev->connect(this);
result->clear();
// this->connect(result->next);
return result;
}
bool empty() const {
return next == this;
}
private:
void clear() {
next = this;
prev = this;
}
};
} // namespace td

View file

@ -0,0 +1,95 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include "td/utils/logging.h"
#include "td/utils/Slice.h"
#include <atomic>
#include <cstdio>
#include <cstring>
namespace td {
template <int buffer_size = 32 * (1 << 10)>
class MemoryLog : public LogInterface {
static constexpr size_t MAX_OUTPUT_SIZE = buffer_size / 16 < (8 << 10) ? buffer_size / 16 : (8 << 10);
public:
MemoryLog() {
std::memset(buffer_, ' ', sizeof(buffer_));
}
void append(CSlice new_slice, int log_level) override {
Slice slice = new_slice;
slice.truncate(MAX_OUTPUT_SIZE);
while (!slice.empty() && slice.back() == '\n') {
slice.remove_suffix(1);
}
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);
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);
} else {
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_[0], slice.data() + first, second);
std::memcpy(&buffer_[second], " ", pad_size);
}
CHECK((start_pos & 15) == 0);
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] = ' ';
if (log_level == VERBOSITY_NAME(FATAL)) {
process_fatal_error(new_slice);
}
}
void rotate() override {
}
Slice get_buffer() const {
return Slice(buffer_, sizeof(buffer_));
}
size_t get_pos() const {
return pos_ & (buffer_size - 1);
}
private:
char buffer_[buffer_size];
std::atomic<uint32> pos_{0};
};
} // namespace td

View file

@ -0,0 +1,56 @@
/*
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-2019 Telegram Systems LLP
*/
#include "td/utils/MimeType.h"
#include "td/utils/logging.h"
const char *extension_to_mime_type(const char *extension, size_t extension_len); // auto-generated
const char *mime_type_to_extension(const char *mime_type, size_t mime_type_len); // auto-generated
namespace td {
string MimeType::to_extension(Slice mime_type, Slice default_value) {
if (mime_type.empty()) {
return default_value.str();
}
const char *result = ::mime_type_to_extension(mime_type.data(), mime_type.size());
if (result != nullptr) {
return result;
}
LOG(INFO) << "Unknown file MIME type " << mime_type;
return default_value.str();
}
string MimeType::from_extension(Slice extension, Slice default_value) {
if (extension.empty()) {
return default_value.str();
}
const char *result = ::extension_to_mime_type(extension.data(), extension.size());
if (result != nullptr) {
return result;
}
LOG(INFO) << "Unknown file extension " << extension;
return default_value.str();
}
} // namespace td

View file

@ -0,0 +1,32 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include "td/utils/Slice.h"
namespace td {
class MimeType {
public:
static string to_extension(Slice mime_type, Slice default_value = Slice());
static string from_extension(Slice extension, Slice default_value = Slice());
};
} // 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-2019 Telegram Systems LLP
*/
#pragma once
namespace td {
template <class T, T empty_val = T()>
class MovableValue {
public:
MovableValue() = default;
MovableValue(T val) : val_(val) {
}
MovableValue(MovableValue &&other) : val_(other.val_) {
other.clear();
}
MovableValue &operator=(MovableValue &&other) {
val_ = other.val_;
other.clear();
return *this;
}
MovableValue(const MovableValue &) = delete;
MovableValue &operator=(const MovableValue &) = delete;
~MovableValue() = default;
void clear() {
val_ = empty_val;
}
const T &get() const {
return val_;
}
private:
T val_ = empty_val;
};
} // namespace td

View file

@ -0,0 +1,27 @@
/*
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-2019 Telegram Systems LLP
*/
#include "td/utils/MpmcQueue.h"
namespace td {
namespace detail {
MpmcStat stat_;
} // namespace detail
} // namespace td

View file

@ -0,0 +1,469 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
// MPMC queue
// Simple semaphore protected implementation
// To close queue, one should send as much sentinel elements as there are readers.
// Once there are no readers and writers, one may easily destroy queue
#include "td/utils/format.h"
#include "td/utils/HazardPointers.h"
#include "td/utils/logging.h"
#include "td/utils/port/thread.h"
#include "td/utils/ScopeGuard.h"
#include <array>
#include <atomic>
namespace td {
namespace detail {
struct MpmcStat {
void alloc_ok(size_t thread_id) {
s(thread_id).alloc_ok_cnt++;
}
void alloc_error(size_t thread_id) {
s(thread_id).alloc_error_cnt++;
}
void push_loop_error(size_t thread_id) {
s(thread_id).push_loop_error_cnt++;
}
void push_loop_ok(size_t thread_id) {
s(thread_id).push_loop_ok_cnt++;
}
void dump() {
int alloc_ok_cnt = 0;
int alloc_error_cnt = 0;
int push_loop_error_cnt = 0;
int push_loop_ok_cnt = 0;
for (auto &d : arr) {
alloc_ok_cnt += d.alloc_ok_cnt;
alloc_error_cnt += d.alloc_error_cnt;
push_loop_error_cnt += d.push_loop_error_cnt;
push_loop_ok_cnt += d.push_loop_ok_cnt;
}
LOG(ERROR) << tag("alloc_ok_cnt", alloc_ok_cnt) << tag("alloc_error_cnt", alloc_error_cnt)
<< tag("push_loop_error_cnt", push_loop_error_cnt) << tag("push_loop_ok_cnt", push_loop_ok_cnt);
}
private:
struct ThreadStat {
int alloc_ok_cnt{0};
int alloc_error_cnt{0};
int push_loop_ok_cnt{0};
int push_loop_error_cnt{0};
char pad[TD_CONCURRENCY_PAD - sizeof(int) * 4];
};
std::array<ThreadStat, 1024> arr;
ThreadStat &s(size_t thread_id) {
return arr[thread_id];
}
};
extern MpmcStat stat_;
} // namespace detail
template <class T>
class OneValue {
public:
bool set_value(T &value) {
value_ = std::move(value);
int state = Empty;
if (state_.compare_exchange_strong(state, Value, std::memory_order_acq_rel)) {
return true;
}
value = std::move(value_);
return false;
}
bool get_value(T &value) {
auto old_state = state_.exchange(Taken, std::memory_order_acq_rel);
if (old_state == Value) {
value = std::move(value_);
return true;
}
return false;
}
void reset() {
state_ = Empty;
value_ = T();
}
private:
enum Type : int { Empty = 0, Taken, Value };
std::atomic<int> state_{Empty};
T value_{};
};
template <class T>
class OneValue<T *> {
public:
bool set_value(T *value) {
T *was = nullptr;
return state_.compare_exchange_strong(was, value, std::memory_order_acq_rel);
}
bool get_value(T *&value) {
value = state_.exchange(Taken(), std::memory_order_acq_rel);
return value != nullptr;
}
void reset() {
state_ = nullptr;
}
OneValue() {
}
private:
std::atomic<T *> state_{nullptr};
T *Taken() {
static T xxx;
return &xxx;
}
};
template <class T>
class MpmcQueueBlock {
public:
explicit MpmcQueueBlock(size_t size) : nodes_(size) {
}
enum class PopStatus { Ok, Empty, Closed };
//blocking pop
//returns Ok or Closed
PopStatus pop(T &value) {
while (true) {
auto read_pos = read_pos_.fetch_add(1, std::memory_order_relaxed);
if (read_pos >= nodes_.size()) {
return PopStatus::Closed;
}
//TODO blocking get_value
if (nodes_[static_cast<size_t>(read_pos)].one_value.get_value(value)) {
return PopStatus::Ok;
}
}
}
//nonblocking pop
//returns Ok, Empty or Closed
PopStatus try_pop(T &value) {
while (true) {
// this check slows 1:1 case but prevents writer starvation in 1:N case
if (write_pos_.load(std::memory_order_relaxed) <= read_pos_.load(std::memory_order_relaxed) &&
read_pos_.load(std::memory_order_relaxed) < nodes_.size()) {
return PopStatus::Empty;
}
auto read_pos = read_pos_.fetch_add(1, std::memory_order_relaxed);
if (read_pos >= nodes_.size()) {
return PopStatus::Closed;
}
if (nodes_[static_cast<size_t>(read_pos)].one_value.get_value(value)) {
return PopStatus::Ok;
}
auto write_pos = write_pos_.load(std::memory_order_relaxed);
if (write_pos <= read_pos + 1) {
return PopStatus::Empty;
}
}
}
enum class PushStatus { Ok, Closed };
PushStatus push(T &value) {
while (true) {
auto write_pos = write_pos_.fetch_add(1, std::memory_order_relaxed);
if (write_pos >= nodes_.size()) {
return PushStatus::Closed;
}
if (nodes_[static_cast<size_t>(write_pos)].one_value.set_value(value)) {
//stat_.push_loop_ok(0);
return PushStatus::Ok;
}
//stat_.push_loop_error(0);
}
}
private:
struct Node {
OneValue<T> one_value;
};
std::atomic<uint64> write_pos_{0};
char pad[TD_CONCURRENCY_PAD - sizeof(std::atomic<uint64>)];
std::atomic<uint64> read_pos_{0};
char pad2[TD_CONCURRENCY_PAD - sizeof(std::atomic<uint64>)];
std::vector<Node> nodes_;
char pad3[TD_CONCURRENCY_PAD - sizeof(std::vector<Node>)];
};
template <class T>
class MpmcQueueOld {
public:
explicit MpmcQueueOld(size_t threads_n) : MpmcQueueOld(1024, threads_n) {
}
static std::string get_description() {
return "Mpmc queue (fetch and add array queue)";
}
MpmcQueueOld(size_t block_size, size_t threads_n) : block_size_{block_size}, hazard_pointers_{threads_n} {
auto node = make_unique<Node>(block_size_);
write_pos_ = node.get();
read_pos_ = node.get();
node.release();
}
MpmcQueueOld(const MpmcQueueOld &other) = delete;
MpmcQueueOld &operator=(const MpmcQueueOld &other) = delete;
MpmcQueueOld(MpmcQueueOld &&other) = delete;
MpmcQueueOld &operator=(MpmcQueueOld &&other) = delete;
~MpmcQueueOld() {
auto *ptr = read_pos_.load(std::memory_order_relaxed);
while (ptr) {
auto *to_delete = ptr;
ptr = ptr->next_.load(std::memory_order_relaxed);
delete to_delete;
}
//stat_.dump();
//stat_ = detail::MpmcStat();
}
size_t hazard_pointers_to_delele_size_unsafe() const {
return hazard_pointers_.to_delete_size_unsafe();
}
void gc(size_t thread_id) {
hazard_pointers_.retire(thread_id);
}
using PushStatus = typename MpmcQueueBlock<T>::PushStatus;
using PopStatus = typename MpmcQueueBlock<T>::PopStatus;
void push(T value, size_t thread_id) {
typename decltype(hazard_pointers_)::Holder hazard_ptr_holder(hazard_pointers_, thread_id, 0);
while (true) {
auto node = hazard_ptr_holder.protect(write_pos_);
auto status = node->block.push(value);
switch (status) {
case PushStatus::Ok:
return;
case PushStatus::Closed: {
auto next = node->next_.load(std::memory_order_acquire);
if (next == nullptr) {
auto new_node = new Node(block_size_);
new_node->block.push(value);
if (node->next_.compare_exchange_strong(next, new_node, std::memory_order_acq_rel)) {
//stat_.alloc_ok(thread_id);
write_pos_.compare_exchange_strong(node, new_node, std::memory_order_acq_rel);
return;
} else {
//stat_.alloc_error(thread_id);
new_node->block.pop(value);
//CHECK(status == PopStatus::Ok);
delete new_node;
}
}
//CHECK(next != nullptr);
write_pos_.compare_exchange_strong(node, next, std::memory_order_acq_rel);
break;
}
}
}
}
bool try_pop(T &value, size_t thread_id) {
typename decltype(hazard_pointers_)::Holder hazard_ptr_holder(hazard_pointers_, thread_id, 0);
while (true) {
auto node = hazard_ptr_holder.protect(read_pos_);
auto status = node->block.try_pop(value);
switch (status) {
case PopStatus::Ok:
return true;
case PopStatus::Empty:
return false;
case PopStatus::Closed: {
auto next = node->next_.load(std::memory_order_acquire);
if (!next) {
return false;
}
if (read_pos_.compare_exchange_strong(node, next, std::memory_order_acq_rel)) {
hazard_ptr_holder.clear();
hazard_pointers_.retire(thread_id, node);
}
break;
}
}
}
}
T pop(size_t thread_id) {
T value;
while (true) {
if (try_pop(value, thread_id)) {
return value;
}
td::this_thread::yield();
}
}
private:
struct Node {
explicit Node(size_t block_size) : block{block_size} {
}
std::atomic<Node *> next_{nullptr};
char pad[TD_CONCURRENCY_PAD - sizeof(std::atomic<Node *>)];
MpmcQueueBlock<T> block;
//Got pad in MpmcQueueBlock
};
std::atomic<Node *> write_pos_{nullptr};
char pad[TD_CONCURRENCY_PAD - sizeof(std::atomic<Node *>)];
std::atomic<Node *> read_pos_{nullptr};
char pad2[TD_CONCURRENCY_PAD - sizeof(std::atomic<Node *>)];
size_t block_size_;
HazardPointers<Node, 1> hazard_pointers_;
//Got pad in HazardPointers
};
template <class T>
class MpmcQueue {
public:
explicit MpmcQueue(size_t threads_n) : MpmcQueue(1024, threads_n) {
}
static std::string get_description() {
return "NEW Mpmc queue (fetch and add array queue)";
}
MpmcQueue(size_t block_size, size_t threads_n) : hazard_pointers_{threads_n} {
auto node = make_unique<Node>();
write_pos_ = node.get();
read_pos_ = node.get();
node.release();
}
MpmcQueue(const MpmcQueue &other) = delete;
MpmcQueue &operator=(const MpmcQueue &other) = delete;
MpmcQueue(MpmcQueue &&other) = delete;
MpmcQueue &operator=(MpmcQueue &&other) = delete;
~MpmcQueue() {
auto *ptr = read_pos_.load(std::memory_order_relaxed);
while (ptr) {
auto *to_delete = ptr;
ptr = ptr->next.load(std::memory_order_relaxed);
delete to_delete;
}
}
size_t hazard_pointers_to_delele_size_unsafe() const {
return hazard_pointers_.to_delete_size_unsafe();
}
void gc(size_t thread_id) {
hazard_pointers_.retire(thread_id);
}
void push(T value, size_t thread_id) {
SCOPE_EXIT {
hazard_pointers_.clear(thread_id, 0);
};
while (true) {
auto node = hazard_pointers_.protect(thread_id, 0, write_pos_);
auto &block = node->block;
auto pos = block.write_pos++;
if (pos >= block.data.size()) {
auto next = node->next.load();
if (next == nullptr) {
auto new_node = new Node{};
new_node->block.write_pos++;
new_node->block.data[0].set_value(value);
Node *null = nullptr;
if (node->next.compare_exchange_strong(null, new_node)) {
write_pos_.compare_exchange_strong(node, new_node);
return;
} else {
new_node->block.data[0].get_value(value);
delete new_node;
}
} else {
write_pos_.compare_exchange_strong(node, next);
}
} else {
if (block.data[static_cast<size_t>(pos)].set_value(value)) {
return;
}
}
}
}
bool try_pop(T &value, size_t thread_id) {
SCOPE_EXIT {
hazard_pointers_.clear(thread_id, 0);
};
while (true) {
auto node = hazard_pointers_.protect(thread_id, 0, read_pos_);
auto &block = node->block;
if (block.write_pos <= block.read_pos && node->next.load(std::memory_order_relaxed) == nullptr) {
return false;
}
auto pos = block.read_pos++;
if (pos >= block.data.size()) {
auto next = node->next.load();
if (!next) {
return false;
}
if (read_pos_.compare_exchange_strong(node, next)) {
hazard_pointers_.clear(thread_id, 0);
hazard_pointers_.retire(thread_id, node);
}
} else {
if (block.data[static_cast<size_t>(pos)].get_value(value)) {
return true;
}
}
}
}
T pop(size_t thread_id) {
T value;
while (true) {
if (try_pop(value, thread_id)) {
return value;
}
td::this_thread::yield();
}
}
private:
struct Block {
std::atomic<uint64> write_pos{0};
char pad[TD_CONCURRENCY_PAD - sizeof(std::atomic<uint64>)];
std::atomic<uint64> read_pos{0};
char pad2[TD_CONCURRENCY_PAD - sizeof(std::atomic<uint64>)];
std::array<OneValue<T>, 1024> data;
char pad3[TD_CONCURRENCY_PAD];
};
struct Node {
Node() = default;
Block block;
std::atomic<Node *> next{nullptr};
char pad[TD_CONCURRENCY_PAD - sizeof(std::atomic<Node *>)];
//Got pad in MpmcQueueBlock
};
std::atomic<Node *> write_pos_{nullptr};
char pad[TD_CONCURRENCY_PAD - sizeof(std::atomic<Node *>)];
std::atomic<Node *> read_pos_{nullptr};
char pad2[TD_CONCURRENCY_PAD - sizeof(std::atomic<Node *>)];
HazardPointers<Node, 1> hazard_pointers_;
//Got pad in HazardPointers
};
} // namespace td

View file

@ -0,0 +1,120 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include "td/utils/port/thread.h"
#include <atomic>
#include <condition_variable>
#include <mutex>
namespace td {
class MpmcWaiter {
public:
int wait(int yields, uint32 worker_id) {
if (yields < RoundsTillSleepy) {
td::this_thread::yield();
return yields + 1;
} else if (yields == RoundsTillSleepy) {
auto state = state_.load(std::memory_order_relaxed);
if (!State::has_worker(state)) {
auto new_state = State::with_worker(state, worker_id);
if (state_.compare_exchange_strong(state, new_state, std::memory_order_acq_rel)) {
td::this_thread::yield();
return yields + 1;
}
if (state == State::awake()) {
return 0;
}
}
td::this_thread::yield();
return 0;
} else if (yields < RoundsTillAsleep) {
auto state = state_.load(std::memory_order_acquire);
if (State::still_sleepy(state, worker_id)) {
td::this_thread::yield();
return yields + 1;
}
return 0;
} else {
auto state = state_.load(std::memory_order_acquire);
if (State::still_sleepy(state, worker_id)) {
std::unique_lock<std::mutex> lock(mutex_);
if (state_.compare_exchange_strong(state, State::asleep(), std::memory_order_acq_rel)) {
condition_variable_.wait(lock);
}
}
return 0;
}
}
int stop_wait(int yields, uint32 worker_id) {
if (yields > RoundsTillSleepy) {
notify_cold();
}
return 0;
}
void notify() {
std::atomic_thread_fence(std::memory_order_seq_cst);
if (state_.load(std::memory_order_acquire) == State::awake()) {
return;
}
notify_cold();
}
private:
struct State {
static constexpr uint32 awake() {
return 0;
}
static constexpr uint32 asleep() {
return 1;
}
static bool is_asleep(uint32 state) {
return (state & 1) != 0;
}
static bool has_worker(uint32 state) {
return (state >> 1) != 0;
}
static int32 with_worker(uint32 state, uint32 worker) {
return state | ((worker + 1) << 1);
}
static bool still_sleepy(uint32 state, uint32 worker) {
return (state >> 1) == (worker + 1);
}
};
//enum { RoundsTillSleepy = 32, RoundsTillAsleep = 64 };
enum { RoundsTillSleepy = 1, RoundsTillAsleep = 2 };
std::atomic<uint32> state_{State::awake()};
std::mutex mutex_;
std::condition_variable condition_variable_;
void notify_cold() {
auto old_state = state_.exchange(State::awake(), std::memory_order_release);
if (State::is_asleep(old_state)) {
std::lock_guard<std::mutex> guard(mutex_);
condition_variable_.notify_all();
}
}
};
} // namespace td

View file

@ -0,0 +1,185 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include <atomic>
namespace td {
//NB: holder of the queue holds all responsibility of freeing its nodes
class MpscLinkQueueImpl {
public:
class Node;
class Reader;
void push(Node *node) {
node->next_ = head_.load(std::memory_order_relaxed);
while (!head_.compare_exchange_strong(node->next_, node, std::memory_order_release, std::memory_order_relaxed)) {
}
}
void push_unsafe(Node *node) {
node->next_ = head_.load(std::memory_order_relaxed);
head_.store(node, std::memory_order_relaxed);
}
void pop_all(Reader &reader) {
return reader.add(head_.exchange(nullptr, std::memory_order_acquire));
}
void pop_all_unsafe(Reader &reader) {
return reader.add(head_.exchange(nullptr, std::memory_order_relaxed));
}
class Node {
friend class MpscLinkQueueImpl;
Node *next_{nullptr};
};
class Reader {
public:
Node *read() {
auto old_head = head_;
if (head_) {
head_ = head_->next_;
}
return old_head;
}
void delay(Node *node) {
node->next_ = head_;
if (!head_) {
tail_ = node;
}
head_ = node;
}
size_t calc_size() const {
size_t res = 0;
for (auto it = head_; it != nullptr; it = it->next_, res++) {
}
return res;
}
private:
friend class MpscLinkQueueImpl;
void add(Node *node) {
if (node == nullptr) {
return;
}
// Reverse list
Node *tail = node;
Node *head = nullptr;
while (node) {
auto next = node->next_;
node->next_ = head;
head = node;
node = next;
}
if (head_ == nullptr) {
head_ = head;
} else {
tail_->next_ = head;
}
tail_ = tail;
}
Node *head_{nullptr};
Node *tail_{nullptr};
};
private:
std::atomic<Node *> head_{nullptr};
};
// Uses MpscLinkQueueImpl.
// Node should have to_mpsc_link_queue_node and from_mpsc_link_queue_node functions
template <class Node>
class MpscLinkQueue {
public:
void push(Node node) {
impl_.push(node.to_mpsc_link_queue_node());
}
void push_unsafe(Node node) {
impl_.push_unsafe(node.to_mpsc_link_queue_node());
}
class Reader {
public:
~Reader() {
CHECK(!read());
}
Node read() {
auto node = impl_.read();
if (!node) {
return {};
}
return Node::from_mpsc_link_queue_node(node);
}
void delay(Node node) {
impl_.delay(node.to_mpsc_link_queue_node());
}
size_t calc_size() const {
return impl_.calc_size();
}
private:
friend class MpscLinkQueue;
MpscLinkQueueImpl::Reader impl_;
MpscLinkQueueImpl::Reader &impl() {
return impl_;
}
};
void pop_all(Reader &reader) {
return impl_.pop_all(reader.impl());
}
void pop_all_unsafe(Reader &reader) {
return impl_.pop_all_unsafe(reader.impl());
}
private:
MpscLinkQueueImpl impl_;
};
template <class Value>
class MpscLinkQueueUniquePtrNode {
public:
MpscLinkQueueUniquePtrNode() = default;
explicit MpscLinkQueueUniquePtrNode(unique_ptr<Value> ptr) : ptr_(std::move(ptr)) {
}
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) {
return MpscLinkQueueUniquePtrNode<Value>(unique_ptr<Value>(Value::from_mpsc_link_queue_node(node)));
}
explicit operator bool() {
return ptr_ != nullptr;
}
Value &value() {
return *ptr_;
}
private:
unique_ptr<Value> ptr_;
};
} // namespace td

View file

@ -0,0 +1,164 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include "td/utils/misc.h"
#include "td/utils/port/EventFd.h"
#if !TD_EVENTFD_UNSUPPORTED
#include "td/utils/SpinLock.h"
#include <utility>
namespace td {
// interface like in PollableQueue
template <class T>
class MpscPollableQueue {
public:
using ValueType = T;
int reader_wait_nonblock() {
auto ready = reader_vector_.size() - reader_pos_;
if (ready != 0) {
return narrow_cast<int>(ready);
}
for (int i = 0; i < 2; i++) {
auto guard = lock_.lock();
if (writer_vector_.empty()) {
if (i == 1) {
wait_event_fd_ = true;
return 0;
}
} else {
reader_vector_.clear();
reader_pos_ = 0;
std::swap(writer_vector_, reader_vector_);
return narrow_cast<int>(reader_vector_.size());
}
event_fd_.acquire();
}
UNREACHABLE();
}
ValueType reader_get_unsafe() {
return std::move(reader_vector_[reader_pos_++]);
}
void reader_flush() {
//nop
}
void writer_put(ValueType value) {
auto guard = lock_.lock();
writer_vector_.push_back(std::move(value));
if (wait_event_fd_) {
wait_event_fd_ = false;
guard.reset();
event_fd_.release();
}
}
EventFd &reader_get_event_fd() {
return event_fd_;
}
void writer_flush() {
//nop
}
void init() {
event_fd_.init();
}
void destroy() {
if (!event_fd_.empty()) {
event_fd_.close();
wait_event_fd_ = false;
writer_vector_.clear();
reader_vector_.clear();
reader_pos_ = 0;
}
}
// Just an example of usage
int reader_wait() {
int res;
while ((res = reader_wait_nonblock()) == 0) {
reader_get_event_fd().wait(1000);
}
return res;
}
private:
SpinLock lock_;
bool wait_event_fd_{false};
EventFd event_fd_;
std::vector<ValueType> writer_vector_;
std::vector<ValueType> reader_vector_;
size_t reader_pos_{0};
};
} // namespace td
#else
namespace td {
// dummy implementation which shouldn't be used
template <class T>
class MpscPollableQueue {
public:
using ValueType = T;
void init() {
UNREACHABLE();
}
template <class PutValueType>
void writer_put(PutValueType &&value) {
UNREACHABLE();
}
void writer_flush() {
UNREACHABLE();
}
int reader_wait_nonblock() {
UNREACHABLE();
return 0;
}
ValueType reader_get_unsafe() {
UNREACHABLE();
return ValueType();
}
void reader_flush() {
UNREACHABLE();
}
MpscPollableQueue() = default;
MpscPollableQueue(const MpscPollableQueue &) = delete;
MpscPollableQueue &operator=(const MpscPollableQueue &) = delete;
MpscPollableQueue(MpscPollableQueue &&) = delete;
MpscPollableQueue &operator=(MpscPollableQueue &&) = delete;
~MpscPollableQueue() = default;
};
} // namespace td
#endif

39
tdutils/td/utils/Named.h Normal file
View file

@ -0,0 +1,39 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include "td/utils/Slice.h"
namespace td {
class Named {
public:
Slice get_name() const {
return name_;
}
void set_name(Slice name) {
name_ = name.str();
}
private:
string name_;
};
} // namespace td

View file

@ -0,0 +1,261 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include "td/utils/logging.h"
#include <atomic>
#include <memory>
#include <utility>
namespace td {
// It is draft object pool implementaion
//
// Compared with std::shared_ptr:
// + WeakPtr are much faster. Just pointer copy. No barriers, no atomics.
// - We can't destroy object, because we don't know if it is pointed to by some weak pointer
//
template <class DataT>
class ObjectPool {
struct Storage;
public:
class WeakPtr {
public:
WeakPtr() : generation_(-1), storage_(nullptr) {
}
WeakPtr(int32 generation, Storage *storage) : generation_(generation), storage_(storage) {
}
DataT &operator*() const {
return storage_->data;
}
DataT *operator->() const {
return &**this;
}
// Pattern of usage: 1. Read an object 2. Check if read was valid via is_alive
//
// 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 our case if we have used an object and it is already invalid, then generation will mismatch
bool is_alive() const {
if (!storage_) {
return false;
}
std::atomic_thread_fence(std::memory_order_acquire);
return generation_ == storage_->generation.load(std::memory_order_relaxed);
}
// Used for ActorId
bool is_alive_unsafe() const {
if (!storage_) {
return false;
}
return generation_ == storage_->generation.load(std::memory_order_relaxed);
}
bool empty() const {
return storage_ == nullptr;
}
void clear() {
generation_ = -1;
storage_ = nullptr;
}
int32 generation() {
return generation_;
}
private:
int32 generation_;
Storage *storage_;
};
class OwnerPtr {
public:
OwnerPtr() = default;
OwnerPtr(const OwnerPtr &) = delete;
OwnerPtr &operator=(const OwnerPtr &) = delete;
OwnerPtr(OwnerPtr &&other) : storage_(other.storage_), parent_(other.parent_) {
other.storage_ = nullptr;
other.parent_ = nullptr;
}
OwnerPtr &operator=(OwnerPtr &&other) {
if (this != &other) {
storage_ = other.storage_;
parent_ = other.parent_;
other.storage_ = nullptr;
other.parent_ = nullptr;
}
return *this;
}
~OwnerPtr() {
reset();
}
DataT *get() {
return &storage_->data;
}
DataT &operator*() {
return *get();
}
DataT *operator->() {
return get();
}
const DataT *get() const {
return &storage_->data;
}
const DataT &operator*() const {
return *get();
}
const DataT *operator->() const {
return get();
}
WeakPtr get_weak() {
return WeakPtr(storage_->generation.load(std::memory_order_relaxed), storage_);
}
int32 generation() {
return storage_->generation.load(std::memory_order_relaxed);
}
Storage *release() {
auto result = storage_;
storage_ = nullptr;
return result;
}
bool empty() const {
return storage_ == nullptr;
}
void reset() {
if (storage_ != nullptr) {
// for crazy cases when data owns owner pointer to itself.
auto tmp = storage_;
storage_ = nullptr;
parent_->release(OwnerPtr(tmp, parent_));
}
}
private:
friend class ObjectPool;
OwnerPtr(Storage *storage, ObjectPool<DataT> *parent) : storage_(storage), parent_(parent) {
}
Storage *storage_ = nullptr;
ObjectPool<DataT> *parent_ = nullptr;
};
template <class... ArgsT>
OwnerPtr create(ArgsT &&... args) {
Storage *storage = get_storage();
storage->init_data(std::forward<ArgsT>(args)...);
return OwnerPtr(storage, this);
}
OwnerPtr create_empty() {
Storage *storage = get_storage();
return OwnerPtr(storage, this);
}
void set_check_empty(bool flag) {
check_empty_flag_ = flag;
}
void release(OwnerPtr &&owner_ptr) {
Storage *storage = owner_ptr.release();
storage->destroy_data();
release_storage(storage);
}
ObjectPool() = default;
ObjectPool(const ObjectPool &) = delete;
ObjectPool &operator=(const ObjectPool &) = delete;
ObjectPool(ObjectPool &&other) = delete;
ObjectPool &operator=(ObjectPool &&other) = delete;
~ObjectPool() {
while (head_.load()) {
auto to_delete = head_.load();
head_ = to_delete->next;
delete to_delete;
storage_count_--;
}
LOG_CHECK(storage_count_.load() == 0) << storage_count_.load();
}
private:
struct Storage {
// union {
DataT data;
//};
Storage *next = nullptr;
std::atomic<int32> generation{1};
template <class... ArgsT>
void init_data(ArgsT &&... args) {
// new (&data) DataT(std::forward<ArgsT>(args)...);
data = DataT(std::forward<ArgsT>(args)...);
}
void destroy_data() {
generation.fetch_add(1, std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_release);
data.clear();
}
};
std::atomic<int32> storage_count_{0};
std::atomic<Storage *> head_{static_cast<Storage *>(nullptr)};
bool check_empty_flag_ = false;
// TODO(perf): allocation Storages in chunks? Anyway we won't be able to release them.
// TODO(perf): memory order
// TODO(perf): use another non lockfree list for release on the same thread
// only one thread, so no aba problem
Storage *get_storage() {
if (head_.load() == nullptr) {
storage_count_++;
return new Storage();
}
Storage *res;
while (true) {
res = head_.load();
auto *next = res->next;
if (head_.compare_exchange_weak(res, next)) {
break;
}
}
return res;
}
// release can be called from other thread
void release_storage(Storage *storage) {
while (true) {
auto *save_head = head_.load();
storage->next = save_head;
if (head_.compare_exchange_weak(save_head, storage)) {
break;
}
}
}
};
} // namespace td

View file

@ -0,0 +1,53 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
namespace td {
class ObserverBase {
public:
ObserverBase() = default;
ObserverBase(const ObserverBase &) = delete;
ObserverBase &operator=(const ObserverBase &) = delete;
ObserverBase(ObserverBase &&) = delete;
ObserverBase &operator=(ObserverBase &&) = delete;
virtual ~ObserverBase() = default;
virtual void notify() = 0;
};
class Observer : ObserverBase {
public:
Observer() = default;
explicit Observer(unique_ptr<ObserverBase> &&ptr) : observer_ptr_(std::move(ptr)) {
}
void notify() override {
if (observer_ptr_) {
observer_ptr_->notify();
}
}
private:
unique_ptr<ObserverBase> observer_ptr_;
};
} // namespace td

View file

@ -0,0 +1,142 @@
/*
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-2019 Telegram Systems LLP
*/
#include "td/utils/OptionsParser.h"
#if TD_HAVE_GETOPT
#include "getopt.h"
#endif
#if !TD_WINDOWS
#include <getopt.h>
#include <unistd.h>
#endif
namespace td {
void OptionsParser::set_description(std::string description) {
description_ = std::move(description);
}
void OptionsParser::add_option(Option::Type type, char short_key, Slice long_key, Slice description,
std::function<Status(Slice)> callback) {
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,
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,
std::function<Status(void)> callback) {
// Ouch. There must be some better way
add_option(Option::Type::NoArg, short_key, long_key, description,
std::bind([](std::function<Status(void)> &func, Slice) { return func(); }, std::move(callback),
std::placeholders::_1));
}
Result<int> OptionsParser::run(int argc, char *argv[]) {
#if TD_HAVE_GETOPT
char buff[1024];
StringBuilder sb(MutableSlice{buff, sizeof(buff)});
for (auto &opt : options_) {
CHECK(opt.type != Option::OptionalArg);
sb << opt.short_key;
if (opt.type == Option::Arg) {
sb << ":";
}
}
if (sb.is_error()) {
return Status::Error("Can't parse options");
}
CSlice short_options = sb.as_cslice();
vector<option> long_options;
for (auto &opt : options_) {
if (opt.long_key.empty()) {
continue;
}
option o;
o.flag = nullptr;
o.val = opt.short_key;
o.has_arg = opt.type == Option::Arg ? required_argument : no_argument;
o.name = opt.long_key.c_str();
long_options.push_back(o);
}
long_options.push_back({nullptr, 0, nullptr, 0});
while (true) {
int opt_i = getopt_long(argc, argv, short_options.c_str(), &long_options[0], nullptr);
if (opt_i == ':') {
return Status::Error("Missing argument");
}
if (opt_i == '?') {
return Status::Error("Unrecognized option");
}
if (opt_i == -1) {
break;
}
bool found = false;
for (auto &opt : options_) {
if (opt.short_key == opt_i) {
Slice arg;
if (opt.type == Option::Arg) {
arg = Slice(optarg);
}
auto status = opt.arg_callback(arg);
if (status.is_error()) {
return std::move(status);
}
found = true;
break;
}
}
if (!found) {
return Status::Error("Unknown argument");
}
}
return optind;
#else
return -1;
#endif
}
StringBuilder &operator<<(StringBuilder &sb, const OptionsParser &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) {
sb << "[";
}
if (opt.type != OptionsParser::Option::NoArg) {
sb << "<arg>";
}
if (opt.type == OptionsParser::Option::OptionalArg) {
sb << "]";
}
sb << "\t" << opt.description;
sb << "\n";
}
return sb;
}
} // namespace td

View file

@ -0,0 +1,60 @@
/*
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-2019 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 OptionsParser {
class Option {
public:
enum Type { NoArg, Arg, OptionalArg };
Type type;
char short_key;
std::string long_key;
std::string description;
std::function<Status(Slice)> arg_callback;
};
public:
void set_description(std::string description);
void add_option(Option::Type type, char short_key, Slice long_key, Slice description,
std::function<Status(Slice)> callback);
void add_option(char short_key, Slice long_key, Slice description, std::function<Status(Slice)> callback);
void add_option(char short_key, Slice long_key, Slice description, std::function<Status(void)> callback);
Result<int> run(int argc, char *argv[]) TD_WARN_UNUSED_RESULT;
friend StringBuilder &operator<<(StringBuilder &sb, const OptionsParser &o);
private:
std::vector<Option> options_;
std::string description_;
};
} // namespace td

View file

@ -0,0 +1,111 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include "td/utils/logging.h"
#include <utility>
namespace td {
// Process states in order defined by their Id
template <class DataT>
class OrderedEventsProcessor {
public:
using SeqNo = uint64;
OrderedEventsProcessor() = default;
explicit OrderedEventsProcessor(SeqNo offset) : offset_(offset), begin_(offset_), end_(offset_) {
}
template <class FunctionT>
void clear(FunctionT &&function) {
for (auto &it : data_array_) {
if (it.second) {
function(std::move(it.first));
}
}
*this = OrderedEventsProcessor();
}
void clear() {
*this = OrderedEventsProcessor();
}
template <class FromDataT, class FunctionT>
void add(SeqNo seq_no, FromDataT &&data, FunctionT &&function) {
LOG_CHECK(seq_no >= begin_) << seq_no << ">=" << begin_; // or ignore?
if (seq_no == begin_) { // run now
begin_++;
function(seq_no, std::forward<FromDataT>(data));
while (begin_ < end_) {
auto &data_flag = data_array_[static_cast<size_t>(begin_ - offset_)];
if (!data_flag.second) {
break;
}
function(begin_, std::move(data_flag.first));
data_flag.second = false;
begin_++;
}
if (begin_ > end_) {
end_ = begin_;
}
if (begin_ == end_) {
offset_ = begin_;
}
// try_compactify
auto begin_pos = static_cast<size_t>(begin_ - offset_);
if (begin_pos > 5 && begin_pos * 2 > data_array_.size()) {
data_array_.erase(data_array_.begin(), data_array_.begin() + begin_pos);
offset_ = begin_;
}
} else {
auto pos = static_cast<size_t>(seq_no - offset_);
auto need_size = pos + 1;
if (data_array_.size() < need_size) {
data_array_.resize(need_size);
}
data_array_[pos].first = std::forward<FromDataT>(data);
data_array_[pos].second = true;
if (end_ < seq_no + 1) {
end_ = seq_no + 1;
}
}
}
bool has_events() const {
return begin_ != end_;
}
SeqNo max_unfinished_seq_no() {
return end_ - 1;
}
SeqNo max_finished_seq_no() {
return begin_ - 1;
}
private:
SeqNo offset_ = 1;
SeqNo begin_ = 1;
SeqNo end_ = 1;
std::vector<std::pair<DataT, bool>> data_array_;
};
} // namespace td

209
tdutils/td/utils/Parser.h Normal file
View file

@ -0,0 +1,209 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include "td/utils/format.h"
#include "td/utils/logging.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
#include <cstring>
#include <utility>
namespace td {
namespace detail {
template <class SliceT>
class ParserImpl {
public:
explicit ParserImpl(SliceT data) : ptr_(data.begin()), end_(data.end()), status_() {
}
ParserImpl(ParserImpl &&other) : ptr_(other.ptr_), end_(other.end_), status_(std::move(other.status_)) {
other.clear();
}
ParserImpl &operator=(ParserImpl &&other) {
if (&other == this) {
return *this;
}
ptr_ = other.ptr_;
end_ = other.end_;
status_ = std::move(other.status_);
other.clear();
return *this;
}
ParserImpl(const ParserImpl &) = delete;
ParserImpl &operator=(const ParserImpl &) = delete;
~ParserImpl() = default;
bool empty() const {
return ptr_ == end_;
}
void clear() {
ptr_ = nullptr;
end_ = ptr_;
status_ = Status::OK();
}
SliceT read_till_nofail(char c) {
if (status_.is_error()) {
return SliceT();
}
auto till = static_cast<decltype(ptr_)>(std::memchr(ptr_, c, end_ - ptr_));
if (till == nullptr) {
till = end_;
}
SliceT result(ptr_, till);
ptr_ = till;
return result;
}
SliceT read_till_nofail(Slice str) {
if (status_.is_error()) {
return SliceT();
}
auto best_till = end_;
for (auto c : str) {
auto till = static_cast<decltype(ptr_)>(std::memchr(ptr_, c, end_ - ptr_));
if (till != nullptr && till < best_till) {
best_till = till;
}
}
SliceT result(ptr_, best_till);
ptr_ = best_till;
return result;
}
template <class F>
SliceT read_while(const F &f) {
auto save_ptr = ptr_;
while (ptr_ != end_ && f(*ptr_)) {
ptr_++;
}
return SliceT(save_ptr, ptr_);
}
SliceT read_all() {
auto save_ptr = ptr_;
ptr_ = end_;
return SliceT(save_ptr, ptr_);
}
SliceT read_till(char c) {
if (status_.is_error()) {
return SliceT();
}
SliceT res = read_till_nofail(c);
if (ptr_ == end_ || ptr_[0] != c) {
status_ = Status::Error(PSLICE() << "Read till " << tag("char", c) << " failed");
return SliceT();
}
return res;
}
char peek_char() {
if (ptr_ == end_) {
return 0;
}
return *ptr_;
}
char *ptr() {
return ptr_;
}
void skip_nofail(char c) {
if (ptr_ != end_ && ptr_[0] == c) {
ptr_++;
}
}
void skip(char c) {
if (status_.is_error()) {
return;
}
if (ptr_ == end_ || ptr_[0] != c) {
status_ = Status::Error(PSLICE() << "Skip " << tag("char", c) << " failed");
return;
}
ptr_++;
}
bool try_skip(char c) {
if (ptr_ != end_ && ptr_[0] == c) {
ptr_++;
return true;
}
return false;
}
void skip_till_not(Slice str) {
while (ptr_ != end_) {
if (std::memchr(str.data(), *ptr_, str.size()) == nullptr) {
break;
}
ptr_++;
}
}
void skip_whitespaces() {
skip_till_not(" \t\r\n");
}
SliceT read_word() {
skip_whitespaces();
return read_till_nofail(" \t\r\n");
}
SliceT data() const {
return SliceT(ptr_, end_);
}
Status &status() {
return status_;
}
bool start_with(Slice prefix) const {
if (prefix.size() > static_cast<size_t>(end_ - ptr_)) {
return false;
}
return prefix == Slice(ptr_, prefix.size());
}
bool skip_start_with(Slice prefix) {
if (start_with(prefix)) {
advance(prefix.size());
return true;
}
return false;
}
void advance(size_t diff) {
ptr_ += diff;
CHECK(ptr_ <= end_);
}
private:
decltype(std::declval<SliceT>().begin()) ptr_;
decltype(std::declval<SliceT>().end()) end_;
Status status_;
};
} // namespace detail
using Parser = detail::ParserImpl<MutableSlice>;
using ConstParser = detail::ParserImpl<Slice>;
} // namespace td

128
tdutils/td/utils/PathView.h Normal file
View file

@ -0,0 +1,128 @@
/*
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-2019 Telegram Systems LLP
*/
#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;
}
}
}
bool empty() const {
return path_.empty();
}
bool is_dir() const {
if (empty()) {
return false;
}
return is_slash(path_.back());
}
Slice parent_dir() const {
return path_.substr(0, last_slash_ + 1);
}
Slice extension() const {
if (last_dot_ == static_cast<int32>(path_.size())) {
return Slice();
}
return path_.substr(last_dot_ + 1);
}
Slice without_extension() const {
return path_.substr(0, last_dot_);
}
Slice file_stem() const {
return path_.substr(last_slash_ + 1, last_dot_ - last_slash_ - 1);
}
Slice file_name() const {
return path_.substr(last_slash_ + 1);
}
Slice path() const {
return path_;
}
bool is_absolute() const {
return !empty() && (is_slash(path_[0]) || (path_.size() >= 3 && path_[1] == ':' && is_slash(path_[2])));
}
bool is_relative() const {
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);
}
private:
static bool is_slash(char c) {
return c == '/' || c == '\\';
}
Slice path_;
int32 last_slash_;
int32 last_dot_;
};
} // namespace td

187
tdutils/td/utils/Random.cpp Normal file
View file

@ -0,0 +1,187 @@
/*
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-2019 Telegram Systems LLP
*/
#include "td/utils/Random.h"
#include "td/utils/logging.h"
#include "td/utils/port/thread_local.h"
#if TD_HAVE_OPENSSL
#include <openssl/rand.h>
#endif
#include <atomic>
#include <cstring>
#include <limits>
#include <random>
namespace td {
#if TD_HAVE_OPENSSL
namespace {
std::atomic<int64> random_seed_generation{0};
} // namespace
void Random::secure_bytes(MutableSlice dest) {
Random::secure_bytes(dest.ubegin(), dest.size());
}
void Random::secure_bytes(unsigned char *ptr, size_t size) {
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;
generation = 0;
}
if (generation != random_seed_generation.load(std::memory_order_relaxed)) {
generation = random_seed_generation.load(std::memory_order_acquire);
buf_pos = buf_size;
}
auto ready = min(size, buf_size - buf_pos);
if (ready != 0) {
std::memcpy(ptr, buf + buf_pos, ready);
buf_pos += ready;
ptr += ready;
size -= ready;
if (size == 0) {
return;
}
}
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;
std::memcpy(ptr, buf, size);
return;
}
CHECK(size <= static_cast<size_t>(std::numeric_limits<int>::max()));
int err = RAND_bytes(ptr, static_cast<int>(size));
// TODO: it CAN fail
LOG_IF(FATAL, err != 1);
}
int32 Random::secure_int32() {
int32 res = 0;
secure_bytes(reinterpret_cast<unsigned char *>(&res), sizeof(int32));
return res;
}
int64 Random::secure_int64() {
int64 res = 0;
secure_bytes(reinterpret_cast<unsigned char *>(&res), sizeof(int64));
return res;
}
uint32 Random::secure_uint32() {
uint32 res = 0;
secure_bytes(reinterpret_cast<unsigned char *>(&res), sizeof(uint32));
return res;
}
uint64 Random::secure_uint64() {
uint64 res = 0;
secure_bytes(reinterpret_cast<unsigned char *>(&res), sizeof(uint64));
return res;
}
void Random::add_seed(Slice bytes, double entropy) {
RAND_add(bytes.data(), static_cast<int>(bytes.size()), entropy);
random_seed_generation++;
}
#endif
static unsigned int rand_device_helper() {
static TD_THREAD_LOCAL std::random_device *rd;
init_thread_local<std::random_device>(rd);
return (*rd)();
}
uint32 Random::fast_uint32() {
static TD_THREAD_LOCAL std::mt19937 *gen;
if (!gen) {
auto &rg = rand_device_helper;
std::seed_seq seq{rg(), rg(), rg(), rg(), rg(), rg(), rg(), rg(), rg(), rg(), rg(), rg()};
init_thread_local<std::mt19937>(gen, seq);
}
return static_cast<uint32>((*gen)());
}
uint64 Random::fast_uint64() {
static TD_THREAD_LOCAL std::mt19937_64 *gen;
if (!gen) {
auto &rg = rand_device_helper;
std::seed_seq seq{rg(), rg(), rg(), rg(), rg(), rg(), rg(), rg(), rg(), rg(), rg(), rg()};
init_thread_local<std::mt19937_64>(gen, seq);
}
return static_cast<uint64>((*gen)());
}
int Random::fast(int min, int max) {
if (min == std::numeric_limits<int>::min() && max == std::numeric_limits<int>::max()) {
// to prevent integer overflow and division by zero
min++;
}
DCHECK(min <= max);
return static_cast<int>(min + fast_uint32() % (max - min + 1)); // TODO signed_cast
}
double Random::fast(double min, double max) {
DCHECK(min <= max);
return min +
fast_uint32() * 1.0 /
(static_cast<double>(std::numeric_limits<td::uint32>::max()) - std::numeric_limits<td::uint32>::min()) *
(max - min);
}
Random::Xorshift128plus::Xorshift128plus(uint64 seed) {
auto next = [&]() {
// splitmix64
seed += static_cast<uint64>(0x9E3779B97F4A7C15);
uint64 z = seed;
z = (z ^ (z >> 30)) * static_cast<uint64>(0xBF58476D1CE4E5B9);
z = (z ^ (z >> 27)) * static_cast<uint64>(0x94D049BB133111EB);
return z ^ (z >> 31);
};
seed_[0] = next();
seed_[1] = next();
}
Random::Xorshift128plus::Xorshift128plus(uint64 seed_a, uint64 seed_b) {
seed_[0] = seed_a;
seed_[1] = seed_b;
}
uint64 Random::Xorshift128plus::operator()() {
uint64 x = seed_[0];
const uint64 y = seed_[1];
seed_[0] = y;
x ^= x << 23;
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);
}
} // namespace td

59
tdutils/td/utils/Random.h Normal file
View file

@ -0,0 +1,59 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include "td/utils/Slice.h"
namespace td {
class Random {
public:
#if TD_HAVE_OPENSSL
static void secure_bytes(MutableSlice dest);
static void secure_bytes(unsigned char *ptr, size_t size);
static int32 secure_int32();
static int64 secure_int64();
static uint32 secure_uint32();
static uint64 secure_uint64();
// works only for current thread
static void add_seed(Slice bytes, double entropy = 0);
#endif
static uint32 fast_uint32();
static uint64 fast_uint64();
// distribution is not uniform, min and max are included
static int fast(int min, int max);
static double fast(double min, double max);
class Xorshift128plus {
public:
explicit Xorshift128plus(uint64 seed);
Xorshift128plus(uint64 seed_a, uint64 seed_b);
uint64 operator()();
int fast(int min, int max);
private:
uint64 seed_[2];
};
};
} // namespace td

View file

@ -0,0 +1,89 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include <cstdlib>
#include <memory>
#include <type_traits>
#include <utility>
namespace td {
class Guard {
public:
Guard() = default;
Guard(const Guard &other) = delete;
Guard &operator=(const Guard &other) = delete;
Guard(Guard &&other) = default;
Guard &operator=(Guard &&other) = default;
virtual ~Guard() = default;
virtual void dismiss() {
std::abort();
}
};
template <class FunctionT>
class LambdaGuard : public Guard {
public:
explicit LambdaGuard(const FunctionT &func) : func_(func) {
}
explicit LambdaGuard(FunctionT &&func) : func_(std::move(func)) {
}
LambdaGuard(const LambdaGuard &other) = delete;
LambdaGuard &operator=(const LambdaGuard &other) = delete;
LambdaGuard(LambdaGuard &&other) : func_(std::move(other.func_)), dismissed_(other.dismissed_) {
other.dismissed_ = true;
}
LambdaGuard &operator=(LambdaGuard &&other) = delete;
void dismiss() {
dismissed_ = true;
}
~LambdaGuard() {
if (!dismissed_) {
func_();
}
}
private:
FunctionT func_;
bool dismissed_ = false;
};
template <class F>
unique_ptr<Guard> create_lambda_guard(F &&f) {
return make_unique<LambdaGuard<F>>(std::forward<F>(f));
}
template <class F>
std::shared_ptr<Guard> create_shared_lambda_guard(F &&f) {
return std::make_shared<LambdaGuard<F>>(std::forward<F>(f));
}
enum class ScopeExit {};
template <class FunctionT>
auto operator+(ScopeExit, FunctionT &&func) {
return LambdaGuard<std::decay_t<FunctionT>>(std::forward<FunctionT>(func));
}
} // namespace td
#define SCOPE_EXIT auto TD_CONCAT(SCOPE_EXIT_VAR_, __LINE__) = ::td::ScopeExit() + [&]()

View file

@ -0,0 +1,294 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include "td/utils/logging.h"
#include "td/utils/MpscLinkQueue.h"
#include <atomic>
#include <memory>
#include <new>
#include <utility>
namespace td {
namespace detail {
class AtomicRefCnt {
public:
explicit AtomicRefCnt(uint64 cnt) : cnt_(cnt) {
}
void inc() {
cnt_.fetch_add(1, std::memory_order_relaxed);
}
bool dec() {
return cnt_.fetch_sub(1, std::memory_order_acq_rel) == 1;
}
uint64 value() const {
return cnt_.load(std::memory_order_relaxed);
}
private:
std::atomic<uint64> cnt_{0};
};
template <class DataT, class DeleterT>
class SharedPtrRaw
: public DeleterT
, private MpscLinkQueueImpl::Node {
public:
explicit SharedPtrRaw(DeleterT deleter) : DeleterT(std::move(deleter)), ref_cnt_{0}, option_magic_(Magic) {
}
~SharedPtrRaw() {
CHECK(use_cnt() == 0);
CHECK(option_magic_ == Magic);
}
template <class... ArgsT>
void init_data(ArgsT &&... args) {
new (&option_data_) DataT(std::forward<ArgsT>(args)...);
}
void destroy_data() {
option_data_.~DataT();
option_magic_ = Magic;
}
uint64 use_cnt() const {
return ref_cnt_.value();
}
void inc() {
ref_cnt_.inc();
}
bool dec() {
return ref_cnt_.dec();
}
DataT &data() {
return option_data_;
}
static SharedPtrRaw *from_mpsc_link_queue_node(MpscLinkQueueImpl::Node *node) {
return static_cast<SharedPtrRaw<DataT, DeleterT> *>(node);
}
MpscLinkQueueImpl::Node *to_mpsc_link_queue_node() {
return static_cast<MpscLinkQueueImpl::Node *>(this);
}
private:
AtomicRefCnt ref_cnt_;
enum { Magic = 0x732817a2 };
union {
DataT option_data_;
uint32 option_magic_;
};
};
template <class T, class DeleterT = std::default_delete<T>>
class SharedPtr {
public:
using Raw = detail::SharedPtrRaw<T, DeleterT>;
SharedPtr() = default;
~SharedPtr() {
if (!raw_) {
return;
}
reset();
}
explicit SharedPtr(Raw *raw) : raw_(raw) {
if (raw_) {
raw_->inc();
}
}
SharedPtr(const SharedPtr &other) : SharedPtr(other.raw_) {
}
SharedPtr &operator=(const SharedPtr &other) {
if (other.raw_) {
other.raw_->inc();
}
reset(other.raw_);
return *this;
}
SharedPtr(SharedPtr &&other) : raw_(other.raw_) {
other.raw_ = nullptr;
}
SharedPtr &operator=(SharedPtr &&other) {
reset(other.raw_);
other.raw_ = nullptr;
return *this;
}
bool empty() const {
return raw_ == nullptr;
}
explicit operator bool() const {
return !empty();
}
uint64 use_cnt() const {
if (!raw_) {
return 0;
}
return raw_->use_cnt();
}
T &operator*() const {
return raw_->data();
}
T *operator->() const {
return &raw_->data();
}
Raw *release() {
auto res = raw_;
raw_ = nullptr;
return res;
}
void reset(Raw *new_raw = nullptr) {
if (raw_ && raw_->dec()) {
raw_->destroy_data();
auto deleter = std::move(static_cast<DeleterT &>(*raw_));
deleter(raw_);
}
raw_ = new_raw;
}
template <class... ArgsT>
static SharedPtr<T, DeleterT> create(ArgsT &&... args) {
auto raw = make_unique<Raw>(DeleterT());
raw->init_data(std::forward<ArgsT>(args)...);
return SharedPtr<T, DeleterT>(raw.release());
}
template <class D, class... ArgsT>
static SharedPtr<T, DeleterT> create_with_deleter(D &&d, ArgsT &&... args) {
auto raw = make_unique<Raw>(std::forward<D>(d));
raw->init_data(std::forward<ArgsT>(args)...);
return SharedPtr<T, DeleterT>(raw.release());
}
bool operator==(const SharedPtr<T, DeleterT> &other) const {
return raw_ == other.raw_;
}
private:
Raw *raw_{nullptr};
};
} // namespace detail
template <class DataT>
class SharedObjectPool {
class Deleter;
public:
using Ptr = detail::SharedPtr<DataT, Deleter>;
SharedObjectPool() = default;
SharedObjectPool(const SharedObjectPool &other) = delete;
SharedObjectPool &operator=(const SharedObjectPool &other) = delete;
SharedObjectPool(SharedObjectPool &&other) = delete;
SharedObjectPool &operator=(SharedObjectPool &&other) = delete;
~SharedObjectPool() {
free_queue_.pop_all(free_queue_reader_);
size_t free_cnt = 0;
while (free_queue_reader_.read()) {
free_cnt++;
}
LOG_CHECK(free_cnt == allocated_.size()) << free_cnt << " " << allocated_.size();
}
template <class... ArgsT>
Ptr alloc(ArgsT &&... args) {
auto *raw = alloc_raw();
raw->init_data(std::forward<ArgsT>(args)...);
return Ptr(raw);
}
size_t total_size() const {
return allocated_.size();
}
uint64 calc_free_size() {
free_queue_.pop_all(free_queue_reader_);
return free_queue_reader_.calc_size();
}
//non thread safe
template <class F>
void for_each(F &&f) {
for (auto &raw : allocated_) {
if (raw->use_cnt() > 0) {
f(raw->data());
}
}
}
private:
using Raw = typename Ptr::Raw;
Raw *alloc_raw() {
free_queue_.pop_all(free_queue_reader_);
auto *raw = free_queue_reader_.read().get();
if (raw) {
return raw;
}
allocated_.push_back(make_unique<Raw>(deleter()));
return allocated_.back().get();
}
void free_raw(Raw *raw) {
free_queue_.push(Node{raw});
}
class Node {
public:
Node() = default;
explicit Node(Raw *raw) : raw_(raw) {
}
MpscLinkQueueImpl::Node *to_mpsc_link_queue_node() {
return raw_->to_mpsc_link_queue_node();
}
static Node from_mpsc_link_queue_node(MpscLinkQueueImpl::Node *node) {
return Node{Raw::from_mpsc_link_queue_node(node)};
}
Raw *get() const {
return raw_;
}
explicit operator bool() const {
return raw_ != nullptr;
}
private:
Raw *raw_{nullptr};
};
class Deleter {
public:
explicit Deleter(SharedObjectPool<DataT> *pool) : pool_(pool) {
}
void operator()(Raw *raw) {
pool_->free_raw(raw);
};
private:
SharedObjectPool<DataT> *pool_;
};
friend class Deleter;
Deleter deleter() {
return Deleter(this);
}
std::vector<unique_ptr<Raw>> allocated_;
MpscLinkQueue<Node> free_queue_;
typename MpscLinkQueue<Node>::Reader free_queue_reader_;
};
} // namespace td

View file

@ -0,0 +1,29 @@
/*
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-2019 Telegram Systems LLP
*/
#include "td/utils/SharedSlice.h"
#include "td/utils/buffer.h"
namespace td {
BufferSlice SharedSlice::clone_as_buffer_slice() const {
return BufferSlice{as_slice().str()};
}
} // namespace td

View file

@ -0,0 +1,392 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/Slice.h"
#include <atomic>
#include <memory>
namespace td {
namespace detail {
struct SharedSliceHeader {
explicit SharedSliceHeader(size_t size) : refcnt_{1}, size_{size} {
}
void inc() {
refcnt_.fetch_add(1, std::memory_order_relaxed);
}
bool dec() {
return refcnt_.fetch_sub(1, std::memory_order_acq_rel) == 1;
}
bool is_unique() const {
// NB: race if std::memory_order_relaxed is used
// reader may see a change by a new writer
return refcnt_.load(std::memory_order_acquire) == 1;
}
size_t size() const {
return size_;
}
private:
std::atomic<uint64> refcnt_;
size_t size_;
};
struct UniqueSliceHeader {
explicit UniqueSliceHeader(size_t size) : size_{size} {
}
void inc() {
}
bool dec() {
return true;
}
bool is_unique() const {
return true;
}
size_t size() const {
return size_;
}
private:
size_t size_;
};
template <class HeaderT, bool zero_on_destruct = false>
class UnsafeSharedSlice {
public:
UnsafeSharedSlice() = default;
UnsafeSharedSlice clone() const {
if (is_null()) {
return UnsafeSharedSlice();
}
header()->inc();
return UnsafeSharedSlice(ptr_.get());
}
bool is_null() const {
return !ptr_;
}
bool is_unique() const {
if (is_null()) {
return true;
}
return header()->is_unique();
}
MutableSlice as_mutable_slice() {
if (is_null()) {
return MutableSlice();
}
return MutableSlice(ptr_.get() + sizeof(HeaderT), header()->size());
}
Slice as_slice() const {
if (is_null()) {
return Slice();
}
return Slice(ptr_.get() + sizeof(HeaderT), header()->size());
}
size_t size() const {
if (is_null()) {
return 0;
}
return header()->size();
}
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 header_ptr = new (ptr.get()) HeaderT(size);
CHECK(reinterpret_cast<char *>(header_ptr) == ptr.get());
return UnsafeSharedSlice(std::move(ptr));
}
static UnsafeSharedSlice create(Slice slice) {
auto res = create(slice.size());
res.as_mutable_slice().copy_from(slice);
return res;
}
void clear() {
ptr_.reset();
}
private:
explicit UnsafeSharedSlice(char *ptr) : ptr_(ptr) {
}
explicit UnsafeSharedSlice(std::unique_ptr<char[]> from) : ptr_(from.release()) {
}
HeaderT *header() const {
return reinterpret_cast<HeaderT *>(ptr_.get());
}
struct Destructor {
void operator()(char *ptr) {
auto header = reinterpret_cast<HeaderT *>(ptr);
if (header->dec()) {
if (zero_on_destruct) {
MutableSlice(ptr, sizeof(HeaderT) + header->size()).fill_zero_secure();
}
std::default_delete<char[]>()(ptr);
}
}
};
std::unique_ptr<char[], Destructor> ptr_;
};
} // namespace detail
class BufferSlice;
class UniqueSharedSlice;
class SharedSlice {
using Impl = detail::UnsafeSharedSlice<detail::SharedSliceHeader>;
public:
SharedSlice() = default;
explicit SharedSlice(Slice slice) : impl_(Impl::create(slice.size())) {
impl_.as_mutable_slice().copy_from(slice);
}
explicit SharedSlice(UniqueSharedSlice from);
SharedSlice(const char *ptr, size_t size) : SharedSlice(Slice(ptr, size)) {
}
SharedSlice clone() const {
return SharedSlice(impl_.clone());
}
Slice as_slice() const {
return impl_.as_slice();
}
BufferSlice clone_as_buffer_slice() const;
operator Slice() const {
return as_slice();
}
// like in std::string
const char *data() const {
return as_slice().data();
}
char operator[](size_t at) const {
return as_slice()[at];
}
bool empty() const {
return size() == 0;
}
size_t size() const {
return impl_.size();
}
// like in std::string
size_t length() const {
return size();
}
void clear() {
impl_.clear();
}
private:
friend class UniqueSharedSlice;
explicit SharedSlice(Impl impl) : impl_(std::move(impl)) {
}
Impl impl_;
};
class UniqueSharedSlice {
using Impl = detail::UnsafeSharedSlice<detail::SharedSliceHeader>;
public:
UniqueSharedSlice() = default;
explicit UniqueSharedSlice(size_t size) : impl_(Impl::create(size)) {
}
explicit UniqueSharedSlice(Slice slice) : impl_(Impl::create(slice)) {
}
UniqueSharedSlice(const char *ptr, size_t size) : UniqueSharedSlice(Slice(ptr, size)) {
}
explicit UniqueSharedSlice(SharedSlice from) : impl_() {
if (from.impl_.is_unique()) {
impl_ = std::move(from.impl_);
} else {
impl_ = Impl::create(from.as_slice());
}
}
UniqueSharedSlice copy() const {
return UniqueSharedSlice(as_slice());
}
Slice as_slice() const {
return impl_.as_slice();
}
MutableSlice as_mutable_slice() {
return impl_.as_mutable_slice();
}
operator Slice() const {
return as_slice();
}
// like in std::string
char *data() {
return as_mutable_slice().data();
}
const char *data() const {
return as_slice().data();
}
char operator[](size_t at) const {
return as_slice()[at];
}
bool empty() const {
return size() == 0;
}
size_t size() const {
return impl_.size();
}
// like in std::string
size_t length() const {
return size();
}
void clear() {
impl_.clear();
}
private:
friend class SharedSlice;
explicit UniqueSharedSlice(Impl impl) : impl_(std::move(impl)) {
}
Impl impl_;
};
inline SharedSlice::SharedSlice(UniqueSharedSlice from) : impl_(std::move(from.impl_)) {
}
template <bool zero_on_destruct>
class UniqueSliceImpl {
using Impl = detail::UnsafeSharedSlice<detail::UniqueSliceHeader, zero_on_destruct>;
public:
UniqueSliceImpl() = default;
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);
}
explicit UniqueSliceImpl(Slice slice) : impl_(Impl::create(slice)) {
}
UniqueSliceImpl(const char *ptr, size_t size) : UniqueSliceImpl(Slice(ptr, size)) {
}
UniqueSliceImpl copy() const {
return UniqueSliceImpl(as_slice());
}
Slice as_slice() const {
return impl_.as_slice();
}
MutableSlice as_mutable_slice() {
return impl_.as_mutable_slice();
}
operator Slice() const {
return as_slice();
}
// like in std::string
char *data() {
return as_mutable_slice().data();
}
const char *data() const {
return as_slice().data();
}
char operator[](size_t at) const {
return as_slice()[at];
}
bool empty() const {
return size() == 0;
}
size_t size() const {
return impl_.size();
}
// like in std::string
size_t length() const {
return size();
}
void clear() {
impl_.clear();
}
private:
explicit UniqueSliceImpl(Impl impl) : impl_(std::move(impl)) {
}
Impl impl_;
};
using UniqueSlice = UniqueSliceImpl<false>;
using SecureString = UniqueSliceImpl<true>;
inline MutableSlice as_mutable_slice(UniqueSharedSlice &unique_shared_slice) {
return unique_shared_slice.as_mutable_slice();
}
inline MutableSlice as_mutable_slice(UniqueSlice &unique_slice) {
return unique_slice.as_mutable_slice();
}
inline MutableSlice as_mutable_slice(SecureString &secure_string) {
return secure_string.as_mutable_slice();
}
} // namespace td

View file

@ -0,0 +1,224 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include <type_traits>
namespace td {
class Slice;
class MutableSlice {
char *s_;
size_t len_;
struct private_tag {};
public:
MutableSlice();
MutableSlice(char *s, size_t len);
MutableSlice(unsigned char *s, size_t len);
MutableSlice(string &s);
template <class T>
explicit MutableSlice(T s, std::enable_if_t<std::is_same<char *, T>::value, private_tag> = {});
MutableSlice(char *s, char *t);
MutableSlice(unsigned char *s, unsigned char *t);
template <size_t N>
constexpr MutableSlice(char (&a)[N]) = delete;
bool empty() const;
size_t size() const;
MutableSlice &remove_prefix(size_t prefix_len);
MutableSlice &remove_suffix(size_t suffix_len);
MutableSlice &truncate(size_t size);
MutableSlice copy() const;
char *data() const;
char *begin() const;
unsigned char *ubegin() const;
char *end() const;
unsigned char *uend() const;
string str() const;
MutableSlice substr(size_t from) const;
MutableSlice substr(size_t from, size_t size) const;
size_t find(char c) const;
size_t rfind(char c) const;
void fill(char c);
void fill_zero();
void fill_zero_secure();
void copy_from(Slice from);
char &back();
char &operator[](size_t i);
static const size_t npos = string::npos;
};
class Slice {
const char *s_;
size_t len_;
struct private_tag {};
public:
Slice();
Slice(const MutableSlice &other);
Slice(const char *s, size_t len);
Slice(const unsigned char *s, size_t len);
Slice(const string &s);
template <class T>
explicit Slice(T s, std::enable_if_t<std::is_same<char *, std::remove_const_t<T>>::value, private_tag> = {});
template <class T>
explicit Slice(T s, std::enable_if_t<std::is_same<const char *, std::remove_const_t<T>>::value, private_tag> = {});
Slice(const char *s, const char *t);
Slice(const unsigned char *s, const unsigned char *t);
template <size_t N>
constexpr Slice(char (&a)[N]) = delete;
template <size_t N>
constexpr Slice(const char (&a)[N]) : s_(a), len_(N - 1) {
}
Slice &operator=(string &&s) = delete;
template <size_t N>
constexpr Slice &operator=(char (&a)[N]) = delete;
template <size_t N>
constexpr Slice &operator=(const char (&a)[N]) {
s_ = a;
len_ = N - 1;
return *this;
}
bool empty() const;
size_t size() const;
Slice &remove_prefix(size_t prefix_len);
Slice &remove_suffix(size_t suffix_len);
Slice &truncate(size_t size);
Slice copy() const;
const char *data() const;
const char *begin() const;
const unsigned char *ubegin() const;
const char *end() const;
const unsigned char *uend() const;
string str() const;
Slice substr(size_t from) const;
Slice substr(size_t from, size_t size) const;
size_t find(char c) const;
size_t rfind(char c) const;
char back() const;
char operator[](size_t i) const;
static const size_t npos = string::npos;
};
bool operator==(const Slice &a, const Slice &b);
bool operator!=(const Slice &a, const Slice &b);
bool operator<(const Slice &a, const Slice &b);
class MutableCSlice : public MutableSlice {
struct private_tag {};
MutableSlice &remove_suffix(size_t suffix_len) = delete;
MutableSlice &truncate(size_t size) = delete;
public:
MutableCSlice() = delete;
MutableCSlice(string &s) : MutableSlice(s) {
}
template <class T>
explicit MutableCSlice(T s, std::enable_if_t<std::is_same<char *, T>::value, private_tag> = {}) : MutableSlice(s) {
}
MutableCSlice(char *s, char *t);
template <size_t N>
constexpr MutableCSlice(char (&a)[N]) = delete;
const char *c_str() const {
return begin();
}
};
class CSlice : public Slice {
struct private_tag {};
Slice &remove_suffix(size_t suffix_len) = delete;
Slice &truncate(size_t size) = delete;
public:
explicit CSlice(const MutableSlice &other) : Slice(other) {
}
CSlice(const MutableCSlice &other) : Slice(other.begin(), other.size()) {
}
CSlice(const string &s) : Slice(s) {
}
template <class T>
explicit CSlice(T s, std::enable_if_t<std::is_same<char *, std::remove_const_t<T>>::value, private_tag> = {})
: Slice(s) {
}
template <class T>
explicit CSlice(T s, std::enable_if_t<std::is_same<const char *, std::remove_const_t<T>>::value, private_tag> = {})
: Slice(s) {
}
CSlice(const char *s, const char *t);
template <size_t N>
constexpr CSlice(char (&a)[N]) = delete;
template <size_t N>
constexpr CSlice(const char (&a)[N]) : Slice(a) {
}
CSlice() : CSlice("") {
}
CSlice &operator=(string &&s) = delete;
template <size_t N>
constexpr CSlice &operator=(char (&a)[N]) = delete;
template <size_t N>
constexpr CSlice &operator=(const char (&a)[N]) {
this->Slice::operator=(a);
return *this;
}
const char *c_str() const {
return begin();
}
};
struct SliceHash {
std::size_t operator()(Slice slice) const;
};
} // namespace td

View file

@ -0,0 +1,43 @@
/*
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-2019 Telegram Systems LLP
*/
#include "td/utils/Slice.h"
#if TD_HAVE_OPENSSL
#include <openssl/crypto.h>
#endif
namespace td {
void MutableSlice::fill(char c) {
std::memset(data(), c, size());
}
void MutableSlice::fill_zero() {
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;
}
#endif
}
} // namespace td

339
tdutils/td/utils/Slice.h Normal file
View file

@ -0,0 +1,339 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include "td/utils/Slice-decl.h"
#include <cstring>
#include <type_traits>
namespace td {
inline MutableSlice::MutableSlice() : s_(const_cast<char *>("")), len_(0) {
}
inline MutableSlice::MutableSlice(char *s, size_t len) : s_(s), len_(len) {
CHECK(s_ != nullptr);
}
inline MutableSlice::MutableSlice(unsigned char *s, size_t len) : s_(reinterpret_cast<char *>(s)), len_(len) {
CHECK(s_ != nullptr);
}
inline MutableSlice::MutableSlice(string &s) : s_(&s[0]), len_(s.size()) {
}
template <class T>
MutableSlice::MutableSlice(T s, std::enable_if_t<std::is_same<char *, T>::value, private_tag>) : s_(s) {
CHECK(s_ != nullptr);
len_ = std::strlen(s_);
}
inline MutableSlice::MutableSlice(char *s, char *t) : MutableSlice(s, t - s) {
}
inline MutableSlice::MutableSlice(unsigned char *s, unsigned char *t) : MutableSlice(s, t - s) {
}
inline size_t MutableSlice::size() const {
return len_;
}
inline MutableSlice &MutableSlice::remove_prefix(size_t prefix_len) {
CHECK(prefix_len <= len_);
s_ += prefix_len;
len_ -= prefix_len;
return *this;
}
inline MutableSlice &MutableSlice::remove_suffix(size_t suffix_len) {
CHECK(suffix_len <= len_);
len_ -= suffix_len;
return *this;
}
inline MutableSlice &MutableSlice::truncate(size_t size) {
if (len_ > size) {
len_ = size;
}
return *this;
}
inline MutableSlice MutableSlice::copy() const {
return *this;
}
inline bool MutableSlice::empty() const {
return len_ == 0;
}
inline char *MutableSlice::data() const {
return s_;
}
inline char *MutableSlice::begin() const {
return s_;
}
inline unsigned char *MutableSlice::ubegin() const {
return reinterpret_cast<unsigned char *>(s_);
}
inline char *MutableSlice::end() const {
return s_ + len_;
}
inline unsigned char *MutableSlice::uend() const {
return reinterpret_cast<unsigned char *>(s_) + len_;
}
inline string MutableSlice::str() const {
return string(begin(), size());
}
inline MutableSlice MutableSlice::substr(size_t from) const {
CHECK(from <= len_);
return MutableSlice(s_ + from, len_ - from);
}
inline MutableSlice MutableSlice::substr(size_t from, size_t size) const {
CHECK(from <= len_);
return MutableSlice(s_ + from, min(size, len_ - from));
}
inline size_t MutableSlice::find(char c) const {
for (size_t pos = 0; pos < len_; pos++) {
if (s_[pos] == c) {
return pos;
}
}
return npos;
}
inline size_t MutableSlice::rfind(char c) const {
for (size_t pos = len_; pos-- > 0;) {
if (s_[pos] == c) {
return pos;
}
}
return npos;
}
inline void MutableSlice::copy_from(Slice from) {
CHECK(size() >= from.size());
std::memcpy(ubegin(), from.ubegin(), from.size());
}
inline char &MutableSlice::back() {
CHECK(1 <= len_);
return s_[len_ - 1];
}
inline char &MutableSlice::operator[](size_t i) {
return s_[i];
}
inline Slice::Slice() : s_(""), len_(0) {
}
inline Slice::Slice(const MutableSlice &other) : s_(other.begin()), len_(other.size()) {
}
inline Slice::Slice(const char *s, size_t len) : s_(s), len_(len) {
CHECK(s_ != nullptr);
}
inline Slice::Slice(const unsigned char *s, size_t len) : s_(reinterpret_cast<const char *>(s)), len_(len) {
CHECK(s_ != nullptr);
}
inline Slice::Slice(const string &s) : s_(s.c_str()), len_(s.size()) {
}
template <class T>
Slice::Slice(T s, std::enable_if_t<std::is_same<char *, std::remove_const_t<T>>::value, private_tag>) : s_(s) {
CHECK(s_ != nullptr);
len_ = std::strlen(s_);
}
template <class T>
Slice::Slice(T s, std::enable_if_t<std::is_same<const char *, std::remove_const_t<T>>::value, private_tag>) : s_(s) {
CHECK(s_ != nullptr);
len_ = std::strlen(s_);
}
inline Slice::Slice(const char *s, const char *t) : s_(s), len_(t - s) {
CHECK(s_ != nullptr);
}
inline Slice::Slice(const unsigned char *s, const unsigned char *t)
: s_(reinterpret_cast<const char *>(s)), len_(t - s) {
CHECK(s_ != nullptr);
}
inline size_t Slice::size() const {
return len_;
}
inline Slice &Slice::remove_prefix(size_t prefix_len) {
CHECK(prefix_len <= len_);
s_ += prefix_len;
len_ -= prefix_len;
return *this;
}
inline Slice &Slice::remove_suffix(size_t suffix_len) {
CHECK(suffix_len <= len_);
len_ -= suffix_len;
return *this;
}
inline Slice &Slice::truncate(size_t size) {
if (len_ > size) {
len_ = size;
}
return *this;
}
inline Slice Slice::copy() const {
return *this;
}
inline bool Slice::empty() const {
return len_ == 0;
}
inline const char *Slice::data() const {
return s_;
}
inline const char *Slice::begin() const {
return s_;
}
inline const unsigned char *Slice::ubegin() const {
return reinterpret_cast<const unsigned char *>(s_);
}
inline const char *Slice::end() const {
return s_ + len_;
}
inline const unsigned char *Slice::uend() const {
return reinterpret_cast<const unsigned char *>(s_) + len_;
}
inline string Slice::str() const {
return string(begin(), size());
}
inline Slice Slice::substr(size_t from) const {
CHECK(from <= len_);
return Slice(s_ + from, len_ - from);
}
inline Slice Slice::substr(size_t from, size_t size) const {
CHECK(from <= len_);
return Slice(s_ + from, min(size, len_ - from));
}
inline size_t Slice::find(char c) const {
for (size_t pos = 0; pos < len_; pos++) {
if (s_[pos] == c) {
return pos;
}
}
return npos;
}
inline size_t Slice::rfind(char c) const {
for (size_t pos = len_; pos-- > 0;) {
if (s_[pos] == c) {
return pos;
}
}
return npos;
}
inline char Slice::back() const {
CHECK(1 <= len_);
return s_[len_ - 1];
}
inline char Slice::operator[](size_t i) const {
return s_[i];
}
inline bool operator==(const Slice &a, const Slice &b) {
return a.size() == b.size() && std::memcmp(a.data(), b.data(), a.size()) == 0;
}
inline bool operator!=(const Slice &a, const Slice &b) {
return !(a == b);
}
inline bool operator<(const Slice &a, const Slice &b) {
auto x = std::memcmp(a.data(), b.data(), td::min(a.size(), b.size()));
if (x == 0) {
return a.size() < b.size();
}
return x < 0;
}
inline MutableCSlice::MutableCSlice(char *s, char *t) : MutableSlice(s, t) {
CHECK(*t == '\0');
}
inline CSlice::CSlice(const char *s, const char *t) : Slice(s, t) {
CHECK(*t == '\0');
}
inline std::size_t SliceHash::operator()(Slice slice) const {
// simple string hash
std::size_t result = 0;
constexpr std::size_t MUL = 123456789;
for (auto c : slice) {
result = result * MUL + c;
}
return result;
}
inline Slice as_slice(Slice slice) {
return slice;
}
inline MutableSlice as_slice(MutableSlice slice) {
return slice;
}
inline Slice as_slice(const string &str) {
return str;
}
inline MutableSlice as_slice(string &str) {
return str;
}
inline MutableSlice as_mutable_slice(MutableSlice slice) {
return slice;
}
inline MutableSlice as_mutable_slice(string &str) {
return str;
}
} // namespace td

138
tdutils/td/utils/Span.h Normal file
View file

@ -0,0 +1,138 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include <array>
namespace td {
namespace detail {
template <class T, class InnerT>
class SpanImpl {
InnerT *data_{nullptr};
size_t size_{0};
public:
SpanImpl() = default;
SpanImpl(InnerT *data, size_t size) : data_(data), size_(size) {
}
SpanImpl(InnerT &data) : SpanImpl(&data, 1) {
}
template <class OtherInnerT>
SpanImpl(const SpanImpl<T, OtherInnerT> &other) : SpanImpl(other.data(), other.size()) {
}
template <size_t N>
SpanImpl(const std::array<T, N> &arr) : SpanImpl(arr.data(), arr.size()) {
}
template <size_t N>
SpanImpl(std::array<T, N> &arr) : SpanImpl(arr.data(), arr.size()) {
}
template <size_t N>
SpanImpl(const T (&arr)[N]) : SpanImpl(arr, N) {
}
template <size_t N>
SpanImpl(T (&arr)[N]) : SpanImpl(arr, N) {
}
SpanImpl(const vector<T> &v) : SpanImpl(v.data(), v.size()) {
}
SpanImpl(vector<T> &v) : SpanImpl(v.data(), v.size()) {
}
template <class OtherInnerT>
SpanImpl &operator=(const SpanImpl<T, OtherInnerT> &other) {
SpanImpl copy{other};
*this = copy;
}
template <class OtherInnerT>
bool operator==(const SpanImpl<T, OtherInnerT> &other) const {
if (size() != other.size()) {
return false;
}
for (size_t i = 0; i < size(); i++) {
if (!((*this)[i] == other[i])) {
return false;
}
}
return true;
}
InnerT &operator[](size_t i) {
DCHECK(i < size());
return data_[i];
}
const InnerT &operator[](size_t i) const {
DCHECK(i < size());
return data_[i];
}
InnerT *data() const {
return data_;
}
InnerT *begin() const {
return data_;
}
InnerT *end() const {
return data_ + size_;
}
size_t size() const {
return size_;
}
bool empty() const {
return size() == 0;
}
SpanImpl &truncate(size_t size) {
CHECK(size <= size_);
size_ = size;
return *this;
}
SpanImpl substr(size_t offset) const {
CHECK(offset <= size_);
return SpanImpl(begin() + offset, size_ - offset);
}
SpanImpl substr(size_t offset, size_t size) const {
CHECK(offset <= size_);
CHECK(size_ - offset >= size);
return SpanImpl(begin() + offset, size);
}
};
} // namespace detail
template <class T>
using Span = detail::SpanImpl<T, const T>;
template <class T>
using MutableSpan = detail::SpanImpl<T, T>;
template <class T>
Span<T> span(const T *ptr, size_t size) {
return Span<T>(ptr, size);
}
template <class T>
MutableSpan<T> mutable_span(T *ptr, size_t size) {
return MutableSpan<T>(ptr, size);
}
} // 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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/port/thread.h"
#include <atomic>
#include <memory>
namespace td {
class SpinLock {
struct Unlock {
void operator()(SpinLock *ptr) {
ptr->unlock();
}
};
class InfBackoff {
int cnt = 0;
public:
bool next() {
cnt++;
if (cnt < 50) {
//TODO pause
return true;
} else {
td::this_thread::yield();
return true;
}
}
};
public:
using Lock = std::unique_ptr<SpinLock, Unlock>;
Lock lock() {
InfBackoff backoff;
while (!try_lock()) {
backoff.next();
}
return Lock(this);
}
bool try_lock() {
return !flag_.test_and_set(std::memory_order_acquire);
}
private:
std::atomic_flag flag_ = ATOMIC_FLAG_INIT;
void unlock() {
flag_.clear(std::memory_order_release);
}
};
} // namespace td

View file

@ -0,0 +1,31 @@
/*
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-2019 Telegram Systems LLP
*/
#include "td/utils/StackAllocator.h"
#include "td/utils/port/thread_local.h"
namespace td {
StackAllocator::Impl &StackAllocator::impl() {
static TD_THREAD_LOCAL StackAllocator::Impl *impl; // static zero-initialized
init_thread_local<Impl>(impl);
return *impl;
}
} // namespace td

View file

@ -0,0 +1,95 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include "td/utils/MovableValue.h"
#include "td/utils/Slice.h"
#include <array>
#include <cstdlib>
#include <memory>
namespace td {
class StackAllocator {
class Deleter {
public:
void operator()(char *ptr) {
free_ptr(ptr);
}
};
// TODO: alloc memory with mmap and unload unused pages
// memory still can be corrupted, but it is better than explicit free function
// TODO: use pointer that can't be even copied
using PtrImpl = std::unique_ptr<char, Deleter>;
class Ptr {
public:
Ptr(char *ptr, size_t size) : ptr_(ptr), size_(size) {
}
MutableSlice as_slice() const {
return MutableSlice(ptr_.get(), size_.get());
}
private:
PtrImpl ptr_;
MovableValue<size_t> size_;
};
static void free_ptr(char *ptr) {
impl().free_ptr(ptr);
}
struct Impl {
static const size_t MEM_SIZE = 1024 * 1024;
std::array<char, MEM_SIZE> mem;
size_t pos{0};
char *alloc(size_t size) {
if (size == 0) {
size = 1;
}
char *res = mem.data() + pos;
size = (size + 7) & -8;
pos += size;
if (pos > MEM_SIZE) {
std::abort(); // memory is over
}
return res;
}
void free_ptr(char *ptr) {
size_t new_pos = ptr - mem.data();
if (new_pos >= pos) {
std::abort(); // shouldn't happen
}
pos = new_pos;
}
};
static Impl &impl();
public:
static Ptr alloc(size_t size) {
return Ptr(impl().alloc(size), size);
}
};
} // namespace td

View file

@ -0,0 +1,66 @@
/*
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-2019 Telegram Systems LLP
*/
#include "td/utils/Status.h"
#if TD_PORT_WINDOWS
#include "td/utils/port/wstring_convert.h"
#endif
#if TD_PORT_POSIX
#include "td/utils/port/thread_local.h"
#include <string.h>
#include <cstring>
#endif
namespace td {
#if TD_PORT_POSIX
CSlice strerror_safe(int code) {
const size_t size = 1000;
static TD_THREAD_LOCAL char *buf;
init_thread_local<char[]>(buf, size);
#if !defined(__GLIBC__) || ((_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && !_GNU_SOURCE)
strerror_r(code, buf, size);
return CSlice(buf, buf + std::strlen(buf));
#else
return CSlice(strerror_r(code, buf, size));
#endif
}
#endif
#if TD_PORT_WINDOWS
string winerror_to_string(int code) {
const size_t size = 1000;
wchar_t wbuf[size];
auto res_size = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, code, 0, wbuf, size - 1, nullptr);
if (res_size == 0) {
return "Unknown windows error";
}
while (res_size != 0 && (wbuf[res_size - 1] == '\n' || wbuf[res_size - 1] == '\r')) {
res_size--;
}
return from_wstring(wbuf, res_size).ok();
}
#endif
} // namespace td

557
tdutils/td/utils/Status.h Normal file
View file

@ -0,0 +1,557 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include "td/utils/logging.h"
#include "td/utils/ScopeGuard.h"
#include "td/utils/Slice.h"
#include "td/utils/StackAllocator.h"
#include "td/utils/StringBuilder.h"
#include <cerrno>
#include <cstring>
#include <memory>
#include <new>
#include <type_traits>
#include <utility>
#define TRY_STATUS(status) \
{ \
auto try_status = (status); \
if (try_status.is_error()) { \
return try_status.move_as_error(); \
} \
}
#define TRY_STATUS_PREFIX(status, prefix) \
{ \
auto try_status = (status); \
if (try_status.is_error()) { \
return try_status.move_as_error_prefix(prefix); \
} \
}
#define TRY_RESULT(name, result) TRY_RESULT_IMPL(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_PREFIX(name, result, prefix) \
TRY_RESULT_PREFIX_IMPL(TD_CONCAT(TD_CONCAT(r_, name), __LINE__), auto name, result, prefix)
#define TRY_RESULT_PREFIX_ASSIGN(name, result, prefix) \
TRY_RESULT_PREFIX_IMPL(TD_CONCAT(TD_CONCAT(r_, name), __LINE__), name, result, prefix)
#define TRY_RESULT_PROMISE_PREFIX(promise_name, name, result, prefix) \
TRY_RESULT_PROMISE_PREFIX_IMPL(promise_name, TD_CONCAT(TD_CONCAT(r_, name), __LINE__), auto name, result, prefix)
#define TRY_RESULT_PROMISE_PREFIX_ASSIGN(promise_name, name, result, prefix) \
TRY_RESULT_PROMISE_PREFIX_IMPL(promise_name, TD_CONCAT(TD_CONCAT(r_, name), __LINE__), name, result, prefix)
#define TRY_RESULT_IMPL(r_name, name, result) \
auto r_name = (result); \
if (r_name.is_error()) { \
return r_name.move_as_error(); \
} \
name = r_name.move_as_ok();
#define TRY_RESULT_PREFIX_IMPL(r_name, name, result, prefix) \
auto r_name = (result); \
if (r_name.is_error()) { \
return r_name.move_as_error_prefix(prefix); \
} \
name = r_name.move_as_ok();
#define TRY_RESULT_PROMISE_PREFIX_IMPL(promise_name, r_name, name, result, prefix) \
auto r_name = (result); \
if (r_name.is_error()) { \
promise.set_error(r_name.move_as_error()); \
return; \
} \
name = r_name.move_as_ok();
#define LOG_STATUS(status) \
{ \
auto log_status = (status); \
if (log_status.is_error()) { \
LOG(ERROR) << log_status.move_as_error(); \
} \
}
#ifndef TD_STATUS_NO_ENSURE
#define ensure() ensure_impl(__FILE__, __LINE__)
#define ensure_error() ensure_error_impl(__FILE__, __LINE__)
#endif
#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)); \
}()
#endif
namespace td {
#if TD_PORT_POSIX
CSlice strerror_safe(int code);
#endif
#if TD_PORT_WINDOWS
string winerror_to_string(int code);
#endif
class Status {
enum class ErrorType : int8 { general, os };
public:
Status() = default;
bool operator==(const Status &other) const {
return ptr_ == other.ptr_;
}
Status clone() const TD_WARN_UNUSED_RESULT {
if (is_ok()) {
return Status();
}
auto info = get_info();
if (info.static_flag) {
return clone_static();
}
return Status(false, info.error_type, info.error_code, message());
}
static Status OK() TD_WARN_UNUSED_RESULT {
return Status();
}
static Status Error(int err, Slice message = Slice()) TD_WARN_UNUSED_RESULT {
return Status(false, ErrorType::general, err, message);
}
static Status Error(Slice message) TD_WARN_UNUSED_RESULT {
return Error(0, message);
}
#if TD_PORT_WINDOWS
static Status WindowsError(int saved_error, Slice message) TD_WARN_UNUSED_RESULT {
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);
}
#endif
static Status Error() TD_WARN_UNUSED_RESULT {
return Error<0>();
}
template <int Code>
static Status Error() {
static Status status(true, ErrorType::general, Code, "");
return status.clone_static();
}
StringBuilder &print(StringBuilder &sb) const {
if (is_ok()) {
return sb << "OK";
}
Info info = get_info();
switch (info.error_type) {
case ErrorType::general:
sb << "[Error";
break;
case ErrorType::os:
#if TD_PORT_POSIX
sb << "[PosixError : " << strerror_safe(info.error_code);
#elif TD_PORT_WINDOWS
sb << "[WindowsError : " << winerror_to_string(info.error_code);
#endif
break;
default:
UNREACHABLE();
break;
}
sb << " : " << code() << " : " << message() << "]";
return sb;
}
string to_string() const {
auto buf = StackAllocator::alloc(4096);
StringBuilder sb(buf.as_slice());
print(sb);
return sb.as_cslice().str();
}
// Default interface
bool is_ok() const TD_WARN_UNUSED_RESULT {
return !is_error();
}
bool is_error() const TD_WARN_UNUSED_RESULT {
return ptr_ != nullptr;
}
#ifdef TD_STATUS_NO_ENSURE
void ensure() const {
if (!is_ok()) {
LOG(FATAL) << "Unexpected Status " << to_string();
}
}
void ensure_error() const {
if (is_ok()) {
LOG(FATAL) << "Unexpected Status::OK";
}
}
#else
void ensure_impl(CSlice file_name, int line) const {
if (!is_ok()) {
LOG(FATAL) << "Unexpected Status " << to_string() << " in file " << file_name << " at line " << line;
}
}
void ensure_error_impl(CSlice file_name, int line) const {
if (is_ok()) {
LOG(FATAL) << "Unexpected Status::OK in file " << file_name << " at line " << line;
}
}
#endif
void ignore() const {
// nop
}
int32 code() const {
if (is_ok()) {
return 0;
}
return get_info().error_code;
}
CSlice message() const {
if (is_ok()) {
return CSlice("OK");
}
return CSlice(ptr_.get() + sizeof(Info));
}
string public_message() const {
if (is_ok()) {
return "OK";
}
Info info = get_info();
switch (info.error_type) {
case ErrorType::general:
return message().str();
case ErrorType::os:
#if TD_PORT_POSIX
return strerror_safe(info.error_code).str();
#elif TD_PORT_WINDOWS
return winerror_to_string(info.error_code);
#endif
default:
UNREACHABLE();
return "";
}
}
const Status &error() const {
return *this;
}
Status move() TD_WARN_UNUSED_RESULT {
return std::move(*this);
}
Status move_as_error() TD_WARN_UNUSED_RESULT {
return std::move(*this);
}
Status move_as_error_prefix(Slice prefix) TD_WARN_UNUSED_RESULT {
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());
default:
UNREACHABLE();
return {};
}
}
private:
struct Info {
bool static_flag : 1;
signed int error_code : 23;
ErrorType error_type;
};
struct Deleter {
void operator()(char *ptr) {
if (!get_info(ptr).static_flag) {
delete[] ptr;
}
}
};
std::unique_ptr<char[], Deleter> ptr_;
Status(Info info, Slice message) {
size_t size = sizeof(Info) + message.size() + 1;
ptr_ = std::unique_ptr<char[], Deleter>(new char[size]);
char *ptr = ptr_.get();
reinterpret_cast<Info *>(ptr)[0] = info;
ptr += sizeof(Info);
std::memcpy(ptr, message.begin(), message.size());
ptr += message.size();
*ptr = 0;
}
Status(bool static_flag, ErrorType error_type, int error_code, Slice message)
: Status(to_info(static_flag, error_type, error_code), message) {
if (static_flag) {
TD_LSAN_IGNORE(ptr_.get());
}
}
Status clone_static() const TD_WARN_UNUSED_RESULT {
CHECK(is_ok() || get_info().static_flag);
Status result;
result.ptr_ = std::unique_ptr<char[], Deleter>(ptr_.get());
return result;
}
static Info to_info(bool static_flag, ErrorType error_type, int error_code) {
const int MIN_ERROR_CODE = -(1 << 22) + 1;
const int MAX_ERROR_CODE = (1 << 22) - 1;
Info tmp;
tmp.static_flag = static_flag;
tmp.error_type = error_type;
if (error_code < MIN_ERROR_CODE) {
LOG(ERROR) << "Error code value is altered from " << error_code;
error_code = MIN_ERROR_CODE;
}
if (error_code > MAX_ERROR_CODE) {
LOG(ERROR) << "Error code value is altered from " << error_code;
error_code = MAX_ERROR_CODE;
}
#if TD_GCC
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wconversion"
#endif
tmp.error_code = error_code;
#if TD_GCC
#pragma GCC diagnostic pop
#endif
CHECK(error_code == tmp.error_code);
return tmp;
}
Info get_info() const {
return get_info(ptr_.get());
}
static Info get_info(char *ptr) {
return reinterpret_cast<Info *>(ptr)[0];
}
};
template <class T = Unit>
class Result {
public:
Result() : status_(Status::Error<-1>()) {
}
template <class S, std::enable_if_t<!std::is_same<std::decay_t<S>, Result>::value, int> = 0>
Result(S &&x) : status_(), value_(std::forward<S>(x)) {
}
struct emplace_t {};
template <class... ArgsT>
Result(emplace_t, ArgsT &&... args) : status_(), value_(std::forward<ArgsT>(args)...) {
}
Result(Status &&status) : status_(std::move(status)) {
CHECK(status_.is_error());
}
Result(const Result &) = delete;
Result &operator=(const Result &) = delete;
Result(Result &&other) : status_(std::move(other.status_)) {
if (status_.is_ok()) {
new (&value_) T(std::move(other.value_));
other.value_.~T();
}
other.status_ = Status::Error<-2>();
}
Result &operator=(Result &&other) {
CHECK(this != &other);
if (status_.is_ok()) {
value_.~T();
}
if (other.status_.is_ok()) {
#if TD_GCC
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif
new (&value_) T(std::move(other.value_));
#if TD_GCC
#pragma GCC diagnostic pop
#endif
other.value_.~T();
}
status_ = std::move(other.status_);
other.status_ = Status::Error<-3>();
return *this;
}
template <class... ArgsT>
void emplace(ArgsT &&... args) {
if (status_.is_ok()) {
value_.~T();
}
new (&value_) T(std::forward<ArgsT>(args)...);
status_ = Status::OK();
}
~Result() {
if (status_.is_ok()) {
value_.~T();
}
}
#ifdef TD_STATUS_NO_ENSURE
void ensure() const {
status_.ensure();
}
void ensure_error() const {
status_.ensure_error();
}
#else
void ensure_impl(CSlice file_name, int line) const {
status_.ensure_impl(file_name, line);
}
void ensure_error_impl(CSlice file_name, int line) const {
status_.ensure_error_impl(file_name, line);
}
#endif
void ignore() const {
status_.ignore();
}
bool is_ok() const {
return status_.is_ok();
}
bool is_error() const {
return status_.is_error();
}
const Status &error() const {
CHECK(status_.is_error());
return status_;
}
Status move_as_error() TD_WARN_UNUSED_RESULT {
CHECK(status_.is_error());
SCOPE_EXIT {
status_ = Status::Error<-4>();
};
return std::move(status_);
}
Status move_as_error_prefix(Slice prefix) TD_WARN_UNUSED_RESULT {
SCOPE_EXIT {
status_ = Status::Error<-5>();
};
return status_.move_as_error_prefix(prefix);
}
const T &ok() const {
LOG_CHECK(status_.is_ok()) << status_;
return value_;
}
T &ok_ref() {
LOG_CHECK(status_.is_ok()) << status_;
return value_;
}
const T &ok_ref() const {
LOG_CHECK(status_.is_ok()) << status_;
return value_;
}
T move_as_ok() {
LOG_CHECK(status_.is_ok()) << status_;
return std::move(value_);
}
Result<T> clone() const TD_WARN_UNUSED_RESULT {
if (is_ok()) {
return Result<T>(ok()); // TODO: return clone(ok());
}
return error().clone();
}
void clear() {
*this = Result<T>();
}
private:
Status status_;
union {
T value_;
};
};
template <>
inline Result<Unit>::Result(Status &&status) : status_(std::move(status)) {
// no assert
}
inline StringBuilder &operator<<(StringBuilder &string_builder, const Status &status) {
return status.print(string_builder);
}
namespace detail {
class SlicifySafe {
public:
Result<CSlice> operator&(Logger &logger) {
if (logger.is_error()) {
return Status::Error("Buffer overflow");
}
return logger.as_cslice();
}
};
class StringifySafe {
public:
Result<string> operator&(Logger &logger) {
if (logger.is_error()) {
return Status::Error("Buffer overflow");
}
return logger.as_cslice().str();
}
};
} // namespace detail
} // namespace td

100
tdutils/td/utils/Storer.h Normal file
View file

@ -0,0 +1,100 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/StorerBase.h"
#include "td/utils/common.h"
#include "td/utils/Slice.h"
#include "td/utils/tl_storers.h"
#include <cstring>
#include <limits>
namespace td {
class SliceStorer : public Storer {
Slice slice;
public:
explicit SliceStorer(Slice slice) : slice(slice) {
}
size_t size() const override {
return slice.size();
}
size_t store(uint8 *ptr) const override {
std::memcpy(ptr, slice.ubegin(), slice.size());
return slice.size();
}
};
inline SliceStorer create_storer(Slice slice) {
return SliceStorer(slice);
}
class ConcatStorer : public Storer {
const Storer &a_;
const Storer &b_;
public:
ConcatStorer(const Storer &a, const Storer &b) : a_(a), b_(b) {
}
size_t size() const override {
return a_.size() + b_.size();
}
size_t store(uint8 *ptr) const override {
uint8 *ptr_save = ptr;
ptr += a_.store(ptr);
ptr += b_.store(ptr);
return ptr - ptr_save;
}
};
inline ConcatStorer create_storer(const Storer &a, const Storer &b) {
return ConcatStorer(a, b);
}
template <class T>
class DefaultStorer : public Storer {
public:
explicit DefaultStorer(const T &object) : object_(object) {
}
size_t size() const override {
if (size_ == std::numeric_limits<size_t>::max()) {
size_ = tl_calc_length(object_);
}
return size_;
}
size_t store(uint8 *ptr) const override {
return tl_store_unsafe(object_, ptr);
}
private:
mutable size_t size_ = std::numeric_limits<size_t>::max();
const T &object_;
};
template <class T>
DefaultStorer<T> create_default_storer(const T &from) {
return DefaultStorer<T>(from);
}
} // namespace td

View file

@ -0,0 +1,37 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/int_types.h"
namespace td {
class Storer {
public:
Storer() = default;
Storer(const Storer &) = delete;
Storer &operator=(const Storer &) = delete;
Storer(Storer &&) = default;
Storer &operator=(Storer &&) = default;
virtual ~Storer() = default;
virtual size_t size() const = 0;
virtual size_t store(uint8 *ptr) const TD_WARN_UNUSED_RESULT = 0;
};
} // namespace td

View file

@ -0,0 +1,226 @@
/*
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-2019 Telegram Systems LLP
*/
#include "td/utils/StringBuilder.h"
#include "td/utils/misc.h"
#include "td/utils/port/thread_local.h"
#include "td/utils/Slice.h"
#include <cstdio>
#include <cstring>
#include <limits>
#include <locale>
#include <memory>
#include <sstream>
#include <utility>
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;
buffer_ = std::make_unique<char[]>(buffer_size);
begin_ptr_ = buffer_.get();
current_ptr_ = begin_ptr_;
end_ptr_ = begin_ptr_ + buffer_size - reserved_size;
} else {
end_ptr_ = slice.end() - reserved_size;
}
}
StringBuilder &StringBuilder::operator<<(Slice slice) {
size_t size = slice.size();
if (unlikely(!reserve(size))) {
if (end_ptr_ < current_ptr_) {
return on_error();
}
auto available_size = static_cast<size_t>(end_ptr_ + reserved_size - 1 - current_ptr_);
if (size > available_size) {
error_flag_ = true;
size = available_size;
}
}
std::memcpy(current_ptr_, slice.begin(), size);
current_ptr_ += size;
return *this;
}
template <class T>
static char *print_uint(char *current_ptr, T x) {
if (x < 100) {
if (x < 10) {
*current_ptr++ = static_cast<char>('0' + x);
} else {
*current_ptr++ = static_cast<char>('0' + x / 10);
*current_ptr++ = static_cast<char>('0' + x % 10);
}
return current_ptr;
}
auto begin_ptr = current_ptr;
do {
*current_ptr++ = static_cast<char>('0' + x % 10);
x /= 10;
} while (x > 0);
auto end_ptr = current_ptr - 1;
while (begin_ptr < end_ptr) {
std::swap(*begin_ptr++, *end_ptr--);
}
return current_ptr;
}
template <class T>
static char *print_int(char *current_ptr, T x) {
if (x < 0) {
if (x == std::numeric_limits<T>::min()) {
std::stringstream ss;
ss << x;
auto len = narrow_cast<int>(static_cast<std::streamoff>(ss.tellp()));
ss.read(current_ptr, len);
return current_ptr + len;
}
*current_ptr++ = '-';
x = -x;
}
return print_uint(current_ptr, x);
}
bool StringBuilder::reserve_inner(size_t size) {
if (!use_buffer_) {
return false;
}
size_t old_data_size = current_ptr_ - begin_ptr_;
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) {
return false;
}
size_t new_buffer_size = (old_buffer_size + 1) * 2;
if (new_buffer_size < need_data_size) {
new_buffer_size = need_data_size;
}
if (new_buffer_size < 100) {
new_buffer_size = 100;
}
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;
CHECK(end_ptr_ > current_ptr_);
CHECK(static_cast<size_t>(end_ptr_ - current_ptr_) >= size);
return true;
}
StringBuilder &StringBuilder::operator<<(int x) {
if (unlikely(!reserve())) {
return on_error();
}
current_ptr_ = print_int(current_ptr_, x);
return *this;
}
StringBuilder &StringBuilder::operator<<(unsigned int x) {
if (unlikely(!reserve())) {
return on_error();
}
current_ptr_ = print_uint(current_ptr_, x);
return *this;
}
StringBuilder &StringBuilder::operator<<(long int x) {
if (unlikely(!reserve())) {
return on_error();
}
current_ptr_ = print_int(current_ptr_, x);
return *this;
}
StringBuilder &StringBuilder::operator<<(long unsigned int x) {
if (unlikely(!reserve())) {
return on_error();
}
current_ptr_ = print_uint(current_ptr_, x);
return *this;
}
StringBuilder &StringBuilder::operator<<(long long int x) {
if (unlikely(!reserve())) {
return on_error();
}
current_ptr_ = print_int(current_ptr_, x);
return *this;
}
StringBuilder &StringBuilder::operator<<(long long unsigned int x) {
if (unlikely(!reserve())) {
return on_error();
}
current_ptr_ = print_uint(current_ptr_, x);
return *this;
}
StringBuilder &StringBuilder::operator<<(FixedDouble x) {
if (unlikely(!reserve(std::numeric_limits<double>::max_exponent10 + x.precision + 4))) {
return on_error();
}
static TD_THREAD_LOCAL std::stringstream *ss;
if (init_thread_local<std::stringstream>(ss)) {
auto previous_locale = ss->imbue(std::locale::classic());
ss->setf(std::ios_base::fixed, std::ios_base::floatfield);
} else {
ss->str(std::string());
ss->clear();
}
ss->precision(x.precision);
*ss << x.d;
int len = narrow_cast<int>(static_cast<std::streamoff>(ss->tellp()));
auto left = end_ptr_ + reserved_size - current_ptr_;
if (unlikely(len >= left)) {
error_flag_ = true;
len = left ? narrow_cast<int>(left - 1) : 0;
}
ss->read(current_ptr_, len);
current_ptr_ += len;
return *this;
}
StringBuilder &StringBuilder::operator<<(const void *ptr) {
if (unlikely(!reserve())) {
return on_error();
}
current_ptr_ += std::snprintf(current_ptr_, reserved_size, "%p", ptr);
return *this;
}
} // namespace td

View file

@ -0,0 +1,150 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include "td/utils/Slice.h"
#include "td/utils/StackAllocator.h"
#include <cstdlib>
#include <memory>
#include <type_traits>
namespace td {
class StringBuilder {
public:
explicit StringBuilder(MutableSlice slice, bool use_buffer = false);
void clear() {
current_ptr_ = begin_ptr_;
error_flag_ = false;
}
MutableCSlice as_cslice() {
if (current_ptr_ >= end_ptr_ + reserved_size) {
std::abort(); // shouldn't happen
}
*current_ptr_ = 0;
return MutableCSlice(begin_ptr_, current_ptr_);
}
bool is_error() const {
return error_flag_;
}
StringBuilder &operator<<(const char *str) {
return *this << Slice(str);
}
StringBuilder &operator<<(const wchar_t *str) = delete;
StringBuilder &operator<<(Slice slice);
StringBuilder &operator<<(bool b) {
return *this << (b ? Slice("true") : Slice("false"));
}
StringBuilder &operator<<(char c) {
if (unlikely(!reserve())) {
return on_error();
}
*current_ptr_++ = c;
return *this;
}
StringBuilder &operator<<(unsigned char c) {
return *this << static_cast<unsigned int>(c);
}
StringBuilder &operator<<(signed char c) {
return *this << static_cast<int>(c);
}
StringBuilder &operator<<(int x);
StringBuilder &operator<<(unsigned int x);
StringBuilder &operator<<(long int x);
StringBuilder &operator<<(long unsigned int x);
StringBuilder &operator<<(long long int x);
StringBuilder &operator<<(long long unsigned int x);
struct FixedDouble {
double d;
int precision;
FixedDouble(double d, int precision) : d(d), precision(precision) {
}
};
StringBuilder &operator<<(FixedDouble x);
StringBuilder &operator<<(double x) {
return *this << FixedDouble(x, 6);
}
StringBuilder &operator<<(const void *ptr);
template <class T>
StringBuilder &operator<<(const T *ptr) {
return *this << static_cast<const void *>(ptr);
}
private:
char *begin_ptr_;
char *current_ptr_;
char *end_ptr_;
bool error_flag_ = false;
bool use_buffer_ = false;
std::unique_ptr<char[]> buffer_;
static constexpr size_t reserved_size = 30;
StringBuilder &on_error() {
error_flag_ = true;
return *this;
}
bool reserve() {
if (end_ptr_ > current_ptr_) {
return true;
}
return reserve_inner(reserved_size);
}
bool reserve(size_t size) {
if (end_ptr_ > current_ptr_ && static_cast<size_t>(end_ptr_ - current_ptr_) >= size) {
return true;
}
return reserve_inner(size);
}
bool reserve_inner(size_t size);
};
template <class T>
std::enable_if_t<std::is_arithmetic<T>::value, string> to_string(const T &x) {
const size_t buf_size = 1000;
auto buf = StackAllocator::alloc(buf_size);
StringBuilder sb(buf.as_slice());
sb << x;
return sb.as_cslice().str();
}
} // namespace td

View file

@ -0,0 +1,66 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/port/thread.h"
#include "td/utils/port/thread_local.h"
#include "td/utils/int_types.h"
#include <atomic>
#include <array>
namespace td {
template <class T>
class ThreadLocalStorage {
public:
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++) {
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++) {
f(nodes_[i].value);
}
}
private:
struct Node {
T value{};
char padding[128];
};
static constexpr int MAX_THREAD_ID = 128;
std::atomic<int> max_thread_id_{MAX_THREAD_ID};
std::array<Node, MAX_THREAD_ID> nodes_;
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];
}
};
} // namespace td

View file

@ -0,0 +1,123 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/ThreadLocalStorage.h"
#include "td/utils/StringBuilder.h"
#include <array>
#include <mutex>
namespace td {
template <size_t N>
class ThreadSafeMultiCounter {
public:
void add(size_t index, int64 diff) {
CHECK(index < N);
tls_.get()[index].fetch_add(diff, std::memory_order_relaxed);
}
int64 sum(size_t index) const {
CHECK(index < N);
int64 res = 0;
tls_.for_each([&](auto &value) { res += value[index].load(); });
return res;
}
private:
ThreadLocalStorage<std::array<std::atomic<int64>, N>> tls_;
};
class ThreadSafeCounter {
public:
void add(int64 diff) {
counter_.add(0, diff);
}
int64 sum() const {
return counter_.sum(0);
}
private:
ThreadSafeMultiCounter<1> counter_;
};
class NamedThreadSafeCounter {
static constexpr int N = 128;
using Counter = ThreadSafeMultiCounter<N>;
public:
class CounterRef {
public:
CounterRef() = default;
CounterRef(size_t index, Counter *counter) : index_(index), counter_(counter) {
}
void add(int64 diff) {
counter_->add(index_, diff);
}
int64 sum() const {
return counter_->sum(index_);
}
private:
size_t index_{0};
Counter *counter_{nullptr};
};
CounterRef get_counter(Slice name) {
std::unique_lock<std::mutex> guard(mutex_);
for (size_t i = 0; i < names_.size(); i++) {
if (names_[i] == name) {
return get_counter_ref(i);
}
}
CHECK(names_.size() < N);
names_.push_back(name.str());
return get_counter_ref(names_.size() - 1);
}
CounterRef get_counter_ref(size_t index) {
return CounterRef(index, &counter_);
}
static NamedThreadSafeCounter &get_default() {
static NamedThreadSafeCounter res;
return res;
}
template <class F>
void for_each(F &&f) const {
std::unique_lock<std::mutex> guard(mutex_);
for (size_t i = 0; i < names_.size(); i++) {
f(names_[i], counter_.sum(i));
}
}
friend StringBuilder &operator<<(StringBuilder &sb, const NamedThreadSafeCounter &counter) {
counter.for_each([&sb](Slice name, int64 cnt) { sb << name << ": " << cnt << "\n"; });
return sb;
}
private:
mutable std::mutex mutex_;
std::vector<std::string> names_;
Counter counter_;
};
} // namespace td

32
tdutils/td/utils/Time.cpp Normal file
View file

@ -0,0 +1,32 @@
/*
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-2019 Telegram Systems LLP
*/
#include "td/utils/Time.h"
#include <cmath>
namespace td {
bool operator==(Timestamp a, Timestamp b) {
return std::abs(a.at() - b.at()) < 1e-6;
}
double Time::now() {
return Clocks::monotonic();
}
} // namespace td

124
tdutils/td/utils/Time.h Normal file
View file

@ -0,0 +1,124 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include "td/utils/port/Clocks.h"
namespace td {
class Time {
public:
static double now();
static double now_cached() {
// Temporary(?) use now in now_cached
// Problem:
// thread A: check that now() > timestamp and notifies thread B
// thread B: must see that now() > timestamp()
//
// now() and now_cached() must be monotonic
//
// if a=now[_cached]() happens before b=now[_cached] than
// a <= b
//
// As an alternative we may say that now_cached is a thread local copy of now
return now();
}
};
inline void relax_timeout_at(double *timeout, double new_timeout) {
if (new_timeout == 0) {
return;
}
if (*timeout == 0 || new_timeout < *timeout) {
*timeout = new_timeout;
}
}
class Timestamp {
public:
Timestamp() = default;
static Timestamp never() {
return Timestamp{};
}
static Timestamp now() {
return Timestamp{Time::now()};
}
static Timestamp now_cached() {
return Timestamp{Time::now_cached()};
}
static Timestamp at(double timeout) {
return Timestamp{timeout};
}
static Timestamp at_unix(double timeout) {
return Timestamp{timeout - td::Clocks::system() + Time::now()};
}
static Timestamp in(double timeout) {
return Timestamp{Time::now_cached() + timeout};
}
bool is_in_past() const {
return at_ <= Time::now_cached();
}
explicit operator bool() const {
return at_ > 0;
}
double at() const {
return at_;
}
double at_unix() const {
return at_ + Clocks::system() - Time::now();
}
double in() const {
return at_ - Time::now_cached();
}
void relax(const Timestamp &timeout) {
if (!timeout) {
return;
}
if (!*this || at_ > timeout.at_) {
at_ = timeout.at_;
}
}
friend bool operator==(Timestamp a, Timestamp b);
private:
double at_{0};
explicit Timestamp(double timeout) : at_(timeout) {
}
};
template <class StorerT>
void store(const Timestamp &timestamp, StorerT &storer) {
storer.store_binary(timestamp.at() - Time::now() + Clocks::system());
}
template <class ParserT>
void parse(Timestamp &timestamp, ParserT &parser) {
timestamp = Timestamp::in(parser.fetch_double() - Clocks::system());
}
} // namespace td

View file

@ -0,0 +1,83 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include <utility>
namespace td {
template <class StatT>
class TimedStat {
public:
TimedStat(double duration, double now)
: duration_(duration), current_(), current_timestamp_(now), next_(), next_timestamp_(now) {
}
TimedStat() : TimedStat(0, 0) {
}
template <class EventT>
void add_event(const EventT &e, double now) {
update(now);
current_.on_event(e);
next_.on_event(e);
}
const StatT &get_stat(double now) {
update(now);
return current_;
}
std::pair<StatT, double> stat_duration(double now) {
update(now);
return std::make_pair(current_, now - current_timestamp_);
}
void clear_events() {
current_.clear();
next_.clear();
}
private:
double duration_;
StatT current_;
double current_timestamp_;
StatT next_;
double next_timestamp_;
void update(double &now) {
if (now < next_timestamp_) {
// LOG_CHECK(now >= next_timestamp_ * (1 - 1e-14)) << now << " " << next_timestamp_;
now = next_timestamp_;
}
if (duration_ == 0) {
return;
}
if (next_timestamp_ + 2 * duration_ < now) {
current_ = StatT();
current_timestamp_ = now;
next_ = StatT();
next_timestamp_ = now;
} else if (next_timestamp_ + duration_ < now) {
current_ = next_;
current_timestamp_ = next_timestamp_;
next_ = StatT();
next_timestamp_ = now;
}
}
};
} // namespace td

View file

@ -0,0 +1,62 @@
/*
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-2019 Telegram Systems LLP
*/
#include "td/utils/Timer.h"
#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 {
Timer::Timer() : start_time_(Time::now()) {
}
double Timer::elapsed() const {
return Time::now() - start_time_;
}
StringBuilder &operator<<(StringBuilder &string_builder, const Timer &timer) {
return string_builder << "in " << Time::now() - timer.start_time_;
}
PerfWarningTimer::PerfWarningTimer(string name, double max_duration)
: name_(std::move(name)), start_at_(Time::now()), max_duration_(max_duration) {
}
PerfWarningTimer::PerfWarningTimer(PerfWarningTimer &&other)
: name_(std::move(other.name_)), start_at_(other.start_at_), max_duration_(other.max_duration_) {
other.start_at_ = 0;
}
PerfWarningTimer::~PerfWarningTimer() {
reset();
}
void PerfWarningTimer::reset() {
if (start_at_ == 0) {
return;
}
double duration = Time::now() - start_at_;
LOG_IF(WARNING, duration > max_duration_)
<< "SLOW: " << tag("name", name_) << tag("duration", format::as_time(duration));
start_at_ = 0;
}
} // namespace td

53
tdutils/td/utils/Timer.h Normal file
View file

@ -0,0 +1,53 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/StringBuilder.h"
namespace td {
class Timer {
public:
Timer();
double elapsed() const;
private:
friend StringBuilder &operator<<(StringBuilder &string_builder, const Timer &timer);
double start_time_;
};
class PerfWarningTimer {
public:
explicit PerfWarningTimer(string name, double max_duration = 0.1);
PerfWarningTimer(const PerfWarningTimer &) = delete;
PerfWarningTimer &operator=(const PerfWarningTimer &) = delete;
PerfWarningTimer(PerfWarningTimer &&other);
PerfWarningTimer &operator=(PerfWarningTimer &&) = delete;
~PerfWarningTimer();
void reset();
private:
string name_;
double start_at_{0};
double max_duration_{0};
};
} // namespace td

View file

@ -0,0 +1,92 @@
/*
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-2019 Telegram Systems LLP
*/
#include "TsFileLog.h"
#include <limits>
namespace td {
namespace detail {
class TsFileLog : public LogInterface {
public:
Status init(string path) {
path_ = std::move(path);
for (int i = 0; i < (int)logs_.size(); i++) {
logs_[i].id = i;
}
return init_info(&logs_[0]);
}
vector<string> get_file_paths() override {
vector<string> res;
for (auto &log : logs_) {
res.push_back(get_path(&log));
}
return res;
}
void append(CSlice cslice) override {
return append(cslice, -1);
}
void append(CSlice cslice, int log_level) override {
get_current_logger()->append(cslice, log_level);
}
private:
struct Info {
FileLog log;
bool is_inited;
int id;
};
static constexpr int MAX_THREAD_ID = 128;
std::string path_;
std::array<Info, MAX_THREAD_ID> logs_;
LogInterface *get_current_logger() {
auto *info = get_current_info();
if (!info->is_inited) {
CHECK(init_info(info).is_ok());
}
return &info->log;
}
Info *get_current_info() {
return &logs_[get_thread_id()];
}
Status init_info(Info *info) {
TRY_STATUS(info->log.init(get_path(info), std::numeric_limits<int64>::max(), info->id == 0));
info->is_inited = true;
return Status::OK();
}
string get_path(Info *info) {
if (info->id == 0) {
return path_;
}
return PSTRING() << path_ << "." << info->id;
}
};
} // namespace detail
Result<td::unique_ptr<LogInterface>> TsFileLog::create(string path) {
auto res = td::make_unique<detail::TsFileLog>();
TRY_STATUS(res->init(path));
return std::move(res);
}
} // namespace td

View file

@ -0,0 +1,28 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/FileLog.h"
namespace td {
class TsFileLog {
public:
static Result<td::unique_ptr<LogInterface>> create(string path);
};
} // namespace td

105
tdutils/td/utils/UInt.h Normal file
View file

@ -0,0 +1,105 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include "td/utils/Slice.h"
#include <cstring>
namespace td {
template <size_t size>
struct UInt {
static_assert(size % 8 == 0, "size should be divisible by 8");
uint8 raw[size / 8];
Slice as_slice() const {
return Slice(raw, size / 8);
}
MutableSlice as_slice() {
return MutableSlice(raw, size / 8);
}
bool is_zero() const {
for (size_t i = 0; i < size / 8; i++) {
if (raw[i] != 0) {
return false;
}
}
return true;
}
void set_zero() {
for (size_t i = 0; i < size / 8; i++) {
raw[i] = 0;
}
}
static UInt zero() {
UInt v;
v.set_zero();
return v;
}
};
template <size_t size>
bool operator==(const UInt<size> &a, const UInt<size> &b) {
return a.as_slice() == b.as_slice();
}
template <size_t size>
bool operator!=(const UInt<size> &a, const UInt<size> &b) {
return !(a == b);
}
template <size_t size>
td::UInt<size> operator^(const UInt<size> &a, const UInt<size> &b) {
td::UInt<size> res;
for (size_t i = 0; i < size / 8; i++) {
res.raw[i] = static_cast<uint8>(a.raw[i] ^ b.raw[i]);
}
return res;
}
template <size_t size>
int get_kth_bit(const UInt<size> &a, uint32 bit) {
uint8 b = a.raw[bit / 8];
bit &= 7;
return (b >> (7 - bit)) & 1;
}
template <size_t size>
Slice as_slice(const UInt<size> &value) {
return value.as_slice();
}
template <size_t size>
MutableSlice as_slice(UInt<size> &value) {
return value.as_slice();
}
template <size_t size>
bool operator<(const UInt<size> &a, const UInt<size> &b) {
return a.as_slice() < b.as_slice();
}
using UInt128 = UInt<128>;
using UInt256 = UInt<256>;
} // namespace td

303
tdutils/td/utils/Variant.h Normal file
View file

@ -0,0 +1,303 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include "td/utils/logging.h"
#include <new>
#include <type_traits>
#include <utility>
namespace td {
namespace detail {
template <size_t... Args>
class MaxSizeImpl {};
template <class T>
constexpr const T &constexpr_max(const T &a, const T &b) {
return a < b ? b : a;
}
template <size_t Res, size_t X, size_t... Args>
class MaxSizeImpl<Res, X, Args...> {
public:
static constexpr size_t value = MaxSizeImpl<constexpr_max(Res, X), Args...>::value;
};
template <size_t Res>
class MaxSizeImpl<Res> {
public:
static constexpr size_t value = Res;
};
template <class... Args>
class MaxSize {
public:
static constexpr size_t value = MaxSizeImpl<0, sizeof(Args)...>::value;
};
template <size_t to_skip, class... Args>
class IthTypeImpl {};
template <class Res, class... Args>
class IthTypeImpl<0, Res, Args...> {
public:
using type = Res;
};
template <size_t pos, class Skip, class... Args>
class IthTypeImpl<pos, Skip, Args...> : public IthTypeImpl<pos - 1, Args...> {};
class Dummy {};
template <size_t pos, class... Args>
class IthType : public IthTypeImpl<pos, Args..., Dummy> {};
template <bool ok, int offset, class... Types>
class FindTypeOffsetImpl {};
template <int offset, class... Types>
class FindTypeOffsetImpl<true, offset, Types...> {
public:
static constexpr int value = offset;
};
template <int offset, class T, class S, class... Types>
class FindTypeOffsetImpl<false, offset, T, S, Types...>
: public FindTypeOffsetImpl<std::is_same<T, S>::value, offset + 1, T, Types...> {};
template <class T, class... Types>
class FindTypeOffset : public FindTypeOffsetImpl<false, -1, T, Types...> {};
template <int offset, class... Types>
class ForEachTypeImpl {};
template <int offset>
class ForEachTypeImpl<offset, Dummy> {
public:
template <class F>
static void visit(F &&f) {
}
};
template <int offset, class T, class... Types>
class ForEachTypeImpl<offset, T, Types...> {
public:
template <class F>
static void visit(F &&f) {
f(offset, static_cast<T *>(nullptr));
ForEachTypeImpl<offset + 1, Types...>::visit(f);
}
};
template <class... Types>
class ForEachType {
public:
template <class F>
static void visit(F &&f) {
ForEachTypeImpl<0, Types..., Dummy>::visit(f);
}
};
} // namespace detail
template <class... Types>
class Variant {
public:
static constexpr int npos = -1;
Variant() {
}
Variant(Variant &&other) noexcept {
other.visit([&](auto &&value) { this->init_empty(std::forward<decltype(value)>(value)); });
}
Variant(const Variant &other) {
other.visit([&](auto &&value) { this->init_empty(std::forward<decltype(value)>(value)); });
}
Variant &operator=(Variant &&other) {
clear();
other.visit([&](auto &&value) { this->init_empty(std::forward<decltype(value)>(value)); });
return *this;
}
Variant &operator=(const Variant &other) {
clear();
other.visit([&](auto &&value) { this->init_empty(std::forward<decltype(value)>(value)); });
return *this;
}
bool operator==(const Variant &other) const {
if (offset_ != other.offset_) {
return false;
}
bool res = false;
for_each([&](int offset, auto *ptr) {
using T = std::decay_t<decltype(*ptr)>;
if (offset == offset_) {
res = this->get<T>() == other.template get<T>();
}
});
return res;
}
bool operator<(const Variant &other) const {
if (offset_ != other.offset_) {
return offset_ < other.offset_;
}
bool res = false;
for_each([&](int offset, auto *ptr) {
using T = std::decay_t<decltype(*ptr)>;
if (offset == offset_) {
res = this->get<T>() < other.template get<T>();
}
});
return res;
}
template <class T, std::enable_if_t<!std::is_same<std::decay_t<T>, Variant>::value, int> = 0>
Variant(T &&t) {
init_empty(std::forward<T>(t));
}
template <class T, std::enable_if_t<!std::is_same<std::decay_t<T>, Variant>::value, int> = 0>
Variant &operator=(T &&t) {
clear();
init_empty(std::forward<T>(t));
return *this;
}
template <class T>
static constexpr int offset() {
return detail::FindTypeOffset<std::decay_t<T>, Types...>::value;
}
template <class T>
void init_empty(T &&t) {
LOG_CHECK(offset_ == npos) << offset_
#if TD_CLANG || TD_GCC
<< ' ' << __PRETTY_FUNCTION__
#endif
;
offset_ = offset<T>();
new (&get<T>()) std::decay_t<T>(std::forward<T>(t));
}
~Variant() {
clear();
}
template <class F>
void visit(F &&f) {
for_each([&](int offset, auto *ptr) {
using T = std::decay_t<decltype(*ptr)>;
if (offset == offset_) {
f(std::move(*this->get_unsafe<T>()));
}
});
}
template <class F>
void for_each(F &&f) {
detail::ForEachType<Types...>::visit(f);
}
template <class F>
void visit(F &&f) const {
for_each([&](int offset, auto *ptr) {
using T = std::decay_t<decltype(*ptr)>;
if (offset == offset_) {
f(std::move(*this->get_unsafe<T>()));
}
});
}
template <class F>
void for_each(F &&f) const {
detail::ForEachType<Types...>::visit(f);
}
void clear() {
visit([](auto &&value) {
using T = std::decay_t<decltype(value)>;
value.~T();
});
offset_ = npos;
}
template <int offset>
auto &get() {
CHECK(offset == offset_);
return *get_unsafe<offset>();
}
template <class T>
auto &get() {
return get<offset<T>()>();
}
template <int offset>
const auto &get() const {
CHECK(offset == offset_);
return *get_unsafe<offset>();
}
template <class T>
const auto &get() const {
return get<offset<T>()>();
}
int32 get_offset() const {
return offset_;
}
private:
union {
int64 align_;
char data_[detail::MaxSize<Types...>::value];
};
int offset_{npos};
template <class T>
auto *get_unsafe() {
return reinterpret_cast<T *>(data_);
}
template <int offset>
auto *get_unsafe() {
using T = typename detail::IthType<offset, Types...>::type;
return get_unsafe<T>();
}
template <class T>
const auto *get_unsafe() const {
return reinterpret_cast<const T *>(data_);
}
template <int offset>
const auto *get_unsafe() const {
using T = typename detail::IthType<offset, Types...>::type;
return get_unsafe<T>();
}
};
template <class T, class... Types>
auto &get(Variant<Types...> &v) {
return v.template get<T>();
}
template <class T, class... Types>
auto &get(const Variant<Types...> &v) {
return v.template get<T>();
}
template <int T, class... Types>
auto &get(Variant<Types...> &v) {
return v.template get<T>();
}
template <int T, class... Types>
auto &get(const Variant<Types...> &v) {
return v.template get<T>();
}
} // namespace td

View file

@ -0,0 +1,84 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/Span.h"
#include <utility>
#include <vector>
namespace td {
template <class T>
class VectorQueue {
public:
template <class S>
void push(S &&s) {
vector_.push_back(std::forward<S>(s));
}
template <class... Args>
void emplace(Args &&... args) {
vector_.emplace_back(std::forward<Args>(args)...);
}
T pop() {
try_shrink();
return std::move(vector_[read_pos_++]);
}
void pop_n(size_t n) {
read_pos_ += n;
try_shrink();
}
T &front() {
return vector_[read_pos_];
}
T &back() {
return vector_.back();
}
bool empty() const {
return size() == 0;
}
size_t size() const {
return vector_.size() - read_pos_;
}
T *data() {
return vector_.data() + read_pos_;
}
const T *data() const {
return vector_.data() + read_pos_;
}
Span<T> as_span() const {
return {data(), size()};
}
MutableSpan<T> as_mutable_span() {
return {data(), size()};
}
private:
std::vector<T> vector_;
size_t read_pos_{0};
void try_shrink() {
if (read_pos_ * 2 > vector_.size() && read_pos_ > 4) {
vector_.erase(vector_.begin(), vector_.begin() + read_pos_);
read_pos_ = 0;
}
}
};
} // namespace td

98
tdutils/td/utils/as.h Normal file
View file

@ -0,0 +1,98 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include <cstring>
#include <type_traits>
namespace td {
namespace detail {
template <class T>
class As {
public:
explicit As(void *ptr) : ptr_(ptr) {
}
As(const As &new_value) = delete;
As &operator=(const As &) = delete;
As(As &&) = default;
As &operator=(As &&new_value) && {
std::memcpy(ptr_, new_value.ptr_, sizeof(T));
return *this;
}
~As() = default;
As &operator=(T new_value) && {
std::memcpy(ptr_, &new_value, sizeof(T));
return *this;
}
operator T() const {
T res;
std::memcpy(&res, ptr_, sizeof(T));
return res;
}
bool operator==(const As &other) const {
return this->operator T() == other.operator T();
}
private:
void *ptr_;
};
template <class T>
class ConstAs {
public:
explicit ConstAs(const void *ptr) : ptr_(ptr) {
}
operator T() const {
T res;
std::memcpy(&res, ptr_, sizeof(T));
return res;
}
private:
const void *ptr_;
};
} // namespace detail
// no std::is_trivially_copyable in libstdc++ before 5.0
#if __GLIBCXX__
#define TD_IS_TRIVIALLY_COPYABLE(T) __has_trivial_copy(T)
#else
#define TD_IS_TRIVIALLY_COPYABLE(T) std::is_trivially_copyable<T>::value
#endif
template <class ToT, class FromT,
std::enable_if_t<TD_IS_TRIVIALLY_COPYABLE(ToT) && TD_IS_TRIVIALLY_COPYABLE(FromT), int> = 0>
detail::As<ToT> as(FromT *from) {
return detail::As<ToT>(from);
}
template <class ToT, class FromT,
std::enable_if_t<TD_IS_TRIVIALLY_COPYABLE(ToT) && TD_IS_TRIVIALLY_COPYABLE(FromT), int> = 0>
const detail::ConstAs<ToT> as(const FromT *from) {
return detail::ConstAs<ToT>(from);
}
} // namespace td

302
tdutils/td/utils/base64.cpp Normal file
View file

@ -0,0 +1,302 @@
/*
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-2019 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
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

40
tdutils/td/utils/base64.h Normal file
View file

@ -0,0 +1,40 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include "td/utils/Slice.h"
#include "td/utils/SharedSlice.h"
#include "td/utils/Status.h"
namespace td {
string base64_encode(Slice input);
Result<string> base64_decode(Slice base64);
Result<SecureString> base64_decode_secure(Slice base64);
string base64url_encode(Slice input);
Result<string> base64url_decode(Slice base64);
bool is_base64(Slice input);
bool is_base64url(Slice input);
string base64_filter(Slice input);
} // namespace td

View file

@ -0,0 +1,144 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/format.h"
#include "td/utils/logging.h"
#include "td/utils/port/Clocks.h"
#include "td/utils/StringBuilder.h"
#include <cmath>
#include <tuple>
#include <utility>
#define BENCH(name, desc) \
class name##Bench : public ::td::Benchmark { \
public: \
std::string get_description() const override { \
return (desc); \
} \
void run(int n) override; \
}; \
void name##Bench::run(int n)
namespace td {
#if TD_MSVC
#pragma optimize("", off)
template <class T>
void do_not_optimize_away(T &&datum) {
datum = datum;
}
#pragma optimize("", on)
#else
template <class T>
void do_not_optimize_away(T &&datum) {
asm volatile("" : "+r"(datum));
}
#endif
class Benchmark {
public:
Benchmark() = default;
Benchmark(const Benchmark &) = delete;
Benchmark &operator=(const Benchmark &) = delete;
Benchmark(Benchmark &&) = delete;
Benchmark &operator=(Benchmark &&) = delete;
virtual ~Benchmark() = default;
virtual std::string get_description() const = 0;
virtual void start_up() {
}
virtual void start_up_n(int n) {
start_up();
}
virtual void tear_down() {
}
virtual void run(int n) = 0;
};
inline std::pair<double, double> bench_n(Benchmark &b, int n) {
double total = -Clocks::monotonic();
b.start_up_n(n);
double t = -Clocks::monotonic();
b.run(n);
t += Clocks::monotonic();
b.tear_down();
total += Clocks::monotonic();
return std::make_pair(t, total);
}
inline std::pair<double, double> bench_n(Benchmark &&b, int n) {
return bench_n(b, n);
}
inline void bench(Benchmark &b, double max_time = 1.0) {
int n = 1;
double pass_time = 0;
double total_pass_time = 0;
while (pass_time < max_time && total_pass_time < max_time * 3 && n < (1 << 30)) {
n *= 2;
std::tie(pass_time, total_pass_time) = bench_n(b, n);
}
pass_time = n / pass_time;
int pass_cnt = 2;
double sum = pass_time;
double square_sum = pass_time * pass_time;
double min_pass_time = pass_time;
double max_pass_time = pass_time;
for (int i = 1; i < pass_cnt; i++) {
pass_time = n / bench_n(b, n).first;
sum += pass_time;
square_sum += pass_time * pass_time;
if (pass_time < min_pass_time) {
min_pass_time = pass_time;
}
if (pass_time > max_pass_time) {
max_pass_time = pass_time;
}
}
double average = sum / pass_cnt;
double d = sqrt(square_sum / pass_cnt - average * average);
auto description = b.get_description();
std::string pad;
if (description.size() < 40) {
pad = std::string(40 - description.size(), ' ');
}
LOG(ERROR) << "Bench [" << pad << description << "]: " << StringBuilder::FixedDouble(average, 3) << '['
<< StringBuilder::FixedDouble(min_pass_time, 3) << '-' << StringBuilder::FixedDouble(max_pass_time, 3)
<< "] ops/sec,\t" << format::as_time(1 / average) << " [d = " << StringBuilder::FixedDouble(d, 6) << ']';
}
inline void bench(Benchmark &&b, double max_time = 1.0) {
bench(b, max_time);
}
} // namespace td

267
tdutils/td/utils/bits.h Normal file
View file

@ -0,0 +1,267 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#if TD_MSVC
#include <intrin.h>
#endif
#ifdef bswap32
#undef bswap32
#endif
#ifdef bswap64
#undef bswap64
#endif
namespace td {
int32 count_leading_zeroes32(uint32 x);
int32 count_leading_zeroes64(uint64 x);
int32 count_trailing_zeroes32(uint32 x);
int32 count_trailing_zeroes64(uint64 x);
uint32 bswap32(uint32 x);
uint64 bswap64(uint64 x);
int32 count_bits32(uint32 x);
int32 count_bits64(uint64 x);
inline uint32 bits_negate32(uint32 x) {
return ~x + 1;
}
inline uint64 bits_negate64(uint64 x) {
return ~x + 1;
}
inline uint32 lower_bit32(uint32 x) {
return x & bits_negate32(x);
}
inline uint64 lower_bit64(uint64 x) {
return x & bits_negate64(x);
}
//TODO: optimize
inline int32 count_leading_zeroes_non_zero32(uint32 x) {
DCHECK(x != 0);
return count_leading_zeroes32(x);
}
inline int32 count_leading_zeroes_non_zero64(uint64 x) {
DCHECK(x != 0);
return count_leading_zeroes64(x);
}
inline int32 count_trailing_zeroes_non_zero32(uint32 x) {
DCHECK(x != 0);
return count_trailing_zeroes32(x);
}
inline int32 count_trailing_zeroes_non_zero64(uint64 x) {
DCHECK(x != 0);
return count_trailing_zeroes64(x);
}
//
// Platform specific implementation
//
#if TD_MSVC
inline int32 count_leading_zeroes32(uint32 x) {
unsigned long res = 0;
if (_BitScanReverse(&res, x)) {
return 31 - res;
}
return 32;
}
inline int32 count_leading_zeroes64(uint64 x) {
#if defined(_M_X64)
unsigned long res = 0;
if (_BitScanReverse64(&res, x)) {
return 63 - res;
}
return 64;
#else
if ((x >> 32) == 0) {
return count_leading_zeroes32(static_cast<uint32>(x)) + 32;
} else {
return count_leading_zeroes32(static_cast<uint32>(x >> 32));
}
#endif
}
inline int32 count_trailing_zeroes32(uint32 x) {
unsigned long res = 0;
if (_BitScanForward(&res, x)) {
return res;
}
return 32;
}
inline int32 count_trailing_zeroes64(uint64 x) {
#if defined(_M_X64)
unsigned long res = 0;
if (_BitScanForward64(&res, x)) {
return res;
}
return 64;
#else
if (static_cast<uint32>(x) == 0) {
return count_trailing_zeroes32(static_cast<uint32>(x >> 32)) + 32;
} else {
return count_trailing_zeroes32(static_cast<uint32>(x));
}
#endif
}
inline uint32 bswap32(uint32 x) {
return _byteswap_ulong(x);
}
inline uint64 bswap64(uint64 x) {
return _byteswap_uint64(x);
}
inline int32 count_bits32(uint32 x) {
return __popcnt(x);
}
inline int32 count_bits64(uint64 x) {
#if defined(_M_X64)
return static_cast<int32>(__popcnt64(x));
#else
return count_bits32(static_cast<uint32>(x >> 32)) + count_bits32(static_cast<uint32>(x));
#endif
}
#elif TD_INTEL
inline int32 count_leading_zeroes32(uint32 x) {
unsigned __int32 res = 0;
if (_BitScanReverse(&res, x)) {
return 31 - res;
}
return 32;
}
inline int32 count_leading_zeroes64(uint64 x) {
#if defined(_M_X64) || defined(__x86_64__)
unsigned __int32 res = 0;
if (_BitScanReverse64(&res, x)) {
return 63 - res;
}
return 64;
#else
if ((x >> 32) == 0) {
return count_leading_zeroes32(static_cast<uint32>(x)) + 32;
} else {
return count_leading_zeroes32(static_cast<uint32>(x >> 32));
}
#endif
}
inline int32 count_trailing_zeroes32(uint32 x) {
unsigned __int32 res = 0;
if (_BitScanForward(&res, x)) {
return res;
}
return 32;
}
inline int32 count_trailing_zeroes64(uint64 x) {
#if defined(_M_X64) || defined(__x86_64__)
unsigned __int32 res = 0;
if (_BitScanForward64(&res, x)) {
return res;
}
return 64;
#else
if (static_cast<uint32>(x) == 0) {
return count_trailing_zeroes32(static_cast<uint32>(x >> 32)) + 32;
} else {
return count_trailing_zeroes32(static_cast<uint32>(x));
}
#endif
}
inline uint32 bswap32(uint32 x) {
return _bswap(static_cast<int>(x));
}
inline uint64 bswap64(uint64 x) {
return _bswap64(static_cast<__int64>(x));
}
inline int32 count_bits32(uint32 x) {
return _popcnt32(static_cast<int>(x));
}
inline int32 count_bits64(uint64 x) {
return _popcnt64(static_cast<__int64>(x));
}
#else
inline int32 count_leading_zeroes32(uint32 x) {
if (x == 0) {
return 32;
}
return __builtin_clz(x);
}
inline int32 count_leading_zeroes64(uint64 x) {
if (x == 0) {
return 64;
}
return __builtin_clzll(x);
}
inline int32 count_trailing_zeroes32(uint32 x) {
if (x == 0) {
return 32;
}
return __builtin_ctz(x);
}
inline int32 count_trailing_zeroes64(uint64 x) {
if (x == 0) {
return 64;
}
return __builtin_ctzll(x);
}
inline uint32 bswap32(uint32 x) {
return __builtin_bswap32(x);
}
inline uint64 bswap64(uint64 x) {
return __builtin_bswap64(x);
}
inline int32 count_bits32(uint32 x) {
return __builtin_popcount(x);
}
inline int32 count_bits64(uint64 x) {
return __builtin_popcountll(x);
}
#endif
} // namespace td

196
tdutils/td/utils/buffer.cpp Normal file
View file

@ -0,0 +1,196 @@
/*
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-2019 Telegram Systems LLP
*/
#include "td/utils/buffer.h"
#include "td/utils/port/thread_local.h"
#include <cstddef>
#include <new>
// fixes https://bugs.llvm.org/show_bug.cgi?id=33723 for clang >= 3.6 + c++11 + libc++
#if TD_CLANG && _LIBCPP_VERSION
#define TD_OFFSETOF __builtin_offsetof
#else
#define TD_OFFSETOF offsetof
#endif
namespace td {
TD_THREAD_LOCAL BufferAllocator::BufferRawTls *BufferAllocator::buffer_raw_tls; // static zero-initialized
std::atomic<size_t> BufferAllocator::buffer_mem;
size_t BufferAllocator::get_buffer_mem() {
return buffer_mem;
}
BufferAllocator::WriterPtr BufferAllocator::create_writer(size_t size) {
if (size < 512) {
size = 512;
}
return create_writer_exact(size);
}
BufferAllocator::WriterPtr BufferAllocator::create_writer_exact(size_t size) {
return WriterPtr(create_buffer_raw(size));
}
BufferAllocator::WriterPtr BufferAllocator::create_writer(size_t size, size_t prepend, size_t append) {
auto ptr = create_writer(size + prepend + append);
ptr->begin_ += prepend;
ptr->end_ += prepend + size;
return ptr;
}
BufferAllocator::ReaderPtr BufferAllocator::create_reader(size_t size) {
if (size < 512) {
return create_reader_fast(size);
}
auto ptr = create_writer_exact(size);
ptr->end_ += (size + 7) & -8;
return create_reader(ptr);
}
BufferAllocator::ReaderPtr BufferAllocator::create_reader_fast(size_t size) {
size = (size + 7) & -8;
init_thread_local<BufferRawTls>(buffer_raw_tls);
auto buffer_raw = buffer_raw_tls->buffer_raw.get();
if (buffer_raw == nullptr || buffer_raw->data_size_ - buffer_raw->end_.load(std::memory_order_relaxed) < size) {
buffer_raw = create_buffer_raw(4096 * 4);
buffer_raw_tls->buffer_raw = std::unique_ptr<BufferRaw, BufferAllocator::BufferRawDeleter>(buffer_raw);
}
buffer_raw->end_.fetch_add(size, std::memory_order_relaxed);
buffer_raw->ref_cnt_.fetch_add(1, std::memory_order_acq_rel);
return ReaderPtr(buffer_raw);
}
BufferAllocator::ReaderPtr BufferAllocator::create_reader(const WriterPtr &raw) {
raw->was_reader_ = true;
raw->ref_cnt_.fetch_add(1, std::memory_order_acq_rel);
return ReaderPtr(raw.get());
}
BufferAllocator::ReaderPtr BufferAllocator::create_reader(const ReaderPtr &raw) {
raw->ref_cnt_.fetch_add(1, std::memory_order_acq_rel);
return ReaderPtr(raw.get());
}
void BufferAllocator::dec_ref_cnt(BufferRaw *ptr) {
int left = ptr->ref_cnt_.fetch_sub(1, std::memory_order_acq_rel);
if (left == 1) {
auto buf_size = max(sizeof(BufferRaw), TD_OFFSETOF(BufferRaw, data_) + ptr->data_size_);
buffer_mem -= buf_size;
ptr->~BufferRaw();
delete[] ptr;
}
}
BufferRaw *BufferAllocator::create_buffer_raw(size_t size) {
size = (size + 7) & -8;
auto buf_size = TD_OFFSETOF(BufferRaw, data_) + size;
if (buf_size < sizeof(BufferRaw)) {
buf_size = sizeof(BufferRaw);
}
buffer_mem += buf_size;
auto *buffer_raw = reinterpret_cast<BufferRaw *>(new char[buf_size]);
return new (buffer_raw) BufferRaw(size);
}
void BufferBuilder::append(BufferSlice slice) {
if (append_inplace(slice.as_slice())) {
return;
}
append_slow(std::move(slice));
}
void BufferBuilder::append(Slice slice) {
if (append_inplace(slice)) {
return;
}
append_slow(BufferSlice(slice));
}
void BufferBuilder::prepend(BufferSlice slice) {
if (prepend_inplace(slice.as_slice())) {
return;
}
prepend_slow(std::move(slice));
}
void BufferBuilder::prepend(Slice slice) {
if (prepend_inplace(slice)) {
return;
}
prepend_slow(BufferSlice(slice));
}
BufferSlice BufferBuilder::extract() {
if (to_append_.empty() && to_prepend_.empty()) {
return buffer_writer_.as_buffer_slice();
}
size_t total_size = size();
BufferWriter writer(0, 0, total_size);
std::move(*this).for_each([&](auto &&slice) {
writer.prepare_append().truncate(slice.size()).copy_from(slice.as_slice());
writer.confirm_append(slice.size());
});
*this = {};
return writer.as_buffer_slice();
}
size_t BufferBuilder::size() const {
size_t total_size = 0;
for_each([&](auto &&slice) { total_size += slice.size(); });
return total_size;
}
bool BufferBuilder::append_inplace(Slice slice) {
if (!to_append_.empty()) {
return false;
}
auto dest = buffer_writer_.prepare_append();
if (dest.size() < slice.size()) {
return false;
}
dest.remove_suffix(dest.size() - slice.size());
dest.copy_from(slice);
buffer_writer_.confirm_append(slice.size());
return true;
}
void BufferBuilder::append_slow(BufferSlice slice) {
to_append_.push_back(std::move(slice));
}
bool BufferBuilder::prepend_inplace(Slice slice) {
if (!to_prepend_.empty()) {
return false;
}
auto dest = buffer_writer_.prepare_prepend();
if (dest.size() < slice.size()) {
return false;
}
dest.remove_prefix(dest.size() - slice.size());
dest.copy_from(slice);
buffer_writer_.confirm_prepend(slice.size());
return true;
}
void BufferBuilder::prepend_slow(BufferSlice slice) {
to_prepend_.push_back(std::move(slice));
}
} // namespace td

815
tdutils/td/utils/buffer.h Normal file
View file

@ -0,0 +1,815 @@
/*
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-2019 Telegram Systems LLP
*/
#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"
#include <atomic>
#include <limits>
#include <memory>
namespace td {
struct BufferRaw {
explicit BufferRaw(size_t size) : data_size_(size) {
}
size_t data_size_;
// Constant after first reader is created.
// May be change by writer before it.
// So writer may do prepends till there is no reader created.
size_t begin_ = 0;
// Write by writer.
// Read by reader.
std::atomic<size_t> end_{0};
mutable std::atomic<int32> ref_cnt_{1};
std::atomic<bool> has_writer_{true};
bool was_reader_{false};
alignas(4) unsigned char data_[1];
};
class BufferAllocator {
public:
class DeleteWriterPtr {
public:
void operator()(BufferRaw *ptr) {
ptr->has_writer_.store(false, std::memory_order_release);
dec_ref_cnt(ptr);
}
};
class DeleteReaderPtr {
public:
void operator()(BufferRaw *ptr) {
dec_ref_cnt(ptr);
}
};
using WriterPtr = std::unique_ptr<BufferRaw, DeleteWriterPtr>;
using ReaderPtr = std::unique_ptr<BufferRaw, DeleteReaderPtr>;
static WriterPtr create_writer(size_t size);
static WriterPtr create_writer(size_t size, size_t prepend, size_t append);
static ReaderPtr create_reader(size_t size);
static ReaderPtr create_reader(const WriterPtr &raw);
static ReaderPtr create_reader(const ReaderPtr &raw);
static size_t get_buffer_mem();
static void clear_thread_local();
private:
static ReaderPtr create_reader_fast(size_t size);
static WriterPtr create_writer_exact(size_t size);
struct BufferRawDeleter {
void operator()(BufferRaw *ptr) {
dec_ref_cnt(ptr);
}
};
struct BufferRawTls {
std::unique_ptr<BufferRaw, BufferRawDeleter> buffer_raw;
};
static TD_THREAD_LOCAL BufferRawTls *buffer_raw_tls;
static void dec_ref_cnt(BufferRaw *ptr);
static BufferRaw *create_buffer_raw(size_t size);
static std::atomic<size_t> buffer_mem;
};
using BufferWriterPtr = BufferAllocator::WriterPtr;
using BufferReaderPtr = BufferAllocator::ReaderPtr;
class BufferSlice {
public:
BufferSlice() = default;
explicit BufferSlice(BufferReaderPtr buffer_ptr) : buffer_(std::move(buffer_ptr)) {
if (is_null()) {
return;
}
begin_ = buffer_->begin_;
sync_with_writer();
}
BufferSlice(BufferReaderPtr buffer_ptr, size_t begin, size_t end)
: buffer_(std::move(buffer_ptr)), begin_(begin), end_(end) {
}
explicit BufferSlice(size_t size) : buffer_(BufferAllocator::create_reader(size)) {
end_ = buffer_->end_.load(std::memory_order_relaxed);
begin_ = end_ - ((size + 7) & -8);
end_ = begin_ + size;
}
explicit BufferSlice(Slice slice) : BufferSlice(slice.size()) {
as_slice().copy_from(slice);
}
BufferSlice(const char *ptr, size_t size) : BufferSlice(Slice(ptr, size)) {
}
BufferSlice clone() const {
if (is_null()) {
return BufferSlice(BufferReaderPtr(), begin_, end_);
}
return BufferSlice(BufferAllocator::create_reader(buffer_), begin_, end_);
}
BufferSlice copy() const {
if (is_null()) {
return BufferSlice(BufferReaderPtr(), begin_, end_);
}
return BufferSlice(as_slice());
}
Slice as_slice() const {
if (is_null()) {
return Slice();
}
return Slice(buffer_->data_ + begin_, size());
}
operator Slice() const {
return as_slice();
}
MutableSlice as_slice() {
if (is_null()) {
return MutableSlice();
}
return MutableSlice(buffer_->data_ + begin_, size());
}
Slice prepare_read() const {
return as_slice();
}
Slice after(size_t offset) const {
auto full = as_slice();
full.remove_prefix(offset);
return full;
}
bool confirm_read(size_t size) {
begin_ += size;
CHECK(begin_ <= end_);
return begin_ == end_;
}
void truncate(size_t limit) {
if (size() > limit) {
end_ = begin_ + limit;
}
}
BufferSlice from_slice(Slice slice) const {
auto res = BufferSlice(BufferAllocator::create_reader(buffer_));
res.begin_ = static_cast<size_t>(slice.ubegin() - buffer_->data_);
res.end_ = static_cast<size_t>(slice.uend() - buffer_->data_);
CHECK(buffer_->begin_ <= res.begin_);
CHECK(res.begin_ <= res.end_);
CHECK(res.end_ <= buffer_->end_.load(std::memory_order_relaxed));
return res;
}
// like in std::string
char *data() {
return as_slice().data();
}
const char *data() const {
return as_slice().data();
}
char operator[](size_t at) const {
return as_slice()[at];
}
bool empty() const {
return size() == 0;
}
bool is_null() const {
return !buffer_;
}
size_t size() const {
if (is_null()) {
return 0;
}
return end_ - begin_;
}
// like in std::string
size_t length() const {
return size();
}
// set end_ into writer's end_
size_t sync_with_writer() {
CHECK(!is_null());
auto old_end = end_;
end_ = buffer_->end_.load(std::memory_order_acquire);
return end_ - old_end;
}
bool is_writer_alive() const {
CHECK(!is_null());
return buffer_->has_writer_.load(std::memory_order_acquire);
}
void clear() {
begin_ = 0;
end_ = 0;
buffer_ = nullptr;
}
private:
BufferReaderPtr buffer_;
size_t begin_ = 0;
size_t end_ = 0;
};
template <class StorerT>
void store(const BufferSlice &buffer_slice, StorerT &storer) {
storer.store_string(buffer_slice);
}
template <class ParserT>
void parse(BufferSlice &buffer_slice, ParserT &parser) {
buffer_slice = parser.template fetch_string<BufferSlice>();
}
class BufferWriter {
public:
BufferWriter() = default;
explicit BufferWriter(size_t size) : BufferWriter(BufferAllocator::create_writer(size)) {
}
BufferWriter(size_t size, size_t prepend, size_t append)
: BufferWriter(BufferAllocator::create_writer(size, prepend, append)) {
}
BufferWriter(Slice slice, size_t prepend, size_t append)
: BufferWriter(BufferAllocator::create_writer(slice.size(), prepend, append)) {
as_slice().copy_from(slice);
}
explicit BufferWriter(BufferWriterPtr buffer_ptr) : buffer_(std::move(buffer_ptr)) {
}
BufferSlice as_buffer_slice() const {
return BufferSlice(BufferAllocator::create_reader(buffer_));
}
bool is_null() const {
return !buffer_;
}
bool empty() const {
return size() == 0;
}
size_t size() const {
if (is_null()) {
return 0;
}
return buffer_->end_.load(std::memory_order_relaxed) - buffer_->begin_;
}
MutableSlice as_slice() {
auto end = buffer_->end_.load(std::memory_order_relaxed);
return MutableSlice(buffer_->data_ + buffer_->begin_, buffer_->data_ + end);
}
Slice as_slice() const {
auto end = buffer_->end_.load(std::memory_order_relaxed);
return Slice(buffer_->data_ + buffer_->begin_, buffer_->data_ + end);
}
MutableSlice prepare_prepend() {
if (is_null()) {
return MutableSlice();
}
CHECK(!buffer_->was_reader_);
return MutableSlice(buffer_->data_, buffer_->begin_);
}
MutableSlice prepare_append() {
if (is_null()) {
return MutableSlice();
}
auto end = buffer_->end_.load(std::memory_order_relaxed);
return MutableSlice(buffer_->data_ + end, buffer_->data_size_ - end);
}
void confirm_append(size_t size) {
if (is_null()) {
CHECK(size == 0);
return;
}
auto new_end = buffer_->end_.load(std::memory_order_relaxed) + size;
CHECK(new_end <= buffer_->data_size_);
buffer_->end_.store(new_end, std::memory_order_release);
}
void confirm_prepend(size_t size) {
if (is_null()) {
CHECK(size == 0);
return;
}
CHECK(buffer_->begin_ >= size);
buffer_->begin_ -= size;
}
private:
BufferWriterPtr buffer_;
};
struct ChainBufferNode {
friend struct DeleteWriterPtr;
struct DeleteWriterPtr {
void operator()(ChainBufferNode *ptr) {
ptr->has_writer_.store(false, std::memory_order_release);
dec_ref_cnt(ptr);
}
};
friend struct DeleteReaderPtr;
struct DeleteReaderPtr {
void operator()(ChainBufferNode *ptr) {
dec_ref_cnt(ptr);
}
};
using WriterPtr = std::unique_ptr<ChainBufferNode, DeleteWriterPtr>;
using ReaderPtr = std::unique_ptr<ChainBufferNode, DeleteReaderPtr>;
static WriterPtr make_writer_ptr(ChainBufferNode *ptr) {
ptr->ref_cnt_.store(1, std::memory_order_relaxed);
ptr->has_writer_.store(true, std::memory_order_relaxed);
return WriterPtr(ptr);
}
static ReaderPtr make_reader_ptr(ChainBufferNode *ptr) {
ptr->ref_cnt_.fetch_add(1, std::memory_order_acq_rel);
return ReaderPtr(ptr);
}
bool has_writer() {
return has_writer_.load(std::memory_order_acquire);
}
bool unique() {
return ref_cnt_.load(std::memory_order_acquire) == 1;
}
ChainBufferNode(BufferSlice slice, bool sync_flag) : slice_(std::move(slice)), sync_flag_(sync_flag) {
}
// reader
// There are two options
// 1. Fixed slice of Buffer
// 2. Slice with non-fixed right end
// In each case slice_ is const. Reader should read it and use sync_with_writer on its own copy.
const BufferSlice slice_;
const bool sync_flag_{false}; // should we call slice_.sync_with_writer or not.
// writer
ReaderPtr next_{nullptr};
private:
std::atomic<int> ref_cnt_{0};
std::atomic<bool> has_writer_{false};
static void clear_nonrecursive(ReaderPtr ptr) {
while (ptr && ptr->unique()) {
ptr = std::move(ptr->next_);
}
}
static void dec_ref_cnt(ChainBufferNode *ptr) {
int left = --ptr->ref_cnt_;
if (left == 0) {
clear_nonrecursive(std::move(ptr->next_));
// TODO(refact): move memory management into allocator (?)
delete ptr;
}
}
};
using ChainBufferNodeWriterPtr = ChainBufferNode::WriterPtr;
using ChainBufferNodeReaderPtr = ChainBufferNode::ReaderPtr;
class ChainBufferNodeAllocator {
public:
static ChainBufferNodeWriterPtr create(BufferSlice slice, bool sync_flag) {
auto *ptr = new ChainBufferNode(std::move(slice), sync_flag);
return ChainBufferNode::make_writer_ptr(ptr);
}
static ChainBufferNodeReaderPtr clone(const ChainBufferNodeReaderPtr &ptr) {
if (!ptr) {
return ChainBufferNodeReaderPtr();
}
return ChainBufferNode::make_reader_ptr(ptr.get());
}
static ChainBufferNodeReaderPtr clone(ChainBufferNodeWriterPtr &ptr) {
if (!ptr) {
return ChainBufferNodeReaderPtr();
}
return ChainBufferNode::make_reader_ptr(ptr.get());
}
};
class ChainBufferIterator {
public:
ChainBufferIterator() = default;
explicit ChainBufferIterator(ChainBufferNodeReaderPtr head) : head_(std::move(head)) {
load_head();
}
ChainBufferIterator clone() const {
return ChainBufferIterator(ChainBufferNodeAllocator::clone(head_), reader_.clone(), need_sync_, offset_);
}
size_t offset() const {
return offset_;
}
void clear() {
*this = ChainBufferIterator();
}
Slice prepare_read() {
if (!head_) {
return Slice();
}
while (true) {
auto res = reader_.prepare_read();
if (!res.empty()) {
return res;
}
auto has_writer = head_->has_writer();
if (need_sync_) {
reader_.sync_with_writer();
res = reader_.prepare_read();
if (!res.empty()) {
return res;
}
}
if (has_writer) {
return Slice();
}
head_ = ChainBufferNodeAllocator::clone(head_->next_);
if (!head_) {
return Slice();
}
load_head();
}
}
// returns only head
BufferSlice read_as_buffer_slice(size_t limit) {
prepare_read();
auto res = reader_.clone();
res.truncate(limit);
confirm_read(res.size());
return res;
}
const BufferSlice &head() const {
return reader_;
}
void confirm_read(size_t size) {
offset_ += size;
reader_.confirm_read(size);
}
void advance_till_end() {
while (true) {
auto ready = prepare_read();
if (ready.empty()) {
break;
}
confirm_read(ready.size());
}
}
size_t advance(size_t offset, MutableSlice dest = MutableSlice()) {
size_t skipped = 0;
while (offset != 0) {
auto ready = prepare_read();
if (ready.empty()) {
break;
}
// read no more than offset
ready.truncate(offset);
offset -= ready.size();
skipped += ready.size();
// copy to dest if possible
auto to_dest_size = min(ready.size(), dest.size());
if (to_dest_size != 0) {
dest.copy_from(ready.substr(0, to_dest_size));
dest.remove_prefix(to_dest_size);
}
confirm_read(ready.size());
}
return skipped;
}
private:
ChainBufferNodeReaderPtr head_;
BufferSlice reader_; // copy of head_->slice_
bool need_sync_ = false; // copy of head_->sync_flag_
size_t offset_ = 0; // position in the union of all nodes
ChainBufferIterator(ChainBufferNodeReaderPtr head, BufferSlice reader, bool need_sync, size_t offset)
: head_(std::move(head)), reader_(std::move(reader)), need_sync_(need_sync), offset_(offset) {
}
void load_head() {
reader_ = head_->slice_.clone();
need_sync_ = head_->sync_flag_;
}
};
class ChainBufferReader {
public:
ChainBufferReader() = default;
explicit ChainBufferReader(ChainBufferNodeReaderPtr head)
: begin_(ChainBufferNodeAllocator::clone(head)), end_(std::move(head)) {
end_.advance_till_end();
}
ChainBufferReader(ChainBufferIterator begin, ChainBufferIterator end, bool sync_flag)
: begin_(std::move(begin)), end_(std::move(end)), sync_flag_(sync_flag) {
}
ChainBufferReader(ChainBufferNodeReaderPtr head, size_t size)
: begin_(ChainBufferNodeAllocator::clone(head)), end_(std::move(head)) {
auto advanced = end_.advance(size);
CHECK(advanced == size);
}
ChainBufferReader(ChainBufferReader &&) = default;
ChainBufferReader &operator=(ChainBufferReader &&) = default;
ChainBufferReader(const ChainBufferReader &) = delete;
ChainBufferReader &operator=(const ChainBufferReader &) = delete;
~ChainBufferReader() = default;
ChainBufferReader clone() {
return ChainBufferReader(begin_.clone(), end_.clone(), sync_flag_);
}
Slice prepare_read() {
auto res = begin_.prepare_read();
res.truncate(size());
return res;
}
void confirm_read(size_t size) {
CHECK(size <= this->size());
begin_.confirm_read(size);
}
size_t advance(size_t offset, MutableSlice dest = MutableSlice()) {
CHECK(offset <= size());
return begin_.advance(offset, dest);
}
size_t size() const {
return end_.offset() - begin_.offset();
}
bool empty() const {
return size() == 0;
}
void sync_with_writer() {
if (sync_flag_) {
end_.advance_till_end();
}
}
void advance_end(size_t size) {
end_.advance(size);
}
const ChainBufferIterator &begin() {
return begin_;
}
const ChainBufferIterator &end() {
return end_;
}
// Return [begin_, tail.begin_)
// *this = tail
ChainBufferReader cut_head(ChainBufferIterator pos) TD_WARN_UNUSED_RESULT {
auto tmp = begin_.clone();
begin_ = pos.clone();
return ChainBufferReader(std::move(tmp), std::move(pos), false);
}
ChainBufferReader cut_head(size_t offset) TD_WARN_UNUSED_RESULT {
LOG_CHECK(offset <= size()) << offset << " " << size();
auto it = begin_.clone();
it.advance(offset);
return cut_head(std::move(it));
}
BufferSlice move_as_buffer_slice() {
BufferSlice res;
if (begin_.head().size() >= size()) {
res = begin_.read_as_buffer_slice(size());
} else {
auto save_size = size();
res = BufferSlice{save_size};
advance(save_size, res.as_slice());
}
*this = ChainBufferReader();
return res;
}
BufferSlice read_as_buffer_slice(size_t limit = std::numeric_limits<size_t>::max()) {
return begin_.read_as_buffer_slice(min(limit, size()));
}
private:
ChainBufferIterator begin_; // use it for prepare_read. Fix result with size()
ChainBufferIterator end_; // keep end as far as we can. use it for size()
bool sync_flag_ = true; // auto sync of end_
// 1. We have fixed size. Than end_ is useless.
// 2. No fixed size. One has to sync end_ with end_.advance_till_end() in order to calculate size.
};
class ChainBufferWriter {
public:
ChainBufferWriter() {
init();
}
void init(size_t size = 0) {
writer_ = BufferWriter(size);
tail_ = ChainBufferNodeAllocator::create(writer_.as_buffer_slice(), true);
head_ = ChainBufferNodeAllocator::clone(tail_);
}
MutableSlice prepare_append(size_t hint = 0) {
CHECK(!empty());
auto res = prepare_append_inplace();
if (res.empty()) {
return prepare_append_alloc(hint);
}
return res;
}
MutableSlice prepare_append_at_least(size_t size) {
CHECK(!empty());
auto res = prepare_append_inplace();
if (res.size() < size) {
return prepare_append_alloc(size);
}
return res;
}
MutableSlice prepare_append_inplace() {
CHECK(!empty());
return writer_.prepare_append();
}
MutableSlice prepare_append_alloc(size_t hint = 0) {
CHECK(!empty());
if (hint < (1 << 10)) {
hint = 1 << 12;
}
BufferWriter new_writer(hint);
auto new_tail = ChainBufferNodeAllocator::create(new_writer.as_buffer_slice(), true);
tail_->next_ = ChainBufferNodeAllocator::clone(new_tail);
writer_ = std::move(new_writer);
tail_ = std::move(new_tail); // release tail_
return writer_.prepare_append();
}
void confirm_append(size_t size) {
CHECK(!empty());
writer_.confirm_append(size);
}
void append(Slice slice, size_t hint = 0) {
while (!slice.empty()) {
auto ready = prepare_append(td::max(slice.size(), hint));
auto shift = min(ready.size(), slice.size());
ready.copy_from(slice.substr(0, shift));
confirm_append(shift);
slice.remove_prefix(shift);
}
}
void append(BufferSlice slice) {
auto ready = prepare_append_inplace();
// TODO(perf): we have to store some stats in ChainBufferWriter
// for better append logic
if (slice.size() < (1 << 8) || ready.size() >= slice.size()) {
return append(slice.as_slice());
}
auto new_tail = ChainBufferNodeAllocator::create(std::move(slice), false);
tail_->next_ = ChainBufferNodeAllocator::clone(new_tail);
writer_ = BufferWriter();
tail_ = std::move(new_tail); // release tail_
}
void append(ChainBufferReader &&reader) {
while (!reader.empty()) {
append(reader.read_as_buffer_slice());
}
}
void append(ChainBufferReader &reader) {
while (!reader.empty()) {
append(reader.read_as_buffer_slice());
}
}
ChainBufferReader extract_reader() {
CHECK(head_);
return ChainBufferReader(std::move(head_));
}
private:
bool empty() const {
return !tail_;
}
ChainBufferNodeReaderPtr head_;
ChainBufferNodeWriterPtr tail_;
BufferWriter writer_;
};
class BufferBuilder {
public:
BufferBuilder() = default;
BufferBuilder(Slice slice, size_t prepend_size, size_t append_size)
: buffer_writer_(slice, prepend_size, append_size) {
}
explicit BufferBuilder(BufferWriter &&buffer_writer) : buffer_writer_(std::move(buffer_writer)) {
}
void append(BufferSlice slice);
void append(Slice slice);
void prepend(BufferSlice slice);
void prepend(Slice slice);
template <class F>
void for_each(F &&f) const & {
for (auto &slice : reversed(to_prepend_)) {
f(slice.as_slice());
}
if (!buffer_writer_.empty()) {
f(buffer_writer_.as_slice());
}
for (auto &slice : to_append_) {
f(slice.as_slice());
}
}
template <class F>
void for_each(F &&f) && {
for (auto &slice : reversed(to_prepend_)) {
f(std::move(slice));
}
if (!buffer_writer_.empty()) {
f(buffer_writer_.as_buffer_slice());
}
for (auto &slice : to_append_) {
f(std::move(slice));
}
}
size_t size() const;
BufferSlice extract();
private:
BufferWriter buffer_writer_;
std::vector<BufferSlice> to_append_;
std::vector<BufferSlice> to_prepend_;
bool append_inplace(Slice slice);
void append_slow(BufferSlice slice);
bool prepend_inplace(Slice slice);
void prepend_slow(BufferSlice slice);
};
inline Slice as_slice(const BufferSlice &value) {
return value.as_slice();
}
inline MutableSlice as_slice(BufferSlice &value) {
return value.as_slice();
}
inline MutableSlice as_mutable_slice(BufferSlice &value) {
return value.as_slice();
}
} // namespace td

View file

@ -0,0 +1,34 @@
/*
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-2019 Telegram Systems LLP
*/
#include "td/utils/check.h"
#include "td/utils/logging.h"
#include "td/utils/Slice.h"
namespace td {
namespace detail {
void process_check_error(const char *message, const char *file, int line) {
::td::Logger(*log_interface, log_options, VERBOSITY_NAME(FATAL), Slice(file), line, Slice())
<< "Check `" << message << "` failed";
::td::process_fatal_error(PSLICE() << "Check `" << message << "` failed in " << file << " at " << line);
}
} // namespace detail
} // namespace td

44
tdutils/td/utils/check.h Normal file
View file

@ -0,0 +1,44 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#define TD_DUMMY_CHECK(condition) ((void)(condition))
#define CHECK(condition) \
if (!(condition)) { \
::td::detail::process_check_error(#condition, __FILE__, __LINE__); \
}
// clang-format off
#ifdef NDEBUG
#define DCHECK TD_DUMMY_CHECK
#else
#define DCHECK CHECK
#endif
// clang-format on
#define UNREACHABLE() ::td::detail::process_check_error("Unreachable", __FILE__, __LINE__)
namespace td {
namespace detail {
[[noreturn]] void process_check_error(const char *message, const char *file, int line);
} // namespace detail
} // namespace td

130
tdutils/td/utils/common.h Normal file
View file

@ -0,0 +1,130 @@
/*
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-2019 Telegram Systems LLP
*/
#pragma once
#include "td/utils/config.h"
#include "td/utils/port/platform.h"
// clang-format off
#if TD_WINDOWS
#ifndef NTDDI_VERSION
#define NTDDI_VERSION 0x06020000
#endif
#ifndef WINVER
#define WINVER 0x0602
#endif
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0602
#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif
#ifndef UNICODE
#define UNICODE
#endif
#ifndef _UNICODE
#define _UNICODE
#endif
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <MSWSock.h>
#include <Windows.h>
#undef ERROR
#endif
// clang-format on
#include "td/utils/check.h"
#include "td/utils/int_types.h"
#include "td/utils/unique_ptr.h"
#include <string>
#include <vector>
#define TD_DEBUG
#define TD_DEFINE_STR_IMPL(x) #x
#define TD_DEFINE_STR(x) TD_DEFINE_STR_IMPL(x)
#define TD_CONCAT_IMPL(x, y) x##y
#define TD_CONCAT(x, y) TD_CONCAT_IMPL(x, y)
// clang-format off
#if TD_WINDOWS
#define TD_DIR_SLASH '\\'
#else
#define TD_DIR_SLASH '/'
#endif
// clang-format on
#if TD_USE_ASAN
#include <sanitizer/lsan_interface.h>
#define TD_LSAN_IGNORE(x) __lsan_ignore_object(x)
#else
#define TD_LSAN_IGNORE(x) (void)(x)
#endif
namespace td {
inline bool likely(bool x) {
#if TD_CLANG || TD_GCC || TD_INTEL
return __builtin_expect(x, 1);
#else
return x;
#endif
}
inline bool unlikely(bool x) {
#if TD_CLANG || TD_GCC || TD_INTEL
return __builtin_expect(x, 0);
#else
return x;
#endif
}
// replace std::max and std::min to not have to include <algorithm> everywhere
// as a side bonus, accept parameters by value, so constexpr variables aren't required to be instantiated
template <class T>
T max(T a, T b) {
return a < b ? b : a;
}
template <class T>
T min(T a, T b) {
return a < b ? a : b;
}
using string = std::string;
template <class ValueT>
using vector = std::vector<ValueT>;
struct Unit {};
struct Auto {
template <class ToT>
operator ToT() const {
return ToT();
}
};
} // namespace td

View file

@ -0,0 +1,9 @@
#pragma once
#cmakedefine01 TD_HAVE_OPENSSL
#cmakedefine01 TD_HAVE_ZLIB
#cmakedefine01 TD_HAVE_CRC32C
#cmakedefine01 TD_HAVE_COROUTINES
#cmakedefine01 TD_HAVE_ABSL
#cmakedefine01 TD_HAVE_GETOPT
#cmakedefine01 TD_FD_DEBUG

851
tdutils/td/utils/crypto.cpp Normal file
View file

@ -0,0 +1,851 @@
/*
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-2019 Telegram Systems LLP
*/
#include "td/utils/crypto.h"
#include "td/utils/as.h"
#include "td/utils/BigNum.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"
#if TD_HAVE_OPENSSL
#include <openssl/aes.h>
#include <openssl/bio.h>
#include <openssl/crypto.h>
#include <openssl/evp.h>
#include <openssl/hmac.h>
#include <openssl/md5.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <openssl/sha.h>
#endif
#if TD_HAVE_ZLIB
#include <zlib.h>
#endif
#if TD_HAVE_CRC32C
#include "crc32c/crc32c.h"
#endif
#include <algorithm>
#include <mutex>
#include <utility>
namespace td {
static uint64 gcd(uint64 a, uint64 b) {
if (a == 0) {
return b;
}
if (b == 0) {
return a;
}
int shift = 0;
while ((a & 1) == 0 && (b & 1) == 0) {
a >>= 1;
b >>= 1;
shift++;
}
while (true) {
while ((a & 1) == 0) {
a >>= 1;
}
while ((b & 1) == 0) {
b >>= 1;
}
if (a > b) {
a -= b;
} else if (b > a) {
b -= a;
} else {
return a << shift;
}
}
}
uint64 pq_factorize(uint64 pq) {
if (pq < 2 || pq > (static_cast<uint64>(1) << 63)) {
return 1;
}
uint64 g = 0;
for (int i = 0, iter = 0; i < 3 || iter < 1000; i++) {
uint64 q = Random::fast(17, 32) % (pq - 1);
uint64 x = Random::fast_uint64() % (pq - 1) + 1;
uint64 y = x;
int lim = 1 << (min(5, i) + 18);
for (int j = 1; j < lim; j++) {
iter++;
uint64 a = x;
uint64 b = x;
uint64 c = q;
// c += a * b
while (b) {
if (b & 1) {
c += a;
if (c >= pq) {
c -= pq;
}
}
a += a;
if (a >= pq) {
a -= pq;
}
b >>= 1;
}
x = c;
uint64 z = x < y ? pq + x - y : x - y;
g = gcd(z, pq);
if (g != 1) {
break;
}
if (!(j & (j - 1))) {
y = x;
}
}
if (g > 1 && g < pq) {
break;
}
}
if (g != 0) {
uint64 other = pq / g;
if (other < g) {
g = other;
}
}
return g;
}
#if TD_HAVE_OPENSSL
void init_crypto() {
static bool is_inited = [] {
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
return OPENSSL_init_crypto(0, nullptr) != 0;
#else
OpenSSL_add_all_algorithms();
return true;
#endif
}();
CHECK(is_inited);
}
template <class FromT>
static string as_big_endian_string(const FromT &from) {
char res[sizeof(FromT)];
as<FromT>(res) = from;
size_t i = sizeof(FromT);
while (i && res[i - 1] == 0) {
i--;
}
std::reverse(res, res + i);
return string(res, res + i);
}
static int pq_factorize_big(Slice pq_str, string *p_str, string *q_str) {
// TODO: qsieve?
// do not work for pq == 1
BigNumContext context;
BigNum a;
BigNum b;
BigNum p;
BigNum q;
BigNum one;
one.set_value(1);
BigNum pq = BigNum::from_binary(pq_str);
bool found = false;
for (int i = 0, iter = 0; !found && (i < 3 || iter < 1000); i++) {
int32 t = Random::fast(17, 32);
a.set_value(Random::fast_uint32());
b = a;
int32 lim = 1 << (i + 23);
for (int j = 1; j < lim; j++) {
iter++;
BigNum::mod_mul(a, a, a, pq, context);
a += t;
if (BigNum::compare(a, pq) >= 0) {
BigNum tmp;
BigNum::sub(tmp, a, pq);
a = std::move(tmp);
}
if (BigNum::compare(a, b) > 0) {
BigNum::sub(q, a, b);
} else {
BigNum::sub(q, b, a);
}
BigNum::gcd(p, q, pq, context);
if (BigNum::compare(p, one) != 0) {
found = true;
break;
}
if ((j & (j - 1)) == 0) {
b = a;
}
}
}
if (found) {
BigNum::div(&q, nullptr, pq, p, context);
if (BigNum::compare(p, q) > 0) {
std::swap(p, q);
}
*p_str = p.to_binary();
*q_str = q.to_binary();
return 0;
}
return -1;
}
int pq_factorize(Slice pq_str, string *p_str, string *q_str) {
size_t size = pq_str.size();
if (static_cast<int>(size) > 8 || (static_cast<int>(size) == 8 && (pq_str.begin()[0] & 128) != 0)) {
return pq_factorize_big(pq_str, p_str, q_str);
}
auto ptr = pq_str.ubegin();
uint64 pq = 0;
for (int i = 0; i < static_cast<int>(size); i++) {
pq = (pq << 8) | ptr[i];
}
uint64 p = pq_factorize(pq);
if (p == 0 || pq % p != 0) {
return -1;
}
*p_str = as_big_endian_string(p);
*q_str = as_big_endian_string(pq / p);
// std::string p2, q2;
// pq_factorize_big(pq_str, &p2, &q2);
// CHECK(*p_str == p2);
// CHECK(*q_str == q2);
return 0;
}
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);
}
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);
}
void aes_ige_encrypt(Slice aes_key, MutableSlice aes_iv, Slice from, MutableSlice to) {
aes_ige_xcrypt(aes_key, aes_iv, from, to, true);
}
void aes_ige_decrypt(Slice aes_key, MutableSlice aes_iv, Slice from, MutableSlice to) {
aes_ige_xcrypt(aes_key, aes_iv, from, to, false);
}
static void aes_cbc_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);
}
LOG_IF(FATAL, err != 0);
CHECK(from.size() <= to.size());
AES_cbc_encrypt(from.ubegin(), to.ubegin(), from.size(), &key, aes_iv.ubegin(), encrypt_flag);
}
void aes_cbc_encrypt(Slice aes_key, MutableSlice aes_iv, Slice from, MutableSlice to) {
aes_cbc_xcrypt(aes_key, aes_iv, from, to, true);
}
void aes_cbc_decrypt(Slice aes_key, MutableSlice aes_iv, Slice from, MutableSlice to) {
aes_cbc_xcrypt(aes_key, aes_iv, from, to, false);
}
AesCbcState::AesCbcState(Slice key256, Slice iv128) : key_(key256), iv_(iv128) {
CHECK(key_.size() == 32);
CHECK(iv_.size() == 16);
}
void AesCbcState::encrypt(Slice from, MutableSlice to) {
::td::aes_cbc_encrypt(key_.as_slice(), iv_.as_mutable_slice(), from, to);
}
void AesCbcState::decrypt(Slice from, MutableSlice to) {
::td::aes_cbc_decrypt(key_.as_slice(), iv_.as_mutable_slice(), from, to);
}
class AesCtrState::Impl {
public:
Impl(Slice key, Slice iv) {
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;
}
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;
}
}
}
to[i] = static_cast<char>(from[i] ^ encrypted_counter[current_pos]);
current_pos = (current_pos + 1) & 15;
}
}
private:
AES_KEY aes_key;
SecureString counter{AES_BLOCK_SIZE};
SecureString encrypted_counter{AES_BLOCK_SIZE};
uint8 current_pos;
};
AesCtrState::AesCtrState() = default;
AesCtrState::AesCtrState(AesCtrState &&from) = default;
AesCtrState &AesCtrState::operator=(AesCtrState &&from) = default;
AesCtrState::~AesCtrState() = default;
void AesCtrState::init(Slice key, Slice iv) {
ctx_ = make_unique<AesCtrState::Impl>(key, iv);
}
void AesCtrState::encrypt(Slice from, MutableSlice to) {
ctx_->encrypt(from, to);
}
void AesCtrState::decrypt(Slice from, MutableSlice to) {
encrypt(from, to); // it is the same as decrypt
}
void sha1(Slice data, unsigned char output[20]) {
auto result = SHA1(data.ubegin(), data.size(), output);
CHECK(result == output);
}
void sha256(Slice data, MutableSlice output) {
CHECK(output.size() >= 32);
auto result = SHA256(data.ubegin(), data.size(), output.ubegin());
CHECK(result == output.ubegin());
}
void sha512(Slice data, MutableSlice output) {
CHECK(output.size() >= 64);
auto result = SHA512(data.ubegin(), data.size(), output.ubegin());
CHECK(result == output.ubegin());
}
string sha256(Slice data) {
string result(32, '\0');
sha256(data, result);
return result;
}
string sha512(Slice data) {
string result(64, '\0');
sha512(data, result);
return result;
}
class Sha256State::Impl {
public:
SHA256_CTX ctx_;
};
Sha256State::Sha256State() = default;
Sha256State::Sha256State(Sha256State &&other) {
impl_ = std::move(other.impl_);
is_inited_ = other.is_inited_;
other.is_inited_ = false;
}
Sha256State &Sha256State::operator=(Sha256State &&other) {
Sha256State copy(std::move(other));
using std::swap;
swap(impl_, copy.impl_);
swap(is_inited_, copy.is_inited_);
return *this;
}
Sha256State::~Sha256State() {
if (is_inited_) {
char result[32];
extract(MutableSlice{result, 32});
CHECK(!is_inited_);
}
}
void Sha256State::init() {
if (!impl_) {
impl_ = make_unique<Sha256State::Impl>();
}
CHECK(!is_inited_);
int err = SHA256_Init(&impl_->ctx_);
LOG_IF(FATAL, err != 1);
is_inited_ = true;
}
void Sha256State::feed(Slice data) {
CHECK(impl_);
CHECK(is_inited_);
int err = SHA256_Update(&impl_->ctx_, data.ubegin(), data.size());
LOG_IF(FATAL, err != 1);
}
void Sha256State::extract(MutableSlice output, bool destroy) {
CHECK(output.size() >= 32);
CHECK(impl_);
CHECK(is_inited_);
int err = SHA256_Final(output.ubegin(), &impl_->ctx_);
LOG_IF(FATAL, err != 1);
is_inited_ = false;
if (destroy) {
impl_.reset();
}
}
void md5(Slice input, MutableSlice output) {
CHECK(output.size() >= MD5_DIGEST_LENGTH);
auto result = MD5(input.ubegin(), input.size(), output.ubegin());
CHECK(result == output.ubegin());
}
static void pbkdf2_impl(Slice password, Slice salt, int iteration_count, MutableSlice dest, const EVP_MD *evp_md) {
CHECK(evp_md != nullptr);
int hash_size = EVP_MD_size(evp_md);
CHECK(dest.size() == static_cast<size_t>(hash_size));
CHECK(iteration_count > 0);
#if OPENSSL_VERSION_NUMBER < 0x10000000L
HMAC_CTX ctx;
HMAC_CTX_init(&ctx);
unsigned char counter[4] = {0, 0, 0, 1};
int password_len = narrow_cast<int>(password.size());
HMAC_Init_ex(&ctx, password.data(), password_len, evp_md, nullptr);
HMAC_Update(&ctx, salt.ubegin(), narrow_cast<int>(salt.size()));
HMAC_Update(&ctx, counter, 4);
HMAC_Final(&ctx, dest.ubegin(), nullptr);
HMAC_CTX_cleanup(&ctx);
if (iteration_count > 1) {
CHECK(hash_size <= 64);
unsigned char buf[64];
std::copy(dest.ubegin(), dest.uend(), buf);
for (int iter = 1; iter < iteration_count; iter++) {
if (HMAC(evp_md, password.data(), password_len, buf, hash_size, buf, nullptr) == nullptr) {
LOG(FATAL) << "Failed to HMAC";
}
for (int i = 0; i < hash_size; i++) {
dest[i] ^= buf[i];
}
}
}
#else
int err = PKCS5_PBKDF2_HMAC(password.data(), narrow_cast<int>(password.size()), salt.ubegin(),
narrow_cast<int>(salt.size()), iteration_count, evp_md, narrow_cast<int>(dest.size()),
dest.ubegin());
LOG_IF(FATAL, err != 1);
#endif
}
void pbkdf2_sha256(Slice password, Slice salt, int iteration_count, MutableSlice dest) {
pbkdf2_impl(password, salt, iteration_count, dest, EVP_sha256());
}
void pbkdf2_sha512(Slice password, Slice salt, int iteration_count, MutableSlice dest) {
pbkdf2_impl(password, salt, iteration_count, dest, EVP_sha512());
}
void hmac_sha256(Slice key, Slice message, MutableSlice dest) {
CHECK(dest.size() == 256 / 8);
unsigned int len = 0;
auto result = HMAC(EVP_sha256(), key.ubegin(), narrow_cast<int>(key.size()), message.ubegin(),
narrow_cast<int>(message.size()), dest.ubegin(), &len);
CHECK(result == dest.ubegin());
CHECK(len == dest.size());
}
void hmac_sha512(Slice key, Slice message, MutableSlice dest) {
CHECK(dest.size() == 512 / 8);
unsigned int len = 0;
auto result = HMAC(EVP_sha512(), key.ubegin(), narrow_cast<int>(key.size()), message.ubegin(),
narrow_cast<int>(message.size()), dest.ubegin(), &len);
CHECK(result == dest.ubegin());
CHECK(len == dest.size());
}
static int get_evp_pkey_type(EVP_PKEY *pkey) {
#if OPENSSL_VERSION_NUMBER < 0x10100000L
return EVP_PKEY_type(pkey->type);
#else
return EVP_PKEY_base_id(pkey);
#endif
}
Result<BufferSlice> rsa_encrypt_pkcs1_oaep(Slice public_key, Slice data) {
BIO *mem_bio = BIO_new_mem_buf(const_cast<void *>(static_cast<const void *>(public_key.data())),
narrow_cast<int>(public_key.size()));
SCOPE_EXIT {
BIO_vfree(mem_bio);
};
EVP_PKEY *pkey = PEM_read_bio_PUBKEY(mem_bio, nullptr, nullptr, nullptr);
if (!pkey) {
return Status::Error("Cannot read public key");
}
SCOPE_EXIT {
EVP_PKEY_free(pkey);
};
if (get_evp_pkey_type(pkey) != EVP_PKEY_RSA) {
return Status::Error("Wrong key type, expected RSA");
}
#if OPENSSL_VERSION_NUMBER < 0x10000000L
RSA *rsa = pkey->pkey.rsa;
int outlen = RSA_size(rsa);
BufferSlice res(outlen);
if (RSA_public_encrypt(narrow_cast<int>(data.size()), const_cast<unsigned char *>(data.ubegin()),
res.as_slice().ubegin(), rsa, RSA_PKCS1_OAEP_PADDING) != outlen) {
#else
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(pkey, nullptr);
if (!ctx) {
return Status::Error("Cannot create EVP_PKEY_CTX");
}
SCOPE_EXIT {
EVP_PKEY_CTX_free(ctx);
};
if (EVP_PKEY_encrypt_init(ctx) <= 0) {
return Status::Error("Cannot init EVP_PKEY_CTX");
}
if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0) {
return Status::Error("Cannot set RSA_PKCS1_OAEP padding in EVP_PKEY_CTX");
}
size_t outlen;
if (EVP_PKEY_encrypt(ctx, nullptr, &outlen, data.ubegin(), data.size()) <= 0) {
return Status::Error("Cannot calculate encrypted length");
}
BufferSlice res(outlen);
if (EVP_PKEY_encrypt(ctx, res.as_slice().ubegin(), &outlen, data.ubegin(), data.size()) <= 0) {
#endif
return Status::Error("Cannot encrypt");
}
return std::move(res);
}
Result<BufferSlice> rsa_decrypt_pkcs1_oaep(Slice private_key, Slice data) {
BIO *mem_bio = BIO_new_mem_buf(const_cast<void *>(static_cast<const void *>(private_key.data())),
narrow_cast<int>(private_key.size()));
SCOPE_EXIT {
BIO_vfree(mem_bio);
};
EVP_PKEY *pkey = PEM_read_bio_PrivateKey(mem_bio, nullptr, nullptr, nullptr);
if (!pkey) {
return Status::Error("Cannot read private key");
}
SCOPE_EXIT {
EVP_PKEY_free(pkey);
};
if (get_evp_pkey_type(pkey) != EVP_PKEY_RSA) {
return Status::Error("Wrong key type, expected RSA");
}
#if OPENSSL_VERSION_NUMBER < 0x10000000L
RSA *rsa = pkey->pkey.rsa;
size_t outlen = RSA_size(rsa);
BufferSlice res(outlen);
auto inlen = RSA_private_decrypt(narrow_cast<int>(data.size()), const_cast<unsigned char *>(data.ubegin()),
res.as_slice().ubegin(), rsa, RSA_PKCS1_OAEP_PADDING);
if (inlen == -1) {
return Status::Error("Cannot decrypt");
}
res.truncate(inlen);
#else
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(pkey, nullptr);
if (!ctx) {
return Status::Error("Cannot create EVP_PKEY_CTX");
}
SCOPE_EXIT {
EVP_PKEY_CTX_free(ctx);
};
if (EVP_PKEY_decrypt_init(ctx) <= 0) {
return Status::Error("Cannot init EVP_PKEY_CTX");
}
if (EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) <= 0) {
return Status::Error("Cannot set RSA_PKCS1_OAEP padding in EVP_PKEY_CTX");
}
size_t outlen;
if (EVP_PKEY_decrypt(ctx, nullptr, &outlen, data.ubegin(), data.size()) <= 0) {
return Status::Error("Cannot calculate decrypted length");
}
BufferSlice res(outlen);
if (EVP_PKEY_decrypt(ctx, res.as_slice().ubegin(), &outlen, data.ubegin(), data.size()) <= 0) {
return Status::Error("Cannot decrypt");
}
#endif
return std::move(res);
}
#if OPENSSL_VERSION_NUMBER < 0x10100000L
namespace {
std::vector<RwMutex> &openssl_mutexes() {
static std::vector<RwMutex> mutexes(CRYPTO_num_locks());
return mutexes;
}
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
void openssl_threadid_callback(CRYPTO_THREADID *thread_id) {
static TD_THREAD_LOCAL int id;
CRYPTO_THREADID_set_pointer(thread_id, &id);
}
#endif
void openssl_locking_function(int mode, int n, const char *file, int line) {
auto &mutexes = openssl_mutexes();
if (mode & CRYPTO_LOCK) {
if (mode & CRYPTO_READ) {
mutexes[n].lock_read_unsafe();
} else {
mutexes[n].lock_write_unsafe();
}
} else {
if (mode & CRYPTO_READ) {
mutexes[n].unlock_read_unsafe();
} else {
mutexes[n].unlock_write_unsafe();
}
}
}
} // namespace
#endif
void init_openssl_threads() {
#if OPENSSL_VERSION_NUMBER < 0x10100000L
static std::mutex init_mutex;
std::lock_guard<std::mutex> lock(init_mutex);
if (CRYPTO_get_locking_callback() == nullptr) {
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
CRYPTO_THREADID_set_callback(openssl_threadid_callback);
#endif
CRYPTO_set_locking_callback(openssl_locking_function);
}
#endif
}
#endif
#if TD_HAVE_ZLIB
uint32 crc32(Slice data) {
return static_cast<uint32>(::crc32(0, data.ubegin(), static_cast<uint32>(data.size())));
}
#endif
#if TD_HAVE_CRC32C
uint32 crc32c(Slice data) {
return crc32c::Crc32c(data.data(), data.size());
}
uint32 crc32c_extend(uint32 old_crc, Slice data) {
return crc32c::Extend(old_crc, data.ubegin(), data.size());
}
namespace {
uint32 gf32_matrix_times(const uint32 *matrix, uint32 vector) {
uint32 sum = 0;
while (vector) {
if (vector & 1) {
sum ^= *matrix;
}
vector >>= 1;
matrix++;
}
return sum;
}
void gf32_matrix_square(uint32 *square, const uint32 *matrix) {
for (int n = 0; n < 32; n++) {
square[n] = gf32_matrix_times(matrix, matrix[n]);
}
}
} // namespace
uint32 crc32c_extend(uint32 old_crc, uint32 data_crc, size_t data_size) {
static uint32 power_buf_raw[1024];
static const uint32 *power_buf = [&] {
auto *buf = power_buf_raw;
buf[0] = 0x82F63B78u;
for (int n = 0; n < 31; n++) {
buf[n + 1] = 1u << n;
}
for (int n = 1; n < 32; n++) {
gf32_matrix_square(buf + (n << 5), buf + ((n - 1) << 5));
}
return buf;
}();
if (data_size == 0) {
return old_crc;
}
const uint32 *p = power_buf + 64;
do {
p += 32;
if (data_size & 1) {
old_crc = gf32_matrix_times(p, old_crc);
}
data_size >>= 1;
} while (data_size != 0);
return old_crc ^ data_crc;
}
#endif
static const uint64 crc64_table[256] = {
0x0000000000000000, 0xb32e4cbe03a75f6f, 0xf4843657a840a05b, 0x47aa7ae9abe7ff34, 0x7bd0c384ff8f5e33,
0xc8fe8f3afc28015c, 0x8f54f5d357cffe68, 0x3c7ab96d5468a107, 0xf7a18709ff1ebc66, 0x448fcbb7fcb9e309,
0x0325b15e575e1c3d, 0xb00bfde054f94352, 0x8c71448d0091e255, 0x3f5f08330336bd3a, 0x78f572daa8d1420e,
0xcbdb3e64ab761d61, 0x7d9ba13851336649, 0xceb5ed8652943926, 0x891f976ff973c612, 0x3a31dbd1fad4997d,
0x064b62bcaebc387a, 0xb5652e02ad1b6715, 0xf2cf54eb06fc9821, 0x41e11855055bc74e, 0x8a3a2631ae2dda2f,
0x39146a8fad8a8540, 0x7ebe1066066d7a74, 0xcd905cd805ca251b, 0xf1eae5b551a2841c, 0x42c4a90b5205db73,
0x056ed3e2f9e22447, 0xb6409f5cfa457b28, 0xfb374270a266cc92, 0x48190ecea1c193fd, 0x0fb374270a266cc9,
0xbc9d3899098133a6, 0x80e781f45de992a1, 0x33c9cd4a5e4ecdce, 0x7463b7a3f5a932fa, 0xc74dfb1df60e6d95,
0x0c96c5795d7870f4, 0xbfb889c75edf2f9b, 0xf812f32ef538d0af, 0x4b3cbf90f69f8fc0, 0x774606fda2f72ec7,
0xc4684a43a15071a8, 0x83c230aa0ab78e9c, 0x30ec7c140910d1f3, 0x86ace348f355aadb, 0x3582aff6f0f2f5b4,
0x7228d51f5b150a80, 0xc10699a158b255ef, 0xfd7c20cc0cdaf4e8, 0x4e526c720f7dab87, 0x09f8169ba49a54b3,
0xbad65a25a73d0bdc, 0x710d64410c4b16bd, 0xc22328ff0fec49d2, 0x85895216a40bb6e6, 0x36a71ea8a7ace989,
0x0adda7c5f3c4488e, 0xb9f3eb7bf06317e1, 0xfe5991925b84e8d5, 0x4d77dd2c5823b7ba, 0x64b62bcaebc387a1,
0xd7986774e864d8ce, 0x90321d9d438327fa, 0x231c512340247895, 0x1f66e84e144cd992, 0xac48a4f017eb86fd,
0xebe2de19bc0c79c9, 0x58cc92a7bfab26a6, 0x9317acc314dd3bc7, 0x2039e07d177a64a8, 0x67939a94bc9d9b9c,
0xd4bdd62abf3ac4f3, 0xe8c76f47eb5265f4, 0x5be923f9e8f53a9b, 0x1c4359104312c5af, 0xaf6d15ae40b59ac0,
0x192d8af2baf0e1e8, 0xaa03c64cb957be87, 0xeda9bca512b041b3, 0x5e87f01b11171edc, 0x62fd4976457fbfdb,
0xd1d305c846d8e0b4, 0x96797f21ed3f1f80, 0x2557339fee9840ef, 0xee8c0dfb45ee5d8e, 0x5da24145464902e1,
0x1a083bacedaefdd5, 0xa9267712ee09a2ba, 0x955cce7fba6103bd, 0x267282c1b9c65cd2, 0x61d8f8281221a3e6,
0xd2f6b4961186fc89, 0x9f8169ba49a54b33, 0x2caf25044a02145c, 0x6b055fede1e5eb68, 0xd82b1353e242b407,
0xe451aa3eb62a1500, 0x577fe680b58d4a6f, 0x10d59c691e6ab55b, 0xa3fbd0d71dcdea34, 0x6820eeb3b6bbf755,
0xdb0ea20db51ca83a, 0x9ca4d8e41efb570e, 0x2f8a945a1d5c0861, 0x13f02d374934a966, 0xa0de61894a93f609,
0xe7741b60e174093d, 0x545a57dee2d35652, 0xe21ac88218962d7a, 0x5134843c1b317215, 0x169efed5b0d68d21,
0xa5b0b26bb371d24e, 0x99ca0b06e7197349, 0x2ae447b8e4be2c26, 0x6d4e3d514f59d312, 0xde6071ef4cfe8c7d,
0x15bb4f8be788911c, 0xa6950335e42fce73, 0xe13f79dc4fc83147, 0x521135624c6f6e28, 0x6e6b8c0f1807cf2f,
0xdd45c0b11ba09040, 0x9aefba58b0476f74, 0x29c1f6e6b3e0301b, 0xc96c5795d7870f42, 0x7a421b2bd420502d,
0x3de861c27fc7af19, 0x8ec62d7c7c60f076, 0xb2bc941128085171, 0x0192d8af2baf0e1e, 0x4638a2468048f12a,
0xf516eef883efae45, 0x3ecdd09c2899b324, 0x8de39c222b3eec4b, 0xca49e6cb80d9137f, 0x7967aa75837e4c10,
0x451d1318d716ed17, 0xf6335fa6d4b1b278, 0xb199254f7f564d4c, 0x02b769f17cf11223, 0xb4f7f6ad86b4690b,
0x07d9ba1385133664, 0x4073c0fa2ef4c950, 0xf35d8c442d53963f, 0xcf273529793b3738, 0x7c0979977a9c6857,
0x3ba3037ed17b9763, 0x888d4fc0d2dcc80c, 0x435671a479aad56d, 0xf0783d1a7a0d8a02, 0xb7d247f3d1ea7536,
0x04fc0b4dd24d2a59, 0x3886b22086258b5e, 0x8ba8fe9e8582d431, 0xcc0284772e652b05, 0x7f2cc8c92dc2746a,
0x325b15e575e1c3d0, 0x8175595b76469cbf, 0xc6df23b2dda1638b, 0x75f16f0cde063ce4, 0x498bd6618a6e9de3,
0xfaa59adf89c9c28c, 0xbd0fe036222e3db8, 0x0e21ac88218962d7, 0xc5fa92ec8aff7fb6, 0x76d4de52895820d9,
0x317ea4bb22bfdfed, 0x8250e80521188082, 0xbe2a516875702185, 0x0d041dd676d77eea, 0x4aae673fdd3081de,
0xf9802b81de97deb1, 0x4fc0b4dd24d2a599, 0xfceef8632775faf6, 0xbb44828a8c9205c2, 0x086ace348f355aad,
0x34107759db5dfbaa, 0x873e3be7d8faa4c5, 0xc094410e731d5bf1, 0x73ba0db070ba049e, 0xb86133d4dbcc19ff,
0x0b4f7f6ad86b4690, 0x4ce50583738cb9a4, 0xffcb493d702be6cb, 0xc3b1f050244347cc, 0x709fbcee27e418a3,
0x3735c6078c03e797, 0x841b8ab98fa4b8f8, 0xadda7c5f3c4488e3, 0x1ef430e13fe3d78c, 0x595e4a08940428b8,
0xea7006b697a377d7, 0xd60abfdbc3cbd6d0, 0x6524f365c06c89bf, 0x228e898c6b8b768b, 0x91a0c532682c29e4,
0x5a7bfb56c35a3485, 0xe955b7e8c0fd6bea, 0xaeffcd016b1a94de, 0x1dd181bf68bdcbb1, 0x21ab38d23cd56ab6,
0x9285746c3f7235d9, 0xd52f0e859495caed, 0x6601423b97329582, 0xd041dd676d77eeaa, 0x636f91d96ed0b1c5,
0x24c5eb30c5374ef1, 0x97eba78ec690119e, 0xab911ee392f8b099, 0x18bf525d915feff6, 0x5f1528b43ab810c2,
0xec3b640a391f4fad, 0x27e05a6e926952cc, 0x94ce16d091ce0da3, 0xd3646c393a29f297, 0x604a2087398eadf8,
0x5c3099ea6de60cff, 0xef1ed5546e415390, 0xa8b4afbdc5a6aca4, 0x1b9ae303c601f3cb, 0x56ed3e2f9e224471,
0xe5c372919d851b1e, 0xa26908783662e42a, 0x114744c635c5bb45, 0x2d3dfdab61ad1a42, 0x9e13b115620a452d,
0xd9b9cbfcc9edba19, 0x6a978742ca4ae576, 0xa14cb926613cf817, 0x1262f598629ba778, 0x55c88f71c97c584c,
0xe6e6c3cfcadb0723, 0xda9c7aa29eb3a624, 0x69b2361c9d14f94b, 0x2e184cf536f3067f, 0x9d36004b35545910,
0x2b769f17cf112238, 0x9858d3a9ccb67d57, 0xdff2a94067518263, 0x6cdce5fe64f6dd0c, 0x50a65c93309e7c0b,
0xe388102d33392364, 0xa4226ac498dedc50, 0x170c267a9b79833f, 0xdcd7181e300f9e5e, 0x6ff954a033a8c131,
0x28532e49984f3e05, 0x9b7d62f79be8616a, 0xa707db9acf80c06d, 0x14299724cc279f02, 0x5383edcd67c06036,
0xe0ada17364673f59};
static uint64 crc64_partial(Slice data, uint64 crc) {
const char *p = data.begin();
for (auto len = data.size(); len > 0; len--) {
crc = crc64_table[(crc ^ *p++) & 0xff] ^ (crc >> 8);
}
return crc;
}
uint64 crc64(Slice data) {
return crc64_partial(data, static_cast<uint64>(-1)) ^ static_cast<uint64>(-1);
}
static const uint16 crc16_table[256] = {
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad,
0xe1ce, 0xf1ef, 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, 0x9339, 0x8318, 0xb37b, 0xa35a,
0xd3bd, 0xc39c, 0xf3ff, 0xe3de, 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, 0xa56a, 0xb54b,
0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4,
0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861,
0x2802, 0x3823, 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, 0x5af5, 0x4ad4, 0x7ab7, 0x6a96,
0x1a71, 0x0a50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, 0x6ca6, 0x7c87,
0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49,
0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a,
0x9f59, 0x8f78, 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, 0x1080, 0x00a1, 0x30c2, 0x20e3,
0x5004, 0x4025, 0x7046, 0x6067, 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, 0x02b1, 0x1290,
0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d,
0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e,
0xc71d, 0xd73c, 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, 0xd94c, 0xc96d, 0xf90e, 0xe92f,
0x99c8, 0x89e9, 0xb98a, 0xa9ab, 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, 0xcb7d, 0xdb5c,
0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92,
0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83,
0x1ce0, 0x0cc1, 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, 0x6e17, 0x7e36, 0x4e55, 0x5e74,
0x2e93, 0x3eb2, 0x0ed1, 0x1ef0};
uint16 crc16(Slice data) {
uint32 crc = 0;
for (auto c : data) {
auto t = (static_cast<unsigned char>(c) ^ (crc >> 8)) & 0xff;
crc = crc16_table[t] ^ (crc << 8);
}
return static_cast<uint16>(crc);
}
} // namespace td

Some files were not shown because too many files have changed in this diff Show more