/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * July 2007 * Author: Joel Edwards * * SafeList is a template class providing an pooled, offset based * doubly linked list. Effectively, all nodes are pulled from a * common pool of memory or "slab" that is automatically freed when * the destructor is called. * * !!WARNING!! * If you use pointers to allocated memory as your payloads for * the list nodes, it is your responsibility to free them! SafeList * will not free space you have allocated, only the space required * for its own nodes. * * All data addition and extraction is done via assignment operator * '=', so any classes passed in as the Type for the template must * have an overloaded '=' operator. It stores a copy of the data * at the reference you provide. If you do not wish the data to be * duplicated, you may pass in a pointer/offset rather than the * object itself. This is adviseable for very large objects. * * TODO: * . Create two mutator functions that take arguments * to specify locations of node additions and extractions. * Wrap existing functions around these. * * Protected Functions: * * uint32_t getNext( uint32_t current ) * uint32_t getPrev( uint32_t current ) * class SLNode *getP( uint32_t offset ) * Type *getPayloadP( uint32_t offset ) * * Public Functions: * * SafeList() * SafeList( uint32_t numNodes ) * ~SafeList() * bool add( Type &payload, bool (*compare)(Type &a1, Type &a2) ) * bool pushHead( Type &payload ) * bool pushTail( Type &payload ) * bool popHead( Type &payload ) * bool popTail( Type &payload ) * bool getDataAt( SLIterator &iter, Type &payload ) * bool getHead( Type &payload ) * bool getTail( Type &payload ) * bool pull( SLIterator &iter, Type &payload ) * Type *sample( SLIterator &iter ) * Type *sampleHead() * Type *sampleTail() * bool sort( bool (*compare)( Type &a1, Type &a2 ) ) * bool setIterator( SLIterator &iter ) * void setIteratorHead( SLIterator &iter ) * void setIteratorTail( SLIterator &iter ) * bool incIterator( SLIterator &iter ) * bool decIterator( SLIterator &iter ) * bool request( uint32_t numNodes ) * bool rebalance( bool fast = true ) * void setCap( uint32_t numNodes ) * void clearCap() * void printNodes() * void printListStatus( ) * bool isEmpty() * uint32_t size() * */ #ifndef _SAFE_LIST_H_ #define _SAFE_LIST_H_ #include #include #include #include #include #include #include #include #include #include template class SLIterator; template class SLNode; template class SLData; template class SafeList { protected: class SLData m_usedNodes; class SLData m_freeNodes; uint8_t *m_list; uint32_t m_listAlloc; uint32_t m_listSlab; uint32_t m_cap; uint32_t getNext( uint32_t current ) { class SLNode *tmpNode = getP(current); if ( !tmpNode ) return 0; return tmpNode->m_next; } uint32_t getPrev( uint32_t current ) { class SLNode *tmpNode = getP(current); if ( !tmpNode ) return 0; return tmpNode->m_prev; } class SLNode *getP( uint32_t offset ) { if ( !offset || !m_list ) return NULL; if ( offset > (m_listAlloc - sizeof(class SLNode)) ) return NULL; class SLNode *tmpNode = (class SLNode *) (m_list + offset); return tmpNode; } Type *getPayloadP( uint32_t offset ) { class SLNode *node = getP( offset ); if ( !node ) return NULL; if ( !node->m_used ) return NULL; return &node->m_payload; } public: // Create a new empty node, and add it to the head of the list. // If Type is a class you must call its constructor manually. // TODO: // . Impliment this function //bool newNode( SLIterator &iter ); // We don't sort bool sort( bool (*compare)( Type &a1, Type &a2 ) ) { return false; }; SafeList() { // Use SafeList64 for 64-bit architectures assert( sizeof(long) == 4 ); m_list = NULL; m_listAlloc = 0; m_listSlab = 0; m_usedNodes.m_head = 0; m_usedNodes.m_tail = 0; m_usedNodes.m_size = 0; m_freeNodes.m_head = 0; m_freeNodes.m_tail = 0; m_freeNodes.m_size = 0; m_cap = 0xffffffff; }; SafeList( uint32_t numNodes ) { // Use SafeList64 for 64-bit architectures assert( sizeof(long) == 4 ); m_list = NULL; m_listAlloc = 0; m_listSlab = 0; m_usedNodes.m_head = 0; m_usedNodes.m_tail = 0; m_usedNodes.m_size = 0; m_freeNodes.m_head = 0; m_freeNodes.m_tail = 0; m_freeNodes.m_size = 0; m_cap = 0xffffffff; // Attempt to set up the new list. request( numNodes ); }; ~SafeList() { if ( m_list ) #ifdef _SL_TEST_ // If SafeList is being tested outside of gb free( m_list ); #else mfree( m_list, m_listAlloc, "SafeList" ); #endif }; // emptyList() effectively clears out all data from the list. // Allocated space is not freed. void emptyList() { m_listSlab = 0; if ( m_list ) m_listSlab = 1; m_usedNodes.m_head = 0; m_usedNodes.m_tail = 0; m_usedNodes.m_size = 0; m_freeNodes.m_head = 0; m_freeNodes.m_tail = 0; m_freeNodes.m_size = 0; } // purge() completely resets the list, freeing all used // memory, and reseting all variables. void purge() { m_usedNodes.m_head = 0; m_usedNodes.m_tail = 0; m_usedNodes.m_size = 0; m_freeNodes.m_head = 0; m_freeNodes.m_tail = 0; m_freeNodes.m_size = 0; if ( m_list ) #ifdef _SL_TEST_ // If SafeList is being tested outside of gb free( m_list ); #else mfree( m_list, m_listAlloc, "SafeList" ); #endif m_list = NULL; m_listAlloc = 0; m_listSlab = 0; m_cap = 0xffffffff; } bool duplicate( SafeList &list ) { purge(); #ifdef _SL_TEST_ // If SafeList is being tested outside of gb m_list = (uint8_t *)malloc( list.m_listAlloc ); #else m_list = (uint8_t *)mmalloc( list.m_listAlloc, "SafeList" ); #endif if ( !m_list ) return false; memcpy( m_list, list.m_list, list.m_listAlloc ); m_listAlloc = list.m_listAlloc; m_listSlab = list.m_listSlab; m_usedNodes.m_head = list.m_usedNodes.m_head; m_usedNodes.m_tail = list.m_usedNodes.m_tail; m_usedNodes.m_size = list.m_usedNodes.m_size; m_freeNodes.m_head = list.m_freeNodes.m_head; m_freeNodes.m_tail = list.m_freeNodes.m_tail; m_freeNodes.m_size = list.m_freeNodes.m_size; m_cap = list.m_cap; return true; } bool exists( Type &item ) { Type temp; SLIterator iter; iter.associate( *this ); do { iter.getCurrent( temp ); if ( temp == item ) return true; } while ( iter++ ); return false; } // If you are going to use add() for insertions, it should // always be used in order to keep the list sorted. bool add( Type &payload, bool (*compare)(Type &a1, Type &a2) ) { if ( m_cap <= m_usedNodes.m_size ) return false; // We don't need to worry about head and tail manipulations // if we can ensure that the node won't end up there. uint32_t existingOff = 0; class SLNode *existing = NULL; bool success = false; if ( m_usedNodes.m_size == 0 ) { return pushHead( payload ); } else if ( m_usedNodes.m_size == 1 ) { existing = getP( m_usedNodes.m_head ); if ( !existing ) return success; if ( compare( payload, existing->m_payload ) ) return pushHead( payload ); return pushTail( payload ); } else { existing = getP( m_usedNodes.m_head ); if ( !existing ) return success; if ( !compare( existing->m_payload, payload ) ) return pushHead( payload ); existing = getP( m_usedNodes.m_tail ); if ( !existing ) return success; if ( !compare( payload, existing->m_payload ) ) return pushTail( payload ); } if ( !request( 1 ) ) { return success; } uint32_t tempNodeOff = 0; uint32_t newFreeOff = 0; class SLNode *tempNode = NULL; class SLNode *tempNode2 = NULL; class SLNode *newFreeHead = NULL; // If we have nodes on the free list, reuse one if ( m_freeNodes.m_size > 0 ) { tempNodeOff = m_freeNodes.m_head; tempNode = getP( tempNodeOff ); if ( !tempNode ) return success; m_freeNodes.m_size--; // If we are taking the only remaining node // in the free list, it is now empty. if ( m_freeNodes.m_size == 0 ) { m_freeNodes.m_head = 0; m_freeNodes.m_tail = 0; } else { newFreeOff = tempNode->m_next; m_freeNodes.m_head = newFreeOff; // If there are more free nodes, set the new // head node for the free list. newFreeHead = getP( newFreeOff ); if ( newFreeHead ) { newFreeHead->m_prev = 0; } } } // Otherwise we get a new node from the slab else { tempNodeOff = m_listSlab; tempNode = getP( tempNodeOff ); if ( !tempNode ) return success; m_listSlab += sizeof(class SLNode); } bool done = false; existingOff = getNext( m_usedNodes.m_head ); while ( !done ) { existing = getP( existingOff ); if ( !compare( existing->m_payload, payload ) ) { // . existing is pointing to our next neighbor // . tempNod2 is pointing to our previous // neighbor tempNode2 = getP( existing->m_prev ); tempNode2->m_next = tempNodeOff; tempNode->m_prev = existing->m_prev; tempNode->m_next = existingOff; existing->m_prev = tempNodeOff; tempNode->m_payload = payload; tempNode->m_used = 1; m_usedNodes.m_size++; success = true; break; } if ( existingOff == m_usedNodes.m_tail ) done = true; existingOff = getNext( existingOff ); } return success; }; bool pushHead( Type &payload ) { if ( m_cap <= m_usedNodes.m_size ) return false; // If we are out of memory, we cannot add a new node. if ( !request( 1 ) ) { return false; } uint32_t tempNodeOff = 0; uint32_t newFreeOff = 0; uint32_t oldUsedOff = 0; class SLNode *tempNode = NULL; class SLNode *newFreeHead = NULL; class SLNode *oldUsedHead = NULL; // If there are nodes in the free list, use them if ( m_freeNodes.m_size > 0 ) { tempNodeOff = m_freeNodes.m_head; tempNode = getP( tempNodeOff ); if ( !tempNode ) return false; m_freeNodes.m_size--; // If we are taking the only remaining node in // the free list, it is now empty. if ( m_freeNodes.m_size == 0 ) { m_freeNodes.m_head = 0; m_freeNodes.m_tail = 0; } else { newFreeOff = getNext( tempNodeOff ); m_freeNodes.m_head = newFreeOff; // If there are more free nodes, set the new // head node for the free list. if ( newFreeOff ) { newFreeHead = getP( newFreeOff ); newFreeHead->m_prev = 0; } } } // Otherwise we are out of free nodes, and we use the slab. else { tempNodeOff = m_listSlab; tempNode = getP( tempNodeOff ); if ( !tempNode ) return false; m_listSlab += sizeof(class SLNode); } oldUsedOff = m_usedNodes.m_head; // If are nodes in the used list, upate its old head node. if ( oldUsedOff ) { oldUsedHead = getP( oldUsedOff ); oldUsedHead->m_prev = tempNodeOff; } // Set up the new node. tempNode->m_next = oldUsedOff; tempNode->m_prev = 0; tempNode->m_payload = payload; tempNode->m_used = 1; // If the used list was empty, we are now the only node in it. if ( m_usedNodes.m_tail == 0 ) m_usedNodes.m_tail = tempNodeOff; m_usedNodes.m_head = tempNodeOff; m_usedNodes.m_size++; return true; }; bool pushTail( Type &payload ) { if ( m_cap <= m_usedNodes.m_size ) return false; // If we are out of memory, we cannot add a new node. if ( !request( 1 ) ) { return false; } uint32_t tempNodeOff = 0; uint32_t newFreeOff = 0; uint32_t oldUsedOff = 0; class SLNode *tempNode = NULL; class SLNode *newFreeTail = NULL; class SLNode *oldUsedTail = NULL; // If there are nodes in the free list, use them if ( m_freeNodes.m_size > 0 ) { tempNodeOff = m_freeNodes.m_tail; tempNode = getP( tempNodeOff ); if ( !tempNode ) return false; m_freeNodes.m_size--; // If we are taking the only remaining node // in the free list, it is now empty. if ( m_freeNodes.m_size == 0 ) { m_freeNodes.m_tail = 0; m_freeNodes.m_head = 0; } else { newFreeOff = getPrev( tempNodeOff ); m_freeNodes.m_tail = newFreeOff; // If there are more free nodes, set the new // head node for the free list. if ( newFreeOff ) { newFreeTail = getP( newFreeOff ); newFreeTail->m_next = 0; } } } // Otherwise, pull a new node from the slab else { tempNodeOff = m_listSlab; tempNode = getP( tempNodeOff ); if ( !tempNode ) return false; m_listSlab += sizeof(class SLNode); } oldUsedOff = m_usedNodes.m_tail; // If are nodes in the used list, upate its old head node. if ( oldUsedOff ) { oldUsedTail = getP( oldUsedOff ); oldUsedTail->m_next = tempNodeOff; } // Set up the new node. tempNode->m_prev = oldUsedOff; tempNode->m_next = 0; tempNode->m_payload = payload; tempNode->m_used = 1; // If the used list was empty, we are now the only node in it. if ( m_usedNodes.m_head == 0 ) m_usedNodes.m_head = tempNodeOff; m_usedNodes.m_tail = tempNodeOff; m_usedNodes.m_size++; return true; }; bool popHead( Type &payload ) { // If we have no used nodes left, there is nothing to return. if ( m_usedNodes.m_size == 0 ) { return false; } uint32_t tempNodeOff = m_usedNodes.m_head; uint32_t newUsedOff = 0; uint32_t oldFreeOff = 0; class SLNode *tempNode = NULL; class SLNode *newUsedHead = NULL; class SLNode *oldFreeHead = NULL; // If the head node is null, the list is empty. // This should never happen if we are keeping track of // our sizes correctly. tempNode = getP( tempNodeOff ); if ( !tempNode ) { return false; } // Store the offsets of the used list's new head node and the // free list's current head node. newUsedOff = tempNode->m_next; oldFreeOff = m_freeNodes.m_head; // If there was a head node for the free list, set its previous // to the node we are deleting. oldFreeHead = getP( oldFreeOff ); if ( oldFreeHead ) { oldFreeHead->m_prev = tempNodeOff; } // If there was a node after the node being deleted, set its // previous to null because it is the new head node. newUsedHead = getP( newUsedOff ); if ( newUsedHead ) { newUsedHead->m_prev = 0; } // Update the deleted node. tempNode->m_next = oldFreeOff; tempNode->m_prev = 0; tempNode->m_used = 0; payload = tempNode->m_payload; // If we are deleting the only node in the used list, // it is now empty. if ( m_usedNodes.m_tail == tempNodeOff ) m_usedNodes.m_tail = 0; m_usedNodes.m_head = newUsedOff; m_usedNodes.m_size--; // If the free list was empty, we are now the only node in it. if ( m_freeNodes.m_tail == 0 ) m_freeNodes.m_tail = tempNodeOff; m_freeNodes.m_head = tempNodeOff; m_freeNodes.m_size++; return true; }; bool popTail( Type &payload ) { // If we have no used nodes left, there is nothing to return. if ( m_usedNodes.m_size == 0 ) { return false; } uint32_t tempNodeOff = m_usedNodes.m_tail; uint32_t newUsedOff = 0; uint32_t oldFreeOff = 0; class SLNode *tempNode = NULL; class SLNode *newUsedTail = NULL; class SLNode *oldFreeTail = NULL; // If the head node is null, the list is empty. // This should never happen if we are keeping track of // our sizes correctly. tempNode = getP( tempNodeOff ); if ( !tempNode) return false; // Store the offsets of the used list's new head node and the // free list's current head node. newUsedOff = tempNode->m_prev; oldFreeOff = m_freeNodes.m_tail; // If there was a head node for the free list, set its previous // to the node we are deleting. if ( oldFreeOff ) { oldFreeTail = getP( oldFreeOff ); if ( !oldFreeTail ) return false; oldFreeTail->m_next = tempNodeOff; } // If there was a node after the node being deleted, set its // previous to null because it is the new head node. if ( newUsedOff ) { newUsedTail = getP( newUsedOff ); if ( !newUsedTail ) return false; newUsedTail->m_next = 0; } // Update the deleted node. tempNode->m_prev = oldFreeOff; tempNode->m_next = 0; tempNode->m_used = 0; payload = tempNode->m_payload; // If we are deleting the only node in the used list, // it is now empty. if ( m_usedNodes.m_head == tempNodeOff ) m_usedNodes.m_head = 0; m_usedNodes.m_tail = newUsedOff; m_usedNodes.m_size--; // If the free list was empty, we are now the only node in it. if ( m_freeNodes.m_head == 0 ) m_freeNodes.m_head = tempNodeOff; m_freeNodes.m_tail = tempNodeOff; m_freeNodes.m_size++; return true; }; bool getDataAt( SLIterator &iter, Type &payload ) { SLNode *node = getP( iter.m_offset ); if ( node && node->m_used ) { payload = node->m_payload; return true; } return false; }; bool getHead( Type &payload ) { SLNode *node = getP( m_usedNodes.m_head ); if ( node ) { payload = node->m_payload; return true; } return false; }; bool getTail( Type &payload ) { SLNode *node = getP( m_usedNodes.m_tail ); if ( node ) { payload = node->m_payload; return true; } return false; }; bool pull( SLIterator &iter, Type &payload ) { if ( m_usedNodes.m_size == 0 ) return false; uint32_t tempNodeOff = iter.m_offset; // If we are at the head or tail of the list, // we simply use the specialized functions // for those cases. if ( tempNodeOff == m_usedNodes.m_head ) return popHead( payload ); if ( tempNodeOff == m_usedNodes.m_tail ) return popTail( payload ); SLNode *tempNode = getP( tempNodeOff ); if ( !tempNode ) return false; if ( !tempNode->m_used ) return false; SLNode *prevNode = getP( tempNode->m_prev ); SLNode *nextNode = getP( tempNode->m_next ); // We already checked if we were at the head // or tail of the list, in which is are the // only cases when one of these will be NULL. if ( ! prevNode || ! nextNode ) return false; payload = tempNode->m_payload; prevNode->m_next = tempNode->m_next; nextNode->m_prev = tempNode->m_prev; uint32_t oldFreeOff = m_freeNodes.m_head; m_freeNodes.m_size++; m_usedNodes.m_size--; m_freeNodes.m_head = tempNodeOff; tempNode->m_prev = 0; tempNode->m_used = 0; SLNode *oldFreeHead = getP( oldFreeOff ); if ( !oldFreeHead ) { m_freeNodes.m_tail = tempNodeOff; tempNode->m_next = 0; return true; } // If this is an invalid pointer, we have not been // keeping account correctly. // Consider getting this earlier if it exists. oldFreeHead->m_prev = tempNodeOff; tempNode->m_next = oldFreeOff; return true; }; Type *sample( SLIterator &iter ) { return getPayloadP( iter.m_offset ); }; Type *sampleHead() { return getPayloadP( m_usedNodes.m_head ); }; Type *sampleTail() { return getPayloadP( m_usedNodes.m_tail ); }; bool isEmpty() { if ( m_usedNodes.m_size > 0 ) return false; return true; }; uint32_t size() { return m_usedNodes.m_size; }; // Associate an iterator with this list. bool setIterator( SLIterator &iter ) { iter.m_offset = m_usedNodes.m_head; iter.m_list = this; return true; }; void setIteratorHead( SLIterator &iter ) { iter.m_offset = m_usedNodes.m_head; iter.m_list = this; }; void setIteratorTail( SLIterator &iter ) { iter.m_offset = m_usedNodes.m_tail; iter.m_list = this; }; bool incIterator( SLIterator &iter ) { SLNode *node = getP( iter.m_offset ); if ( node && node->m_next ) { iter.m_offset = node->m_next; return true; } return false; }; bool decIterator( SLIterator &iter ) { SLNode *node = getP( iter.m_offset ); if ( node && node->m_prev ) { iter.m_offset = node->m_prev; return true; } return false; }; // Request space for more nodes. We only realloc // if we don't have enough space in both the free // list and the slab for the requested number of // nodes. bool request( uint32_t numNodes ) { if ( m_cap <= (m_usedNodes.m_size + numNodes) ) return false; // If no nodes were requested, nothing to do. if ( !numNodes ) { return true; } uint32_t bytesToAdd = numNodes * sizeof(class SLNode); // If we already have enough space, no need to realloc. if ( ( (m_freeNodes.m_size * sizeof(class SLNode)) + (m_listAlloc - m_listSlab) ) >= bytesToAdd ) return true; if ( numNodes == 1 ) { // If we are out of nodes, add 50% again ontop of the // previous amount of space. uint32_t listNodes = m_listAlloc / sizeof(class SLNode); if ( listNodes > 2 ) { numNodes = listNodes / 2; bytesToAdd = numNodes * sizeof(class SLNode); } } // If this is our first allocation, we need to add the // zero byte at the beginning of the "slab". if ( (!m_list) || (!m_listAlloc) ) { bytesToAdd++; } // If the requested number of nodes will cause overflow, don't // attempt to realloc, otherwise we will decrease the size of // our buffer. if ( (0xffffffff - bytesToAdd) < m_listAlloc ) { return false; } uint8_t *tmpList = m_list; uint32_t tmpAlloc = m_listAlloc; m_listAlloc += bytesToAdd; #ifdef _SL_TEST_ // If SafeList is being tested outside of gb m_list = (uint8_t *)realloc( m_list, m_listAlloc ); #else m_list = (uint8_t *)mrealloc( m_list, tmpAlloc, m_listAlloc, "SafeList" ); #endif if (!m_list) { m_list = tmpList; m_listAlloc = tmpAlloc; return false; } if ( !m_listSlab ) m_listSlab = 1; return true; }; // Move all nodes to the beginning of our allocation // and resize if we have to much unused space allocated. // @ fast - if this is true we only rebalance if we can first // allocate a new buffer to push the used nodes onto // discarding all free nodes, otherwise we // don't perform the operation and return false. bool rebalance( bool fast = true ) { // This feature is not yet supported return false; }; // setCap() will set the max number of nodes allowed // in this list. If we call setCap on with a // number of nodes than is less than the current // size, we simply refuse to add any more; and // once we get below that cap, we will never pass // it again. void setCap( uint32_t numNodes ) { m_cap = numNodes; }; // clearCap() takes away the upper limit on the // number of nodes. We can add nodes until we run // out of memory. void clearCap() { m_cap = 0xffffffff; }; // For debugging only void printNodes() { class SLNode *tempNode; uint32_t tempNodeOff; uint32_t nodeId; if ( !m_list || (m_listAlloc < sizeof(class SLNode)) ) { return; } printf( "================ USED NODES ================\n\n" ); tempNodeOff = m_usedNodes.m_head; nodeId = 0; while ( tempNodeOff ) { tempNode = getP( tempNodeOff ); printf( "Node %u:\n" "next off = %u\n" "prev off = %u\n" "p_value = %x\n\n", nodeId, tempNode->m_next, tempNode->m_prev, (uint32_t)tempNode ); tempNodeOff = getNext( tempNodeOff ); nodeId++; } printf( "================ FREE NODES ================\n\n" ); tempNodeOff = m_freeNodes.m_head; nodeId = 0; while ( tempNodeOff ) { tempNode = getP( tempNodeOff ); printf( "Node %u:\n" "next off = %u\n" "prev off = %u\n" "p_value = %x\n\n", nodeId, tempNode->m_next, tempNode->m_prev, (uint32_t)tempNode ); tempNodeOff = getNext( tempNodeOff ); nodeId++; } printf( "================== SLAB SPACE REMAINING " "====================" "\n\n" ); printf( "Slab has room for %u additional nodes.\n", (m_listAlloc - m_listSlab) / sizeof(class SLNode) ); printf( "Slab has %u wasted bytes at end of allocation.\n\n", (m_listAlloc - m_listSlab) % sizeof(class SLNode) ); }; // For debugging only void printListStatus( ) { printf( "================= SafeList current status " "==================" "\n" "Bytes Allocated: %u\n" "Bytes Used: %u\n" "Bytes on Slab: %u\n" "Slab Nodes: %u\n" "\n" "FreeList\n" "Nodes: %u\n" "Bytes: %u\n" "\n" "UsedList\n" "Nodes: %u\n" "Bytes: %u\n" "" "===========================================" "=================" "\n", m_listAlloc, m_listSlab, m_listAlloc - m_listSlab, (m_listAlloc - m_listSlab) / sizeof(class SLNode), m_freeNodes.m_size, m_freeNodes.m_size * sizeof(class SLNode), m_usedNodes.m_size, m_usedNodes.m_size * sizeof(class SLNode) ); }; }; template class SLIterator { friend class SafeList; protected: uint32_t m_offset; class SafeList *m_list; public: SLIterator() { m_offset = 0; m_list = NULL; } // Got to next element, return false if we are at // the tail node. bool operator ++ ( int ) { if ( !m_list ) return false; return m_list->incIterator( *this ); } // Got to previous element, return false if we are at // the head node. bool operator -- ( int ) { if ( !m_list ) return false; return m_list->decIterator( *this ); } // Set this iterator equal to an iterator of the same type. void operator = (SLIterator &i) { m_offset = i.m_offset; m_list = i.m_list; } // Set iterator to the head of the list. void setHead () { if ( m_list ) m_list->setIteratorHead( *this ); } // Set iterator to the tail of the list. void setTail () { if ( m_list ) m_list->setIteratorTail( *this ); } // May not want to be able to do this... // Although it simply calls a public function, // so there is no security issue. // Gets the data at the current point if it exists, // otherwise payload remains unmodified. bool getCurrent ( Type &payload ) { if ( !m_list ) return false; return m_list->getDataAt( *this, payload ); } // Associates this iterator with the list passed as the arg. bool associate ( SafeList &list ) { return list.setIterator( *this ); } // Drops the list this iterator is associated with. void dropList () { m_offset = 0; m_list = NULL; } }; template class SLNode { friend class SafeList; protected: uint32_t m_next; uint32_t m_prev; Type m_payload; uint8_t m_used; }; template class SLData { friend class SafeList; protected: uint32_t m_head; uint32_t m_tail; uint32_t m_size; }; #endif