/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifndef BAGEL_BOFLIB_LIST_H #define BAGEL_BOFLIB_LIST_H #include "common/scummsys.h" #include "bagel/boflib/misc.h" namespace Bagel { namespace SpaceBar { #define MIN_NODES 5 // Minimum # of pre-allocated nodes in node array template class CBofListNode { protected: T _cItem; // Data contained at this node public: CBofListNode() { _pNext = _pPrev = nullptr; } CBofListNode(T cItem) { _pNext = _pPrev = nullptr; _cItem = cItem; } T getNodeItem() { return _cItem; } void setNodeItem(T cItem) { _cItem = cItem; } CBofListNode *_pNext; // Next node in list CBofListNode *_pPrev; // Previous node in list }; template class CBofList { private: void newItemList() { if (_pItemList != nullptr) { bofFree(_pItemList); _pItemList = nullptr; } if (_nNumItems != 0) { _pItemList = (void **)bofAlloc(_nNumItems * sizeof(void *)); } } void killItemList() { if (_pItemList != nullptr) { bofFree(_pItemList); _pItemList = nullptr; } } void recalcItemList() { // We only want to recalc if we're about to overflow what we have if (_nNumItems >= _nItemsAllocated) { if (_pItemList != nullptr) { bofFree(_pItemList); _pItemList = nullptr; } if (_nNumItems != 0) { assert(_nItemsAllocated < 0x8000); _nItemsAllocated *= 2; if (_nItemsAllocated == 0) _nItemsAllocated = MIN_NODES; _pItemList = (void **)bofAlloc(_nItemsAllocated * sizeof(void *)); } } if (_nNumItems != 0) { assert(_pItemList != nullptr); int i = 0; CBofListNode *pNode = _pHead; while (pNode != nullptr) { *(_pItemList + i++) = pNode; pNode = pNode->_pNext; } } } /** * Allocates a new CBofListNode with specified data * @param cItem Data to store in new node * @returns Pointer to new node */ CBofListNode *newNode(T cItem) { CBofListNode *pNewNode = new CBofListNode(cItem); return pNewNode; } /** * Calculates the actual head of this linked list * @remarks This function is used for debugging to verify that _pHead * is still pointing to the 1st node in the list. * @returns Pointer to head of list */ CBofListNode *getActualHead() { CBofListNode *pNode; CBofListNode *pLast = pNode = _pHead; while (pNode != nullptr) { pLast = pNode; pNode = pNode->_pPrev; } return pLast; } /** * Calculates the actual tail of this linked list * @remarks This function is used for debugging to verify that _pTail * is still pointing to the last node in the list. * @returns Pointer to tail of list */ CBofListNode *getActualTail() { CBofListNode *pNode; CBofListNode *pLast = pNode = _pTail; while (pNode != nullptr) { pLast = pNode; pNode = pLast->_pNext; } return pLast; } protected: uint32 _nNumItems; uint32 _nItemsAllocated; CBofListNode *_pHead; // pointer to head of list CBofListNode *_pTail; // pointer to tail of list void **_pItemList; // pointer to secondary node list public: /* * Constructor */ CBofList() { _nNumItems = 0; _nItemsAllocated = 0; _pHead = _pTail = nullptr; _pItemList = nullptr; } /** * Destructor */ virtual ~CBofList() { removeAll(); killItemList(); assert(_nNumItems == 0); } int getCount() const { return _nNumItems; } /** * Retrieves the number of items in this list * @returns Returns the number of linked items in this linked list. */ int getActualCount() const { uint32 nCount = 0; CBofListNode *pNode = _pHead; while (pNode != nullptr) { nCount++; pNode = pNode->_pNext; } // There should be no discrepancy assert(_nNumItems == nCount); return _nNumItems; } /** * Returns true if the list is empty * @return */ bool isEmpty() const { return _pHead == nullptr; } /** * Retrieves the item at the specified location * @returns Returns the item located at the node with given index. * @param nNodeIndex Index of node to retrieve */ inline T getNodeItem(int nNodeIndex) { CBofListNode *pNode = getNode(nNodeIndex); assert(pNode != nullptr); return pNode->getNodeItem(); } void setNodeItem(int nNodeIndex, T tNewItem) { CBofListNode *pNode = getNode(nNodeIndex); assert(pNode != nullptr); pNode->setNodeItem(tNewItem); } T operator[](int nIndex) { return getNodeItem(nIndex); } /** * Retrieves the node at the specified location * @returns Returns the node located at the given index. * @param nNodeIndex Index of node to retrieve */ CBofListNode *getNode(int nNodeIndex) { assert(nNodeIndex >= 0 && nNodeIndex < getCount()); CBofListNode *pNode; if (_pItemList == nullptr) { pNode = _pHead; while (pNode != nullptr) { if (nNodeIndex-- == 0) break; pNode = pNode->_pNext; } } else { pNode = (CBofListNode *)(*(_pItemList + nNodeIndex)); } return pNode; } /** * Inserts a new node as the previous node to the one specified * @param nNodeIndex Index of node to insert before * @param cNewItem Data to store at new node */ void insertBefore(int nNodeIndex, T cNewItem) { assert(!isEmpty()); insertBefore(getNode(nNodeIndex), cNewItem); } /** * Inserts a new node as the previous node to the one specified * @param pNode Node to insert before * @param cNewItem Data to store at new node */ void insertBefore(CBofListNode *pNode, T cNewItem) { assert(pNode != nullptr); assert(!isEmpty()); if (pNode == _pHead) { addToHead(cNewItem); } else { CBofListNode *pNewNode = newNode(cNewItem); pNewNode->_pPrev = pNode->_pPrev; pNewNode->_pNext = pNode; if (pNode->_pPrev != nullptr) pNode->_pPrev->_pNext = pNewNode; pNode->_pPrev = pNewNode; } // one more item in list assert(_nNumItems != 0xFFFF); _nNumItems++; recalcItemList(); } /** * Inserts a new node as the next node to the one specified * @param nNodeIndex Index of node to insert after * @param cNewItem Data to store at new node */ void insertAfter(int nNodeIndex, T cNewItem) { assert(!isEmpty()); insertAfter(getNode(nNodeIndex), cNewItem); } /** * Inserts a new node as the next node to the one specified * @param pNode Node to insert after * @param cNewItem Data to store at new node */ void insertAfter(CBofListNode *pNode, T cNewItem) { assert(pNode != nullptr); assert(!isEmpty()); if (pNode == _pTail) { addToTail(cNewItem); } else { CBofListNode *pNewNode = newNode(cNewItem); pNewNode->_pPrev = pNode; pNewNode->_pNext = pNode->_pNext; if (pNode->_pNext != nullptr) pNode->_pNext->_pPrev = pNewNode; pNode->_pNext = pNewNode; } // one more item in list assert(_nNumItems != 0xFFFF); _nNumItems++; recalcItemList(); } /** * Removes specified node from the list * @param pNode Node to remove * @returns Item stored at specified location */ T remove(CBofListNode *pNode) { assert(pNode != nullptr); // One less item in list _nNumItems--; //assert(_nNumItems >= 0); if (pNode != nullptr) { T retVal = pNode->getNodeItem(); if (_pHead == pNode) _pHead = _pHead->_pNext; if (_pTail == pNode) _pTail = _pTail->_pPrev; if (pNode->_pPrev != nullptr) pNode->_pPrev->_pNext = pNode->_pNext; if (pNode->_pNext != nullptr) pNode->_pNext->_pPrev = pNode->_pPrev; delete pNode; recalcItemList(); return retVal; } else { return T(); } } /** * Removes specified node (by index) from the list * @param nNodeIndex Index of node to remove * @returns Item stored at specified location */ T remove(int nNodeIndex) { return remove(getNode(nNodeIndex)); } /** * Removes all nodes from this list * @remarks Deletes all memory used by the nodes in this list */ void removeAll() { int i = getCount(); while (i-- != 0) remove(0); } /** * Removes specified node (by index) from the list * @returns Item stored at specified location */ inline T removeHead() { assert(_pHead != nullptr); return remove(_pHead); } /** * Removes specified node (by index) from the list * @returns Item stored at specified location */ inline T removeTail() { assert(_pTail != nullptr); return remove(_pTail); } /** * Adds specified node as the new head of this list * @param pNewNode Pointer to node to add to the list */ inline void addToHead(CBofListNode *pNewNode) { assert(pNewNode != nullptr); pNewNode->_pNext = _pHead; pNewNode->_pPrev = nullptr; if (_pHead != nullptr) _pHead->_pPrev = pNewNode; _pHead = pNewNode; if (_pTail == nullptr) _pTail = _pHead; // one less item in list assert(_nNumItems != 0xFFFF); _nNumItems++; recalcItemList(); } /** * Adds specified item as the new head of this list * @param cItem Item to add to the list */ inline void addToHead(T cItem) { addToHead(newNode(cItem)); } /** * Adds specified node as the new tail of this list * @param pNewNode Pointer to node to add to the list */ void addToTail(CBofListNode *pNewNode) { assert(pNewNode != nullptr); pNewNode->_pPrev = _pTail; pNewNode->_pNext = nullptr; if (_pTail != nullptr) _pTail->_pNext = pNewNode; _pTail = pNewNode; if (_pHead == nullptr) _pHead = _pTail; // One more item in list assert(_nNumItems != 0xFFFF); _nNumItems++; recalcItemList(); } /** * Adds specified item as the new tail of this list * @param cItem Item to add to the list */ void addToTail(T cItem) { addToTail(newNode(cItem)); } CBofListNode *getHead() const { return _pHead; } CBofListNode *getTail() const { return _pTail; } }; } // namespace SpaceBar } // namespace Bagel #endif