SetWithPosition: fixes and tests
GitOrigin-RevId: 7b74a05c72261222a4f43b86eecad668ffd40403
This commit is contained in:
parent
6ca19e05c5
commit
5bba9b0470
@ -569,6 +569,7 @@ set(TDLIB_SOURCE
|
||||
td/telegram/SecureStorage.h
|
||||
td/telegram/SecureValue.h
|
||||
td/telegram/SequenceDispatcher.h
|
||||
td/telegram/SetWithPosition.h
|
||||
td/telegram/StateManager.h
|
||||
td/telegram/StickersManager.h
|
||||
td/telegram/StorageManager.h
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "td/telegram/ChannelId.h"
|
||||
#include "td/telegram/files/FileManager.h"
|
||||
#include "td/telegram/MessageId.h"
|
||||
#include "td/telegram/SetWithPosition.h"
|
||||
#include "td/telegram/UserId.h"
|
||||
|
||||
#include "td/utils/Variant.h"
|
||||
@ -22,57 +23,6 @@
|
||||
namespace td {
|
||||
|
||||
extern int VERBOSITY_NAME(file_references);
|
||||
template <class T>
|
||||
class SetWithPosition {
|
||||
public:
|
||||
void add(T value) {
|
||||
auto it = std::find(values_.begin(), values_.end(), value);
|
||||
if (it != end(values_)) {
|
||||
return;
|
||||
}
|
||||
values_.push_back(value);
|
||||
}
|
||||
void remove(T value) {
|
||||
auto it = std::find(values_.begin(), values_.end(), value);
|
||||
if (it == end(values_)) {
|
||||
return;
|
||||
}
|
||||
size_t i = it - values_.begin();
|
||||
values_.erase(it);
|
||||
if (pos_ > i) {
|
||||
pos_--;
|
||||
}
|
||||
}
|
||||
void reset_position() {
|
||||
pos_ = 0;
|
||||
}
|
||||
T next() {
|
||||
return values_[pos_++];
|
||||
}
|
||||
bool has_next() {
|
||||
return pos_ < values_.size();
|
||||
}
|
||||
void merge(SetWithPosition &&other) {
|
||||
std::vector<T> new_values_;
|
||||
for (size_t i = 0; i < pos_; i++) {
|
||||
new_values_.push_back(values_[i]);
|
||||
}
|
||||
for (size_t i = 0; i < other.pos_; i++) {
|
||||
new_values_.push_back(other.values_[i]);
|
||||
}
|
||||
for (size_t i = pos_; i < values_.size(); i++) {
|
||||
new_values_.push_back(values_[i]);
|
||||
}
|
||||
for (size_t i = other.pos_; i < other.values_.size(); i++) {
|
||||
new_values_.push_back(other.values_[i]);
|
||||
}
|
||||
pos_ += other.pos_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<T> values_;
|
||||
size_t pos_{0};
|
||||
};
|
||||
|
||||
class FileReferenceManager : public Actor {
|
||||
struct Node;
|
||||
|
62
td/telegram/SetWithPosition.h
Normal file
62
td/telegram/SetWithPosition.h
Normal file
@ -0,0 +1,62 @@
|
||||
//
|
||||
// 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
|
||||
|
||||
namespace td {
|
||||
template <class T>
|
||||
class SetWithPosition {
|
||||
public:
|
||||
void add(T value) {
|
||||
auto it = std::find(values_.begin(), values_.end(), value);
|
||||
if (it != end(values_)) {
|
||||
return;
|
||||
}
|
||||
values_.push_back(value);
|
||||
}
|
||||
void remove(T value) {
|
||||
auto it = std::find(values_.begin(), values_.end(), value);
|
||||
if (it == end(values_)) {
|
||||
return;
|
||||
}
|
||||
size_t i = it - values_.begin();
|
||||
values_.erase(it);
|
||||
if (pos_ > i) {
|
||||
pos_--;
|
||||
}
|
||||
}
|
||||
void reset_position() {
|
||||
pos_ = 0;
|
||||
}
|
||||
T next() {
|
||||
return values_[pos_++];
|
||||
}
|
||||
bool has_next() {
|
||||
return pos_ < values_.size();
|
||||
}
|
||||
void merge(SetWithPosition &&other) {
|
||||
SetWithPosition res;
|
||||
for (size_t i = 0; i < pos_; i++) {
|
||||
res.add(values_[i]);
|
||||
}
|
||||
for (size_t i = 0; i < other.pos_; i++) {
|
||||
res.add(other.values_[i]);
|
||||
}
|
||||
res.pos_ = res.values_.size();
|
||||
for (size_t i = pos_; i < values_.size(); i++) {
|
||||
res.add(values_[i]);
|
||||
}
|
||||
for (size_t i = other.pos_; i < other.values_.size(); i++) {
|
||||
res.add(other.values_[i]);
|
||||
}
|
||||
*this = std::move(res);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<T> values_;
|
||||
size_t pos_{0};
|
||||
};
|
||||
} // namespace td
|
@ -8,6 +8,7 @@ set(TD_TEST_SOURCE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/message_entities.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/secret.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/secure_storage.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/set_with_position.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/string_cleaning.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/tdclient.cpp
|
||||
|
||||
@ -29,6 +30,10 @@ target_link_libraries(all_tests PRIVATE tdactor tddb tdcore tdnet tdutils)
|
||||
|
||||
if (NOT CMAKE_CROSSCOMPILING OR EMSCRIPTEN)
|
||||
#Tests
|
||||
add_executable(set_with_position ${TESTS_MAIN} set_with_position.cpp)
|
||||
target_link_libraries(set_with_position PRIVATE tdutils)
|
||||
target_include_directories(set_with_position PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>)
|
||||
|
||||
add_executable(run_all_tests ${TESTS_MAIN} ${TD_TEST_SOURCE})
|
||||
if (CLANG AND NOT CYGWIN AND NOT EMSCRIPTEN)
|
||||
target_compile_options(run_all_tests PUBLIC -fsanitize=undefined -fno-sanitize=vptr)
|
||||
|
167
test/set_with_position.cpp
Normal file
167
test/set_with_position.cpp
Normal file
@ -0,0 +1,167 @@
|
||||
//
|
||||
// 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/Random.h"
|
||||
|
||||
#include "td/telegram/SetWithPosition.h"
|
||||
|
||||
#include <set>
|
||||
|
||||
using namespace td;
|
||||
|
||||
template <class T, template <class> class Set = SetWithPosition>
|
||||
class CheckedSetWithPosition {
|
||||
public:
|
||||
void add(int x) {
|
||||
s_.add(x);
|
||||
if (checked_.count(x) != 0) {
|
||||
return;
|
||||
}
|
||||
not_checked_.insert(x);
|
||||
}
|
||||
void remove(int x) {
|
||||
s_.remove(x);
|
||||
checked_.erase(x);
|
||||
not_checked_.erase(x);
|
||||
}
|
||||
bool has_next() {
|
||||
auto res = !not_checked_.empty();
|
||||
//LOG(ERROR) << res;
|
||||
ASSERT_EQ(res, s_.has_next());
|
||||
return res;
|
||||
}
|
||||
void reset_position() {
|
||||
s_.reset_position();
|
||||
not_checked_.insert(checked_.begin(), checked_.end());
|
||||
checked_ = {};
|
||||
}
|
||||
|
||||
T next() {
|
||||
CHECK(has_next());
|
||||
auto next = s_.next();
|
||||
//LOG(ERROR) << next;
|
||||
ASSERT_TRUE(not_checked_.count(next) != 0);
|
||||
not_checked_.erase(next);
|
||||
checked_.insert(next);
|
||||
return next;
|
||||
}
|
||||
|
||||
void merge(CheckedSetWithPosition &&other) {
|
||||
for (auto x : other.checked_) {
|
||||
not_checked_.erase(x);
|
||||
checked_.insert(x);
|
||||
}
|
||||
for (auto x : other.not_checked_) {
|
||||
if (checked_.count(x) != 0) {
|
||||
continue;
|
||||
}
|
||||
not_checked_.insert(x);
|
||||
}
|
||||
s_.merge(std::move(other.s_));
|
||||
}
|
||||
|
||||
private:
|
||||
std::set<T> checked_;
|
||||
std::set<T> not_checked_;
|
||||
Set<T> s_;
|
||||
};
|
||||
|
||||
template <template <class> class RawSet>
|
||||
void test_hands() {
|
||||
using Set = CheckedSetWithPosition<int, RawSet>;
|
||||
|
||||
Set a;
|
||||
a.add(1);
|
||||
a.add(2);
|
||||
a.next();
|
||||
Set b;
|
||||
b.add(1);
|
||||
b.add(3);
|
||||
|
||||
a.merge(std::move(b));
|
||||
while (a.has_next()) {
|
||||
a.next();
|
||||
}
|
||||
}
|
||||
template <template <class> class RawSet>
|
||||
void test_stress() {
|
||||
Random::Xorshift128plus rnd(123);
|
||||
using Set = CheckedSetWithPosition<int, RawSet>;
|
||||
for (int t = 0; t < 100; t++) {
|
||||
std::vector<unique_ptr<Set>> sets(1000);
|
||||
for (auto &s : sets) {
|
||||
s = make_unique<Set>();
|
||||
}
|
||||
int n;
|
||||
auto merge = [&] {
|
||||
int a = rnd.fast(0, n - 2);
|
||||
int b = rnd.fast(a + 1, n - 1);
|
||||
std::swap(sets[b], sets[n - 1]);
|
||||
std::swap(sets[a], sets[n - 2]);
|
||||
a = n - 2;
|
||||
b = n - 1;
|
||||
if (rnd.fast(0, 1) == 0) {
|
||||
std::swap(sets[a], sets[b]);
|
||||
}
|
||||
sets[a]->merge(std::move(*sets[b]));
|
||||
sets.pop_back();
|
||||
};
|
||||
auto next = [&] {
|
||||
int i = rnd.fast(0, n - 1);
|
||||
if (sets[i]->has_next()) {
|
||||
sets[i]->next();
|
||||
}
|
||||
};
|
||||
auto add = [&] {
|
||||
int i = rnd.fast(0, n - 1);
|
||||
int x = rnd.fast(0, 10);
|
||||
sets[i]->add(x);
|
||||
};
|
||||
auto remove = [&] {
|
||||
int i = rnd.fast(0, n - 1);
|
||||
int x = rnd.fast(0, 10);
|
||||
sets[i]->remove(x);
|
||||
};
|
||||
auto reset_position = [&] {
|
||||
int i = rnd.fast(0, n - 1);
|
||||
sets[i]->reset_position();
|
||||
};
|
||||
struct Step {
|
||||
std::function<void()> func;
|
||||
td::uint32 weight;
|
||||
};
|
||||
std::vector<Step> steps{{merge, 1}, {next, 10}, {add, 10}, {remove, 10}, {reset_position, 5}};
|
||||
td::uint32 steps_sum = 0;
|
||||
for (auto &step : steps) {
|
||||
steps_sum += step.weight;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
n = static_cast<int>(sets.size());
|
||||
if (n == 1) {
|
||||
break;
|
||||
}
|
||||
auto w = rnd() % steps_sum;
|
||||
for (auto &step : steps) {
|
||||
if (w < step.weight) {
|
||||
step.func();
|
||||
break;
|
||||
}
|
||||
w -= step.weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(SetWithPosition, hands) {
|
||||
//test_hands<SetWithPosition>();
|
||||
test_hands<SetWithPosition>();
|
||||
}
|
||||
TEST(SetWithPosition, stress) {
|
||||
//test_stress<SetWithPosition>();
|
||||
test_stress<SetWithPosition>();
|
||||
}
|
Reference in New Issue
Block a user