tdutils: import changes from other project
GitOrigin-RevId: 0cfbe2418933cffdce4f0d1b8461c247a814d868
This commit is contained in:
parent
7a863daa50
commit
f63ce74fef
@ -99,12 +99,15 @@ set(TDUTILS_SOURCE
|
|||||||
td/utils/MpmcQueue.cpp
|
td/utils/MpmcQueue.cpp
|
||||||
td/utils/OptionsParser.cpp
|
td/utils/OptionsParser.cpp
|
||||||
td/utils/Random.cpp
|
td/utils/Random.cpp
|
||||||
|
td/utils/Slice.cpp
|
||||||
|
td/utils/SharedSlice.cpp
|
||||||
td/utils/StackAllocator.cpp
|
td/utils/StackAllocator.cpp
|
||||||
td/utils/Status.cpp
|
td/utils/Status.cpp
|
||||||
td/utils/StringBuilder.cpp
|
td/utils/StringBuilder.cpp
|
||||||
td/utils/tests.cpp
|
|
||||||
td/utils/Time.cpp
|
td/utils/Time.cpp
|
||||||
td/utils/Timer.cpp
|
td/utils/Timer.cpp
|
||||||
|
td/utils/TsFileLog.cpp
|
||||||
|
td/utils/tests.cpp
|
||||||
td/utils/tl_parsers.cpp
|
td/utils/tl_parsers.cpp
|
||||||
td/utils/translit.cpp
|
td/utils/translit.cpp
|
||||||
td/utils/unicode.cpp
|
td/utils/unicode.cpp
|
||||||
@ -231,6 +234,7 @@ set(TDUTILS_SOURCE
|
|||||||
td/utils/Time.h
|
td/utils/Time.h
|
||||||
td/utils/TimedStat.h
|
td/utils/TimedStat.h
|
||||||
td/utils/Timer.h
|
td/utils/Timer.h
|
||||||
|
td/utils/TsFileLog.h
|
||||||
td/utils/tl_helpers.h
|
td/utils/tl_helpers.h
|
||||||
td/utils/tl_parsers.h
|
td/utils/tl_parsers.h
|
||||||
td/utils/tl_storers.h
|
td/utils/tl_storers.h
|
||||||
@ -250,12 +254,12 @@ set(TDUTILS_TEST_SOURCE
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/test/ConcurrentHashMap.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/test/ConcurrentHashMap.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/test/crypto.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/test/crypto.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/test/Enumerator.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/test/Enumerator.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/test/EpochBasedMemoryReclamation.cpp
|
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/test/filesystem.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/test/filesystem.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/test/gzip.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/test/gzip.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/test/HazardPointers.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/test/HazardPointers.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/test/heap.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/test/heap.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/test/json.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/test/json.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/test/log.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/test/misc.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/test/misc.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/test/MpmcQueue.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/test/MpmcQueue.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/test/MpmcWaiter.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/test/MpmcWaiter.cpp
|
||||||
@ -264,6 +268,7 @@ set(TDUTILS_TEST_SOURCE
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/test/port.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/test/port.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/test/pq.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/test/pq.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/test/SharedObjectPool.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/test/SharedObjectPool.cpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/test/SharedSlice.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/test/variant.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/test/variant.cpp
|
||||||
PARENT_SCOPE
|
PARENT_SCOPE
|
||||||
)
|
)
|
||||||
@ -300,7 +305,7 @@ if (CRC32C_FOUND)
|
|||||||
target_link_libraries(tdutils PRIVATE crc32c)
|
target_link_libraries(tdutils PRIVATE crc32c)
|
||||||
endif()
|
endif()
|
||||||
if (ABSL_FOUND)
|
if (ABSL_FOUND)
|
||||||
target_link_libraries(tdutils PUBLIC absl::base absl::container absl::hash)
|
target_link_libraries_system(tdutils PUBLIC absl::flat_hash_map absl::flat_hash_set absl::hash)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (WIN32 AND WINGETOPT_FOUND)
|
if (WIN32 AND WINGETOPT_FOUND)
|
||||||
|
@ -185,6 +185,9 @@ class DecTree {
|
|||||||
void remove(const KeyType &key) {
|
void remove(const KeyType &key) {
|
||||||
root_ = remove_node(std::move(root_), key);
|
root_ = remove_node(std::move(root_), key);
|
||||||
}
|
}
|
||||||
|
void reset() {
|
||||||
|
root_ = nullptr;
|
||||||
|
}
|
||||||
ValueType *get(const KeyType &key) {
|
ValueType *get(const KeyType &key) {
|
||||||
return get_node(root_, key);
|
return get_node(root_, key);
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
namespace td {
|
namespace td {
|
||||||
|
|
||||||
Status FileLog::init(string path, int64 rotate_threshold) {
|
Status FileLog::init(string path, int64 rotate_threshold, bool redirect_stderr) {
|
||||||
if (path == path_) {
|
if (path == path_) {
|
||||||
set_rotate_threshold(rotate_threshold);
|
set_rotate_threshold(rotate_threshold);
|
||||||
return Status::OK();
|
return Status::OK();
|
||||||
@ -30,7 +30,7 @@ Status FileLog::init(string path, int64 rotate_threshold) {
|
|||||||
|
|
||||||
fd_.close();
|
fd_.close();
|
||||||
fd_ = std::move(fd);
|
fd_ = std::move(fd);
|
||||||
if (!Stderr().empty()) {
|
if (!Stderr().empty() && redirect_stderr) {
|
||||||
fd_.get_native_fd().duplicate(Stderr().get_native_fd()).ignore();
|
fd_.get_native_fd().duplicate(Stderr().get_native_fd()).ignore();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,6 +43,7 @@ Status FileLog::init(string path, int64 rotate_threshold) {
|
|||||||
TRY_RESULT(size, fd_.get_size());
|
TRY_RESULT(size, fd_.get_size());
|
||||||
size_ = size;
|
size_ = size;
|
||||||
rotate_threshold_ = rotate_threshold;
|
rotate_threshold_ = rotate_threshold;
|
||||||
|
redirect_stderr_ = redirect_stderr;
|
||||||
return Status::OK();
|
return Status::OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,7 +109,7 @@ void FileLog::do_rotate() {
|
|||||||
process_fatal_error(PSLICE() << r_fd.error() << " in " << __FILE__ << " at " << __LINE__);
|
process_fatal_error(PSLICE() << r_fd.error() << " in " << __FILE__ << " at " << __LINE__);
|
||||||
}
|
}
|
||||||
fd_ = r_fd.move_as_ok();
|
fd_ = r_fd.move_as_ok();
|
||||||
if (!Stderr().empty()) {
|
if (!Stderr().empty() && redirect_stderr_) {
|
||||||
fd_.get_native_fd().duplicate(Stderr().get_native_fd()).ignore();
|
fd_.get_native_fd().duplicate(Stderr().get_native_fd()).ignore();
|
||||||
}
|
}
|
||||||
size_ = 0;
|
size_ = 0;
|
||||||
|
@ -18,7 +18,7 @@ class FileLog : public LogInterface {
|
|||||||
static constexpr int64 DEFAULT_ROTATE_THRESHOLD = 10 * (1 << 20);
|
static constexpr int64 DEFAULT_ROTATE_THRESHOLD = 10 * (1 << 20);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Status init(string path, int64 rotate_threshold = DEFAULT_ROTATE_THRESHOLD);
|
Status init(string path, int64 rotate_threshold = DEFAULT_ROTATE_THRESHOLD, bool redirect_stderr = true);
|
||||||
|
|
||||||
Slice get_path() const;
|
Slice get_path() const;
|
||||||
|
|
||||||
@ -37,6 +37,7 @@ class FileLog : public LogInterface {
|
|||||||
string path_;
|
string path_;
|
||||||
int64 size_ = 0;
|
int64 size_ = 0;
|
||||||
int64 rotate_threshold_ = 0;
|
int64 rotate_threshold_ = 0;
|
||||||
|
bool redirect_stderr_;
|
||||||
|
|
||||||
void do_rotate();
|
void do_rotate();
|
||||||
};
|
};
|
||||||
|
@ -181,7 +181,7 @@ class JsonObjectScope;
|
|||||||
|
|
||||||
class JsonBuilder {
|
class JsonBuilder {
|
||||||
public:
|
public:
|
||||||
explicit JsonBuilder(StringBuilder &&sb) : sb_(std::move(sb)) {
|
explicit JsonBuilder(StringBuilder &&sb, int32 offset = -1) : sb_(std::move(sb)), offset_(offset) {
|
||||||
}
|
}
|
||||||
StringBuilder &string_builder() {
|
StringBuilder &string_builder() {
|
||||||
return sb_;
|
return sb_;
|
||||||
@ -191,9 +191,33 @@ class JsonBuilder {
|
|||||||
JsonArrayScope enter_array() TD_WARN_UNUSED_RESULT;
|
JsonArrayScope enter_array() TD_WARN_UNUSED_RESULT;
|
||||||
JsonObjectScope enter_object() 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:
|
private:
|
||||||
StringBuilder sb_;
|
StringBuilder sb_;
|
||||||
JsonScope *scope_ = nullptr;
|
JsonScope *scope_ = nullptr;
|
||||||
|
int32 offset_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Jsonable {};
|
class Jsonable {};
|
||||||
@ -328,6 +352,7 @@ class JsonValueScope : public JsonScope {
|
|||||||
class JsonArrayScope : public JsonScope {
|
class JsonArrayScope : public JsonScope {
|
||||||
public:
|
public:
|
||||||
explicit JsonArrayScope(JsonBuilder *jb) : JsonScope(jb) {
|
explicit JsonArrayScope(JsonBuilder *jb) : JsonScope(jb) {
|
||||||
|
jb->inc_offset();
|
||||||
*sb_ << "[";
|
*sb_ << "[";
|
||||||
}
|
}
|
||||||
JsonArrayScope(JsonArrayScope &&other) = default;
|
JsonArrayScope(JsonArrayScope &&other) = default;
|
||||||
@ -337,6 +362,11 @@ class JsonArrayScope : public JsonScope {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
void leave() {
|
void leave() {
|
||||||
|
jb_->dec_offset();
|
||||||
|
if (jb_->is_pretty()) {
|
||||||
|
*sb_ << "\n";
|
||||||
|
jb_->print_offset();
|
||||||
|
}
|
||||||
*sb_ << "]";
|
*sb_ << "]";
|
||||||
}
|
}
|
||||||
template <class T>
|
template <class T>
|
||||||
@ -355,6 +385,10 @@ class JsonArrayScope : public JsonScope {
|
|||||||
} else {
|
} else {
|
||||||
is_first_ = true;
|
is_first_ = true;
|
||||||
}
|
}
|
||||||
|
if (jb_->is_pretty()) {
|
||||||
|
*sb_ << "\n";
|
||||||
|
jb_->print_offset();
|
||||||
|
}
|
||||||
return jb_->enter_value();
|
return jb_->enter_value();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -365,6 +399,7 @@ class JsonArrayScope : public JsonScope {
|
|||||||
class JsonObjectScope : public JsonScope {
|
class JsonObjectScope : public JsonScope {
|
||||||
public:
|
public:
|
||||||
explicit JsonObjectScope(JsonBuilder *jb) : JsonScope(jb) {
|
explicit JsonObjectScope(JsonBuilder *jb) : JsonScope(jb) {
|
||||||
|
jb->inc_offset();
|
||||||
*sb_ << "{";
|
*sb_ << "{";
|
||||||
}
|
}
|
||||||
JsonObjectScope(JsonObjectScope &&other) = default;
|
JsonObjectScope(JsonObjectScope &&other) = default;
|
||||||
@ -374,6 +409,11 @@ class JsonObjectScope : public JsonScope {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
void leave() {
|
void leave() {
|
||||||
|
jb_->dec_offset();
|
||||||
|
if (jb_->is_pretty()) {
|
||||||
|
*sb_ << "\n";
|
||||||
|
jb_->print_offset();
|
||||||
|
}
|
||||||
*sb_ << "}";
|
*sb_ << "}";
|
||||||
}
|
}
|
||||||
template <class S, class T>
|
template <class S, class T>
|
||||||
@ -392,8 +432,16 @@ class JsonObjectScope : public JsonScope {
|
|||||||
} else {
|
} else {
|
||||||
is_first_ = true;
|
is_first_ = true;
|
||||||
}
|
}
|
||||||
|
if (jb_->is_pretty()) {
|
||||||
|
*sb_ << "\n";
|
||||||
|
jb_->print_offset();
|
||||||
|
}
|
||||||
jb_->enter_value() << key;
|
jb_->enter_value() << key;
|
||||||
|
if (jb_->is_pretty()) {
|
||||||
|
*sb_ << " : ";
|
||||||
|
} else {
|
||||||
*sb_ << ":";
|
*sb_ << ":";
|
||||||
|
}
|
||||||
jb_->enter_value() << value;
|
jb_->enter_value() << value;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
@ -737,11 +785,14 @@ inline Result<JsonValue> json_decode(MutableSlice json) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <class StrT, class ValT>
|
template <class StrT, class ValT>
|
||||||
StrT json_encode(const ValT &val) {
|
StrT json_encode(const ValT &val, bool pretty = false) {
|
||||||
auto buf_len = 1 << 18;
|
auto buf_len = 1 << 18;
|
||||||
auto buf = StackAllocator::alloc(buf_len);
|
auto buf = StackAllocator::alloc(buf_len);
|
||||||
JsonBuilder jb(StringBuilder(buf.as_slice(), true));
|
JsonBuilder jb(StringBuilder(buf.as_slice(), true), pretty ? 0 : -1);
|
||||||
jb.enter_value() << val;
|
jb.enter_value() << val;
|
||||||
|
if (pretty) {
|
||||||
|
jb.string_builder() << "\n";
|
||||||
|
}
|
||||||
LOG_IF(ERROR, jb.string_builder().is_error()) << "JSON buffer overflow";
|
LOG_IF(ERROR, jb.string_builder().is_error()) << "JSON buffer overflow";
|
||||||
auto slice = jb.string_builder().as_cslice();
|
auto slice = jb.string_builder().as_cslice();
|
||||||
return StrT(slice.begin(), slice.size());
|
return StrT(slice.begin(), slice.size());
|
||||||
|
@ -90,7 +90,8 @@ class MpmcWaiter {
|
|||||||
return (state >> 1) == (worker + 1);
|
return (state >> 1) == (worker + 1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
enum { RoundsTillSleepy = 32, RoundsTillAsleep = 64 };
|
//enum { RoundsTillSleepy = 32, RoundsTillAsleep = 64 };
|
||||||
|
enum { RoundsTillSleepy = 1, RoundsTillAsleep = 2 };
|
||||||
std::atomic<uint32> state_{State::awake()};
|
std::atomic<uint32> state_{State::awake()};
|
||||||
std::mutex mutex_;
|
std::mutex mutex_;
|
||||||
std::condition_variable condition_variable_;
|
std::condition_variable condition_variable_;
|
||||||
|
17
tdutils/td/utils/SharedSlice.cpp
Normal file
17
tdutils/td/utils/SharedSlice.cpp
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
//
|
||||||
|
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
#include "td/utils/SharedSlice.h"
|
||||||
|
|
||||||
|
#include "td/utils/buffer.h"
|
||||||
|
|
||||||
|
namespace td {
|
||||||
|
|
||||||
|
BufferSlice SharedSlice::clone_as_buffer_slice() const {
|
||||||
|
return BufferSlice{as_slice().str()};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace td
|
377
tdutils/td/utils/SharedSlice.h
Normal file
377
tdutils/td/utils/SharedSlice.h
Normal file
@ -0,0 +1,377 @@
|
|||||||
|
//
|
||||||
|
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "td/utils/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 {
|
||||||
|
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
|
@ -52,6 +52,9 @@ class MutableSlice {
|
|||||||
MutableSlice substr(size_t from, size_t size) const;
|
MutableSlice substr(size_t from, size_t size) const;
|
||||||
size_t find(char c) const;
|
size_t find(char c) const;
|
||||||
size_t rfind(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);
|
void copy_from(Slice from);
|
||||||
|
|
||||||
|
31
tdutils/td/utils/Slice.cpp
Normal file
31
tdutils/td/utils/Slice.cpp
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
//
|
||||||
|
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
#include "td/utils/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
|
@ -316,4 +316,12 @@ inline MutableSlice as_slice(string &str) {
|
|||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline MutableSlice as_mutable_slice(MutableSlice slice) {
|
||||||
|
return slice;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline MutableSlice as_mutable_slice(string &str) {
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace td
|
} // namespace td
|
||||||
|
54
tdutils/td/utils/ThreadLocalStorage.h
Normal file
54
tdutils/td/utils/ThreadLocalStorage.h
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
//
|
||||||
|
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "td/utils/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
|
@ -5,45 +5,107 @@
|
|||||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
//
|
//
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "td/utils/ThreadLocalStorage.h"
|
||||||
#include "td/utils/common.h"
|
#include "td/utils/StringBuilder.h"
|
||||||
#include "td/utils/port/thread_local.h"
|
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <atomic>
|
#include <mutex>
|
||||||
|
|
||||||
namespace td {
|
namespace td {
|
||||||
|
template <size_t N>
|
||||||
class ThreadSafeCounter {
|
class ThreadSafeMultiCounter {
|
||||||
public:
|
public:
|
||||||
void add(int64 diff) {
|
void add(size_t index, int64 diff) {
|
||||||
auto &node = thread_local_node();
|
CHECK(index < N);
|
||||||
node.count_.store(node.count_.load(std::memory_order_relaxed) + diff, std::memory_order_relaxed);
|
tls_.get()[index].fetch_add(diff, std::memory_order_relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
int64 sum() const {
|
int64 sum(size_t index) const {
|
||||||
int n = max_thread_id_.load();
|
CHECK(index < N);
|
||||||
int64 res = 0;
|
int64 res = 0;
|
||||||
for (int i = 0; i < n; i++) {
|
tls_.for_each([&](auto &value) { res += value[index].load(); });
|
||||||
res += nodes_[i].count_.load();
|
|
||||||
}
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Node {
|
ThreadLocalStorage<std::array<std::atomic<int64>, N>> tls_;
|
||||||
std::atomic<int64> count_{0};
|
};
|
||||||
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() {
|
class ThreadSafeCounter {
|
||||||
auto thread_id = get_thread_id();
|
public:
|
||||||
CHECK(static_cast<size_t>(thread_id) < nodes_.size());
|
void add(int64 diff) {
|
||||||
return nodes_[thread_id];
|
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
|
} // namespace td
|
||||||
|
78
tdutils/td/utils/TsFileLog.cpp
Normal file
78
tdutils/td/utils/TsFileLog.cpp
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
//
|
||||||
|
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
#include "TsFileLog.h"
|
||||||
|
|
||||||
|
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
|
16
tdutils/td/utils/TsFileLog.h
Normal file
16
tdutils/td/utils/TsFileLog.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
//
|
||||||
|
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "td/utils/FileLog.h"
|
||||||
|
|
||||||
|
namespace td {
|
||||||
|
class TsFileLog {
|
||||||
|
public:
|
||||||
|
static Result<td::unique_ptr<LogInterface>> create(string path);
|
||||||
|
};
|
||||||
|
} // namespace td
|
@ -10,6 +10,8 @@
|
|||||||
#include "td/utils/Slice.h"
|
#include "td/utils/Slice.h"
|
||||||
#include "td/utils/Status.h"
|
#include "td/utils/Status.h"
|
||||||
|
|
||||||
|
#include "td/utils/format.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
|
|
||||||
@ -48,7 +50,7 @@ string base64_encode(Slice input) {
|
|||||||
|
|
||||||
static unsigned char char_to_value[256];
|
static unsigned char char_to_value[256];
|
||||||
static void init_base64_table() {
|
static void init_base64_table() {
|
||||||
static bool is_inited = []() {
|
static bool is_inited = [] {
|
||||||
std::fill(std::begin(char_to_value), std::end(char_to_value), static_cast<unsigned char>(64));
|
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++) {
|
for (unsigned char i = 0; i < 64; i++) {
|
||||||
char_to_value[static_cast<size_t>(symbols64[i])] = i;
|
char_to_value[static_cast<size_t>(symbols64[i])] = i;
|
||||||
@ -58,9 +60,7 @@ static void init_base64_table() {
|
|||||||
CHECK(is_inited);
|
CHECK(is_inited);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<string> base64_decode(Slice base64) {
|
Result<Slice> base64_drop_padding(Slice base64) {
|
||||||
init_base64_table();
|
|
||||||
|
|
||||||
if ((base64.size() & 3) != 0) {
|
if ((base64.size() & 3) != 0) {
|
||||||
return Status::Error("Wrong string length");
|
return Status::Error("Wrong string length");
|
||||||
}
|
}
|
||||||
@ -73,9 +73,11 @@ Result<string> base64_decode(Slice base64) {
|
|||||||
if (padding_length >= 3) {
|
if (padding_length >= 3) {
|
||||||
return Status::Error("Wrong string padding");
|
return Status::Error("Wrong string padding");
|
||||||
}
|
}
|
||||||
|
return base64;
|
||||||
|
}
|
||||||
|
|
||||||
string output;
|
template <class F>
|
||||||
output.reserve(((base64.size() + 3) >> 2) * 3);
|
Status base64_do_decode(Slice base64, F &&append) {
|
||||||
for (size_t i = 0; i < base64.size();) {
|
for (size_t i = 0; i < base64.size();) {
|
||||||
size_t left = min(base64.size() - i, static_cast<size_t>(4));
|
size_t left = min(base64.size() - i, static_cast<size_t>(4));
|
||||||
int c = 0;
|
int c = 0;
|
||||||
@ -86,25 +88,53 @@ Result<string> base64_decode(Slice base64) {
|
|||||||
}
|
}
|
||||||
c |= value << ((3 - t) * 6);
|
c |= value << ((3 - t) * 6);
|
||||||
}
|
}
|
||||||
output += static_cast<char>(static_cast<unsigned char>(c >> 16)); // implementation-defined
|
append(static_cast<char>(static_cast<unsigned char>(c >> 16))); // implementation-defined
|
||||||
if (left == 2) {
|
if (left == 2) {
|
||||||
if ((c & ((1 << 16) - 1)) != 0) {
|
if ((c & ((1 << 16) - 1)) != 0) {
|
||||||
return Status::Error("Wrong padding in the string");
|
return Status::Error("Wrong padding in the string");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
output += static_cast<char>(static_cast<unsigned char>(c >> 8)); // implementation-defined
|
append(static_cast<char>(static_cast<unsigned char>(c >> 8))); // implementation-defined
|
||||||
if (left == 3) {
|
if (left == 3) {
|
||||||
if ((c & ((1 << 8) - 1)) != 0) {
|
if ((c & ((1 << 8) - 1)) != 0) {
|
||||||
return Status::Error("Wrong padding in the string");
|
return Status::Error("Wrong padding in the string");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
output += static_cast<char>(static_cast<unsigned char>(c)); // implementation-defined
|
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;
|
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-_";
|
static const char *const url_symbols64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
||||||
|
|
||||||
string base64url_encode(Slice input) {
|
string base64url_encode(Slice input) {
|
||||||
@ -133,7 +163,7 @@ string base64url_encode(Slice input) {
|
|||||||
|
|
||||||
static unsigned char url_char_to_value[256];
|
static unsigned char url_char_to_value[256];
|
||||||
static void init_base64url_table() {
|
static void init_base64url_table() {
|
||||||
static bool is_inited = []() {
|
static bool is_inited = [] {
|
||||||
std::fill(std::begin(url_char_to_value), std::end(url_char_to_value), static_cast<unsigned char>(64));
|
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++) {
|
for (unsigned char i = 0; i < 64; i++) {
|
||||||
url_char_to_value[static_cast<size_t>(url_symbols64[i])] = i;
|
url_char_to_value[static_cast<size_t>(url_symbols64[i])] = i;
|
||||||
|
@ -8,12 +8,14 @@
|
|||||||
|
|
||||||
#include "td/utils/common.h"
|
#include "td/utils/common.h"
|
||||||
#include "td/utils/Slice.h"
|
#include "td/utils/Slice.h"
|
||||||
|
#include "td/utils/SharedSlice.h"
|
||||||
#include "td/utils/Status.h"
|
#include "td/utils/Status.h"
|
||||||
|
|
||||||
namespace td {
|
namespace td {
|
||||||
|
|
||||||
string base64_encode(Slice input);
|
string base64_encode(Slice input);
|
||||||
Result<string> base64_decode(Slice base64);
|
Result<string> base64_decode(Slice base64);
|
||||||
|
Result<SecureString> base64_decode_secure(Slice base64);
|
||||||
|
|
||||||
string base64url_encode(Slice input);
|
string base64url_encode(Slice input);
|
||||||
Result<string> base64url_decode(Slice base64);
|
Result<string> base64url_decode(Slice base64);
|
||||||
|
@ -796,5 +796,8 @@ inline Slice as_slice(const BufferSlice &value) {
|
|||||||
inline MutableSlice as_slice(BufferSlice &value) {
|
inline MutableSlice as_slice(BufferSlice &value) {
|
||||||
return value.as_slice();
|
return value.as_slice();
|
||||||
}
|
}
|
||||||
|
inline MutableSlice as_mutable_slice(BufferSlice &value) {
|
||||||
|
return value.as_slice();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace td
|
} // namespace td
|
||||||
|
@ -241,66 +241,74 @@ int pq_factorize(Slice pq_str, string *p_str, string *q_str) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void aes_ige_xcrypt(const UInt256 &aes_key, UInt256 *aes_iv, Slice from, MutableSlice to, bool encrypt_flag) {
|
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;
|
AES_KEY key;
|
||||||
int err;
|
int err;
|
||||||
if (encrypt_flag) {
|
if (encrypt_flag) {
|
||||||
err = AES_set_encrypt_key(aes_key.raw, 256, &key);
|
err = AES_set_encrypt_key(aes_key.ubegin(), 256, &key);
|
||||||
} else {
|
} else {
|
||||||
err = AES_set_decrypt_key(aes_key.raw, 256, &key);
|
err = AES_set_decrypt_key(aes_key.ubegin(), 256, &key);
|
||||||
}
|
}
|
||||||
LOG_IF(FATAL, err != 0);
|
LOG_IF(FATAL, err != 0);
|
||||||
CHECK(from.size() <= to.size());
|
CHECK(from.size() <= to.size());
|
||||||
AES_ige_encrypt(from.ubegin(), to.ubegin(), from.size(), &key, aes_iv->raw, encrypt_flag);
|
AES_ige_encrypt(from.ubegin(), to.ubegin(), from.size(), &key, aes_iv.ubegin(), encrypt_flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
void aes_ige_encrypt(const UInt256 &aes_key, UInt256 *aes_iv, Slice from, MutableSlice to) {
|
void aes_ige_encrypt(Slice aes_key, MutableSlice aes_iv, Slice from, MutableSlice to) {
|
||||||
aes_ige_xcrypt(aes_key, aes_iv, from, to, true);
|
aes_ige_xcrypt(aes_key, aes_iv, from, to, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void aes_ige_decrypt(const UInt256 &aes_key, UInt256 *aes_iv, Slice from, MutableSlice to) {
|
void aes_ige_decrypt(Slice aes_key, MutableSlice aes_iv, Slice from, MutableSlice to) {
|
||||||
aes_ige_xcrypt(aes_key, aes_iv, from, to, false);
|
aes_ige_xcrypt(aes_key, aes_iv, from, to, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void aes_cbc_xcrypt(const UInt256 &aes_key, UInt128 *aes_iv, Slice from, MutableSlice to, bool encrypt_flag) {
|
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;
|
AES_KEY key;
|
||||||
int err;
|
int err;
|
||||||
if (encrypt_flag) {
|
if (encrypt_flag) {
|
||||||
err = AES_set_encrypt_key(aes_key.raw, 256, &key);
|
err = AES_set_encrypt_key(aes_key.ubegin(), 256, &key);
|
||||||
} else {
|
} else {
|
||||||
err = AES_set_decrypt_key(aes_key.raw, 256, &key);
|
err = AES_set_decrypt_key(aes_key.ubegin(), 256, &key);
|
||||||
}
|
}
|
||||||
LOG_IF(FATAL, err != 0);
|
LOG_IF(FATAL, err != 0);
|
||||||
CHECK(from.size() <= to.size());
|
CHECK(from.size() <= to.size());
|
||||||
AES_cbc_encrypt(from.ubegin(), to.ubegin(), from.size(), &key, aes_iv->raw, encrypt_flag);
|
AES_cbc_encrypt(from.ubegin(), to.ubegin(), from.size(), &key, aes_iv.ubegin(), encrypt_flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
void aes_cbc_encrypt(const UInt256 &aes_key, UInt128 *aes_iv, Slice from, MutableSlice to) {
|
void aes_cbc_encrypt(Slice aes_key, MutableSlice aes_iv, Slice from, MutableSlice to) {
|
||||||
aes_cbc_xcrypt(aes_key, aes_iv, from, to, true);
|
aes_cbc_xcrypt(aes_key, aes_iv, from, to, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void aes_cbc_decrypt(const UInt256 &aes_key, UInt128 *aes_iv, Slice from, MutableSlice to) {
|
void aes_cbc_decrypt(Slice aes_key, MutableSlice aes_iv, Slice from, MutableSlice to) {
|
||||||
aes_cbc_xcrypt(aes_key, aes_iv, from, to, false);
|
aes_cbc_xcrypt(aes_key, aes_iv, from, to, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
AesCbcState::AesCbcState(const UInt256 &key, const UInt128 &iv) : key_(key), iv_(iv) {
|
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) {
|
void AesCbcState::encrypt(Slice from, MutableSlice to) {
|
||||||
::td::aes_cbc_encrypt(key_, &iv_, from, to);
|
::td::aes_cbc_encrypt(key_.as_slice(), iv_.as_mutable_slice(), from, to);
|
||||||
}
|
}
|
||||||
void AesCbcState::decrypt(Slice from, MutableSlice to) {
|
void AesCbcState::decrypt(Slice from, MutableSlice to) {
|
||||||
::td::aes_cbc_decrypt(key_, &iv_, from, to);
|
::td::aes_cbc_decrypt(key_.as_slice(), iv_.as_mutable_slice(), from, to);
|
||||||
}
|
}
|
||||||
|
|
||||||
class AesCtrState::Impl {
|
class AesCtrState::Impl {
|
||||||
public:
|
public:
|
||||||
Impl(const UInt256 &key, const UInt128 &iv) {
|
Impl(Slice key, Slice iv) {
|
||||||
|
CHECK(key.size() == 32);
|
||||||
|
CHECK(iv.size() == 16);
|
||||||
static_assert(AES_BLOCK_SIZE == 16, "");
|
static_assert(AES_BLOCK_SIZE == 16, "");
|
||||||
if (AES_set_encrypt_key(key.raw, 256, &aes_key) < 0) {
|
if (AES_set_encrypt_key(key.ubegin(), 256, &aes_key) < 0) {
|
||||||
LOG(FATAL) << "Failed to set encrypt key";
|
LOG(FATAL) << "Failed to set encrypt key";
|
||||||
}
|
}
|
||||||
MutableSlice(counter, AES_BLOCK_SIZE).copy_from(as_slice(iv));
|
counter.as_mutable_slice().copy_from(as_slice(iv));
|
||||||
current_pos = 0;
|
current_pos = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -308,9 +316,10 @@ class AesCtrState::Impl {
|
|||||||
CHECK(to.size() >= from.size());
|
CHECK(to.size() >= from.size());
|
||||||
for (size_t i = 0; i < from.size(); i++) {
|
for (size_t i = 0; i < from.size(); i++) {
|
||||||
if (current_pos == 0) {
|
if (current_pos == 0) {
|
||||||
AES_encrypt(counter, encrypted_counter, &aes_key);
|
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--) {
|
for (int j = 15; j >= 0; j--) {
|
||||||
if (++counter[j] != 0) {
|
if (++ptr[j] != 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -322,8 +331,8 @@ class AesCtrState::Impl {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
AES_KEY aes_key;
|
AES_KEY aes_key;
|
||||||
uint8 counter[AES_BLOCK_SIZE];
|
SecureString counter{AES_BLOCK_SIZE};
|
||||||
uint8 encrypted_counter[AES_BLOCK_SIZE];
|
SecureString encrypted_counter{AES_BLOCK_SIZE};
|
||||||
uint8 current_pos;
|
uint8 current_pos;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -332,7 +341,7 @@ AesCtrState::AesCtrState(AesCtrState &&from) = default;
|
|||||||
AesCtrState &AesCtrState::operator=(AesCtrState &&from) = default;
|
AesCtrState &AesCtrState::operator=(AesCtrState &&from) = default;
|
||||||
AesCtrState::~AesCtrState() = default;
|
AesCtrState::~AesCtrState() = default;
|
||||||
|
|
||||||
void AesCtrState::init(const UInt256 &key, const UInt128 &iv) {
|
void AesCtrState::init(Slice key, Slice iv) {
|
||||||
ctx_ = make_unique<AesCtrState::Impl>(key, iv);
|
ctx_ = make_unique<AesCtrState::Impl>(key, iv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,8 +9,8 @@
|
|||||||
#include "td/utils/buffer.h"
|
#include "td/utils/buffer.h"
|
||||||
#include "td/utils/common.h"
|
#include "td/utils/common.h"
|
||||||
#include "td/utils/Slice.h"
|
#include "td/utils/Slice.h"
|
||||||
|
#include "td/utils/SharedSlice.h"
|
||||||
#include "td/utils/Status.h"
|
#include "td/utils/Status.h"
|
||||||
#include "td/utils/UInt.h"
|
|
||||||
|
|
||||||
namespace td {
|
namespace td {
|
||||||
|
|
||||||
@ -21,11 +21,11 @@ void init_crypto();
|
|||||||
|
|
||||||
int pq_factorize(Slice pq_str, string *p_str, string *q_str);
|
int pq_factorize(Slice pq_str, string *p_str, string *q_str);
|
||||||
|
|
||||||
void aes_ige_encrypt(const UInt256 &aes_key, UInt256 *aes_iv, Slice from, MutableSlice to);
|
void aes_ige_encrypt(Slice aes_key, MutableSlice aes_iv, Slice from, MutableSlice to);
|
||||||
void aes_ige_decrypt(const UInt256 &aes_key, UInt256 *aes_iv, Slice from, MutableSlice to);
|
void aes_ige_decrypt(Slice aes_key, MutableSlice aes_iv, Slice from, MutableSlice to);
|
||||||
|
|
||||||
void aes_cbc_encrypt(const UInt256 &aes_key, UInt128 *aes_iv, Slice from, MutableSlice to);
|
void aes_cbc_encrypt(Slice aes_key, MutableSlice aes_iv, Slice from, MutableSlice to);
|
||||||
void aes_cbc_decrypt(const UInt256 &aes_key, UInt128 *aes_iv, Slice from, MutableSlice to);
|
void aes_cbc_decrypt(Slice aes_key, MutableSlice aes_iv, Slice from, MutableSlice to);
|
||||||
|
|
||||||
class AesCtrState {
|
class AesCtrState {
|
||||||
public:
|
public:
|
||||||
@ -36,7 +36,7 @@ class AesCtrState {
|
|||||||
AesCtrState &operator=(AesCtrState &&from);
|
AesCtrState &operator=(AesCtrState &&from);
|
||||||
~AesCtrState();
|
~AesCtrState();
|
||||||
|
|
||||||
void init(const UInt256 &key, const UInt128 &iv);
|
void init(Slice key, Slice iv);
|
||||||
|
|
||||||
void encrypt(Slice from, MutableSlice to);
|
void encrypt(Slice from, MutableSlice to);
|
||||||
|
|
||||||
@ -49,14 +49,14 @@ class AesCtrState {
|
|||||||
|
|
||||||
class AesCbcState {
|
class AesCbcState {
|
||||||
public:
|
public:
|
||||||
AesCbcState(const UInt256 &key, const UInt128 &iv);
|
AesCbcState(Slice key256, Slice iv128);
|
||||||
|
|
||||||
void encrypt(Slice from, MutableSlice to);
|
void encrypt(Slice from, MutableSlice to);
|
||||||
void decrypt(Slice from, MutableSlice to);
|
void decrypt(Slice from, MutableSlice to);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
UInt256 key_;
|
SecureString key_;
|
||||||
UInt128 iv_;
|
SecureString iv_;
|
||||||
};
|
};
|
||||||
|
|
||||||
void sha1(Slice data, unsigned char output[20]);
|
void sha1(Slice data, unsigned char output[20]);
|
||||||
|
@ -32,6 +32,10 @@ template <>
|
|||||||
BufferSlice create_empty<BufferSlice>(size_t size) {
|
BufferSlice create_empty<BufferSlice>(size_t size) {
|
||||||
return BufferSlice{size};
|
return BufferSlice{size};
|
||||||
}
|
}
|
||||||
|
template <>
|
||||||
|
SecureString create_empty<SecureString>(size_t size) {
|
||||||
|
return SecureString{size};
|
||||||
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
Result<T> read_file_impl(CSlice path, int64 size, int64 offset) {
|
Result<T> read_file_impl(CSlice path, int64 size, int64 offset) {
|
||||||
@ -48,7 +52,7 @@ Result<T> read_file_impl(CSlice path, int64 size, int64 offset) {
|
|||||||
}
|
}
|
||||||
size -= offset;
|
size -= offset;
|
||||||
auto content = create_empty<T>(narrow_cast<size_t>(size));
|
auto content = create_empty<T>(narrow_cast<size_t>(size));
|
||||||
TRY_RESULT(got_size, from_file.pread(as_slice(content), offset));
|
TRY_RESULT(got_size, from_file.pread(as_mutable_slice(content), offset));
|
||||||
if (got_size != static_cast<size_t>(size)) {
|
if (got_size != static_cast<size_t>(size)) {
|
||||||
return Status::Error("Failed to read file");
|
return Status::Error("Failed to read file");
|
||||||
}
|
}
|
||||||
@ -66,6 +70,10 @@ Result<string> read_file_str(CSlice path, int64 size, int64 offset) {
|
|||||||
return read_file_impl<string>(path, size, offset);
|
return read_file_impl<string>(path, size, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result<SecureString> read_file_secure(CSlice path, int64 size, int64 offset) {
|
||||||
|
return read_file_impl<SecureString>(path, size, offset);
|
||||||
|
}
|
||||||
|
|
||||||
// Very straightforward function. Don't expect much of it.
|
// Very straightforward function. Don't expect much of it.
|
||||||
Status copy_file(CSlice from, CSlice to, int64 size) {
|
Status copy_file(CSlice from, CSlice to, int64 size) {
|
||||||
TRY_RESULT(content, read_file(from, size));
|
TRY_RESULT(content, read_file(from, size));
|
||||||
|
@ -8,12 +8,14 @@
|
|||||||
|
|
||||||
#include "td/utils/buffer.h"
|
#include "td/utils/buffer.h"
|
||||||
#include "td/utils/Slice.h"
|
#include "td/utils/Slice.h"
|
||||||
|
#include "td/utils/SharedSlice.h"
|
||||||
#include "td/utils/Status.h"
|
#include "td/utils/Status.h"
|
||||||
|
|
||||||
namespace td {
|
namespace td {
|
||||||
|
|
||||||
Result<BufferSlice> read_file(CSlice path, int64 size = -1, int64 offset = 0);
|
Result<BufferSlice> read_file(CSlice path, int64 size = -1, int64 offset = 0);
|
||||||
Result<string> read_file_str(CSlice path, int64 size = -1, int64 offset = 0);
|
Result<string> read_file_str(CSlice path, int64 size = -1, int64 offset = 0);
|
||||||
|
Result<SecureString> read_file_secure(CSlice path, int64 size = -1, int64 offset = 0);
|
||||||
|
|
||||||
Status copy_file(CSlice from, CSlice to, int64 size = -1) TD_WARN_UNUSED_RESULT;
|
Status copy_file(CSlice from, CSlice to, int64 size = -1) TD_WARN_UNUSED_RESULT;
|
||||||
|
|
||||||
|
@ -30,7 +30,9 @@ class ThreadStl {
|
|||||||
ThreadStl &operator=(const ThreadStl &other) = delete;
|
ThreadStl &operator=(const ThreadStl &other) = delete;
|
||||||
ThreadStl(ThreadStl &&) = default;
|
ThreadStl(ThreadStl &&) = default;
|
||||||
ThreadStl &operator=(ThreadStl &&) = default;
|
ThreadStl &operator=(ThreadStl &&) = default;
|
||||||
~ThreadStl() = default;
|
~ThreadStl() {
|
||||||
|
join();
|
||||||
|
}
|
||||||
template <class Function, class... Args>
|
template <class Function, class... Args>
|
||||||
explicit ThreadStl(Function &&f, Args &&... args) {
|
explicit ThreadStl(Function &&f, Args &&... args) {
|
||||||
thread_ = std::thread([args = std::make_tuple(decay_copy(std::forward<Function>(f)),
|
thread_ = std::thread([args = std::make_tuple(decay_copy(std::forward<Function>(f)),
|
||||||
@ -42,11 +44,15 @@ class ThreadStl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void join() {
|
void join() {
|
||||||
|
if (thread_.joinable()) {
|
||||||
thread_.join();
|
thread_.join();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
void detach() {
|
void detach() {
|
||||||
|
if (thread_.joinable()) {
|
||||||
thread_.detach();
|
thread_.detach();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
void set_name(CSlice name) {
|
void set_name(CSlice name) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#include "td/utils/logging.h"
|
#include "td/utils/logging.h"
|
||||||
#include "td/utils/misc.h"
|
#include "td/utils/misc.h"
|
||||||
#include "td/utils/Slice.h"
|
#include "td/utils/Slice.h"
|
||||||
|
#include "td/utils/SharedSlice.h"
|
||||||
#include "td/utils/StackAllocator.h"
|
#include "td/utils/StackAllocator.h"
|
||||||
#include "td/utils/Status.h"
|
#include "td/utils/Status.h"
|
||||||
#include "td/utils/tl_parsers.h"
|
#include "td/utils/tl_parsers.h"
|
||||||
@ -115,11 +116,20 @@ template <class StorerT>
|
|||||||
void store(const string &x, StorerT &storer) {
|
void store(const string &x, StorerT &storer) {
|
||||||
storer.store_string(x);
|
storer.store_string(x);
|
||||||
}
|
}
|
||||||
|
template <class StorerT>
|
||||||
|
void store(const SecureString &x, StorerT &storer) {
|
||||||
|
storer.store_string(x.as_slice());
|
||||||
|
}
|
||||||
template <class ParserT>
|
template <class ParserT>
|
||||||
void parse(string &x, ParserT &parser) {
|
void parse(string &x, ParserT &parser) {
|
||||||
x = parser.template fetch_string<string>();
|
x = parser.template fetch_string<string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class ParserT>
|
||||||
|
void parse(SecureString &x, ParserT &parser) {
|
||||||
|
x = parser.template fetch_string<SecureString>();
|
||||||
|
}
|
||||||
|
|
||||||
template <class T, class StorerT>
|
template <class T, class StorerT>
|
||||||
void store(const vector<T> &vec, StorerT &storer) {
|
void store(const vector<T> &vec, StorerT &storer) {
|
||||||
storer.store_binary(narrow_cast<int32>(vec.size()));
|
storer.store_binary(narrow_cast<int32>(vec.size()));
|
||||||
@ -228,6 +238,21 @@ string serialize(const T &object) {
|
|||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
SecureString serialize_secure(const T &object) {
|
||||||
|
TlStorerCalcLength calc_length;
|
||||||
|
store(object, calc_length);
|
||||||
|
size_t length = calc_length.get_length();
|
||||||
|
|
||||||
|
SecureString key(length, '\0');
|
||||||
|
CHECK(is_aligned_pointer<4>(key.data()));
|
||||||
|
MutableSlice data = key.as_mutable_slice();
|
||||||
|
TlStorerUnsafe storer(data.ubegin());
|
||||||
|
store(object, storer);
|
||||||
|
CHECK(storer.get_buf() == data.uend());
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
TD_WARN_UNUSED_RESULT Status unserialize(T &object, Slice data) {
|
TD_WARN_UNUSED_RESULT Status unserialize(T &object, Slice data) {
|
||||||
TlParser parser(data);
|
TlParser parser(data);
|
||||||
|
@ -148,16 +148,29 @@ class TlParser {
|
|||||||
if (result_len < 254) {
|
if (result_len < 254) {
|
||||||
result_begin = reinterpret_cast<const char *>(data + 1);
|
result_begin = reinterpret_cast<const char *>(data + 1);
|
||||||
result_aligned_len = (result_len >> 2) << 2;
|
result_aligned_len = (result_len >> 2) << 2;
|
||||||
|
data += sizeof(int32);
|
||||||
} else if (result_len == 254) {
|
} else if (result_len == 254) {
|
||||||
result_len = data[1] + (data[2] << 8) + (data[3] << 16);
|
result_len = data[1] + (data[2] << 8) + (data[3] << 16);
|
||||||
result_begin = reinterpret_cast<const char *>(data + 4);
|
result_begin = reinterpret_cast<const char *>(data + 4);
|
||||||
result_aligned_len = ((result_len + 3) >> 2) << 2;
|
result_aligned_len = ((result_len + 3) >> 2) << 2;
|
||||||
|
data += sizeof(int32);
|
||||||
} else {
|
} else {
|
||||||
set_error("Can't fetch string, 255 found");
|
check_len(sizeof(int32));
|
||||||
|
result_len = data[1] + (data[2] << 8) + (data[3] << 16) + (data[4] << 24) + (static_cast<uint64>(data[5]) << 32) +
|
||||||
|
(static_cast<uint64>(data[6]) << 40) + (static_cast<uint64>(data[7]) << 48);
|
||||||
|
if (result_len > std::numeric_limits<size_t>::max() - 3) {
|
||||||
|
set_error("Too big string found");
|
||||||
return T();
|
return T();
|
||||||
}
|
}
|
||||||
|
result_begin = reinterpret_cast<const char *>(data + 8);
|
||||||
|
result_aligned_len = ((result_len + 3) >> 2) << 2;
|
||||||
|
data += sizeof(int64);
|
||||||
|
}
|
||||||
check_len(result_aligned_len);
|
check_len(result_aligned_len);
|
||||||
data += result_aligned_len + sizeof(int32);
|
if (!error.empty()) {
|
||||||
|
return T();
|
||||||
|
}
|
||||||
|
data += result_aligned_len;
|
||||||
return T(result_begin, result_len);
|
return T(result_begin, result_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,6 +178,9 @@ class TlParser {
|
|||||||
T fetch_string_raw(const size_t size) {
|
T fetch_string_raw(const size_t size) {
|
||||||
//CHECK(size % sizeof(int32) == 0);
|
//CHECK(size % sizeof(int32) == 0);
|
||||||
check_len(size);
|
check_len(size);
|
||||||
|
if (!error.empty()) {
|
||||||
|
return T();
|
||||||
|
}
|
||||||
const char *result = reinterpret_cast<const char *>(data);
|
const char *result = reinterpret_cast<const char *>(data);
|
||||||
data += size;
|
data += size;
|
||||||
return T(result, size);
|
return T(result, size);
|
||||||
|
@ -62,6 +62,15 @@ class TlStorerUnsafe {
|
|||||||
*buf_++ = static_cast<unsigned char>(len & 255);
|
*buf_++ = static_cast<unsigned char>(len & 255);
|
||||||
*buf_++ = static_cast<unsigned char>((len >> 8) & 255);
|
*buf_++ = static_cast<unsigned char>((len >> 8) & 255);
|
||||||
*buf_++ = static_cast<unsigned char>(len >> 16);
|
*buf_++ = static_cast<unsigned char>(len >> 16);
|
||||||
|
} else if (len < (1ull << 32)) {
|
||||||
|
*buf_++ = static_cast<unsigned char>(255);
|
||||||
|
*buf_++ = static_cast<unsigned char>(len & 255);
|
||||||
|
*buf_++ = static_cast<unsigned char>((len >> 8) & 255);
|
||||||
|
*buf_++ = static_cast<unsigned char>((len >> 16) & 255);
|
||||||
|
*buf_++ = static_cast<unsigned char>((len >> 24) & 255);
|
||||||
|
*buf_++ = static_cast<unsigned char>(0);
|
||||||
|
*buf_++ = static_cast<unsigned char>(0);
|
||||||
|
*buf_++ = static_cast<unsigned char>(0);
|
||||||
} else {
|
} else {
|
||||||
LOG(FATAL) << "String size " << len << " is too big to be stored";
|
LOG(FATAL) << "String size " << len << " is too big to be stored";
|
||||||
}
|
}
|
||||||
@ -119,8 +128,10 @@ class TlStorerCalcLength {
|
|||||||
size_t add = str.size();
|
size_t add = str.size();
|
||||||
if (add < 254) {
|
if (add < 254) {
|
||||||
add += 1;
|
add += 1;
|
||||||
} else {
|
} else if (add < (1 << 24)) {
|
||||||
add += 4;
|
add += 4;
|
||||||
|
} else {
|
||||||
|
add += 8;
|
||||||
}
|
}
|
||||||
add = (add + 3) & -4;
|
add = (add + 3) & -4;
|
||||||
length += add;
|
length += add;
|
||||||
|
85
tdutils/test/SharedSlice.cpp
Normal file
85
tdutils/test/SharedSlice.cpp
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
//
|
||||||
|
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
#include "td/utils/tests.h"
|
||||||
|
#include "td/utils/SharedSlice.h"
|
||||||
|
|
||||||
|
using namespace td;
|
||||||
|
|
||||||
|
TEST(SharedSlice, Hands) {
|
||||||
|
{
|
||||||
|
SharedSlice h("hello");
|
||||||
|
ASSERT_EQ("hello", h.as_slice());
|
||||||
|
// auto g = h; // CE
|
||||||
|
auto g = h.clone();
|
||||||
|
ASSERT_EQ("hello", g.as_slice());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
SharedSlice h("hello");
|
||||||
|
UniqueSharedSlice g(std::move(h));
|
||||||
|
ASSERT_EQ("", h.as_slice());
|
||||||
|
ASSERT_EQ("hello", g.as_slice());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
SharedSlice h("hello");
|
||||||
|
SharedSlice t = h.clone();
|
||||||
|
UniqueSharedSlice g(std::move(h));
|
||||||
|
ASSERT_EQ("", h.as_slice());
|
||||||
|
ASSERT_EQ("hello", g.as_slice());
|
||||||
|
ASSERT_EQ("hello", t.as_slice());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
UniqueSharedSlice g(5);
|
||||||
|
g.as_mutable_slice().copy_from("hello");
|
||||||
|
SharedSlice h(std::move(g));
|
||||||
|
ASSERT_EQ("hello", h);
|
||||||
|
ASSERT_EQ("", g);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
UniqueSlice h("hello");
|
||||||
|
UniqueSlice g(std::move(h));
|
||||||
|
ASSERT_EQ("", h.as_slice());
|
||||||
|
ASSERT_EQ("hello", g.as_slice());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
SecureString h("hello");
|
||||||
|
SecureString g(std::move(h));
|
||||||
|
ASSERT_EQ("", h.as_slice());
|
||||||
|
ASSERT_EQ("hello", g.as_slice());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Stage stage;
|
||||||
|
SharedSlice a, b;
|
||||||
|
std::vector<td::thread> threads(2);
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
threads[i] = td::thread([i, &stage, &a, &b] {
|
||||||
|
for (int j = 0; j < 10000; j++) {
|
||||||
|
if (i == 0) {
|
||||||
|
a = SharedSlice("hello");
|
||||||
|
b = a.clone();
|
||||||
|
}
|
||||||
|
stage.wait((2 * j + 1) * 2);
|
||||||
|
if (i == 0) {
|
||||||
|
ASSERT_EQ('h', a[0]);
|
||||||
|
a.clear();
|
||||||
|
} else {
|
||||||
|
UniqueSharedSlice c(std::move(b));
|
||||||
|
c.as_mutable_slice()[0] = '!';
|
||||||
|
}
|
||||||
|
stage.wait((2 * j + 2) * 2);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
for (auto &thread : threads) {
|
||||||
|
thread.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -45,18 +45,18 @@ TEST(Crypto, AesCtrState) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
td::AesCtrState state;
|
td::AesCtrState state;
|
||||||
state.init(key, iv);
|
state.init(as_slice(key), as_slice(iv));
|
||||||
td::string t(length, '\0');
|
td::string t(length, '\0');
|
||||||
state.encrypt(s, t);
|
state.encrypt(s, t);
|
||||||
ASSERT_EQ(answers1[i], td::crc32(t));
|
ASSERT_EQ(answers1[i], td::crc32(t));
|
||||||
state.init(key, iv);
|
state.init(as_slice(key), as_slice(iv));
|
||||||
state.decrypt(t, t);
|
state.decrypt(t, t);
|
||||||
ASSERT_STREQ(s, t);
|
ASSERT_STREQ(s, t);
|
||||||
|
|
||||||
for (auto &c : iv.raw) {
|
for (auto &c : iv.raw) {
|
||||||
c = 0xFF;
|
c = 0xFF;
|
||||||
}
|
}
|
||||||
state.init(key, iv);
|
state.init(as_slice(key), as_slice(iv));
|
||||||
state.encrypt(s, t);
|
state.encrypt(s, t);
|
||||||
ASSERT_EQ(answers2[i], td::crc32(t));
|
ASSERT_EQ(answers2[i], td::crc32(t));
|
||||||
|
|
||||||
|
122
tdutils/test/log.cpp
Normal file
122
tdutils/test/log.cpp
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
//
|
||||||
|
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2019
|
||||||
|
//
|
||||||
|
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
//
|
||||||
|
#include "td/utils/tests.h"
|
||||||
|
#include "td/utils/FileLog.h"
|
||||||
|
#include "td/utils/TsFileLog.h"
|
||||||
|
#include "td/utils/logging.h"
|
||||||
|
#include "td/utils/port/path.h"
|
||||||
|
#include "td/utils/benchmark.h"
|
||||||
|
|
||||||
|
// Thread safe logging with tests
|
||||||
|
//
|
||||||
|
// LOG uses thread local LogInterface
|
||||||
|
// void append(CSlice slice, int log_level);
|
||||||
|
//
|
||||||
|
|
||||||
|
template <class Log>
|
||||||
|
class LogBenchmark : public td::Benchmark {
|
||||||
|
public:
|
||||||
|
explicit LogBenchmark(std::string name, int threads_n, std::function<td::unique_ptr<Log>()> creator)
|
||||||
|
: name_(std::move(name)), threads_n_(threads_n), creator_(std::move(creator)) {
|
||||||
|
}
|
||||||
|
std::string get_description() const override {
|
||||||
|
return PSTRING() << name_ << " " << td::tag("threads_n", threads_n_);
|
||||||
|
}
|
||||||
|
void start_up() override {
|
||||||
|
log_ = creator_();
|
||||||
|
threads_.resize(threads_n_);
|
||||||
|
}
|
||||||
|
void tear_down() override {
|
||||||
|
for (auto path : log_->get_file_paths()) {
|
||||||
|
td::unlink(path).ignore();
|
||||||
|
}
|
||||||
|
log_.reset();
|
||||||
|
}
|
||||||
|
void run(int n) override {
|
||||||
|
for (auto &thread : threads_) {
|
||||||
|
thread = td::thread([this, n] { this->run_thread(n); });
|
||||||
|
}
|
||||||
|
for (auto &thread : threads_) {
|
||||||
|
thread.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void run_thread(int n) {
|
||||||
|
auto str = PSTRING() << "#" << n << " : fsjklfdjsklfjdsklfjdksl\n";
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
log_->append(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string name_;
|
||||||
|
td::unique_ptr<Log> log_;
|
||||||
|
int threads_n_{0};
|
||||||
|
std::function<td::unique_ptr<Log>()> creator_;
|
||||||
|
std::vector<td::thread> threads_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class F>
|
||||||
|
void bench_log(std::string name, int threads_n, F &&f) {
|
||||||
|
bench(LogBenchmark<typename decltype(f())::element_type>(std::move(name), threads_n, std::move(f)));
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(Log, TsLogger) {
|
||||||
|
bench_log("NewTsFileLog", 4, [] { return td::TsFileLog::create("tmplog").move_as_ok(); });
|
||||||
|
bench_log("TsFileLog", 8, [] {
|
||||||
|
class FileLog : public td::LogInterface {
|
||||||
|
public:
|
||||||
|
FileLog() {
|
||||||
|
file_log_.init("tmplog", std::numeric_limits<td::int64>::max(), false);
|
||||||
|
ts_log_.init(&file_log_);
|
||||||
|
}
|
||||||
|
~FileLog() {
|
||||||
|
}
|
||||||
|
void append(td::CSlice slice) override {
|
||||||
|
ts_log_.append(slice, -1);
|
||||||
|
}
|
||||||
|
std::vector<std::string> get_file_paths() override {
|
||||||
|
return file_log_.get_file_paths();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
td::FileLog file_log_;
|
||||||
|
td::TsLog ts_log_{nullptr};
|
||||||
|
};
|
||||||
|
return td::make_unique<FileLog>();
|
||||||
|
});
|
||||||
|
|
||||||
|
bench_log("noop", 4, [] {
|
||||||
|
class NoopLog : public td::LogInterface {
|
||||||
|
public:
|
||||||
|
void append(td::CSlice slice) override {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return td::make_unique<NoopLog>();
|
||||||
|
});
|
||||||
|
|
||||||
|
bench_log("FileLog", 4, [] {
|
||||||
|
class FileLog : public td::LogInterface {
|
||||||
|
public:
|
||||||
|
FileLog() {
|
||||||
|
file_log_.init("tmplog", std::numeric_limits<td::int64>::max(), false);
|
||||||
|
}
|
||||||
|
~FileLog() {
|
||||||
|
}
|
||||||
|
void append(td::CSlice slice) override {
|
||||||
|
file_log_.append(slice, -1);
|
||||||
|
}
|
||||||
|
std::vector<std::string> get_file_paths() override {
|
||||||
|
return file_log_.get_file_paths();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
td::FileLog file_log_;
|
||||||
|
};
|
||||||
|
return td::make_unique<FileLog>();
|
||||||
|
});
|
||||||
|
}
|
@ -895,6 +895,9 @@ static void test_hash() {
|
|||||||
test_hash<HashT, BadValue>({BadValue{1}, BadValue{2}}).ensure_error();
|
test_hash<HashT, BadValue>({BadValue{1}, BadValue{2}}).ensure_error();
|
||||||
test_hash<HashT, ValueA>({ValueA{1}, ValueA{2}}).ensure();
|
test_hash<HashT, ValueA>({ValueA{1}, ValueA{2}}).ensure();
|
||||||
test_hash<HashT, ValueB>({ValueB{1}, ValueB{2}}).ensure();
|
test_hash<HashT, ValueB>({ValueB{1}, ValueB{2}}).ensure();
|
||||||
|
test_hash<HashT, std::pair<int, int>>({{1, 1}, {1, 2}}).ensure();
|
||||||
|
// FIXME: use some better hash
|
||||||
|
//test_hash<HashT, std::pair<int, int>>({{1, 1}, {1, 2}, {2, 1}, {2, 2}}).ensure();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Misc, Hasher) {
|
TEST(Misc, Hasher) {
|
||||||
|
Loading…
Reference in New Issue
Block a user