// // File: hzTmplArray.h // // 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. //
#ifndef hzTmplArray_h #define hzTmplArray_h
#include "hzLock.h" #include "hzProcess.h"
/* ** Prototypes */
void Fatal (const char* va_alist ...) ;
#define HZ_ARRAY_NODESIZE 8
template<class OBJ> class hzArray { // Category: Object Collection // // The hzArray class template facilitates an array of objects. The objects are not stored in an actual array (of continuous memory) but in a tree of blocks // based on absolute positioning. On the highest level the data blocks comprise only an array of HZ_ARRAY_NODESIZE objects while on the inner levels the index // blocks comprise nothing but HZ_ARRAY_NODESIZE pointers to the blocks above. With a population of 0 the root of the tree is null and the hzArray has a level // of zero. With a population between 1 and HZ_ARRAY_NODESIZE, the root points to a single data block and the hzArray now has 1 level. As the population rises // above HZ_ARRAY_NODESIZE the level goes up to 2 and from now on the root is an index node. A new level is created when the root splits into two nodes, which // happens when the population goes above HZ_ARRAY_NODESIZE to the power of the current level. // // Note that hzArray does not support an Insert() or a Delete() method. It is intended only to be aggregated to by a series of appends and then accesses by // the [] operator. The hzArray is thus an unordered collection.
struct _hz_ar_data { // Block of HZ_ARRAY_NODESIZE objects
OBJ m_Objs[HZ_ARRAY_NODESIZE] ; // Objects held in the array } ;
struct _hz_ar_indx { // Array of pointers (to either index or data blocks)
void* m_Ptrs[HZ_ARRAY_NODESIZE] ; // The pointers } ;
struct _array_ca { // Array control area
hzLockRW m_Lock ; // Read/Write lock void* m_pRoot ; // Root node uint32_t m_nFactor ; // 1 for level 0, HZ_ARRAY_NODESIZE for level 1, HZ_ARRAY_NODESIZE squared for 2 etc uint32_t m_nNoDN ; // Number of data nodes uint32_t m_nNoIN ; // Number of index nodes uint32_t m_nCount ; // Number of items uint32_t m_nLevel ; // The level of the array //_mut uint32_t m_nCopy ; // Number of copies OBJ m_Default ; // Default (empty) element
//_array_ca (void) { m_pRoot = 0 ; m_nLevel = m_nNoDN = m_nNoIN = m_nCount = m_nCopy = 0 ; m_nFactor = 1 ; } _array_ca (void) { m_pRoot = 0 ; m_nLevel = m_nNoDN = m_nNoIN = m_nCount = 0 ; m_nFactor = 1 ; } ~_array_ca (void) {}
void Lock (void) { m_Lock.LockWrite() ; } void Unlock (void) { m_Lock.Unlock() ; } void KillLock (void) { m_Lock.Kill() ; } } ;
void _clear (void* pN, uint32_t nLevel, uint32_t maxLevel) { _hz_ar_indx* pIdx ; // Current index block pointer _hz_ar_data* pDN ; // Current object block pointer uint32_t n ; // Object block iterator
if (!mx) return ; if (nLevel < maxLevel) { pIdx = (_hz_ar_indx*) pN ; for (n = 0 ; n < HZ_ARRAY_NODESIZE && pIdx->m_Ptrs[n] ; n++) _clear(pIdx->m_Ptrs[n], nLevel+1, maxLevel) ; } else { pDN = (_hz_ar_data*) pN ; //pIdx->m_Ptrs[n] ; delete pDN ; } }
_array_ca* mx ; // The actual array
// Prevent copies hzArray<OBJ> (hzArray<OBJ>& op) ; hzArray<OBJ>& operator= (const hzArray<OBJ>& op) ;
public: hzArray (void) { _hzGlobal_Memstats.m_numArrays++ ; mx = new _array_ca() ; } ~hzArray (void) { if (mx) { mx->Lock() ; /* if (mx->m_nCopy) { // Reduce copy count mx->m_nCopy-- ; mx->Unlock() ; } else { */ mx->KillLock() ;
// Clear Root if (mx->m_pRoot) _clear(mx->m_pRoot, 1, mx->m_nLevel) ;
_hzGlobal_Memstats.m_numArrayDA-- ; delete mx ; //} } _hzGlobal_Memstats.m_numArrays-- ; }
void SetDefault (OBJ dflt) { mx->m_Default = dflt ; }
void Clear (void) { // Clear all nodes
_hzfunc("hzArray::Clear") ;
if (mx) { mx->Lock() ;
if (mx->m_pRoot) _clear(mx->m_pRoot, 1, mx->m_nLevel) ;
mx->m_pRoot = 0 ; mx->m_nLevel = 0 ; mx->m_nCount = 0 ; mx->m_nFactor = 1 ;
mx->Unlock() ; } }
hzEcode Add (const OBJ& obj) { // Add (append) an object to the end of the collection // // Arguments: 1) obj Object to add
_hzfunc("hzArray::Add") ;
_hz_ar_data* pDN ; // Data node _hz_ar_indx* pIdx ; // Index node
uint32_t x ; // Position so far uint32_t n ; // Offest at current level uint32_t f ; // Division factor uint32_t dnAddr ; // Slot on higest index node for target data node uint32_t nSlot ; // Target slot on data node
if (!mx) mx = new _array_ca() ;
mx->Lock() ;
// If no root if (!mx->m_pRoot) { mx->m_nNoDN = 1 ; pDN = new _hz_ar_data() ; pDN->m_Objs[0] = obj ; mx->m_pRoot = pDN ; mx->m_nLevel = 1 ; mx->m_nCount = 1 ; mx->Unlock() ; return E_OK ; }
if (mx->m_nCount < HZ_ARRAY_NODESIZE) { // If root is a data node pDN = (_hz_ar_data*) mx->m_pRoot ; pDN->m_Objs[mx->m_nCount] = obj ; mx->m_nCount++ ; mx->Unlock() ; return E_OK ; }
// Do we need a new level? if (mx->m_nCount == (mx->m_nFactor * HZ_ARRAY_NODESIZE)) { mx->m_nNoIN++ ; pIdx = new _hz_ar_indx() ; pIdx->m_Ptrs[0] = mx->m_pRoot ; mx->m_pRoot = pIdx ; mx->m_nLevel++ ; mx->m_nFactor *= HZ_ARRAY_NODESIZE ; }
// Go to highest data node pIdx = (_hz_ar_indx*) mx->m_pRoot ;
for (x = mx->m_nCount, f = mx->m_nFactor ; f >= (HZ_ARRAY_NODESIZE * HZ_ARRAY_NODESIZE) ;) { n = x/f ; x %= f ; f /= HZ_ARRAY_NODESIZE ;
if (!pIdx->m_Ptrs[n]) { mx->m_nNoIN++ ; pIdx->m_Ptrs[n] = new _hz_ar_indx() ; } pIdx = (_hz_ar_indx*) pIdx->m_Ptrs[n] ; }
// Data node 'address' is dnAddr = x/f ; if (!pIdx->m_Ptrs[dnAddr]) { mx->m_nNoDN++ ; pIdx->m_Ptrs[dnAddr] = new _hz_ar_data() ; } pDN = (_hz_ar_data*) pIdx->m_Ptrs[dnAddr] ;
// Data node slot is remainder nSlot = x%f ; pDN->m_Objs[nSlot] = obj ; mx->m_nCount++ ; mx->Unlock() ; return E_OK ; }
OBJ& operator[] (uint32_t nPosn) const { _hzfunc("hzArray::operator[]") ;
_hz_ar_indx* pIdx ; // Index node pointer _hz_ar_data* pDN ; // Index node pointer
uint32_t x ; // Position reached so far uint32_t n ; // Offest at current level uint32_t f ; // Division factor
if (nPosn >= mx->m_nCount) return mx->m_Default ;
f = mx->m_nFactor ;
if (f == 1) { pDN = (_hz_ar_data*) mx->m_pRoot ; n = nPosn ; return pDN->m_Objs[nPosn] ; }
pIdx = (_hz_ar_indx*) mx->m_pRoot ;
for (x = nPosn, f = mx->m_nFactor ; f >= (HZ_ARRAY_NODESIZE * HZ_ARRAY_NODESIZE) ;) { n = x/f ; x %= f ; f /= HZ_ARRAY_NODESIZE ; pIdx = (_hz_ar_indx*) pIdx->m_Ptrs[n] ; } pDN = (_hz_ar_data*) pIdx->m_Ptrs[x/f] ; return pDN->m_Objs[x%f] ; }
OBJ* InSitu (uint32_t nPosn) const { // Return pointer to the actual in-situ object
_hzfunc("hzArray::InSitu") ;
_hz_ar_indx* pIdx ; // Index node pointer _hz_ar_data* pDN ; // Index node pointer OBJ* pObj ; // Object pointer
uint32_t x ; // Position reached so far uint32_t n ; // Offest at current level uint32_t f ; // Division factor
if (nPosn >= mx->m_nCount) return 0 ; //mx->m_Default ;
f = mx->m_nFactor ;
if (f == 1) { pDN = (_hz_ar_data*) mx->m_pRoot ; pObj = (OBJ*) pDN->m_Objs ; return pObj + nPosn ; }
pIdx = (_hz_ar_indx*) mx->m_pRoot ;
for (x = nPosn, f = mx->m_nFactor ; f >= (HZ_ARRAY_NODESIZE * HZ_ARRAY_NODESIZE) ;) { n = x/f ; x %= f ; f /= HZ_ARRAY_NODESIZE ; pIdx = (_hz_ar_indx*) pIdx->m_Ptrs[n] ; } pDN = (_hz_ar_data*) pIdx->m_Ptrs[x/f] ; pObj = (OBJ*) pDN->m_Objs ; return pObj + (x%f) ; }
// Diagnostics uint32_t Level (void) const { return mx ? mx->m_nLevel : 0 ; } uint32_t Factor (void) const { return mx ? mx->m_nFactor : 0 ; } uint32_t dBlocks (void) const { return mx ? mx->m_nNoDN : 0 ; } uint32_t iBlocks (void) const { return mx ? mx->m_nNoIN : 0 ; } uint32_t Count (void) const { return mx ? mx->m_nCount : 0 ; } } ;
#endif // hzTmplArray_h