// // File: hzTmplList.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 hzTmplList_h #define hzTmplList_h
#include "hzLock.h" #include "hzProcess.h"
/* ** Prototypes */
void Fatal (const char* va_alist ...) ;
/* ** Section 1 Simple Collections */
#define HZ_LIST_NODESIZE 8
template <class OBJ> class _hz_listitem { // Category: Collection Support // // List element containing an element and a pointer to the next element. For use in the unordered list class templates, hzList, hzQue and hzStack.
public: _hz_listitem* next ; // Next item in list OBJ m_Obj ; // Object in list
_hz_listitem (OBJ obj) { next = 0 ; m_Obj = obj ; } } ;
template <class OBJ> class hzList { // Category: Object Collection // // Single Linked list // // hzList is a single linked list with each element linked only to the next element. This saves space compared to a double linked list, but at the price of // no reverse iteration. HadronZoo has not yet created a double linked variant as the experience so far has not suggested a pressing need for one. Thusfar, // the usage pattern has been that lists are compiled, and then iterated from begining to end. At some point a double linked list wil be brought in. // // hzList provides an interator - the subclass Iter (hzList::Iter). The list iterator can be directly assigned to the begining of a list OR to another list // iterator. Iteration is via the ++ operator until Iter::Valid() is no longer true. Objects are available via the iterator's Element() method. // // hzList does not itself provide methods of Insert() and Delete(), instead these are provided by the iterator. This is because there is no way to specify // a position within a list except in terms of the 'current' element. The concept of a current element only exists within an iterator, not within a list. // // <b>Usage Considerations:</b> // // In common with other HadronZoo collection classes, hzList does not facilitate hard copies. Lists are intended to be short but potentially numerous. Many // tree-like classes use lists as the means by which nodes manage sub-nodes. It is common for applications to have many thousands of lists and it is common // for large numbers of these to be empty. So hzList only comprises a pointer to a 'data area' that is initialized to NULL in the constructor. Only when an // object is added will the data area be created. The data area is deleted when the last object is deleted from the list. // // Note that it is common for items to be deleted during iteration as it becomes clear to the iterating process, the item is no longer needed. This is safe // as long as only one iterator is operating on the list but not safe otherwise. Although the deletion takes place within a lock there is no way of knowing // if another iterator is pointing to the item in question, and no way for the other iterator to know its current item is defunct. A process using the other // iterator may attempt to perform an operation on a defunct object or seek to advance the other iterator on the basis of a defunct pointer.
struct _list_ca { // Struct to hold list contents. This is used to prevent lists from real copying.
_hz_listitem<OBJ>* m_pList ; // Ptr to first element in the list _hz_listitem<OBJ>* m_pLast ; // Ptr to last element in the list
hzLockS m_Lock ; // Built in lock uint32_t m_nCount ; // No of elements in the list uint32_t m_nCopy ; // No of copies uint32_t m_nIter ; // No of iterators of this list
_list_ca (void) { m_pList = m_pLast = 0 ; m_nCount = m_nCopy = m_nIter = 0 ; _hzGlobal_Memstats.m_numListDC++ ; } ~_list_ca (void) { Clear() ; _hzGlobal_Memstats.m_numListDC-- ; }
void Lock (void) { if (_hzGlobal_MT) m_Lock.Lock() ; } void Unlock (void) { if (_hzGlobal_MT) m_Lock.Unlock() ; }
void Clear (void) { // Clear all list contents.
_hz_listitem<OBJ>* pC ; // List element pointer _hz_listitem<OBJ>* pN ; // Next object iterator
Lock() ; if (m_nCopy > 0) m_nCopy-- ; else { for (pC = m_pList ; pC ; pC = pN) { pN = pC->next ; delete pC ; } m_pList = m_pLast = 0 ; m_nCount = 0 ; } Unlock() ; }
hzEcode _add (const OBJ obj) { // Add item to end of list // // Arguments: 1) obj Object to add
_hzfunc_ct("_list_ca::_add") ;
_hz_listitem<OBJ>* pTL ; // List element pointer
if (!(pTL = new _hz_listitem<OBJ>(obj))) Fatal("_list_ca::_add. Link allocation failure") ;
Lock() ; if (m_pList == 0) m_pList = m_pLast = pTL ; else { m_pLast->next = pTL ; m_pLast = pTL ; } m_nCount++ ; Unlock() ; return E_OK ; }
hzEcode _insert (const OBJ obj, _hz_listitem<OBJ>* currlink, bool bAfter) { // Insert item before/after supplied current link. // // Argument: 1) obj The object to insert // 2) currLink The current link // 3) bAfter Indicate insertation should be after current link, not before // // Returns: E_ARGUMENT If the current link is not supplied // E_NOTFOUND If the supplied current link isn't actually in the list // E_OK If the object was inserted
_hzfunc_ct("_list_ca::_insert") ;
_hz_listitem<OBJ>* marker ; // List element pointer _hz_listitem<OBJ>* newlink ; // List element pointer
if (!(newlink = new _hz_listitem<OBJ>(obj))) Fatal("_list_ca::_add. Link allocation failure") ;
Lock() ; if (m_pList == 0) m_pList = m_pLast = newlink ; else { if (!currlink) return E_NOINIT ;
if (bAfter) { // To insert after currlink newlink->next = currlink->next ; currlink->next = newlink ; } else { for (marker = m_pList ; marker ; marker = marker->next) { if (marker->next == currlink) break ; }
if (!marker) return E_NOTFOUND ;
newlink->next = currlink ; marker->next = newlink ; } }
m_nCount++ ; Unlock() ; return E_OK ; }
hzEcode _delete (_hz_listitem<OBJ>* pTL) { // Find the requested element in the list and delete it. This will also set the previous element's next pointer to the next element. // // Note that the practice of deleting elements is not fully recomended. The process is inefficient because in order to keep memory consumption low, // list elements have no backward pointer. In order to find the element prior to the target element, this function must iterate from the start. For // the most part, lists are intended to be relatively short so this is OK. However this is not the only issue. In general, Lists are intended to be // processed by iterating from begining to end. If the element is deleted by a call to Delete() on the iterator itself (a common method), it should // be noted the current element of the iterator then becomes the next element in the list. Control loops in which Delete() is conditionally called // on the iterator, need to ensure the iterator is not simply incremented each time, otherwise elements will be missed. // // Argument: pTL The list item to be deleted // // Returns: E_NOTFOUND If the supplied item is not in the list // E_OK If the item is deleted
_hz_listitem<OBJ>* prev = 0 ; // Previous link
Lock() ; if (pTL == m_pList) m_pList = m_pList->next ; else { // Advance prev link to be pointing to the item to be deleted for (prev = m_pList ; prev ; prev = prev->next) { if (prev->next == pTL) break ; }
// Sanity check, if null prev then exit if (!prev) return E_NOTFOUND ; }
// If the item is the last if (pTL == m_pLast) m_pLast = prev ;
// tie adjacent items together if (prev) prev->next = pTL->next ;
delete pTL ; m_nCount-- ; Unlock() ; return E_OK ; } } ;
_list_ca* mx ; // Pointer to internal list container
// Prevent copies // hzList<OBJ> (const hzList<OBJ>&) ; // hzList<OBJ>& operator= (const hzList<OBJ>&) ;
public: struct Iter { // Generic iterator for the hzList class template
_hz_listitem<OBJ>* m_pCurr ; // Current element _list_ca* m_pHandle ; // Pointer to the content
OBJ dflt ; // Default (null) element
Iter (void) { m_pHandle = 0 ; m_pCurr = 0 ; } Iter (OBJ x) { m_pHandle = 0 ; m_pCurr = 0 ; dflt = x ; } ~Iter (void) { m_pHandle = 0 ; m_pCurr = 0 ; }
void SetDefault (OBJ df) { dflt = df ; }
hzEcode Insert (OBJ obj, bool bAfter=false) { // Insert an element into the actual list. By default this will be before the current position but if bAfter is set then // the element will be placed after the current position. // // Returns: E_ARGUMENT If the current link is not supplied // E_NOTFOUND If the supplied current link isn't actually in the list // E_OK If the object was inserted
_hzfunc_ct("hzList::Iter::Insert") ;
if (!m_pHandle || !m_pCurr) return E_NOINIT ; return mx->_insert(m_pCurr, bAfter) ; }
hzEcode Delete (void) { // Delete the iterator's current element from the list. This operation will fail if the iterator is not the only one using the list's internal data // area. In the event the deletion is successful, as the curent element then ceases to exist the current element is set to the next element in the // list. The process calling delete on the iterator, should check if the deletion took place and if so, refrain from incrementing the iterator at // the top of the iteration loop. // // Returns: E_NOTFOUND If the iterator is not pointing to a list internal area. // E_CONFLICT If there is more than one iterator, none can delete // E_OK If the object was inserted
_hz_listitem<OBJ>* marker ; // List element pointer
hzEcode rc ; // Return code
if (!m_pHandle) return E_NOTFOUND ; if (m_pHandle->m_nIter > 1) return E_CONFLICT ; marker = m_pCurr->next ; rc = m_pHandle->_delete(m_pCurr) ; m_pCurr = marker ; return rc ; }
// Assignment operators hzList<OBJ>::Iter& operator= (const hzList<OBJ>& sup) { // Make an iterator equal to the beginning of the supplied list
m_pHandle = sup.mx ; m_pCurr = m_pHandle ? m_pHandle->m_pList : 0 ; return *this ; }
hzList<OBJ>::Iter& operator= (const Iter& i) { // Make an iterator equal to another
m_pHandle = i.m_pHandle ; m_pCurr = i.m_pCurr ; return *this ; }
void operator++ (void) { // Increment iterator
if (m_pCurr) m_pCurr = m_pCurr->next ; }
void operator++ (int) { // Increment iterator
if (m_pCurr) m_pCurr = m_pCurr->next ; }
OBJ& Element (void) { // Return curent element
if (!m_pCurr) return dflt ; return m_pCurr->m_Obj ; }
bool Valid (void) { return m_pCurr ? true : false ; } } ;
/* ** Public hzList functions */
hzList (void) { mx = 0 ; _hzGlobal_Memstats.m_numLists++ ; }
hzList<OBJ> (const hzList<OBJ>& op) { mx = 0 ;
if (op.mx) { mx->Lock() ; mx->m_nCopy++ ; mx = op.mx ; mx->Unlock() ; } }
~hzList (void) { if (mx) { if (mx->m_nCopy > 0) mx->m_nCopy-- ; else { _hzGlobal_Memstats.m_numLists-- ; delete mx ; mx = 0 ; } } }
// Assignment operator const hzList<OBJ>& operator= (const hzList<OBJ>& op) { if (mx && mx == op.mx) return *this ;
if (mx) { // If the list is populated, clear it. Note will not delete objects if the list is of pointers rather than real objects and // will therefore be a memory leak.
if (mx->m_nCopy) mx->m_nCopy-- ; else mx->Clear() ; }
mx = op.mx ; if (mx) mx->m_nCopy++ ; return *this ; }
uint32_t Count (void) const { // Return the number of items in the list return mx ? mx->m_nCount : 0 ; }
hzEcode Add (const OBJ obj) { // Add an object to the end of the list
_hzfunc_ct("hzList::Add") ;
if (!mx) { if (!(mx = new _list_ca())) Fatal("hzList::Add. Container allocation failure") ; } return mx->_add(obj) ; }
void Clear (void) { // Clear: Empty the list if (mx) { if (mx->m_nCopy > 0) mx->m_nCopy-- ; else mx->Clear() ; } mx = 0 ; } } ;
#endif // hzTmplList_h