rippled
Loading...
Searching...
No Matches
ApplyView.cpp
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012, 2013 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#include <xrpl/basics/contract.h>
21#include <xrpl/beast/utility/instrumentation.h>
22#include <xrpl/ledger/ApplyView.h>
23#include <xrpl/protocol/Protocol.h>
24
25namespace ripple {
26
29 bool preserveOrder,
30 Keylet const& directory,
31 uint256 const& key,
32 std::function<void(std::shared_ptr<SLE> const&)> const& describe)
33{
34 auto root = peek(directory);
35
36 if (!root)
37 {
38 // No root, make it.
39 root = std::make_shared<SLE>(directory);
40 root->setFieldH256(sfRootIndex, directory.key);
41 describe(root);
42
44 v.push_back(key);
45 root->setFieldV256(sfIndexes, v);
46
47 insert(root);
48 return std::uint64_t{0};
49 }
50
51 std::uint64_t page = root->getFieldU64(sfIndexPrevious);
52
53 auto node = root;
54
55 if (page)
56 {
57 node = peek(keylet::page(directory, page));
58 if (!node)
59 LogicError("Directory chain: root back-pointer broken.");
60 }
61
62 auto indexes = node->getFieldV256(sfIndexes);
63
64 // If there's space, we use it:
65 if (indexes.size() < dirNodeMaxEntries)
66 {
67 if (preserveOrder)
68 {
69 if (std::find(indexes.begin(), indexes.end(), key) != indexes.end())
70 LogicError("dirInsert: double insertion");
71
72 indexes.push_back(key);
73 }
74 else
75 {
76 // We can't be sure if this page is already sorted because
77 // it may be a legacy page we haven't yet touched. Take
78 // the time to sort it.
79 std::sort(indexes.begin(), indexes.end());
80
81 auto pos = std::lower_bound(indexes.begin(), indexes.end(), key);
82
83 if (pos != indexes.end() && key == *pos)
84 LogicError("dirInsert: double insertion");
85
86 indexes.insert(pos, key);
87 }
88
89 node->setFieldV256(sfIndexes, indexes);
90 update(node);
91 return page;
92 }
93
94 // Check whether we're out of pages.
95 if (++page >= dirNodeMaxPages)
96 return std::nullopt;
97
98 // We are about to create a new node; we'll link it to
99 // the chain first:
100 node->setFieldU64(sfIndexNext, page);
101 update(node);
102
103 root->setFieldU64(sfIndexPrevious, page);
104 update(root);
105
106 // Insert the new key:
107 indexes.clear();
108 indexes.push_back(key);
109
110 node = std::make_shared<SLE>(keylet::page(directory, page));
111 node->setFieldH256(sfRootIndex, directory.key);
112 node->setFieldV256(sfIndexes, indexes);
113
114 // Save some space by not specifying the value 0 since
115 // it's the default.
116 if (page != 1)
117 node->setFieldU64(sfIndexPrevious, page - 1);
118 describe(node);
119 insert(node);
120
121 return page;
122}
123
124bool
126{
127 auto node = peek(directory);
128
129 if (!node)
130 return false;
131
132 // Verify that the passed directory node is the directory root.
133 if (directory.type != ltDIR_NODE ||
134 node->getFieldH256(sfRootIndex) != directory.key)
135 {
136 // LCOV_EXCL_START
137 UNREACHABLE("ripple::ApplyView::emptyDirDelete : invalid node type");
138 return false;
139 // LCOV_EXCL_STOP
140 }
141
142 // The directory still contains entries and so it cannot be removed
143 if (!node->getFieldV256(sfIndexes).empty())
144 return false;
145
146 std::uint64_t constexpr rootPage = 0;
147 auto prevPage = node->getFieldU64(sfIndexPrevious);
148 auto nextPage = node->getFieldU64(sfIndexNext);
149
150 if (nextPage == rootPage && prevPage != rootPage)
151 LogicError("Directory chain: fwd link broken");
152
153 if (prevPage == rootPage && nextPage != rootPage)
154 LogicError("Directory chain: rev link broken");
155
156 // Older versions of the code would, in some cases, allow the last
157 // page to be empty. Remove such pages:
158 if (nextPage == prevPage && nextPage != rootPage)
159 {
160 auto last = peek(keylet::page(directory, nextPage));
161
162 if (!last)
163 LogicError("Directory chain: fwd link broken.");
164
165 if (!last->getFieldV256(sfIndexes).empty())
166 return false;
167
168 // Update the first page's linked list and
169 // mark it as updated.
170 node->setFieldU64(sfIndexNext, rootPage);
171 node->setFieldU64(sfIndexPrevious, rootPage);
172 update(node);
173
174 // And erase the empty last page:
175 erase(last);
176
177 // Make sure our local values reflect the
178 // updated information:
179 nextPage = rootPage;
180 prevPage = rootPage;
181 }
182
183 // If there are no other pages, erase the root:
184 if (nextPage == rootPage && prevPage == rootPage)
185 erase(node);
186
187 return true;
188}
189
190bool
192 Keylet const& directory,
193 std::uint64_t page,
194 uint256 const& key,
195 bool keepRoot)
196{
197 auto node = peek(keylet::page(directory, page));
198
199 if (!node)
200 return false;
201
202 std::uint64_t constexpr rootPage = 0;
203
204 {
205 auto entries = node->getFieldV256(sfIndexes);
206
207 auto it = std::find(entries.begin(), entries.end(), key);
208
209 if (entries.end() == it)
210 return false;
211
212 // We always preserve the relative order when we remove.
213 entries.erase(it);
214
215 node->setFieldV256(sfIndexes, entries);
216 update(node);
217
218 if (!entries.empty())
219 return true;
220 }
221
222 // The current page is now empty; check if it can be
223 // deleted, and, if so, whether the entire directory
224 // can now be removed.
225 auto prevPage = node->getFieldU64(sfIndexPrevious);
226 auto nextPage = node->getFieldU64(sfIndexNext);
227
228 // The first page is the directory's root node and is
229 // treated specially: it can never be deleted even if
230 // it is empty, unless we plan on removing the entire
231 // directory.
232 if (page == rootPage)
233 {
234 if (nextPage == page && prevPage != page)
235 LogicError("Directory chain: fwd link broken");
236
237 if (prevPage == page && nextPage != page)
238 LogicError("Directory chain: rev link broken");
239
240 // Older versions of the code would, in some cases,
241 // allow the last page to be empty. Remove such
242 // pages if we stumble on them:
243 if (nextPage == prevPage && nextPage != page)
244 {
245 auto last = peek(keylet::page(directory, nextPage));
246 if (!last)
247 LogicError("Directory chain: fwd link broken.");
248
249 if (last->getFieldV256(sfIndexes).empty())
250 {
251 // Update the first page's linked list and
252 // mark it as updated.
253 node->setFieldU64(sfIndexNext, page);
254 node->setFieldU64(sfIndexPrevious, page);
255 update(node);
256
257 // And erase the empty last page:
258 erase(last);
259
260 // Make sure our local values reflect the
261 // updated information:
262 nextPage = page;
263 prevPage = page;
264 }
265 }
266
267 if (keepRoot)
268 return true;
269
270 // If there's no other pages, erase the root:
271 if (nextPage == page && prevPage == page)
272 erase(node);
273
274 return true;
275 }
276
277 // This can never happen for nodes other than the root:
278 if (nextPage == page)
279 LogicError("Directory chain: fwd link broken");
280
281 if (prevPage == page)
282 LogicError("Directory chain: rev link broken");
283
284 // This node isn't the root, so it can either be in the
285 // middle of the list, or at the end. Unlink it first
286 // and then check if that leaves the list with only a
287 // root:
288 auto prev = peek(keylet::page(directory, prevPage));
289 if (!prev)
290 LogicError("Directory chain: fwd link broken.");
291 // Fix previous to point to its new next.
292 prev->setFieldU64(sfIndexNext, nextPage);
293 update(prev);
294
295 auto next = peek(keylet::page(directory, nextPage));
296 if (!next)
297 LogicError("Directory chain: rev link broken.");
298 // Fix next to point to its new previous.
299 next->setFieldU64(sfIndexPrevious, prevPage);
300 update(next);
301
302 // The page is no longer linked. Delete it.
303 erase(node);
304
305 // Check whether the next page is the last page and, if
306 // so, whether it's empty. If it is, delete it.
307 if (nextPage != rootPage && next->getFieldU64(sfIndexNext) == rootPage &&
308 next->getFieldV256(sfIndexes).empty())
309 {
310 // Since next doesn't point to the root, it
311 // can't be pointing to prev.
312 erase(next);
313
314 // The previous page is now the last page:
315 prev->setFieldU64(sfIndexNext, rootPage);
316 update(prev);
317
318 // And the root points to the last page:
319 auto root = peek(keylet::page(directory, rootPage));
320 if (!root)
321 LogicError("Directory chain: root link broken.");
322 root->setFieldU64(sfIndexPrevious, prevPage);
323 update(root);
324
325 nextPage = rootPage;
326 }
327
328 // If we're not keeping the root, then check to see if
329 // it's left empty. If so, delete it as well.
330 if (!keepRoot && nextPage == rootPage && prevPage == rootPage)
331 {
332 if (prev->getFieldV256(sfIndexes).empty())
333 erase(prev);
334 }
335
336 return true;
337}
338
339bool
341 Keylet const& directory,
342 std::function<void(uint256 const&)> const& callback)
343{
345
346 do
347 {
348 auto const page = peek(keylet::page(directory, pi.value_or(0)));
349
350 if (!page)
351 return false;
352
353 for (auto const& item : page->getFieldV256(sfIndexes))
354 callback(item);
355
356 pi = (*page)[~sfIndexNext];
357
358 erase(page);
359 } while (pi);
360
361 return true;
362}
363
364} // namespace ripple
bool dirDelete(Keylet const &directory, std::function< void(uint256 const &)> const &)
Remove the specified directory, invoking the callback for every node.
virtual void update(std::shared_ptr< SLE > const &sle)=0
Indicate changes to a peeked SLE.
bool dirRemove(Keylet const &directory, std::uint64_t page, uint256 const &key, bool keepRoot)
Remove an entry from a directory.
bool emptyDirDelete(Keylet const &directory)
Remove the specified directory, if it is empty.
std::optional< std::uint64_t > dirAdd(bool preserveOrder, Keylet const &directory, uint256 const &key, std::function< void(std::shared_ptr< SLE > const &)> const &describe)
Add an entry to a directory using the specified insert strategy.
Definition ApplyView.cpp:28
virtual void insert(std::shared_ptr< SLE > const &sle)=0
Insert a new state SLE.
virtual std::shared_ptr< SLE > peek(Keylet const &k)=0
Prepare to modify the SLE associated with key.
virtual void erase(std::shared_ptr< SLE > const &sle)=0
Remove a peeked SLE.
void push_back(uint256 const &v)
T find(T... args)
T is_same_v
T lower_bound(T... args)
Keylet page(uint256 const &root, std::uint64_t index=0) noexcept
A page in a directory.
Definition Indexes.cpp:380
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
std::uint64_t constexpr dirNodeMaxPages
The maximum number of pages allowed in a directory.
Definition Protocol.h:59
std::size_t constexpr dirNodeMaxEntries
The maximum number of entries per directory page.
Definition Protocol.h:56
Number root(Number f, unsigned d)
Definition Number.cpp:636
void LogicError(std::string const &how) noexcept
Called when faulty logic causes a broken invariant.
T sort(T... args)
A pair of SHAMap key and LedgerEntryType.
Definition Keylet.h:39
LedgerEntryType type
Definition Keylet.h:41
uint256 key
Definition Keylet.h:40
T value_or(T... args)