//////////////////////////////////////////////////////////////////////////////// /// @brief priorty queue /// /// @file /// /// /// DISCLAIMER /// /// Copyright by triAGENS GmbH - All rights reserved. /// /// The Programs (which include both the software and documentation) /// contain proprietary information of triAGENS GmbH; they are /// provided under a license agreement containing restrictions on use and /// disclosure and are also protected by copyright, patent and other /// intellectual and industrial property laws. Reverse engineering, /// disassembly or decompilation of the Programs, except to the extent /// required to obtain interoperability with other independently created /// software or as specified by law, is prohibited. /// /// The Programs are not intended for use in any nuclear, aviation, mass /// transit, medical, or other inherently dangerous applications. It shall /// be the licensee's responsibility to take all appropriate fail-safe, /// backup, redundancy, and other measures to ensure the safe use of such /// applications if the Programs are used for such purposes, and triAGENS /// GmbH disclaims liability for any damages caused by such use of /// the Programs. /// /// This software is the confidential and proprietary information of /// triAGENS GmbH. You shall not disclose such confidential and /// proprietary information and shall use it only in accordance with the /// terms of the license agreement you entered into with triAGENS GmbH. /// /// Copyright holder is triAGENS GmbH, Cologne, Germany /// /// @author Dr. Frank Celler /// @author Dr. O /// @author Martin Schoenert /// @author Copyright 2009-2012, triagens GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// #include "priorityqueue.h" #include "ShapedJson/shaped-json.h" #include // ----------------------------------------------------------------------------- // --SECTION-- useful forward declarations // ----------------------------------------------------------------------------- /* oreste's debug only typedef struct { size_t numFields; // the number of fields TRI_shaped_json_t* fields; // list of shaped json objects which the collection should know about void* data; // master document pointer void* collection; // pointer to the collection; uint64_t pqSlot; // int pointer to the position in the pq array } xx; */ //////////////////////////////////////////////////////////////////////////////// /// @addtogroup PriorityQueue /// @{ //////////////////////////////////////////////////////////////////////////////// static bool AddPQueue (TRI_pqueue_t*, void*); static bool CheckPQSize (TRI_pqueue_t*); //static bool CompareIsLess (TRI_pqueue_t*, void*, void*); static bool FixPQ (TRI_pqueue_t*, uint64_t); static char* GetItemAddress (TRI_pqueue_t*, uint64_t); static bool RemovePQueue (TRI_pqueue_t*, uint64_t, bool); static void* TopPQueue (TRI_pqueue_t*); //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- priority queue constructors and destructors // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup PriorityQueue /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief initialises a priority queue //////////////////////////////////////////////////////////////////////////////// bool TRI_InitPQueue (TRI_pqueue_t* pq, size_t initialCapacity, size_t itemSize, bool reverse, void (*clearStorage) (struct TRI_pqueue_s*, void*), uint64_t (*getStorage) (struct TRI_pqueue_s*, void*), bool (*isLess) (struct TRI_pqueue_s*, void*, void*), void (*updateStorage) (struct TRI_pqueue_s*, void*, uint64_t pos)) { if (pq == NULL) { return false; } // .......................................................................... // Assign the call back functions // .......................................................................... // .......................................................................... // This callback is used to remove any memory which may be used as part of // the storage within the array _items. Callback required since we do not // know if there is any internal structure. // .......................................................................... pq->clearStoragePQ = clearStorage; // .......................................................................... // Returns the position within the _items array the element is. // Currently this simply is used to perform a consistency check. // .......................................................................... pq->getStoragePQ = getStorage; // .......................................................................... // The actual comparison function which returns true if left item is // less than right item (otherwise false). // .......................................................................... pq->isLessPQ = isLess; // .......................................................................... // Stores the position of the element within the _items array. Its purpose // is to allow the storage position to be located independent of the // priority queue _items array. // .......................................................................... pq->updateStoragePQ = updateStorage; // .......................................................................... // Assign the element size // .......................................................................... pq->_base._itemSize = itemSize; // .......................................................................... // Initialise invalid access counters // .......................................................................... pq->_base._nrFalseRemoves = 0; pq->_base._nrFalseAdds = 0; // .......................................................................... // Set the capacity and assign memeory for storage // .......................................................................... pq->_base._capacity = initialCapacity; pq->_base._items = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, pq->_base._itemSize * pq->_base._capacity, false); if (pq->_base._items == NULL) { TRI_set_errno(TRI_ERROR_OUT_OF_MEMORY); LOG_ERROR("out of memory when creating priority queue storage"); return false; } // .......................................................................... // Initialise the memory allcoated for the storage of pq (0 filled) // .......................................................................... memset(pq->_base._items, 0, pq->_base._itemSize * pq->_base._capacity); // .......................................................................... // initialise the number of items stored // .......................................................................... pq->_base._count = 0; // .......................................................................... // Determines if the pq should be reversed // .......................................................................... pq->_base._reverse = reverse; // .......................................................................... // The static call back functions to query, modifiy the pq // .......................................................................... pq->add = AddPQueue; pq->remove = RemovePQueue; pq->top = TopPQueue; return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief destroys a priority queue, but does not free the pointer //////////////////////////////////////////////////////////////////////////////// void TRI_DestroyPQueue(TRI_pqueue_t* pq) { size_t j; void* thisItem; if (pq == NULL) { return; } for (j = 0; j < pq->_base._count; ++j) { thisItem = (j * pq->_base._itemSize) + pq->_base._items; pq->clearStoragePQ(pq,thisItem); } TRI_Free(TRI_UNKNOWN_MEM_ZONE, pq->_base._items); } //////////////////////////////////////////////////////////////////////////////// /// @brief destroys a priority queue and frees the pointer //////////////////////////////////////////////////////////////////////////////// void TRI_FreePQueue(TRI_pqueue_t* pq) { if (pq == NULL) { return; } TRI_DestroyPQueue(pq); TRI_Free(TRI_UNKNOWN_MEM_ZONE, pq); } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- priority queue callback functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup PriorityQueue /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief inserts an item into the priority queue //////////////////////////////////////////////////////////////////////////////// static bool AddPQueue(TRI_pqueue_t* pq, void* item) { size_t k; // ........................................................................... // Some basic checks for consistency // ........................................................................... if (pq == NULL) { return false; } if (pq->getStoragePQ(pq,item) != 0) { pq->_base._nrFalseAdds++; } // ........................................................................... // handle case that pq is too small // ........................................................................... if (!CheckPQSize(pq)) { return false; } // ........................................................................... // increase the number of items in the queue // ........................................................................... pq->_base._count++; // ........................................................................... // add the item to the end // ........................................................................... k = (pq->_base._count - 1) * pq->_base._itemSize; memcpy( k + pq->_base._items, item, pq->_base._itemSize); // ........................................................................... // call back so that the item can store the position of the entry into the // pq array back into the document (row, whatever). // ........................................................................... pq->updateStoragePQ(pq, k + pq->_base._items, pq->_base._count); // ........................................................................... // and fix the priority queue (re-sort) // ........................................................................... FixPQ(pq, pq->_base._count); /* for (k = 0; k < pq->_base._count; ++k) { tt = (xx*)( (k * pq->_base._itemSize) + pq->_base._items); printf("%s:%u:zzzzzzzzzzzzzzzzzzzzzz:%f:%u;%u\n",__FILE__,__LINE__, *((double*)((tt->fields)->_data.data)), tt->pqSlot, tt->numFields); } */ return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief rmeoves an item from the pq -- needs the position //////////////////////////////////////////////////////////////////////////////// static bool RemovePQueue(TRI_pqueue_t* pq, uint64_t position, bool destroyItem) { char* lastItem; char* thisItem; if (pq == NULL) { return false; } // ........................................................................... // Check that the position sent is valid // ........................................................................... if (position == 0 || position > pq->_base._count) { pq->_base._nrFalseRemoves++; return false; } // ........................................................................... // obtain the pointer positions // ........................................................................... thisItem = ((position - 1) * pq->_base._itemSize) + pq->_base._items; lastItem = ((pq->_base._count - 1) * pq->_base._itemSize) + pq->_base._items; // ........................................................................... // Set the position in the priority queue array back in the document // ........................................................................... pq->updateStoragePQ(pq, thisItem, 0); if (destroyItem) { pq->clearStoragePQ(pq,thisItem); } // ........................................................................... // perhaps we are lucky and the item to be removed is at the end of queue? // ........................................................................... if (position == pq->_base._count) { memset(thisItem, 0, pq->_base._itemSize); pq->_base._count--; return true; } // ........................................................................... // No such luck - so work a little harder. // ........................................................................... memcpy(thisItem, lastItem, pq->_base._itemSize); memset(lastItem, 0, pq->_base._itemSize); pq->_base._count--; FixPQ(pq,position); return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief returns the top most item in the priority queue //////////////////////////////////////////////////////////////////////////////// static void* TopPQueue(TRI_pqueue_t* pq) { if ( (pq == NULL) || (pq->_base._count == 0) ) { return NULL; } return (pq->_base._items); } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- priority queue helper functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup PriorityQueue /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief checks if additional capacity is required and extends if so //////////////////////////////////////////////////////////////////////////////// static bool CheckPQSize(TRI_pqueue_t* pq) { char* newItems; if (pq == NULL) { return false; } if (pq->_base._capacity > (pq->_base._count + 1) ) { return true; } pq->_base._capacity = pq->_base._capacity * 2; newItems = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, pq->_base._capacity * pq->_base._itemSize, false); if (newItems == NULL) { return false; } memset(newItems, 0, pq->_base._itemSize * pq->_base._capacity); memcpy(newItems, pq->_base._items, (pq->_base._count * pq->_base._itemSize) ); TRI_Free(TRI_UNKNOWN_MEM_ZONE, pq->_base._items); pq->_base._items = newItems; return true; } //////////////////////////////////////////////////////////////////////////////// /// @brief rebalances the heap (priority queue storage) using partial order //////////////////////////////////////////////////////////////////////////////// static bool FixPQ(TRI_pqueue_t* pq, uint64_t position) { char* currentItem; // the element which may be out of place in the heap uint64_t currentPos; // the current item we are checking against. char* parentItem; char* itemAddress; char* leftChildItem; char* rightChildItem; currentPos = position; currentItem = (char*)(TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, pq->_base._itemSize, false)); if (currentItem == NULL) { // out of memory return false; } itemAddress = GetItemAddress(pq,currentPos); if (itemAddress == NULL) { TRI_Free(TRI_UNKNOWN_MEM_ZONE, currentItem); return false; } // ........................................................................... // Temporarily save our item just changed (via removal or addition) // ........................................................................... memcpy(currentItem,itemAddress,pq->_base._itemSize); // ........................................................................... // Remember that given position m, it's parent in the heap is given by m/2 // Remember that the item we have to fix can be in the wrong place either because // it is less than it's parent or because it is greater than its children. // ........................................................................... // ........................................................................... // item is moved up if the item is LESS than its parent. // ........................................................................... while ((currentPos / 2) >= 1) { parentItem = GetItemAddress(pq, currentPos / 2); if (!pq->isLessPQ(pq,currentItem,parentItem)) { break; } itemAddress = GetItemAddress(pq,currentPos); memcpy(itemAddress,parentItem,pq->_base._itemSize); pq->updateStoragePQ(pq, itemAddress, currentPos); currentPos = currentPos / 2; } // ........................................................................... // If the position has changed, then we moved the item up somewhere // ........................................................................... if (currentPos != position) { itemAddress = GetItemAddress(pq,currentPos); memcpy(itemAddress,currentItem,pq->_base._itemSize); pq->updateStoragePQ(pq, itemAddress, currentPos); TRI_Free(TRI_UNKNOWN_MEM_ZONE, currentItem); return true; } // ........................................................................... // ok perhaps item has to move down the heap since it is too heavy // ........................................................................... while (pq->_base._count >= (2 * currentPos)) { leftChildItem = GetItemAddress(pq, (currentPos * 2)); rightChildItem = GetItemAddress(pq, (currentPos * 2) + 1); // this is to keep the heap balanced if ( (pq->_base._count > (2 * currentPos)) && (pq->isLessPQ(pq, rightChildItem,leftChildItem)) && (pq->isLessPQ(pq, rightChildItem, currentItem)) ) { itemAddress = GetItemAddress(pq,currentPos); memcpy(itemAddress,rightChildItem,pq->_base._itemSize); pq->updateStoragePQ(pq, itemAddress, currentPos); currentPos = (2 * currentPos) + 1; } // move item down else if (pq->isLessPQ(pq, leftChildItem, currentItem)) { itemAddress = GetItemAddress(pq,currentPos); memcpy(itemAddress,leftChildItem,pq->_base._itemSize); pq->updateStoragePQ(pq, itemAddress, currentPos); currentPos = 2 * currentPos; } else { break; } } itemAddress = GetItemAddress(pq,currentPos); memcpy(itemAddress,currentItem,pq->_base._itemSize); pq->updateStoragePQ(pq, itemAddress, currentPos); TRI_Free(TRI_UNKNOWN_MEM_ZONE, currentItem); return true; } /* currently unused static bool CompareIsLess(TRI_pqueue_t* pq, void* leftItem, void* rightItem) { if (pq->_base._reverse) { return !pq->isLessPQ(pq, leftItem, rightItem); } return pq->isLessPQ(pq, leftItem, rightItem); } */ //////////////////////////////////////////////////////////////////////////////// /// @brief given a position returns the element stored in the priorty queue //////////////////////////////////////////////////////////////////////////////// static char* GetItemAddress(TRI_pqueue_t* pq, uint64_t position) { if (position < 1 || position > pq->_base._count) { return NULL; } return ((position - 1) * pq->_base._itemSize) + pq->_base._items; } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // Local Variables: // mode: outline-minor // outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)" // End: