Const iterator works.
Reviewers: florijan, mislav.bradac Reviewed By: mislav.bradac Subscribers: buda, pullbot Differential Revision: https://phabricator.memgraph.io/D886
This commit is contained in:
parent
f10380a861
commit
cce6db3442
@ -75,5 +75,5 @@ class AccessorBase {
|
|||||||
size_t size() const { return accessor.size(); }
|
size_t size() const { return accessor.size(); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
typename list::Accessor accessor;
|
typename list::template Accessor<SkipList<T>> accessor;
|
||||||
};
|
};
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
#include "utils/assert.hpp"
|
#include "utils/assert.hpp"
|
||||||
#include "utils/crtp.hpp"
|
#include "utils/crtp.hpp"
|
||||||
@ -451,10 +452,11 @@ class SkipList : private Lockable<lock_t> {
|
|||||||
|
|
||||||
friend class Accessor;
|
friend class Accessor;
|
||||||
|
|
||||||
|
template <typename TSkipList>
|
||||||
class Accessor {
|
class Accessor {
|
||||||
friend class SkipList;
|
friend class SkipList;
|
||||||
|
|
||||||
Accessor(SkipList *skiplist)
|
Accessor(TSkipList *skiplist)
|
||||||
: skiplist(skiplist), status_(skiplist->gc.CreateNewAccessor()) {
|
: skiplist(skiplist), status_(skiplist->gc.CreateNewAccessor()) {
|
||||||
debug_assert(skiplist != nullptr, "Skiplist is nullptr.");
|
debug_assert(skiplist != nullptr, "Skiplist is nullptr.");
|
||||||
}
|
}
|
||||||
@ -473,15 +475,37 @@ class SkipList : private Lockable<lock_t> {
|
|||||||
status_.alive_ = false;
|
status_.alive_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Iterator begin() { return skiplist->begin(); }
|
// TODO(dgleich): Remove after C++17 compile flag.
|
||||||
|
template <class B>
|
||||||
|
struct negation : std::integral_constant<bool, !bool(B::value)> {};
|
||||||
|
|
||||||
ConstIterator begin() const { return skiplist->cbegin(); }
|
template <typename TIsConst = TSkipList>
|
||||||
|
Iterator begin(typename std::enable_if<
|
||||||
|
negation<std::is_const<TIsConst>>::value>::type * = 0) {
|
||||||
|
return skiplist->begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename TIsConst = TSkipList>
|
||||||
|
ConstIterator begin(
|
||||||
|
typename std::enable_if<std::is_const<TIsConst>::value>::type * =
|
||||||
|
0) const {
|
||||||
|
return skiplist->cbegin();
|
||||||
|
}
|
||||||
|
|
||||||
ConstIterator cbegin() const { return skiplist->cbegin(); }
|
ConstIterator cbegin() const { return skiplist->cbegin(); }
|
||||||
|
|
||||||
Iterator end() { return skiplist->end(); }
|
template <typename TIsConst = TSkipList>
|
||||||
|
Iterator end(typename std::enable_if<
|
||||||
|
negation<std::is_const<TIsConst>>::value>::type * = 0) {
|
||||||
|
return skiplist->end();
|
||||||
|
}
|
||||||
|
|
||||||
ConstIterator end() const { return skiplist->cend(); }
|
template <typename TIsConst = TSkipList>
|
||||||
|
ConstIterator end(
|
||||||
|
typename std::enable_if<std::is_const<TIsConst>::value>::type * =
|
||||||
|
0) const {
|
||||||
|
return skiplist->cend();
|
||||||
|
}
|
||||||
|
|
||||||
ConstIterator cend() const { return skiplist->cend(); }
|
ConstIterator cend() const { return skiplist->cend(); }
|
||||||
|
|
||||||
@ -526,7 +550,7 @@ class SkipList : private Lockable<lock_t> {
|
|||||||
*/
|
*/
|
||||||
template <class TItem>
|
template <class TItem>
|
||||||
Iterator find_or_larger(const TItem &item) {
|
Iterator find_or_larger(const TItem &item) {
|
||||||
return skiplist->find_or_larger<Iterator, TItem>(item);
|
return skiplist->template find_or_larger<Iterator, TItem>(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -669,14 +693,16 @@ class SkipList : private Lockable<lock_t> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
SkipList *skiplist;
|
TSkipList *skiplist;
|
||||||
Node *preds[H], *succs[H];
|
Node *preds[H], *succs[H];
|
||||||
typename SkipListGC<Node>::AccessorStatus &status_;
|
typename SkipListGC<Node>::AccessorStatus &status_;
|
||||||
};
|
};
|
||||||
|
|
||||||
Accessor access() { return Accessor(this); }
|
Accessor<SkipList> access() { return Accessor<SkipList>(this); }
|
||||||
|
|
||||||
const Accessor access() const { return Accessor(this); }
|
Accessor<const SkipList> caccess() const {
|
||||||
|
return Accessor<const SkipList>(this);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
using guard_t = std::unique_lock<lock_t>;
|
using guard_t = std::unique_lock<lock_t>;
|
||||||
@ -1022,5 +1048,5 @@ class SkipList : private Lockable<lock_t> {
|
|||||||
*/
|
*/
|
||||||
std::atomic<size_t> count{0};
|
std::atomic<size_t> count{0};
|
||||||
Node *header;
|
Node *header;
|
||||||
SkipListGC<Node> gc;
|
mutable SkipListGC<Node> gc;
|
||||||
};
|
};
|
||||||
|
@ -17,7 +17,7 @@ namespace IndexUtils {
|
|||||||
* iterating, i.e. it allows us to iterate over the suffix of some skiplist
|
* iterating, i.e. it allows us to iterate over the suffix of some skiplist
|
||||||
* hence the name SkipListSuffix.
|
* hence the name SkipListSuffix.
|
||||||
*/
|
*/
|
||||||
template <class TIterator, class TValue>
|
template <class TIterator, class TValue, typename TAccessor>
|
||||||
class SkipListSuffix {
|
class SkipListSuffix {
|
||||||
public:
|
public:
|
||||||
class Iterator {
|
class Iterator {
|
||||||
@ -39,15 +39,16 @@ class SkipListSuffix {
|
|||||||
TIterator current_;
|
TIterator current_;
|
||||||
};
|
};
|
||||||
|
|
||||||
SkipListSuffix(const TIterator begin,
|
SkipListSuffix(
|
||||||
typename SkipList<TValue>::Accessor &&accessor)
|
const TIterator begin,
|
||||||
|
typename SkipList<TValue>::template Accessor<TAccessor> &&accessor)
|
||||||
: begin_(begin), accessor_(std::move(accessor)) {}
|
: begin_(begin), accessor_(std::move(accessor)) {}
|
||||||
|
|
||||||
Iterator begin() const { return Iterator(begin_); }
|
Iterator begin() const { return Iterator(begin_); }
|
||||||
Iterator end() { return Iterator(accessor_.end()); }
|
Iterator end() { return Iterator(accessor_.end()); }
|
||||||
|
|
||||||
TIterator begin_;
|
TIterator begin_;
|
||||||
typename SkipList<TValue>::Accessor accessor_;
|
typename SkipList<TValue>::template Accessor<TAccessor> accessor_;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -67,20 +68,23 @@ class SkipListSuffix {
|
|||||||
* ignored).
|
* ignored).
|
||||||
* @Tparam TIndexEntry - index entry inside skiplist
|
* @Tparam TIndexEntry - index entry inside skiplist
|
||||||
* @Tparam TRecord - type of record under index (edge/vertex usually.)
|
* @Tparam TRecord - type of record under index (edge/vertex usually.)
|
||||||
|
* @Tparam TAccessor - type of accessor to use (const skiplist/non const
|
||||||
|
* skiplist).
|
||||||
* @return iterable collection of distinct vlist records<TRecord> for which
|
* @return iterable collection of distinct vlist records<TRecord> for which
|
||||||
* exists function evaluates as true
|
* exists function evaluates as true
|
||||||
*/
|
*/
|
||||||
template <class TIterator, class TIndexEntry, class TRecord>
|
template <class TIterator, class TIndexEntry, class TRecord, typename TAccessor>
|
||||||
static auto GetVlists(
|
static auto GetVlists(
|
||||||
typename SkipList<TIndexEntry>::Accessor &&skiplist_accessor,
|
typename SkipList<TIndexEntry>::template Accessor<TAccessor>
|
||||||
|
&&skiplist_accessor,
|
||||||
TIterator begin,
|
TIterator begin,
|
||||||
const std::function<bool(const TIndexEntry &entry)> predicate,
|
const std::function<bool(const TIndexEntry &entry)> predicate,
|
||||||
const tx::Transaction &t,
|
const tx::Transaction &t,
|
||||||
const std::function<bool(const TIndexEntry &, const TRecord *)> exists,
|
const std::function<bool(const TIndexEntry &, const TRecord *)> exists,
|
||||||
bool current_state = false) {
|
bool current_state = false) {
|
||||||
TIndexEntry *prev = nullptr;
|
TIndexEntry *prev = nullptr;
|
||||||
auto range =
|
auto range = iter::takewhile(
|
||||||
iter::takewhile(predicate, SkipListSuffix<TIterator, TIndexEntry>(
|
predicate, SkipListSuffix<TIterator, TIndexEntry, TAccessor>(
|
||||||
begin, std::move(skiplist_accessor)));
|
begin, std::move(skiplist_accessor)));
|
||||||
auto filtered = iter::filter(
|
auto filtered = iter::filter(
|
||||||
[&t, exists, prev, current_state](TIndexEntry &entry) mutable {
|
[&t, exists, prev, current_state](TIndexEntry &entry) mutable {
|
||||||
@ -176,4 +180,4 @@ static void Refresh(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}; // IndexUtils
|
}; // namespace IndexUtils
|
||||||
|
@ -172,7 +172,7 @@ class LabelPropertyIndex {
|
|||||||
auto access = GetKeyStorage(key)->access();
|
auto access = GetKeyStorage(key)->access();
|
||||||
auto begin = access.begin();
|
auto begin = access.begin();
|
||||||
return IndexUtils::GetVlists<typename SkipList<IndexEntry>::Iterator,
|
return IndexUtils::GetVlists<typename SkipList<IndexEntry>::Iterator,
|
||||||
IndexEntry, Vertex>(
|
IndexEntry, Vertex, SkipList<IndexEntry>>(
|
||||||
std::move(access), begin, [](const IndexEntry &) { return true; }, t,
|
std::move(access), begin, [](const IndexEntry &) { return true; }, t,
|
||||||
[key](const IndexEntry &entry, const Vertex *const vertex) {
|
[key](const IndexEntry &entry, const Vertex *const vertex) {
|
||||||
return LabelPropertyIndex::Exists(key, entry.value_, vertex);
|
return LabelPropertyIndex::Exists(key, entry.value_, vertex);
|
||||||
@ -269,8 +269,7 @@ class LabelPropertyIndex {
|
|||||||
auto access = GetKeyStorage(key)->access();
|
auto access = GetKeyStorage(key)->access();
|
||||||
|
|
||||||
// create the iterator startpoint based on the lower bound
|
// create the iterator startpoint based on the lower bound
|
||||||
auto start_iter = lower
|
auto start_iter = lower ? access.find_or_larger(make_index_bound(
|
||||||
? access.find_or_larger(make_index_bound(
|
|
||||||
lower, lower.value().IsInclusive()))
|
lower, lower.value().IsInclusive()))
|
||||||
: access.begin();
|
: access.begin();
|
||||||
|
|
||||||
@ -394,8 +393,7 @@ class LabelPropertyIndex {
|
|||||||
*/
|
*/
|
||||||
std::vector<Key> Keys() {
|
std::vector<Key> Keys() {
|
||||||
std::vector<Key> keys;
|
std::vector<Key> keys;
|
||||||
for (auto &kv : indices_.access())
|
for (auto &kv : indices_.access()) keys.push_back(kv.first);
|
||||||
keys.push_back(kv.first);
|
|
||||||
return keys;
|
return keys;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
47
tests/unit/skiplist_access.cpp
Normal file
47
tests/unit/skiplist_access.cpp
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#include "gmock/gmock.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "data_structures/concurrent/skiplist.hpp"
|
||||||
|
|
||||||
|
TEST(SkipList, Access) {
|
||||||
|
SkipList<int> input;
|
||||||
|
{
|
||||||
|
auto accessor = input.access();
|
||||||
|
accessor.insert(1);
|
||||||
|
accessor.insert(2);
|
||||||
|
accessor.insert(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto accessor = input.access();
|
||||||
|
std::vector<int> results;
|
||||||
|
for (auto it = accessor.begin(); it != accessor.end(); ++it)
|
||||||
|
results.push_back(*it);
|
||||||
|
|
||||||
|
EXPECT_THAT(results, testing::ElementsAre(1, 2, 3));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SkipList, ConstAccess) {
|
||||||
|
SkipList<int> input;
|
||||||
|
{
|
||||||
|
auto accessor = input.access();
|
||||||
|
accessor.insert(1);
|
||||||
|
accessor.insert(2);
|
||||||
|
accessor.insert(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
const SkipList<int> &skiplist = input;
|
||||||
|
auto accessor = skiplist.caccess();
|
||||||
|
|
||||||
|
std::vector<int> results;
|
||||||
|
for (auto it = accessor.begin(); it != accessor.end(); ++it)
|
||||||
|
results.push_back(*it);
|
||||||
|
|
||||||
|
EXPECT_THAT(results, testing::ElementsAre(1, 2, 3));
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
::testing::InitGoogleTest(&argc, argv);
|
||||||
|
return RUN_ALL_TESTS();
|
||||||
|
}
|
@ -17,8 +17,9 @@ int Count(TIterable &collection) {
|
|||||||
TEST(SkipListSuffix, EmptyRange) {
|
TEST(SkipListSuffix, EmptyRange) {
|
||||||
SkipList<int> V;
|
SkipList<int> V;
|
||||||
auto access = V.access();
|
auto access = V.access();
|
||||||
auto r1 = IndexUtils::SkipListSuffix<typename SkipList<int>::Iterator, int>(
|
auto r1 = IndexUtils::SkipListSuffix<typename SkipList<int>::Iterator, int,
|
||||||
access.begin(), std::move(access));
|
SkipList<int>>(access.begin(),
|
||||||
|
std::move(access));
|
||||||
EXPECT_EQ(Count(r1), 0);
|
EXPECT_EQ(Count(r1), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,8 +29,9 @@ TEST(SkipListSuffix, NonEmptyRange) {
|
|||||||
access.insert(1);
|
access.insert(1);
|
||||||
access.insert(5);
|
access.insert(5);
|
||||||
access.insert(3);
|
access.insert(3);
|
||||||
auto r1 = IndexUtils::SkipListSuffix<typename SkipList<int>::Iterator, int>(
|
auto r1 = IndexUtils::SkipListSuffix<typename SkipList<int>::Iterator, int,
|
||||||
access.begin(), std::move(access));
|
SkipList<int>>(access.begin(),
|
||||||
|
std::move(access));
|
||||||
EXPECT_EQ(Count(r1), 3);
|
EXPECT_EQ(Count(r1), 3);
|
||||||
auto iter = r1.begin();
|
auto iter = r1.begin();
|
||||||
EXPECT_EQ(*iter, 1);
|
EXPECT_EQ(*iter, 1);
|
||||||
|
Loading…
Reference in New Issue
Block a user