1
0
Fork 0
arangodb/lib/BasicsC/skip-list.c

2512 lines
93 KiB
C

////////////////////////////////////////////////////////////////////////////////
/// @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 <BasicsC/logging.h>
#include <BasicsC/random.h>
#include <BasicsC/skip-list.h>
#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: