// // File: hzIsam.cpp // // Legal Notice: This file is part of the HadronZoo C++ Class Library. Copyright 2025 HadronZoo Project (http://www.hadronzoo.com) // // The HadronZoo C++ Class Library is free software: You can redistribute it, and/or modify it under the terms of the GNU Lesser General Public License, as published by the Free // Software Foundation, either version 3 of the License, or any later version. // // The HadronZoo C++ Class Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR // A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License along with the HadronZoo C++ Class Library. If not, see http://www.gnu.org/licenses. //
#include <iostream>
#include "hzString.h" #include "hzLock.h" #include "hzProcess.h"
using namespace std ;
/* ** Prototypes */
#if 0 void Fatal (const char* va_alist ...) ;
#define CTMPL_NODESIZE 8
/* ** Section 2 Function Templates and support classes and class templatess */
// Compare template<class X> int _tmpl_zet_compare (const X a, const X b) { return a>b ? 1 : a<b ? -1 : 0 ; }
#define HZ_ORDSZE 8 #define HZ_ISAM_LOW 5
class _hz_zet_node_Idx ;
class _hz_zet_node_Any { // Category: Collection Support // // Pure virtual base class for index and data nodes
public: _hz_zet_node_Idx* parent ; // Parent node
virtual ~_hz_zet_node_Any (void) {}
virtual _hz_zet_node_Idx* Parent (void) = 0 ; virtual _hz_zet_node_Any* Infra (void) = 0 ; virtual _hz_zet_node_Any* Ultra (void) = 0 ;
virtual uint32_t Cumulative (void) = 0 ; virtual uint32_t Level (void) = 0 ; virtual uint32_t Usage (void) = 0 ; virtual uint32_t IsamId (void) = 0 ; virtual uint32_t Ident (void) = 0 ; virtual uint32_t InfId (void) = 0 ; virtual uint32_t UltId (void) = 0 ;
virtual void SetParent (_hz_zet_node_Idx*) = 0 ; } ;
class _hz_zet_node_Idx : public _hz_zet_node_Any { // Category: Collection Support // // Common Index nodes public: _hz_zet_node_Idx* infra ; // Infra adjacent _hz_zet_node_Idx* ultra ; // Ultra adjacent _hz_zet_node_Any* m_Ptrs[HZ_ORDSZE] ; // Pointers either to index or data nodes
uint32_t cumulative ; // Number of elements ultimately pointed to by this block uint32_t m_Id ; // Unique node id (for diagnostics) uint16_t m_isamId ; // Ties the node to the ISAM uint16_t usage ; // Number of elements in this block uint16_t level ; // Level of this block in the tree uint16_t resv ; // Reserved
_hz_zet_node_Idx (void) { //printf("NEW ISAM BLOCK\n") ; m_Id = ++_hzGlobal_Memstats.m_numIsamIndx ; m_Ptrs[0] = m_Ptrs[1] = m_Ptrs[2] = m_Ptrs[3] = m_Ptrs[4] = m_Ptrs[5] = m_Ptrs[6] = m_Ptrs[7] = 0 ; parent = infra = ultra = 0 ; cumulative = 0 ; m_isamId = usage = level = resv = 0 ; }
~_hz_zet_node_Idx (void) { _hzGlobal_Memstats.m_numIsamIndx-- ; }
_hz_zet_node_Idx* Parent (void) { return parent ; } _hz_zet_node_Idx* Infra (void) { return infra ; } _hz_zet_node_Idx* Ultra (void) { return ultra ; }
uint32_t Cumulative (void) { return cumulative ; } uint32_t Usage (void) { return usage ; } uint32_t Level (void) { return level ; } uint32_t IsamId (void) { return m_isamId ; } uint32_t Ident (void) { return m_Id ; } uint32_t InfId (void) { return infra ? infra->m_Id : 0 ; } uint32_t UltId (void) { return ultra ? ultra->m_Id : 0 ; }
void SetParent (_hz_zet_node_Idx* par) { parent = par ; } void SetLevel (int32_t v) { level = v ; } } ;
template <class KEY> class _hz_zet_node_Dat : public _hz_zet_node_Any { // Category: Collection Support // // Single datum data blocks for use in the _hz_isam_Relpos and _hz_isam_val ISAM templates.
public: _hz_zet_node_Dat<KEY>* infra ; // Infra adjacent _hz_zet_node_Dat<KEY>* ultra ; // Ultra adjacent
KEY m_Keys[HZ_ORDSZE] ; // Pointers either to mapentries or higher nodes uint32_t m_Id ; // Unique node id (for diagnostics) uint16_t m_isamId ; // Ties node to ISAM uint16_t usage ; // Number of elements in this block
_hz_zet_node_Dat (void) { //printf("NEW DATA A BLK %lu\n", sizeof(KEY)) ; m_Id = ++_hzGlobal_Memstats.m_numIsamData ; parent = 0 ; infra = ultra = 0 ; m_isamId = usage = 0 ; memset(m_Keys, 0, (HZ_ORDSZE * sizeof(KEY))) ; }
~_hz_zet_node_Dat (void) { _hzGlobal_Memstats.m_numIsamData-- ; }
_hz_zet_node_Dat<KEY>* allocUltra (void) { // Allocate ultra node to this // // Arguments: None // Returns: Pointer to the allocated ultra node
_hz_zet_node_Dat<KEY>* pUlt ; // Ultra node
pUlt = new _hz_zet_node_Dat<KEY> ;
if (ultra) ultra->infra = pUlt ; pUlt->ultra = ultra ; pUlt->infra = this ; pUlt->m_isamId = m_isamId ; ultra = pUlt ; return pUlt ; }
// Get values _hz_zet_node_Idx* Parent (void) { return parent ; } _hz_zet_node_Dat<KEY>* Infra (void) { return infra ; } _hz_zet_node_Dat<KEY>* Ultra (void) { return ultra ; }
uint32_t Cumulative (void) { return usage ; } uint32_t Usage (void) { return usage ; } uint32_t Level (void) { return 0 ; } uint32_t IsamId (void) { return m_isamId ; } uint32_t Ident (void) { return m_Id ; } uint32_t InfId (void) { return infra ? infra->m_Id : 0 ; } uint32_t UltId (void) { return ultra ? ultra->m_Id : 0 ; }
void* Keys (void) { return &m_Keys ; }
// Set values void SetParent (_hz_zet_node_Idx* par) { parent = par ; } void SetInfra (_hz_zet_node_Dat<KEY>* pInf) { infra = (_hz_zet_node_Dat<KEY>*) pInf ; } void SetUltra (_hz_zet_node_Dat<KEY>* pUlt) { ultra = (_hz_zet_node_Dat<KEY>*) pUlt ; }
void IncUsage (void) { usage++ ; } void DecUsage (void) { usage-- ; } void SetUsage (uint32_t u) { usage = u ; }
void setfromCur (int32_t slot, int32_t srcSlot) { m_Keys[slot] = m_Keys[srcSlot] ; } void setfromInf (int32_t slot, int32_t srcSlot) { m_Keys[slot] = infra->m_Keys[srcSlot] ; } void setfromUlt (int32_t slot, int32_t srcSlot) { m_Keys[slot] = ultra->m_Keys[srcSlot] ; } } ;
template <class KEY> class _hz_zet_isam_Base { // Category: Collection Support // // The _hz_zet_isam_Base is what holds the ISAM root (and thus the index and data) on behalf of ...
class _isam_ca { // ISAM control area
public: _hz_zet_node_Any* m_pRoot ; // Start of index hzLocker* m_pLock ; // Locking (off by default) uint32_t isamId ; // For diagnostics uint32_t m_nNodes ; // Number of nodes (also as node id allocator) uint32_t m_nElements ; // Number of data objects _m uint32_t m_copy ; // Copy count KEY m_Default ; // Default (null) object
_isam_ca (void) { m_pLock = 0 ; m_pRoot = 0 ; m_nNodes = m_nElements = m_copy = 0 ; memset(&m_Default, 0, sizeof(KEY)) ; }
~_isam_ca (void) { _clear() ; }
void _clearnode (_hz_zet_node_Any* pBlk) { // Recursively clear supplied node and any child nodes // // Argument: pBlk The supplied node // Returns: None
_hz_zet_node_Idx* pIdx ; // Current ISAM block pointer uint32_t n ; // ISAM block selector
if (!pBlk->Level()) delete pBlk ; else { pIdx = (_hz_zet_node_Idx*) pBlk ;
for (n = 0 ; n < pIdx->usage ; n++) _clearnode(pIdx->m_Ptrs[n]) ; delete pIdx ; } }
void _clear (void) { // Clear the map of all keys and elements // // Begin at root and take the least pointer out to the lowest data node and pass through the list of data nodes deleting them. Then drop back one level // to delete the index nodes level by level // // Arguments: None // Returns: None
if (!m_pRoot) return ; _clearnode(m_pRoot) ; m_nElements = 0 ; m_pRoot = 0 ; }
void SetLock (hzLockOpt eLock) { if (eLock == HZ_ATOMIC) m_pLock = new hzLockRW() ; if (eLock == HZ_MUTEX) m_pLock = new hzLockRWD() ; } } ;
_isam_ca* mz ; // The internal ISAM structure hzString m_Name ; // ISAM name (all ISAM dependent templates use this)
// Prevent copies _hz_zet_isam_Base<KEY> (const _hz_zet_isam_Base<KEY>&) ; _hz_zet_isam_Base<KEY>& operator= (const _hz_zet_isam_Base<KEY>&) ;
public:
_hz_zet_isam_Base (void) { mz = 0 ; }
~_hz_zet_isam_Base (void) { LockWrite() ; if (mz->m_copy) { mz->m_copy-- ; Unlock() ; } else { mz->_clear() ; Unlock() ; delete mz ; } _hzGlobal_Memstats.m_numIsams-- ; }
void Start (void) { mz = new _isam_ca() ; mz->isamId = ++_hzGlobal_Memstats.m_numIsams ; //printf("ALLOC ISAM INTERNAL %d\n", mz->isamId) ; fflush(stdout) ; }
void SetLock (hzLockOpt eLock) { if (eLock == HZ_ATOMIC) mz->m_pLock = new hzLockRW() ; else if (eLock == HZ_MUTEX) mz->m_pLock = new hzLockRWD() ; else mz->m_pLock = 0 ; }
void SetName (const hzString& name) { m_Name = name ; } void SetDefault (const KEY& key) { mz->m_Default = key ; }
void LockRead (void) { if (mz->m_pLock) mz->m_pLock->LockRead() ; } void LockWrite (void) { if (mz->m_pLock) mz->m_pLock->LockWrite() ; } void Unlock (void) { if (mz->m_pLock) mz->m_pLock->Unlock() ; }
void Clear (void) { // Clear the map of all keys and elements // // Begin at root and take the least pointer out to the lowest data node and pass through the list of data nodes deleting them. Then drop back one level // to delete the index nodes level by level // // Arguments: None // Returns: None
LockWrite() ; mz->_clear() ; Unlock() ; }
/* _hz_zet_isam_Base<KEY>& operator= (const _hz_zet_isam_Base<KEY>& op) { // If this vector has the same internal data as the operand do nothing - otherwise clear this vector first and then assign to the operand. if (mz && mz == op.mz) return *this ;
Clear() ; mz = op.mz ; m_Name = op.m_Name ; LockWrite() ; mz->m_copy++ ; Unlock() ; return *this ; } */
_hz_zet_node_Dat<KEY>* _nodetr (uint32_t& nOset, uint32_t nPosn, bool bExpand) const { // Purpose: This function finds for an absolute element position, the data node and slot where the element resides. It is the first of three 'data-node // locator' functions and the only one that can reside in a base ISAM as it does not rely on key comparison. These functions are critical since // all access to elements depend on them. // // Method: Starting at the root we add up the cumulatives until they exceed the required position. Then go up to the next level and do the same. // The exceptions to this are where there is no data and therefore no root or where the only node is the root. // // Arguments: 1) nOset A reference to store the node offset of the element // 2) nPosn The required position // 3) bExpand Expand flag. If set function can find the end postion + 1 (set for insert, clear for lookup) // // Returns: Pointer to the data node for the element
_hzfunc_ct("_hz_zet_isam_Base::_nodetr") ;
_hz_zet_node_Idx* pIdx = 0 ; // Current index node _hz_zet_node_Any* pChild = 0 ; // Can be index node or outer level data node _hz_zet_node_Dat<KEY>* pDN = 0 ; // Target data node
uint32_t nStart = 0 ; // True starting position of current node as per cumulatives uint32_t nMin = 0 ; // The real position within the collection of the current node's lowest element. uint32_t nMax = 0 ; // The real position within the collection of the current node's lowest element plus current node usage uint32_t n ; // Element iterator
// Check if there is a root if (!mz->m_pRoot) { nOset = -1 ; return 0 ; }
// Check limits nMax = bExpand ? mz->m_pRoot->Cumulative() : mz->m_pRoot->Cumulative()-1 ; if (nPosn > nMax) { nOset = -1 ; return 0 ; }
// If the root is the only node if (mz->m_pRoot->Level() == 0) { pDN = (_hz_zet_node_Dat<KEY>*) mz->m_pRoot ; nOset = nPosn ; return pDN ; }
// Common case, start at root which is an index node pIdx = (_hz_zet_node_Idx*) mz->m_pRoot ; nMax = nMin = nStart = 0 ;
for (nStart = 0 ; !pDN ;) { // Look on the node for the offset/pointer to the next node or the nMin = nStart ; for (n = 0 ; n < pIdx->Usage() ; n++) { pChild = (_hz_zet_node_Any*) pIdx->m_Ptrs[n] ; if (!pChild) Fatal("_hz__zt_isam_Base::_nodetr: isam=%d (%s): Missing node pointer on non leaf node", mz->isamId, *m_Name) ;
if (pChild->Parent() != pIdx) Fatal("_hz_zet_isam_Base::_nodetr: isam=%d (%s): Child node %d (level %d) does not point back to current node %d (level %d)", mz->isamId, *m_Name, pChild->Ident(), pChild->Level(), pIdx->m_Id, pIdx->level) ;
// If the cumulative exceeds the desired posn we have the node nStart = nMin ; nMax = nMin + pChild->Cumulative() ; if (nPosn < nMax) break ; nMin = nMax ; }
// Go to next level or accept data node as target if (pChild->Level()) pIdx = (_hz_zet_node_Idx*) pChild ; else pDN = (_hz_zet_node_Dat<KEY>*) pChild ; }
nOset = nPosn - nStart ;
if (nOset > pDN->Usage()) Fatal("_hz_zet_isam_Base::_nodetr: isam=%d (%s): Node %d usage %u has offset %u", mz->isamId, *m_Name, pDN->Ident(), pDN->Usage(), nOset) ;
return pDN ; }
hzEcode _acceptIndx (_hz_zet_node_Idx* pCur, _hz_zet_node_Any* pOld, _hz_zet_node_Any* pNew) { // This function is called on an index node which may have either data node pointers or index node pointers as elements. It is only called when a child node // pOld spawns a ultra sibling pNew. Note that nodes never spawn infra siblings. The action is to find the slot occupied by pOld and place pNew in the // next slot up. This is an insert action with all elements higher than pOld moving up one place to make room for pNew. // // In the event the current node pCur is null this is because pOld is currently the root and has no parent. A new root is formed with pOld in slot 0 and pNew // (which must be pOld's ultra node) in slot 1. // // In the event the current node is full we first check if the infra exists and has space. If it does we move slot 0 to the top slot of the infra, move // elements upto and including pOld down one place and put pNew in pOld's former slot. If the infra is full we see if a ultra has space. If not create // a new ultra node. We then insert this node's top top element in the ultra's slot 0. In the special case where pOld is in the top slot of this node // we just put pNew in the ultra slot 0. // // Note that if a new ultra sibling is created a pointer to it must be entered in the parent node. _acceptIndx is called on the parent node with this node // as pOld and the new sibling as pNew. If there is no parent a parent is created and both this node as the sibling added as children. This new parent node // is returned. The calling function (_acceptData) then sets this as the new root to the ISAM. // // Arguments: 1) pCur The pointer to the current node (parent to pOld and pNew) // 2) pOld The pointer to the pre-existing child node (prognitor) // 3) pNew The pointer to the newly created ultra sibling of the prognitor // // Returns: E_OK Always
_hzfunc_ct("_hz_zet_isam_Base::_acceptIndx") ;
_hz_zet_node_Any* pNode = 0 ; // Shift element _hz_zet_node_Idx* pIdxNew = 0 ; // New ultra sibling _hz_zet_node_Idx* pPar = 0 ; // Parent for cumulatives uint32_t slot = 0 ; // Slot found for existing pOld uint32_t n = 0 ; // Slot iterator uint32_t za ; // Shift cumulative 1 uint32_t zb ; // Shift cumulative 2
// Verify arguments if (!pOld) Fatal("_hz_zet_isam_Base::_acceptIndx: isam=%d (%s): No pre-existing item supplied", mz->isamId, *m_Name) ; if (!pNew) Fatal("_hz_zet_isam_Base::_acceptIndx: isam=%d (%s): No new item supplied", mz->isamId, *m_Name) ;
if (pNew->Infra() != pOld) Fatal("_hz_zet_isam_Base::_acceptIndx: isam=%d (%s): new %d does not point back to old %d", mz->isamId, *m_Name, pNew->Ident(), pOld->Ident()) ; if (pOld->Ultra() != pNew) Fatal("_hz_zet_isam_Base::_acceptIndx: isam=%d (%s): old %d does not point forward to new %d", mz->isamId, *m_Name, pOld->Ident(), pNew->Ident()) ;
// Do we need a new root? if (!pCur) { pCur = new _hz_zet_node_Idx() ; pCur->m_Ptrs[0] = pOld ; pCur->m_Ptrs[1] = pNew ; pOld->SetParent(pCur) ; pNew->SetParent(pCur) ; pCur->m_isamId = mz->isamId ; pCur->usage = 2 ; pCur->cumulative = pOld->Cumulative() + pNew->Cumulative() ; pCur->level = pOld->Level()+1 ; mz->m_pRoot = pCur ; return E_OK ; }
// Given we have a current node, further checks if ((pOld->Level()+1) != pCur->level) Fatal("_hz_zet_isam_Base::_acceptIndx: isam=%d (%s): At level %d but old element level is %d", mz->isamId, *m_Name, pCur->level, pOld->Level()) ; if ((pNew->Level()+1) != pCur->level) Fatal("_hz_zet_isam_Base::_acceptIndx: isam=%d (%s): At level %d but new element level is %d", mz->isamId, *m_Name, pCur->level, pNew->Level()) ;
if (!pNew->Parent()) pNew->SetParent(pCur) ; if (pNew->Parent() != pCur) Fatal("%s: isam=%d (%s): new %d does not have this node (%d) as its parent instad it has %d", mz->isamId, *m_Name, pNew->Ident(), pCur->m_Id, pNew->Parent()->Ident()) ;
// Find out which slot pOld is on for (slot = 0 ; slot < pCur->usage ; slot++) { if (pCur->m_Ptrs[slot] == pOld) break ; }
if (slot == pCur->usage) Fatal("_hz_zet_isam_Base::_acceptIndx: isam=%d (%s): On node %d: Child node %d not found", mz->isamId, *m_Name, pCur->m_Id, pOld->Ident()) ; slot++ ;
// If there is space on this node just insert the new element if (pCur->usage < HZ_ORDSZE) { for (n = pCur->usage ; n > slot ; n--) pCur->m_Ptrs[n] = pCur->m_Ptrs[n-1] ; pCur->m_Ptrs[slot] = pNew ; pCur->usage++ ;
// Update cumulatives pCur->cumulative += pNew->Cumulative() ; for (pPar = pCur->parent ; pPar ; pPar = pPar->parent) pPar->cumulative += pNew->Cumulative() ; return E_OK ; }
// This node is full so we first see if we have an infra and with space if (pCur->infra && pCur->infra->usage < HZ_ORDSZE) { // Move element 0 to infra node pNode = pCur->m_Ptrs[0] ; za = pNode->Cumulative() ; pNode->SetParent(pCur->infra) ; pCur->infra->m_Ptrs[pCur->infra->usage] = pNode ; pCur->infra->usage++ ;
// Insert the new element slot-- ; for (n = 0 ; n < slot ; n++) pCur->m_Ptrs[n] = pCur->m_Ptrs[n+1] ; zb = pNew->Cumulative() ; pNew->SetParent(pCur) ; pCur->m_Ptrs[slot] = pNew ;
// Update cumulatives pCur->infra->cumulative += za ; pCur->cumulative += (zb - za) ; if (pCur->parent == pCur->infra->parent) { for (pPar = pCur->parent ; pPar ; pPar = pPar->parent) pPar->cumulative += zb ; } else { for (pPar = pCur->infra->parent ; pPar ; pPar = pPar->parent) pPar->cumulative += za ; for (pPar = pCur->parent ; pPar ; pPar = pPar->parent) pPar->cumulative += (zb - za) ; } return E_OK ; }
// No room on the infra so we try the ultra. If full or non-existant, create an empty one. if (!pCur->ultra || pCur->ultra->usage == HZ_ORDSZE) { pIdxNew = new _hz_zet_node_Idx ; if (!pIdxNew) Fatal("_hz_zet_isam_Base::_acceptIndx: Could not allocate sibling\n") ;
pIdxNew->m_isamId = mz->isamId ; pIdxNew->level = pCur->level ; if (pCur->ultra) pCur->ultra->infra = pIdxNew ; pIdxNew->ultra = pCur->ultra ; pIdxNew->infra = pCur ; pCur->ultra = pIdxNew ;
// The new sibling must be given a parent. _acceptIndx(pCur->parent, pCur, pIdxNew) ; }
// OK now we can boogie as there is a ultra with space and parent if (slot == HZ_ORDSZE) { // pOld is in this node's top slot so pNew goes into slot 0 of ultra zb = pNew->Cumulative() ; for (n = pCur->ultra->usage ; n ; n--) pCur->ultra->m_Ptrs[n] = pCur->ultra->m_Ptrs[n-1] ; pCur->ultra->m_Ptrs[0] = pNew ; pNew->SetParent(pCur->ultra) ; pCur->ultra->usage++ ; pCur->ultra->cumulative += zb ;
for (pPar = pCur->ultra->parent ; pPar ; pPar = pPar->parent) pPar->cumulative += zb ; return E_OK ; }
// Higest element of this node goes into slot 0 of ultra, then new element inserted into this node for (n = pCur->ultra->usage ; n ; n--) pCur->ultra->m_Ptrs[n] = pCur->ultra->m_Ptrs[n-1] ; pNode = pCur->m_Ptrs[pCur->usage-1] ; za = pNode->Cumulative() ; pNode->SetParent(pCur->ultra) ; pCur->ultra->m_Ptrs[0] = pNode ; pCur->ultra->usage++ ; pCur->ultra->cumulative += za ;
// Insert new element into this node zb = pNew->Cumulative() ; for (n = pCur->usage-1 ; n > slot ; n--) pCur->m_Ptrs[n] = pCur->m_Ptrs[n-1] ; pCur->m_Ptrs[slot] = pNew ; pCur->cumulative += (zb - za) ;
if (pCur->parent == pCur->ultra->parent) { for (pPar = pCur->parent ; pPar ; pPar = pPar->parent) pPar->cumulative += zb ; } else { for (pPar = pCur->ultra->parent ; pPar ; pPar = pPar->parent) pPar->cumulative += za ; for (pPar = pCur->parent ; pPar ; pPar = pPar->parent) pPar->cumulative += (zb - za) ; }
return E_OK ; }
void _expelIndx (_hz_zet_node_Idx* pCur, _hz_zet_node_Any* pChild) { // Expel the defunct child from this node. If this action leaves the current node disposable (empty or sparse enough to migrate its elements to its siblings) // the current node will be excluded from its peers by connecting the infra and ultra siblings to each other. In this event and if this node has a parent // ExpelIndex is called for the parent with this node as the child to be disposed of. // // Arguments: 1) pCur Parent ISAM block // 2) pChild Child ISAM block to be removed // // Returns: None
_hzfunc_ct("_hz_zet_node_Idx::_expelIndx") ;
_hz_zet_node_Any* pXfer ; // Element (block pointer) being moved _hz_zet_node_Idx* pPar = 0 ; // Parent for cumulatives uint32_t x = 0 ; // No of elements moved to infra uint32_t y = 0 ; // Offset into current node for copying elements to ultra uint32_t slot ; // Slot of element being deleted uint32_t za = 0 ; // Shift cumulative 1 uint32_t zb = 0 ; // Shift cumulative 2
if (!pCur) Fatal("_hz_zet_node_Idx::_expelIndx: isam=%d (%s): No node supplied from which to expell", *m_Name, mz->isamId) ; if (!pChild) Fatal("_hz_zet_node_Idx::_expelIndx: isam=%d (%s): No child node supplied to expell from node %d", mz->isamId, *m_Name, pCur->m_Id) ;
for (slot = 0 ; slot < pCur->usage ; slot++) { if (pCur->m_Ptrs[slot] == pChild) break ; } if (slot == pCur->usage) Fatal("_hz_zet_node_Idx::_expelIndx: isam=%d (%s): No pointer to depricated child node found on this node (%d)", mz->isamId, *m_Name, pCur->m_Id) ;
if (slot < 0 || slot >= pCur->usage) Fatal("_hz_zet_node_Idx::_expelIndx: isam=%d (%s): Node %d Illegal slot value (%d)", mz->isamId, *m_Name, pCur->m_Id, slot) ;
pCur->usage-- ; for (x = slot, y = pCur->usage ; x < y ; x++) pCur->m_Ptrs[x] = pCur->m_Ptrs[x+1] ; pCur->m_Ptrs[pCur->usage] = 0 ; pCur->cumulative -= pChild->Cumulative() ; for (pPar = pCur->parent ; pPar ; pPar = pPar->parent) pPar->cumulative -= pChild->Cumulative() ; delete pChild ;
if (pCur->usage && pCur->usage < HZ_ISAM_LOW) { // Now that an entry has been removed, can we merge the current node with either or both adjacents? x = pCur->infra ? HZ_ORDSZE - pCur->infra->usage : 0 ; y = pCur->ultra ? HZ_ORDSZE - pCur->ultra->usage : 0 ;
if ((x + y) >= pCur->usage) { // Move all that we can to infra node first if (pCur->infra && pCur->infra->usage < HZ_ORDSZE) { for (x = 0 ; pCur->usage && pCur->infra->usage < HZ_ORDSZE ; x++) { pXfer = pCur->m_Ptrs[x] ; pXfer->SetParent(pCur->infra) ;
pCur->infra->m_Ptrs[pCur->infra->usage] = pXfer ; za += pXfer->Cumulative() ;
pCur->infra->usage++ ; pCur->usage-- ; } pCur->infra->cumulative += za ;
for (pPar = pCur->infra->parent ; pPar ; pPar = pPar->parent) pPar->cumulative += za ; }
if (pCur->ultra && pCur->ultra->usage < HZ_ORDSZE) { // If any elements remain on the current block we need to shift upwards the elements on the ultra to accomodate them for (y = pCur->ultra->usage + pCur->usage - 1 ; y >= pCur->usage ; y--) pCur->ultra->m_Ptrs[y] = pCur->ultra->m_Ptrs[y - pCur->usage] ;
// Move remainder to ultra node for (y = 0 ; y < pCur->usage ; y++) { pXfer = pCur->m_Ptrs[x + y] ; pXfer->SetParent(pCur->ultra) ;
pCur->ultra->m_Ptrs[y] = pXfer ; zb += pXfer->Cumulative() ; } pCur->usage = 0 ;
pCur->ultra->cumulative += zb ; pCur->ultra->usage += y ; for (pPar = pCur->ultra->parent ; pPar ; pPar = pPar->parent) pPar->cumulative += zb ; } } }
if (!pCur->usage) { // Connect adjacents if (pCur->ultra) pCur->ultra->infra = pCur->infra ; if (pCur->infra) pCur->infra->ultra = pCur->ultra ;
if (pCur->parent) _expelIndx(pCur->parent, pCur) ; } }
_hz_zet_node_Dat<KEY>* _acceptData (_hz_zet_node_Dat<KEY>* pDN, const KEY& key, uint32_t& nSlot) { // With a target data node and slot identified by _nodetr this funtion performs an insert. This has the following outcomes:- // // 1) If the target is not full the new entry will be placed in the target at the designated slot, shifting entries above the slot up by one place. // 2) If the target node is full but the target's infra node has space then one of two things will happen:- // a) If the designated slot is 0 then the new entry will be placed directly in the top slot of the infra node. // b) If the designated slot is above 0 then slot 0 of the target is moved to the top slot of the infra node, the designated slot is reduced by 1 and all // slots lower than the new designated slot is shifted down one place. // 3) If the target node is full and there is no room on an infra, there will always be room on an ultra node. This is because if the ultra does not exist or // is full, a new ultra will be created. Then one of two things will happen:- // a) If the designated slot is HZ_ORDSZE (which is possible if _nodetr() is called with bExpand = true), then the new entry will be placed in slot 0 of // the ultra node. // b) If the designated slot is < HZ_ORDSZE then the top slot of the target node is moved to slot 0 of the ultra and then the insert will proceed as in // scenario (1) // // It is essential that this function informs the called of the node and slot the new entry actually ended up in as only the key can be placed. If there are // objects to be placed alongside the key, this will be done by the caller after this function has returned. To this end the actual data node is returned and // nSlot, supplied as an int32_t& is set. // // Arguments 1) The target node identified by _nodetr() // 2) The key value // 3) The reference to target slot identified by _nodetr(), _findDnodeByKey() or _findAbsoluteByKey() and adjusted by this function. // // Returns Pointer to the actual node the new entry resides in // // Note that the target slot can be set to HZ_ORDSZE by _nodetr(), _findDnodeByKey() or _findAbsoluteByKey() if and only if the target node is full and has // no ultra. After this function the slot will always have a value between 0 and HZ_ORDSZE-1
_hzfunc_ct("_hz_zet_isam_Base::_acceptData") ;
_hz_zet_node_Dat<KEY>* pInfra = 0 ; // New data node _hz_zet_node_Dat<KEY>* pUltra = 0 ; // New data node _hz_zet_node_Idx* pIdx ; // Index node (for updating cimulatives)
KEY* pKeys ; // Access to node's keys uint32_t n ; // Loop controller
// Test inputs if (!pDN) Fatal("_hz_zet_isam_Base::_acceptData: isam=%d (%s): No taget data node supplied", mz->isamId, *m_Name) ;
n = pDN->Usage() ; if (n > HZ_ORDSZE) Fatal("_hz_zet_isam_Base::_acceptData: isam=%d (%s): Garbage usage value (%d)", mz->isamId, *m_Name, n) ; if (nSlot > HZ_ORDSZE) Fatal("_hz_zet_isam_Base::_acceptData: isam=%d (%s): Illegal slot value (%d)", mz->isamId, *m_Name, nSlot) ; if (nSlot > pDN->Usage()) Fatal("_hz_zet_isam_Base::_acceptData: isam=%d (%s): Excessive slot value (%d)", mz->isamId, *m_Name, nSlot) ;
mz->m_nElements++ ;
// If new element can be accomodated on this node then add it and return if (pDN->Usage() < HZ_ORDSZE) { for (n = pDN->Usage() ; n > nSlot ; n--) pDN->setfromCur(n, n-1) ;
pKeys = (KEY*) pDN->Keys() ; pKeys[nSlot] = key ; pDN->IncUsage() ;
for (pIdx = pDN->parent ; pIdx ; pIdx = pIdx->parent) pIdx->cumulative++ ; return pDN ; }
// This node is full so we first see if the first element can be moved to this node's infra, if it exists pInfra = (_hz_zet_node_Dat<KEY>*) pDN->Infra() ; if (pInfra && pInfra->Usage() < HZ_ORDSZE) { if (nSlot == 0) { // Store object direcly in top slot in infra node nSlot = pInfra->Usage() ; pKeys = (KEY*) pInfra->Keys() ; pKeys[nSlot] = key ; pInfra->IncUsage() ; for (pIdx = pInfra->parent ; pIdx ; pIdx = pIdx->parent) pIdx->cumulative++ ; return pInfra ; }
// Move element 0 to infra node, decrement slot move all elements upto slot down one place, store object in slot pInfra->setfromUlt(pInfra->Usage(), 0) ; pInfra->IncUsage() ; for (pIdx = pInfra->parent ; pIdx ; pIdx = pIdx->parent) pIdx->cumulative++ ;
nSlot-- ; for (n = 0 ; n < nSlot ; n++) pDN->setfromCur(n, n+1) ; pKeys = (KEY*) pDN->Keys() ; pKeys[nSlot] = key ; return pDN ; }
// No room on the infra so we try the ultra. If there isn't one or there is but it is full, create an empty one. Note that if we create a new node at // this point, we don't update the index cumulatives below. This is because we are going to return the new node so that it will be added to the parent and // at that point the cumulatives on the parent index nodes will be adjusted. pUltra = (_hz_zet_node_Dat<KEY>*) pDN->Ultra() ; if (!pUltra || pUltra->Usage() == HZ_ORDSZE) { pUltra = pDN->allocUltra() ; if (!pUltra) Fatal("_hz_zet_isam_Base::_acceptData: Could not allocate sibling") ;
// Put this new node into the index _acceptIndx((_hz_zet_node_Idx*) pDN->Parent(), pDN, pUltra) ; }
// OK now we can boogie as there is a ultra with space for (n = pUltra->Usage() ; n > 0 ; n--) pUltra->setfromCur(n, n-1) ;
if (nSlot == HZ_ORDSZE) { // New element goes into slot 0 of ultra pKeys = (KEY*) pUltra->Keys() ; pKeys[0] = key ; pUltra->IncUsage() ; for (pIdx = pUltra->parent ; pIdx ; pIdx = pIdx->parent) pIdx->cumulative++ ; nSlot = 0 ; return pUltra ; }
// Higest element of this node goes into slot 0 of ultra, then new element inserted into this node pUltra->setfromInf(0, pDN->Usage()-1) ; pUltra->IncUsage() ; for (pIdx = pUltra->parent ; pIdx ; pIdx = pIdx->parent) pIdx->cumulative++ ;
// Insert new element into this node for (n = pDN->Usage()-1 ; n > nSlot ; n--) pDN->setfromCur(n, n-1) ; pKeys = (KEY*) pDN->Keys() ; pKeys[nSlot] = key ; return pDN ; }
_hz_zet_node_Dat<KEY>* _expelData (_hz_zet_node_Dat<KEY>* pDN, uint32_t nSlot) { // The node can be removed if empty or if all remaining elements can be migrated to its infra and ultra adjacents. By convention we migrate what // we can to the infra adjacent first and then migrate any remaining to the ultra adjacent. // // Arguments: 1) pDN The node from which an element is to be removed // 2) nSlot The slot number of the depricated element // // Returns: Pointer to the current node remanent // NULL if the current node is emptied by this action // // Note: In most cases, the return is ignored by the caller.
_hzfunc_ct("_hz_zet_isam_Base::_expelData") ;
_hz_zet_node_Idx* pPar ; // For setting cumulatives in index _hz_zet_node_Dat<KEY>* pInfra ; // For setting cumulatives in index _hz_zet_node_Dat<KEY>* pUltra ; // For setting cumulatives in index KEY* pKeys ; // Keys on node uint32_t x = 0 ; // Loop control, doubles as No of elements moved to infra uint32_t y = 0 ; // Loop control, doubles as No of elements moved to ultra uint32_t ino ; // ISAM number
// Test inputs if (!pDN) Fatal("_hz_zet_isam_Base::_expelData: isam=%d (%s): No taget data node supplied", mz->isamId, *m_Name) ; ino = pDN->IsamId() ;
x = pDN->Usage() ; if (x > HZ_ORDSZE) Fatal("_hz_zet_isam_Base::_expelData: isam=%d (%s): Garbage usage value (%d)", mz->isamId, *m_Name, x) ; if (nSlot > HZ_ORDSZE) Fatal("_hz_zet_isam_Base::_expelData: isam=%d (%s): Illegal slot value (%d)", mz->isamId, *m_Name, nSlot) ; if (nSlot >= pDN->Usage()) Fatal("_hz_zet_isam_Base::_expelData: isam=%d (%s): Excessive slot value (%d)", mz->isamId, *m_Name, nSlot) ;
mz->m_nElements-- ;
// Exclude the slot pDN->DecUsage() ; for (x = nSlot, y = pDN->Usage() ; x < y ; x++) pDN->setfromCur(x, x+1) ; pKeys = (KEY*) pDN->Keys() ; pKeys[pDN->Usage()] = mz->m_Default ;
for (pPar = pDN->parent ; pPar ; pPar = pPar->parent) pPar->cumulative-- ;
// See if reduced node can be purged if (pDN->Usage() && pDN->Usage() < HZ_ISAM_LOW) { pInfra = (_hz_zet_node_Dat<KEY>*) pDN->Infra() ; pUltra = (_hz_zet_node_Dat<KEY>*) pDN->Ultra() ;
x = pInfra ? HZ_ORDSZE - pInfra->Usage() : 0 ; y = pUltra ? HZ_ORDSZE - pUltra->Usage() : 0 ;
if ((x + y) >= pDN->Usage()) { // All remaining slots on this node can be accomodated by adjacents. Begin by moving all that we can to infra node first x = y = 0 ; if (pInfra && pInfra->Usage() < HZ_ORDSZE) { for (x = 0 ; pDN->Usage() && pInfra->Usage() < HZ_ORDSZE ; x++) { pInfra->setfromUlt(pInfra->Usage(), x) ; pInfra->IncUsage() ; pDN->DecUsage() ; }
for (pPar = pInfra->Parent() ; pPar ; pPar = pPar->parent) pPar->cumulative += x ; if (pInfra->IsamId() != ino || pInfra->Usage() < 0 || pInfra->Usage() > HZ_ORDSZE) Fatal("_hz_zet_isam_Base::_expelData: isam=%d (%s): Infra %d Invalid: isam id (%d) usage (%d)", ino, *m_Name, pInfra->Ident(), pInfra->IsamId(), pInfra->Usage()) ; }
if (pUltra && pDN->Usage() && pUltra->Usage() < HZ_ORDSZE) { // If any elements remain on the current block we need to shift upwards the elements on the ultra to accomodate them for (y = pUltra->Usage() + pDN->Usage() - 1 ; y >= pDN->Usage() ; y--) pUltra->setfromCur(y, y - pDN->Usage()) ; pUltra->SetUsage(pUltra->Usage() + pDN->Usage()) ;
// Move remainder to ultra node for (y = 0 ; y < pDN->Usage() ; y++) pUltra->setfromInf(y, x + y) ; pDN->SetUsage(0) ;
for (pPar = pUltra->Parent() ; pPar ; pPar = pPar->parent) pPar->cumulative += y ; if (pUltra->IsamId() != ino || pUltra->Usage() < 0 || pUltra->Usage() > HZ_ORDSZE) Fatal("_hz_zet_isam_Base::_expelData: isam=%d (%s): Ultra %d Invalid: isam id (%d) usage (%d)", ino, *m_Name, pUltra->Ident(), pUltra->IsamId(), pUltra->Usage()) ; }
x += y ; for (pPar = pDN->Parent() ; pPar ; pPar = pPar->parent) pPar->cumulative -= x ; } }
if (pDN->Usage()) return pDN ;
// Eliminate node: Connect adjacents pInfra = (_hz_zet_node_Dat<KEY>*) pDN->Infra() ; pUltra = (_hz_zet_node_Dat<KEY>*) pDN->Ultra() ; if (pUltra) pUltra->SetInfra(pInfra) ; if (pInfra) pInfra->SetUltra(pUltra) ;
if (pDN->Parent()) _expelIndx(pDN->Parent(), pDN) ; else { delete pDN ; mz->m_pRoot = 0 ; }
return 0 ; }
bool _shownode (_hz_zet_node_Any* pNode, hzLogger* plog, bool bData) const { // Support function to Show(). Prints out elements for a given node. // // Arguments: 1) pNode The current node // 2) plog The logger to use for output // 3) bData If set, output data part of element // // Returns: True If there were no integrity issues // False If there were
_hz_zet_node_Idx* pIdx = 0 ; // Index node _hz_zet_node_Any* pChild ; // Child from slot _hz_zet_node_Any* pPrev ; // Lower adjacent sibling _hz_zet_node_Any* pNext ; // Higher adjacent sibling
uint32_t nIndex ; // Iterator of children uint32_t nTotal ; // Cumulative uint32_t testA ; // Id of derived node uint32_t testB ; // Id of derived node uint32_t par ; // Parent node uint32_t inf ; // Inferior node uint32_t sup ; // superior node bool bError = false ; // Error indicator
par = pNode->Parent() ? pNode->Parent()->Ident() : -1 ; inf = pNode->Infra()? pNode->Infra()->Ident() : -1 ; sup = pNode->Ultra()? pNode->Ultra()->Ident() : -1 ;
if (!pNode->Level()) { if (bData) plog->Out("\treport isam=%d Data Node %d parent %d inf %d sup %d level %d, usage %2d\n", mz->isamId, pNode->Ident(), par, inf, sup, pNode->Level(), pNode->Usage()) ; return true ; }
pIdx = (_hz_zet_node_Idx*) pNode ; if (bData) plog->Out("report isam=%d Index Node %d parent %d inf %d sup %d level %d, usage %2d cumulate %d\n", mz->isamId, pNode->Ident(), par, inf, sup, pNode->Level(), pNode->Usage(), pNode->Cumulative()) ;
if (pNode->Level() < 0 || pNode->Level() > 10) { plog->Out("\tOut of range level - no furthur analysis of node.\n") ; return false ; }
if (pNode->Usage() < 0 || pNode->Usage() > HZ_ORDSZE) { plog->Out("\tOut of range usage - no furthur analysis of node.\n") ; return false ; }
// Check pointers for (nIndex = 0 ; nIndex < pIdx->Usage() ; nIndex++) { pChild = pIdx->m_Ptrs[nIndex] ; if (!pChild) { bError = true ; plog->Out("\tInvalid pointer in posn %d\n", nIndex) ; } }
// Check cumulatives nTotal = 0 ; for (nIndex = 0 ; nIndex < pIdx->Usage() ; nIndex++) { pChild = (_hz_zet_node_Any*) pIdx->m_Ptrs[nIndex] ; nTotal += pChild->Cumulative() ; } if (nTotal != pIdx->Cumulative()) { bError = true ; plog->Out(" -- ERROR: Counted cumulative for index node %d is %d, but empirical cumulatives is %d\n", pIdx->m_Id, nTotal, pIdx->Cumulative()) ; }
// Recurse to higher nodes for (nIndex = 0 ; nIndex < pIdx->Usage() ; nIndex++) { pChild = (_hz_zet_node_Any*) pIdx->m_Ptrs[nIndex] ; if (!_shownode(pChild, plog, bData)) bError = true ; }
// Check integrity of this node in connection with its neighbours for (nIndex = 0 ; nIndex < pIdx->Usage() ; nIndex++) { pChild = (_hz_zet_node_Any*) pIdx->m_Ptrs[nIndex] ;
// We are not on a data node
if (pChild->Parent() != pNode) { bError = true ; plog->Out(" -- ERROR: Child in posn %d (%d) does not have this node (%d) as it's parent. Instead it has (%d)\n", nIndex, pChild->Ident(), pNode->Ident(), pChild->Parent()?pChild->Parent()->Ident():-1) ; continue ; }
// Test siblings
if (nIndex == 0) { if (!pIdx->infra && pChild->Infra()) { bError = true ; plog->Out(" -- ERROR: Node %d While there is no infra node to this, the zeroth child has an infra (%d)\n", pIdx->m_Id, pChild->Infra()->Ident()) ; } if (pIdx->infra && !pChild->Infra()) { bError = true ; plog->Out(" -- ERROR: Node %d There is an infra to this (%d) but the zeroth child does not have an infra\n", pIdx->m_Id, pIdx->infra->m_Id) ; } if (pIdx->infra && pChild->Infra()) { testA = pChild->Infra() && pChild->Infra()->Parent() ? pChild->Infra()->Parent()->Ident() : -1 ; testB = pIdx->infra ? pIdx->infra->m_Id : -1 ;
if (testA != testB) { bError = true ; plog->Out(" -- ERROR: Node %d The parent of the zeroth child infra (%d) is not the infra of this (%d)\n", pIdx->m_Id, testA, testB) ; } } } else if (nIndex == (pIdx->Usage()-1)) { if (!pIdx->ultra && pChild->Ultra()) { bError = true ; plog->Out(" -- ERROR: While there is no ultra node to this node, the highest child has a ultra\n") ; } if (pIdx->ultra && !pChild->Ultra()) { bError = true ; plog->Out(" -- ERROR: There is an ultra to this node but the highest child does not have a ultra\n") ; } if (pIdx->infra && pChild->Ultra()) { if (pChild->Ultra()->Parent() != pIdx->ultra) { bError = true ; plog->Out(" -- ERROR: The parent of the highest child ultra is not the ultra of this node\n") ; } } } else { pPrev = (_hz_zet_node_Any*) pIdx->m_Ptrs[nIndex-1] ; pNext = (_hz_zet_node_Any*) pIdx->m_Ptrs[nIndex+1] ;
if (pChild->Infra() != pPrev) { bError = true ; plog->Out(" -- ERROR: Child %d infra is not the same as child %d\n", nIndex, nIndex-1) ; } if (pChild->Ultra() != pNext) { bError = true ; plog->Out(" -- ERROR: Child %d ultra is not the same as child %d\n", nIndex, nIndex+1) ; } } }
return bError ? false : true ; }
hzEcode NodeReport (bool bData = false) const { // Category: Diagnostics // // Starting from the root node and moving out to data nodes, this checks that nodes do not contain null pointers, have correct parent pointers and have // correct cumulative values // // Arguments: 1) bData If set, print data object // // Returns: E_CORRUPT If any node has integrity issues // E_OK If all nodes succeed
hzLogger* plog ; // Current thread logger hzEcode rc = E_OK ; // Return code
plog = GetThreadLogger() ; if (plog) { if (!mz->m_pRoot) { plog->Out("The _hz_zet_isam_Base object is empty\n") ; return E_OK ; }
if (!_shownode(mz->m_pRoot, plog, bData)) rc = E_CORRUPT ; } return rc ; }
hzEcode _integBase (hzLogger* plog, _hz_zet_node_Idx* pN) const { // Quick index node integrity test // // Arguments: 1) plog Logger // 2) pN Current node // // Returns: E_CORRUPT If node fails integrity test // E_OK If node passes
_hz_zet_node_Any* pInf ; // Infra to the current node _hz_zet_node_Any* pUlt ; // Ultra to the current node _hz_zet_node_Any* pCA ; // Child node A _hz_zet_node_Any* pCB ; // Child node B _hz_zet_node_Idx* par ; // Parent of test node
uint32_t n ; // Loop control uint32_t err = 0 ; // Set if error
pInf = pN->Infra() ; pUlt = pN->Ultra() ;
// Test if the infra and ultra pointers point back to current node if (pInf && (pInf->Ultra() != pN)) { err=1 ; plog->Out("On node %d: Infra node %d does not point back to this node\n", pN->Ident(), pInf->Ident()) ; } if (pUlt && (pUlt->Infra() != pN)) { err=1 ; plog->Out("On node %d: Infra node %d does not point back to this node\n", pN->Ident(), pInf->Ident()) ; }
// Test if the slot 0 child has an infra whose parent is this node's infra pCA = pN->m_Ptrs[0] ; if (pInf) { if (!pCA->Infra()) { err=1 ; plog->Out("On node %d: Child 0 (%d) has no infra while this node does\n", pN->Ident(), pCA->Ident()) ; } else { par = pCA->Infra()->Parent() ; if (!par) { err=1 ; plog->Out("On node %d: Child 0 (%d) has parent -1 diff to the infra %d\n", pN->Ident(), pCA->Ident(), pInf->Ident()) ; } if (par && par != pInf) { err=1 ; plog->Out("On node %d: Child 0 (%d) has parent %d diff to the infra %d\n", pN->Ident(), pCA->Ident(), par->Ident(), pInf->Ident()) ; } } }
// Test if the top child has an ultra whose parent is this node's ultra pCB = pN->m_Ptrs[pN->Usage()-1] ; if (pUlt) { if (!pCB->Ultra()) { err=1 ; plog->Out("On node %d: Child 0 (%d) has no infra while this node does\n", pN->Ident(), pCB->Ident()) ; } else { par = pCB->Ultra()->Parent() ; if (!par) { err=1 ; plog->Out("On node %d: Child 0 (%d) has parent -1 diff to the infra %d\n", pN->Ident(), pCB->Ident(), pUlt->Ident()) ; } if (par && par != pUlt) { err=1 ; plog->Out("On node %d: Child 0 (%d) has parent %d diff to the infra %d\n", pN->Ident(), pCB->Ident(), par->Ident(), pUlt->Ident()) ; } } }
// For each slot test if the child's infra points to the child of the next lower slot // For each slot test if the child's ultra points to the child of the next higher slot for (n = pN->Usage()-1 ; n > 0 ; n--) { pCA = pN->m_Ptrs[n-1] ; if (pCA->Ultra() != pCB) { err=1; plog->Out("On node %d: Child in slot %d has infra of %d but slot %d points to %d\n", pN->Ident(), n+1, pCA->InfId(), n, pCB->Ident()) ; } if (pCB->Infra() != pCA) { err=1; plog->Out("On node %d: Child in slot %d has ultra of %d but slot %d points to %d\n", pN->Ident(), n, pCA->UltId(), n+1, pCA->Ident()) ; } pCB = pN->m_Ptrs[n-1] ; }
// Recurse to child nodes if (pN->Level() > 1) { for (n = 0 ; n < pN->Usage() ; n++) { if (_integBase(plog, (_hz_zet_node_Idx*) pN->m_Ptrs[n]) != E_OK) err=1; } }
return err ? E_CORRUPT : E_OK ; }
hzEcode IntegBase (void) const { // This test that index node are consistent. For each node pointer in an index node, the node pointed to is tested to ensure it's adjacents agree with .. // // Arguments: None // // Returns: E_CORRUPT If any ode fails integrity test // E_OK If all nodes pass
_hz_zet_node_Idx* pN ; // ISAM root hzLogger* plog ; // Current thread logger
if (!mz->m_pRoot) return E_OK ; if (!mz->m_pRoot->Level()) return E_OK ;
pN = (_hz_zet_node_Idx*) mz->m_pRoot ; plog = GetThreadLogger() ; return _integBase(plog, pN) ; }
// Set functions void SetRoot (_hz_zet_node_Any* pN) { if (!mz) Fatal("ISAM %s has no root\n", *m_Name) ; if (mz->m_pRoot) Fatal("ISAM %s already has a root\n", *m_Name) ;
mz->m_nElements = 1 ; mz->m_pRoot = pN ; }
//void SetInitPop (void) { mz->m_nElements = 1 ; }
// Get functions KEY& DefaultKey (void) const { return mz->m_Default ; }
_hz_zet_node_Any* Root (void) const { return mz->m_pRoot ; }
uint32_t Nodes (void) const { return mz->m_nNodes ; } uint32_t Count (void) const { return mz->m_pRoot ? mz->m_pRoot->Cumulative() : 0 ; } uint32_t Cumulative (void) const { return mz->m_pRoot ? mz->m_pRoot->Cumulative() : 0 ; } uint32_t Population (void) const { return mz->m_nElements ; } uint32_t Level (void) const { return mz->m_pRoot ? mz->m_pRoot->Level() : 0 ; } uint32_t IsamId (void) const { return mz->isamId ; } hzString Name (void) const { return m_Name ; } bool IsInit (void) const { return mz ? true : false ; } } ;
/* Move to hzBasedefs maybe enum _hz_sbias { // Category: Collection Support // // ISAM Search Bias // // In an ISAM in which the keys must be unique, a simple binary chop comparison process will determine if any given key is present in the ISAM and if it is, give the location. // However in an ISAM in which the same key can map to many objects, to iterate the objects found under the key, there has to be a method of identifying the first and the last // object. Simply chopping and comparing until there is a match will not acheive this. Furthermore, in any ISAM regardless of whether keys are to be unique, the slot where the // new key will go has to be identified and this can go wrong. For example, a new key could have a value that fell between that of slot 3 and 4. The comparison sequence would // be 7, 3, 5 and then 4. This would be OK because slot 4 is where the new key will go with what was at slot 4 moving to slot 5. If however the new value fell between 2 and 3, // the comparison sequence would be 7, 3, 1, and then 2. This is wrong as the new key needs to go in at slot 3. // // To resolve the matter, extra steps are taken at the end of the binary chop. These differ depending on what is required. The _hz_sbias enum serves to specify the option.
HZ_ISAMSRCH_LO, // Find the first instance of the key within the ISAM or fail HZ_ISAMSRCH_HI, // Find the last instance of the key within the ISAM or fail HZ_ISAMSRCH_END // Find the slot where where the key will reside. So if (b) target=(b+1) else target=(lowest slot exceeding the key). } ; */
template<class KEY> static _hz_zet_node_Dat<KEY>* _findDnodeByKey (uint32_t& nSlot, const _hz_zet_isam_Base<KEY>& isam, const KEY& val, int32_t (*cmpfn)(const KEY&,const KEY&), _hz_sbias eBias) { // Category: Collection Support // // This is the second data-node locator function and is best suited to unique key ISAMs. On the assumption the ISAM in question is in ascending // order KEY value, this function supports searches of type HZ_ISAMSRCH_LO and HZ_ISAMSRCH_UIN. // // Note this function impliments a binary chop using 'pointer forwarding'. The lookup begins with a binary chop on the root node but since the // elements for comparison are found only on data nodes, when on an index node we recurively follow the pointers to the zeroth element on the // next node up until we are on a data node. Then we do the compare as though the data node value was the value of the slot in the (current) // index node. We then follow the winning slot up the next level and binary chop that. Eventually we get to binary chop a data node and get the // actual position that either matches the key or is just above or just below the key. // // Arguments: 1) A reference to store the node offset of the element // 2) The isam to be searched // 3) The search key // 4) The pointer to the compare function // 5) The lookup bias (HZ_ISAMSRCH_LO/HZ_ISAMSRCH_HI) // // Returns: Pointer to the data node if the key is matched, // NULL pointer otherwise.
_hzfunc_ct("_findDnodeByKey") ;
_hz_zet_node_Dat<KEY>* pTgt = 0 ; // Target data node _hz_zet_node_Dat<KEY>* pTst = 0 ; // Target data node _hz_zet_node_Idx* pIdx = 0 ; // Current index node _hz_zet_node_Idx* pFwd = 0 ; // Forward pointer
KEY* pKeys ; // Array of keys from data node int32_t nRes ; // Comparison result uint32_t nPos ; // Middle of index uint32_t nDiv ; // Divider
// if (eBias != HZ_ISAMSRCH_LO && eBias != HZ_ISAMSRCH_UIN) // Fatal("_findDnodeByKey: Illegal bias %d", eBias) ;
if (!cmpfn) cmpfn = _tmpl_zet_compare ;
nSlot = -1 ; //if (!isam.mx->m_pRoot) if (!isam.Root()) return 0 ;
// Find target data node //if (isam.mx->m_pRoot && isam.mx->m_pRoot->Level() == 0) if (isam.Root() && isam.Level() == 0) //pTgt = (_hz_zet_node_Dat*) isam.mx->m_pRoot ; pTgt = (_hz_zet_node_Dat<KEY>*) isam.Root() ; else { // Determine next level node //for (pIdx = (_hz_zet_node_Idx*) isam.mx->m_pRoot ; pIdx ;) for (pIdx = (_hz_zet_node_Idx*) isam.Root() ; pIdx ;) { for (nPos = 7, nDiv = 4 ;; nDiv /= 2) { if (nPos >= pIdx->usage) { nPos -= nDiv ; continue ; }
if (pIdx->level == 1) pTst = (_hz_zet_node_Dat<KEY>*) pIdx->m_Ptrs[nPos] ; else { for (pFwd = (_hz_zet_node_Idx*) pIdx->m_Ptrs[nPos] ; pFwd->Level() > 1 ; pFwd = (_hz_zet_node_Idx*) pFwd->m_Ptrs[0]) ; pTst = (_hz_zet_node_Dat<KEY>*) pFwd->m_Ptrs[0] ; }
// Now do the compare pKeys = (KEY*) pTst->Keys() ; nRes = cmpfn(val, pKeys[0]) ;
if (!nRes) { pTgt = pTst ; break ; }
if (nRes < 0) { if (nPos == 1) { nPos = 0 ; break ; } if (!nDiv) { nPos-- ; break ; } nPos -= nDiv ; } else { if (pIdx->usage == (nPos+1)) break ; if (!nDiv) break ; nPos += nDiv ; } }
if (pTgt) break ;
if (pIdx->Level() > 1) pIdx = (_hz_zet_node_Idx*) pIdx->m_Ptrs[nPos] ; else { pTgt = (_hz_zet_node_Dat<KEY>*) pIdx->m_Ptrs[nPos] ; break ; } } }
if (!pTgt) return 0 ;
// Identify the offset pKeys = (KEY*) pTgt->Keys() ;
for (nRes = 0, nSlot = 7, nDiv = 4 ;; nDiv /= 2) { // Ignore positions higher than highest used slot if (nSlot >= pTgt->Usage()) { nRes = -1 ; if (!nDiv) { nSlot = pTgt->Usage() ; break ; } nSlot -= nDiv ; continue ; }
// Do the compare nRes = cmpfn(val, pKeys[nSlot]) ; if (nRes == 0) break ;
if (!nDiv) break ; if (nRes > 0) nSlot += nDiv ; else nSlot -= nDiv ; }
if (eBias == HZ_ISAMSRCH_LO) { // Exact match if (nRes) return 0 ;
if (nSlot >= pTgt->Usage()) Fatal("_findDnodeByKey: isam=%d: Node %d usage %d has offset %d", isam.IsamId(), pTgt->Ident(), pTgt->Usage(), nSlot) ; return pTgt ; }
// Deal with HZ_ISAMSRCH_UIN for inserts.
if (nRes > 0) { if (nSlot < pTgt->Usage()) nSlot++ ; if (nSlot == HZ_ORDSZE && pTgt->Ultra()) { pTgt = (_hz_zet_node_Dat<KEY>*) pTgt->Ultra() ; nSlot = 0 ; } }
if (nSlot > pTgt->Usage()) Fatal("_findDnodeByKey: isam=%d: Node %d usage %d has offset %d", isam.IsamId(), pTgt->Ident(), pTgt->Usage(), nSlot) ;
return pTgt ; }
template<class KEY> static _hz_zet_node_Dat<KEY>* _findAbsoluteByKey (uint32_t& nAbs, uint32_t& nSlot, const _hz_zet_isam_Base<KEY>& isam, const KEY& val, int32_t (*cmpfn)(const KEY&, const KEY&), _hz_sbias eBias) { // Category: Collection Support // // This is the final data-node locator function. On the assumption the ISAM is in ascending order of KEY // value, this function supports searches of types HZ_ISAMSRCH_LO, HZ_ISAMSRCH_HI and HZ_ISAMSRCH_END. It also provides the absolute position of // the element in the ISAM in order to facilitate numeric iteration. // // This function impliments a binary chop using relative positions. The ISAM is treated as an array and numerically chopped using _nodetr(). If // matches are found the lowest and highest matches are recorded. The nature of the chop is such that, assuming the ISAM elements are actually // ordered, once a match is found the final test position must be within one place of either the lowest or the highest match. On the first match, // both the lowest and highest matches are set to the position and it is possible, if this occurs ... // // Arguments: 1) A reference to store the absolute position // 2) A reference to store the node offset of the element // 3) The isam to be searched // 4) The search key // 5) The pointer to the compare function // 6) The lookup bias (HZ_ISAMSRCH_LO/HZ_ISAMSRCH_HI) // // Returns: Pointer to the data node if the key is matched, // NULL pointer otherwise. // // Note that numeric iterators are invalidated by insert and delete operations.
_hzfunc_ct("_findAbsoluteByKey") ;
_hz_zet_node_Dat<KEY>* pDN = 0 ; // Target data node _hz_zet_node_Dat<KEY>* pMK = 0 ; // Marked data node
KEY* pKeys ; // Array of keys from data node uint32_t max ; // Top position uint32_t posn ; // Test position uint32_t posnMk ; // Marked position uint32_t slot ; // Slot uint32_t slotMk ; // Marked slot uint32_t nDiv ; // divider int32_t res ; // result of compare
// if (eBias != HZ_ISAMSRCH_LO && eBias != HZ_ISAMSRCH_HI && eBias != HZ_ISAMSRCH_END) // Fatal("_findAbsoluteByKey: Illegal bias %d", eBias) ;
nAbs = nSlot = posnMk = slotMk = 0xffffffff ; if (!isam.Root()) return 0 ;
// Set up initial test position and the divider max = isam.Count()-1 ; for (posn = 2 ; posn < max ; posn *= 2) ; nDiv = posn / 2 ; posn-- ;
// Run up and down collection until we can divide no more for (;;) { // Ignore positions higher than highest used slot if (posn > max) { res = -1 ; if (!nDiv) break ; posn -= nDiv ; nDiv /= 2 ; continue ; }
// Do the compare pDN = isam._nodetr(slot, posn, false) ; pKeys = (KEY*) pDN->Keys() ; res = cmpfn(val, pKeys[slot]) ;
if (res == 0) { // Have an exact match so mark it but is it the higest or lowest? pMK = pDN ; slotMk = slot ; posnMk = posn ;
if (!nDiv) break ;
// After marking posn, go lower or higher according to bias if (eBias == HZ_ISAMSRCH_LO) posn -= nDiv ; else posn += nDiv ; } else { // No match, go higher or lower if you can, otherwise fail if (!nDiv) break ;
if (res > 0) posn += nDiv ; else posn -= nDiv ; }
nDiv /= 2 ; }
// If looking for lowest or highest and nothing found, give up if (eBias == HZ_ISAMSRCH_LO || eBias == HZ_ISAMSRCH_HI) { if (!pMK) return 0 ; nAbs = posnMk ; nSlot = slotMk ; return pMK ; }
// For HZ_ISAMSRCH_END if we have found highest entry, go up one place if (pMK) { pDN = isam._nodetr(slot, posnMk+1, true) ; nAbs = posnMk+1 ; nSlot = slot ; return pDN ; }
// Otherwise go up one place ONLY if the last compare was > 0
if (res > 0) posn++ ; if (posn > max) posn = max+1 ; pDN = isam._nodetr(slot, posn, true) ; nAbs = posn ; nSlot = slot ; return pDN ; }
template <class KEY> class _hz_isam_val { // Category: Collection Support // // _hz_isam_val - Single datum ISAM proving insert, delete and lookup by value (effectively a set)
_hz_zet_isam_Base<KEY> isam ; // Underlying ISAM
int32_t (*m_cmpfn)(const KEY&, const KEY&) ; // Compare function (if null this will be _tmpl_zet_compare)
// Prevent copies _hz_isam_val<KEY> (const _hz_isam_val<KEY>&) ; _hz_isam_val<KEY>& operator= (const _hz_isam_val<KEY>&) ;
public: _hz_isam_val (void) { m_cmpfn = _tmpl_zet_compare ; } ~_hz_isam_val (void) { Clear() ; }
void Start (void) { isam.Start() ; }
void SetLock (hzLockOpt eLock) { isam.SetLock(eLock) ; } void SetName (const hzString& name) { isam.SetName(name) ; } void SetDefault (const KEY& key) { isam.SetDefault(key) ; }
void SetCompare (int32_t (*cmpfn)(const KEY&, const KEY&)) { m_cmpfn = cmpfn ; }
void Clear (void) { isam.Clear() ; }
hzEcode _insUV (const KEY& key) { // Purpose: Insert an element by unique value. // // Arguments: 1) key The key to insert // // Returns: E_OK If operation successful // // Exits: E_CORRUPT If a target data node and slot was not located (integrity) or a population test fails // E_MEMORY If there was insufficient memory
_hzfunc_ct("_hz_isam_val::_insUV") ;
_hz_zet_node_Dat<KEY>* pDN = 0 ; // Current data node
uint32_t nOset ; // Offset into node (set by nodetrace)
// If ordered list is empty if (!isam.Root()) { pDN = new _hz_zet_node_Dat<KEY> ; if (!pDN) Fatal("_hz_isam_val::_insUV: isam=%d: Could not allocate root node", isam.IsamId()) ;
pDN->m_Keys[0] = key ; pDN->m_isamId = isam.IsamId() ; pDN->usage = 1 ; isam.SetRoot(pDN) ; return E_OK ; }
// Locate the 'target' top level node //pDN = (_hz_zet_node_Dat<KEY>*) _findDnodeByKey(nOset, isam, key, m_cmpfn, HZ_ISAMSRCH_UIN) ; pDN = (_hz_zet_node_Dat<KEY>*) _findDnodeByKey(nOset, isam, key, m_cmpfn, HZ_ISAMSRCH_END) ; if (!pDN) Fatal("_hz_isam_val::_insUV: isam=%d: No data node found", isam.IsamId()) ; if (nOset > HZ_ORDSZE) Fatal("_hz_isam_val::_insUV: isam=%d: Illegal slot no %d for node %d", isam.IsamId(), nOset, pDN->m_Id) ;
// If new element has identical key then ignore if (nOset < HZ_ORDSZE) { if (m_cmpfn(key, pDN->m_Keys[nOset]) == 0) return E_OK ; }
//isam.mx->m_nElements++ ; pDN = (_hz_zet_node_Dat<KEY>*) isam._acceptData(pDN, key, nOset) ;
if (Count() != isam.Population()) Fatal("_hz_isam_val::_insUV: isam=%d: Expected population %d, actual %d ", isam.IsamId(), isam.Population(), Count()) ;
if (isam.IntegBase() != E_OK) Fatal("_hz_isam_val::_insUV: isam=%d: INTEGRITY FAILURE", isam.IsamId()) ; return E_OK ; }
hzEcode _delUV (const KEY& key) { // Arguments: 1) key The key be removed // // Returns: E_OK If operation successful // E_NOTFOUND If the key was not found in the ISAM
_hzfunc_ct("_hz_isam_val::_delUV") ;
_hz_zet_node_Dat<KEY>* pDN = 0 ; // Target data node (from which delete will occur) uint32_t nOset ; // Position within node
// Check args if (!isam.Root()) return E_NOTFOUND ;
// Locate the 'target' top level node pDN = (_hz_zet_node_Dat<KEY>*) _findDnodeByKey(nOset, isam, key, m_cmpfn, HZ_ISAMSRCH_LO) ; if (!pDN) return E_NOTFOUND ;
if (nOset >= HZ_ORDSZE) Fatal("_hz_isam_val::_delUV: isam=%d: Illegal slot no %d for node %d", isam.IsamId(), nOset, pDN->m_Id) ;
// Expel the entity from the node //isam.mx->m_nElements-- ; pDN = (_hz_zet_node_Dat<KEY>*) isam._expelData(pDN, nOset) ;
if (Count() != isam.Population()) Fatal("_hz_isam_val::_delUV: isam=%d: Expected population %d, actual %d ", isam.IsamId(), isam.Population(), Count()) ; return E_OK ; }
KEY& _locatePos (uint32_t nPosn) { // Arguments: 1) nPosn Absolute position of element // // Returns: Reference to the key part of the resident element (non-const) or the default key if the supplied position is out of range.
_hzfunc_ct("_hz_isam_val::_locatePos") ;
_hz_zet_node_Dat<KEY>* pDN ; // Target ISAM data block uint32_t nOset ; // Slot obtained from isam._nodetr()
if (!isam.Root() || nPosn >= isam.Cumulative()) //return isam.mx->m_Default ; return isam.DefaultKey() ;
pDN = (_hz_zet_node_Dat<KEY>*) isam._nodetr(nOset, nPosn, false) ;
if (!pDN) Fatal("_hz_isam_val::_locatePos: isam=%d: Data node not found for position %d", isam.IsamId(), nPosn) ; if (nOset < 0 || nOset >= pDN->Usage()) Fatal("_hz_isam_val::_locatePos: isam=%d: Data node %d: Offset is %d, usage is %d", isam.IsamId(), pDN->m_Id, nOset, pDN->Usage()) ;
return pDN->m_Keys[nOset] ; }
const KEY& _c_locatePos (uint32_t nPosn) const { // Arguments: 1) nPosn Absolute position of element // // Returns: Reference to the key part of the resident element (const) or the default key if the supplied position is out of range.
_hzfunc_ct("_hz_isam_val::_c_locatePos") ;
_hz_zet_node_Dat<KEY>* pDN ; // Target ISAM data block uint32_t nOset ; // Slot obtained from isam._nodetr()
if (!isam.Root() || nPosn >= isam.Cumulative()) //return isam.mx->m_Default ; return isam.DefaultKey() ;
pDN = (_hz_zet_node_Dat<KEY>*) isam._nodetr(nOset, nPosn, false) ;
if (!pDN) Fatal("_hz_isam_val::_c_locatePos: isam=%d: Data node not found for position %d", isam.IsamId(), nPosn) ; if (nOset >= pDN->Usage()) Fatal("_hz_isam_val::_c_locatePos: isam=%d: Data node %d: Offset is %d, usage is %d", isam.IsamId(), pDN->m_Id, nOset, pDN->Usage()) ;
return pDN->m_Keys[nOset] ; }
KEY& _locUV (const KEY& key) const { // Arguments: 1) key Key value to search for // // Returns: Reference to the key part of the resident element or the default key if the supplied key does not exist.
_hzfunc_ct("_hz_isam_val::_locUV") ;
_hz_zet_node_Dat<KEY>* pDN ; // Target ISAM data block uint32_t nOset ; // Slot obtained
if (!isam.Root()) //return isam.mx->m_Default ; return isam.DefaultKey() ;
pDN = (_hz_zet_node_Dat<KEY>*) _findDnodeByKey(nOset, isam, key, m_cmpfn, HZ_ISAMSRCH_LO) ; if (!pDN) //return isam.mx->m_Default ; return isam.DefaultKey() ;
if (nOset >= pDN->Usage()) Fatal("_hz_isam_val::_locUV: Isam %d Data node %d: Offset is %d, usage is %d", isam.IsamId(), pDN->m_Id, nOset, pDN->Usage()) ;
return pDN->m_Keys[nOset] ; }
int32_t _getPosByKey (const KEY& key, _hz_sbias eBias) const { // Arguments: 1) key Key to search for // 2) eBias Search bias // // Returns: Value being the current absolute position of the entry matching the supplied KEY and bias
_hzfunc_ct("_hz_isam_val::_getPosByKey") ;
_hz_zet_node_Dat<KEY>* pDN ; // Target ISAM data block uint32_t nOset ; // Slot obtained uint32_t nAbs ; // Absolute position
if (!isam.Root()) return -1 ;
pDN = _findAbsoluteByKey(nAbs, nOset, isam, key, m_cmpfn, eBias) ; if (!pDN) return -1 ; return nAbs ; }
bool _exists (const KEY& key) const { uint32_t nOset ; // Slot obtained
return _findDnodeByKey(nOset, isam, key, m_cmpfn, HZ_ISAMSRCH_LO) ? true : false ; }
hzEcode Integrity (void) const { // Test integrity of the ISAM. This was a relic of early development days but has been left in for the benefit of testing any future changes. There are // no circumstances where an application should ever have to test ISAM integrity. // // Arguments: None // // Returns: E_CORRUPT Integrity failure // E_OK Integrity test passed
_hz_zet_node_Dat<KEY>* pA ; // Data block of lower position _hz_zet_node_Dat<KEY>* pB ; // Data block of higher position
uint32_t osetA ; // Data block offset of lower position uint32_t osetB ; // Data block offset of higher position uint32_t nItems ; // Number of elements uint32_t nCount ; // Element iterator int32_t res ; // Comparison result hzEcode rc = E_OK ; // Return code
nItems = Count() ; if (nItems < 2) return E_OK ;
for (nCount = 1 ; rc == E_OK && nCount < nItems ; nCount++) { pA = (_hz_zet_node_Dat<KEY>*) isam._nodetr(osetA, nCount-1, false) ; pB = (_hz_zet_node_Dat<KEY>*) isam._nodetr(osetB, nCount, false) ;
res = m_cmpfn(pB->m_Keys[osetB], pA->m_Keys[osetA]) ;
if (!res) { threadLog("isam=%d pop %d - Integrity fails duplicate at posn %d\n", isam.IsamId(), Count(), nCount) ; rc = E_CORRUPT ; } if (res<0) { threadLog("isam=%d pop %d - Integrity fails order at posn %d\n", isam.IsamId(), Count(), nCount) ; rc = E_CORRUPT ; } } return rc ; }
hzEcode NodeReport (bool bData) const { isam.NodeReport(bData) ; }
uint32_t Count (void) const { return isam.Count() ; } uint32_t Level (void) const { return isam.Level() ; } uint32_t IsamId (void) const { return isam.IsamId() ; } bool IsInit (void) const { return isam.IsInit() ; } } ;
/* ** Section 3 The Indexed Collection Class Templates */
/* ** The formal hzMapS template */
/* ** The hzSet template */
template<class KEY> class hzSet { // Category: Object Collection // // The hzSet template is a memory resident set of unique objects. It is similar to hzMapS except that the objects serve as thier own keys. Note that the objects // must have the comparison operators implimented.
_hz_isam_val<KEY> m_Data ; // _hz_isam_val ordered list by value
// Prevent copies hzSet<KEY> (const hzSet<KEY>&) ; hzSet<KEY>& operator= (const hzSet<KEY>&) ;
public: hzSet (void) { m_Data.Start() ; m_Data.SetLock(HZ_NOLOCK) ; _hzGlobal_Memstats.m_numSets++ ; } hzSet (hzLockOpt eLock) { m_Data.Start() ; m_Data.SetLock(eLock) ; _hzGlobal_Memstats.m_numSets++ ; } hzSet (const hzString& name) { m_Data.Start() ; m_Data.SetLock(HZ_NOLOCK) ; m_Data.SetName(name) ; _hzGlobal_Memstats.m_numSets++ ; } hzSet (hzLockOpt eLock, const hzString& name) { m_Data.Start() ; m_Data.SetLock(eLock) ; m_Data.SetName(name) ; _hzGlobal_Memstats.m_numSets++ ; }
~hzSet (void) { _hzGlobal_Memstats.m_numSets-- ; }
// Inits void SetCompare (int32_t (*cmpfn)(const KEY&, const KEY&)) { m_Data.SetCompare(cmpfn) ; }
void SetName (const hzString& name) { m_Data.SetName(name) ; } void SetLock (hzLockOpt eLock) { m_Data.SetLock(eLock) ; }
void SetDefault (KEY val) { m_Data.SetDefault(val) ; }
// Data operations void Clear (void) { m_Data.Clear() ; } // hzEcode Insert (const KEY& key) { _hzfunc("hzSet::Insert") ; return m_Data._insUV(key) ; } // hzEcode Delete (const KEY& key) { _hzfunc("hzSet::Insert") ; return m_Data._delUV(key) ; } hzEcode Insert (const KEY& key) { return m_Data._insUV(key) ; } hzEcode Delete (const KEY& key) { return m_Data._delUV(key) ; }
// Lookup const KEY& GetObj (uint32_t nIndex) const { return m_Data._c_locatePos(nIndex) ; } KEY& GetReal (uint32_t nIndex) { return m_Data._locatePos(nIndex) ; } bool Exists (const KEY& key) const { return m_Data._exists(key) ; } KEY& operator[] (const KEY& key) const { return m_Data._locUV(key) ; }
// Diagnostics uint32_t Nodes (void) const { return m_Data.Nodes() ; } uint32_t Count (void) const { return m_Data.Count() ; } bool IsInit (void) const { return m_Data.IsInit() ; } hzEcode Integrity (void) const { return m_Data.Integrity() ; } hzEcode NodeErrors (void) const { return m_Data.NodeReport(true) ; } hzEcode NodeReport (void) const { return m_Data.NodeReport(false) ; } } ; #endif