diff --git a/Builds/VisualStudio2013/RippleD.vcxproj b/Builds/VisualStudio2013/RippleD.vcxproj index 4dd0314d4c..90f82f345d 100644 --- a/Builds/VisualStudio2013/RippleD.vcxproj +++ b/Builds/VisualStudio2013/RippleD.vcxproj @@ -2299,6 +2299,8 @@ + + True True @@ -2319,6 +2321,10 @@ True True + + True + True + True True @@ -2349,6 +2355,10 @@ + + True + True + diff --git a/Builds/VisualStudio2013/RippleD.vcxproj.filters b/Builds/VisualStudio2013/RippleD.vcxproj.filters index a1b9facb28..2136ad6e78 100644 --- a/Builds/VisualStudio2013/RippleD.vcxproj.filters +++ b/Builds/VisualStudio2013/RippleD.vcxproj.filters @@ -3036,6 +3036,9 @@ ripple\ledger\detail + + ripple\ledger + ripple\ledger\impl @@ -3051,6 +3054,9 @@ ripple\ledger\impl + + ripple\ledger\impl + ripple\ledger\impl @@ -3081,6 +3087,9 @@ ripple\ledger + + ripple\ledger\tests + ripple\ledger\tests diff --git a/src/ripple/ledger/Directory.h b/src/ripple/ledger/Directory.h new file mode 100644 index 0000000000..118677c4dd --- /dev/null +++ b/src/ripple/ledger/Directory.h @@ -0,0 +1,135 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2015 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#ifndef RIPPLE_LEDGER_DIR_H_INCLUDED +#define RIPPLE_LEDGER_DIR_H_INCLUDED + +#include +#include + +namespace ripple { + +class Dir +{ +private: + ReadView const* view_ = nullptr; + Keylet root_; + + // memoization + std::shared_ptr sle_; + STVector256 const* indexes_ = nullptr; + +public: + class const_iterator; + using value_type = std::shared_ptr; + + Dir(ReadView const&, Keylet const&); + + const_iterator + begin() const; + + const_iterator + end() const; + + const_iterator + find(uint256 const& page_key, uint256 const& sle_key) const; +}; + +class Dir::const_iterator +{ +public: + using value_type = + Dir::value_type; + using pointer = + value_type const*; + using reference = + value_type const&; + using difference_type = + std::ptrdiff_t; + using iterator_category = + std::forward_iterator_tag; + + const_iterator() = default; + + const_iterator& + operator=(const_iterator const& other); + + bool + operator==(const_iterator const& other) const; + + bool + operator!=(const_iterator const& other) const + { + return ! (*this == other); + } + + reference + operator*() const; + + pointer + operator->() const + { + return &**this; + } + + const_iterator& + operator++(); + + const_iterator + operator++(int); + + Keylet const& + page() const + { + return page_; + } + + uint256 + index() const + { + return index_; + } + +private: + friend class Dir; + + const_iterator(ReadView const& view, + Keylet const& root, + Keylet const& page) + : view_(&view) + , root_(root) + , page_(page) + { + } + + ReadView const* view_ = nullptr; + Keylet root_; + Keylet page_; + uint256 index_; + boost::optional mutable cache_; + + // memoization + std::shared_ptr sle_; + STVector256 const* indexes_ = nullptr; + std::vector::const_iterator it_; +}; + +} // ripple + +#endif diff --git a/src/ripple/ledger/impl/Directory.cpp b/src/ripple/ledger/impl/Directory.cpp new file mode 100644 index 0000000000..aa4590109f --- /dev/null +++ b/src/ripple/ledger/impl/Directory.cpp @@ -0,0 +1,177 @@ +//------------ ------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2015 Ripple Labs Inc. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include +#include + +namespace ripple { + +using const_iterator = Dir::const_iterator; + +Dir::Dir(ReadView const& view, + Keylet const& key) + : view_(&view) + , root_(key) + , sle_(view_->read(root_)) + +{ + if (sle_ != nullptr) + indexes_ = &sle_->getFieldV256(sfIndexes); +} + +auto +Dir::begin() const -> + const_iterator +{ + auto it = const_iterator(*view_, root_, root_); + if (sle_ != nullptr) + { + it.sle_ = sle_; + if (! indexes_->empty()) + { + it.indexes_ = indexes_; + it.it_ = std::begin(*indexes_); + it.index_ = *it.it_; + } + } + + return it; +} + +auto +Dir::end() const -> + const_iterator +{ + return const_iterator(*view_, root_, root_); +} + +const_iterator +Dir::find(uint256 const& page_key, uint256 const& sle_key) const +{ + if (sle_ == nullptr) + return end(); + + auto it = const_iterator(*view_, root_, keylet::page(page_key, 0)); + if (root_.key == page_key) + { + it.sle_ = sle_; + it.indexes_ = indexes_; + } + else + { + it.sle_ = view_->read(it.page_); + if (it.sle_ == nullptr) + return end(); + it.indexes_ = &it.sle_->getFieldV256(sfIndexes); + } + + it.it_ = std::find(std::begin(*it.indexes_), + std::end(*it.indexes_), sle_key); + if (it.it_ == std::end(*it.indexes_)) + return end(); + + it.index_ = *it.it_; + return it; +} + +const_iterator& +const_iterator::operator=(const_iterator const& other) +{ + if (this != &other) + { + view_ = other.view_; + root_ = other.root_; + page_ = other.page_; + index_ = other.index_; + cache_ = other.cache_; + sle_ = other.sle_; + indexes_ = other.indexes_; + it_ = other.it_; + } + return *this; +} + +bool +const_iterator::operator==(const_iterator const& other) const +{ + if (view_ == nullptr || other.view_ == nullptr) + return false; + + assert(view_ == other.view_ && root_.key == other.root_.key); + return page_.key == other.page_.key && index_ == other.index_; +} + +const_iterator::reference +const_iterator::operator*() const +{ + assert(index_ != beast::zero); + if (! cache_) + cache_ = view_->read(keylet::child(index_)); + return *cache_; +} + +const_iterator& +const_iterator::operator++() +{ + assert(index_ != beast::zero); + if (++it_ != std::end(*indexes_)) + { + index_ = *it_; + } + else + { + auto const next = + sle_->getFieldU64(sfIndexNext); + if (next == 0) + { + page_.key = root_.key; + index_ = beast::zero; + } + else + { + page_ = keylet::page(root_, next); + sle_ = view_->read(page_); + assert(sle_); + indexes_ = &sle_->getFieldV256(sfIndexes); + if (indexes_->empty()) + { + index_ = beast::zero; + } + else + { + it_ = std::begin(*indexes_); + index_ = *it_; + } + } + } + + cache_ = boost::none; + return *this; +} + +const_iterator +const_iterator::operator++(int) +{ + assert(index_ != beast::zero); + const_iterator tmp(*this); + ++(*this); + return tmp; +} + +} // ripple diff --git a/src/ripple/ledger/tests/Directory_test.cpp b/src/ripple/ledger/tests/Directory_test.cpp new file mode 100644 index 0000000000..5c5809c7ee --- /dev/null +++ b/src/ripple/ledger/tests/Directory_test.cpp @@ -0,0 +1,83 @@ +//------------------------------------------------------------------------------ +/* + This file is part of rippled: https://github.com/ripple/rippled + Copyright (c) 2012, 2013 Ripple Labs Inc. + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include +#include +#include + +namespace ripple { +namespace test { + +struct Directory_test : public beast::unit_test::suite +{ + void testDirectory() + { + using namespace jtx; + Env env(*this); + auto gw = Account("gw"); + auto USD = gw["USD"]; + + { + auto dir = Dir(*env.open(), + keylet::ownerDir(Account("alice"))); + expect(std::begin(dir) == std::end(dir)); + expect(std::end(dir) == dir.find(uint256(), uint256())); + } + + env.fund(XRP(10000), "alice", "bob", gw); + + auto i = 10; + for (; i <= 400; i += 10) + env(offer("alice", USD(i), XRP(10))); + env(offer("bob", USD(500), XRP(10))); + + { + auto dir = Dir(*env.open(), + keylet::ownerDir(Account("bob"))); + expect(std::begin(dir)->get()-> + getFieldAmount(sfTakerPays) == USD(500)); + } + + auto dir = Dir(*env.open(), + keylet::ownerDir(Account("alice"))); + i = 0; + for (auto const& e : dir) + expect(e->getFieldAmount(sfTakerPays) == USD(i += 10)); + + expect(std::begin(dir) != std::end(dir)); + expect(std::end(dir) == + dir.find(std::begin(dir).page().key, + uint256())); + expect(std::begin(dir) == + dir.find(std::begin(dir).page().key, + std::begin(dir).index())); + auto entry = std::next(std::begin(dir), 32); + auto it = dir.find(entry.page().key, entry.index()); + expect(it != std::end(dir)); + expect((*it)->getFieldAmount(sfTakerPays) == USD(330)); + } + + void run() override + { + testDirectory(); + } +}; + +BEAST_DEFINE_TESTSUITE(Directory,ledger,ripple); + +} // test +} // ripple diff --git a/src/ripple/unity/ledger.cpp b/src/ripple/unity/ledger.cpp index 4711aa44f4..9e9c4d14cb 100644 --- a/src/ripple/unity/ledger.cpp +++ b/src/ripple/unity/ledger.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -32,3 +33,4 @@ #include #include +#include