| 187 | } |
| 188 | |
| 189 | void CCoinsViewCache::BatchWrite(CoinsViewCacheCursor& cursor, const uint256& in_block_hash) |
| 190 | { |
| 191 | for (auto it{cursor.Begin()}; it != cursor.End(); it = cursor.NextAndMaybeErase(*it)) { |
| 192 | if (!it->second.IsDirty()) { // TODO a cursor can only contain dirty entries |
| 193 | continue; |
| 194 | } |
| 195 | auto [itUs, inserted]{cacheCoins.try_emplace(it->first)}; |
| 196 | if (inserted) { |
| 197 | if (it->second.IsFresh() && it->second.coin.IsSpent()) { |
| 198 | cacheCoins.erase(itUs); // TODO fresh coins should have been removed at spend |
| 199 | } else { |
| 200 | // The parent cache does not have an entry, while the child cache does. |
| 201 | // Move the data up and mark it as dirty. |
| 202 | CCoinsCacheEntry& entry{itUs->second}; |
| 203 | assert(entry.coin.DynamicMemoryUsage() == 0); |
| 204 | if (cursor.WillErase(*it)) { |
| 205 | // Since this entry will be erased, |
| 206 | // we can move the coin into us instead of copying it |
| 207 | entry.coin = std::move(it->second.coin); |
| 208 | } else { |
| 209 | entry.coin = it->second.coin; |
| 210 | } |
| 211 | CCoinsCacheEntry::SetDirty(*itUs, m_sentinel); |
| 212 | ++m_dirty_count; |
| 213 | cachedCoinsUsage += entry.coin.DynamicMemoryUsage(); |
| 214 | // We can mark it FRESH in the parent if it was FRESH in the child |
| 215 | // Otherwise it might have just been flushed from the parent's cache |
| 216 | // and already exist in the grandparent |
| 217 | if (it->second.IsFresh()) CCoinsCacheEntry::SetFresh(*itUs, m_sentinel); |
| 218 | } |
| 219 | } else { |
| 220 | // Found the entry in the parent cache |
| 221 | if (it->second.IsFresh() && !itUs->second.coin.IsSpent()) { |
| 222 | // The coin was marked FRESH in the child cache, but the coin |
| 223 | // exists in the parent cache. If this ever happens, it means |
| 224 | // the FRESH flag was misapplied and there is a logic error in |
| 225 | // the calling code. |
| 226 | throw std::logic_error("FRESH flag misapplied to coin that exists in parent cache"); |
| 227 | } |
| 228 | |
| 229 | if (itUs->second.IsFresh() && it->second.coin.IsSpent()) { |
| 230 | // The grandparent cache does not have an entry, and the coin |
| 231 | // has been spent. We can just delete it from the parent cache. |
| 232 | Assume(TrySub(m_dirty_count, itUs->second.IsDirty())); |
| 233 | Assume(TrySub(cachedCoinsUsage, itUs->second.coin.DynamicMemoryUsage())); |
| 234 | cacheCoins.erase(itUs); |
| 235 | } else { |
| 236 | // A normal modification. |
| 237 | Assume(TrySub(cachedCoinsUsage, itUs->second.coin.DynamicMemoryUsage())); |
| 238 | if (cursor.WillErase(*it)) { |
| 239 | // Since this entry will be erased, |
| 240 | // we can move the coin into us instead of copying it |
| 241 | itUs->second.coin = std::move(it->second.coin); |
| 242 | } else { |
| 243 | itUs->second.coin = it->second.coin; |
| 244 | } |
| 245 | cachedCoinsUsage += itUs->second.coin.DynamicMemoryUsage(); |
| 246 | if (!itUs->second.IsDirty()) { |
no test coverage detected