//////////////////////////////////////////////////////////////////////////////// /// @brief skiplist implementation /// /// @file /// /// DISCLAIMER /// /// Copyright 2004-2012 triagens GmbH, Cologne, Germany /// /// Licensed under the Apache License, Version 2.0 (the "License"); /// you may not use this file except in compliance with the License. /// You may obtain a copy of the License at /// /// http://www.apache.org/licenses/LICENSE-2.0 /// /// Unless required by applicable law or agreed to in writing, software /// distributed under the License is distributed on an "AS IS" BASIS, /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. /// See the License for the specific language governing permissions and /// limitations under the License. /// /// Copyright holder is triAGENS GmbH, Cologne, Germany /// /// @author Dr. O /// @author Copyright 2006-2012, triAGENS GmbH, Cologne, Germany //////////////////////////////////////////////////////////////////////////////// #include #include #include #define SKIP_LIST_ABSOLUTE_MAX_HEIGHT 100 // ----------------------------------------------------------------------------- // --SECTION-- SKIPLIST // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // --SECTION-- common private functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup skip-list /// @{ //////////////////////////////////////////////////////////////////////////////// // ............................................................................. // Forward declared static functions // ............................................................................. static int CreateNewNode (TRI_skip_list_base_t* skiplist, TRI_skip_list_node_t**); static int FreeSkipListNode (TRI_skip_list_base_t* skiplist, TRI_skip_list_node_t* node); static int GrowNodeHeight (TRI_skip_list_node_t* node, uint32_t newHeight); static int JoinNodes (TRI_skip_list_node_t* leftNode, TRI_skip_list_node_t* rightNode, uint32_t startLevel, uint32_t endLevel); static void* NextNodeBaseSkipList (TRI_skip_list_base_t* skiplist, void* currentNode); static void* PrevNodeBaseSkipList (TRI_skip_list_base_t* skiplist, void* currentNode); static int32_t RandLevel (TRI_skip_list_base_t* skiplist); //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- unique skiplist constructors and destructors // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup Skiplist_unique /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief initialises an skip list //////////////////////////////////////////////////////////////////////////////// int TRI_InitSkipList (TRI_skip_list_t* skiplist, uint32_t elementSize, int (*compareElementElement) (TRI_skip_list_t*, void*, void*, int), int (*compareKeyElement) (TRI_skip_list_t*, void*, void*, int), int (*actionElement) (TRI_skip_list_t*, TRI_skip_list_action_e, void*, const void*, void*), TRI_skip_list_prob_e probability, uint32_t maximumHeight) { int result; // .......................................................................... // Safety thirst // .......................................................................... if (skiplist == NULL) { return TRI_ERROR_INTERNAL; } // .......................................................................... // Assign the call back functions // .......................................................................... if (compareElementElement == NULL || compareKeyElement == NULL || actionElement == NULL) { return TRI_ERROR_INTERNAL; } skiplist->_compareElementElement = compareElementElement; skiplist->_compareKeyElement = compareKeyElement; skiplist->_actionElement = actionElement; // .......................................................................... // A unique skiplist was created // .......................................................................... skiplist->_base._unique = true; // .......................................................................... // Assign the maximum height of the skip list. This maximum height must be // no greater than the absolute max height defined as a compile time parameter // .......................................................................... skiplist->_base._maxHeight = maximumHeight; if (maximumHeight > SKIP_LIST_ABSOLUTE_MAX_HEIGHT) { LOG_ERROR("Invalid maximum height for skiplist"); return TRI_ERROR_INTERNAL; } // .......................................................................... // Assign the probability and determine the number of random numbers which // we will require -- do it once off here // .......................................................................... skiplist->_base._prob = probability; skiplist->_base._numRandom = 0; switch (skiplist->_base._prob) { case TRI_SKIP_LIST_PROB_HALF: { // determine the number of random numbers which we require. skiplist->_base._numRandom = (skiplist->_base._maxHeight / 32); if ((skiplist->_base._maxHeight % 32) != 0) { ++(skiplist->_base._numRandom); } break; } case TRI_SKIP_LIST_PROB_THIRD: { // determine the number of random numbers which we require. skiplist->_base._numRandom = (skiplist->_base._maxHeight / 16); if ((skiplist->_base._maxHeight % 16) != 0) { ++(skiplist->_base._numRandom); } break; } case TRI_SKIP_LIST_PROB_QUARTER: { // determine the number of random numbers which we require. skiplist->_base._numRandom = (skiplist->_base._maxHeight / 16); if ((skiplist->_base._maxHeight % 16) != 0) { ++(skiplist->_base._numRandom); } break; } default: { LOG_ERROR("Invalid skiplist probability used"); return TRI_ERROR_INTERNAL; } } // end of switch statement // .......................................................................... // Create storage for where to store the random numbers which we generated // do it here once off. // .......................................................................... skiplist->_base._random = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(uint32_t) * skiplist->_base._numRandom, false); if (skiplist->_base._random == NULL) { return TRI_ERROR_OUT_OF_MEMORY; } // .......................................................................... // Assign the element size // .......................................................................... skiplist->_base._elementSize = elementSize; // .......................................................................... // Initialise the vertical storage of the lists and the place where we // are going to store elements // .......................................................................... skiplist->_base._startNode._column = NULL; skiplist->_base._startNode._colLength = 0; skiplist->_base._startNode._extraData = NULL; skiplist->_base._startNode._element = NULL; skiplist->_base._endNode._column = NULL; skiplist->_base._endNode._colLength = 0; skiplist->_base._endNode._extraData = NULL; skiplist->_base._endNode._element = NULL; // .......................................................................... // Whenever a probability of 1/2, 1/3, 1/4 is used, on average there will be // each node will have a height of two. So initialise the start and end nodes // with this 'average' height // .......................................................................... result = GrowNodeHeight(&(skiplist->_base._startNode), 2); // may fail if (result == TRI_ERROR_NO_ERROR) { result = GrowNodeHeight(&(skiplist->_base._endNode), 2); // may fail if (result == TRI_ERROR_NO_ERROR) { // ...................................................................... // Join the empty lists together // [N]<----------------------------------->[N] // [N]<----------------------------------->[N] // .......................................................................... result = JoinNodes(&(skiplist->_base._startNode), &(skiplist->_base._endNode), 0, 1); // list 0 & 1 } } if (result != TRI_ERROR_NO_ERROR) { FreeSkipListNode(&(skiplist->_base), &(skiplist->_base._startNode)); FreeSkipListNode(&(skiplist->_base), &(skiplist->_base._endNode)); TRI_Free(TRI_UNKNOWN_MEM_ZONE, skiplist->_base._random); return result; } return TRI_ERROR_NO_ERROR; } //////////////////////////////////////////////////////////////////////////////// /// @brief destroys a skip list, but does not free the pointer //////////////////////////////////////////////////////////////////////////////// void TRI_DestroySkipList (TRI_skip_list_t* skiplist) { TRI_skip_list_node_t* nextNode; TRI_skip_list_node_t* oldNextNode; if (skiplist == NULL) { return; } nextNode = &(skiplist->_base._startNode); while (nextNode != NULL) { oldNextNode = nextNode->_column[0]._next; TRI_Free(TRI_UNKNOWN_MEM_ZONE, nextNode->_column); if (nextNode->_element != NULL) { skiplist->_actionElement(skiplist, TRI_SKIP_LIST_DESTROY_ELEMENT, nextNode->_element, NULL, NULL); TRI_Free(TRI_UNKNOWN_MEM_ZONE, nextNode->_element); } TRI_Free(TRI_UNKNOWN_MEM_ZONE, nextNode); nextNode = oldNextNode; } TRI_Free(TRI_UNKNOWN_MEM_ZONE, skiplist->_base._random); } //////////////////////////////////////////////////////////////////////////////// /// @brief destroys a skip list and frees the pointer //////////////////////////////////////////////////////////////////////////////// void TRI_FreeSkipList (TRI_skip_list_t* skiplist) { TRI_DestroySkipList(skiplist); TRI_Free(TRI_UNKNOWN_MEM_ZONE, skiplist); } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- unique skiplist public functions // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup Skiplist_unique /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief returns the end node associated with a skip list //////////////////////////////////////////////////////////////////////////////// void* TRI_EndNodeSkipList(TRI_skip_list_t* skiplist) { if (skiplist != NULL) { return &(skiplist->_base._endNode); } return NULL; } //////////////////////////////////////////////////////////////////////////////// /// @brief adds an key/element to the skip list //////////////////////////////////////////////////////////////////////////////// int TRI_InsertElementSkipList(TRI_skip_list_t* skiplist, void* element, bool overwrite) { // Use TRI_InsertKeySkipList instead of calling this method assert(false); return 0; } //////////////////////////////////////////////////////////////////////////////// /// @brief adds an element to the skip list //////////////////////////////////////////////////////////////////////////////// int TRI_InsertKeySkipList (TRI_skip_list_t* skiplist, void* key, void* element, bool overwrite) { int32_t newHeight; int32_t currentLevel; uint32_t oldColLength; TRI_skip_list_node_t* currentNode; TRI_skip_list_node_t* nextNode; TRI_skip_list_node_t* newNode; TRI_skip_list_node_t* tempLeftNode; TRI_skip_list_node_t* tempRightNode; int compareResult; int result ; // ........................................................................... // Just in case // ........................................................................... if (skiplist == NULL) { return TRI_ERROR_INTERNAL; } // ........................................................................... // Determine the number of levels in which to add the item. That is, determine // the height of the node so that it participates in that many lists. // ........................................................................... newHeight = RandLevel(&(skiplist->_base)); // ........................................................................... // Something wrong since the newHeight must be non-negative // ........................................................................... if (newHeight < 0) { return TRI_ERROR_INTERNAL; } // ........................................................................... // convert the level to a height // ........................................................................... newHeight += 1; // ........................................................................... // Grow lists if required by increasing the height of the start and end nodes // ........................................................................... oldColLength = skiplist->_base._startNode._colLength; if ((uint32_t)(newHeight) > oldColLength) { result = GrowNodeHeight(&(skiplist->_base._startNode), newHeight); if (result == TRI_ERROR_NO_ERROR) { result = GrowNodeHeight(&(skiplist->_base._endNode), newHeight); if (result == TRI_ERROR_NO_ERROR) { result = JoinNodes(&(skiplist->_base._startNode),&(skiplist->_base._endNode), oldColLength , newHeight - 1); } } if (result != TRI_ERROR_NO_ERROR) { return result; } } // ........................................................................... // Create the new node to be inserted. If there is some sort of failure, // then we delete the node memory. // ........................................................................... result = CreateNewNode(&(skiplist->_base), &newNode); if (result != TRI_ERROR_NO_ERROR) { // out of memory? return result; } // ........................................................................... // Copy the contents of element into the new node to be inserted. // If a duplicate has been found, then we destroy the allocated memory. // ........................................................................... newNode->_column = NULL; newNode->_colLength = 0; newNode->_extraData = NULL; newNode->_element = NULL; result = skiplist->_actionElement(skiplist, TRI_SKIP_LIST_CREATE_ELEMENT, newNode->_element, element, NULL); if (result != TRI_ERROR_NO_ERROR) { return result; } result = GrowNodeHeight(newNode, newHeight); if (result != TRI_ERROR_NO_ERROR) { FreeSkipListNode(&(skiplist->_base), newNode); return result; } // ........................................................................... // Determine the path where the new item is to be inserted. If the item // already exists either replace it or return false. Recall that this // skip list is used for unique key/value pairs. Use the skiplist-multi // non-unique key/value pairs. // ........................................................................... currentLevel = skiplist->_base._startNode._colLength - 1; // NOT current height BUT current level is required here currentNode = &(skiplist->_base._startNode); START: // ......................................................................... // Find the next node in the current level of the lists. // ......................................................................... nextNode = (TRI_skip_list_node_t*)(currentNode->_column[currentLevel]._next); // ......................................................................... // WE HAVE FOUR CASES TO CONSIDER // ......................................................................... // ......................................................................... // CASE ONE: // At this level we have the smallest (start) and largest (end) nodes ONLY. // CASE TWO: // We have arrived at the end of the nodes and we are not at the // start of the nodes either. // ......................................................................... if (nextNode == &(skiplist->_base._endNode)) { // ....................................................................... // Store the current node and level in the path // ....................................................................... if (currentLevel < newHeight) { newNode->_column[currentLevel]._prev = currentNode; } // ....................................................................... // if we are at the lowest level of the lists, insert the item to the // right of the current node // ....................................................................... if (currentLevel == 0) { goto END; } // ....................................................................... // We have not yet reached the lowest level continue down. // ....................................................................... --currentLevel; goto START; } // ......................................................................... // CASE THREE: // We are the smallest left most node and the NEXT node is NOT the end node. // Compare this element with the element in the right node to see what we do. // CASE FOUR: // We are somewhere in the middle of a list, away from the smallest and // largest nodes. // ......................................................................... else { // nextNode != &(skiplist->_endNode // ....................................................................... // Use the callback to determine if the element is less or greater than // the next node element. // ....................................................................... compareResult = skiplist->_compareKeyElement(skiplist,key, nextNode->_element, 0); // ....................................................................... // The element matches the next element. Overwrite if possible and return. // We do not allow elements with duplicate 'keys'. // ....................................................................... if (compareResult == 0) { FreeSkipListNode(&(skiplist->_base), newNode); if (overwrite) { result = skiplist->_actionElement(skiplist, TRI_SKIP_LIST_REPLACE_ELEMENT, nextNode->_element, element, NULL); return result; } return TRI_set_errno(TRI_ERROR_ARANGO_UNIQUE_CONSTRAINT_VIOLATED); } // ....................................................................... // The element is greater than the next node element. Keep going on this // level. // ....................................................................... if (compareResult > 0) { currentNode = nextNode; goto START; } // ....................................................................... // The element is less than the next node. Can we drop down the list? // Store the current node and level in the path. // ....................................................................... if (currentLevel < newHeight) { newNode->_column[currentLevel]._prev = currentNode; } // ....................................................................... // We have reached the lowest level of the lists. Time to insert item. // ....................................................................... if (currentLevel == 0) { goto END; } // ....................................................................... // Drop down the list // ....................................................................... --currentLevel; goto START; } END: // .......................................................................... // Ok finished with the loop and we should have a path with AT MOST // SKIPLIST_ABSOLUTE_MAX_HEIGHT number of elements. // .......................................................................... { int j; for (j = 0; j < newHeight; ++j) { tempLeftNode = newNode->_column[j]._prev; tempRightNode = tempLeftNode->_column[j]._next; JoinNodes(tempLeftNode, newNode, j, j); JoinNodes(newNode, tempRightNode, j, j); } } return TRI_ERROR_NO_ERROR; } //////////////////////////////////////////////////////////////////////////////// /// @brief returns greatest node less than a given key //////////////////////////////////////////////////////////////////////////////// void* TRI_LeftLookupByKeySkipList (TRI_skip_list_t* skiplist, void* key) { int32_t currentLevel; TRI_skip_list_node_t* currentNode; TRI_skip_list_node_t* nextNode; // ........................................................................... // Just in case // ........................................................................... if (skiplist == NULL) { return NULL; } // ........................................................................... // Determine the starting level and the starting node // ........................................................................... currentLevel = skiplist->_base._startNode._colLength - 1; currentNode = &(skiplist->_base._startNode); START: // ......................................................................... // Find the next node in the current level of the lists. // ......................................................................... nextNode = (TRI_skip_list_node_t*)(currentNode->_column[currentLevel]._next); // ......................................................................... // WE HAVE FOUR CASES TO CONSIDER // ......................................................................... // ......................................................................... // CASE ONE: // At this level we have the smallest (start) and largest (end) nodes ONLY. // CASE TWO: // We have arrived at the end of the nodes and we are not at the // start of the nodes either. // ......................................................................... if (nextNode == &(skiplist->_base._endNode)) { // ....................................................................... // We are at the lowest level of the lists, and we haven't found the item // yet. // ....................................................................... if (currentLevel == 0) { return currentNode; } // ....................................................................... // We have not yet reached the lowest level continue down. // ....................................................................... --currentLevel; goto START; } // ......................................................................... // CASE THREE: // We are the smallest left most node and the NEXT node is NOT the end node. // Compare this element with the element in the right node to see what we do. // CASE FOUR: // We are somewhere in the middle of a list, away from the smallest and // largest nodes. // ......................................................................... else { // nextNode != &(skiplist->_endNode int compareResult; // ....................................................................... // Use the callback to determine if the element is less or greater than // the next node element. // ....................................................................... compareResult = skiplist->_compareKeyElement(skiplist, key, nextNode->_element, -1); // ....................................................................... // -1 is returned if the number of fields (attributes) in the key is LESS // than the number of fields in the index definition. This has the effect // of being slightly less efficient since we have to proceed to the level // 0 list in the set of skip lists. // ....................................................................... // ....................................................................... // We have found the item! // ....................................................................... if (compareResult == 0) { assert(false); } // ....................................................................... // The element is greater than the next node element. Keep going on this // level. // ....................................................................... if (compareResult > 0) { currentNode = nextNode; goto START; } // ....................................................................... // We have reached the lowest level of the lists -- no such item. // ....................................................................... if (currentLevel == 0) { return currentNode; } // ....................................................................... // Drop down the list // ....................................................................... --currentLevel; goto START; } // END: assert(false); // there is no way we can be here return NULL; } //////////////////////////////////////////////////////////////////////////////// /// @brief locate a node using an element //////////////////////////////////////////////////////////////////////////////// void* TRI_LookupByElementSkipList (TRI_skip_list_t* skiplist, void* element) { assert(false); // there is no way we can be here return NULL; } //////////////////////////////////////////////////////////////////////////////// /// @brief returns node which matches a key //////////////////////////////////////////////////////////////////////////////// void* TRI_LookupByKeySkipList (TRI_skip_list_t* skiplist, void* key) { int32_t currentLevel; TRI_skip_list_node_t* currentNode; TRI_skip_list_node_t* nextNode; // ........................................................................... // Just in case // ........................................................................... if (skiplist == NULL) { return NULL; } // ........................................................................... // Determine the starting level and the starting node // ........................................................................... currentLevel = skiplist->_base._startNode._colLength - 1; currentNode = &(skiplist->_base._startNode); START: // ......................................................................... // Find the next node in the current level of the lists. // ......................................................................... nextNode = (TRI_skip_list_node_t*)(currentNode->_column[currentLevel]._next); // ......................................................................... // WE HAVE FOUR CASES TO CONSIDER // ......................................................................... // ......................................................................... // CASE ONE: // At this level we have the smallest (start) and largest (end) nodes ONLY. // CASE TWO: // We have arrived at the end of the nodes and we are not at the // start of the nodes either. // ......................................................................... if (nextNode == &(skiplist->_base._endNode)) { // ....................................................................... // We are at the lowest level of the lists, and we haven't found the item // yet. Eventually we would like to return iterators. // ....................................................................... if (currentLevel == 0) { return NULL; } // ....................................................................... // We have not yet reached the lowest level continue down. // ....................................................................... --currentLevel; goto START; } // ......................................................................... // CASE THREE: // We are the smallest left most node and the NEXT node is NOT the end node. // Compare this element with the element in the right node to see what we do. // CASE FOUR: // We are somewhere in the middle of a list, away from the smallest and // largest nodes. // ......................................................................... else { // nextNode != &(skiplist->_endNode int compareResult; // ....................................................................... // Use the callback to determine if the element is less or greater than // the next node element. // ....................................................................... compareResult = skiplist->_compareKeyElement(skiplist, key, nextNode->_element, 0); // ....................................................................... // We have found the item! // ....................................................................... if (compareResult == 0) { return nextNode; } // ....................................................................... // The element is greater than the next node element. Keep going on this // level. // ....................................................................... if (compareResult > 0) { currentNode = nextNode; goto START; } // ....................................................................... // We have reached the lowest level of the lists -- no such item. // ....................................................................... if (currentLevel == 0) { return NULL; } // ....................................................................... // Drop down the list // ....................................................................... --currentLevel; goto START; } // END: assert(false); // there is no way we can be here return NULL; } //////////////////////////////////////////////////////////////////////////////// /// @brief given a node returns the next node (if possible) in the skiplist //////////////////////////////////////////////////////////////////////////////// void* TRI_NextNodeSkipList(TRI_skip_list_t* skiplist, void* currentNode) { if (skiplist != NULL) { return NextNodeBaseSkipList( (TRI_skip_list_base_t*)(skiplist), currentNode); } return NULL; } //////////////////////////////////////////////////////////////////////////////// /// @brief given a node returns the previous node (if possible) in the skiplist //////////////////////////////////////////////////////////////////////////////// void* TRI_PrevNodeSkipList(TRI_skip_list_t* skiplist, void* currentNode) { if (skiplist != NULL) { return PrevNodeBaseSkipList( (TRI_skip_list_base_t*)(skiplist), currentNode); } return NULL; } //////////////////////////////////////////////////////////////////////////////// /// @brief removes an element from the skip list //////////////////////////////////////////////////////////////////////////////// int TRI_RemoveElementSkipList (TRI_skip_list_t* skiplist, void* element, void* old) { int32_t currentLevel; TRI_skip_list_node_t* currentNode; TRI_skip_list_node_t* nextNode; TRI_skip_list_node_t* tempLeftNode; TRI_skip_list_node_t* tempRightNode; int compareResult; unsigned int j; // ........................................................................... // Just in case // ........................................................................... if (skiplist == NULL) { return TRI_ERROR_INTERNAL; } // ........................................................................... // Start at the top most list and left most position of that list. // ........................................................................... currentLevel = skiplist->_base._startNode._colLength - 1; // we want 'level' not 'height' currentNode = &(skiplist->_base._startNode); START: // ......................................................................... // Find the next node in the current level of the lists. // ......................................................................... nextNode = (TRI_skip_list_node_t*)(currentNode->_column[currentLevel]._next); // ......................................................................... // WE HAVE FOUR CASES TO CONSIDER // ......................................................................... // ......................................................................... // CASE ONE: // At this level we have the smallest (start) and largest (end) nodes ONLY. // CASE TWO: // We have arrived at the end of the nodes and we are not at the // start of the nodes either. // ......................................................................... if (nextNode == &(skiplist->_base._endNode)) { // ....................................................................... // We are at the lowest level of the lists, and we haven't found the item // yet. Nothing to remove so return. // ....................................................................... if (currentLevel == 0) { return TRI_WARNING_ARANGO_INDEX_SKIPLIST_REMOVE_ITEM_MISSING; } // ....................................................................... // We have not yet reached the lowest level continue down. // ....................................................................... --currentLevel; goto START; } // ......................................................................... // CASE THREE: // We are the smallest left most node and the NEXT node is NOT the end node. // Compare this element with the element in the right node to see what we do. // CASE FOUR: // We are somewhere in the middle of a list, away from the smallest and // largest nodes. // ......................................................................... else { // nextNode != &(skiplist->_endNode // ....................................................................... // Use the callback to determine if the element is less or greater than // the next node element. // ....................................................................... compareResult = skiplist->_compareElementElement(skiplist, element, nextNode->_element, -1); // ....................................................................... // We have found the item! // ....................................................................... if (compareResult == 0) { currentNode = nextNode; goto END; } // ....................................................................... // The element is greater than the next node element. Keep going on this // level. // ....................................................................... if (compareResult > 0) { currentNode = nextNode; goto START; } // ....................................................................... // We have reached the lowest level of the lists -- no such item. // ....................................................................... if (currentLevel == 0) { return TRI_WARNING_ARANGO_INDEX_SKIPLIST_REMOVE_ITEM_MISSING; } // ....................................................................... // Drop down the list // ....................................................................... --currentLevel; goto START; } END: // .......................................................................... // If requested copy the contents of the element we have located into the // storage sent. // .......................................................................... if (old != NULL) { skiplist->_actionElement(skiplist, TRI_SKIP_LIST_REPLACE_ELEMENT, old, currentNode->_element, NULL); } // .......................................................................... // Attempt to remove the node which will then remove element as well // .......................................................................... for (j = 0; j < currentNode->_colLength; ++j) { tempLeftNode = currentNode->_column[j]._prev; tempRightNode = currentNode->_column[j]._next; JoinNodes(tempLeftNode, tempRightNode, j, j); } FreeSkipListNode(&(skiplist->_base), currentNode); return TRI_ERROR_NO_ERROR; } //////////////////////////////////////////////////////////////////////////////// /// @brief removes an key/element to the skip list //////////////////////////////////////////////////////////////////////////////// int TRI_RemoveKeySkipList (TRI_skip_list_t* skiplist, void* key, void* old) { // Use the TRI_RemoveElementSkipList method instead. assert(false); return 0; } //////////////////////////////////////////////////////////////////////////////// /// @brief returns smallest node greater than a given key //////////////////////////////////////////////////////////////////////////////// void* TRI_RightLookupByKeySkipList (TRI_skip_list_t* skiplist, void* key) { int32_t currentLevel; TRI_skip_list_node_t* currentNode; TRI_skip_list_node_t* prevNode; // ........................................................................... // Just in case // ........................................................................... if (skiplist == NULL) { return NULL; } // ........................................................................... // Determine the starting level and the starting node // ........................................................................... currentLevel = skiplist->_base._startNode._colLength - 1; currentNode = &(skiplist->_base._endNode); START: // ......................................................................... // Find the next node in the current level of the lists. // ......................................................................... prevNode = (TRI_skip_list_node_t*)(currentNode->_column[currentLevel]._prev); // ......................................................................... // WE HAVE FOUR CASES TO CONSIDER // ......................................................................... // ......................................................................... // CASE ONE: // At this level we have the smallest (start) and largest (end) nodes ONLY. // CASE TWO: // We have arrived at the end of the nodes and we are not at the // start of the nodes either. // ......................................................................... if (prevNode == &(skiplist->_base._startNode)) { // ....................................................................... // We are at the lowest level of the lists, and we haven't found the item // yet. Eventually we would like to return iterators. // ....................................................................... if (currentLevel == 0) { return currentNode; } // ....................................................................... // We have not yet reached the lowest level continue down. // ....................................................................... --currentLevel; goto START; } // ......................................................................... // CASE THREE: // We are the smallest left most node and the NEXT node is NOT the end node. // Compare this element with the element in the right node to see what we do. // CASE FOUR: // We are somewhere in the middle of a list, away from the smallest and // largest nodes. // ......................................................................... else { // nextNode != &(skiplist->_endNode // ....................................................................... // Use the callback to determine if the element is less or greater than // the next node element. // ....................................................................... int compareResult = skiplist->_compareKeyElement(skiplist, key, prevNode->_element, 1); // ....................................................................... // If the number of fields (attributes) in the key is LESS than the number // of fields in the element to be compared to, then EVEN if the keys which // which are common to both equate as EQUAL, we STILL return 1 rather than // 0! This ensures that the right interval end point is correctly positioned // -- slightly inefficient since the lowest level skip list 0 has to be reached // in this case. // ....................................................................... // ....................................................................... // We have found the item! // ....................................................................... if (compareResult == 0) { assert(false); } // ....................................................................... // The key is less than the prev node element. Keep going on this // level. // ....................................................................... if (compareResult < 0) { currentNode = prevNode; goto START; } // ....................................................................... // We have reached the lowest level of the lists -- no such item. // ....................................................................... if (currentLevel == 0) { return currentNode; } // ....................................................................... // Drop down the list // ....................................................................... --currentLevel; goto START; } // END: assert(false); // there is no way we can be here return NULL; } //////////////////////////////////////////////////////////////////////////////// /// @brief returns the start node associated with a skip list. //////////////////////////////////////////////////////////////////////////////// void* TRI_StartNodeSkipList(TRI_skip_list_t* skiplist) { if (skiplist != NULL) { return &(skiplist->_base._startNode); } return NULL; } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- non-unique skiplist constructors and destructors // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup Skiplist_non_unique /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief initialises a multi skip list which allows duplicate entries //////////////////////////////////////////////////////////////////////////////// int TRI_InitSkipListMulti (TRI_skip_list_multi_t* skiplist, uint32_t elementSize, int (*compareElementElement) (TRI_skip_list_multi_t*, void*, void*, int), int (*compareKeyElement) (TRI_skip_list_multi_t*, void*, void*, int), bool (*equalElementElement) (TRI_skip_list_multi_t*, void*, void*), int (*actionElement) (TRI_skip_list_multi_t*, TRI_skip_list_action_e, void*, const void*, void*), TRI_skip_list_prob_e probability, uint32_t maximumHeight) { int result; // .......................................................................... // Safety thirst // .......................................................................... if (skiplist == NULL) { return TRI_ERROR_INTERNAL; } // .......................................................................... // Assign the call back functions // .......................................................................... if (compareElementElement == NULL || compareKeyElement == NULL || equalElementElement == NULL || actionElement == NULL) { return TRI_ERROR_INTERNAL; } skiplist->_compareElementElement = compareElementElement; skiplist->_compareKeyElement = compareKeyElement; skiplist->_equalElementElement = equalElementElement; skiplist->_actionElement = actionElement; // .......................................................................... // A unique skiplist was created // .......................................................................... skiplist->_base._unique = false; // .......................................................................... // Assign the maximum height of the skip list. This maximum height must be // no greater than the absolute max height defined as a compile time parameter // .......................................................................... skiplist->_base._maxHeight = maximumHeight; if (maximumHeight > SKIP_LIST_ABSOLUTE_MAX_HEIGHT) { LOG_ERROR("Invalid maximum height for skiplist"); return TRI_ERROR_INTERNAL; } // .......................................................................... // Assign the probability and determine the number of random numbers which // we will require -- do it once off here // .......................................................................... skiplist->_base._prob = probability; skiplist->_base._numRandom = 0; switch (skiplist->_base._prob) { case TRI_SKIP_LIST_PROB_HALF: { // determine the number of random numbers which we require. skiplist->_base._numRandom = (skiplist->_base._maxHeight / 32); if ((skiplist->_base._maxHeight % 32) != 0) { ++(skiplist->_base._numRandom); } break; } case TRI_SKIP_LIST_PROB_THIRD: { // determine the number of random numbers which we require. skiplist->_base._numRandom = (skiplist->_base._maxHeight / 16); if ((skiplist->_base._maxHeight % 16) != 0) { ++(skiplist->_base._numRandom); } break; } case TRI_SKIP_LIST_PROB_QUARTER: { // determine the number of random numbers which we require. skiplist->_base._numRandom = (skiplist->_base._maxHeight / 16); if ((skiplist->_base._maxHeight % 16) != 0) { ++(skiplist->_base._numRandom); } break; } default: { LOG_ERROR("Invalid skiplist probability used"); return TRI_ERROR_INTERNAL; } } // end of switch statement // .......................................................................... // Create storage for where to store the random numbers which we generated // do it here once off. // .......................................................................... skiplist->_base._random = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(uint32_t) * skiplist->_base._numRandom, false); if (skiplist->_base._random == NULL) { return TRI_ERROR_OUT_OF_MEMORY; } // .......................................................................... // Assign the element size // .......................................................................... skiplist->_base._elementSize = elementSize; // .......................................................................... // Initialise the vertical storage of the lists and the place where we // are going to store elements // .......................................................................... skiplist->_base._startNode._column = NULL; skiplist->_base._startNode._colLength = 0; skiplist->_base._startNode._extraData = NULL; skiplist->_base._startNode._element = NULL; skiplist->_base._endNode._column = NULL; skiplist->_base._endNode._colLength = 0; skiplist->_base._endNode._extraData = NULL; skiplist->_base._endNode._element = NULL; // .......................................................................... // Whenever a probability of 1/2, 1/3, 1/4 is used, on average there will be // each node will have a height of two. So initialise the start and end nodes // with this 'average' height // .......................................................................... result = GrowNodeHeight(&(skiplist->_base._startNode), 2); // may fail if (result == TRI_ERROR_NO_ERROR) { result = GrowNodeHeight(&(skiplist->_base._endNode), 2); // may fail if (result == TRI_ERROR_NO_ERROR) { // ...................................................................... // Join the empty lists together // [N]<----------------------------------->[N] // [N]<----------------------------------->[N] // .......................................................................... result = JoinNodes(&(skiplist->_base._startNode), &(skiplist->_base._endNode), 0, 1); // list 0 & 1 } } if (result != TRI_ERROR_NO_ERROR) { FreeSkipListNode(&(skiplist->_base), &(skiplist->_base._startNode)); FreeSkipListNode(&(skiplist->_base), &(skiplist->_base._endNode)); TRI_Free(TRI_UNKNOWN_MEM_ZONE, skiplist->_base._random); return result; } return TRI_ERROR_NO_ERROR; } //////////////////////////////////////////////////////////////////////////////// /// @brief destroys a multi skip list, but does not free the pointer //////////////////////////////////////////////////////////////////////////////// void TRI_DestroySkipListMulti (TRI_skip_list_multi_t* skiplist) { TRI_skip_list_node_t* nextNode; TRI_skip_list_node_t* oldNextNode; if (skiplist == NULL) { return; } nextNode = &(skiplist->_base._startNode); while (nextNode != NULL) { oldNextNode = nextNode->_column[0]._next; TRI_Free(TRI_UNKNOWN_MEM_ZONE, nextNode->_column); if (nextNode->_element != NULL) { skiplist->_actionElement(skiplist, TRI_SKIP_LIST_DESTROY_ELEMENT, nextNode->_element, NULL, NULL); TRI_Free(TRI_UNKNOWN_MEM_ZONE, nextNode->_element); } TRI_Free(TRI_UNKNOWN_MEM_ZONE, nextNode); nextNode = oldNextNode; } TRI_Free(TRI_UNKNOWN_MEM_ZONE, skiplist->_base._random); } //////////////////////////////////////////////////////////////////////////////// /// @brief destroys a multi skip list and frees the pointer //////////////////////////////////////////////////////////////////////////////// void TRI_FreeSkipListMulti (TRI_skip_list_multi_t* skiplist) { TRI_DestroySkipListMulti(skiplist); TRI_Free(TRI_UNKNOWN_MEM_ZONE, skiplist); } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // ----------------------------------------------------------------------------- // --SECTION-- non-unique skiplist public methods // ----------------------------------------------------------------------------- //////////////////////////////////////////////////////////////////////////////// /// @addtogroup Skiplist_non_unique /// @{ //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /// @brief Returns the end node associated with a skip list. //////////////////////////////////////////////////////////////////////////////// void* TRI_EndNodeSkipListMulti(TRI_skip_list_multi_t* skiplist) { if (skiplist != NULL) { return &(skiplist->_base._endNode); } return NULL; } //////////////////////////////////////////////////////////////////////////////// /// @brief returns greatest node less than a given key //////////////////////////////////////////////////////////////////////////////// void* TRI_LeftLookupByKeySkipListMulti(TRI_skip_list_multi_t* skiplist, void* key) { int32_t currentLevel; TRI_skip_list_node_t* currentNode; TRI_skip_list_node_t* nextNode; // ........................................................................... // Just in case // ........................................................................... if (skiplist == NULL) { return NULL; } // ........................................................................... // Determine the starting level and the starting node // ........................................................................... currentLevel = skiplist->_base._startNode._colLength - 1; currentNode = &(skiplist->_base._startNode); START: // ......................................................................... // Find the next node in the current level of the lists. // ......................................................................... nextNode = (TRI_skip_list_node_t*)(currentNode->_column[currentLevel]._next); // ......................................................................... // WE HAVE FOUR CASES TO CONSIDER // ......................................................................... // ......................................................................... // CASE ONE: // At this level we have the smallest (start) and largest (end) nodes ONLY. // CASE TWO: // We have arrived at the end of the nodes and we are not at the // start of the nodes either. // ......................................................................... if (nextNode == &(skiplist->_base._endNode)) { // ....................................................................... // We are at the lowest level of the lists, and we haven't found the item // yet. Eventually we would like to return iterators. // ....................................................................... if (currentLevel == 0) { return currentNode; } // ....................................................................... // We have not yet reached the lowest level continue down. // ....................................................................... --currentLevel; goto START; } // ......................................................................... // CASE THREE: // We are the smallest left most node and the NEXT node is NOT the end node. // Compare this element with the element in the right node to see what we do. // CASE FOUR: // We are somewhere in the middle of a list, away from the smallest and // largest nodes. // ......................................................................... else { // nextNode != &(skiplist->_endNode // ....................................................................... // Use the callback to determine if the element is less or greater than // the next node element. // ....................................................................... int compareResult = skiplist->_compareKeyElement(skiplist, key, nextNode->_element, -1); // ....................................................................... // We have found the item! Not possible // ....................................................................... if (compareResult == 0) { //return &(nextNode->_element); //return currentNode; assert(false); return nextNode->_column[0]._prev; } // ....................................................................... // The element is greater than the next node element. Keep going on this // level. // ....................................................................... if (compareResult > 0) { currentNode = nextNode; goto START; } // ....................................................................... // We have reached the lowest level of the lists -- no such item. // ....................................................................... if (currentLevel == 0) { return currentNode; } // ....................................................................... // Drop down the list // ....................................................................... --currentLevel; goto START; } // END: assert(false); // there is no way we can be here return NULL; } //////////////////////////////////////////////////////////////////////////////// /// @brief locate a node using an element //////////////////////////////////////////////////////////////////////////////// void* TRI_LookupByElementSkipListMulti(TRI_skip_list_multi_t* skiplist, void* element) { assert(false); // there is no way you should be here return 0; } //////////////////////////////////////////////////////////////////////////////// /// @brief returns node which matches a key //////////////////////////////////////////////////////////////////////////////// void* TRI_LookupByKeySkipListMulti(TRI_skip_list_multi_t* skiplist, void* key) { assert(false); // there is no way you should be here return 0; } //////////////////////////////////////////////////////////////////////////////// /// @brief adds an element to a multi skip list using an element for searching //////////////////////////////////////////////////////////////////////////////// int TRI_InsertElementSkipListMulti(TRI_skip_list_multi_t* skiplist, void* element, bool overwrite) { int32_t newHeight; int32_t currentLevel; uint32_t oldColLength; TRI_skip_list_node_t* currentNode; TRI_skip_list_node_t* nextNode; TRI_skip_list_node_t* newNode; TRI_skip_list_node_t* tempLeftNode; TRI_skip_list_node_t* tempRightNode; int result; int compareResult; // ........................................................................... // Just in case // ........................................................................... if (skiplist == NULL) { return TRI_ERROR_INTERNAL; } // ........................................................................... // Determine the number of levels in which to add the item. That is, determine // the height of the node so that it participates in that many lists. // ........................................................................... newHeight = RandLevel(&(skiplist->_base)); // ........................................................................... // Something wrong since the newHeight must be non-negative // ........................................................................... if (newHeight < 0) { return TRI_ERROR_INTERNAL; } // ........................................................................... // convert the level to a height // ........................................................................... newHeight += 1; // ........................................................................... // Grow lists if required by increasing the height of the start and end nodes // ........................................................................... oldColLength = skiplist->_base._startNode._colLength; if ((uint32_t)(newHeight) > oldColLength) { result = GrowNodeHeight(&(skiplist->_base._startNode), newHeight); if (result == TRI_ERROR_NO_ERROR) { result = GrowNodeHeight(&(skiplist->_base._endNode), newHeight); if (result == TRI_ERROR_NO_ERROR) { result = JoinNodes(&(skiplist->_base._startNode), &(skiplist->_base._endNode), oldColLength , newHeight - 1); } } if (result != TRI_ERROR_NO_ERROR) { return result; } } // ........................................................................... // Create the new node to be inserted. If there is some sort of failure, // then we delete the node memory. // ........................................................................... result = CreateNewNode(&(skiplist->_base), &newNode); if (result != TRI_ERROR_NO_ERROR) { // out of memory? return result; } // ........................................................................... // Copy the contents of element into the new node to be inserted. // If a duplicate has been found, then we destroy the allocated memory. // ........................................................................... newNode->_column = NULL; newNode->_colLength = 0; newNode->_extraData = NULL; newNode->_element = NULL; result = skiplist->_actionElement(skiplist, TRI_SKIP_LIST_CREATE_ELEMENT, newNode->_element, element, NULL); if (result != TRI_ERROR_NO_ERROR) { return result; } result = GrowNodeHeight(newNode, newHeight); if (result != TRI_ERROR_NO_ERROR) { FreeSkipListNode(&(skiplist->_base), newNode); return result; } // ........................................................................... // Determine the path where the new item is to be inserted. If the item // already exists either replace it or return false. Recall that this // skip list is used for unique key/value pairs. Use the skiplist-multi // non-unique key/value pairs. // ........................................................................... currentLevel = skiplist->_base._startNode._colLength - 1; // NOT current height BUT current level is required here currentNode = &(skiplist->_base._startNode); START: // ......................................................................... // Find the next node in the current level of the lists. // ......................................................................... nextNode = (TRI_skip_list_node_t*)(currentNode->_column[currentLevel]._next); // ......................................................................... // WE HAVE FOUR CASES TO CONSIDER // ......................................................................... // ......................................................................... // CASE ONE: // At this level we have the smallest (start) and largest (end) nodes ONLY. // CASE TWO: // We have arrived at the end of the nodes and we are not at the // start of the nodes either. // ......................................................................... if (nextNode == &(skiplist->_base._endNode)) { // ....................................................................... // Store the current node and level in the path // ....................................................................... if (currentLevel < newHeight) { newNode->_column[currentLevel]._prev = currentNode; } // ....................................................................... // if we are at the lowest level of the lists, insert the item to the // right of the current node // ....................................................................... if (currentLevel == 0) { goto END; } // ....................................................................... // We have not yet reached the lowest level continue down. // ....................................................................... --currentLevel; goto START; } // ......................................................................... // CASE THREE: // We are the smallest left most node and the NEXT node is NOT the end node. // Compare this element with the element in the right node to see what we do. // CASE FOUR: // We are somewhere in the middle of a list, away from the smallest and // largest nodes. // ......................................................................... else { // nextNode != &(skiplist->_endNode // ....................................................................... // Use the callback to determine if the element is less or greater than // the next node element. // ....................................................................... compareResult = skiplist->_compareElementElement(skiplist, element, nextNode->_element, -1); // ....................................................................... // The element matches the next element. Overwrite if possible and return. // We do not allow non-unique elements (non-unique 'keys' ok). // ....................................................................... if (compareResult == 0) { FreeSkipListNode(&(skiplist->_base), newNode); if (overwrite) { result = skiplist->_actionElement(skiplist, TRI_SKIP_LIST_REPLACE_ELEMENT, nextNode->_element, element, NULL); return result; } return TRI_ERROR_ARANGO_INDEX_SKIPLIST_INSERT_ITEM_DUPLICATED; } // ....................................................................... // The element is greater than the next node element. Keep going on this // level. // ....................................................................... if (compareResult > 0) { currentNode = nextNode; goto START; } // ....................................................................... // The element is less than the next node. Can we drop down the list? // Store the current node and level in the path. // ....................................................................... if (currentLevel < newHeight) { newNode->_column[currentLevel]._prev = currentNode; } // ....................................................................... // We have reached the lowest level of the lists. Time to insert item. // ....................................................................... if (currentLevel == 0) { goto END; } // ....................................................................... // Drop down the list // ....................................................................... --currentLevel; goto START; } END: // .......................................................................... // Ok finished with the loop and we should have a path with AT MOST // SKIPLIST_ABSOLUTE_MAX_HEIGHT number of elements. // .......................................................................... { int j; for (j = 0; j < newHeight; ++j) { tempLeftNode = newNode->_column[j]._prev; tempRightNode = tempLeftNode->_column[j]._next; JoinNodes(tempLeftNode, newNode, j, j); JoinNodes(newNode, tempRightNode, j, j); } } return TRI_ERROR_NO_ERROR; } //////////////////////////////////////////////////////////////////////////////// /// @brief adds an key/element to a multi skip list //////////////////////////////////////////////////////////////////////////////// int TRI_InsertKeySkipListMulti(TRI_skip_list_multi_t* skiplist, void* key, void* element, bool overwrite) { // Use TRI_InsertelementSkipList instead of calling this method assert(false); return 0; } //////////////////////////////////////////////////////////////////////////////// /// @brief given a node returns the next node (if possible) in the skiplist //////////////////////////////////////////////////////////////////////////////// void* TRI_NextNodeSkipListMulti(TRI_skip_list_multi_t* skiplist, void* currentNode) { if (skiplist != NULL) { return NextNodeBaseSkipList( (TRI_skip_list_base_t*)(skiplist), currentNode); } return NULL; } //////////////////////////////////////////////////////////////////////////////// /// @brief given a node returns the previous node (if possible) in the skiplist //////////////////////////////////////////////////////////////////////////////// void* TRI_PrevNodeSkipListMulti(TRI_skip_list_multi_t* skiplist, void* currentNode) { if (skiplist != NULL) { return PrevNodeBaseSkipList( (TRI_skip_list_base_t*)(skiplist), currentNode); } return NULL; } //////////////////////////////////////////////////////////////////////////////// /// @brief removes a key/element from a multi skip list //////////////////////////////////////////////////////////////////////////////// int TRI_RemoveElementSkipListMulti (TRI_skip_list_multi_t* skiplist, void* element, void* old) { int32_t currentLevel; TRI_skip_list_node_t* currentNode; TRI_skip_list_node_t* nextNode; TRI_skip_list_node_t* tempLeftNode; TRI_skip_list_node_t* tempRightNode; int compareResult; // ........................................................................... // Just in case // ........................................................................... if (skiplist == NULL) { return TRI_ERROR_INTERNAL; } // ........................................................................... // Start at the top most list and left most position of that list. // ........................................................................... currentLevel = skiplist->_base._startNode._colLength - 1; // current level not height currentNode = &(skiplist->_base._startNode); START: // ......................................................................... // Find the next node in the current level of the lists. // ......................................................................... nextNode = (TRI_skip_list_node_t*)(currentNode->_column[currentLevel]._next); // ......................................................................... // WE HAVE FOUR CASES TO CONSIDER // ......................................................................... // ......................................................................... // CASE ONE: // At this level we have the smallest (start) and largest (end) nodes ONLY. // CASE TWO: // We have arrived at the end of the nodes and we are not at the // start of the nodes either. // ......................................................................... if (nextNode == &(skiplist->_base._endNode)) { // ....................................................................... // We are at the lowest level of the lists, and we haven't found the item // yet. Nothing to remove so return. // ....................................................................... if (currentLevel == 0) { return TRI_WARNING_ARANGO_INDEX_SKIPLIST_REMOVE_ITEM_MISSING; } // ....................................................................... // We have not yet reached the lowest level continue down. // ....................................................................... --currentLevel; goto START; } // ......................................................................... // CASE THREE: // We are the smallest left most node and the NEXT node is NOT the end node. // Compare this element with the element in the right node to see what we do. // CASE FOUR: // We are somewhere in the middle of a list, away from the smallest and // largest nodes. // ......................................................................... else { // nextNode != &(skiplist->_endNode // ....................................................................... // Use the callback to determine if the element is less or greater than // the next node element. // ....................................................................... compareResult = skiplist->_compareElementElement(skiplist, element, nextNode->_element, TRI_SKIP_LIST_COMPARE_SLIGHTLY_LESS); // ....................................................................... // We have found an item which matches the key // ....................................................................... if (compareResult == TRI_SKIP_LIST_COMPARE_STRICTLY_EQUAL) { currentNode = nextNode; goto END; } // ....................................................................... // The element is greater than the next node element. Keep going on this // level. // ....................................................................... if (compareResult > 0) { currentNode = nextNode; goto START; } // ....................................................................... // We have reached the lowest level of the lists -- no such item. // ....................................................................... if (currentLevel == 0) { // ..................................................................... // The element could not be located // ..................................................................... if (compareResult == TRI_SKIP_LIST_COMPARE_STRICTLY_LESS) { return TRI_WARNING_ARANGO_INDEX_SKIPLIST_REMOVE_ITEM_MISSING; } // ..................................................................... // The element could be located (by matching the key) and we are at the lowest level // ..................................................................... if (compareResult == TRI_SKIP_LIST_COMPARE_SLIGHTLY_LESS) { goto END; } // can not occur assert(false); } // ....................................................................... // Drop down the list // ....................................................................... --currentLevel; goto START; } END: // .......................................................................... // locate the correct element -- since we allow duplicates // .......................................................................... while (currentNode != NULL) { if (skiplist->_equalElementElement(skiplist, element, currentNode->_element)) { break; } currentNode = NextNodeBaseSkipList(&(skiplist->_base), currentNode); } // .......................................................................... // The actual element could not be located - an element with a matching key // may exist, but the same data stored within the element could not be located // .......................................................................... if (currentNode == NULL) { return TRI_WARNING_ARANGO_INDEX_SKIPLIST_REMOVE_ITEM_MISSING; } // .......................................................................... // Perhaps the user wants a copy before we destory the data? // .......................................................................... if (old != NULL) { skiplist->_actionElement(skiplist, TRI_SKIP_LIST_REPLACE_ELEMENT, old, currentNode->_element, NULL); } // .......................................................................... // remove element // .......................................................................... { unsigned int j; for (j = 0; j < currentNode->_colLength; ++j) { tempLeftNode = currentNode->_column[j]._prev; tempRightNode = currentNode->_column[j]._next; JoinNodes(tempLeftNode, tempRightNode, j, j); } } FreeSkipListNode(&(skiplist->_base), currentNode); return TRI_ERROR_NO_ERROR; } //////////////////////////////////////////////////////////////////////////////// /// @brief removes a key/element from a multi skip list //////////////////////////////////////////////////////////////////////////////// int TRI_RemoveKeySkipListMulti(TRI_skip_list_multi_t* skiplist, void* key, void* old) { // Use the TRI_RemoveElementSkipListMulti method instead. assert(false); return 0; } //////////////////////////////////////////////////////////////////////////////// /// @brief returns smallest node greater than a given key //////////////////////////////////////////////////////////////////////////////// void* TRI_RightLookupByKeySkipListMulti(TRI_skip_list_multi_t* skiplist, void* key) { int32_t currentLevel; TRI_skip_list_node_t* currentNode; TRI_skip_list_node_t* prevNode; // ........................................................................... // Just in case // ........................................................................... if (skiplist == NULL) { return NULL; } // ........................................................................... // Determine the starting level and the starting node // ........................................................................... currentLevel = skiplist->_base._startNode._colLength - 1; currentNode = &(skiplist->_base._endNode); START: // ......................................................................... // Find the next node in the current level of the lists. // ......................................................................... prevNode = (TRI_skip_list_node_t*)(currentNode->_column[currentLevel]._prev); // ......................................................................... // WE HAVE FOUR CASES TO CONSIDER // ......................................................................... // ......................................................................... // CASE ONE: // At this level we have the smallest (start) and largest (end) nodes ONLY. // CASE TWO: // We have arrived at the end of the nodes and we are not at the // start of the nodes either. // ......................................................................... if (prevNode == &(skiplist->_base._startNode)) { // ....................................................................... // We are at the lowest level of the lists, and we haven't found the item // yet. Eventually we would like to return iterators. // ....................................................................... if (currentLevel == 0) { return currentNode; } // ....................................................................... // We have not yet reached the lowest level continue down. // ....................................................................... --currentLevel; goto START; } // ......................................................................... // CASE THREE: // We are the smallest left most node and the NEXT node is NOT the end node. // Compare this element with the element in the right node to see what we do. // CASE FOUR: // We are somewhere in the middle of a list, away from the smallest and // largest nodes. // ......................................................................... else { // nextNode != &(skiplist->_endNode // ....................................................................... // Use the callback to determine if the element is less or greater than // the next node element. // ....................................................................... int compareResult = skiplist->_compareKeyElement(skiplist, key, prevNode->_element, 1); // ....................................................................... // We have found the item! Not possible since we are searching by key! // ....................................................................... if (compareResult == 0) { assert(false); } // ....................................................................... // The element is greater than the next node element. Keep going on this // level. // ....................................................................... if (compareResult < 0) { currentNode = prevNode; goto START; } // ....................................................................... // We have reached the lowest level of the lists -- no such item. // ....................................................................... if (currentLevel == 0) { return currentNode; } // ....................................................................... // Drop down the list // ....................................................................... --currentLevel; goto START; } // END: assert(false); // there is no way we can be here return NULL; } //////////////////////////////////////////////////////////////////////////////// /// @brief returns the start node associated with a multi skip list. //////////////////////////////////////////////////////////////////////////////// void* TRI_StartNodeSkipListMulti(TRI_skip_list_multi_t* skiplist) { return &(skiplist->_base._startNode); } // ............................................................................. // Implementation of forward declared functions // ............................................................................. //////////////////////////////////////////////////////////////////////////////// /// @brief frees a node, destroying it first //////////////////////////////////////////////////////////////////////////////// static int CreateNewNode(TRI_skip_list_base_t* skiplist, TRI_skip_list_node_t** newNode) { *newNode = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_skip_list_node_t), false); if (*newNode == NULL) { // out of memory? return TRI_ERROR_OUT_OF_MEMORY; } (*newNode)->_element = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, skiplist->_elementSize, false); if ((*newNode)->_element == NULL) { // out of memory? TRI_Free(TRI_UNKNOWN_MEM_ZONE, *newNode); return TRI_ERROR_OUT_OF_MEMORY; } return TRI_ERROR_NO_ERROR; } static int FreeSkipListNode (TRI_skip_list_base_t* skiplist, TRI_skip_list_node_t* node) { if (node == NULL || skiplist == NULL) { return TRI_ERROR_INTERNAL; } if (node->_column != NULL) { TRI_Free(TRI_UNKNOWN_MEM_ZONE, node->_column); } node->_colLength = 0; node->_column = NULL; if ( (node != &(skiplist->_startNode)) && (node != &(skiplist->_endNode)) ) { // ......................................................................... // execute the destroy element callback function which will do whatever // needs to be done with the element. // ......................................................................... if (node->_element != NULL) { if (skiplist->_unique) { ((TRI_skip_list_t*)(skiplist))->_actionElement((TRI_skip_list_t*)(skiplist), TRI_SKIP_LIST_DESTROY_ELEMENT, node->_element, NULL, NULL); } else { ((TRI_skip_list_multi_t*)(skiplist))->_actionElement((TRI_skip_list_multi_t*)(skiplist), TRI_SKIP_LIST_DESTROY_ELEMENT, node->_element, NULL, NULL); } node->_element = NULL; } // ......................................................................... // Only free the memory if the node is neither the Start nor the End node // ......................................................................... TRI_Free(TRI_UNKNOWN_MEM_ZONE, node); } return TRI_ERROR_NO_ERROR; } //////////////////////////////////////////////////////////////////////////////// /// @brief Grow the node at the height specified. //////////////////////////////////////////////////////////////////////////////// static int GrowNodeHeight (TRI_skip_list_node_t* node, uint32_t newHeight) { TRI_skip_list_nb_t* newColumn = NULL; uint32_t j; if (node->_colLength >= newHeight) { return TRI_ERROR_NO_ERROR; } // ........................................................................... // Create new node column (tower) // ........................................................................... newColumn = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_skip_list_nb_t) * newHeight, false); if (newColumn == NULL) { // out of memory? return TRI_ERROR_OUT_OF_MEMORY; } // ........................................................................... // Copy the contents of the column (tower) from the existing column into // the new column. Note that the old column may have a tower of height 0! // ........................................................................... if (node->_column != NULL) { // I assume that the height of the tower must be greater than zero. memcpy(newColumn, node->_column, node->_colLength * sizeof(TRI_skip_list_nb_t) ); } // ........................................................................... // Initialise the next/prev pointers of the linked lists to NULL // ........................................................................... for (j = node->_colLength; j < newHeight; ++j) { newColumn[j]._prev = NULL; newColumn[j]._next = NULL; } // ........................................................................... // Assign the new tower to the existing node and remove any memory which // has been allocated to the old tower // ........................................................................... node->_colLength = newHeight; if (node->_column != NULL) { TRI_Free(TRI_UNKNOWN_MEM_ZONE, node->_column); } node->_column = newColumn; return TRI_ERROR_NO_ERROR; } //////////////////////////////////////////////////////////////////////////////// /// @brief joins a left node and right node together //////////////////////////////////////////////////////////////////////////////// static int JoinNodes(TRI_skip_list_node_t* leftNode, TRI_skip_list_node_t* rightNode, uint32_t startLevel, uint32_t endLevel) { uint32_t j; if (startLevel > endLevel) { // something wrong assert(false); return TRI_ERROR_INTERNAL; } // change level to heigth endLevel += 1; if (leftNode->_colLength < endLevel) { assert(false); return TRI_ERROR_INTERNAL; } if (rightNode->_colLength < endLevel) { assert(false); return TRI_ERROR_INTERNAL; } for (j = startLevel; j < endLevel; ++j) { (leftNode->_column)[j]._next = rightNode; (rightNode->_column)[j]._prev = leftNode; } return TRI_ERROR_NO_ERROR; } //////////////////////////////////////////////////////////////////////////////// /// @brief given a node returns the next node (if possible) in the skiplist //////////////////////////////////////////////////////////////////////////////// static void* NextNodeBaseSkipList(TRI_skip_list_base_t* skiplist, void* currentNode) { TRI_skip_list_node_t* node; if (currentNode == NULL) { return &(skiplist->_startNode); } if (currentNode == &(skiplist->_endNode)) { return NULL; } node = (TRI_skip_list_node_t*)(currentNode); return(node->_column[0]._next); } //////////////////////////////////////////////////////////////////////////////// /// @brief given a node returns the next node (if possible) in the skiplist //////////////////////////////////////////////////////////////////////////////// static void* PrevNodeBaseSkipList(TRI_skip_list_base_t* skiplist, void* currentNode) { TRI_skip_list_node_t* node; if (currentNode == NULL) { return &(skiplist->_endNode); } if (currentNode == &(skiplist->_startNode)) { return NULL; } node = (TRI_skip_list_node_t*)(currentNode); return(node->_column[0]._prev); } //////////////////////////////////////////////////////////////////////////////// /// @brief determines at what 'height' the item is to be added //////////////////////////////////////////////////////////////////////////////// static int32_t RandLevel (TRI_skip_list_base_t* skiplist) { uint32_t level = 0; int counter = 0; uint32_t* ptr = skiplist->_random; int j; // ........................................................................... // Obtain the random numbers and store them in the pre allocated storage // ........................................................................... for (j = 0; j < skiplist->_numRandom; ++j) { *ptr = TRI_UInt32Random(); ++ptr; } ptr = skiplist->_random; // ........................................................................... // Use the bit list to determine the probability of the level. // For 1/2: if bit (0) we stop, otherwise increase level. // For 1/3: if bits (0,0) we stop, if bits (1,1) ignore and continue, otherwise increase level // For 1/4: if bits (0,0) we stop, otherwise increase level // ........................................................................... switch (skiplist->_prob) { case TRI_SKIP_LIST_PROB_HALF: { counter = 0; while (level < skiplist->_maxHeight) { if ((1 & (*ptr)) == 0) { break; } ++level; (*ptr) = (*ptr) >> 1; ++counter; if (counter == 32) { ++ptr; counter = 0; } } break; } case TRI_SKIP_LIST_PROB_THIRD: { while (level < skiplist->_maxHeight) { if ((3 & (*ptr)) == 0) { break; } else if ((3 & (*ptr)) == 3) { // do nothing do not increase level } else { ++level; } (*ptr) = (*ptr) >> 2; ++counter; if (counter == 16) { ++ptr; counter = 0; } } break; } case TRI_SKIP_LIST_PROB_QUARTER: { counter = 0; while (level < skiplist->_maxHeight) { if ((3 & (*ptr)) == 0) { break; } ++level; (*ptr) = (*ptr) >> 2; ++counter; if (counter == 16) { ++ptr; counter = 0; } } break; } default: { return -1; } } return level; } //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// // Local Variables: // mode: outline-minor // outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)" // End: