Compare commits
3 commits
master
...
cache_nth_
Author | SHA1 | Date | |
---|---|---|---|
|
d9e83ba216 | ||
|
c309f811c1 | ||
|
fbf7de9fb2 |
4 changed files with 133 additions and 79 deletions
|
@ -266,10 +266,9 @@ expirationQueueRowType* CClaimTrieCacheBase::getExpirationQueueCacheRow(int, boo
|
||||||
|
|
||||||
bool CClaimTrieCacheBase::haveClaim(const std::string& name, const COutPoint& outPoint) const
|
bool CClaimTrieCacheBase::haveClaim(const std::string& name, const COutPoint& outPoint) const
|
||||||
{
|
{
|
||||||
auto it = nodesToAddOrUpdate.find(name);
|
if (auto it = nodesToAddOrUpdate.find(name))
|
||||||
if (it && it->haveClaim(outPoint))
|
return it->haveClaim(outPoint);
|
||||||
return true;
|
if (nodesToDelete.count(name))
|
||||||
if (it || nodesToDelete.count(name))
|
|
||||||
return false;
|
return false;
|
||||||
CClaimTrieData data;
|
CClaimTrieData data;
|
||||||
return base->find(name, data) && data.haveClaim(outPoint);
|
return base->find(name, data) && data.haveClaim(outPoint);
|
||||||
|
@ -325,12 +324,14 @@ bool CClaimTrieCacheBase::haveSupportInQueue(const std::string& name, const COut
|
||||||
return haveInQueue<CSupportValue>(name, outPoint, nValidAtHeight);
|
return haveInQueue<CSupportValue>(name, outPoint, nValidAtHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CClaimTrie::recurseNodes(const std::string& name, const CClaimTrieDataNode& current, std::function<recurseNodesCB> function) const {
|
void CClaimTrie::recurseNodes(const std::string& name, const CClaimTrieDataNode& current, std::function<recurseNodesCB> function) const
|
||||||
|
{
|
||||||
CClaimTrieData data;
|
CClaimTrieData data;
|
||||||
find(name, data);
|
find(name, data);
|
||||||
|
|
||||||
data.hash = current.hash;
|
if (!(data.flags & CAME_FROM_NODE_CACHE))
|
||||||
data.flags |= current.children.empty() ? 0 : CClaimTrieDataFlags::POTENTIAL_CHILDREN;
|
data.hash = current.hash;
|
||||||
|
|
||||||
function(name, data, current.children);
|
function(name, data, current.children);
|
||||||
|
|
||||||
for (auto& child: current.children) {
|
for (auto& child: current.children) {
|
||||||
|
@ -382,10 +383,9 @@ CAmount CClaimTrie::getTotalValueOfClaimsInTrie(bool fControllingOnly) const
|
||||||
|
|
||||||
bool CClaimTrieCacheBase::getInfoForName(const std::string& name, CClaimValue& claim) const
|
bool CClaimTrieCacheBase::getInfoForName(const std::string& name, CClaimValue& claim) const
|
||||||
{
|
{
|
||||||
auto it = nodesToAddOrUpdate.find(name);
|
if (auto it = nodesToAddOrUpdate.find(name))
|
||||||
if (it && it->getBestClaim(claim))
|
return it->getBestClaim(claim);
|
||||||
return true;
|
if (nodesToDelete.count(name))
|
||||||
if (it || nodesToDelete.count(name))
|
|
||||||
return false;
|
return false;
|
||||||
CClaimTrieData claims;
|
CClaimTrieData claims;
|
||||||
return base->find(name, claims) && claims.getBestClaim(claim);
|
return base->find(name, claims) && claims.getBestClaim(claim);
|
||||||
|
@ -492,7 +492,8 @@ bool CClaimTrie::checkConsistency(const uint256& rootHash) const
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::pair<std::string, CClaimTrieDataNode>> CClaimTrie::nodes(const std::string &key) const {
|
std::vector<std::pair<std::string, CClaimTrieDataNode>> CClaimTrie::nodes(const std::string &key) const
|
||||||
|
{
|
||||||
std::vector<std::pair<std::string, CClaimTrieDataNode>> ret;
|
std::vector<std::pair<std::string, CClaimTrieDataNode>> ret;
|
||||||
CClaimTrieDataNode node;
|
CClaimTrieDataNode node;
|
||||||
|
|
||||||
|
@ -527,30 +528,62 @@ std::vector<std::pair<std::string, CClaimTrieDataNode>> CClaimTrie::nodes(const
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CClaimTrie::contains(const std::string &key) const {
|
bool CClaimTrie::contains(const std::string& key) const
|
||||||
return db->Exists(std::make_pair(TRIE_NODE_CHILDREN, key));
|
{
|
||||||
|
return cacheNodes.find(key) || db->Exists(std::make_pair(TRIE_NODE_CHILDREN, key));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CClaimTrie::empty() const {
|
bool CClaimTrie::empty() const
|
||||||
return !contains({});
|
{
|
||||||
|
return cacheNodes.empty() && !contains({});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CClaimTrie::find(const std::string& key, CClaimTrieDataNode &node) const {
|
std::vector<std::string> extractChildren(CClaimPrefixTrie::const_iterator it)
|
||||||
return db->Read(std::make_pair(TRIE_NODE_CHILDREN, key), node);
|
{
|
||||||
|
std::vector<std::string> children;
|
||||||
|
auto size = it.key().size();
|
||||||
|
for (auto& child : it.children()) // ordering here is important
|
||||||
|
children.emplace_back(child.key().substr(size));
|
||||||
|
return children;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CClaimTrie::find(const std::string& key, CClaimTrieData &data) const {
|
static std::size_t cacheHits = 0;
|
||||||
return db->Read(std::make_pair(TRIE_NODE_CLAIMS, key), data);
|
static std::size_t cacheMisses = 0;
|
||||||
|
bool CClaimTrie::find(const std::string& key, CClaimTrieDataNode& node) const
|
||||||
|
{
|
||||||
|
if (auto it = cacheNodes.find(key)) {
|
||||||
|
if (it.hasChildren()) {
|
||||||
|
cacheHits++;
|
||||||
|
node.hash = it->hash;
|
||||||
|
node.children = extractChildren(it);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto ret = db->Read(std::make_pair(TRIE_NODE_CHILDREN, key), node);
|
||||||
|
if (ret) ++cacheMisses;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CClaimTrie::find(const std::string& key, CClaimTrieData& data) const
|
||||||
|
{
|
||||||
|
if (auto it = cacheNodes.find(key)) {
|
||||||
|
cacheHits++;
|
||||||
|
data = it.data();
|
||||||
|
data.flags |= CAME_FROM_NODE_CACHE;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
auto ret = db->Read(std::make_pair(TRIE_NODE_CLAIMS, key), data);
|
||||||
|
if (ret) ++cacheMisses;
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename K, typename T>
|
template <typename K, typename T>
|
||||||
void BatchWrite(CDBBatch& batch, uint8_t dbkey, const K& key, const std::vector<T>& value)
|
void BatchWrite(CDBBatch& batch, uint8_t dbkey, const K& key, const T& value)
|
||||||
{
|
{
|
||||||
if (value.empty()) {
|
if (value.empty())
|
||||||
batch.Erase(std::make_pair(dbkey, key));
|
batch.Erase(std::make_pair(dbkey, key));
|
||||||
} else {
|
else
|
||||||
batch.Write(std::make_pair(dbkey, key), value);
|
batch.Write(std::make_pair(dbkey, key), value);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Container>
|
template <typename Container>
|
||||||
|
@ -579,26 +612,39 @@ bool CClaimTrieCacheBase::flush()
|
||||||
|
|
||||||
getMerkleHash();
|
getMerkleHash();
|
||||||
|
|
||||||
|
if (base->cacheNodes.height() > 40000)
|
||||||
|
base->cacheNodes.clear();
|
||||||
|
|
||||||
for (auto it = nodesToAddOrUpdate.begin(); it != nodesToAddOrUpdate.end(); ++it) {
|
for (auto it = nodesToAddOrUpdate.begin(); it != nodesToAddOrUpdate.end(); ++it) {
|
||||||
bool removed = forDeleteFromBase.erase(it.key());
|
bool removed = forDeleteFromBase.erase(it.key());
|
||||||
if (it->flags & CClaimTrieDataFlags::HASH_DIRTY) {
|
if (it->flags & HASH_DIRTY) {
|
||||||
CClaimTrieDataNode node;
|
CClaimTrieDataNode node;
|
||||||
node.hash = it->hash;
|
node.hash = it->hash;
|
||||||
for (auto &child: it.children()) // ordering here is important
|
node.children = extractChildren(it);
|
||||||
node.children.push_back(child.key().substr(it.key().size()));
|
|
||||||
|
|
||||||
batch.Write(std::make_pair(TRIE_NODE_CHILDREN, it.key()), node);
|
batch.Write(std::make_pair(TRIE_NODE_CHILDREN, it.key()), node);
|
||||||
|
|
||||||
if (removed || (it->flags & CClaimTrieDataFlags::CLAIMS_DIRTY))
|
if (removed || (it->flags & CLAIMS_DIRTY))
|
||||||
batch.Write(std::make_pair(TRIE_NODE_CLAIMS, it.key()), it.data());
|
BatchWrite(batch, TRIE_NODE_CLAIMS, it.key(), it.data());
|
||||||
}
|
}
|
||||||
|
it->flags = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& name: forDeleteFromBase) {
|
for (auto& name: forDeleteFromBase) {
|
||||||
|
base->cacheNodes.erase(name);
|
||||||
batch.Erase(std::make_pair(TRIE_NODE_CHILDREN, name));
|
batch.Erase(std::make_pair(TRIE_NODE_CHILDREN, name));
|
||||||
batch.Erase(std::make_pair(TRIE_NODE_CLAIMS, name));
|
batch.Erase(std::make_pair(TRIE_NODE_CLAIMS, name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (auto it = nodesToAddOrUpdate.begin(); it != nodesToAddOrUpdate.end(); ++it) {
|
||||||
|
if (it.depth() < 4) {
|
||||||
|
base->cacheNodes.copy(it, it.depth() == 3 || (it->flags & HASH_DIRTY && !it.hasChildren()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "Height: %d, Hits: %zu, Misses: %zu, Cache Size: %zu\n", nNextHeight, cacheHits, cacheMisses, base->cacheNodes.height());
|
||||||
|
cacheHits = cacheMisses = 0;
|
||||||
|
|
||||||
BatchWriteQueue(batch, SUPPORT, supportCache);
|
BatchWriteQueue(batch, SUPPORT, supportCache);
|
||||||
|
|
||||||
BatchWriteQueue(batch, CLAIM_QUEUE_ROW, claimQueueCache);
|
BatchWriteQueue(batch, CLAIM_QUEUE_ROW, claimQueueCache);
|
||||||
|
@ -614,10 +660,9 @@ bool CClaimTrieCacheBase::flush()
|
||||||
LogPrintf("TrieCache size: %zu nodes on block %d, batch writes %zu bytes.\n",
|
LogPrintf("TrieCache size: %zu nodes on block %d, batch writes %zu bytes.\n",
|
||||||
nodesToAddOrUpdate.height(), nNextHeight, batch.SizeEstimate());
|
nodesToAddOrUpdate.height(), nNextHeight, batch.SizeEstimate());
|
||||||
}
|
}
|
||||||
auto ret = base->db->WriteBatch(batch);
|
|
||||||
|
|
||||||
clear();
|
clear();
|
||||||
return ret;
|
return base->db->WriteBatch(batch);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CClaimTrieCacheBase::validateTrieConsistency(const CBlockIndex* tip)
|
bool CClaimTrieCacheBase::validateTrieConsistency(const CBlockIndex* tip)
|
||||||
|
@ -718,22 +763,21 @@ CClaimPrefixTrie::iterator CClaimTrieCacheBase::cacheData(const std::string& nam
|
||||||
// do not insert nodes that are already present
|
// do not insert nodes that are already present
|
||||||
CClaimTrieData data;
|
CClaimTrieData data;
|
||||||
base->find(node.first, data);
|
base->find(node.first, data);
|
||||||
data.hash = node.second.hash;
|
if (!(data.flags & CAME_FROM_NODE_CACHE))
|
||||||
data.flags = node.second.children.empty() ? 0 : CClaimTrieDataFlags::POTENTIAL_CHILDREN;
|
data.hash = node.second.hash;
|
||||||
nodesToAddOrUpdate.insert(node.first, data);
|
nodesToAddOrUpdate.insert(node.first, std::move(data));
|
||||||
}
|
}
|
||||||
for (auto& child : node.second.children) {
|
for (auto& child : node.second.children) {
|
||||||
auto childKey = node.first + child;
|
auto childKey = node.first + child;
|
||||||
if (nodesAlreadyCached.insert(childKey).second) {
|
if (nodesAlreadyCached.insert(childKey).second) {
|
||||||
CClaimTrieData childData;
|
CClaimTrieData childData;
|
||||||
if (!base->find(childKey, childData))
|
base->find(childKey, childData);
|
||||||
childData = {};
|
if (!(childData.flags & CAME_FROM_NODE_CACHE)) {
|
||||||
CClaimTrieDataNode childNode;
|
CClaimTrieDataNode childNode;
|
||||||
if (base->find(childKey, childNode)) {
|
if (base->find(childKey, childNode))
|
||||||
childData.hash = childNode.hash;
|
childData.hash = childNode.hash;
|
||||||
childData.flags = childNode.children.empty() ? 0 : CClaimTrieDataFlags::POTENTIAL_CHILDREN;
|
|
||||||
}
|
}
|
||||||
nodesToAddOrUpdate.insert(childKey, childData);
|
nodesToAddOrUpdate.insert(childKey, std::move(childData));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -741,8 +785,6 @@ CClaimPrefixTrie::iterator CClaimTrieCacheBase::cacheData(const std::string& nam
|
||||||
auto it = nodesToAddOrUpdate.find(name);
|
auto it = nodesToAddOrUpdate.find(name);
|
||||||
if (!it && create) {
|
if (!it && create) {
|
||||||
it = nodesToAddOrUpdate.insert(name, CClaimTrieData{});
|
it = nodesToAddOrUpdate.insert(name, CClaimTrieData{});
|
||||||
// if (it.hasChildren()) any children should be in the trie (not base alone)
|
|
||||||
// it->flags |= CClaimTrieDataFlags::POTENTIAL_CHILDREN;
|
|
||||||
confirmTakeoverWorkaroundNeeded(name);
|
confirmTakeoverWorkaroundNeeded(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -778,10 +820,11 @@ bool CClaimTrieCacheBase::getLastTakeoverForName(const std::string& name, uint16
|
||||||
void CClaimTrieCacheBase::markAsDirty(const std::string& name, bool fCheckTakeover)
|
void CClaimTrieCacheBase::markAsDirty(const std::string& name, bool fCheckTakeover)
|
||||||
{
|
{
|
||||||
for (auto& node : nodesToAddOrUpdate.nodes(name)) {
|
for (auto& node : nodesToAddOrUpdate.nodes(name)) {
|
||||||
node->flags |= CClaimTrieDataFlags::HASH_DIRTY;
|
node->flags |= HASH_DIRTY;
|
||||||
|
|
||||||
node->hash.SetNull();
|
node->hash.SetNull();
|
||||||
if (node.key() == name)
|
if (node.key() == name)
|
||||||
node->flags |= CClaimTrieDataFlags::CLAIMS_DIRTY;
|
node->flags |= CLAIMS_DIRTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fCheckTakeover)
|
if (fCheckTakeover)
|
||||||
|
@ -1490,7 +1533,7 @@ void CClaimTrieCacheBase::recurseNodes(const std::string &name,
|
||||||
else {
|
else {
|
||||||
for (auto it = begin(); it != end(); ++it) {
|
for (auto it = begin(); it != end(); ++it) {
|
||||||
function(it.key(), it.data());
|
function(it.key(), it.data());
|
||||||
if ((it->flags & CClaimTrieDataFlags::POTENTIAL_CHILDREN) && !it.hasChildren()) {
|
if (!it.hasChildren()) {
|
||||||
CClaimTrieDataNode node;
|
CClaimTrieDataNode node;
|
||||||
if (base->find(it.key(), node))
|
if (base->find(it.key(), node))
|
||||||
for (auto& partialKey: node.children) {
|
for (auto& partialKey: node.children) {
|
||||||
|
|
|
@ -139,7 +139,7 @@ typedef std::vector<CSupportValue> supportEntryType;
|
||||||
enum CClaimTrieDataFlags: uint32_t {
|
enum CClaimTrieDataFlags: uint32_t {
|
||||||
HASH_DIRTY = 1U,
|
HASH_DIRTY = 1U,
|
||||||
CLAIMS_DIRTY = 2U,
|
CLAIMS_DIRTY = 2U,
|
||||||
POTENTIAL_CHILDREN = 4U, // existing on disk
|
CAME_FROM_NODE_CACHE = 4U,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CClaimTrieData
|
struct CClaimTrieData
|
||||||
|
@ -188,7 +188,8 @@ struct CClaimTrieData
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CClaimTrieDataNode {
|
struct CClaimTrieDataNode
|
||||||
|
{
|
||||||
uint256 hash;
|
uint256 hash;
|
||||||
// we're using a vector to avoid RAM thrashing and for faster serialization ops.
|
// we're using a vector to avoid RAM thrashing and for faster serialization ops.
|
||||||
// We're assuming its data is inserted in order and never modified.
|
// We're assuming its data is inserted in order and never modified.
|
||||||
|
@ -364,6 +365,8 @@ struct CClaimSupportToName
|
||||||
const std::vector<CSupportValue> unmatchedSupports;
|
const std::vector<CSupportValue> unmatchedSupports;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef CPrefixTrie<std::string, CClaimTrieData> CClaimPrefixTrie;
|
||||||
|
|
||||||
class CClaimTrie
|
class CClaimTrie
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -402,6 +405,7 @@ protected:
|
||||||
int nProportionalDelayFactor = 0;
|
int nProportionalDelayFactor = 0;
|
||||||
std::unique_ptr<CDBWrapper> db;
|
std::unique_ptr<CDBWrapper> db;
|
||||||
|
|
||||||
|
CClaimPrefixTrie cacheNodes;
|
||||||
using recurseNodesCB = void(const std::string&, const CClaimTrieData&, const std::vector<std::string>&);
|
using recurseNodesCB = void(const std::string&, const CClaimTrieData&, const std::vector<std::string>&);
|
||||||
void recurseNodes(const std::string& name, const CClaimTrieDataNode& current, std::function<recurseNodesCB> function) const;
|
void recurseNodes(const std::string& name, const CClaimTrieDataNode& current, std::function<recurseNodesCB> function) const;
|
||||||
};
|
};
|
||||||
|
@ -508,8 +512,6 @@ typedef std::map<int, expirationQueueRowType> expirationQueueType;
|
||||||
typedef std::set<CClaimValue> claimIndexClaimListType;
|
typedef std::set<CClaimValue> claimIndexClaimListType;
|
||||||
typedef std::vector<CClaimIndexElement> claimIndexElementListType;
|
typedef std::vector<CClaimIndexElement> claimIndexElementListType;
|
||||||
|
|
||||||
typedef CPrefixTrie<std::string, CClaimTrieData> CClaimPrefixTrie;
|
|
||||||
|
|
||||||
class CClaimTrieCacheBase
|
class CClaimTrieCacheBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -108,16 +108,18 @@ template <typename TKey, typename TData>
|
||||||
template <bool IsConst>
|
template <bool IsConst>
|
||||||
typename CPrefixTrie<TKey, TData>::template Iterator<IsConst>::reference CPrefixTrie<TKey, TData>::Iterator<IsConst>::operator*() const
|
typename CPrefixTrie<TKey, TData>::template Iterator<IsConst>::reference CPrefixTrie<TKey, TData>::Iterator<IsConst>::operator*() const
|
||||||
{
|
{
|
||||||
assert(!node.expired());
|
auto shared = node.lock();
|
||||||
return TPair(name, node.lock()->data);
|
assert(shared);
|
||||||
|
return TPair(name, *(shared->data));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TKey, typename TData>
|
template <typename TKey, typename TData>
|
||||||
template <bool IsConst>
|
template <bool IsConst>
|
||||||
typename CPrefixTrie<TKey, TData>::template Iterator<IsConst>::pointer CPrefixTrie<TKey, TData>::Iterator<IsConst>::operator->() const
|
typename CPrefixTrie<TKey, TData>::template Iterator<IsConst>::pointer CPrefixTrie<TKey, TData>::Iterator<IsConst>::operator->() const
|
||||||
{
|
{
|
||||||
assert(!node.expired());
|
auto shared = node.lock();
|
||||||
return &(node.lock()->data);
|
assert(shared);
|
||||||
|
return shared->data.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TKey, typename TData>
|
template <typename TKey, typename TData>
|
||||||
|
@ -129,18 +131,20 @@ const TKey& CPrefixTrie<TKey, TData>::Iterator<IsConst>::key() const
|
||||||
|
|
||||||
template <typename TKey, typename TData>
|
template <typename TKey, typename TData>
|
||||||
template <bool IsConst>
|
template <bool IsConst>
|
||||||
TData& CPrefixTrie<TKey, TData>::Iterator<IsConst>::data()
|
typename CPrefixTrie<TKey, TData>::template Iterator<IsConst>::TDataRef CPrefixTrie<TKey, TData>::Iterator<IsConst>::data()
|
||||||
{
|
{
|
||||||
assert(!node.expired());
|
auto shared = node.lock();
|
||||||
return node.lock()->data;
|
assert(shared);
|
||||||
|
return *(shared->data);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TKey, typename TData>
|
template <typename TKey, typename TData>
|
||||||
template <bool IsConst>
|
template <bool IsConst>
|
||||||
const TData& CPrefixTrie<TKey, TData>::Iterator<IsConst>::data() const
|
const TData& CPrefixTrie<TKey, TData>::Iterator<IsConst>::data() const
|
||||||
{
|
{
|
||||||
assert(!node.expired());
|
auto shared = node.lock();
|
||||||
return node.lock()->data;
|
assert(shared);
|
||||||
|
return *(shared->data);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TKey, typename TData>
|
template <typename TKey, typename TData>
|
||||||
|
@ -244,15 +248,16 @@ std::shared_ptr<typename CPrefixTrie<TKey, TData>::Node>& CPrefixTrie<TKey, TDat
|
||||||
return it->second;
|
return it->second;
|
||||||
}
|
}
|
||||||
if (count < it->first.size()) {
|
if (count < it->first.size()) {
|
||||||
const TKey prefix(key.begin(), key.begin() + count);
|
TKey prefix(key.begin(), key.begin() + count);
|
||||||
const TKey postfix(it->first.begin() + count, it->first.end());
|
TKey postfix(it->first.begin() + count, it->first.end());
|
||||||
auto nodes = std::move(it->second);
|
auto nodes = std::move(it->second);
|
||||||
children.erase(it);
|
children.erase(it);
|
||||||
++size;
|
++size;
|
||||||
it = children.emplace(prefix, std::make_shared<Node>()).first;
|
it = children.emplace(std::move(prefix), std::make_shared<Node>()).first;
|
||||||
it->second->children.emplace(postfix, std::move(nodes));
|
it->second->children.emplace(std::move(postfix), std::move(nodes));
|
||||||
if (key.size() == count)
|
if (key.size() == count)
|
||||||
return it->second;
|
return it->second;
|
||||||
|
it->second->data = std::make_shared<TData>();
|
||||||
}
|
}
|
||||||
return insert(TKey(key.begin() + count, key.end()), it->second);
|
return insert(TKey(key.begin() + count, key.end()), it->second);
|
||||||
}
|
}
|
||||||
|
@ -269,12 +274,12 @@ void CPrefixTrie<TKey, TData>::erase(const TKey& key, std::shared_ptr<Node>& nod
|
||||||
if (!find(key, node, cb))
|
if (!find(key, node, cb))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
nodes.back().second->data = {};
|
nodes.back().second->data = std::make_shared<TData>();
|
||||||
for (; nodes.size() > 1; nodes.pop_back()) {
|
for (; nodes.size() > 1; nodes.pop_back()) {
|
||||||
// if we have only one child and no data ourselves, bring them up to our level
|
// if we have only one child and no data ourselves, bring them up to our level
|
||||||
auto& cNode = nodes.back().second;
|
auto& cNode = nodes.back().second;
|
||||||
auto onlyOneChild = cNode->children.size() == 1;
|
auto onlyOneChild = cNode->children.size() == 1;
|
||||||
auto noData = cNode->data.empty();
|
auto noData = cNode->data->empty();
|
||||||
if (onlyOneChild && noData) {
|
if (onlyOneChild && noData) {
|
||||||
auto child = cNode->children.begin();
|
auto child = cNode->children.begin();
|
||||||
auto& prefix = nodes.back().first;
|
auto& prefix = nodes.back().first;
|
||||||
|
@ -282,7 +287,7 @@ void CPrefixTrie<TKey, TData>::erase(const TKey& key, std::shared_ptr<Node>& nod
|
||||||
auto& postfix = child->first;
|
auto& postfix = child->first;
|
||||||
newKey.insert(newKey.end(), postfix.begin(), postfix.end());
|
newKey.insert(newKey.end(), postfix.begin(), postfix.end());
|
||||||
auto& pNode = nodes[nodes.size() - 2].second;
|
auto& pNode = nodes[nodes.size() - 2].second;
|
||||||
pNode->children.emplace(newKey, std::move(child->second));
|
pNode->children.emplace(std::move(newKey), std::move(child->second));
|
||||||
pNode->children.erase(prefix);
|
pNode->children.erase(prefix);
|
||||||
--size;
|
--size;
|
||||||
continue;
|
continue;
|
||||||
|
@ -302,6 +307,7 @@ void CPrefixTrie<TKey, TData>::erase(const TKey& key, std::shared_ptr<Node>& nod
|
||||||
template <typename TKey, typename TData>
|
template <typename TKey, typename TData>
|
||||||
CPrefixTrie<TKey, TData>::CPrefixTrie() : size(0), root(std::make_shared<Node>())
|
CPrefixTrie<TKey, TData>::CPrefixTrie() : size(0), root(std::make_shared<Node>())
|
||||||
{
|
{
|
||||||
|
root->data = std::make_shared<TData>();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TKey, typename TData>
|
template <typename TKey, typename TData>
|
||||||
|
@ -309,17 +315,19 @@ template <typename TDataUni>
|
||||||
typename CPrefixTrie<TKey, TData>::iterator CPrefixTrie<TKey, TData>::insert(const TKey& key, TDataUni&& data)
|
typename CPrefixTrie<TKey, TData>::iterator CPrefixTrie<TKey, TData>::insert(const TKey& key, TDataUni&& data)
|
||||||
{
|
{
|
||||||
auto& node = key.empty() ? root : insert(key, root);
|
auto& node = key.empty() ? root : insert(key, root);
|
||||||
node->data = std::forward<TDataUni>(data);
|
node->data = std::make_shared<TData>(std::forward<TDataUni>(data));
|
||||||
return key.empty() ? begin() : iterator{key, node};
|
return key.empty() ? begin() : iterator{key, node};
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TKey, typename TData>
|
template <typename TKey, typename TData>
|
||||||
typename CPrefixTrie<TKey, TData>::iterator CPrefixTrie<TKey, TData>::copy(CPrefixTrie<TKey, TData>::const_iterator it)
|
typename CPrefixTrie<TKey, TData>::iterator CPrefixTrie<TKey, TData>::copy(CPrefixTrie<TKey, TData>::const_iterator it, bool wipeChildren)
|
||||||
{
|
{
|
||||||
auto& key = it.key();
|
auto& key = it.key();
|
||||||
auto& node = key.empty() ? root : insert(key, root);
|
auto& node = key.empty() ? root : insert(key, root);
|
||||||
node->data = it.node.lock()->data;
|
node->data = it.node.lock()->data;
|
||||||
return key.empty() ? begin() : iterator{key, node};
|
if (wipeChildren)
|
||||||
|
node->children.clear();
|
||||||
|
return iterator{key, node};
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TKey, typename TData>
|
template <typename TKey, typename TData>
|
||||||
|
@ -335,7 +343,7 @@ typename CPrefixTrie<TKey, TData>::iterator CPrefixTrie<TKey, TData>::insert(CPr
|
||||||
auto& node = insert(key, shared);
|
auto& node = insert(key, shared);
|
||||||
copy = iterator{name, node};
|
copy = iterator{name, node};
|
||||||
}
|
}
|
||||||
copy.node.lock()->data = std::forward<TDataUni>(data);
|
copy.node.lock()->data = std::make_shared<TData>(std::forward<TDataUni>(data));
|
||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -404,7 +412,7 @@ bool CPrefixTrie<TKey, TData>::erase(const TKey& key)
|
||||||
{
|
{
|
||||||
auto size_was = height();
|
auto size_was = height();
|
||||||
if (key.empty()) {
|
if (key.empty()) {
|
||||||
root->data = {};
|
root->data = std::make_shared<TData>();
|
||||||
} else {
|
} else {
|
||||||
erase(key, root);
|
erase(key, root);
|
||||||
}
|
}
|
||||||
|
@ -415,7 +423,7 @@ template <typename TKey, typename TData>
|
||||||
void CPrefixTrie<TKey, TData>::clear()
|
void CPrefixTrie<TKey, TData>::clear()
|
||||||
{
|
{
|
||||||
size = 0;
|
size = 0;
|
||||||
root->data = {};
|
root->data = std::make_shared<TData>();
|
||||||
root->children.clear();
|
root->children.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -428,7 +436,7 @@ bool CPrefixTrie<TKey, TData>::empty() const
|
||||||
template <typename TKey, typename TData>
|
template <typename TKey, typename TData>
|
||||||
std::size_t CPrefixTrie<TKey, TData>::height() const
|
std::size_t CPrefixTrie<TKey, TData>::height() const
|
||||||
{
|
{
|
||||||
return size + (root->data.empty() ? 0 : 1);
|
return size + (root->data->empty() ? 0 : 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TKey, typename TData>
|
template <typename TKey, typename TData>
|
||||||
|
|
|
@ -29,7 +29,7 @@ class CPrefixTrie
|
||||||
Node(Node&& o) noexcept = default;
|
Node(Node&& o) noexcept = default;
|
||||||
Node& operator=(Node&& o) noexcept = default;
|
Node& operator=(Node&& o) noexcept = default;
|
||||||
Node& operator=(const Node&) = delete;
|
Node& operator=(const Node&) = delete;
|
||||||
TData data;
|
std::shared_ptr<TData> data;
|
||||||
};
|
};
|
||||||
|
|
||||||
using TChildren = decltype(Node::children);
|
using TChildren = decltype(Node::children);
|
||||||
|
@ -41,9 +41,10 @@ class CPrefixTrie
|
||||||
friend class Iterator;
|
friend class Iterator;
|
||||||
friend class CPrefixTrie<TKey, TData>;
|
friend class CPrefixTrie<TKey, TData>;
|
||||||
|
|
||||||
using TKeyRef = std::reference_wrapper<const TKey>;
|
using TDataRef = typename std::conditional<IsConst, const TData&, TData&>::type;
|
||||||
using TDataRef = std::reference_wrapper<TData>;
|
using TKeyWrap = std::reference_wrapper<const TKey>;
|
||||||
using TPair = std::pair<TKeyRef, TDataRef>;
|
using TDataWrap = std::reference_wrapper<TData>;
|
||||||
|
using TPair = std::pair<TKeyWrap, TDataWrap>;
|
||||||
|
|
||||||
TKey name;
|
TKey name;
|
||||||
std::weak_ptr<Node> node;
|
std::weak_ptr<Node> node;
|
||||||
|
@ -94,7 +95,7 @@ class CPrefixTrie
|
||||||
|
|
||||||
const TKey& key() const;
|
const TKey& key() const;
|
||||||
|
|
||||||
TData& data();
|
TDataRef data();
|
||||||
const TData& data() const;
|
const TData& data() const;
|
||||||
|
|
||||||
std::size_t depth() const;
|
std::size_t depth() const;
|
||||||
|
@ -132,7 +133,7 @@ public:
|
||||||
template <typename TDataUni>
|
template <typename TDataUni>
|
||||||
iterator insert(iterator& it, const TKey& key, TDataUni&& data);
|
iterator insert(iterator& it, const TKey& key, TDataUni&& data);
|
||||||
|
|
||||||
iterator copy(const_iterator it);
|
iterator copy(const_iterator it, bool wipeChildren);
|
||||||
|
|
||||||
iterator find(const TKey& key);
|
iterator find(const TKey& key);
|
||||||
const_iterator find(const TKey& key) const;
|
const_iterator find(const TKey& key) const;
|
||||||
|
|
Loading…
Reference in a new issue