@@ -245,9 +245,26 @@ class CCoinsViewCursor
245
245
246
246
/* *
247
247
* Cursor for iterating over the linked list of flagged entries in CCoinsViewCache.
248
+ *
249
+ * This is a helper struct to encapsulate the diverging logic between a non-erasing
250
+ * CCoinsViewCache::Sync and an erasing CCoinsViewCache::Flush. This allows the receiver
251
+ * of CCoinsView::BatchWrite to iterate through the flagged entries without knowing
252
+ * the caller's intent.
253
+ *
254
+ * However, the receiver can still call CoinsViewCacheCursor::WillErase to see if the
255
+ * caller will erase the entry after BatchWrite returns. If so, the receiver can
256
+ * perform optimizations such as moving the coin out of the CCoinsCachEntry instead
257
+ * of copying it.
248
258
*/
249
259
struct CoinsViewCacheCursor
250
260
{
261
+ // ! If will_erase is not set, iterating through the cursor will erase spent coins from the map,
262
+ // ! and other coins will be unflagged (removing them from the linked list).
263
+ // ! If will_erase is set, the underlying map and linked list will not be modified,
264
+ // ! as the caller is expected to wipe the entire map anyway.
265
+ // ! This is an optimization compared to erasing all entries as the cursor iterates them when will_erase is set.
266
+ // ! Calling CCoinsMap::clear() afterwards is faster because a CoinsCachePair cannot be coerced back into a
267
+ // ! CCoinsMap::iterator to be erased, and must therefore be looked up again by key in the CCoinsMap before being erased.
251
268
CoinsViewCacheCursor (size_t & usage LIFETIMEBOUND,
252
269
CoinsCachePair& sentinel LIFETIMEBOUND,
253
270
CCoinsMap& map LIFETIMEBOUND,
@@ -257,9 +274,24 @@ struct CoinsViewCacheCursor
257
274
inline CoinsCachePair* Begin () const noexcept { return m_sentinel.second .Next (); }
258
275
inline CoinsCachePair* End () const noexcept { return &m_sentinel; }
259
276
260
- inline CoinsCachePair* NextAndMaybeErase (CoinsCachePair& current) noexcept { return current.second .Next (); }
277
+ // ! Return the next entry after current, possibly erasing current
278
+ inline CoinsCachePair* NextAndMaybeErase (CoinsCachePair& current) noexcept
279
+ {
280
+ const auto next_entry{current.second .Next ()};
281
+ // If we are not going to erase the cache, we must still erase spent entries.
282
+ // Otherwise clear the flags on the entry.
283
+ if (!m_will_erase) {
284
+ if (current.second .coin .IsSpent ()) {
285
+ m_usage -= current.second .coin .DynamicMemoryUsage ();
286
+ m_map.erase (current.first );
287
+ } else {
288
+ current.second .ClearFlags ();
289
+ }
290
+ }
291
+ return next_entry;
292
+ }
261
293
262
- inline bool WillErase (CoinsCachePair& current) const noexcept { return m_will_erase; }
294
+ inline bool WillErase (CoinsCachePair& current) const noexcept { return m_will_erase || current. second . coin . IsSpent () ; }
263
295
private:
264
296
size_t & m_usage;
265
297
CoinsCachePair& m_sentinel;
0 commit comments