Move unused datastructures to poc
Reviewers: buda Reviewed By: buda Subscribers: pullbot Differential Revision: https://phabricator.memgraph.io/D526
This commit is contained in:
parent
88153595ce
commit
0588de76bb
@ -26,5 +26,9 @@ foreach(poc_cpp ${poc_cpps})
|
||||
|
||||
# link libraries
|
||||
target_link_libraries(${target_name} memgraph_lib)
|
||||
# gtest
|
||||
target_link_libraries(${target_name} gtest gtest_main gmock)
|
||||
# google-benchmark
|
||||
target_link_libraries(${target_name} benchmark ${CMAKE_THREAD_LIBS_INIT})
|
||||
|
||||
endforeach()
|
||||
|
@ -3,10 +3,10 @@
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "memory/allocator.hpp"
|
||||
#include "memory/maker.hpp"
|
||||
#include "utils/assert.hpp"
|
||||
#include "utils/measure_time.hpp"
|
||||
#include "utils/memory/allocator.hpp"
|
||||
#include "utils/memory/maker.hpp"
|
||||
|
||||
struct TestStructure {
|
||||
TestStructure(int a, int b, int c, int d) : a(a), b(b), c(c), d(d) {}
|
@ -1,6 +1,6 @@
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "utils/memory/allocator.hpp"
|
||||
#include "memory/allocator.hpp"
|
||||
|
||||
TEST(AllocatorTest, ABlockOfIntegersCanBeAllocated) {
|
||||
constexpr int N = 100;
|
@ -2,7 +2,7 @@
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "data_structures/bloom/bloom_filter.hpp"
|
||||
#include "bloom_filter.hpp"
|
||||
#include "utils/hashing/fnv64.hpp"
|
||||
|
||||
using StringHashFunction = std::function<uint64_t(const std::string &)>;
|
@ -4,7 +4,7 @@
|
||||
#include <benchmark/benchmark_api.h>
|
||||
#include <glog/logging.h>
|
||||
|
||||
#include "data_structures/bloom/bloom_filter.hpp"
|
||||
#include "bloom_filter.hpp"
|
||||
#include "utils/hashing/fnv64.hpp"
|
||||
#include "utils/random/random_generator.hpp"
|
||||
|
@ -1,6 +1,6 @@
|
||||
#include "gtest/gtest.h"
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "utils/memory/block_allocator.hpp"
|
||||
#include "memory/block_allocator.hpp"
|
||||
|
||||
TEST(BlockAllocatorTest, UnusedVsReleaseSize) {
|
||||
BlockAllocator<64> block_allocator(10);
|
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <bitset>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
@ -5,8 +5,8 @@
|
||||
#include <gflags/gflags.h>
|
||||
#include <glog/logging.h>
|
||||
|
||||
#include "data_structures/bloom/bloom_filter.hpp"
|
||||
#include "data_structures/concurrent/concurrent_bloom_map.hpp"
|
||||
#include "bloom_filter.hpp"
|
||||
#include "concurrent_bloom_map.hpp"
|
||||
#include "utils/hashing/fnv64.hpp"
|
||||
#include "utils/random/random_generator.hpp"
|
||||
|
@ -50,7 +50,8 @@ class HP {
|
||||
hp.clear(*this);
|
||||
}
|
||||
|
||||
reference& operator=(reference&& other) { return *this; }
|
||||
// TODO: ???
|
||||
reference& operator=(reference&&) { return *this; }
|
||||
|
||||
private:
|
||||
reference(int64_t idx) : idx(idx) {}
|
@ -3,6 +3,7 @@
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
|
||||
// I heard this is patented.
|
||||
template <class T>
|
||||
class atomic_shared_ptr final {
|
||||
public:
|
@ -2,9 +2,9 @@
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "memory/block_allocator.hpp"
|
||||
#include "utils/exceptions.hpp"
|
||||
#include "utils/likely.hpp"
|
||||
#include "utils/memory/block_allocator.hpp"
|
||||
|
||||
// http://en.cppreference.com/w/cpp/language/new
|
||||
|
51
poc/ptr_int.hpp
Normal file
51
poc/ptr_int.hpp
Normal file
@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include <cinttypes>
|
||||
|
||||
constexpr std::size_t log2(std::size_t n) {
|
||||
return ((n < 2) ? 0 : 1 + log2(n >> 1));
|
||||
}
|
||||
|
||||
template <typename PtrT>
|
||||
struct PointerPackTraits {
|
||||
// here is a place to embed something like platform specific things
|
||||
// TODO: cover more cases
|
||||
constexpr static int free_bits = log2(alignof(PtrT));
|
||||
|
||||
static auto get_ptr(uintptr_t value) { return (PtrT)(value); }
|
||||
};
|
||||
|
||||
template <typename PtrT, int IntBits, typename IntT = unsigned,
|
||||
typename PtrTraits = PointerPackTraits<PtrT>>
|
||||
class PtrInt {
|
||||
private:
|
||||
constexpr static int int_shift = PtrTraits::free_bits - IntBits;
|
||||
constexpr static uintptr_t ptr_mask =
|
||||
~(uintptr_t)(((intptr_t)1 << PtrTraits::free_bits) - 1);
|
||||
constexpr static uintptr_t int_mask =
|
||||
(uintptr_t)(((intptr_t)1 << IntBits) - 1);
|
||||
|
||||
uintptr_t value{0};
|
||||
|
||||
public:
|
||||
PtrInt(PtrT pointer, IntT integer) {
|
||||
set_ptr(pointer);
|
||||
set_int(integer);
|
||||
}
|
||||
|
||||
auto set_ptr(PtrT pointer) {
|
||||
auto integer = static_cast<uintptr_t>(get_int());
|
||||
auto ptr = reinterpret_cast<uintptr_t>(pointer);
|
||||
value = (ptr_mask & ptr) | (integer << int_shift);
|
||||
}
|
||||
|
||||
auto set_int(IntT integer) {
|
||||
auto ptr = reinterpret_cast<uintptr_t>(get_ptr());
|
||||
auto int_shifted = static_cast<uintptr_t>(integer << int_shift);
|
||||
value = (int_mask & int_shifted) | ptr;
|
||||
}
|
||||
|
||||
auto get_ptr() const { return PtrTraits::get_ptr(value & ptr_mask); }
|
||||
|
||||
auto get_int() const { return (IntT)((value >> int_shift) & int_mask); }
|
||||
};
|
26
poc/ptr_int_test.cpp
Normal file
26
poc/ptr_int_test.cpp
Normal file
@ -0,0 +1,26 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "ptr_int.hpp"
|
||||
|
||||
TEST(PtrInt, SizeOf) {
|
||||
ASSERT_EQ(sizeof(PtrInt<int *, 1, int>), sizeof(uintptr_t));
|
||||
}
|
||||
|
||||
TEST(PtrInt, ConstructionAndRead) {
|
||||
auto ptr1 = std::make_unique<int>(2);
|
||||
PtrInt<int *, 2, int> pack1(ptr1.get(), 1);
|
||||
|
||||
ASSERT_EQ(pack1.get_int(), 1);
|
||||
ASSERT_EQ(pack1.get_ptr(), ptr1.get());
|
||||
|
||||
auto ptr2 = std::make_unique<int>(2);
|
||||
PtrInt<int *, 3, int> pack2(ptr2.get(), 4);
|
||||
|
||||
ASSERT_EQ(pack2.get_int(), 4);
|
||||
ASSERT_EQ(pack2.get_ptr(), ptr2.get());
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
@ -1,9 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
|
||||
#include "option_ptr.hpp"
|
||||
#include "utils/assert.hpp"
|
||||
#include "utils/crtp.hpp"
|
||||
#include "utils/option_ptr.hpp"
|
||||
|
||||
// RobinHood base.
|
||||
// Entries are POINTERS alligned to 8B.
|
||||
@ -286,3 +288,174 @@ class RhBase {
|
||||
friend class IteratorBase<Iterator>;
|
||||
friend class IteratorBase<ConstIterator>;
|
||||
};
|
||||
|
||||
/**
|
||||
* HashMap with RobinHood collision resolution policy.
|
||||
* Single threaded.
|
||||
* Entries are saved as pointers alligned to 8B.
|
||||
* Entries must know thers key.
|
||||
* D must have method const K & get_key()
|
||||
* K must be comparable with ==.
|
||||
* HashMap behaves as if it isn't owner of entries.
|
||||
* BE CAREFUL - this structure assumes that the pointer to Data is 8-alligned!
|
||||
*/
|
||||
template <class K, class D, size_t init_size_pow2 = 2>
|
||||
class RhHashMap : public RhBase<K, D, init_size_pow2> {
|
||||
typedef RhBase<K, D, init_size_pow2> base;
|
||||
using base::array;
|
||||
using base::index;
|
||||
using base::capacity;
|
||||
using base::count;
|
||||
using typename base::Combined;
|
||||
|
||||
void increase_size() {
|
||||
size_t old_size = capacity;
|
||||
auto a = array;
|
||||
if (base::increase_size()) {
|
||||
for (int i = 0; i < old_size; i++) {
|
||||
if (a[i].valid()) {
|
||||
insert(a[i].ptr());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(a);
|
||||
}
|
||||
|
||||
public:
|
||||
using base::RhBase;
|
||||
|
||||
bool contains(const K &key) { return find(key).is_present(); }
|
||||
|
||||
OptionPtr<D> find(const K key) {
|
||||
size_t mask = this->mask();
|
||||
size_t now = index(key, mask);
|
||||
size_t off = 0;
|
||||
size_t border = 8 <= capacity ? 8 : capacity;
|
||||
|
||||
while (off < border) {
|
||||
Combined other = array[now];
|
||||
if (other.valid()) {
|
||||
auto other_off = other.off();
|
||||
if (other_off == off && key == other.ptr()->get_key()) {
|
||||
// Found data.
|
||||
return OptionPtr<D>(other.ptr());
|
||||
|
||||
} else if (other_off < off) { // Other is rich
|
||||
break;
|
||||
} // Else other has equal or greater offset, so he is poor.
|
||||
} else {
|
||||
// Empty slot means that there is no searched data.
|
||||
break;
|
||||
}
|
||||
|
||||
off++;
|
||||
now = (now + 1) & mask;
|
||||
}
|
||||
return OptionPtr<D>();
|
||||
}
|
||||
|
||||
// Inserts element. Returns true if element wasn't in the map.
|
||||
bool insert(D *data) {
|
||||
permanent_assert(!(((uint64_t) static_cast<void *>(data) & 7)),
|
||||
"Data is not 8-alligned.");
|
||||
if (count < capacity) {
|
||||
size_t mask = this->mask();
|
||||
auto key = std::ref(data->get_key());
|
||||
size_t now = index(key, mask);
|
||||
size_t off = 0;
|
||||
size_t border = 8 <= capacity ? 8 : capacity;
|
||||
|
||||
while (off < border) {
|
||||
Combined other = array[now];
|
||||
if (other.valid()) {
|
||||
auto other_off = other.off();
|
||||
if (other_off == off && key == other.ptr()->get_key()) {
|
||||
// Element already exists.
|
||||
return false;
|
||||
|
||||
} else if (other_off < off) { // Other is rich
|
||||
// Set data.
|
||||
array[now] = Combined(data, off);
|
||||
|
||||
// Move other data to the higher indexes,
|
||||
while (other.increment_off()) {
|
||||
now = (now + 1) & mask;
|
||||
auto tmp = array[now];
|
||||
array[now] = other;
|
||||
other = tmp;
|
||||
if (!other.valid()) {
|
||||
count++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
data = other.ptr();
|
||||
break; // Cant insert removed element because it would
|
||||
// be to far from his real place.
|
||||
} // Else other has equal or greater offset, so he is poor.
|
||||
} else {
|
||||
// Data can be placed in this empty slot.
|
||||
array[now] = Combined(data, off);
|
||||
count++;
|
||||
return true;
|
||||
}
|
||||
|
||||
off++;
|
||||
now = (now + 1) & mask;
|
||||
}
|
||||
}
|
||||
|
||||
// There isn't enough space for element pointed by data so whe must
|
||||
// increase array.
|
||||
increase_size();
|
||||
return insert(data);
|
||||
}
|
||||
|
||||
// Removes element. Returns removed element if it existed.
|
||||
OptionPtr<D> remove(const K &key) {
|
||||
size_t mask = this->mask();
|
||||
size_t now = index(key, mask);
|
||||
size_t off = 0;
|
||||
size_t border = 8 <= capacity ? 8 : capacity;
|
||||
|
||||
while (off < border) {
|
||||
Combined other = array[now];
|
||||
if (other.valid()) {
|
||||
auto other_off = other.off();
|
||||
auto other_ptr = other.ptr();
|
||||
if (other_off == off && key == other_ptr->get_key()) { // Found it
|
||||
|
||||
auto before = now;
|
||||
// Whe must move other elements one slot lower.
|
||||
do {
|
||||
// This is alright even for off=0 on found element
|
||||
// because it wont be seen.
|
||||
other.decrement_off_unsafe();
|
||||
|
||||
array[before] = other;
|
||||
before = now;
|
||||
now = (now + 1) & mask;
|
||||
other = array[now];
|
||||
} while (other.valid() &&
|
||||
other.off() > 0); // Exit if whe encounter empty
|
||||
// slot or data which is exactly
|
||||
// in slot which it want's to be.
|
||||
|
||||
array[before] = Combined();
|
||||
count--;
|
||||
return OptionPtr<D>(other_ptr);
|
||||
|
||||
} else if (other_off < off) { // Other is rich
|
||||
break;
|
||||
} // Else other has equal or greater offset, so he is poor.
|
||||
} else {
|
||||
// If the element to be removed existed in map it would be here.
|
||||
break;
|
||||
}
|
||||
|
||||
off++;
|
||||
now = (now + 1) & mask;
|
||||
}
|
||||
return OptionPtr<D>();
|
||||
}
|
||||
};
|
@ -1,7 +1,7 @@
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <memory>
|
||||
#include "data_structures/map/rh_hashmap.hpp"
|
||||
#include "rh_hashmap.hpp"
|
||||
|
||||
class Data {
|
||||
private:
|
@ -1,6 +1,6 @@
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "utils/memory/stack_allocator.hpp"
|
||||
#include "memory/stack_allocator.hpp"
|
||||
|
||||
struct Object {
|
||||
int a;
|
@ -3,9 +3,9 @@
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "data_structures/union_find/union_find.hpp"
|
||||
#include "union_find.hpp"
|
||||
|
||||
void _expect_fully(UnionFind<> &uf, bool connected, int from = 0, int to = -1) {
|
||||
void ExpectFully(UnionFind<> &uf, bool connected, int from = 0, int to = -1) {
|
||||
if (to == -1) to = uf.size();
|
||||
|
||||
for (int i = from; i < to; i++)
|
||||
@ -42,27 +42,27 @@ TEST(UnionFindTest, ModifiedSizeTest) {
|
||||
|
||||
TEST(UnionFindTest, Disconectivity) {
|
||||
UnionFind<> uf(10);
|
||||
_expect_fully(uf, false);
|
||||
ExpectFully(uf, false);
|
||||
}
|
||||
|
||||
TEST(UnionFindTest, ConnectivityAlongChain) {
|
||||
UnionFind<> uf(10);
|
||||
for (unsigned int i = 1; i < uf.size(); i++) uf.connect(i - 1, i);
|
||||
_expect_fully(uf, true);
|
||||
ExpectFully(uf, true);
|
||||
}
|
||||
|
||||
TEST(UnionFindTest, ConnectivityOnTree) {
|
||||
UnionFind<> uf(10);
|
||||
_expect_fully(uf, false);
|
||||
ExpectFully(uf, false);
|
||||
|
||||
uf.connect(0, 1);
|
||||
uf.connect(0, 2);
|
||||
_expect_fully(uf, true, 0, 3);
|
||||
_expect_fully(uf, false, 2);
|
||||
ExpectFully(uf, true, 0, 3);
|
||||
ExpectFully(uf, false, 2);
|
||||
|
||||
uf.connect(2, 3);
|
||||
_expect_fully(uf, true, 0, 4);
|
||||
_expect_fully(uf, false, 3);
|
||||
ExpectFully(uf, true, 0, 4);
|
||||
ExpectFully(uf, false, 3);
|
||||
}
|
||||
|
||||
TEST(UnionFindTest, DisjointChains) {
|
@ -1,177 +0,0 @@
|
||||
#include <functional>
|
||||
|
||||
#include "data_structures/map/rh_common.hpp"
|
||||
#include "utils/assert.hpp"
|
||||
#include "utils/crtp.hpp"
|
||||
#include "utils/option_ptr.hpp"
|
||||
|
||||
/**
|
||||
* HashMap with RobinHood collision resolution policy.
|
||||
* Single threaded.
|
||||
* Entries are saved as pointers alligned to 8B.
|
||||
* Entries must know thers key.
|
||||
* D must have method const K & get_key()
|
||||
* K must be comparable with ==.
|
||||
* HashMap behaves as if it isn't owner of entries.
|
||||
* BE CAREFUL - this structure assumes that the pointer to Data is 8-alligned!
|
||||
*/
|
||||
template <class K, class D, size_t init_size_pow2 = 2>
|
||||
class RhHashMap : public RhBase<K, D, init_size_pow2> {
|
||||
typedef RhBase<K, D, init_size_pow2> base;
|
||||
using base::array;
|
||||
using base::index;
|
||||
using base::capacity;
|
||||
using base::count;
|
||||
using typename base::Combined;
|
||||
|
||||
void increase_size() {
|
||||
size_t old_size = capacity;
|
||||
auto a = array;
|
||||
if (base::increase_size()) {
|
||||
for (int i = 0; i < old_size; i++) {
|
||||
if (a[i].valid()) {
|
||||
insert(a[i].ptr());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(a);
|
||||
}
|
||||
|
||||
public:
|
||||
using base::RhBase;
|
||||
|
||||
bool contains(const K &key) { return find(key).is_present(); }
|
||||
|
||||
OptionPtr<D> find(const K key) {
|
||||
size_t mask = this->mask();
|
||||
size_t now = index(key, mask);
|
||||
size_t off = 0;
|
||||
size_t border = 8 <= capacity ? 8 : capacity;
|
||||
|
||||
while (off < border) {
|
||||
Combined other = array[now];
|
||||
if (other.valid()) {
|
||||
auto other_off = other.off();
|
||||
if (other_off == off && key == other.ptr()->get_key()) {
|
||||
// Found data.
|
||||
return OptionPtr<D>(other.ptr());
|
||||
|
||||
} else if (other_off < off) { // Other is rich
|
||||
break;
|
||||
} // Else other has equal or greater offset, so he is poor.
|
||||
} else {
|
||||
// Empty slot means that there is no searched data.
|
||||
break;
|
||||
}
|
||||
|
||||
off++;
|
||||
now = (now + 1) & mask;
|
||||
}
|
||||
return OptionPtr<D>();
|
||||
}
|
||||
|
||||
// Inserts element. Returns true if element wasn't in the map.
|
||||
bool insert(D *data) {
|
||||
permanent_assert(!(((uint64_t) static_cast<void *>(data) & 7)),
|
||||
"Data is not 8-alligned.");
|
||||
if (count < capacity) {
|
||||
size_t mask = this->mask();
|
||||
auto key = std::ref(data->get_key());
|
||||
size_t now = index(key, mask);
|
||||
size_t off = 0;
|
||||
size_t border = 8 <= capacity ? 8 : capacity;
|
||||
|
||||
while (off < border) {
|
||||
Combined other = array[now];
|
||||
if (other.valid()) {
|
||||
auto other_off = other.off();
|
||||
if (other_off == off && key == other.ptr()->get_key()) {
|
||||
// Element already exists.
|
||||
return false;
|
||||
|
||||
} else if (other_off < off) { // Other is rich
|
||||
// Set data.
|
||||
array[now] = Combined(data, off);
|
||||
|
||||
// Move other data to the higher indexes,
|
||||
while (other.increment_off()) {
|
||||
now = (now + 1) & mask;
|
||||
auto tmp = array[now];
|
||||
array[now] = other;
|
||||
other = tmp;
|
||||
if (!other.valid()) {
|
||||
count++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
data = other.ptr();
|
||||
break; // Cant insert removed element because it would
|
||||
// be to far from his real place.
|
||||
} // Else other has equal or greater offset, so he is poor.
|
||||
} else {
|
||||
// Data can be placed in this empty slot.
|
||||
array[now] = Combined(data, off);
|
||||
count++;
|
||||
return true;
|
||||
}
|
||||
|
||||
off++;
|
||||
now = (now + 1) & mask;
|
||||
}
|
||||
}
|
||||
|
||||
// There isn't enough space for element pointed by data so whe must
|
||||
// increase array.
|
||||
increase_size();
|
||||
return insert(data);
|
||||
}
|
||||
|
||||
// Removes element. Returns removed element if it existed.
|
||||
OptionPtr<D> remove(const K &key) {
|
||||
size_t mask = this->mask();
|
||||
size_t now = index(key, mask);
|
||||
size_t off = 0;
|
||||
size_t border = 8 <= capacity ? 8 : capacity;
|
||||
|
||||
while (off < border) {
|
||||
Combined other = array[now];
|
||||
if (other.valid()) {
|
||||
auto other_off = other.off();
|
||||
auto other_ptr = other.ptr();
|
||||
if (other_off == off && key == other_ptr->get_key()) { // Found it
|
||||
|
||||
auto before = now;
|
||||
// Whe must move other elements one slot lower.
|
||||
do {
|
||||
// This is alright even for off=0 on found element
|
||||
// because it wont be seen.
|
||||
other.decrement_off_unsafe();
|
||||
|
||||
array[before] = other;
|
||||
before = now;
|
||||
now = (now + 1) & mask;
|
||||
other = array[now];
|
||||
} while (other.valid() &&
|
||||
other.off() > 0); // Exit if whe encounter empty
|
||||
// slot or data which is exactly
|
||||
// in slot which it want's to be.
|
||||
|
||||
array[before] = Combined();
|
||||
count--;
|
||||
return OptionPtr<D>(other_ptr);
|
||||
|
||||
} else if (other_off < off) { // Other is rich
|
||||
break;
|
||||
} // Else other has equal or greater offset, so he is poor.
|
||||
} else {
|
||||
// If the element to be removed existed in map it would be here.
|
||||
break;
|
||||
}
|
||||
|
||||
off++;
|
||||
now = (now + 1) & mask;
|
||||
}
|
||||
return OptionPtr<D>();
|
||||
}
|
||||
};
|
@ -1,58 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "utils/assert.hpp"
|
||||
|
||||
// data structure namespace short ds
|
||||
// TODO: document strategy related to namespace naming
|
||||
// (namespace names should be short but eazy to memorize)
|
||||
namespace ds {
|
||||
|
||||
// static array is data structure which size (capacity) can be known at compile
|
||||
// time
|
||||
// this data structure isn't concurrent
|
||||
template <typename T, size_t N>
|
||||
class static_array {
|
||||
public:
|
||||
// default constructor
|
||||
static_array() {}
|
||||
|
||||
// explicit constructor which populates the data array with
|
||||
// initial values, array structure after initialization
|
||||
// is N * [initial_value]
|
||||
explicit static_array(const T &initial_value) {
|
||||
for (size_t i = 0; i < size(); ++i) {
|
||||
data[i] = initial_value;
|
||||
}
|
||||
}
|
||||
|
||||
// returns array size
|
||||
size_t size() const { return N; }
|
||||
|
||||
// returns element reference on specific index
|
||||
T &operator[](size_t index) {
|
||||
debug_assert(index < N, "Index " << index << " must be less than " << N);
|
||||
return data[index];
|
||||
}
|
||||
|
||||
// returns const element reference on specific index
|
||||
const T &operator[](size_t index) const {
|
||||
debug_assert(index < N, "Index " << index << " must be less than " << N);
|
||||
return data[index];
|
||||
}
|
||||
|
||||
// returns begin iterator
|
||||
T *begin() { return &data[0]; }
|
||||
|
||||
// returns const begin iterator
|
||||
const T *begin() const { return &data[0]; }
|
||||
|
||||
// returns end iterator
|
||||
T *end() { return &data[N]; }
|
||||
|
||||
// returns const end iterator
|
||||
const T *end() const { return &data[N]; }
|
||||
|
||||
private:
|
||||
T data[N];
|
||||
};
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
#include "dbms/cleaner.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
#include <thread>
|
||||
|
||||
#include "database/db_transaction.hpp"
|
||||
#include "threading/thread.hpp"
|
||||
|
||||
#include "logging/default.hpp"
|
||||
|
||||
Cleaning::Cleaning(ConcurrentMap<std::string, GraphDb> &dbs,
|
||||
size_t cleaning_cycle)
|
||||
: dbms(dbs), cleaning_cycle(cleaning_cycle) {
|
||||
// Start the cleaning thread
|
||||
cleaners.push_back(
|
||||
std::make_unique<Thread>([&, cleaning_cycle = cleaning_cycle ]() {
|
||||
Logger logger = logging::log->logger("Cleaner");
|
||||
logger.info("Started with cleaning cycle of {} sec", cleaning_cycle);
|
||||
|
||||
std::time_t last_clean = std::time(nullptr);
|
||||
while (cleaning.load(std::memory_order_acquire)) {
|
||||
std::time_t now = std::time(nullptr);
|
||||
|
||||
// Maybe it's cleaning time.
|
||||
if (now >= last_clean + cleaning_cycle) {
|
||||
logger.info("Started cleaning cyle");
|
||||
|
||||
// Clean all databases
|
||||
for (auto &db : dbs.access()) {
|
||||
logger.info("Cleaning database \"{}\"", db.first);
|
||||
DbTransaction t(db.second);
|
||||
try {
|
||||
logger.info("Cleaning edges");
|
||||
t.clean_edge_section();
|
||||
|
||||
logger.info("Cleaning vertices");
|
||||
t.clean_vertex_section();
|
||||
|
||||
logger.info("Cleaning garbage");
|
||||
db.second.garbage.clean();
|
||||
|
||||
} catch (const std::exception &e) {
|
||||
logger.error("Error occured while cleaning database \"{}\"",
|
||||
db.first);
|
||||
logger.error("{}", e.what());
|
||||
}
|
||||
// NOTE: Whe should commit even if error occured.
|
||||
t.trans.commit();
|
||||
}
|
||||
last_clean = now;
|
||||
logger.info("Finished cleaning cyle");
|
||||
|
||||
} else {
|
||||
// Cleaning isn't scheduled for now so i should sleep.
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
Cleaning::~Cleaning() {
|
||||
// Stop cleaning
|
||||
cleaning.store(false, std::memory_order_release);
|
||||
for (auto &t : cleaners) {
|
||||
// Join with cleaners
|
||||
t.get()->join();
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "database/graph_db.hpp"
|
||||
#include "threading/thread.hpp"
|
||||
|
||||
class Thread;
|
||||
|
||||
class Cleaning {
|
||||
public:
|
||||
// How much sec is a cleaning_cycle in which cleaner will clean at most
|
||||
// once. Starts cleaner thread.
|
||||
Cleaning(ConcurrentMap<std::string, GraphDb> &dbs, size_t cleaning_cycle);
|
||||
|
||||
// Destroys this object after this thread joins cleaning thread.
|
||||
~Cleaning();
|
||||
|
||||
private:
|
||||
ConcurrentMap<std::string, GraphDb> &dbms;
|
||||
|
||||
const size_t cleaning_cycle;
|
||||
|
||||
std::vector<std::unique_ptr<Thread>> cleaners;
|
||||
|
||||
// Should i continue cleaning.
|
||||
std::atomic<bool> cleaning = {true};
|
||||
};
|
@ -57,13 +57,4 @@ class Dbms {
|
||||
|
||||
// currently active database
|
||||
std::atomic<GraphDb *> active_db;
|
||||
|
||||
// // Cleaning thread.
|
||||
// TODO re-enable cleaning
|
||||
// Cleaning cleaning = {dbs, CONFIG_INTEGER(config::CLEANING_CYCLE_SEC)};
|
||||
//
|
||||
// // Snapshoting thread.
|
||||
// TODO re-enable cleaning
|
||||
// Snapshoter snapshoter = {dbs,
|
||||
// CONFIG_INTEGER(config::SNAPSHOT_CYCLE_SEC)};
|
||||
};
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
#include "io/network/stream_buffer.hpp"
|
||||
#include "io/network/stream_listener.hpp"
|
||||
#include "memory/literals.hpp"
|
||||
#include "utils/memory_literals.hpp"
|
||||
|
||||
namespace io::network {
|
||||
|
||||
|
@ -1,25 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "recycler.hpp"
|
||||
|
||||
template <class T, class Allocator>
|
||||
class DeferredRecycler : Recycler<T, Allocator> {
|
||||
public:
|
||||
using Recycler<T, Allocator>::acquire;
|
||||
|
||||
void recycle(T* item) {
|
||||
auto guard = this->acquire_unique();
|
||||
dirty.push_back(item);
|
||||
}
|
||||
|
||||
void clean() {
|
||||
auto guard = this->acquire_unique();
|
||||
|
||||
for (auto item : dirty) this->recycle_or_delete(item);
|
||||
|
||||
dirty.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
std::queue<T*> dirty;
|
||||
};
|
@ -1,22 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "threading/sync/lockable.hpp"
|
||||
#include "threading/sync/spinlock.hpp"
|
||||
|
||||
template <class T, class lock_t = SpinLock>
|
||||
class FreeList : Lockable<lock_t> {
|
||||
public:
|
||||
void swap(std::vector<T> &dst) { std::swap(data, dst); }
|
||||
|
||||
void add(T element) {
|
||||
auto lock = this->acquire_unique();
|
||||
data.emplace_back(element);
|
||||
}
|
||||
|
||||
size_t size() const { return data.size(); }
|
||||
|
||||
private:
|
||||
std::vector<T> data;
|
||||
};
|
@ -1,24 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// TODO: remove from here and from the project
|
||||
#include <atomic>
|
||||
#include <iostream>
|
||||
|
||||
#include "threading/sync/lockable.hpp"
|
||||
#include "utils/crtp.hpp"
|
||||
|
||||
template <class Derived, class lock_t = SpinLock>
|
||||
class LazyGC : public Crtp<Derived>, public Lockable<lock_t> {
|
||||
public:
|
||||
// AddRef method should be called by a thread
|
||||
// when the thread has to do something over
|
||||
// object which has to be lazy cleaned when
|
||||
// the thread finish it job
|
||||
void AddRef() {
|
||||
auto lock = this->acquire_unique();
|
||||
++reference_count_;
|
||||
}
|
||||
|
||||
protected:
|
||||
size_t reference_count_{0};
|
||||
};
|
@ -1,41 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
|
||||
#include "storage/model/edge.hpp"
|
||||
#include "storage/model/record.hpp"
|
||||
#include "storage/model/vertex.hpp"
|
||||
|
||||
// TODO implement the memory engine using the allocator style allocation to
|
||||
// make this class non-dependent on the memory allocation strategy
|
||||
|
||||
// TODO implement real recycling of vertices and edges to improve performance
|
||||
class MemoryEngine {
|
||||
public:
|
||||
template <class T, typename... Args>
|
||||
T* create(Args&&... args) {
|
||||
return new T(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T* allocate() {
|
||||
return static_cast<T*>(malloc(sizeof(T)));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void recycle(Record<T>* record) {
|
||||
recycle(&record->derived());
|
||||
}
|
||||
|
||||
void recycle(Vertex* v) { delete v; }
|
||||
|
||||
void recycle(Edge* e) { delete e; }
|
||||
|
||||
private:
|
||||
std::unique_lock<SpinLock> acquire() {
|
||||
return std::unique_lock<SpinLock>(lock);
|
||||
}
|
||||
|
||||
SpinLock lock;
|
||||
};
|
@ -1,41 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
|
||||
#include "threading/sync/lockable.hpp"
|
||||
#include "threading/sync/spinlock.hpp"
|
||||
|
||||
template <class T, class Allocator = std::allocator<T>>
|
||||
class Recycler : public Lockable<SpinLock> {
|
||||
static constexpr size_t default_max_reserved = 100;
|
||||
|
||||
public:
|
||||
Recycler() = default;
|
||||
Recycler(size_t max_reserved) : max_reserved(max_reserved) {}
|
||||
|
||||
template <class... Args>
|
||||
T* acquire(Args&&... args) {
|
||||
auto guard = acquire_unique();
|
||||
return fetch_or_create(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
void release(T* item) {
|
||||
auto guard = acquire_unique();
|
||||
return recycle_or_delete(item);
|
||||
}
|
||||
|
||||
protected:
|
||||
Allocator alloc;
|
||||
size_t max_reserved{default_max_reserved};
|
||||
std::queue<T*> items;
|
||||
|
||||
template <class... Args>
|
||||
T* fetch_or_create(Args&&... args) {
|
||||
return new T(std::forward<Args>(args)...); // todo refactor :D
|
||||
}
|
||||
|
||||
void recycle_or_delete(T* item) {
|
||||
delete item; // todo refactor :D
|
||||
}
|
||||
};
|
@ -2,7 +2,6 @@
|
||||
|
||||
#include <shared_mutex>
|
||||
|
||||
#include "memory/lazy_gc.hpp"
|
||||
#include "storage/locking/record_lock.hpp"
|
||||
#include "threading/sync/lockable.hpp"
|
||||
#include "transactions/transaction.hpp"
|
||||
|
@ -1,32 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <future>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
|
||||
#include "threading/pool.hpp"
|
||||
|
||||
/**
|
||||
* @brief - Singleton class which implements thread pool.
|
||||
*/
|
||||
class GlobalPool {
|
||||
public:
|
||||
// Guaranteed by the C++11 standard to be thread-safe.
|
||||
static GlobalPool *getSingletonInstance() {
|
||||
static GlobalPool instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
void run(Pool::task_t f) { thread_pool_.run(f); }
|
||||
|
||||
GlobalPool(const GlobalPool &) = delete;
|
||||
GlobalPool(const GlobalPool &&) = delete;
|
||||
GlobalPool operator=(const GlobalPool &) = delete;
|
||||
GlobalPool operator=(const GlobalPool &&) = delete;
|
||||
|
||||
private:
|
||||
GlobalPool() {}
|
||||
Pool thread_pool_;
|
||||
};
|
@ -24,11 +24,7 @@ namespace traversal_template {
|
||||
* it is mentioned that Vertex uniqueness can result in exponential performance
|
||||
* degradation. Figure out why and how.
|
||||
*/
|
||||
enum class Uniqueness {
|
||||
None,
|
||||
Vertex,
|
||||
Edge
|
||||
};
|
||||
enum class Uniqueness { None, Vertex, Edge };
|
||||
|
||||
/**
|
||||
* Indicates how a path should be expanded using the traversal API. For the
|
||||
@ -47,14 +43,13 @@ enum class Uniqueness {
|
||||
*
|
||||
* Note that this implies that a Path has direction (start and finish).
|
||||
*/
|
||||
enum class Expansion {
|
||||
Front,
|
||||
Back
|
||||
};
|
||||
enum class Expansion { Front, Back };
|
||||
|
||||
/**
|
||||
* Indicates which relationships from the expansion vertex should be used to expand
|
||||
* the path. Direction::In means that incoming relationships are used for expansion.
|
||||
* Indicates which relationships from the expansion vertex should be used to
|
||||
* expand
|
||||
* the path. Direction::In means that incoming relationships are used for
|
||||
* expansion.
|
||||
*
|
||||
* For example, for the given graph data:
|
||||
*
|
||||
@ -64,13 +59,10 @@ enum class Expansion {
|
||||
*
|
||||
* p = (b)
|
||||
*
|
||||
* Expansion (let's assume Expansion::Back) in the Direction::In would result in:
|
||||
* Expansion (let's assume Expansion::Back) in the Direction::In would result
|
||||
* in:
|
||||
*
|
||||
* q = (b)<-[]-(a)
|
||||
*/
|
||||
enum class Direction {
|
||||
In,
|
||||
Out,
|
||||
Both
|
||||
};
|
||||
enum class Direction { In, Out, Both };
|
||||
}
|
||||
|
@ -5,9 +5,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <list>
|
||||
|
||||
#include "enums.hpp"
|
||||
#include "utils/assert.hpp"
|
||||
@ -26,23 +26,19 @@ namespace traversal_template {
|
||||
* @tparam TVertex
|
||||
* @tparam TEdge
|
||||
*/
|
||||
template<typename TVertex, typename TEdge>
|
||||
template <typename TVertex, typename TEdge>
|
||||
class Path {
|
||||
public:
|
||||
|
||||
public:
|
||||
Path() {}
|
||||
|
||||
size_t Size() const {
|
||||
return vertices_.size();
|
||||
}
|
||||
size_t Size() const { return vertices_.size(); }
|
||||
|
||||
friend std::ostream &operator<<(std::ostream &stream, const Path &path) {
|
||||
auto vertices_it = path.vertices_.begin();
|
||||
auto vertices_end = path.vertices_.end();
|
||||
auto edges_it = path.edges_.begin();
|
||||
|
||||
if (vertices_it != vertices_end)
|
||||
stream << *vertices_it++;
|
||||
if (vertices_it != vertices_end) stream << *vertices_it++;
|
||||
|
||||
while (vertices_it != vertices_end)
|
||||
|
||||
@ -67,7 +63,8 @@ public:
|
||||
* @return A reference to this same path.
|
||||
*/
|
||||
Path &Start(const TVertex &v) {
|
||||
debug_assert(vertices_.size() == 0, "Can only start iteration on empty path");
|
||||
debug_assert(vertices_.size() == 0,
|
||||
"Can only start iteration on empty path");
|
||||
vertices_.push_back(v);
|
||||
return *this;
|
||||
}
|
||||
@ -96,7 +93,8 @@ public:
|
||||
* Gets the last Vertex of this path. Fails if the path contains no elements.
|
||||
*/
|
||||
const TVertex &Back() const {
|
||||
debug_assert(vertices_.size() > 0, "Can only get a Vertex on non-empty path");
|
||||
debug_assert(vertices_.size() > 0,
|
||||
"Can only get a Vertex on non-empty path");
|
||||
return vertices_.back();
|
||||
}
|
||||
|
||||
@ -116,21 +114,23 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the last element from the path. Fails if the path contains no elements.
|
||||
* Removes the last element from the path. Fails if the path contains no
|
||||
* elements.
|
||||
*/
|
||||
void PopBack() {
|
||||
debug_assert(vertices_.size() > 0, "Can only remove a vertex from a non-empty path");
|
||||
debug_assert(vertices_.size() > 0,
|
||||
"Can only remove a vertex from a non-empty path");
|
||||
vertices_.pop_back();
|
||||
|
||||
if (vertices_.size() > 0)
|
||||
edges_.pop_back();
|
||||
if (vertices_.size() > 0) edges_.pop_back();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the first Vertex of this path. Fails if the path contains no elements.
|
||||
*/
|
||||
const TVertex &Front() const {
|
||||
debug_assert(vertices_.size() > 0, "Can only get a vertex from a non-empty path");
|
||||
debug_assert(vertices_.size() > 0,
|
||||
"Can only get a vertex from a non-empty path");
|
||||
return vertices_.front();
|
||||
}
|
||||
|
||||
@ -148,14 +148,15 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the first element from the path. Fails if the path contains no elements.
|
||||
* Removes the first element from the path. Fails if the path contains no
|
||||
* elements.
|
||||
*/
|
||||
void PopFront() {
|
||||
debug_assert(vertices_.size() > 0, "Can only remove a vertex from a non-empty path");
|
||||
debug_assert(vertices_.size() > 0,
|
||||
"Can only remove a vertex from a non-empty path");
|
||||
vertices_.pop_front();
|
||||
|
||||
if (vertices_.size() > 0)
|
||||
edges_.pop_front();
|
||||
if (vertices_.size() > 0) edges_.pop_front();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -176,7 +177,7 @@ public:
|
||||
*/
|
||||
const auto &Edges() const { return edges_; }
|
||||
|
||||
private:
|
||||
private:
|
||||
std::list<TVertex> vertices_;
|
||||
std::list<TEdge> edges_;
|
||||
};
|
||||
@ -192,7 +193,8 @@ private:
|
||||
template <typename TVertex, typename TEdge>
|
||||
class Paths : public std::list<std::reference_wrapper<Path<TVertex, TEdge>>> {
|
||||
using Path = Path<TVertex, TEdge>;
|
||||
public:
|
||||
|
||||
public:
|
||||
bool operator==(const Paths<TVertex, TEdge> &other) const {
|
||||
return std::equal(this->begin(), this->end(), other.begin(),
|
||||
[](const std::reference_wrapper<Path> &p1,
|
||||
@ -201,7 +203,7 @@ public:
|
||||
});
|
||||
}
|
||||
|
||||
bool operator!=(const Paths &other) const { return !(*this == other);}
|
||||
bool operator!=(const Paths &other) const { return !(*this == other); }
|
||||
|
||||
friend std::ostream &operator<<(std::ostream &stream, const Paths &paths) {
|
||||
stream << "[";
|
||||
@ -214,4 +216,3 @@ public:
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -5,14 +5,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <functional>
|
||||
#include <list>
|
||||
|
||||
#include "enums.hpp"
|
||||
#include "path.hpp"
|
||||
#include "utils/assert.hpp"
|
||||
|
||||
|
||||
/**
|
||||
* This namespace contains traversal class templates that must
|
||||
* be parameterized with Vertex and Edge classes. This abstraction
|
||||
@ -49,8 +48,7 @@ namespace traversal_template {
|
||||
*/
|
||||
template <typename TVertex, typename TEdge>
|
||||
class UniquenessGroup {
|
||||
public:
|
||||
|
||||
public:
|
||||
UniquenessGroup(const Path<TVertex, TEdge> &path) : current_path_(path) {}
|
||||
|
||||
/**
|
||||
@ -61,12 +59,10 @@ public:
|
||||
* @return
|
||||
*/
|
||||
bool Contains(const TVertex &vertex) const {
|
||||
if (current_path_.Contains(vertex))
|
||||
return true;
|
||||
if (current_path_.Contains(vertex)) return true;
|
||||
|
||||
for (const auto &group : subgroups_)
|
||||
if (group.get().Contains(vertex))
|
||||
return true;
|
||||
if (group.get().Contains(vertex)) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -79,12 +75,10 @@ public:
|
||||
* @return
|
||||
*/
|
||||
bool Contains(const TEdge &edge) const {
|
||||
if (current_path_.Contains(edge))
|
||||
return true;
|
||||
if (current_path_.Contains(edge)) return true;
|
||||
|
||||
for (const auto &group : subgroups_)
|
||||
if (group.get().Contains(edge))
|
||||
return true;
|
||||
if (group.get().Contains(edge)) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -95,14 +89,17 @@ public:
|
||||
*
|
||||
* @param subgroup
|
||||
*/
|
||||
void Add(const UniquenessGroup<TVertex, TEdge> &subgroup) { subgroups_.emplace_back(subgroup); }
|
||||
void Add(const UniquenessGroup<TVertex, TEdge> &subgroup) {
|
||||
subgroups_.emplace_back(subgroup);
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
// the currently traversed path of this uniqueness group
|
||||
// set by the BeginType
|
||||
const Path<TVertex, TEdge> ¤t_path_;
|
||||
|
||||
std::vector<std::reference_wrapper<const UniquenessGroup<TVertex, TEdge>>> subgroups_;
|
||||
std::vector<std::reference_wrapper<const UniquenessGroup<TVertex, TEdge>>>
|
||||
subgroups_;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -116,14 +113,13 @@ private:
|
||||
* @tparam TVertex
|
||||
* @tparam TEdge
|
||||
*/
|
||||
template<typename TVisitable, typename TVertex, typename TEdge>
|
||||
template <typename TVisitable, typename TVertex, typename TEdge>
|
||||
class ExpandBaseType {
|
||||
|
||||
using TPath = Path<TVertex, TEdge>;
|
||||
using VertexFilter = std::function<bool(const TVertex &)>;
|
||||
using EdgeFilter = std::function<bool(const TEdge &)>;
|
||||
|
||||
public:
|
||||
public:
|
||||
/**
|
||||
* @return This expander's visitable's uniqueness group.
|
||||
*/
|
||||
@ -131,8 +127,7 @@ public:
|
||||
return visitable_.UniquenessGroup();
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
protected:
|
||||
// tracking last appended path elements during traversal
|
||||
TVertex const *current_vertex_ = nullptr;
|
||||
TEdge const *current_edge_ = nullptr;
|
||||
@ -161,18 +156,15 @@ protected:
|
||||
* provided).
|
||||
* @param uniqueness Which kind of uniqueness should be applied.
|
||||
*/
|
||||
ExpandBaseType(TVisitable &&visitable,
|
||||
Expansion expansion,
|
||||
Direction direction,
|
||||
VertexFilter vertex_filter,
|
||||
EdgeFilter edge_filter,
|
||||
Uniqueness uniqueness
|
||||
) : visitable_(std::forward<TVisitable>(visitable)),
|
||||
expansion_(expansion),
|
||||
direction_(direction),
|
||||
vertex_filter_(vertex_filter),
|
||||
edge_filter_(edge_filter),
|
||||
uniqueness_(uniqueness) {}
|
||||
ExpandBaseType(TVisitable &&visitable, Expansion expansion,
|
||||
Direction direction, VertexFilter vertex_filter,
|
||||
EdgeFilter edge_filter, Uniqueness uniqueness)
|
||||
: visitable_(std::forward<TVisitable>(visitable)),
|
||||
expansion_(expansion),
|
||||
direction_(direction),
|
||||
vertex_filter_(vertex_filter),
|
||||
edge_filter_(edge_filter),
|
||||
uniqueness_(uniqueness) {}
|
||||
|
||||
/**
|
||||
* Visits the given visitor with every expansion of the given path
|
||||
@ -183,9 +175,9 @@ protected:
|
||||
* @param p
|
||||
*/
|
||||
void VisitExpansions(std::function<void(TPath &)> visitor, TPath &p) {
|
||||
|
||||
// the start or end point of the vertex
|
||||
const auto &origin_vertex = expansion_ == Expansion::Back ? p.Back() : p.Front();
|
||||
const auto &origin_vertex =
|
||||
expansion_ == Expansion::Back ? p.Back() : p.Front();
|
||||
|
||||
if (direction_ == Direction::In || direction_ == Direction::Both)
|
||||
VisitExpansions(origin_vertex.in(), p, visitor, Direction::In);
|
||||
@ -193,7 +185,7 @@ protected:
|
||||
VisitExpansions(origin_vertex.out(), p, visitor, Direction::Out);
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
/**
|
||||
* Helper method that handles path expansion and visiting w.r.t.
|
||||
* expansion params.
|
||||
@ -209,21 +201,21 @@ private:
|
||||
* @param visitor
|
||||
* @param direction
|
||||
*/
|
||||
template<typename Edges>
|
||||
void VisitExpansions(Edges &edges,
|
||||
TPath &p,
|
||||
template <typename Edges>
|
||||
void VisitExpansions(Edges &edges, TPath &p,
|
||||
std::function<void(TPath &)> visitor,
|
||||
Direction direction) {
|
||||
|
||||
for (const TEdge &e : edges) {
|
||||
// edge filtering and uniqueness
|
||||
if (edge_filter_ && !edge_filter_(e)) continue;
|
||||
if (uniqueness_ == Uniqueness::Edge && UniquenessGroup().Contains(e)) continue;
|
||||
if (uniqueness_ == Uniqueness::Edge && UniquenessGroup().Contains(e))
|
||||
continue;
|
||||
|
||||
// vertex filtering and uniqueness
|
||||
const TVertex &v = (direction == Direction::In) ? e.from() : e.to();
|
||||
if (vertex_filter_ && !vertex_filter_(v)) continue;
|
||||
if (uniqueness_ == Uniqueness::Vertex && UniquenessGroup().Contains(v))continue;
|
||||
if (uniqueness_ == Uniqueness::Vertex && UniquenessGroup().Contains(v))
|
||||
continue;
|
||||
|
||||
current_edge_ = &e;
|
||||
current_vertex_ = &v;
|
||||
@ -257,15 +249,13 @@ private:
|
||||
* @tparam TVertex
|
||||
* @tparam TEdge
|
||||
*/
|
||||
template<typename TVisitable, typename TVertex, typename TEdge>
|
||||
template <typename TVisitable, typename TVertex, typename TEdge>
|
||||
class ExpandVariableType : public ExpandBaseType<TVisitable, TVertex, TEdge> {
|
||||
|
||||
using TPath = Path<TVertex, TEdge>;
|
||||
using VertexFilter = std::function<bool(const TVertex &)>;
|
||||
using EdgeFilter = std::function<bool(const TEdge &)>;
|
||||
|
||||
|
||||
public:
|
||||
public:
|
||||
/**
|
||||
* For most params see the ExpandBaseType::ExpandBaseType documentation.
|
||||
*
|
||||
@ -274,30 +264,26 @@ public:
|
||||
* @param max_length Maximum number of vertices in a path for it to be
|
||||
* visited. Exclusive.
|
||||
*/
|
||||
ExpandVariableType(TVisitable &&visitable,
|
||||
Expansion expansion,
|
||||
Direction direction,
|
||||
VertexFilter vertex_filter,
|
||||
EdgeFilter edge_filter,
|
||||
int min_length,
|
||||
int max_length,
|
||||
Uniqueness uniqueness) :
|
||||
ExpandBaseType<TVisitable, TVertex, TEdge>(std::forward<TVisitable>(visitable),
|
||||
expansion, direction, {}, edge_filter, uniqueness),
|
||||
min_length_(min_length),
|
||||
max_length_(max_length),
|
||||
current_vertex_filter_(vertex_filter) {
|
||||
}
|
||||
ExpandVariableType(TVisitable &&visitable, Expansion expansion,
|
||||
Direction direction, VertexFilter vertex_filter,
|
||||
EdgeFilter edge_filter, int min_length, int max_length,
|
||||
Uniqueness uniqueness)
|
||||
: ExpandBaseType<TVisitable, TVertex, TEdge>(
|
||||
std::forward<TVisitable>(visitable), expansion, direction, {},
|
||||
edge_filter, uniqueness),
|
||||
min_length_(min_length),
|
||||
max_length_(max_length),
|
||||
current_vertex_filter_(vertex_filter) {}
|
||||
|
||||
/**
|
||||
* Calls the given visitor function once for every path this traversal generates.
|
||||
* Calls the given visitor function once for every path this traversal
|
||||
* generates.
|
||||
*
|
||||
* @param visitor
|
||||
*/
|
||||
void Visit(std::function<void(TPath &)> visitor) {
|
||||
this->visitable_.Visit([this, &visitor](TPath &p) {
|
||||
VisitRecursive(visitor, p, p.Size());
|
||||
});
|
||||
this->visitable_.Visit(
|
||||
[this, &visitor](TPath &p) { VisitRecursive(visitor, p, p.Size()); });
|
||||
}
|
||||
|
||||
/**
|
||||
@ -306,10 +292,8 @@ public:
|
||||
*
|
||||
* @return An expansion that generates paths one traversal longer.
|
||||
*/
|
||||
auto Expand(Expansion expansion,
|
||||
Direction direction,
|
||||
VertexFilter vertex_filter = {},
|
||||
EdgeFilter edge_filter = {});
|
||||
auto Expand(Expansion expansion, Direction direction,
|
||||
VertexFilter vertex_filter = {}, EdgeFilter edge_filter = {});
|
||||
|
||||
/**
|
||||
* Expands from this expansion along a variable number traversal.
|
||||
@ -317,13 +301,10 @@ public:
|
||||
*
|
||||
* @return An expansion that generates paths variable length longer.
|
||||
*/
|
||||
auto ExpandVariable(
|
||||
Expansion expansion,
|
||||
Direction direction,
|
||||
VertexFilter vertex_filter = {},
|
||||
EdgeFilter edge_filter = {},
|
||||
int min_length = 0,
|
||||
int max_length = 1000);
|
||||
auto ExpandVariable(Expansion expansion, Direction direction,
|
||||
VertexFilter vertex_filter = {},
|
||||
EdgeFilter edge_filter = {}, int min_length = 0,
|
||||
int max_length = 1000);
|
||||
|
||||
/**
|
||||
* Returns a reference to the vertex currently being traversed
|
||||
@ -331,7 +312,9 @@ public:
|
||||
* TRAVERSAL!!!
|
||||
*/
|
||||
const TVertex &CurrentVertex() const {
|
||||
debug_assert(this->current_vertex_ != nullptr, "Current vertex not set, function most likely called outside of traversal");
|
||||
debug_assert(this->current_vertex_ != nullptr,
|
||||
"Current vertex not set, function most likely called outside "
|
||||
"of traversal");
|
||||
return *this->current_vertex_;
|
||||
}
|
||||
|
||||
@ -341,18 +324,22 @@ public:
|
||||
* TRAVERSAL!!!
|
||||
*/
|
||||
const std::list<TEdge> &CurrentEdges() const {
|
||||
debug_assert(this->current_edge_ != nullptr, "Current edge not set, function most likely called outside of traversal");
|
||||
debug_assert(this->current_edge_ != nullptr,
|
||||
"Current edge not set, function most likely called outside of "
|
||||
"traversal");
|
||||
return current_edges_;
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
// see constructor documentation for member var explanation
|
||||
const int min_length_;
|
||||
const int max_length_;
|
||||
|
||||
// the expand variable has another vertex filter used only on the last path element
|
||||
// the expand variable has another vertex filter used only on the last path
|
||||
// element
|
||||
// because traversal is done recursively using superclass functionality,
|
||||
// so we give an empty filter to superclass so it does not end traversal, and use
|
||||
// so we give an empty filter to superclass so it does not end traversal, and
|
||||
// use
|
||||
// this filter to see if we actually need to visit the path or not
|
||||
const VertexFilter current_vertex_filter_;
|
||||
|
||||
@ -369,22 +356,25 @@ private:
|
||||
* @param p_start_size The size of the path before variable-length expansion.
|
||||
* It's necessary to keep track of it because min and max length are
|
||||
* evaluated against how much this traversal generated, not against the
|
||||
* actual path length (there could have been plan expansions before this variable length).
|
||||
* actual path length (there could have been plan expansions before this
|
||||
* variable length).
|
||||
*/
|
||||
void VisitRecursive(std::function<void(TPath &)> visitor, TPath &p, const size_t p_start_size) {
|
||||
debug_assert(p.Size() >= p_start_size, "Current path must be greater then start size");
|
||||
void VisitRecursive(std::function<void(TPath &)> visitor, TPath &p,
|
||||
const size_t p_start_size) {
|
||||
debug_assert(p.Size() >= p_start_size,
|
||||
"Current path must be greater then start size");
|
||||
|
||||
size_t recursion_size = p.Size() - p_start_size;
|
||||
|
||||
// only append to current_edges once the first traversal happened
|
||||
if (recursion_size > 0)
|
||||
current_edges_.emplace_back(*this->current_edge_);
|
||||
if (recursion_size > 0) current_edges_.emplace_back(*this->current_edge_);
|
||||
|
||||
if (recursion_size >= min_length_ && (!current_vertex_filter_ || current_vertex_filter_(*this->current_vertex_)))
|
||||
if (recursion_size >= min_length_ &&
|
||||
(!current_vertex_filter_ ||
|
||||
current_vertex_filter_(*this->current_vertex_)))
|
||||
visitor(p);
|
||||
|
||||
if (recursion_size >= max_length_ - 1)
|
||||
return;
|
||||
if (recursion_size >= max_length_ - 1) return;
|
||||
|
||||
// a lambda we'll inject to ExpandVisit, that calls this function
|
||||
// with the expanded path
|
||||
@ -394,8 +384,7 @@ private:
|
||||
|
||||
this->VisitExpansions(recursive_visitor, p);
|
||||
|
||||
if (recursion_size > 0)
|
||||
current_edges_.pop_back();
|
||||
if (recursion_size > 0) current_edges_.pop_back();
|
||||
}
|
||||
};
|
||||
|
||||
@ -409,36 +398,32 @@ private:
|
||||
* @tparam TVertex
|
||||
* @tparam TEdge
|
||||
*/
|
||||
template<typename TVisitable, typename TVertex, typename TEdge>
|
||||
template <typename TVisitable, typename TVertex, typename TEdge>
|
||||
class ExpandType : public ExpandBaseType<TVisitable, TVertex, TEdge> {
|
||||
|
||||
using TPath = Path<TVertex, TEdge>;
|
||||
using VertexFilter = std::function<bool(const TVertex &)>;
|
||||
using EdgeFilter = std::function<bool(const TEdge &)>;
|
||||
|
||||
|
||||
public:
|
||||
public:
|
||||
/**
|
||||
* For all params see the ExpandBaseType::ExpandBaseType documentation.
|
||||
*/
|
||||
ExpandType(TVisitable &&visitable,
|
||||
Expansion expansion,
|
||||
Direction direction,
|
||||
VertexFilter vertex_filter,
|
||||
EdgeFilter edge_filter,
|
||||
Uniqueness uniqueness) :
|
||||
ExpandBaseType<TVisitable, TVertex, TEdge>(std::forward<TVisitable>(visitable),
|
||||
expansion, direction, vertex_filter, edge_filter, uniqueness) {}
|
||||
ExpandType(TVisitable &&visitable, Expansion expansion, Direction direction,
|
||||
VertexFilter vertex_filter, EdgeFilter edge_filter,
|
||||
Uniqueness uniqueness)
|
||||
: ExpandBaseType<TVisitable, TVertex, TEdge>(
|
||||
std::forward<TVisitable>(visitable), expansion, direction,
|
||||
vertex_filter, edge_filter, uniqueness) {}
|
||||
|
||||
/**
|
||||
* Calls the given visitor function once for every path this traversal generates.
|
||||
* Calls the given visitor function once for every path this traversal
|
||||
* generates.
|
||||
*
|
||||
* @param visitor
|
||||
*/
|
||||
void Visit(std::function<void(TPath &)> visitor) {
|
||||
this->visitable_.Visit([this, &visitor](TPath &p) {
|
||||
this->VisitExpansions(visitor, p);
|
||||
});
|
||||
this->visitable_.Visit(
|
||||
[this, &visitor](TPath &p) { this->VisitExpansions(visitor, p); });
|
||||
}
|
||||
|
||||
/**
|
||||
@ -447,7 +432,9 @@ public:
|
||||
* TRAVERSAL!!!
|
||||
*/
|
||||
const TVertex &CurrentVertex() const {
|
||||
debug_assert(this->current_vertex_ != nullptr, "Current vertex not set, function most likely called outside of traversal");
|
||||
debug_assert(this->current_vertex_ != nullptr,
|
||||
"Current vertex not set, function most likely called outside "
|
||||
"of traversal");
|
||||
return *this->current_vertex_;
|
||||
}
|
||||
|
||||
@ -457,7 +444,9 @@ public:
|
||||
* TRAVERSAL!!!
|
||||
*/
|
||||
const TEdge &CurrentEdge() const {
|
||||
debug_assert(this->current_edge_ != nullptr, "Current edge not set, function most likely called outside of traversal");
|
||||
debug_assert(this->current_edge_ != nullptr,
|
||||
"Current edge not set, function most likely called outside of "
|
||||
"traversal");
|
||||
return *this->current_edge_;
|
||||
}
|
||||
|
||||
@ -467,12 +456,11 @@ public:
|
||||
*
|
||||
* @return An expansion that generates paths one traversal longer.
|
||||
*/
|
||||
auto Expand(Expansion expansion,
|
||||
Direction direction,
|
||||
VertexFilter vertex_filter = {},
|
||||
EdgeFilter edge_filter = {}) {
|
||||
auto Expand(Expansion expansion, Direction direction,
|
||||
VertexFilter vertex_filter = {}, EdgeFilter edge_filter = {}) {
|
||||
return ExpandType<ExpandType<TVisitable, TVertex, TEdge> &, TVertex, TEdge>(
|
||||
*this, expansion, direction, vertex_filter, edge_filter, this->uniqueness_);
|
||||
*this, expansion, direction, vertex_filter, edge_filter,
|
||||
this->uniqueness_);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -481,15 +469,14 @@ public:
|
||||
*
|
||||
* @return An expansion that generates paths variable length longer.
|
||||
*/
|
||||
auto ExpandVariable(
|
||||
Expansion expansion,
|
||||
Direction direction,
|
||||
VertexFilter vertex_filter = {},
|
||||
EdgeFilter edge_filter = {},
|
||||
int min_length = 0,
|
||||
int max_length = 1000) {
|
||||
return ExpandVariableType<const ExpandType<TVisitable, TVertex, TEdge> &, TVertex, TEdge>(
|
||||
*this, expansion, direction, vertex_filter, edge_filter, min_length, max_length, this->uniqueness_);
|
||||
auto ExpandVariable(Expansion expansion, Direction direction,
|
||||
VertexFilter vertex_filter = {},
|
||||
EdgeFilter edge_filter = {}, int min_length = 0,
|
||||
int max_length = 1000) {
|
||||
return ExpandVariableType<const ExpandType<TVisitable, TVertex, TEdge> &,
|
||||
TVertex, TEdge>(
|
||||
*this, expansion, direction, vertex_filter, edge_filter, min_length,
|
||||
max_length, this->uniqueness_);
|
||||
}
|
||||
};
|
||||
|
||||
@ -500,17 +487,17 @@ public:
|
||||
* @tparam TVertex
|
||||
* @tparam TEdge
|
||||
*/
|
||||
template<typename TIterable, typename TVertex, typename TEdge>
|
||||
template <typename TIterable, typename TVertex, typename TEdge>
|
||||
class BeginType {
|
||||
|
||||
using TPath = Path<TVertex, TEdge>;
|
||||
using VertexFilter = std::function<bool(const TVertex &)>;
|
||||
using EdgeFilter = std::function<bool(const TEdge &)>;
|
||||
|
||||
public:
|
||||
|
||||
BeginType(const TIterable &vertices, VertexFilter vertex_filter) :
|
||||
vertices_(vertices), vertex_filter_(vertex_filter), uniqueness_group_(path) {}
|
||||
public:
|
||||
BeginType(const TIterable &vertices, VertexFilter vertex_filter)
|
||||
: vertices_(vertices),
|
||||
vertex_filter_(vertex_filter),
|
||||
uniqueness_group_(path) {}
|
||||
|
||||
/**
|
||||
* Calls the visitor with a path containing a single vertex
|
||||
@ -521,9 +508,7 @@ public:
|
||||
*/
|
||||
void Visit(std::function<void(TPath &)> visitor) {
|
||||
for (const TVertex &v : vertices_) {
|
||||
|
||||
if (vertex_filter_ && !vertex_filter_(v))
|
||||
continue;
|
||||
if (vertex_filter_ && !vertex_filter_(v)) continue;
|
||||
|
||||
path.Start(v);
|
||||
visitor(path);
|
||||
@ -531,7 +516,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The UniquenessGroup of this BeginType (the only one that
|
||||
* exists for all the expansions from this Begin).
|
||||
@ -546,7 +530,9 @@ public:
|
||||
* TRAVERSAL!!!
|
||||
*/
|
||||
const TVertex &CurrentVertex() const {
|
||||
debug_assert(path.Size() > 0, "Current path is empty, function most likely called outside of traversal");
|
||||
debug_assert(path.Size() > 0,
|
||||
"Current path is empty, function most likely called outside "
|
||||
"of traversal");
|
||||
return path.Front();
|
||||
}
|
||||
|
||||
@ -556,10 +542,8 @@ public:
|
||||
*
|
||||
* @return An expansion that generates paths one traversal longer.
|
||||
*/
|
||||
auto Expand(Expansion expansion,
|
||||
Direction direction,
|
||||
VertexFilter vertex_filter = {},
|
||||
EdgeFilter edge_filter = {},
|
||||
auto Expand(Expansion expansion, Direction direction,
|
||||
VertexFilter vertex_filter = {}, EdgeFilter edge_filter = {},
|
||||
Uniqueness uniqueness = Uniqueness::Edge) {
|
||||
return ExpandType<BeginType<TIterable, TVertex, TEdge> &, TVertex, TEdge>(
|
||||
*this, expansion, direction, vertex_filter, edge_filter, uniqueness);
|
||||
@ -571,21 +555,21 @@ public:
|
||||
*
|
||||
* @return An expansion that generates paths variable length longer.
|
||||
*/
|
||||
auto ExpandVariable(
|
||||
Expansion expansion,
|
||||
Direction direction,
|
||||
VertexFilter vertex_filter = {},
|
||||
EdgeFilter edge_filter = {},
|
||||
int min_length = 1,
|
||||
int max_length = 1000,
|
||||
Uniqueness uniqueness = Uniqueness::Edge) {
|
||||
return ExpandVariableType<BeginType<TIterable, TVertex, TEdge> &, TVertex, TEdge>(
|
||||
*this, expansion, direction, vertex_filter, edge_filter, min_length, max_length, uniqueness);
|
||||
auto ExpandVariable(Expansion expansion, Direction direction,
|
||||
VertexFilter vertex_filter = {},
|
||||
EdgeFilter edge_filter = {}, int min_length = 1,
|
||||
int max_length = 1000,
|
||||
Uniqueness uniqueness = Uniqueness::Edge) {
|
||||
return ExpandVariableType<BeginType<TIterable, TVertex, TEdge> &, TVertex,
|
||||
TEdge>(*this, expansion, direction, vertex_filter,
|
||||
edge_filter, min_length, max_length,
|
||||
uniqueness);
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
const TIterable &vertices_;
|
||||
// the BeingType has only one path that gets appended to and emitted to visitors
|
||||
// the BeingType has only one path that gets appended to and emitted to
|
||||
// visitors
|
||||
TPath path;
|
||||
const VertexFilter vertex_filter_;
|
||||
// TODO review: why do I have to have namespace:: here???
|
||||
@ -601,7 +585,7 @@ private:
|
||||
*
|
||||
* @return A BeginType.
|
||||
*/
|
||||
template<typename TIterable, typename TVertex, typename TEdge>
|
||||
template <typename TIterable, typename TVertex, typename TEdge>
|
||||
auto Begin(const TIterable &vertices,
|
||||
std::function<bool(const TVertex &)> vertex_filter = {}) {
|
||||
return BeginType<TIterable, TVertex, TEdge>(vertices, vertex_filter);
|
||||
@ -610,14 +594,14 @@ auto Begin(const TIterable &vertices,
|
||||
/**
|
||||
* Creates a start point for a recursion of Cartesian wrappers.
|
||||
*/
|
||||
template<typename TVisitable, typename TVertex, typename TEdge>
|
||||
template <typename TVisitable, typename TVertex, typename TEdge>
|
||||
class CartesianUnaryType {
|
||||
using TPath = Path<TVertex, TEdge>;
|
||||
using TPaths = Paths<TVertex, TEdge>;
|
||||
|
||||
public:
|
||||
CartesianUnaryType(TVisitable &&visitable) :
|
||||
visitable_(std::forward<TVisitable>(visitable)) {}
|
||||
public:
|
||||
CartesianUnaryType(TVisitable &&visitable)
|
||||
: visitable_(std::forward<TVisitable>(visitable)) {}
|
||||
|
||||
void Visit(std::function<void(TPaths &)> visitor) const {
|
||||
TPaths paths;
|
||||
@ -628,29 +612,35 @@ public:
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
const TVisitable visitable_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Provides means to visit a cartesian product of traversals.
|
||||
*
|
||||
* @tparam TVisitableFirst A visitable whose visitor accepts a list of path reference (recursion down).
|
||||
* @tparam TVisitableOthers Visitables whose visitor accepts a single path reference.
|
||||
* @tparam TVisitableFirst A visitable whose visitor accepts a list of path
|
||||
* reference (recursion down).
|
||||
* @tparam TVisitableOthers Visitables whose visitor accepts a single path
|
||||
* reference.
|
||||
*/
|
||||
template<typename TVisitableFirst, typename TVisitableOthers, typename TVertex, typename TEdge>
|
||||
template <typename TVisitableFirst, typename TVisitableOthers, typename TVertex,
|
||||
typename TEdge>
|
||||
class CartesianBinaryType {
|
||||
using TPath = Path<TVertex, TEdge>;
|
||||
using TPaths = Paths<TVertex, TEdge>;
|
||||
|
||||
public:
|
||||
public:
|
||||
/**
|
||||
* @tparam visitable_first A visitable whose visitor accepts a list of path reference (recursion down).
|
||||
* @tparam visitable_others Visitable whose visitor accepts a single path reference.
|
||||
* @tparam visitable_first A visitable whose visitor accepts a list of path
|
||||
* reference (recursion down).
|
||||
* @tparam visitable_others Visitable whose visitor accepts a single path
|
||||
* reference.
|
||||
*/
|
||||
CartesianBinaryType(TVisitableFirst &&visitable_first, TVisitableOthers &&visitable_others) :
|
||||
visitable_first_(std::forward<TVisitableFirst>(visitable_first)),
|
||||
visitable_others_(std::forward<TVisitableOthers>(visitable_others)) {}
|
||||
CartesianBinaryType(TVisitableFirst &&visitable_first,
|
||||
TVisitableOthers &&visitable_others)
|
||||
: visitable_first_(std::forward<TVisitableFirst>(visitable_first)),
|
||||
visitable_others_(std::forward<TVisitableOthers>(visitable_others)) {}
|
||||
|
||||
/**
|
||||
* Calls the given visitor with a list of reference wrappers to Paths
|
||||
@ -659,7 +649,6 @@ public:
|
||||
* @param visitor
|
||||
*/
|
||||
void Visit(std::function<void(TPaths &)> visitor) const {
|
||||
|
||||
// TODO currently cartesian product does NOT check for uniqueness
|
||||
// for example between edges in the emitted path combinations
|
||||
|
||||
@ -672,14 +661,15 @@ public:
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
const TVisitableFirst visitable_first_;
|
||||
const TVisitableOthers visitable_others_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates an object that can be visited with a function that accepts a list
|
||||
* of path reference wrappers. That function will be called one for every element
|
||||
* of path reference wrappers. That function will be called one for every
|
||||
* element
|
||||
* of a cartesian product of the given visitables (that emit paths).
|
||||
*
|
||||
* @tparam TVisitableFirst A visitable that emits paths.
|
||||
@ -688,15 +678,16 @@ private:
|
||||
* @param others An arbitrary number of visitables that emit paths.
|
||||
* @return See above.
|
||||
*/
|
||||
template<typename TVisitable, typename TVertex, typename TEdge>
|
||||
template <typename TVisitable, typename TVertex, typename TEdge>
|
||||
auto Cartesian(TVisitable &&visitable) {
|
||||
return CartesianUnaryType<TVisitable, TVertex, TEdge>(std::forward<TVisitable>(visitable));
|
||||
|
||||
return CartesianUnaryType<TVisitable, TVertex, TEdge>(
|
||||
std::forward<TVisitable>(visitable));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an object that can be visited with a function that accepts a list
|
||||
* of path reference wrappers. That function will be called one for every element
|
||||
* of path reference wrappers. That function will be called one for every
|
||||
* element
|
||||
* of a cartesian product of the given visitables (that emit paths).
|
||||
*
|
||||
* @tparam TVisitableFirst A visitable that emits paths.
|
||||
@ -705,34 +696,32 @@ auto Cartesian(TVisitable &&visitable) {
|
||||
* @param others An arbitrary number of visitables that emit paths.
|
||||
* @return See above.
|
||||
*/
|
||||
template<typename TVisitableFirst, typename Vertex, typename Edge, typename... TVisitableOthers>
|
||||
template <typename TVisitableFirst, typename Vertex, typename Edge,
|
||||
typename... TVisitableOthers>
|
||||
auto Cartesian(TVisitableFirst &&first, TVisitableOthers &&... others) {
|
||||
return CartesianBinaryType<TVisitableFirst, decltype(Cartesian(std::forward<TVisitableOthers>(others)...)),
|
||||
Vertex, Edge>(
|
||||
std::forward<TVisitableFirst>(first),
|
||||
Cartesian(std::forward<TVisitableOthers>(others)...)
|
||||
);
|
||||
return CartesianBinaryType<
|
||||
TVisitableFirst,
|
||||
decltype(Cartesian(std::forward<TVisitableOthers>(others)...)), Vertex,
|
||||
Edge>(std::forward<TVisitableFirst>(first),
|
||||
Cartesian(std::forward<TVisitableOthers>(others)...));
|
||||
}
|
||||
|
||||
template<typename TVisitable, typename TVertex, typename TEdge>
|
||||
auto ExpandVariableType<TVisitable, TVertex, TEdge>::Expand(Expansion expansion,
|
||||
Direction direction,
|
||||
VertexFilter vertex_filter,
|
||||
EdgeFilter edge_filter) {
|
||||
return ExpandType<ExpandVariableType<TVisitable, TVertex, TEdge> &, TVertex, TEdge>(
|
||||
*this, expansion, direction, vertex_filter, edge_filter, this->uniqueness_);
|
||||
template <typename TVisitable, typename TVertex, typename TEdge>
|
||||
auto ExpandVariableType<TVisitable, TVertex, TEdge>::Expand(
|
||||
Expansion expansion, Direction direction, VertexFilter vertex_filter,
|
||||
EdgeFilter edge_filter) {
|
||||
return ExpandType<ExpandVariableType<TVisitable, TVertex, TEdge> &, TVertex,
|
||||
TEdge>(*this, expansion, direction, vertex_filter,
|
||||
edge_filter, this->uniqueness_);
|
||||
}
|
||||
|
||||
template<typename TVisitable, typename TVertex, typename TEdge>
|
||||
template <typename TVisitable, typename TVertex, typename TEdge>
|
||||
auto ExpandVariableType<TVisitable, TVertex, TEdge>::ExpandVariable(
|
||||
Expansion expansion,
|
||||
Direction direction,
|
||||
VertexFilter vertex_filter,
|
||||
EdgeFilter edge_filter,
|
||||
int min_length,
|
||||
int max_length) {
|
||||
return ExpandVariableType<ExpandVariableType<TVisitable, TVertex, TEdge> &, TVertex, TEdge>(
|
||||
*this, expansion, direction, vertex_filter, edge_filter, min_length, max_length, this->uniqueness_);
|
||||
Expansion expansion, Direction direction, VertexFilter vertex_filter,
|
||||
EdgeFilter edge_filter, int min_length, int max_length) {
|
||||
return ExpandVariableType<ExpandVariableType<TVisitable, TVertex, TEdge> &,
|
||||
TVertex, TEdge>(
|
||||
*this, expansion, direction, vertex_filter, edge_filter, min_length,
|
||||
max_length, this->uniqueness_);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -9,8 +9,8 @@
|
||||
#include "path.hpp"
|
||||
#include "templates.hpp"
|
||||
|
||||
#include "storage/vertex_accessor.hpp"
|
||||
#include "storage/edge_accessor.hpp"
|
||||
#include "storage/vertex_accessor.hpp"
|
||||
|
||||
/**
|
||||
* A specialization of the "traversal" namespace that uses
|
||||
@ -25,34 +25,36 @@ using Paths = traversal_template::Paths<VertexAccessor, EdgeAccessor>;
|
||||
/**
|
||||
* Specialization of the traversal_template::Begin function.
|
||||
*/
|
||||
template<typename TCollection>
|
||||
auto Begin(const TCollection &vertices, std::function<bool(const VertexAccessor &)> vertex_filter = {}) {
|
||||
return traversal_template::Begin<TCollection, VertexAccessor, EdgeAccessor>(vertices, vertex_filter);
|
||||
template <typename TCollection>
|
||||
auto Begin(const TCollection &vertices,
|
||||
std::function<bool(const VertexAccessor &)> vertex_filter = {}) {
|
||||
return traversal_template::Begin<TCollection, VertexAccessor, EdgeAccessor>(
|
||||
vertices, vertex_filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specialization of the traversal_template::Cartesian function that accepts
|
||||
* a single argument.
|
||||
*/
|
||||
template<typename TVisitable>
|
||||
template <typename TVisitable>
|
||||
auto Cartesian(TVisitable &&visitable) {
|
||||
return traversal_template::Cartesian<TVisitable, VertexAccessor, EdgeAccessor>(
|
||||
return traversal_template::Cartesian<TVisitable, VertexAccessor,
|
||||
EdgeAccessor>(
|
||||
std::forward<TVisitable>(visitable));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Specialization of the traversal_template::Cartesian function that accepts
|
||||
* multiple arguments.
|
||||
*/
|
||||
template<typename TVisitableFirst, typename... TVisitableOthers>
|
||||
template <typename TVisitableFirst, typename... TVisitableOthers>
|
||||
auto Cartesian(TVisitableFirst &&first, TVisitableOthers &&... others) {
|
||||
return traversal_template::CartesianBinaryType<TVisitableFirst, decltype(Cartesian(
|
||||
std::forward<TVisitableOthers>(others)...)),
|
||||
return traversal_template::CartesianBinaryType<
|
||||
TVisitableFirst,
|
||||
decltype(Cartesian(std::forward<TVisitableOthers>(others)...)),
|
||||
VertexAccessor, EdgeAccessor>(
|
||||
std::forward<TVisitableFirst>(first),
|
||||
Cartesian(std::forward<TVisitableOthers>(others)...)
|
||||
);
|
||||
Cartesian(std::forward<TVisitableOthers>(others)...));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,8 +4,6 @@ project(${project_name}_tests)
|
||||
|
||||
enable_testing()
|
||||
|
||||
include_directories(${catch_source_dir}/include)
|
||||
|
||||
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/test_results/unit)
|
||||
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/test_results/benchmark)
|
||||
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/test_results/property_based)
|
||||
|
@ -11,7 +11,6 @@
|
||||
#include "data_structures/concurrent/concurrent_map.hpp"
|
||||
#include "data_structures/concurrent/concurrent_set.hpp"
|
||||
#include "data_structures/concurrent/skiplist.hpp"
|
||||
#include "data_structures/static_array.hpp"
|
||||
#include "utils/assert.hpp"
|
||||
#include "utils/sysinfo/memory.hpp"
|
||||
|
||||
@ -73,7 +72,7 @@ void check_size_list(S &acc, long long size) {
|
||||
for ([[gnu::unused]] auto elem : acc) {
|
||||
++iterator_counter;
|
||||
}
|
||||
permanent_assert(iterator_counter == size,
|
||||
permanent_assert(static_cast<int64_t>(iterator_counter) == size,
|
||||
"Iterator count should be " << size << ", but size is "
|
||||
<< iterator_counter);
|
||||
}
|
||||
@ -91,7 +90,7 @@ void check_size(typename S::Accessor &acc, long long size) {
|
||||
for ([[gnu::unused]] auto elem : acc) {
|
||||
++iterator_counter;
|
||||
}
|
||||
permanent_assert(iterator_counter == size,
|
||||
permanent_assert(static_cast<int64_t>(iterator_counter) == size,
|
||||
"Iterator count should be " << size << ", but size is "
|
||||
<< iterator_counter);
|
||||
}
|
||||
@ -111,7 +110,7 @@ void check_order(typename S::Accessor &acc) {
|
||||
}
|
||||
|
||||
void check_zero(size_t key_range, long array[], const char *str) {
|
||||
for (int i = 0; i < key_range; i++) {
|
||||
for (int i = 0; i < static_cast<int>(key_range); i++) {
|
||||
permanent_assert(array[i] == 0,
|
||||
str << " doesn't hold it's guarantees. It has " << array[i]
|
||||
<< " extra elements.");
|
||||
@ -119,7 +118,7 @@ void check_zero(size_t key_range, long array[], const char *str) {
|
||||
}
|
||||
|
||||
void check_set(DynamicBitset<> &db, std::vector<bool> &set) {
|
||||
for (int i = 0; i < set.size(); i++) {
|
||||
for (int i = 0; i < static_cast<int>(set.size()); i++) {
|
||||
permanent_assert(!(set[i] ^ db.at(i)),
|
||||
"Set constraints aren't fullfilled.");
|
||||
}
|
||||
@ -177,7 +176,7 @@ std::vector<bool> collect_set(
|
||||
std::vector<bool> set;
|
||||
for (auto &data : collect(futures)) {
|
||||
set.resize(data.second.size());
|
||||
for (int i = 0; i < data.second.size(); i++) {
|
||||
for (int i = 0; i < static_cast<int>(data.second.size()); i++) {
|
||||
set[i] = set[i] | data.second[i];
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
#include "common.h"
|
||||
#include "common.hpp"
|
||||
|
||||
constexpr size_t THREADS_NO = std::min(max_no_threads, 8);
|
||||
constexpr size_t key_range = 1e2;
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "common.h"
|
||||
#include "common.hpp"
|
||||
|
||||
constexpr size_t THREADS_NO = std::min(max_no_threads, 8);
|
||||
constexpr size_t op_per_thread = 1e5;
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "common.h"
|
||||
#include "common.hpp"
|
||||
|
||||
constexpr size_t THREADS_NO = std::min(max_no_threads, 4);
|
||||
constexpr size_t op_per_thread = 1e5;
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "common.h"
|
||||
#include "common.hpp"
|
||||
|
||||
constexpr size_t THREADS_NO = std::min(max_no_threads, 8);
|
||||
constexpr size_t op_per_thread = 1e5;
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "common.h"
|
||||
#include "common.hpp"
|
||||
|
||||
constexpr size_t THREADS_NO = std::min(max_no_threads, 4);
|
||||
constexpr size_t op_per_thread = 1e5;
|
||||
|
@ -5,7 +5,7 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "utils/assert.hpp"
|
||||
#include "utils/timer/timer.hpp"
|
||||
#include "utils/timer.hpp"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
using namespace utils;
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "common.h"
|
||||
#include "common.hpp"
|
||||
|
||||
constexpr size_t THREADS_NO = std::min(max_no_threads, 8);
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "common.h"
|
||||
#include "common.hpp"
|
||||
|
||||
constexpr size_t THREADS_NO = std::min(max_no_threads, 8);
|
||||
constexpr size_t elems_per_thread = 100000;
|
||||
|
@ -1,19 +1,19 @@
|
||||
#include "common.h"
|
||||
#include "common.hpp"
|
||||
|
||||
constexpr size_t THREADS_NO = std::min(max_no_threads, 8);
|
||||
constexpr size_t elems_per_thread = 1e5;
|
||||
|
||||
// TODO: document the test
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int main(int, char **argv) {
|
||||
google::InitGoogleLogging(argv[0]);
|
||||
memory_check(THREADS_NO, [&] {
|
||||
ds::static_array<std::thread, THREADS_NO> threads;
|
||||
std::vector<std::thread> threads;
|
||||
map_t skiplist;
|
||||
|
||||
// put THREADS_NO * elems_per_thread items to the skiplist
|
||||
for (size_t thread_i = 0; thread_i < THREADS_NO; ++thread_i) {
|
||||
threads[thread_i] = std::thread(
|
||||
threads.emplace_back(
|
||||
[&skiplist](size_t start, size_t end) {
|
||||
auto accessor = skiplist.access();
|
||||
for (size_t elem_i = start; elem_i < end; ++elem_i) {
|
||||
@ -54,8 +54,8 @@ int main(int argc, char **argv) {
|
||||
// check size
|
||||
{
|
||||
auto accessor = skiplist.access();
|
||||
permanent_assert(accessor.size() == 0,
|
||||
"Size should be 0, but size is " << accessor.size());
|
||||
permanent_assert(accessor.size() == 0, "Size should be 0, but size is "
|
||||
<< accessor.size());
|
||||
}
|
||||
|
||||
// check count
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "common.h"
|
||||
#include "common.hpp"
|
||||
|
||||
constexpr size_t THREADS_NO = std::min(max_no_threads, 8);
|
||||
constexpr size_t elements = 2e6;
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "common.h"
|
||||
#include "common.hpp"
|
||||
|
||||
constexpr size_t THREADS_NO = std::min(max_no_threads, 1);
|
||||
constexpr size_t elems_per_thread = 16e5;
|
||||
@ -9,16 +9,16 @@ constexpr size_t elems_per_thread = 16e5;
|
||||
// 2. analyse this code
|
||||
// 3. fix the memory leak
|
||||
// 4. write proper test
|
||||
int main(int argc, char **argv) {
|
||||
int main(int, char **argv) {
|
||||
google::InitGoogleLogging(argv[0]);
|
||||
|
||||
memory_check(THREADS_NO, [&] {
|
||||
ds::static_array<std::thread, THREADS_NO> threads;
|
||||
std::vector<std::thread> threads;
|
||||
map_t skiplist;
|
||||
|
||||
// put THREADS_NO * elems_per_thread items to the skiplist
|
||||
for (size_t thread_i = 0; thread_i < THREADS_NO; ++thread_i) {
|
||||
threads[thread_i] = std::thread(
|
||||
threads.emplace_back(
|
||||
[&skiplist](size_t start, size_t end) {
|
||||
auto accessor = skiplist.access();
|
||||
for (size_t elem_i = start; elem_i < end; ++elem_i) {
|
||||
@ -59,8 +59,8 @@ int main(int argc, char **argv) {
|
||||
// check size
|
||||
{
|
||||
auto accessor = skiplist.access();
|
||||
permanent_assert(accessor.size() == 0,
|
||||
"Size should be 0, but size is " << accessor.size());
|
||||
permanent_assert(accessor.size() == 0, "Size should be 0, but size is "
|
||||
<< accessor.size());
|
||||
}
|
||||
|
||||
// check count
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "common.h"
|
||||
#include "common.hpp"
|
||||
|
||||
constexpr size_t THREADS_NO = std::min(max_no_threads, 8);
|
||||
constexpr size_t op_per_thread = 1e5;
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "common.h"
|
||||
#include "common.hpp"
|
||||
|
||||
constexpr size_t THREADS_NO = std::min(max_no_threads, 8);
|
||||
constexpr size_t key_range = 1e5;
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "common.h"
|
||||
#include "common.hpp"
|
||||
|
||||
constexpr size_t THREADS_NO = std::min(max_no_threads, 8);
|
||||
constexpr size_t key_range = 1e4;
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "common.h"
|
||||
#include "common.hpp"
|
||||
|
||||
constexpr size_t THREADS_NO = std::min(max_no_threads, 8);
|
||||
constexpr size_t key_range = 1e4;
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include "common.h"
|
||||
#include "common.hpp"
|
||||
|
||||
constexpr size_t THREADS_NO = std::min(max_no_threads, 8);
|
||||
constexpr size_t key_range = 1e5;
|
||||
|
Loading…
Reference in New Issue
Block a user