#ifndef TREE_REFERENCE_H #define TREE_REFERENCE_H #include "tree_pool.h" #include static inline int min(int i, int j) { return i < j ? i : j; } static inline int max(int i, int j) { return i > j ? i : j; } class Cursor; template class TreeReference { friend class TreeNode; friend class AdditionNode; friend class ExpressionNode; friend class Cursor; template friend class TreeReference; template friend class ExpressionReference; template friend class LayoutReference; public: TreeReference(const TreeReference & tr) { setTo(tr); } TreeReference(TreeReference&& tr) { setTo(tr); } TreeReference& operator=(const TreeReference& tr) { setTo(tr); return *this; } TreeReference& operator=(TreeReference&& tr) { setTo(tr); return *this; } inline bool operator==(TreeReference t) { return m_identifier == t.identifier(); } void setTo(const TreeReference & tr) { setIdentifierAndRetain(tr.identifier()); } TreeReference clone() const { TreeNode * myNode = node(); if (myNode->isAllocationFailure()) { int allocationFailureNodeId = myNode->allocationFailureNodeIdentifier(); return TreeReference(TreePool::sharedPool()->node(allocationFailureNodeId)); } TreeNode * nodeCopy = TreePool::sharedPool()->deepCopy(myNode); return TreeReference(nodeCopy); } ~TreeReference() { if (m_identifier >= 0) { assert(node()); assert(node()->identifier() == m_identifier); node()->release(); } } bool isDefined() const { return m_identifier >= 0 && node() != nullptr; } bool isAllocationFailure() const { return node()->isAllocationFailure(); } int nodeRetainCount() const { return node()->retainCount(); } void incrementNumberOfChildren(int increment = 1) { return node()->incrementNumberOfChildren(increment); } void decrementNumberOfChildren(int decrement = 1) { return node()->decrementNumberOfChildren(decrement); } operator TreeReference() const { return TreeReference(this->node()); } T * typedNode() const { // TODO: Here, assert that the node type is indeed T // ?? Might be allocation failure, not T return static_cast(node()); } TreeNode * node() const { return TreePool::sharedPool()->node(m_identifier); } int identifier() const { return m_identifier; } // Hierarchy int numberOfChildren() const { return node()->numberOfChildren(); } TreeReference parent() const { return TreeReference(node()->parentTree()); } TreeReference treeChildAtIndex(int i) const { return TreeReference(node()->childTreeAtIndex(i)); } // Hierarchy operations void addChild(TreeReference t) { return addChildAtIndex(t, 0); } void addChildAtIndex(TreeReference t, int index) { if (node()->isAllocationFailure()) { return; } assert(index >= 0 && index <= numberOfChildren()); // Retain t before detaching it, else it might get destroyed t.node()->retain(); // Detach t from its parent TreeReference tParent = t.parent(); if (tParent.isDefined()) { tParent.removeChild(t); } // Move t TreeNode * newChildPosition = node()->next(); for (int i = 0; i < index; i++) { newChildPosition = newChildPosition->nextSibling(); } TreePool::sharedPool()->move(t.node(), newChildPosition); node()->incrementNumberOfChildren(); } void removeChild(TreeReference t) { TreePool::sharedPool()->move(t.node(), TreePool::sharedPool()->last()); t.node()->release(); node()->decrementNumberOfChildren(); } void removeChildren() { node()->releaseChildren(); TreePool::sharedPool()->moveChildren(node(), TreePool::sharedPool()->last()); node()->eraseNumberOfChildren(); } void replaceWith(TreeReference t) { TreeReference p = parent(); if (p.isDefined()) { p.replaceChildAtIndex(p.node()->indexOfChildByIdentifier(identifier()), t); } } void replaceChildAtIndex(int oldChildIndex, TreeReference newChild) { if (newChild.isAllocationFailure()) { replaceWithAllocationFailure(); return; } TreeReference p = newChild.parent(); if (p.isDefined()) { p.decrementNumberOfChildren(); } assert(oldChildIndex >= 0 && oldChildIndex < numberOfChildren()); TreeReference oldChild = treeChildAtIndex(oldChildIndex); TreePool::sharedPool()->move(newChild.node(), oldChild.node()->next()); newChild.node()->retain(); TreePool::sharedPool()->move(oldChild.node(), TreePool::sharedPool()->last()); oldChild.node()->release(); } void replaceWithAllocationFailure() { TreeReference p = parent(); int indexInParentNode = node()->indexInParent(); int currentRetainCount = node()->retainCount(); TreeNode * staticAllocFailNode = typedNode()->failedAllocationStaticNode(); // Move the node to the end of the pool and decrease children count of parent TreePool::sharedPool()->move(node(), TreePool::sharedPool()->last()); if (p.isDefined()) { p.decrementNumberOfChildren(); } // Release all children and delete the node in the pool node()->releaseChildrenAndDestroy(); /* Create an allocation failure node with the previous node id. We know * there is room in the pool as we deleted the previous node and an * AllocationFailure nodes size is smaller or equal to any other node size.*/ //TODO static assert that the size is smaller TreeNode * newAllocationFailureNode = TreePool::sharedPool()->deepCopy(staticAllocFailNode); newAllocationFailureNode->rename(m_identifier); if (p.isDefined()) { assert(indexInParentNode >= 0); /* Set the refCount to previousRefCount-1 because the previous parent is * no longer retaining the node. When we add this node to the parent, it * will retain it and increment the retain count. */ newAllocationFailureNode->setReferenceCounter(currentRetainCount - 1); p.addChildAtIndex(newAllocationFailureNode, indexInParentNode); } else { newAllocationFailureNode->setReferenceCounter(currentRetainCount); } } void swapChildren(int i, int j) { assert(i >= 0 && i < numberOfChildren()); assert(j >= 0 && j < numberOfChildren()); if (i == j) { return; } int firstChildIndex = min(i, j); int secondChildIndex = max(i, j); TreeReference firstChild = treeChildAtIndex(firstChildIndex); TreeReference secondChild = treeChildAtIndex(secondChildIndex); TreeNode * firstChildNode = firstChild.node(); TreePool::sharedPool()->move(firstChildNode, secondChild.node()->next()); TreePool::sharedPool()->move(secondChild.node(), firstChildNode); } void mergeChildren(TreeReference t) { // Steal operands int numberOfNewChildren = t.numberOfChildren(); TreePool::sharedPool()->moveChildren(t.node(), node()->lastDescendant()->next()); t.node()->eraseNumberOfChildren(); // If t is a child, remove it if (node()->hasChild(t.node())) { removeChild(t); } node()->incrementNumberOfChildren(numberOfNewChildren); } protected: TreeReference() { TreeNode * node = TreePool::sharedPool()->createTreeNode(); m_identifier = node->identifier(); } TreeReference(TreeNode * node) { if (node == nullptr) { m_identifier = -1; } else { setIdentifierAndRetain(node->identifier()); } } void setIdentifierAndRetain(int newId) { m_identifier = newId; node()->retain(); } private: int m_identifier; }; typedef TreeReference TreeRef; #endif