// // File: hzIsamT.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 hzIsamT_h #define hzIsamT_h
//#include <iostream> #include <fstream>
#include "hzString.h"
/* ** Definitions */
#define HZ_T_ISAMNODE_FULL 8 // Maximum node population #define HZ_T_ISAMNODE_HALF 4 // Maximum node population #define HZ_T_ISAMNODE_LOW 5 // Minimum node population #define HZ_T_BADSLOT 0xffffffff // Invalid node slot
enum _hz_sbias { // 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). } ;
class _hz_vn_Idx ; // Index node class hzLogger ; // Log channel
class _hz_vn_Any { // Base class for vector index and data nodes
public: virtual ~_hz_vn_Any (void) {}
virtual _hz_vn_Idx* Parent (void) = 0 ; virtual _hz_vn_Any* Infra (void) = 0 ; virtual _hz_vn_Any* Ultra (void) = 0 ; virtual uint32_t Cumulative (void) = 0 ; virtual uint32_t Level (void) = 0 ; virtual uint32_t IsamId (void) = 0 ; virtual int32_t Usage (void) = 0 ; virtual void SetParent (_hz_vn_Idx*) = 0 ; } ;
class _hz_vn_Idx : public _hz_vn_Any { // Vector index nodes
public: _hz_vn_Idx* parent ; // Parent node _hz_vn_Idx* infra ; // Infra adjacent _hz_vn_Idx* ultra ; // Ultra adjacent _hz_vn_Any* m_Ptrs[HZ_T_ISAMNODE_FULL] ; // Pointers either to index or data nodes uint32_t m_nCumulative ; // Number of elements ultimately pointed to by this block uint16_t m_isamId ; // Ties the node to the ISAM uint16_t level ; // Level of this block in the tree int16_t usage ; // Number of elements in this block uint16_t resv ; // Reserved
_hz_vn_Idx (void) { _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 ; m_nCumulative = 0 ; m_isamId = usage = level = resv = 0 ; }
~_hz_vn_Idx (void) { _hzGlobal_Memstats.m_numIsamIndx-- ; }
// Set methods void SetCumulatives (void) ;
void SetParent (_hz_vn_Idx* par) { parent = par ; } void SetLevel (int32_t v) { level = v ; }
// Get methods _hz_vn_Idx* Parent (void) { return parent ; } _hz_vn_Idx* Infra (void) { return infra ; } _hz_vn_Idx* Ultra (void) { return ultra ; }
uint32_t Cumulative (void) { return m_nCumulative ; } uint32_t Level (void) { return level ; } uint32_t IsamId (void) { return m_isamId ; } int32_t Usage (void) { return usage ; } } ;
class _hz_vn_Dat : public _hz_vn_Any { // Vector data nodes
public: _hz_vn_Idx* parent ; // Parent node _hz_vn_Dat* infra ; // Infra adjacent _hz_vn_Dat* ultra ; // Ultra adjacent uchar* m_pElements ; // Pointers to bucket of elements uint32_t m_Id ; // Unique node id (for diagnostics) uint16_t m_isamId ; // Ties node to ISAM int16_t usage ; // Number of elements in this block
_hz_vn_Dat (void) { m_Id = ++_hzGlobal_Memstats.m_numIsamData ; parent = 0 ; infra = ultra = 0 ; m_pElements = 0 ; m_isamId = usage = 0 ; }
~_hz_vn_Dat (void) { if (m_pElements) delete[] m_pElements ; _hzGlobal_Memstats.m_numIsamData-- ; }
// Get values _hz_vn_Idx* Parent (void) { return parent ; } _hz_vn_Dat* Infra (void) { return infra ; } _hz_vn_Dat* Ultra (void) { return ultra ; }
uint32_t Cumulative (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 ; } int32_t Usage (void) { return usage ; }
void* Keys (void) { return m_pElements ; }
// Set values void SetParent (_hz_vn_Idx* par) { parent = par ; } void SetInfra (_hz_vn_Dat* pInf) { infra = (_hz_vn_Dat*) pInf ; } void SetUltra (_hz_vn_Dat* pUlt) { ultra = (_hz_vn_Dat*) pUlt ; }
void IncUsage (void) { usage++ ; } void DecUsage (void) { usage-- ; } void SetUsage (uint32_t u) { usage = u ; } } ;
/* ** The Vector Template itself! */
class _hz_tmpl_ISAM { // Category: Object Collection // // The hzVect class template provides a form of vector or an array. The elements are accessible via the [] operator but are held in an _hz_isam_vect ISAM rather // than in a contiguous buffer. This confers the advantage of avoiding expensive reallocation in the general case where the number of elements is not known in // advance but with the caveat that elements have relative rather then absolute addresses. // // Note that as the [] operator returns a reference to an element, out of range calls must return something and to this end hzVect has a default element which is // returned in this event. The default element is created by default hzVect construction and so is 'empty' but this is the only guide to the calling program that // the requested element was out of range. // // Note that in stark contrast to the vector class template of the STL, the [] operator NEVER creates elements. This must be done explicitly either by the Add() // method which always places the new element at the end or the Insert() method which requires an additional argument to indicate the position. // // Errors: The only relevant error that can occur is that of memory allocaton which currently causes imeadiate termination of the progrem.
_hz_vn_Any* m_pRoot ; // Start of index hzLocker* m_pLock ; // Locking (off by default) hzString m_Name ; // Optional ISAM name (all ISAM dependent templates use this) uint32_t m_isamId ; // For diagnostics uint32_t m_nNodes ; // Number of nodes (also as node id allocator) uint32_t m_nElements ; // Number of data objects uint16_t m_nSizeKey ; // Key size uint16_t m_nSizeObj ; // Object size
void _clearnode (_hz_vn_Any* pBlk) { // Recursively clear supplied node and any child nodes
_hz_vn_Idx* pIdx ; // Current ISAM block pointer int32_t n ; // ISAM block selector
if (!pBlk->Level()) delete pBlk ; else { pIdx = (_hz_vn_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. Calls recursive _clearnode() function on the root node.
if (!m_pRoot) return ; _clearnode(m_pRoot) ; m_nElements = 0 ; m_pRoot = 0 ; }
// Prevent copies _hz_tmpl_ISAM (const _hz_tmpl_ISAM&) ; _hz_tmpl_ISAM& operator= (const _hz_tmpl_ISAM&) ;
public: int32_t (*m_compare) (const void* pKey, const _hz_vn_Dat* pDN, uint32_t nSlot) ;
_hz_tmpl_ISAM (void) { m_pRoot = 0 ; m_pLock = 0 ; m_nNodes = m_nElements = 0 ; m_nSizeKey = m_nSizeObj = 0 ; m_isamId = ++_hzGlobal_Memstats.m_numIsams ; }
~_hz_tmpl_ISAM (void) { _hzGlobal_Memstats.m_numIsams-- ; }
// Initialization void Start (uint32_t nSizeKey, uint32_t nSizeObj) { m_nSizeKey = nSizeKey ; m_nSizeObj = nSizeObj ; }
void SetName (const hzString& name) { m_Name = name ; }
void SetLock (hzLockOpt eLock) { if (eLock == HZ_ATOMIC) m_pLock = new hzLockRW() ; else if (eLock == HZ_MUTEX) m_pLock = new hzLockRWD() ; else m_pLock = 0 ; }
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() ; _clear() ; Unlock() ; }
void LockRead (void) { if (m_pLock) m_pLock->LockRead() ; } void LockWrite (void) { if (m_pLock) m_pLock->LockWrite() ; } void Unlock (void) { if (m_pLock) m_pLock->Unlock() ; }
// Internal functions void _moveElement (_hz_vn_Dat* pNodeTgt, _hz_vn_Dat* pNodeSrc, uint32_t tgtSlot, uint32_t srcSlot) ; _hz_vn_Dat* _allocDataSlot (_hz_vn_Dat* pDN, int32_t& nSlot) ; _hz_vn_Dat* _expelDataSlot (_hz_vn_Dat* pDN, int32_t nSlot) ; _hz_vn_Dat* _findDnodeByPos (int32_t& nSlot, uint32_t nPosn, bool bExpand) const ; _hz_vn_Dat* _findDnodeByKey (int32_t& nSlot, const void* pKey, _hz_sbias eBias) const ; _hz_vn_Dat* _findAllByKey (int32_t& nSlot, uint32_t& nPosn, const void* pKey, _hz_sbias eBias) const ; hzEcode _allocIndxSlot (_hz_vn_Any* pOld, _hz_vn_Any* pNew) ; void _expelIndxSlot (_hz_vn_Idx* pCur, _hz_vn_Any* pChild) ;
// Data Operations _hz_vn_Dat* InsertPosn (int32_t& nSlot, uint32_t nPosn) ; hzEcode DeletePosn (uint32_t nPosn) ; _hz_vn_Dat* InsertKeyU (int32_t& nSlot, const void* pKey) ; _hz_vn_Dat* InsertKeyM (int32_t& nSlot, const void* pKey) ; _hz_vn_Dat* DeleteKey (int32_t& nSlot, const void* pKey) ;
// Diagnostics bool _shownode (_hz_vn_Any* pNode, hzLogger* plog, bool bData) const ; hzEcode _integBase (hzLogger* plog, _hz_vn_Idx* pN) const ; hzEcode NodeReport (bool bData = false) const ; hzEcode IntegBase (void) const ;
// Get info _hz_vn_Any* Root (void) const { return m_pRoot ; }
uint32_t Nodes (void) const { return m_nNodes ; } uint32_t Count (void) const { return m_pRoot ? m_pRoot->Cumulative() : 0 ; } uint32_t Cumulative (void) const { return m_pRoot ? m_pRoot->Cumulative() : 0 ; } uint32_t Population (void) const { return m_nElements ; } uint32_t Level (void) const { return m_pRoot ? m_pRoot->Level() : 0 ; } uint32_t IsamId (void) const { return m_isamId ; } hzString Name (void) const { return m_Name ; } //hzString Path (void) const { return m_Path ; } } ;
/* ** Standard Function Templates */
template <class KEY> class _hz_set_bkt { // Data bucket, held by data nodes in a set of keys.
public: KEY m_Keys[HZ_T_ISAMNODE_FULL] ; // Elements } ;
template <class KEY, class OBJ> class _hz_map_bkt { // Data bucket, held by data nodes in a map of key/object pairs.
public: KEY m_Keys[HZ_T_ISAMNODE_FULL] ; // Keys OBJ m_Objs[HZ_T_ISAMNODE_FULL] ; // Objects } ;
template<class KEY> int32_t _tmpl_set_compare (const void* pKey, const _hz_vn_Dat* pDN, uint32_t nSlot) { // Called internally by _hz_tmpl_ISAM::_findDnodeByKey in the case of a set (keys only).
_hz_set_bkt<KEY>* pBuck ; KEY* key ;
key = (KEY*) pKey ; pBuck = (_hz_set_bkt<KEY>*) pDN->m_pElements ;
return *key > pBuck->m_Keys[nSlot] ? 1 : *key < pBuck->m_Keys[nSlot] ? -1 : 0 ; }
template<class KEY, class OBJ> int32_t _tmpl_map_compare (const void* pKey, const _hz_vn_Dat* pDN, uint32_t nSlot) { // Called internally by _hz_tmpl_ISAM::_findDnodeByKey in the case of a map (key/object pairs).
_hz_map_bkt<KEY,OBJ>* pBuck ;
KEY* key ;
key = (KEY*) pKey ; pBuck = (_hz_map_bkt<KEY,OBJ>*) pDN->m_pElements ;
return *key > pBuck->m_Keys[nSlot] ? 1 : *key < pBuck->m_Keys[nSlot] ? -1 : 0 ; }
#if 0 template<class KEY> std::ostream& operator<< (std::ostream& os, const void* pKey) { // Export key value in text form
KEY* key ;
key = (KEY*) pKey ; return os.write((const char*) pKey, sizeof(KEY)) ; } #endif
#endif // hzIsamT_h