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