PsTreeNode.cpp
// PsTreeNode.cpp
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2010
@import "PsTreeNode.h"
#include "Ceda/cxUtils/Tracer.h"
#include "Ceda/cxUtils/MsWindows.h"
#include <atomic>
static std::atomic<int> s_numAddedNodes(0);
@api int GetNumAddedNodes()
{
return s_numAddedNodes;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// PsTreeNode
PsTreeNode::PsTreeNode() :
m_amount(0),
m_totalAmount(0)
{
}
PsTreeNode::~PsTreeNode()
{
}
bool PsTreeNode::operator==(const PsTreeNode& rhs) const
{
if (m_amount != rhs.m_amount ||
m_totalAmount != rhs.m_totalAmount ||
m_children.size() != rhs.m_children.size())
{
return false;
}
//Tracer() << "A = " << m_amount << " equal\n";
for (ceda::ssize_t i=0 ; i < m_children.size() ; ++i)
{
if (*m_children[i] != *rhs.m_children[i]) return false;
}
return true;
}
void PsTreeNode::ValidateChildren() const
{
for (ceda::ssize_t i=0 ; i < m_children.size() ; ++i)
{
cxAssert(m_children[i]);
}
}
void PsTreeNode::Serialise(ceda::Archive& ar) const
{
ValidateChildren();
ar << m_parent
<< m_amount
<< m_totalAmount
<< m_children;
}
void PsTreeNode::Deserialise(ceda::InputArchive& ar)
{
ar >> m_parent
>> m_amount
>> m_totalAmount
>> m_children;
ValidateChildren();
}
void PsTreeNode::AdjustTotals(ceda::int32 delta)
{
PsTreeNode* p = this;
while(p)
{
p->m_totalAmount += delta;
p->MarkAsDirtyWithoutTrace();
p = p->m_parent.Get();
}
}
void PsTreeNode::OffsetAmount(ceda::int32 delta)
{
m_amount += delta;
AdjustTotals(delta); // marks this node as dirty
}
PsTreeNode* PsTreeNode::RemoveChild(ceda::ssize_t index)
{
cxAlwaysAssert(0 <= index && index < m_children.size());
PsTreeNode* c = m_children[index].Get();
cxAlwaysAssert(c);
AdjustTotals(-c->m_totalAmount); // marks this node as dirty
m_children.erase(m_children.begin() + index);
c->m_parent = nullptr;
c->MarkAsDirtyWithoutTrace();
return c;
}
void PsTreeNode::RemoveThisNode()
{
cxAlwaysAssert(m_parent);
for (ceda::ssize_t i=0 ; i < m_parent->m_children.size() ; ++i)
{
if (m_parent->m_children[i] == this)
{
cxVerify(m_parent->RemoveChild(i) == this);
return;
}
}
cxAlwaysAssert(0);
}
void PsTreeNode::DeleteThisNode()
{
RemoveThisNode();
// todo. This should only require a PSpace if this node persists. Need a suitable API function
AsyncPermanentlyDeleteSubTree(this);
}
void PsTreeNode::AddChild(ceda::ssize_t index, PsTreeNode* c)
{
cxAlwaysAssert(0 <= index && index <= m_children.size());
cxAlwaysAssert(c);
cxAlwaysAssert(c->m_parent == ceda::null);
s_numAddedNodes++;
AdjustTotals(+c->m_totalAmount);
m_children.insert(m_children.begin() + index, c);
c->m_parent = this;
MarkAsDirtyWithoutTrace();
DeclareReachable(this,c);
}
ceda::ssize_t PsTreeNode::GetNumNodes() const
{
ceda::ssize_t count = 1;
for (ceda::ssize_t i=0 ; i < m_children.size() ; ++i)
{
const PsTreeNode* c = m_children[i].Get();
cxAlwaysAssert(c);
count += c->GetNumNodes();
}
return count;
}
void PsTreeNode::CheckInvariant() const
{
int sum = m_amount;
for (ceda::ssize_t i=0 ; i < m_children.size() ; ++i)
{
const PsTreeNode* c = m_children[i].Get();
cxAlwaysAssert(c);
c->CheckInvariant();
sum += c->m_totalAmount;
}
cxAlwaysAssert(sum == m_totalAmount);
}
void PsTreeNode::GetAllNodes(ceda::xvector<PsTreeNode*>& nodes)
{
nodes.push_back(this);
for (ceda::ssize_t i=0 ; i < m_children.size() ; ++i)
{
PsTreeNode* c = m_children[i].Get();
cxAlwaysAssert(c);
c->GetAllNodes(nodes);
}
}
void PsTreeNode::TraceTree() const
{
Tracer() << "A= " << m_amount << " T= " << m_totalAmount << '\n';
if (!m_children.empty())
{
Tracer() << "{\n";
{
ceda::TraceIndenter indent(2);
for (ceda::ssize_t i=0 ; i < m_children.size() ; ++i)
{
PsTreeNode* c = m_children[i].Get();
cxAlwaysAssert(c);
c->TraceTree();
}
}
Tracer() << "}\n";
}
}
bool PsTreeNode::IsResidentInStore() const
{
for (ceda::ssize_t i=0 ; i < m_children.size() ; ++i)
{
PsTreeNode* c = m_children[i].TryGet();
if (!c || !c->IsResidentInStore()) return false;
}
return true;
}
bool PsTreeNode::TraceSubTreeResidentInStore() const
{
bool allNodesResident = true;
Tracer() << "A= " << m_amount << " T= " << m_totalAmount << '\n';
if (!m_children.empty())
{
Tracer() << "{\n";
{
ceda::TraceIndenter indent(2);
for (ceda::ssize_t i=0 ; i < m_children.size() ; ++i)
{
if (PsTreeNode* c = m_children[i].TryGet())
{
// Recurse
if (!c->TraceSubTreeResidentInStore())
{
allNodesResident = false;
}
}
else
{
Tracer() << '[' << m_children[i].GetOid() << ']' << '\n';
allNodesResident = false;
}
}
}
Tracer() << "}\n";
}
return allNodesResident;
}
void PsTreeNode::GetOidsNotResidentInStore(ceda::xvector<ceda::OID>& oids) const
{
for (ceda::ssize_t i=0 ; i < m_children.size() ; ++i)
{
if (PsTreeNode* c = m_children[i].TryGet())
{
// Recurse
c->GetOidsNotResidentInStore(oids);
}
else
{
oids.push_back(m_children[i].GetOid());
}
}
}
void PsTreeNode::BuildTree(ceda::ssize_t depth, ceda::ssize_t fanout)
{
m_children.clear();
if (depth > 0)
{
m_children.resize(fanout);
for (ceda::ssize_t i=0 ; i < fanout ; ++i)
{
PsTreeNode* c = $new PsTreeNode;
c->m_parent = this;
m_children[i] = c;
DeclareReachable(this,c);
c->BuildTree(depth-1, fanout);
}
}
MarkAsDirtyWithoutTrace();
}