Skip to content

Commit

Permalink
Storage iterators are bidirectional ones
Browse files Browse the repository at this point in the history
Signed-off-by: Anthony Fieroni <[email protected]>
  • Loading branch information
bvbfan committed Mar 22, 2021
1 parent 54fb391 commit 99d45cc
Show file tree
Hide file tree
Showing 4 changed files with 167 additions and 59 deletions.
1 change: 1 addition & 0 deletions src/dbwrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ CDBIterator::~CDBIterator() { delete piter; }
bool CDBIterator::Valid() const { return piter->Valid(); }
void CDBIterator::SeekToFirst() { piter->SeekToFirst(); }
void CDBIterator::Next() { piter->Next(); }
void CDBIterator::Prev() { piter->Prev(); }

namespace dbwrapper_private {

Expand Down
1 change: 1 addition & 0 deletions src/dbwrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ class CDBIterator
}

void Next();
void Prev();

template<typename K> bool GetKey(K& key) {
leveldb::Slice slKey = piter->key();
Expand Down
115 changes: 59 additions & 56 deletions src/flushablestorage.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class CStorageKVIterator {
virtual ~CStorageKVIterator() = default;
virtual void Seek(const TBytes& key) = 0;
virtual void Next() = 0;
virtual void Prev() = 0;
virtual bool Valid() = 0;
virtual TBytes Key() = 0;
virtual TBytes Value() = 0;
Expand Down Expand Up @@ -95,7 +96,12 @@ class CStorageLevelDBIterator : public CStorageKVIterator {
void Seek(const TBytes& key) override {
it->Seek(refTBytes(key)); // lower_bound in fact
}
void Next() override { it->Next(); }
void Next() override {
it->Next();
}
void Prev() override {
it->Prev();
}
bool Valid() override {
return it->Valid();
}
Expand Down Expand Up @@ -160,80 +166,77 @@ class CStorageLevelDB : public CStorageKV {
// Flushable Key-Value Storage Iterator
class CFlushableStorageKVIterator : public CStorageKVIterator {
public:
explicit CFlushableStorageKVIterator(std::unique_ptr<CStorageKVIterator>&& pIt_, MapKV& map_) : pIt{std::move(pIt_)}, map(map_) {
inited = parentOk = mapOk = useMap = false;
explicit CFlushableStorageKVIterator(std::unique_ptr<CStorageKVIterator>&& pIt, MapKV& map) : map(map), pIt(std::move(pIt)) {
itState = Invalid;
}
CFlushableStorageKVIterator(const CFlushableStorageKVIterator&) = delete;
~CFlushableStorageKVIterator() override = default;

void Seek(const TBytes& key) override {
prevKey.clear();
pIt->Seek(key);
parentOk = pIt->Valid();
mIt = map.lower_bound(key);
mapOk = mIt != map.end();
inited = true;
Next();
mIt = Advance(map.lower_bound(key), map.end(), std::greater<TBytes>{}, {});
}
void Next() override {
if (!inited) throw std::runtime_error("Iterator wasn't inited.");

if (!prevKey.empty()) {
useMap ? nextMap() : nextParent();
assert(Valid());
mIt = Advance(mIt, map.end(), std::greater<TBytes>{}, Key());
}
void Prev() override {
assert(Valid());
auto tmp = mIt;
if (tmp != map.end()) {
++tmp;
}

while (mapOk || parentOk) {
if (mapOk) {
while (mapOk && (!parentOk || mIt->first <= pIt->Key())) {
bool ok = false;

if (mIt->second) {
ok = prevKey.empty() || mIt->first > prevKey;
} else {
prevKey = mIt->first;
}
if (ok) {
useMap = true;
prevKey = mIt->first;
return;
}
nextMap();
}
}
if (parentOk) {
if (prevKey.empty() || pIt->Key() > prevKey) {
useMap = false;
prevKey = pIt->Key();
return;
}
nextParent();
}
auto it = std::reverse_iterator<decltype(tmp)>(tmp);
auto end = Advance(it, map.rend(), std::less<TBytes>{}, Key());
if (!map.empty() && mIt == map.end()) {
--mIt;
}
std::advance(mIt, -std::distance(it, end));
}
bool Valid() override {
return mapOk || parentOk;
return itState != Invalid;
}
TBytes Key() override {
return useMap ? mIt->first : pIt->Key();
assert(Valid());
return itState == Map ? mIt->first : pIt->Key();
}
TBytes Value() override {
return useMap ? *mIt->second : pIt->Value();
assert(Valid());
return itState == Map ? *mIt->second : pIt->Value();
}
private:
void nextMap() {
mapOk = mapOk && ++mIt != map.end();
template<typename TIterator, typename Compare>
TIterator Advance(TIterator it, TIterator end, Compare comp, TBytes prevKey) {

while (it != end || pIt->Valid()) {
while (it != end && (!pIt->Valid() || !comp(it->first, pIt->Key()))) {
if (it->second && (prevKey.empty() || comp(it->first, prevKey))) {
itState = Map;
return it;
}
++it;
}
if (pIt->Valid()) {
if (prevKey.empty() || comp(pIt->Key(), prevKey)) {
itState = Parent;
return it;
}
NextParent(it);
}
}
itState = Invalid;
return it;
}
void nextParent() {
parentOk = parentOk && (pIt->Next(), pIt->Valid());
void NextParent(MapKV::const_iterator&) {
pIt->Next();
}
void NextParent(std::reverse_iterator<MapKV::const_iterator>&) {
pIt->Prev();
}
bool inited;
bool useMap;
std::unique_ptr<CStorageKVIterator> pIt;
bool parentOk;
const MapKV& map;
MapKV::const_iterator mIt;
bool mapOk;
TBytes prevKey;
std::unique_ptr<CStorageKVIterator> pIt;
enum IteratorState { Invalid, Map, Parent } itState;
};

// Flushable Key-Value Storage
Expand Down Expand Up @@ -387,9 +390,9 @@ class CStorageView {
return it;
}

template<typename DbKeyType>
static bool ReadDbKey(const TBytes& dbKey, DbKeyType& key) {
DbKeyType tkey;
template<typename KeyType>
static bool ReadDbKey(const TBytes& dbKey, KeyType& key) {
KeyType tkey;
if (BytesToDbType(dbKey, tkey) && tkey.first == key.first) {
key = tkey;
return true;
Expand Down
109 changes: 106 additions & 3 deletions src/test/storage_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ struct TestBackward {
const unsigned char TestBackward::prefix = 'B';


BOOST_AUTO_TEST_CASE(for_each_order)
BOOST_AUTO_TEST_CASE(ForEachTest)
{
{
pcustomcsview->WriteBy<TestForward>(TestForward{0}, 1);
Expand All @@ -309,7 +309,6 @@ BOOST_AUTO_TEST_CASE(for_each_order)

int test = 1;
pcustomcsview->ForEach<TestForward, TestForward, int>([&] (TestForward const & key, int value) {
// printf("%ld : %d\n", key.n, value);
BOOST_CHECK(value == test);
++test;
return true;
Expand All @@ -325,14 +324,118 @@ BOOST_AUTO_TEST_CASE(for_each_order)

int test = 6;
pcustomcsview->ForEach<TestBackward, TestBackward, int>([&] (TestBackward const & key, int value) {
// printf("%ld : %d\n", key.n, value);
BOOST_CHECK(value == test);
--test;
return true;
}, TestBackward{ (uint32_t) -1 });
}
}

BOOST_AUTO_TEST_CASE(LowerBoundTest)
{
{
CCustomCSView view(*pcustomcsview);
view.WriteBy<TestForward>(TestForward{0}, 1);
view.WriteBy<TestForward>(TestForward{1}, 2);
view.WriteBy<TestForward>(TestForward{255}, 3);
view.WriteBy<TestForward>(TestForward{256}, 4);
view.WriteBy<TestForward>(TestForward{((uint16_t)-1) -1}, 5);
view.WriteBy<TestForward>(TestForward{(uint16_t)-1}, 6);
view.WriteBy<TestForward>(TestForward{((uint32_t)-1) -1}, 7);
view.WriteBy<TestForward>(TestForward{((uint32_t)-1)}, 8);

int test = 4;
auto dbKey = CStorageView::MakeDbKey<TestForward>(TestForward{256});
auto it = view.LowerBound(dbKey);
while (it->Valid() && CStorageView::ReadDbKey(it->Key(), dbKey)) {
BOOST_CHECK(dbKey.second.n >= 256);
BOOST_CHECK(CStorageView::ReadValue<int>(it->Value()) == test);
test++;
it->Next();
}
BOOST_CHECK(test == 9);
// go backward
test--;
it = view.LowerBound(dbKey);
while (it->Valid() && CStorageView::ReadDbKey(it->Key(), dbKey)) {
BOOST_CHECK_EQUAL(CStorageView::ReadValue<int>(it->Value()), test);
test--;
it->Prev();
}
BOOST_CHECK(test == 0);

CCustomCSView view2(view);
view2.WriteBy<TestForward>(TestForward{256}, 8);
view2.WriteBy<TestForward>(TestForward{(uint16_t)-1}, 11);

dbKey = CStorageView::MakeDbKey<TestForward>(TestForward{((uint16_t)-1) -1});
it = view2.LowerBound(dbKey);
BOOST_CHECK(it->Valid());
it->Prev();
BOOST_CHECK(it->Valid());
BOOST_CHECK(CStorageView::ReadValue<int>(it->Value()) == 8);
it->Next();
BOOST_CHECK(it->Valid());
BOOST_CHECK(CStorageView::ReadValue<int>(it->Value()) == 5);
it->Next();
BOOST_CHECK(it->Valid());
BOOST_CHECK(CStorageView::ReadValue<int>(it->Value()) == 11);
}

{
CCustomCSView view(*pcustomcsview);
view.WriteBy<TestBackward>(TestBackward{0}, 1);
view.WriteBy<TestBackward>(TestBackward{1}, 2);
view.WriteBy<TestBackward>(TestBackward{255}, 3);
view.WriteBy<TestBackward>(TestBackward{256}, 4);

auto dbKey = CStorageView::MakeDbKey<TestBackward>(TestBackward{254});
auto it = view.LowerBound(dbKey);
int test = 2;
// go forward (prev in backward)
while (it->Valid() && CStorageView::ReadDbKey(it->Key(), dbKey)) {
BOOST_CHECK(CStorageView::ReadValue<int>(it->Value()) == test);
test++;
it->Prev();
}
BOOST_CHECK(test == 5);

CCustomCSView view2(view);
view2.WriteBy<TestBackward>(TestBackward{256}, 5);

test = 5;
dbKey = CStorageView::MakeDbKey<TestBackward>(TestBackward{257});
it = view2.LowerBound(dbKey);
while (it->Valid() && CStorageView::ReadDbKey(it->Key(), dbKey)) {
BOOST_CHECK(CStorageView::ReadValue<int>(it->Value()) == test);
test == 5 ? test-=2 : test--;
it->Next();
}
BOOST_CHECK(test == 0);

dbKey = CStorageView::MakeDbKey<TestBackward>(TestBackward{254});
it = view2.LowerBound(dbKey);
test = 2;
// go forward (prev in backward)
while (it->Valid() && CStorageView::ReadDbKey(it->Key(), dbKey)) {
BOOST_CHECK(CStorageView::ReadValue<int>(it->Value()) == test);
test == 3 ? test+=2 : test++;
it->Prev();
}
BOOST_CHECK(test == 6);

dbKey = CStorageView::MakeDbKey<TestBackward>(TestBackward{255});
it = view2.LowerBound(dbKey);
BOOST_CHECK(it->Valid());
it->Prev();
BOOST_CHECK(it->Valid());
BOOST_CHECK(CStorageView::ReadValue<int>(it->Value()) == 5);
it->Next();
BOOST_CHECK(it->Valid());
BOOST_CHECK(CStorageView::ReadValue<int>(it->Value()) == 3);
}
}

BOOST_AUTO_TEST_CASE(AccountHistoryDescOrderTest)
{
CCustomCSView view(*pcustomcsview);
Expand Down

0 comments on commit 99d45cc

Please sign in to comment.