2525
2626namespace ripple {
2727
28+ struct Gap
29+ {
30+ uint64_t const page;
31+ SLE::pointer node;
32+ uint64_t const nextPage;
33+ SLE::pointer next;
34+ };
35+
2836std::optional<std::uint64_t >
2937ApplyView::dirAdd (
3038 bool preserveOrder,
3139 Keylet const & directory,
3240 uint256 const & key,
3341 std::function<void (std::shared_ptr<SLE> const &)> const & describe)
3442{
35- auto root = peek (directory);
43+ auto createRoot =
44+ [this ](
45+ Keylet const & directory,
46+ uint256 const & key,
47+ std::function<void (std::shared_ptr<SLE> const &)> const & describe) {
48+ auto newRoot = std::make_shared<SLE>(directory);
49+ newRoot->setFieldH256 (sfRootIndex, directory.key );
50+ describe (newRoot);
3651
37- if (!root)
38- {
39- // No root, make it.
40- root = std::make_shared<SLE>(directory);
41- root->setFieldH256 (sfRootIndex, directory.key );
42- describe (root);
52+ STVector256 v;
53+ v.push_back (key);
54+ newRoot->setFieldV256 (sfIndexes, v);
4355
44- STVector256 v ;
45- v. push_back (key) ;
46- root-> setFieldV256 (sfIndexes, v) ;
56+ insert (newRoot) ;
57+ return std:: uint64_t { 0 } ;
58+ } ;
4759
48- insert (root);
49- return std::uint64_t {0 };
50- }
60+ auto findPreviousPage = [this ](Keylet const & directory, SLE::ref start) {
61+ std::uint64_t page = start->getFieldU64 (sfIndexPrevious);
5162
52- std:: uint64_t page = root-> getFieldU64 (sfIndexPrevious) ;
63+ auto node = start ;
5364
54- auto node = root;
55-
56- if (page)
57- {
58- node = peek (keylet::page (directory, page));
59- if (!node)
60- LogicError (" Directory chain: root back-pointer broken." );
61- }
62-
63- auto indexes = node->getFieldV256 (sfIndexes);
65+ if (page)
66+ {
67+ node = peek (keylet::page (directory, page));
68+ if (!node)
69+ LogicError (" Directory chain: root back-pointer broken." );
70+ }
6471
65- // If there's space, we use it:
66- if (indexes.size () < dirNodeMaxEntries)
67- {
72+ auto indexes = node->getFieldV256 (sfIndexes);
73+ return std::make_tuple (page, node, indexes);
74+ };
75+ auto insertKey = [this ](
76+ SLE::ref node,
77+ std::uint64_t page,
78+ bool preserveOrder,
79+ STVector256& indexes,
80+ uint256 const & key) {
6881 if (preserveOrder)
6982 {
7083 if (std::find (indexes.begin (), indexes.end (), key) != indexes.end ())
@@ -90,36 +103,106 @@ ApplyView::dirAdd(
90103 node->setFieldV256 (sfIndexes, indexes);
91104 update (node);
92105 return page;
93- }
106+ };
107+ auto insertPage =
108+ [this ](
109+ std::uint64_t page,
110+ SLE::pointer node,
111+ std::uint64_t nextPage,
112+ SLE::ref next,
113+ uint256 const & key,
114+ STVector256& indexes,
115+ Keylet const & directory,
116+ std::function<void (std::shared_ptr<SLE> const &)> const & describe)
117+ -> std::optional<std::uint64_t > {
118+ // Check whether we're out of pages.
119+ if (++page >= dirNodeMaxPages)
120+ {
121+ return std::nullopt ;
122+ }
94123
95- // Check whether we're out of pages.
96- if (++page >= dirNodeMaxPages)
97- return std::nullopt ;
124+ // We are about to create a new node; we'll link it to
125+ // the chain first:
126+ node->setFieldU64 (sfIndexNext, page);
127+ update (node);
98128
99- // We are about to create a new node; we'll link it to
100- // the chain first:
101- node->setFieldU64 (sfIndexNext, page);
102- update (node);
129+ next->setFieldU64 (sfIndexPrevious, page);
130+ update (next);
103131
104- root->setFieldU64 (sfIndexPrevious, page);
105- update (root);
132+ // Insert the new key:
133+ indexes.clear ();
134+ indexes.push_back (key);
106135
107- // Insert the new key:
108- indexes. clear ( );
109- indexes. push_back (key );
136+ node = std::make_shared<SLE>( keylet::page (directory, page));
137+ node-> setFieldH256 (sfRootIndex, directory. key );
138+ node-> setFieldV256 (sfIndexes, indexes );
110139
111- node = std::make_shared<SLE>(keylet::page (directory, page));
112- node->setFieldH256 (sfRootIndex, directory.key );
113- node->setFieldV256 (sfIndexes, indexes);
140+ // Save some space by not specifying the value 0 since
141+ // it's the default.
142+ if (page != 1 )
143+ node->setFieldU64 (sfIndexPrevious, page - 1 );
144+ if (nextPage)
145+ node->setFieldU64 (sfIndexNext, nextPage);
146+ describe (node);
147+ insert (node);
148+
149+ return page;
150+ };
114151
115- // Save some space by not specifying the value 0 since
116- // it's the default.
117- if (page != 1 )
118- node->setFieldU64 (sfIndexPrevious, page - 1 );
119- describe (node);
120- insert (node);
152+ auto const root = peek (directory);
153+
154+ if (!root)
155+ {
156+ // No root, make it.
157+ return createRoot (directory, key, describe);
158+ }
159+
160+ auto [page, node, indexes] = findPreviousPage (directory, root);
161+
162+ if (rules ().enabled (featureDefragDirectories))
163+ {
164+ // If there are more nodes than just the root, and there's no space in
165+ // the last one, walk backwards to find one with space, or to find one
166+ // missing.
167+ std::optional<Gap> gapPages;
168+ while (page && indexes.size () >= dirNodeMaxEntries)
169+ {
170+ // Find a page with space, or a gap in pages.
171+ auto [prevPage, prevNode, prevIndexes] =
172+ findPreviousPage (directory, node);
173+ if (!gapPages && prevPage != page - 1 )
174+ gapPages.emplace (prevPage, prevNode, page, node);
175+ page = prevPage;
176+ node = prevNode;
177+ indexes = prevIndexes;
178+ }
179+ // We looped through all the pages back to the root.
180+ if (!page)
181+ {
182+ // If we found a gap, use it.
183+ if (gapPages)
184+ {
185+ return insertPage (
186+ gapPages->page ,
187+ gapPages->node ,
188+ gapPages->nextPage ,
189+ gapPages->node ,
190+ key,
191+ indexes,
192+ directory,
193+ describe);
194+ }
195+ std::tie (page, node, indexes) = findPreviousPage (directory, root);
196+ }
197+ }
198+
199+ // If there's space, we use it:
200+ if (indexes.size () < dirNodeMaxEntries)
201+ {
202+ return insertKey (node, page, preserveOrder, indexes, key);
203+ }
121204
122- return page;
205+ return insertPage ( page, node, 0 , root, key, indexes, directory, describe) ;
123206}
124207
125208bool
0 commit comments