/* Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved. 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; version 2 of the License. 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, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #ifndef NDB_LINKEDSTACK_HPP #define NDB_LINKEDSTACK_HPP #include /** * LinkedStack * * A templated class for a stack of elements E. * Storage for the elements is allocated using the passed * allocator. * Push copies the supplied element into the stack * Pop overwrites the supplied element with the contents * of the top of the stack * Internally, the class allocates 'blocks' of elements of * the size passed, linking them together as necessary. * As the stack shrinks, the blocks are not released. * Blocks are returned to the allocator when release() is * called. * reset() empties the stack without releasing the allocated * storage. */ template class LinkedStack { private: struct BlockHeader { BlockHeader* next; BlockHeader* prev; E* elements; }; BlockHeader* allocBlock() { /* Alloc blockheader and element array */ BlockHeader* h = (BlockHeader*) A::alloc(allocatorContext, sizeof(BlockHeader)); E* e = (E*) A::mem_calloc(allocatorContext, blockElements, sizeof(E)); h->next = NULL; h->prev = NULL; h->elements = e; return h; } bool valid() { if (stackTop) { assert(firstBlock != NULL); assert(currBlock != NULL); /* Check that currBlock is positioned on correct * block, except for block boundary case */ Uint32 blockNum = (stackTop - 1) / blockElements; BlockHeader* bh = firstBlock; while(blockNum--) { bh = bh->next; } assert(bh == currBlock); } else { assert(currBlock == NULL); } return true; } /* Note that stackTop is 'next insertion point' whereas * currBlock points to block last inserted to. * On block boundaries, they refer to different blocks */ void* allocatorContext; BlockHeader* firstBlock; BlockHeader* currBlock; Uint32 stackTop; Uint32 blockElements; public: LinkedStack(Uint32 _blockElements, void* _allocatorContext=NULL) : allocatorContext(_allocatorContext), firstBlock(NULL), currBlock(NULL), stackTop(0), blockElements(_blockElements) { assert(blockElements > 0); assert(valid()); } ~LinkedStack() { assert(valid()); /* Release block storage if present */ release(); } bool push(E& elem) { assert(valid()); Uint32 blockOffset = stackTop % blockElements; if (blockOffset == 0) { /* On block boundary */ if (stackTop) { /* Some elements exist already */ if (!currBlock->next) { /* End of block list, alloc another */ BlockHeader* newBlock = allocBlock(); if (!newBlock) return false; currBlock->next = newBlock; currBlock->next->prev = currBlock; } currBlock = currBlock->next; } else { /* First element */ if (!firstBlock) { BlockHeader* newBlock = allocBlock(); if (!newBlock) return false; firstBlock = currBlock = newBlock; } currBlock = firstBlock; } } currBlock->elements[ blockOffset ] = elem; stackTop++; assert(valid()); return true; } bool pop(E& elem) { assert(valid()); if (stackTop) { stackTop--; Uint32 blockOffset = stackTop % blockElements; elem = currBlock->elements[ blockOffset ]; if (blockOffset == 0) { /* Block boundary, shift back to prev block. */ if (stackTop) assert(currBlock->prev); currBlock = currBlock->prev; } assert(valid()); return true; } return false; } Uint32 size() const { return stackTop; } void reset() { assert(valid()); stackTop = 0; currBlock = NULL; assert(valid()); }; void release() { assert(valid()); BlockHeader* h = firstBlock; while (h) { BlockHeader* n = h->next; A::mem_free(allocatorContext, h->elements); A::mem_free(allocatorContext, h); h = n; }; stackTop = 0; firstBlock = currBlock = NULL; assert(valid()); } }; #endif