From 36733eb19f44b96ba2df3eeebaced626e6942885 Mon Sep 17 00:00:00 2001 From: Oreste Panaia Date: Fri, 15 Mar 2013 18:12:20 +0800 Subject: [PATCH] preparation for skiplists supporting transactions --- arangod/Makefile.files | 2 + arangod/SkipListsEx/compareEx.h | 1107 +++++++++++ arangod/SkipListsEx/skiplistEx.c | 2537 +++++++++++++++++++++++++ arangod/SkipListsEx/skiplistEx.h | 557 ++++++ arangod/SkipListsEx/skiplistExIndex.c | 1417 ++++++++++++++ arangod/SkipListsEx/skiplistExIndex.h | 208 ++ arangod/VocBase/transaction.h | 5 +- lib/Basics/Nonce.cpp | 4 +- lib/BasicsC/locks-posix.c | 54 + lib/BasicsC/locks-win32.c | 34 + lib/BasicsC/locks.h | 62 + lib/BasicsC/operating-system.h | 4 + m4/configure.basics | 3 + 13 files changed, 5991 insertions(+), 3 deletions(-) create mode 100644 arangod/SkipListsEx/compareEx.h create mode 100644 arangod/SkipListsEx/skiplistEx.c create mode 100644 arangod/SkipListsEx/skiplistEx.h create mode 100644 arangod/SkipListsEx/skiplistExIndex.c create mode 100644 arangod/SkipListsEx/skiplistExIndex.h diff --git a/arangod/Makefile.files b/arangod/Makefile.files index a0ec90ad46..2ce07c349f 100755 --- a/arangod/Makefile.files +++ b/arangod/Makefile.files @@ -69,6 +69,8 @@ bin_arangod_SOURCES = \ arangod/RestServer/arango.cpp \ arangod/SkipLists/skiplist.c \ arangod/SkipLists/skiplistIndex.c \ + arangod/SkipListsEx/skiplistEx.c \ + arangod/SkipListsEx/skiplistExIndex.c \ arangod/V8Server/ApplicationV8.cpp \ arangod/V8Server/v8-actions.cpp \ arangod/V8Server/v8-query.cpp \ diff --git a/arangod/SkipListsEx/compareEx.h b/arangod/SkipListsEx/compareEx.h new file mode 100644 index 0000000000..f8ccfc7365 --- /dev/null +++ b/arangod/SkipListsEx/compareEx.h @@ -0,0 +1,1107 @@ +//////////////////////////////////////////////////////////////////////////////// +/// @brief compare methods used for skiplist indexes +/// +/// @file +/// +/// DISCLAIMER +/// +/// Copyright by triAGENS GmbH - All rights reserved. +/// +/// The Programs (which include both the software and documentation) +/// contain proprietary information of triAGENS GmbH; they are +/// provided under a license agreement containing restrictions on use and +/// disclosure and are also protected by copyright, patent and other +/// intellectual and industrial property laws. Reverse engineering, +/// disassembly or decompilation of the Programs, except to the extent +/// required to obtain interoperability with other independently created +/// software or as specified by law, is prohibited. +/// +/// The Programs are not intended for use in any nuclear, aviation, mass +/// transit, medical, or other inherently dangerous applications. It shall +/// be the licensee's responsibility to take all appropriate fail-safe, +/// backup, redundancy, and other measures to ensure the safe use of such +/// applications if the Programs are used for such purposes, and triAGENS +/// GmbH disclaims liability for any damages caused by such use of +/// the Programs. +/// +/// This software is the confidential and proprietary information of +/// triAGENS GmbH. You shall not disclose such confidential and +/// proprietary information and shall use it only in accordance with the +/// terms of the license agreement you entered into with triAGENS GmbH. +/// +/// Copyright holder is triAGENS GmbH, Cologne, Germany +/// +/// @author Dr. O +/// @author Copyright 2011, triagens GmbH, Cologne, Germany +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TRIAGENS_DURHAM_VOC_BASE_SKIPLIST_EX_COMPARE_H +#define TRIAGENS_DURHAM_VOC_BASE_SKIPLIST_EX_COMPARE_H 1 + +#include "ShapedJson/json-shaper.h" +#include "ShapedJson/shaped-json.h" +#include "VocBase/primary-collection.h" +#include "BasicsC/utf8-helper.h" + +#define USE_STATIC_SKIPLIST_EX_COMPARE 1 + +#define SKIPLIST_EX_ELEMENT_TYPE(a,b) \ + struct a { \ + size_t numFields; \ + TRI_shaped_json_t* fields; \ + void* data; \ + void* collection; \ + } b + + +#ifdef __cplusplus +extern "C" { +#endif + + +static int IndexStaticCopyElementElement (TRI_skiplistEx_base_t* skiplist, void* left, void* right) { + typedef SKIPLIST_EX_ELEMENT_TYPE(LocalElement_s,LocalElement_t); + + LocalElement_t* leftElement = (LocalElement_t*)(left); + LocalElement_t* rightElement = (LocalElement_t*)(right); + + if (leftElement == NULL || rightElement == NULL) { + assert(0); + return TRI_ERROR_INTERNAL; + } + + leftElement->numFields = rightElement->numFields; + leftElement->data = rightElement->data; + leftElement->collection = rightElement->collection; + leftElement->fields = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_shaped_json_t) * leftElement->numFields, false); + + if (leftElement->fields == NULL) { + return TRI_ERROR_OUT_OF_MEMORY; + } + + memcpy(leftElement->fields, rightElement->fields, sizeof(TRI_shaped_json_t) * leftElement->numFields); + + return TRI_ERROR_NO_ERROR; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief destroys an element -- removing any allocated memory within the structure +//////////////////////////////////////////////////////////////////////////////// + +static void IndexStaticDestroyElement(TRI_skiplistEx_base_t* skiplist, void* element) { + typedef SKIPLIST_EX_ELEMENT_TYPE(LocalElement_s,LocalElement_t); + + // ........................................................................... + // Each 'field' in the hElement->fields is a TRI_shaped_json_t object, this + // structure has internal structure of its own -- which also has memory + // allocated -- HOWEVER we DO NOT deallocate this memory here since it + // is actually part of the document structure. This memory should be deallocated + // when the document has been destroyed. + // ........................................................................... + + LocalElement_t* hElement = (LocalElement_t*)(element); + if (element != NULL) { + if (hElement->fields != NULL) { + TRI_Free(TRI_UNKNOWN_MEM_ZONE, hElement->fields); + } + } +} + + + + + +// ............................................................................. +// left < right return -1 +// left > right return 1 +// left == right return 0 +// ............................................................................. + +typedef struct weighted_attribute_s { + TRI_shape_aid_t _aid; + int64_t _weight; + TRI_shaped_json_t _value; + //const TRI_shape_t* _shape; + //const char* _aname; + const TRI_shaper_t* _shaper; +} weighted_attribute_t; + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief Helper method to deal with freeing memory associated with +/// the weight comparisions +//////////////////////////////////////////////////////////////////////////////// + +static void freeShapeTypeJsonArrayHelper(weighted_attribute_t** leftWeightedList, + weighted_attribute_t** rightWeightedList) { + if (*leftWeightedList != NULL) { + TRI_Free(TRI_UNKNOWN_MEM_ZONE, *leftWeightedList); + *leftWeightedList = NULL; + } + if (*rightWeightedList != NULL) { + TRI_Free(TRI_UNKNOWN_MEM_ZONE, *rightWeightedList); + *rightWeightedList = NULL; + } +} + + +// return the number of entries +static int compareShapeTypeJsonArrayHelper(const TRI_shape_t* shape, const TRI_shaper_t* shaper, + const TRI_shaped_json_t* shapedJson, + weighted_attribute_t** attributeArray) { + char* charShape = (char*)(shape); + TRI_shape_size_t fixedEntries; // the number of entries in the JSON array whose value is of a fixed size + TRI_shape_size_t variableEntries; // the number of entries in the JSON array whose value is not of a known fixed size + TRI_shape_size_t j; + int jj; + const TRI_shape_aid_t* aids; + const TRI_shape_sid_t* sids; + const TRI_shape_size_t* offsets; + + // ............................................................................. + // Ensure we return an empty array - in case of funny business below + // ............................................................................. + + *attributeArray = NULL; + + + // ............................................................................. + // Determine the number of fixed sized values + // ............................................................................. + + charShape = charShape + sizeof(TRI_shape_t); + fixedEntries = *((TRI_shape_size_t*)(charShape)); + + + // ............................................................................. + // Determine the number of variable sized values + // ............................................................................. + + charShape = charShape + sizeof(TRI_shape_size_t); + variableEntries = *((TRI_shape_size_t*)(charShape)); + + + // ............................................................................. + // It may happen that the shaped_json_array is 'empty {}' + // ............................................................................. + + if ((fixedEntries + variableEntries) == 0) { + return 0; + } + + + // ............................................................................. + // Allocate memory to hold the attribute information required for comparison + // ............................................................................. + + *attributeArray = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, (sizeof(weighted_attribute_t) * (fixedEntries + variableEntries)), false); + if (*attributeArray == NULL) { + return -1; + } + + + // ............................................................................. + // Determine the list of shape identifiers + // ............................................................................. + + charShape = charShape + sizeof(TRI_shape_size_t); + sids = (const TRI_shape_sid_t*)(charShape); + + charShape = charShape + (sizeof(TRI_shape_sid_t) * (fixedEntries + variableEntries)); + aids = (const TRI_shape_aid_t*)(charShape); + + charShape = charShape + (sizeof(TRI_shape_aid_t) * (fixedEntries + variableEntries)); + offsets = (const TRI_shape_size_t*)(charShape); + + for (j = 0; j < fixedEntries; ++j) { + (*attributeArray)[j]._aid = aids[j]; + (*attributeArray)[j]._weight = shaper->lookupAttributeWeight((TRI_shaper_t*)(shaper),aids[j]); + //(*attributeArray)[j]._shape = shaper->lookupShapeId((TRI_shaper_t*)(shaper), sids[j]); + (*attributeArray)[j]._value._sid = sids[j]; + (*attributeArray)[j]._value._data.data = shapedJson->_data.data + offsets[j]; + (*attributeArray)[j]._value._data.length = offsets[j + 1] - offsets[j]; + (*attributeArray)[j]._shaper = shaper; + //(*attributeArray)[j]._aname = NULL; + } + + offsets = (const TRI_shape_size_t*)(shapedJson->_data.data); + for (j = 0; j < variableEntries; ++j) { + jj = j + fixedEntries; + (*attributeArray)[jj]._aid = aids[jj]; + (*attributeArray)[jj]._weight = shaper->lookupAttributeWeight((TRI_shaper_t*)(shaper),aids[jj]); + //(*attributeArray)[jj]._shape = shaper->lookupShapeId((TRI_shaper_t*)(shaper), sids[jj]); + (*attributeArray)[jj]._value._sid = sids[jj]; + (*attributeArray)[jj]._value._data.data = shapedJson->_data.data + offsets[j]; + (*attributeArray)[jj]._value._data.length = offsets[j + 1] - offsets[j]; + (*attributeArray)[jj]._shaper = shaper; + //(*attributeArray)[jj]._aname = NULL; + } + + return (fixedEntries + variableEntries); +} + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief Compares to weighted attributes +//////////////////////////////////////////////////////////////////////////////// + +static int attributeWeightCompareFunction(const void* leftItem, const void* rightItem) { + const weighted_attribute_t* l = (const weighted_attribute_t*)(leftItem); + const weighted_attribute_t* r = (const weighted_attribute_t*)(rightItem); + + if (l->_weight < r->_weight) { return -1; } + if (l->_weight > r->_weight) { return 1; } + return 0; +} + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief Helper method for recursion for CompareShapedJsonShapedJson +//////////////////////////////////////////////////////////////////////////////// + + +static int CompareShapeTypes (const TRI_shaped_json_t* left, const TRI_shaped_json_t* right, TRI_shaper_t* leftShaper, TRI_shaper_t* rightShaper) { + + int result; + int i; + size_t j; + TRI_shape_type_t leftType; + TRI_shape_type_t rightType; + const TRI_shape_t* leftShape; + const TRI_shape_t* rightShape; + size_t leftListLength; + size_t rightListLength; + size_t listLength; + TRI_shaped_json_t leftElement; + TRI_shaped_json_t rightElement; + char* leftString; + char* rightString; + weighted_attribute_t* leftWeightedList; + weighted_attribute_t* rightWeightedList; + int leftNumWeightedList; + int rightNumWeightedList; + int numWeightedList; + + leftShape = leftShaper->lookupShapeId(leftShaper, left->_sid); + rightShape = rightShaper->lookupShapeId(rightShaper, right->_sid); + leftType = leftShape->_type; + rightType = rightShape->_type; + + switch (leftType) { + + case TRI_SHAPE_ILLEGAL: { + switch (rightType) { + case TRI_SHAPE_ILLEGAL: { + return 0; + } + case TRI_SHAPE_NULL: + case TRI_SHAPE_BOOLEAN: + case TRI_SHAPE_NUMBER: + case TRI_SHAPE_SHORT_STRING: + case TRI_SHAPE_LONG_STRING: + case TRI_SHAPE_ARRAY: + case TRI_SHAPE_LIST: + case TRI_SHAPE_HOMOGENEOUS_LIST: + case TRI_SHAPE_HOMOGENEOUS_SIZED_LIST: { + return -1; + } + } // end of switch (rightType) + } // end of case TRI_SHAPE_ILLEGAL + + + case TRI_SHAPE_NULL: { + switch (rightType) { + case TRI_SHAPE_ILLEGAL: { + return 1; + } + case TRI_SHAPE_NULL: { + return 0; + } + case TRI_SHAPE_BOOLEAN: + case TRI_SHAPE_NUMBER: + case TRI_SHAPE_SHORT_STRING: + case TRI_SHAPE_LONG_STRING: + case TRI_SHAPE_ARRAY: + case TRI_SHAPE_LIST: + case TRI_SHAPE_HOMOGENEOUS_LIST: + case TRI_SHAPE_HOMOGENEOUS_SIZED_LIST: { + return -1; + } + } // end of switch (rightType) + } // end of case TRI_SHAPE_NULL + + + case TRI_SHAPE_BOOLEAN: { + switch (rightType) { + case TRI_SHAPE_ILLEGAL: + case TRI_SHAPE_NULL: { + return 1; + } + case TRI_SHAPE_BOOLEAN: { + // check which is false and which is true! + if ( *((TRI_shape_boolean_t*)(left->_data.data)) == *((TRI_shape_boolean_t*)(right->_data.data)) ) { + return 0; + } + if ( *((TRI_shape_boolean_t*)(left->_data.data)) < *((TRI_shape_boolean_t*)(right->_data.data)) ) { + return -1; + } + return 1; + } + case TRI_SHAPE_NUMBER: + case TRI_SHAPE_SHORT_STRING: + case TRI_SHAPE_LONG_STRING: + case TRI_SHAPE_ARRAY: + case TRI_SHAPE_LIST: + case TRI_SHAPE_HOMOGENEOUS_LIST: + case TRI_SHAPE_HOMOGENEOUS_SIZED_LIST: { + return -1; + } + } // end of switch (rightType) + } // end of case TRI_SHAPE_BOOLEAN + + + case TRI_SHAPE_NUMBER: { + switch (rightType) { + case TRI_SHAPE_ILLEGAL: + case TRI_SHAPE_NULL: + case TRI_SHAPE_BOOLEAN: { + return 1; + } + case TRI_SHAPE_NUMBER: { + // compare the numbers + if ( *((TRI_shape_number_t*)(left->_data.data)) == *((TRI_shape_number_t*)(right->_data.data)) ) { + return 0; + } + if ( *((TRI_shape_number_t*)(left->_data.data)) < *((TRI_shape_number_t*)(right->_data.data)) ) { + return -1; + } + return 1; + } + case TRI_SHAPE_SHORT_STRING: + case TRI_SHAPE_LONG_STRING: + case TRI_SHAPE_ARRAY: + case TRI_SHAPE_LIST: + case TRI_SHAPE_HOMOGENEOUS_LIST: + case TRI_SHAPE_HOMOGENEOUS_SIZED_LIST: { + return -1; + } + } // end of switch (rightType) + } // end of case TRI_SHAPE_NUMBER + + + + case TRI_SHAPE_SHORT_STRING: + case TRI_SHAPE_LONG_STRING: { + switch (rightType) { + case TRI_SHAPE_ILLEGAL: + case TRI_SHAPE_NULL: + case TRI_SHAPE_BOOLEAN: + case TRI_SHAPE_NUMBER: { + return 1; + } + case TRI_SHAPE_SHORT_STRING: + case TRI_SHAPE_LONG_STRING: { + // compare strings + // extract the strings + if (leftType == TRI_SHAPE_SHORT_STRING) { + leftString = (char*)(sizeof(TRI_shape_length_short_string_t) + left->_data.data); + } + else { + leftString = (char*)(sizeof(TRI_shape_length_long_string_t) + left->_data.data); + } + + if (rightType == TRI_SHAPE_SHORT_STRING) { + rightString = (char*)(sizeof(TRI_shape_length_short_string_t) + right->_data.data); + } + else { + rightString = (char*)(sizeof(TRI_shape_length_long_string_t) + right->_data.data); + } + +#ifdef TRI_HAVE_ICU + result = TRI_compare_utf8(leftString,rightString); +#else + result = strcmp(leftString,rightString); +#endif + return result; + } + case TRI_SHAPE_ARRAY: + case TRI_SHAPE_LIST: + case TRI_SHAPE_HOMOGENEOUS_LIST: + case TRI_SHAPE_HOMOGENEOUS_SIZED_LIST: { + return -1; + } + } // end of switch (rightType) + } // end of case TRI_SHAPE_LONG/SHORT_STRING + + + case TRI_SHAPE_HOMOGENEOUS_LIST: + case TRI_SHAPE_HOMOGENEOUS_SIZED_LIST: + case TRI_SHAPE_LIST: { + switch (rightType) { + case TRI_SHAPE_ILLEGAL: + case TRI_SHAPE_NULL: + case TRI_SHAPE_BOOLEAN: + case TRI_SHAPE_NUMBER: + case TRI_SHAPE_SHORT_STRING: + case TRI_SHAPE_LONG_STRING: { + return 1; + } + case TRI_SHAPE_HOMOGENEOUS_LIST: + case TRI_SHAPE_HOMOGENEOUS_SIZED_LIST: + case TRI_SHAPE_LIST: { + // unfortunately recursion: check the types of all the entries + leftListLength = *((TRI_shape_length_list_t*)(left->_data.data)); + rightListLength = *((TRI_shape_length_list_t*)(right->_data.data)); + + // determine the smallest list + if (leftListLength > rightListLength) { + listLength = rightListLength; + } + else { + listLength = leftListLength; + } + + for (j = 0; j < listLength; ++j) { + + if (leftType == TRI_SHAPE_HOMOGENEOUS_LIST) { + TRI_AtHomogeneousListShapedJson((const TRI_homogeneous_list_shape_t*)(leftShape), + left,j,&leftElement); + } + else if (leftType == TRI_SHAPE_HOMOGENEOUS_SIZED_LIST) { + TRI_AtHomogeneousSizedListShapedJson((const TRI_homogeneous_sized_list_shape_t*)(leftShape), + left,j,&leftElement); + } + else { + TRI_AtListShapedJson((const TRI_list_shape_t*)(leftShape),left,j,&leftElement); + } + + + if (rightType == TRI_SHAPE_HOMOGENEOUS_LIST) { + TRI_AtHomogeneousListShapedJson((const TRI_homogeneous_list_shape_t*)(rightShape), + right,j,&rightElement); + } + else if (rightType == TRI_SHAPE_HOMOGENEOUS_SIZED_LIST) { + TRI_AtHomogeneousSizedListShapedJson((const TRI_homogeneous_sized_list_shape_t*)(rightShape), + right,j,&rightElement); + } + else { + TRI_AtListShapedJson((const TRI_list_shape_t*)(rightShape),right,j,&rightElement); + } + + result = CompareShapeTypes (&leftElement, &rightElement, leftShaper, rightShaper); + if (result != 0) { + return result; + } + } + + // up to listLength everything matches + if (leftListLength < rightListLength) { + return -1; + } + else if (leftListLength > rightListLength) { + return 1; + } + return 0; + } + + + case TRI_SHAPE_ARRAY: + { + return -1; + } + } // end of switch (rightType) + } // end of case TRI_SHAPE_LIST ... + + + + case TRI_SHAPE_ARRAY: { + switch (rightType) { + case TRI_SHAPE_ILLEGAL: + case TRI_SHAPE_NULL: + case TRI_SHAPE_BOOLEAN: + case TRI_SHAPE_NUMBER: + case TRI_SHAPE_SHORT_STRING: + case TRI_SHAPE_LONG_STRING: + case TRI_SHAPE_HOMOGENEOUS_LIST: + case TRI_SHAPE_HOMOGENEOUS_SIZED_LIST: + case TRI_SHAPE_LIST: { + return 1; + } + + case TRI_SHAPE_ARRAY: { + assert(0); + // ............................................................................ + // We are comparing a left JSON array with another JSON array on the right + // The comparison works as follows: + // + // Suppose that leftShape has m key/value pairs and that the + // rightShape has n key/value pairs + // + // Extract the m key aids (attribute identifiers) from the leftShape + // Extract the n key aids (attribute identifiers) from the rightShape + // + // Sort the key aids for both the left and right shape + // according to the weight of the key (attribute) + // + // Let lw_j denote the weight of the jth key from the sorted leftShape key list + // and rw_j the corresponding rightShape. + // + // If lw_j < rw_j return -1 + // If lw_j > rw_j return 1 + // If lw_j == rw_j, then we extract the values and compare the values + // using recursion. + // + // If lv_j < rv_j return -1 + // If lv_j > rv_j return 1 + // If lv_j == rv_j, then repeat the process with j+1. + // ............................................................................ + + + // ............................................................................ + // generate the left and right lists. + // ............................................................................ + + leftNumWeightedList = compareShapeTypeJsonArrayHelper(leftShape, leftShaper, left, &leftWeightedList); + rightNumWeightedList = compareShapeTypeJsonArrayHelper(rightShape, rightShaper, right, &rightWeightedList); + + + // ............................................................................ + // If the left and right both resulted in errors, we return equality for want + // of something better. + // ............................................................................ + + if ( (leftNumWeightedList < 0) && (rightNumWeightedList < 0) ) { // probably out of memory error + freeShapeTypeJsonArrayHelper(&leftWeightedList, &rightWeightedList); + return 0; + } + + // ............................................................................ + // If the left had an error, we rank the left as the smallest item in the order + // ............................................................................ + + if (leftNumWeightedList < 0) { // probably out of memory error + freeShapeTypeJsonArrayHelper(&leftWeightedList, &rightWeightedList); + return -1; // attempt to compare as low as possible + } + + + // ............................................................................ + // If the right had an error, we rank the right as the largest item in the order + // ............................................................................ + + if (rightNumWeightedList < 0) { + freeShapeTypeJsonArrayHelper(&leftWeightedList, &rightWeightedList); + return 1; + } + + + // ............................................................................ + // Are we comparing two empty shaped_json_arrays? + // ............................................................................ + + if ( (leftNumWeightedList == 0) && (rightNumWeightedList == 0) ) { + freeShapeTypeJsonArrayHelper(&leftWeightedList, &rightWeightedList); + return 0; + } + + + // ............................................................................ + // If the left is empty, then it is smaller than the right, right? + // ............................................................................ + + if (leftNumWeightedList == 0) { + freeShapeTypeJsonArrayHelper(&leftWeightedList, &rightWeightedList); + return -1; + } + + + // ............................................................................ + // ...and the opposite of the above. + // ............................................................................ + + if (rightNumWeightedList == 0) { + freeShapeTypeJsonArrayHelper(&leftWeightedList, &rightWeightedList); + return 1; + } + + + + // .............................................................................. + // We now have to sort the left and right weighted list according to attribute weight + // .............................................................................. + + qsort(leftWeightedList, leftNumWeightedList, sizeof(weighted_attribute_t), attributeWeightCompareFunction); + qsort(rightWeightedList, rightNumWeightedList, sizeof(weighted_attribute_t), attributeWeightCompareFunction); + + + // .............................................................................. + // check the weight and if equal check the values. Notice that numWeightedList + // below MUST be greater or equal to 1. + // .............................................................................. + + numWeightedList = (leftNumWeightedList < rightNumWeightedList ? leftNumWeightedList: rightNumWeightedList); + + for (i = 0; i < numWeightedList; ++i) { + + if (leftWeightedList[i]._weight != rightWeightedList[i]._weight) { + result = (leftWeightedList[i]._weight < rightWeightedList[i]._weight ? -1: 1); + break; + } + + result = CompareShapeTypes (&(leftWeightedList[i]._value), &(rightWeightedList[i]._value), leftShaper, rightShaper); + if (result != 0) { + break; + } + + // the attributes are equal now check for the values + /* start oreste debug + const char* name = leftShaper->lookupAttributeId(leftShaper,leftWeightedList[i]._aid); + printf("%s:%u:w=%ld:%s\n",__FILE__,__LINE__,leftWeightedList[i]._weight,name); + const char* name = rightShaper->lookupAttributeId(rightShaper,rightWeightedList[i]._aid); + printf("%s:%u:w=%ld:%s\n",__FILE__,__LINE__,rightWeightedList[i]._weight,name); + end oreste debug */ + } + + + if (result == 0) { + // ............................................................................ + // The comparisions above indicate that the shaped_json_arrays are equal, + // however one more check to determine if the number of elements in the arrays + // are equal. + // ............................................................................ + if (leftNumWeightedList < rightNumWeightedList) { + result = -1; + } + else if (leftNumWeightedList > rightNumWeightedList) { + result = 1; + } + } + + + // .............................................................................. + // Deallocate any memory for the comparisions and return the result + // .............................................................................. + + freeShapeTypeJsonArrayHelper(&leftWeightedList, &rightWeightedList); + return result; + } + } // end of switch (rightType) + } // end of case TRI_SHAPE_ARRAY + + } // end of switch (leftType) + + assert(false); + + return 0; //shut the vc++ up +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief Compare a shapded json object recursively if necessary +//////////////////////////////////////////////////////////////////////////////// + +static int CompareShapedJsonShapedJson (const TRI_shaped_json_t* left, const TRI_shaped_json_t* right, TRI_shaper_t* leftShaper, TRI_shaper_t* rightShaper) { + + int result; + + // ............................................................................ + // the following order is currently defined for placing an order on documents + // undef < null < boolean < number < strings < lists < hash arrays + // note: undefined will be treated as NULL pointer not NULL JSON OBJECT + // within each type class we have the following order + // boolean: false < true + // number: natural order + // strings: lexicographical + // lists: lexicorgraphically and within each slot according to these rules. + // ............................................................................ + + + if (left == NULL && right == NULL) { + return 0; + } + + if (left == NULL && right != NULL) { + return -1; + } + + if (left != NULL && right == NULL) { + return 1; + } + + result = CompareShapeTypes (left, right, leftShaper, rightShaper); + + + // ............................................................................ + // In the above function CompareShaeTypes we use strcmp which may return + // an integer greater than 1 or less than -1. From this function we only + // need to know whether we have equality (0), less than (-1) or greater than (1) + // ............................................................................ + + if (result < 0) { + result = -1; + } + else if (result > 0) { + result = 1; + } + + + return result; + +} // end of function CompareShapedJsonShapedJson + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief compares two elements in a skip list +//////////////////////////////////////////////////////////////////////////////// + +static int IndexStaticCompareElementElement (struct TRI_skiplistEx_s* skiplist, void* leftElement, void* rightElement, int defaultEqual) { + typedef SKIPLIST_EX_ELEMENT_TYPE(LocalElement_s,LocalElement_t); + // ............................................................................. + // Compare two elements and determines: + // left < right : return -1 + // left == right : return 0 + // left > right : return 1 + // ............................................................................. + int compareResult; + LocalElement_t* hLeftElement = (LocalElement_t*)(leftElement); + LocalElement_t* hRightElement = (LocalElement_t*)(rightElement); + TRI_shaper_t* leftShaper; + TRI_shaper_t* rightShaper; + size_t j; + + // ............................................................................ + // the following order is currently defined for placing an order on documents + // undef < null < boolean < number < strings < lists < hash arrays + // note: undefined will be treated as NULL pointer not NULL JSON OBJECT + // within each type class we have the following order + // boolean: false < true + // number: natural order + // strings: lexicographical + // lists: lexicographically and within each slot according to these rules. + // ............................................................................ + + if (leftElement == NULL && rightElement == NULL) { + return 0; + } + + if (leftElement != NULL && rightElement == NULL) { + return 1; + } + + if (leftElement == NULL && rightElement != NULL) { + return -1; + } + + if (leftElement == rightElement) { + return 0; + } + + // ............................................................................ + // This call back function is used when we insert and remove unique skip + // list entries. + // ............................................................................ + + if (hLeftElement->numFields != hRightElement->numFields) { + assert(false); + } + + + // ............................................................................ + // The document could be the same -- so no further comparison is required. + // ............................................................................ + if (hLeftElement->data == hRightElement->data) { + return 0; + } + + + leftShaper = ((TRI_primary_collection_t*)(hLeftElement->collection))->_shaper; + rightShaper = ((TRI_primary_collection_t*)(hRightElement->collection))->_shaper; + + for (j = 0; j < hLeftElement->numFields; j++) { + compareResult = CompareShapedJsonShapedJson((j + hLeftElement->fields), (j + hRightElement->fields), leftShaper, rightShaper); + if (compareResult != 0) { + return compareResult; + } + } + + // ............................................................................ + // This is where the difference between CompareKeyElement (below) and + // CompareElementElement comes into play. Here if the 'keys' are the same, + // but the doc ptr is different (which it is since we are here), then + // we return what was requested to be returned: 0,-1 or 1. What is returned + // depends on the purpose of calling this callback. + // ............................................................................ + + return defaultEqual; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief compares a key and an element +//////////////////////////////////////////////////////////////////////////////// + +static int IndexStaticCompareKeyElement (struct TRI_skiplistEx_s* skiplist, void* leftElement, void* rightElement, int defaultEqual) { + typedef SKIPLIST_EX_ELEMENT_TYPE(LocalElement_s,LocalElement_t); + + // ............................................................................. + // Compare two elements and determines: + // left < right : return -1 + // left == right : return 0 + // left > right : return 1 + // ............................................................................. + int compareResult; + size_t numFields; + LocalElement_t* hLeftElement = (LocalElement_t*)(leftElement); + LocalElement_t* hRightElement = (LocalElement_t*)(rightElement); + TRI_shaper_t* leftShaper; + TRI_shaper_t* rightShaper; + size_t j; + + // ............................................................................ + // the following order is currently defined for placing an order on documents + // undef < null < boolean < number < strings < lists < hash arrays + // note: undefined will be treated as NULL pointer not NULL JSON OBJECT + // within each type class we have the following order + // boolean: false < true + // number: natural order + // strings: lexicographical + // lists: lexicorgraphically and within each slot according to these rules. + // associative array: ordered keys followed by value of key + // ............................................................................ + + if (leftElement == NULL && rightElement == NULL) { + return 0; + } + + if (leftElement == NULL && rightElement != NULL) { + return -1; + } + + if (leftElement != NULL && rightElement == NULL) { + return 1; + } + + if (leftElement == rightElement) { + return 0; + } + + // ............................................................................ + // The document could be the same -- so no further comparison is required. + // ............................................................................ + if (hLeftElement->data == hRightElement->data) { + return 0; + } + + // ............................................................................ + // This call back function is used when we query the index, as such + // the number of fields which we are using for the query may be less than + // the number of fields that the index is defined with. + // ............................................................................ + + + if (hLeftElement->numFields < hRightElement->numFields) { + numFields = hLeftElement->numFields; + } + else { + numFields = hRightElement->numFields; + } + + + leftShaper = ((TRI_primary_collection_t*)(hLeftElement->collection))->_shaper; + rightShaper = ((TRI_primary_collection_t*)(hRightElement->collection))->_shaper; + + for (j = 0; j < numFields; j++) { + compareResult = CompareShapedJsonShapedJson((j + hLeftElement->fields), + (j + hRightElement->fields), + leftShaper, + rightShaper); + if (compareResult != 0) { + return compareResult; + } + } + + // ............................................................................ + // The 'keys' match -- however, we may only have a partial match in reality + // if not all keys comprising index have been used. + // ............................................................................ + return defaultEqual; +} + + + +//-------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------- +// Non-unique skiplist +//-------------------------------------------------------------------------------- +//-------------------------------------------------------------------------------- + + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief used to determine the order of two elements +//////////////////////////////////////////////////////////////////////////////// + +static int IndexStaticMultiCompareElementElement (TRI_skiplistEx_multi_t* multiSkiplist, void* leftElement, void* rightElement, int defaultEqual) { + typedef SKIPLIST_EX_ELEMENT_TYPE(LocalElement_s,LocalElement_t); + + int compareResult; + LocalElement_t* hLeftElement = (LocalElement_t*)(leftElement); + LocalElement_t* hRightElement = (LocalElement_t*)(rightElement); + TRI_shaper_t* leftShaper; + TRI_shaper_t* rightShaper; + size_t j; + + + if (leftElement == NULL && rightElement == NULL) { + return TRI_SKIPLIST_EX_COMPARE_STRICTLY_EQUAL; + } + + if (leftElement != NULL && rightElement == NULL) { + return TRI_SKIPLIST_EX_COMPARE_STRICTLY_GREATER; + } + + if (leftElement == NULL && rightElement != NULL) { + return TRI_SKIPLIST_EX_COMPARE_STRICTLY_LESS; + } + + if (leftElement == rightElement) { + return TRI_SKIPLIST_EX_COMPARE_STRICTLY_EQUAL; + } + + if (hLeftElement->numFields != hRightElement->numFields) { + assert(false); + } + + if (hLeftElement->data == hRightElement->data) { + return TRI_SKIPLIST_EX_COMPARE_STRICTLY_EQUAL; + } + + + leftShaper = ((TRI_primary_collection_t*)(hLeftElement->collection))->_shaper; + rightShaper = ((TRI_primary_collection_t*)(hRightElement->collection))->_shaper; + + for (j = 0; j < hLeftElement->numFields; j++) { + compareResult = CompareShapedJsonShapedJson((j + hLeftElement->fields), (j + hRightElement->fields), leftShaper, rightShaper); + if (compareResult != 0) { + + // ...................................................................... + // The function CompareShaedJsonShapedJson can only return 0, -1, or 1 + // that is, TRI_SKIPLIST_COMPARE_STRICTLY_EQUAL (0) + // TRI_SKIPLIST_COMPARE_STRICTLY_LESS (-1) + // TRI_SKIPLIST_COMPARE_STRICTLY_GREATER (1) + // ...................................................................... + + return compareResult; + + } + } + + return defaultEqual; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief used to determine the order of two keys +//////////////////////////////////////////////////////////////////////////////// + +static int IndexStaticMultiCompareKeyElement (TRI_skiplistEx_multi_t* multiSkiplist, void* leftElement, void* rightElement, int defaultEqual) { + typedef SKIPLIST_EX_ELEMENT_TYPE(LocalElement_s,LocalElement_t); + + int compareResult; + size_t numFields; + LocalElement_t* hLeftElement = (LocalElement_t*)(leftElement); + LocalElement_t* hRightElement = (LocalElement_t*)(rightElement); + TRI_shaper_t* leftShaper; + TRI_shaper_t* rightShaper; + size_t j; + + if (leftElement == NULL && rightElement == NULL) { + return 0; + } + + if (leftElement != NULL && rightElement == NULL) { + return 1; + } + + if (leftElement == NULL && rightElement != NULL) { + return -1; + } + + + // ............................................................................ + // The document could be the same -- so no further comparison is required. + // ............................................................................ + + if (hLeftElement->data == hRightElement->data) { + return 0; + } + + + // ............................................................................ + // This call back function is used when we query the index, as such + // the number of fields which we are using for the query may be less than + // the number of fields that the index is defined with. + // ............................................................................ + + if (hLeftElement->numFields < hRightElement->numFields) { + numFields = hLeftElement->numFields; + } + else { + numFields = hRightElement->numFields; + } + + leftShaper = ((TRI_primary_collection_t*)(hLeftElement->collection))->_shaper; + rightShaper = ((TRI_primary_collection_t*)(hRightElement->collection))->_shaper; + + for (j = 0; j < numFields; j++) { + compareResult = CompareShapedJsonShapedJson((j + hLeftElement->fields), (j + hRightElement->fields), leftShaper, rightShaper); + if (compareResult != 0) { + return compareResult; + } + } + + return defaultEqual; +} + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief used to determine the order of two keys +//////////////////////////////////////////////////////////////////////////////// + +static bool IndexStaticMultiEqualElementElement (TRI_skiplistEx_multi_t* multiSkiplist, void* leftElement, void* rightElement) { + typedef SKIPLIST_EX_ELEMENT_TYPE(LocalElement_s,LocalElement_t); + + LocalElement_t* hLeftElement = (LocalElement_t*)(leftElement); + LocalElement_t* hRightElement = (LocalElement_t*)(rightElement); + + if (leftElement == rightElement) { + return true; + } + + return (hLeftElement->data == hRightElement->data); +} + + + + + + + + +#ifdef __cplusplus +} +#endif + +#endif + +// Local Variables: +// mode: outline-minor +// outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)" +// End: + diff --git a/arangod/SkipListsEx/skiplistEx.c b/arangod/SkipListsEx/skiplistEx.c new file mode 100644 index 0000000000..b912b23d2e --- /dev/null +++ b/arangod/SkipListsEx/skiplistEx.c @@ -0,0 +1,2537 @@ +//////////////////////////////////////////////////////////////////////////////// +/// @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 "skiplistEx.h" +#include "compareEx.h" + +#define SKIPLIST_EX_ABSOLUTE_MAX_HEIGHT 100 + +// ----------------------------------------------------------------------------- +// --SECTION-- SKIPLIST_EX +// ----------------------------------------------------------------------------- + + +// ----------------------------------------------------------------------------- +// --SECTION-- STATIC FORWARD DECLARATIONS +// --SECTION-- common private functions +// ----------------------------------------------------------------------------- + +static void DestroyBaseSkipListEx (TRI_skiplistEx_base_t*); +static void DestroySkipListExNode (TRI_skiplistEx_base_t*, TRI_skiplistEx_node_t*); +static void FreeSkipListExNode (TRI_skiplistEx_base_t*, TRI_skiplistEx_node_t*); +static bool GrowNodeHeight (TRI_skiplistEx_node_t*, uint32_t); +static void* NextNodeBaseSkipListEx (TRI_skiplistEx_base_t*, void*, uint64_t); +static void* PrevNodeBaseSkipListEx (TRI_skiplistEx_base_t*, void*, uint64_t); +static int32_t RandLevel (TRI_skiplistEx_base_t*); + + +// .............................................................................. +// These operations have to be handled very carefully for transactions which are +// supposedly lock free. +// .............................................................................. + +// .............................................................................. +// DestroyNodeCAS: Attempts to physically (as opposed to logically) remove the +// node from the skip list. In the first iteration of a lock +// free skip list can we call this directly without Garbage +// Collection -- knowning that no other writers are operating +// at the same time? No other insertions/deletions are happening. +// +// InsertNodeCAS: inserts a node into the skip list - with transaction and +// lock-free support. +// +// RemoveNodeCAS: marks nodes as ghosts to be destroyed as some future time. +// .............................................................................. + +static void DestroyNodeCAS (TRI_skiplistEx_node_t*, uint64_t); +static void InsertNodeCAS (TRI_skiplistEx_node_t*, uint64_t); +static void JoinNodesCAS (TRI_skiplistEx_node_t*, TRI_skiplistEx_node_t*, uint32_t, uint32_t, uint64_t); +static void RemoveNodeCAS (TRI_skiplistEx_node_t*, uint64_t); + +// .............................................................................. +// DestroyNodeNoCAS: simply removes the node from its nearest neighbours and +// joins these nearest neighbours together without being +// directly concerned about transactions. NOT USED +// +// InsertNodeNoCAS: simply joins two nodes and is not concerned directly with +// transactions. It will however store the transaction number +// of the writer in the node. +// +// RemoveNodeNoCAS: simply marks the node as a ghost node which should be removed +// at some future time. It will store the transaction number +// of the writer in the node. NOT USED +// .............................................................................. + +static void DestroyNodeNoCAS (TRI_skiplistEx_node_t*, uint64_t); +static void InsertNodeNoCAS (TRI_skiplistEx_node_t*, uint64_t); +static void JoinNodesNoCAS (TRI_skiplistEx_node_t*, TRI_skiplistEx_node_t*, uint32_t, uint32_t, uint64_t); +static void RemoveNodesNoCAS (TRI_skiplistEx_node_t*, uint64_t); + + + +// ----------------------------------------------------------------------------- +// --SECTION-- unique skiplist constructors and destructors +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup Skiplist_unique +/// @{ +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @brief initialises an skip list +//////////////////////////////////////////////////////////////////////////////// + +int TRI_InitSkipListEx (TRI_skiplistEx_t* skiplist, size_t elementSize, + int (*compareElementElement) (TRI_skiplistEx_t*, void*, void*, int), + int (*compareKeyElement) (TRI_skiplistEx_t*, void*, void*, int), + TRI_skiplistEx_prob_e probability, + uint32_t maximumHeight, + uint64_t lastKnownTransID) { + + bool growResult; + if (skiplist == NULL) { + return TRI_ERROR_INTERNAL; + } + + // .......................................................................... + // Assign the STATIC comparision call back functions + // .......................................................................... + + skiplist->compareElementElement = IndexStaticCompareElementElement; // compareElementElement; + skiplist->compareKeyElement = IndexStaticCompareKeyElement; // compareKeyElement; + + // .......................................................................... + // 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 > SKIPLIST_EX_ABSOLUTE_MAX_HEIGHT) { + LOG_ERROR("Invalid maximum height for skiplist"); + assert(false); + } + + // .......................................................................... + // 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_SKIPLIST_EX_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_SKIPLIST_EX_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_SKIPLIST_EX_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: { + assert(false); + // todo: log error + break; + } + } // 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); + // TODO: memory allocation might fail + + // .......................................................................... + // 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._startNode._delTransID = UINT64_MAX; + skiplist->_base._startNode._insTransID = lastKnownTransID; + + + skiplist->_base._endNode._column = NULL; + skiplist->_base._endNode._colLength = 0; + skiplist->_base._endNode._extraData = NULL; + skiplist->_base._endNode._element = NULL; + skiplist->_base._endNode._delTransID = UINT64_MAX; + skiplist->_base._endNode._insTransID = lastKnownTransID; + + // .......................................................................... + // 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 + // .......................................................................... + growResult = GrowNodeHeight(&(skiplist->_base._startNode), 2); // may fail + growResult = growResult && GrowNodeHeight(&(skiplist->_base._endNode), 2); // may fail + if (! growResult) { + // TODO: undo growth by cutting down the node height + return TRI_ERROR_INTERNAL; + } + + // .......................................................................... + // Join the empty lists together + // no locking requirements for joining nodes since the skip list index is not known + // to anyone yet! + // [N]<----------------------------------->[N] + // [N]<----------------------------------->[N] + // .......................................................................... + JoinNodesNoCAS(&(skiplist->_base._startNode), &(skiplist->_base._endNode), 0, 1, lastKnownTransID); // list 0 & 1 + + return TRI_ERROR_NO_ERROR; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief destroys a skip list, but does not free the pointer +//////////////////////////////////////////////////////////////////////////////// + +void TRI_DestroySkipListEx(TRI_skiplistEx_t* skiplist) { + if (skiplist != NULL) { + DestroyBaseSkipListEx( (TRI_skiplistEx_base_t*)(skiplist) ); + } +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief destroys a skip list and frees the pointer +//////////////////////////////////////////////////////////////////////////////// + +void TRI_FreeSkipListEx(TRI_skiplistEx_t* skiplist) { + TRI_DestroySkipListEx(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 +//////////////////////////////////////////////////////////////////////////////// + +// ............................................................................. +// Observe that this is some sort of read transaction. The only possibilitiy +// we have is that the index must have been created AFTER this read transaction +// occurred (given that the skip list is valid of course). We do not check +// for this. (Note: the START (HEAD) and END (TAIL) nodes never change once +// the skip list is created.) +// ............................................................................. + +void* TRI_EndNodeSkipListEx(TRI_skiplistEx_t* skiplist) { + if (skiplist != NULL) { + return &(skiplist->_base._endNode); + } + return NULL; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief adds an key/element to the skip list +//////////////////////////////////////////////////////////////////////////////// + +int TRI_InsertElementSkipListEx(TRI_skiplistEx_t* skiplist, void* element, bool overwrite, uint64_t thisTransID) { + // Use TRI_InsertKeySkipList instead of calling this method + assert(false); + return 0; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief adds an element to the skip list +//////////////////////////////////////////////////////////////////////////////// + +int TRI_InsertKeySkipListEx (TRI_skiplistEx_t* skiplist, void* key, void* element, bool overwrite, uint64_t thisTransID) { + //This uses the compareKeyElement callback + int32_t newHeight; + int32_t currentLevel; + uint32_t oldColLength; + TRI_skiplistEx_node_t* currentNode; + TRI_skiplistEx_node_t* nextNode; + TRI_skiplistEx_node_t* newNode; + TRI_skiplistEx_node_t* tempLeftNode; + TRI_skiplistEx_node_t* tempRightNode; + int compareResult; + bool growResult; + int j; + + // ........................................................................... + // 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) { + growResult = GrowNodeHeight(&(skiplist->_base._startNode), newHeight); + growResult = growResult && GrowNodeHeight(&(skiplist->_base._endNode), newHeight); + if (!growResult) { + // todo: undo growth by cutting down the node height + return TRI_ERROR_OUT_OF_MEMORY; + } + JoinNodesCAS(&(skiplist->_base._startNode),&(skiplist->_base._endNode), oldColLength , newHeight - 1, thisTransID); + } + + + // ........................................................................... + // Create the new node to be inserted. If there is some sort of failure, + // then we delete the node memory. + // ........................................................................... + newNode = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_skiplistEx_node_t) + skiplist->_base._elementSize, false); + if (newNode == NULL) { // out of memory? + return TRI_ERROR_OUT_OF_MEMORY; + } + + + // ........................................................................... + // 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; + + j = IndexStaticCopyElementElement(&(skiplist->_base), &(newNode->_element), element); + if (j != TRI_ERROR_NO_ERROR) { + return j; + } + + growResult = GrowNodeHeight(newNode, newHeight); + if (!growResult) { + FreeSkipListExNode(&(skiplist->_base), newNode); + return TRI_ERROR_OUT_OF_MEMORY; + } + + + // ........................................................................... + // Assign the deletion transaction id and the insertion transaction id + // ........................................................................... + + newNode->_delTransID = UINT64_MAX; // since we are inserting this new node it can not be deleted + newNode->_insTransID = thisTransID; // this is what was given to us + + + // ........................................................................... + // 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_skiplistEx_node_t*)(currentNode->_column[currentLevel]._next); + + + // ......................................................................... + // Since this skiplist apparently supports transactions we have a few + // things to consider. ** NOTE ** we are here INSERTING a new node in a + // UNIQUE skiplist. + // + // We attempt to locate the 'position' of where the node should be inserted + // by imagining we have a KEY-> key + epsilon. Now with the ASSUMPTION that + // while this WRITER is writing (here) NO NEW writers and readers can be started + // we check the previous node + // ......................................................................... + + + // ......................................................................... + // 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 = IndexStaticCompareKeyElement(skiplist,key,&(nextNode->_element), 0); + + + // ....................................................................... + // The element matches the next element. + // However since we support transactions some things are different and we + // we have to traead carefully. + // ....................................................................... + + if (compareResult == 0) { + + // ..................................................................... + // It may happen that this node is NOT deleted and simply there - + // check the ins & del transaction numbers. + // ..................................................................... + + if (nextNode->_insTransID <= thisTransID) { + // ................................................................... + // node has been previously inserted + // ................................................................... + + if (nextNode->_delTransID > thisTransID) { + // ................................................................... + // Node has NOT been deleted (e.g. imagine it will be deleted some time in the future). + // Treat this as a duplicate key, overwrite if possible and return. + // We do not allow elements with duplicate 'keys'. + // ................................................................... + FreeSkipListExNode(&(skiplist->_base), newNode); + + if (overwrite) { + j = IndexStaticCopyElementElement(&(skiplist->_base), &(nextNode->_element), element); + return j; + } + return TRI_set_errno(TRI_ERROR_ARANGO_UNIQUE_CONSTRAINT_VIOLATED); + } + + // ................................................................... + // The only case left here is that the node has been deleted by either + // this transaction (which could happen in an UPDATE) or by some + // previous write transaction. Treat this case as if the element is + // greater than the next node element. Keep going on this level. + // ................................................................... + currentNode = nextNode; + goto START; + + } + + + if (nextNode->_insTransID > thisTransID) { + // ................................................................... + // Something terrible has happened since writers have been serialized, + // how is that an existing node has a higher transaction number than + // this transaction + // ................................................................... + printf("%s:%s:%d:Can not be here!\n",__FILE__,__FUNCTION__,__LINE__); + assert(false); // there is no way we can be here + } + + } + + // ....................................................................... + // 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_EX_ABSOLUTE_MAX_HEIGHT number of elements. + // .......................................................................... + + + // .......................................................................... + // this is the tricky part II since we have to attempt to do this as + // 'lock-free' as possible + // .......................................................................... + + // this should be called JoinTower! + for (j = 0; j < newHeight; ++j) { + tempLeftNode = newNode->_column[j]._prev; + tempRightNode = tempLeftNode->_column[j]._next; + JoinNodesCAS(tempLeftNode, newNode, j, j, thisTransID); + JoinNodesCAS(newNode, tempRightNode, j, j, thisTransID); + } + + return TRI_ERROR_NO_ERROR; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns greatest node less than a given key +//////////////////////////////////////////////////////////////////////////////// + +void* TRI_LeftLookupByKeySkipListEx(TRI_skiplistEx_t* skiplist, void* key, uint64_t thisTransID) { + int32_t currentLevel; + TRI_skiplistEx_node_t* currentNode; + TRI_skiplistEx_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_skiplistEx_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 = IndexStaticCompareKeyElement(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_LookupByElementSkipListEx (TRI_skiplistEx_t* skiplist, void* element, uint64_t thisTransID) { + assert(false); // there is no way we can be here + return NULL; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns node which matches a key +//////////////////////////////////////////////////////////////////////////////// + +void* TRI_LookupByKeySkipListEx (TRI_skiplistEx_t* skiplist, void* key, uint64_t thisTransID) { + int32_t currentLevel; + TRI_skiplistEx_node_t* currentNode; + TRI_skiplistEx_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_skiplistEx_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 = IndexStaticCompareKeyElement(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_NextNodeSkipListEx(TRI_skiplistEx_t* skiplist, void* currentNode, uint64_t thisTransID) { + if (skiplist != NULL) { + return NextNodeBaseSkipListEx( (TRI_skiplistEx_base_t*)(skiplist), currentNode, thisTransID); + } + return NULL; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief given a node returns the previous node (if possible) in the skiplist +//////////////////////////////////////////////////////////////////////////////// + +void* TRI_PrevNodeSkipListEx(TRI_skiplistEx_t* skiplist, void* currentNode, uint64_t thisTransID) { + if (skiplist != NULL) { + return PrevNodeBaseSkipListEx( (TRI_skiplistEx_base_t*)(skiplist), currentNode, thisTransID); + } + return NULL; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief removes an element from the skip list +//////////////////////////////////////////////////////////////////////////////// + +int TRI_RemoveElementSkipListEx (TRI_skiplistEx_t* skiplist, void* element, void* old, uint64_t thisTransID) { + // This uses the compareElementElement callback + int32_t currentLevel; + TRI_skiplistEx_node_t* currentNode; + TRI_skiplistEx_node_t* nextNode; + TRI_skiplistEx_node_t* tempLeftNode; + TRI_skiplistEx_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_skiplistEx_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 = IndexStaticCompareElementElement(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) { + IndexStaticCopyElementElement(&(skiplist->_base), old, &(currentNode->_element)); + } + + + // .......................................................................... + // 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; + JoinNodesCAS(tempLeftNode, tempRightNode, j, j, thisTransID); + } + + FreeSkipListExNode(&(skiplist->_base), currentNode); + return TRI_ERROR_NO_ERROR; + +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief removes an key/element to the skip list +//////////////////////////////////////////////////////////////////////////////// + +int TRI_RemoveKeySkipListEx(TRI_skiplistEx_t* skiplist, void* key, void* old, uint64_t thisTransID) { + // Use the TRI_RemoveElementSkipList method instead. + assert(false); + return 0; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns smallest node greater than a given key +//////////////////////////////////////////////////////////////////////////////// + +void* TRI_RightLookupByKeySkipListEx(TRI_skiplistEx_t* skiplist, void* key, uint64_t thisTransID) { + int32_t currentLevel; + TRI_skiplistEx_node_t* currentNode; + TRI_skiplistEx_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_skiplistEx_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 = IndexStaticCompareKeyElement(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 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 skip list. +//////////////////////////////////////////////////////////////////////////////// + + + +void* TRI_StartNodeSkipListEx(TRI_skiplistEx_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_InitSkipListExMulti (TRI_skiplistEx_multi_t* skiplist, + size_t elementSize, + int (*compareElementElement) (TRI_skiplistEx_multi_t*, void*, void*, int), + int (*compareKeyElement) (TRI_skiplistEx_multi_t*, void*, void*, int), + bool (*equalElementElement) (TRI_skiplistEx_multi_t*, void*, void*), + TRI_skiplistEx_prob_e probability, + uint32_t maximumHeight, + uint64_t lastKnownTransID) { + + bool growResult; + + if (skiplist == NULL) { + return TRI_ERROR_INTERNAL; + } + + // .......................................................................... + // Assign the comparision call back functions + // .......................................................................... + + skiplist->compareElementElement = IndexStaticMultiCompareElementElement; //compareElementElement; + skiplist->compareKeyElement = IndexStaticMultiCompareKeyElement; // compareKeyElement; + skiplist->equalElementElement = IndexStaticMultiEqualElementElement; //equalElementElement; + + // .......................................................................... + // 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 > SKIPLIST_EX_ABSOLUTE_MAX_HEIGHT) { + LOG_ERROR("Invalid maximum height for skiplist"); + assert(false); + } + + // .......................................................................... + // 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_SKIPLIST_EX_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_SKIPLIST_EX_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_SKIPLIST_EX_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: { + assert(false); + // TODO: log error + break; + } + } // 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); + /* TODO: memory allocation might fail */ + + // .......................................................................... + // 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 + // each node will have a height of two. So initialise the start and end nodes + // with this 'average' height + // .......................................................................... + growResult = GrowNodeHeight(&(skiplist->_base._startNode), 2); + growResult = growResult && GrowNodeHeight(&(skiplist->_base._endNode), 2); + if (!growResult) { + // todo: truncate the nodes and return + return TRI_ERROR_INTERNAL; + } + + // .......................................................................... + // Join the empty lists together + // [N]<----------------------------------->[N] + // [N]<----------------------------------->[N] + // .......................................................................... + JoinNodesNoCAS(&(skiplist->_base._startNode),&(skiplist->_base._endNode),0,1, lastKnownTransID); // list 0 & 1 + + return TRI_ERROR_NO_ERROR; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief destroys a multi skip list, but does not free the pointer +//////////////////////////////////////////////////////////////////////////////// + +void TRI_DestroySkipListExMulti (TRI_skiplistEx_multi_t* skiplist) { + if (skiplist == NULL) { + return; + } + DestroyBaseSkipListEx( (TRI_skiplistEx_base_t*)(skiplist) ); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief destroys a multi skip list and frees the pointer +//////////////////////////////////////////////////////////////////////////////// + +void TRI_FreeSkipListExMulti (TRI_skiplistEx_multi_t* skiplist) { + TRI_DestroySkipListExMulti(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_EndNodeSkipListExMulti(TRI_skiplistEx_multi_t* skiplist) { + if (skiplist != NULL) { + return &(skiplist->_base._endNode); + } + return NULL; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns greatest node less than a given key +//////////////////////////////////////////////////////////////////////////////// + +void* TRI_LeftLookupByKeySkipListExMulti(TRI_skiplistEx_multi_t* skiplist, void* key, uint64_t thistransID) { + int32_t currentLevel; + TRI_skiplistEx_node_t* currentNode; + TRI_skiplistEx_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_skiplistEx_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 = IndexStaticMultiCompareKeyElement(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_LookupByElementSkipListExMulti(TRI_skiplistEx_multi_t* skiplist, void* element, uint64_t thisTransID) { + assert(false); // there is no way you should be here + return 0; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns node which matches a key +//////////////////////////////////////////////////////////////////////////////// + +void* TRI_LookupByKeySkipListExMulti(TRI_skiplistEx_multi_t* skiplist, void* key, uint64_t thisTransID) { + 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_InsertElementSkipListExMulti(TRI_skiplistEx_multi_t* skiplist, void* element, bool overwrite, uint64_t thisTransID) { + //This uses the compareElementElement callback + int32_t newHeight; + int32_t currentLevel; + uint32_t oldColLength; + TRI_skiplistEx_node_t* currentNode; + TRI_skiplistEx_node_t* nextNode; + TRI_skiplistEx_node_t* newNode; + TRI_skiplistEx_node_t* tempLeftNode; + TRI_skiplistEx_node_t* tempRightNode; + bool growResult; + int compareResult; + int j; + + // ........................................................................... + // 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) { + growResult = GrowNodeHeight(&(skiplist->_base._startNode), newHeight); + growResult = growResult && GrowNodeHeight(&(skiplist->_base._endNode), newHeight); + if (!growResult) { + // todo: truncate the nodes and return; + return TRI_ERROR_OUT_OF_MEMORY; + } + JoinNodesCAS(&(skiplist->_base._startNode),&(skiplist->_base._endNode), oldColLength , newHeight - 1, thisTransID); + } + + + // ........................................................................... + // Create the new node to be inserted. If there is some sort of failure, + // then we delete the node memory. + // ........................................................................... + newNode = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_skiplistEx_node_t) + skiplist->_base._elementSize, false); + if (newNode == NULL) { // out of memory? + return TRI_ERROR_OUT_OF_MEMORY; + } + + + // ........................................................................... + // 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; + + j = IndexStaticCopyElementElement(&(skiplist->_base), &(newNode->_element),element); + + if (j != TRI_ERROR_NO_ERROR) { + return j; + } + + growResult = GrowNodeHeight(newNode, newHeight); + if (!growResult) { + FreeSkipListExNode(&(skiplist->_base), newNode); + return TRI_ERROR_OUT_OF_MEMORY; + } + + // ........................................................................... + // 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_skiplistEx_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 = IndexStaticMultiCompareElementElement(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) { + FreeSkipListExNode(&(skiplist->_base), newNode); + if (overwrite) { + j = IndexStaticCopyElementElement(&(skiplist->_base), &(nextNode->_element),element); + return j; + } + 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. + // .......................................................................... + + for (j = 0; j < newHeight; ++j) { + tempLeftNode = newNode->_column[j]._prev; + tempRightNode = tempLeftNode->_column[j]._next; + JoinNodesCAS(tempLeftNode, newNode, j, j, thisTransID); + JoinNodesCAS(newNode, tempRightNode, j, j, thisTransID); + } + + + return TRI_ERROR_NO_ERROR; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief adds an key/element to a multi skip list +//////////////////////////////////////////////////////////////////////////////// + +int TRI_InsertKeySkipListExMulti(TRI_skiplistEx_multi_t* skiplist, void* key, void* element, bool overwrite, uint64_t thisTransID) { + // Use TRI_InsertelementSkipListEx instead of calling this method + assert(false); + return 0; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief given a node returns the next node (if possible) in the skiplist +//////////////////////////////////////////////////////////////////////////////// + +void* TRI_NextNodeSkipListExMulti(TRI_skiplistEx_multi_t* skiplist, void* currentNode, uint64_t thisTransID) { + if (skiplist != NULL) { + return NextNodeBaseSkipListEx( (TRI_skiplistEx_base_t*)(skiplist), currentNode, thisTransID); + } + return NULL; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief given a node returns the previous node (if possible) in the skiplist +//////////////////////////////////////////////////////////////////////////////// + +void* TRI_PrevNodeSkipListExMulti(TRI_skiplistEx_multi_t* skiplist, void* currentNode, uint64_t thisTransID) { + if (skiplist != NULL) { + return PrevNodeBaseSkipListEx( (TRI_skiplistEx_base_t*)(skiplist), currentNode, thisTransID); + } + return NULL; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief removes a key/element from a multi skip list +//////////////////////////////////////////////////////////////////////////////// + + + +int TRI_RemoveElementSkipListExMulti (TRI_skiplistEx_multi_t* skiplist, void* element, void* old, uint64_t thisTransID) { + + int32_t currentLevel; + TRI_skiplistEx_node_t* currentNode; + TRI_skiplistEx_node_t* nextNode; + TRI_skiplistEx_node_t* tempLeftNode; + TRI_skiplistEx_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; // current level not height + currentNode = &(skiplist->_base._startNode); + + START: + + + // ......................................................................... + // Find the next node in the current level of the lists. + // ......................................................................... + nextNode = (TRI_skiplistEx_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 = IndexStaticMultiCompareElementElement(skiplist,element,&(nextNode->_element), TRI_SKIPLIST_EX_COMPARE_SLIGHTLY_LESS); + + // ....................................................................... + // We have found an item which matches the key + // ....................................................................... + if (compareResult == TRI_SKIPLIST_EX_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_SKIPLIST_EX_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_SKIPLIST_EX_COMPARE_SLIGHTLY_LESS) { + goto END; + } + + // can not occur + assert(false); + } + + // ....................................................................... + // Drop down the list + // ....................................................................... + --currentLevel; + + goto START; + } + + + + END: + + // .......................................................................... + // locate the correct elemet -- since we allow duplicates + // .......................................................................... + + while (currentNode != NULL) { + if (IndexStaticMultiEqualElementElement(skiplist, element, &(currentNode->_element))) { + break; + } + currentNode = NextNodeBaseSkipListEx(&(skiplist->_base), currentNode, thisTransID); + } + + + // .......................................................................... + // 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) { + IndexStaticCopyElementElement(&(skiplist->_base), old, &(currentNode->_element)); + } + + + // .......................................................................... + // remove element + // .......................................................................... + + for (j = 0; j < currentNode->_colLength; ++j) { + tempLeftNode = currentNode->_column[j]._prev; + tempRightNode = currentNode->_column[j]._next; + JoinNodesCAS(tempLeftNode, tempRightNode, j, j, thisTransID); + } + + FreeSkipListExNode(&(skiplist->_base), currentNode); + + return TRI_ERROR_NO_ERROR; + +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief removes a key/element from a multi skip list +//////////////////////////////////////////////////////////////////////////////// + +int TRI_RemoveKeySkipListExMulti(TRI_skiplistEx_multi_t* skiplist, void* key, void* old, uint64_t thisTransID) { + // Use the TRI_RemoveElementSkipListExMulti method instead. + assert(false); + return 0; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns smallest node greater than a given key +//////////////////////////////////////////////////////////////////////////////// + +void* TRI_RightLookupByKeySkipListExMulti(TRI_skiplistEx_multi_t* skiplist, void* key, uint64_t thisTransID) { + int32_t currentLevel; + TRI_skiplistEx_node_t* currentNode; + TRI_skiplistEx_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_skiplistEx_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 = IndexStaticMultiCompareKeyElement(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_StartNodeSkipListExMulti(TRI_skiplistEx_multi_t* skiplist) { + return &(skiplist->_base._startNode); +} + + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + + + + +//////////////////////////////////////////////////////////////////////////////// +// IMPLEMENTATION OF STATIC FORWARD DECLARED FUNCTIONS +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup SkiplistEx_common +/// @{ +//////////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief destroys a skip list, but does not free the pointer +//////////////////////////////////////////////////////////////////////////////// + +static void DestroyBaseSkipListEx(TRI_skiplistEx_base_t* baseSkiplist) { + TRI_skiplistEx_node_t* nextNode; + TRI_skiplistEx_node_t* oldNextNode; + + if (baseSkiplist == NULL) { + return; + } + + nextNode = &(baseSkiplist->_startNode); + while (nextNode != NULL) { + oldNextNode = nextNode->_column[0]._next; + TRI_Free(TRI_UNKNOWN_MEM_ZONE, nextNode->_column); + if ((nextNode != &(baseSkiplist->_startNode)) && (nextNode != &(baseSkiplist->_endNode))) { + IndexStaticDestroyElement(baseSkiplist, &(nextNode->_element)); + TRI_Free(TRI_UNKNOWN_MEM_ZONE, nextNode); + } + nextNode = oldNextNode; + } + TRI_Free(TRI_UNKNOWN_MEM_ZONE, baseSkiplist->_random); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief destroys the internal structure allocation for a node +//////////////////////////////////////////////////////////////////////////////// + +static void DestroySkipListExNode (TRI_skiplistEx_base_t* skiplist, TRI_skiplistEx_node_t* node) { + if (node == NULL) { + return; + } + TRI_Free(TRI_UNKNOWN_MEM_ZONE, node->_column); + IndexStaticDestroyElement(skiplist, &(node->_element)); +} + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief frees a node, destroying it first +//////////////////////////////////////////////////////////////////////////////// + +static void FreeSkipListExNode (TRI_skiplistEx_base_t* skiplist, TRI_skiplistEx_node_t* node) { + DestroySkipListExNode(skiplist, node); + if ( (node == &(skiplist->_startNode)) || + (node == &(skiplist->_endNode)) ) { + return; + } + TRI_Free(TRI_UNKNOWN_MEM_ZONE, node); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief Grow the node at the height specified. +//////////////////////////////////////////////////////////////////////////////// + +static bool GrowNodeHeight (TRI_skiplistEx_node_t* node, uint32_t newHeight) { + TRI_skiplistEx_nb_t* oldColumn = node->_column; + uint32_t j; + + if (node->_colLength >= newHeight) { + return true; + } + + node->_column = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_skiplistEx_nb_t) * newHeight, false); + + if (node->_column == NULL) { + // out of memory? + return false; + } + + if (oldColumn != NULL) { + memcpy(node->_column, oldColumn, node->_colLength * sizeof(TRI_skiplistEx_nb_t) ); + TRI_Free(TRI_UNKNOWN_MEM_ZONE, oldColumn); + } + + // ........................................................................... + // Initialise the storage + // ........................................................................... + + for (j = node->_colLength; j < newHeight; ++j) { + (node->_column)[j]._prev = NULL; + (node->_column)[j]._next = NULL; + } + + node->_colLength = newHeight; + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief destroys node +//////////////////////////////////////////////////////////////////////////////// + +static void DestroyNodeCAS (TRI_skiplistEx_node_t* theNode, + uint64_t thisTransID) { + assert(false); +} + +static void InsertNodeCAS (TRI_skiplistEx_node_t* theNode, + uint64_t thisTransID) { + assert(false); +} + +static void JoinNodesCAS(TRI_skiplistEx_node_t* leftNode, + TRI_skiplistEx_node_t* rightNode, + uint32_t startLevel, + uint32_t endLevel, + uint64_t thisTransID) { + assert(false); +} + +static void RemoveNodeCAS (TRI_skiplistEx_node_t* theNode, + uint64_t thisTransID) { + assert(false); +} + +static void DestroyNodeNoCAS (TRI_skiplistEx_node_t* theNode, + uint64_t thisTransID) { + assert(false); +} + +static void InsertNodeNoCAS (TRI_skiplistEx_node_t* theNode, + uint64_t thisTransID) { + assert(false); +} + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief joins a left node and right node together +//////////////////////////////////////////////////////////////////////////////// + + + +static void JoinNodesNoCAS(TRI_skiplistEx_node_t* leftNode, + TRI_skiplistEx_node_t* rightNode, + uint32_t startLevel, + uint32_t endLevel, + uint64_t thisTransID) { + uint32_t j; + + if (startLevel > endLevel) { // something wrong + assert(false); + return; + } + + // change level to height + endLevel += 1; + + if (leftNode->_colLength < endLevel) { + assert(false); + return; + } + + if (rightNode->_colLength < endLevel) { + assert(false); + return; + } + + for (j = startLevel; j < endLevel; ++j) { + (leftNode->_column)[j]._next = rightNode; + (rightNode->_column)[j]._prev = leftNode; + } +} + + +static void RemoveNodeNoCAS (TRI_skiplistEx_node_t* theNode, + uint64_t thisTransID) { + assert(false); +} + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief given a node returns the next node (if possible) in the skiplist +//////////////////////////////////////////////////////////////////////////////// + +static void* NextNodeBaseSkipListEx(TRI_skiplistEx_base_t* skiplist, void* currentNode, uint64_t thisTransID) { + TRI_skiplistEx_node_t* node; + + if (currentNode == NULL) { + return &(skiplist->_startNode); + } + if (currentNode == &(skiplist->_endNode)) { + return NULL; + } + node = (TRI_skiplistEx_node_t*)(currentNode); + return(node->_column[0]._next); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief given a node returns the next node (if possible) in the skiplist +//////////////////////////////////////////////////////////////////////////////// + +static void* PrevNodeBaseSkipListEx(TRI_skiplistEx_base_t* skiplist, void* currentNode, uint64_t thisTransID) { + TRI_skiplistEx_node_t* node; + + if (currentNode == NULL) { + return &(skiplist->_endNode); + } + if (currentNode == &(skiplist->_startNode)) { + return NULL; + } + node = (TRI_skiplistEx_node_t*)(currentNode); + return(node->_column[0]._prev); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief determines at what 'height' the item is to be added +//////////////////////////////////////////////////////////////////////////////// + +static int32_t RandLevel (TRI_skiplistEx_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_SKIPLIST_EX_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_SKIPLIST_EX_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_SKIPLIST_EX_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: diff --git a/arangod/SkipListsEx/skiplistEx.h b/arangod/SkipListsEx/skiplistEx.h new file mode 100644 index 0000000000..0463c925fe --- /dev/null +++ b/arangod/SkipListsEx/skiplistEx.h @@ -0,0 +1,557 @@ +//////////////////////////////////////////////////////////////////////////////// +/// @brief skip list which suports transactions 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 +//////////////////////////////////////////////////////////////////////////////// + + +#ifndef TRIAGENS_BASICS_C_SKIPLIST_EX_H +#define TRIAGENS_BASICS_C_SKIPLIST_EX_H 1 + +#include "BasicsC/common.h" +#include "BasicsC/locks.h" +#include "BasicsC/vector.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// ----------------------------------------------------------------------------- +// --SECTION-- skiplistEx public types +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup SkiplistEx +/// @{ +//////////////////////////////////////////////////////////////////////////////// + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief types which enumerate the probability used to determine the height of node +//////////////////////////////////////////////////////////////////////////////// + +typedef enum { + TRI_SKIPLIST_EX_PROB_HALF, + TRI_SKIPLIST_EX_PROB_THIRD, + TRI_SKIPLIST_EX_PROB_QUARTER +} +TRI_skiplistEx_prob_e; + + +typedef enum { + TRI_SKIPLIST_EX_COMPARE_STRICTLY_LESS = -1, + TRI_SKIPLIST_EX_COMPARE_STRICTLY_GREATER = 1, + TRI_SKIPLIST_EX_COMPARE_STRICTLY_EQUAL = 0, + TRI_SKIPLIST_EX_COMPARE_SLIGHTLY_LESS = -2, + TRI_SKIPLIST_EX_COMPARE_SLIGHTLY_GREATER = 2 +} +TRI_skiplistEx_compare_e; + +//////////////////////////////////////////////////////////////////////////////// +/// @brief storage structure for a node's nearest neighbours +//////////////////////////////////////////////////////////////////////////////// + +// ............................................................................. +// The nearest neighbour node needs to be modified for handling transactions. +// We introduce a structure for the forward and back links. +// To implement 'lock free', we require an atomic Compare & Save (C&S) function +// to use this correctly on Mac/Windows we need to align pointer/integers +// on 32 or 64 bit boundaries. +// ............................................................................. + +typedef struct TRI_skiplistEx_nb_s { + void* _prev; // points to the previous nearest neighbour of this node (the left node) + void* _next; // points to the successor of this node (right node) + uint64_t _flag; // the _flag field operates as follows: + // if (_flag & 1) == 1, then the Tower Node (the node which uses this structure) is a Glass Tower Node. + // +} TRI_skiplistEx_nb_t; // nearest neighbour; + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief structure of a skip list node (unique and non-unique) +//////////////////////////////////////////////////////////////////////////////// + +typedef struct TRI_skiplistEx_node_s { + TRI_skiplistEx_nb_t* _column; // these represent the levels and the links within these, an array of these + uint32_t _colLength; // the height of the column + void* _extraData; + void* _element; + uint64_t _delTransID; // the transaction id which removed (deleted) this node + uint64_t _insTransID; // the transaction id which inserted this node +} +TRI_skiplistEx_node_t; + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief The base structure of a skiplist (unique and non-unique) +//////////////////////////////////////////////////////////////////////////////// + +typedef struct TRI_skiplistEx_base_s { + // ........................................................................... + // The maximum height of this skip list. Thus 2^(_maxHeight) elements can be + // stored in the skip list. + // ........................................................................... + uint32_t _maxHeight; + + // ........................................................................... + // The size of each element which is to be stored. + // ........................................................................... + uint32_t _elementSize; + + // ........................................................................... + // The actual list itself + // ........................................................................... + char* _skiplist; + + // ........................................................................... + // The probability which is used to determine the level for insertions + // into the list. Note the following + // ........................................................................... + TRI_skiplistEx_prob_e _prob; + int32_t _numRandom; + uint32_t* _random; + + + TRI_skiplistEx_node_t _startNode; + TRI_skiplistEx_node_t _endNode; + +} +TRI_skiplistEx_base_t; + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + + +// ----------------------------------------------------------------------------- +// --SECTION-- unique skiplistEx public types +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup SkiplistEx_unique +/// @{ +//////////////////////////////////////////////////////////////////////////////// + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief structure used for a skip list which only accepts unique entries +//////////////////////////////////////////////////////////////////////////////// + +typedef struct TRI_skiplistEx_s { + TRI_skiplistEx_base_t _base; + // ........................................................................... + // callback compare function + // < 0: implies left < right + // == 0: implies left == right + // > 0: implies left > right + // ........................................................................... + int (*compareElementElement) (struct TRI_skiplistEx_s*, void*, void*, int); + int (*compareKeyElement) (struct TRI_skiplistEx_s*, void*, void*, int); +} +TRI_skiplistEx_t; + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief structure used for a skip list which only accepts unique entries and is thread safe +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +// structure for a skiplist which allows unique entries -- with locking +// available for its nearest neighbours. +// TODO: implement locking for nearest neighbours rather than for all of index +//////////////////////////////////////////////////////////////////////////////// + +typedef struct TRI_skiplistEx_synced_s { + TRI_skiplistEx_t _base; + TRI_read_write_lock_t _lock; +} TRI_skiplistEx_synced_t; + + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + + +// ----------------------------------------------------------------------------- +// --SECTION-- unique skiplist constructors and destructors +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup Skiplist_unique +/// @{ +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @brief initialises a skip list +//////////////////////////////////////////////////////////////////////////////// + +int TRI_InitSkipListEx (TRI_skiplistEx_t*, + size_t elementSize, + int (*compareElementElement) (TRI_skiplistEx_t*, void*, void*, int), + int (*compareKeyElement) (TRI_skiplistEx_t*, void*, void*, int), + TRI_skiplistEx_prob_e, + uint32_t, + uint64_t lastKnownTransID); + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief destroys a skip list, but does not free the pointer +//////////////////////////////////////////////////////////////////////////////// + +void TRI_DestroySkipListEx (TRI_skiplistEx_t*); + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief destroys a skip list and frees the pointer +//////////////////////////////////////////////////////////////////////////////// + +void TRI_FreeSkipListEx (TRI_skiplistEx_t*); + + + + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + +// ----------------------------------------------------------------------------- +// --SECTION-- unique skiplist public functions +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup Skiplist_unique +/// @{ +//////////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns the end node which belongs to a skiplist +//////////////////////////////////////////////////////////////////////////////// + +void* TRI_EndNodeSkipListEx (TRI_skiplistEx_t*); + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief adds an element to the skip list using element for comparison +//////////////////////////////////////////////////////////////////////////////// + +int TRI_InsertElementSkipListEx (TRI_skiplistEx_t*, void*, bool, uint64_t thisTransID); + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief adds an element to the skip list using key for comparison +//////////////////////////////////////////////////////////////////////////////// + +int TRI_InsertKeySkipListEx (TRI_skiplistEx_t*, void*, void*, bool, uint64_t thisTransID); + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief lookups an element given a key, returns greatest left element +//////////////////////////////////////////////////////////////////////////////// + +void* TRI_LeftLookupByKeySkipListEx (TRI_skiplistEx_t*, void*, uint64_t thisTransID); + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief lookups an element given a key, returns null if not found +//////////////////////////////////////////////////////////////////////////////// + +void* TRI_LookupByKeySkipListEx (TRI_skiplistEx_t*, void*, uint64_t thisTransID); + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief given a node returns the next node in the skip list, if the end is reached returns the end node +//////////////////////////////////////////////////////////////////////////////// + +void* TRI_NextNodeSkipListEx (TRI_skiplistEx_t*, void*, uint64_t thisTransID); + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief given a node returns the prev node in the skip list, if the beginning is reached returns the start node +//////////////////////////////////////////////////////////////////////////////// + +void* TRI_PrevNodeSkipListEx (TRI_skiplistEx_t*, void*, uint64_t thisTransID); + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief removes an element from the skip list using element for comparison +//////////////////////////////////////////////////////////////////////////////// + +int TRI_RemoveElementSkipListEx (TRI_skiplistEx_t*, void*, void*, uint64_t thisTransID); + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief removes an element from the skip list using key for comparison +//////////////////////////////////////////////////////////////////////////////// + +int TRI_RemoveKeySkipListEx (TRI_skiplistEx_t*, void*, void*, uint64_t thisTransID); + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief lookups an element given a key, returns least right element +//////////////////////////////////////////////////////////////////////////////// + +void* TRI_RightLookupByKeySkipListEx (TRI_skiplistEx_t*, void*, uint64_t thisTransID); + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns the start node which belongs to a skiplist +//////////////////////////////////////////////////////////////////////////////// + +void* TRI_StartNodeSkipListEx (TRI_skiplistEx_t*); + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + +// ----------------------------------------------------------------------------- +// --SECTION-- non-unique skiplist public types +// ----------------------------------------------------------------------------- + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief structure used for a multi skiplist +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup Skiplist_non_unique +/// @{ +//////////////////////////////////////////////////////////////////////////////// + +typedef struct TRI_skiplistEx_multi_s { + TRI_skiplistEx_base_t _base; + // ........................................................................... + // callback compare function + // < 0: implies left < right + // == 0: implies left == right + // > 0: implies left > right + // ........................................................................... + int (*compareElementElement) (struct TRI_skiplistEx_multi_s*, void*, void*, int); + int (*compareKeyElement) (struct TRI_skiplistEx_multi_s*, void*, void*, int); + + // ........................................................................... + // Returns true if the element is an exact copy, or if the data which the + // element points to is an exact copy + // ........................................................................... + bool (*equalElementElement) (struct TRI_skiplistEx_multi_s*, void*, void*); +} TRI_skiplistEx_multi_t; + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief structure used for a multi skip list and is thread safe +//////////////////////////////////////////////////////////////////////////////// + +typedef struct TRI_skiplistEx_synced_multi_s { + TRI_skiplistEx_t _base; + TRI_read_write_lock_t _lock; +} TRI_skiplistEx_synced_multi_t; + + + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + + + +// ----------------------------------------------------------------------------- +// --SECTION-- non-unique skiplist constructors and destructors +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup Skiplist_non_unique +/// @{ +//////////////////////////////////////////////////////////////////////////////// + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief initialises a skip list +//////////////////////////////////////////////////////////////////////////////// + + +int TRI_InitSkipListExMulti (TRI_skiplistEx_multi_t*, + size_t elementSize, + int (*compareElementElement) (TRI_skiplistEx_multi_t*, void*, void*, int), + int (*compareKeyElement) (TRI_skiplistEx_multi_t*, void*, void*, int), + bool (*equalElementElement) (TRI_skiplistEx_multi_t*, void*, void*), + TRI_skiplistEx_prob_e, + uint32_t, + uint64_t lastKnownTransID); + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief destroys a multi skip list, but does not free the pointer +//////////////////////////////////////////////////////////////////////////////// + +void TRI_DestroySkipListExMulti (TRI_skiplistEx_multi_t*); + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief destroys a skip list and frees the pointer +//////////////////////////////////////////////////////////////////////////////// + +void TRI_FreeSkipListExMulti (TRI_skiplistEx_multi_t*); + + + + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + + + + +// ----------------------------------------------------------------------------- +// --SECTION-- non-unique skiplistEx public functions +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup SkiplistEx_non_unique +/// @{ +//////////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns the end node which belongs to a skiplist +//////////////////////////////////////////////////////////////////////////////// + +void* TRI_EndNodeSkipListExMulti (TRI_skiplistEx_multi_t*); + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief adds an element to the skip list using element for comparison +//////////////////////////////////////////////////////////////////////////////// + +int TRI_InsertElementSkipListExMulti (TRI_skiplistEx_multi_t*, void*, bool, uint64_t thisTransID); + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief adds an element to the skip list using key for comparison +//////////////////////////////////////////////////////////////////////////////// + +int TRI_InsertKeySkipListExMulti (TRI_skiplistEx_multi_t*, void*, void*, bool, uint64_t thisTransID); + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief lookups an element given a key, returns greatest left element +//////////////////////////////////////////////////////////////////////////////// + +void* TRI_LeftLookupByKeySkipListExMulti (TRI_skiplistEx_multi_t*, void*, uint64_t thisTransID); + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief lookups an element given a key, returns null if not found +//////////////////////////////////////////////////////////////////////////////// + +void* TRI_LookupByKeySkipListExMulti (TRI_skiplistEx_multi_t*, void*, uint64_t thisTransID); + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief given a node returns the next node in the skip list, if the end is reached returns the end node +//////////////////////////////////////////////////////////////////////////////// + +void* TRI_NextNodeSkipListExMulti (TRI_skiplistEx_multi_t*, void*, uint64_t thisTransID); + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief given a node returns the prev node in the skip list, if the beginning is reached returns the start node +//////////////////////////////////////////////////////////////////////////////// + +void* TRI_PrevNodeSkipListExMulti (TRI_skiplistEx_multi_t*, void*, uint64_t thisTransID); + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief removes an element from the skip list using element for comparison +//////////////////////////////////////////////////////////////////////////////// + +int TRI_RemoveElementSkipListExMulti (TRI_skiplistEx_multi_t*, void*, void*, uint64_t thisTransID); + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief removes an element from the skip list using key for comparison +//////////////////////////////////////////////////////////////////////////////// + +int TRI_RemoveKeySkipListExMulti (TRI_skiplistEx_multi_t*, void*, void*, uint64_t thisTransID); + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief lookups an element given a key, returns least right element +//////////////////////////////////////////////////////////////////////////////// + +void* TRI_RightLookupByKeySkipListExMulti (TRI_skiplistEx_multi_t*, void*, uint64_t thisTransID); + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief returns the start node which belongs to a skiplist +//////////////////////////////////////////////////////////////////////////////// + +void* TRI_StartNodeSkipListExMulti (TRI_skiplistEx_multi_t*); + + + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + + +#ifdef __cplusplus +} +#endif + +#endif + +// Local Variables: +// mode: outline-minor +// outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)" +// End: diff --git a/arangod/SkipListsEx/skiplistExIndex.c b/arangod/SkipListsEx/skiplistExIndex.c new file mode 100644 index 0000000000..5029e5a208 --- /dev/null +++ b/arangod/SkipListsEx/skiplistExIndex.c @@ -0,0 +1,1417 @@ +//////////////////////////////////////////////////////////////////////////////// +/// @brief skiplistEx index +/// +/// @file +/// +/// DISCLAIMER +/// +/// Copyright by triAGENS GmbH - All rights reserved. +/// +/// The Programs (which include both the software and documentation) +/// contain proprietary information of triAGENS GmbH; they are +/// provided under a license agreement containing restrictions on use and +/// disclosure and are also protected by copyright, patent and other +/// intellectual and industrial property laws. Reverse engineering, +/// disassembly or decompilation of the Programs, except to the extent +/// required to obtain interoperability with other independently created +/// software or as specified by law, is prohibited. +/// +/// The Programs are not intended for use in any nuclear, aviation, mass +/// transit, medical, or other inherently dangerous applications. It shall +/// be the licensee's responsibility to take all appropriate fail-safe, +/// backup, redundancy, and other measures to ensure the safe use of such +/// applications if the Programs are used for such purposes, and triAGENS +/// GmbH disclaims liability for any damages caused by such use of +/// the Programs. +/// +/// This software is the confidential and proprietary information of +/// triAGENS GmbH. You shall not disclose such confidential and +/// proprietary information and shall use it only in accordance with the +/// terms of the license agreement you entered into with triAGENS GmbH. +/// +/// Copyright holder is triAGENS GmbH, Cologne, Germany +/// +/// @author Dr. O +/// @author Copyright 2011, triagens GmbH, Cologne, Germany +//////////////////////////////////////////////////////////////////////////////// + +#include "skiplistExIndex.h" + + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// Common private methods +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + + +// ............................................................................. +// forward declaration - some helper methods +// ............................................................................. + +static bool skiplistExIndex_findHelperIntervalValid (SkiplistExIndex*, TRI_skiplistEx_iterator_interval_t*); +static bool multiSkiplistExIndex_findHelperIntervalValid (SkiplistExIndex*, TRI_skiplistEx_iterator_interval_t*); + + +// ............................................................................. +// forward declaration of static functions which are used by the query engine +// ............................................................................. + +static int SkiplistExIndex_queryMethodCall (void*, TRI_index_operator_t*, TRI_index_challenge_t*, void*); +static TRI_index_iterator_t* SkiplistExIndex_resultMethodCall (void*, TRI_index_operator_t*, void*, bool (*filter) (TRI_index_iterator_t*)); +static int SkiplistExIndex_freeMethodCall (void*, void*); + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief Attempts to determine if there is a next document within an interval - without advancing the iterator. +//////////////////////////////////////////////////////////////////////////////// + +static bool SkiplistExHasNextIterationCallback(TRI_skiplistEx_iterator_t* iterator) { + TRI_skiplistEx_iterator_interval_t* interval; + void* leftNode; + + // ............................................................................. + // Some simple checks. + // ............................................................................. + + if (iterator == NULL) { + return false; + } + + if (iterator->_intervals._length == 0) { + return false; + } + + + // ............................................................................. + // if we have more intervals than the one we are currently working on then of course we have a next doc + // ............................................................................. + if (iterator->_intervals._length - 1 > iterator->_currentInterval) { + return true; + } + + + // ............................................................................. + // Obtain the current interval -- in case we ever use more than one interval + // ............................................................................. + + interval = (TRI_skiplistEx_iterator_interval_t*) ( TRI_AtVector(&(iterator->_intervals), iterator->_currentInterval) ); + + + // ............................................................................. + // Obtain the left end point we are currently at + // ............................................................................. + + if (iterator->_cursor == NULL) { + leftNode = interval->_leftEndPoint; + } + else { + leftNode = iterator->_cursor; + } + + + // ............................................................................. + // If the left == right end point AND there are no more intervals then we have + // no next. + // ............................................................................. + + if (leftNode == interval->_rightEndPoint) { + return false; + } + + // ........................................................................... + // interval of the type (a,b) -- but nothing between a and b + // such intervals are optimised out so will not be here + // ........................................................................... + if (iterator->_index->_unique) { + leftNode = TRI_NextNodeSkipListEx(iterator->_index->_skiplistEx.uniqueSkiplistEx, leftNode, iterator->_thisTransID); + } + else { + leftNode = TRI_NextNodeSkipListExMulti(iterator->_index->_skiplistEx.nonUniqueSkiplistEx, leftNode, iterator->_thisTransID); + } + + + // ........................................................................... + // Check various possibilities + // ........................................................................... + + if (leftNode == NULL) { + return false; + } + + if (leftNode == interval->_rightEndPoint) { + return false; + } + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief Attempts to determine if there is a previous document within an interval - without advancing the iterator. +//////////////////////////////////////////////////////////////////////////////// + +static bool SkiplistExHasPrevIterationCallback(TRI_skiplistEx_iterator_t* iterator) { + TRI_skiplistEx_iterator_interval_t* interval; + void* rightNode; + + // ............................................................................. + // Some simple checks. + // ............................................................................. + + if (iterator == NULL) { + return false; + } + + if (iterator->_intervals._length == 0) { + return false; + } + + // ............................................................................. + // this check is as follows if we have more intervals than the one + // we are currently working on -- then of course we have a prev doc + // ............................................................................. + if (iterator->_currentInterval > 0 ) { + return true; + } + + + // ............................................................................. + // Obtain the current interval -- in case we ever use more than one interval + // ............................................................................. + + interval = (TRI_skiplistEx_iterator_interval_t*) ( TRI_AtVector(&(iterator->_intervals), iterator->_currentInterval) ); + + + // ............................................................................. + // Obtain the left end point we are currently at + // ............................................................................. + + if (iterator->_cursor == NULL) { + rightNode = interval->_rightEndPoint; + } + else { + rightNode = iterator->_cursor; + } + + + // ............................................................................. + // If the left == right end point AND there are no more intervals then we have + // no next. + // ............................................................................. + + if (rightNode == interval->_leftEndPoint) { + return false; + } + + // ........................................................................... + // interval of the type (a,b) -- but nothing between a and b + // such intervals are optimised out so will not be here + // ........................................................................... + if (iterator->_index->_unique) { + rightNode = TRI_PrevNodeSkipListEx(iterator->_index->_skiplistEx.uniqueSkiplistEx, rightNode, iterator->_thisTransID); + } + else { + rightNode = TRI_PrevNodeSkipListExMulti(iterator->_index->_skiplistEx.nonUniqueSkiplistEx, rightNode, iterator->_thisTransID); + } + + + // ........................................................................... + // Check various possibilities + // ........................................................................... + + if (rightNode == NULL) { + return false; + } + + if (rightNode == interval->_leftEndPoint) { + return false; + } + + return true; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief Jumps forwards or backwards by jumpSize and returns the document +//////////////////////////////////////////////////////////////////////////////// + +static void* SkiplistExIteration(TRI_skiplistEx_iterator_t* iterator, int64_t jumpSize) { + TRI_skiplistEx_iterator_interval_t* interval; + TRI_skiplistEx_node_t* currentNode; + int64_t j; + + // ............................................................................. + // Some simple checks. + // ............................................................................. + + if (iterator == NULL) { + return NULL; + } + + if (iterator->_intervals._length == 0) { + return NULL; + } + + currentNode = (TRI_skiplistEx_node_t*) (iterator->_cursor); + + if (jumpSize == 0) { + if (currentNode == NULL) { + return NULL; + } + else { + return &(currentNode->_element); + } + } + + + + // ............................................................................. + // If the current cursor is NULL and jumpSize < 0, then start at the endpoint of + // the right most interval. + // ............................................................................. + if (currentNode == NULL && jumpSize < 0) { + interval = (TRI_skiplistEx_iterator_interval_t*) ( TRI_AtVector(&(iterator->_intervals), iterator->_intervals._length - 1) ); + + if (iterator->_index->_unique) { + iterator->_cursor = TRI_PrevNodeSkipListEx(iterator->_index->_skiplistEx.uniqueSkiplistEx, interval->_rightEndPoint, iterator->_thisTransID); + } + else { + iterator->_cursor = TRI_PrevNodeSkipListExMulti(iterator->_index->_skiplistEx.nonUniqueSkiplistEx, interval->_rightEndPoint, iterator->_thisTransID); + } + + currentNode = (TRI_skiplistEx_node_t*) (iterator->_cursor); + + if (currentNode == NULL) { + return NULL; + } + + if (currentNode == interval->_leftEndPoint) { + return NULL; + } + + return &(currentNode->_element); + } + + + // ............................................................................. + // If the current cursor is NULL and jumpSize > 0, then start at the left point of + // the left most interval. + // ............................................................................. + if (currentNode == NULL && jumpSize > 0) { + interval = (TRI_skiplistEx_iterator_interval_t*) ( TRI_AtVector(&(iterator->_intervals), 0) ); + + if (iterator->_index->_unique) { + iterator->_cursor = TRI_NextNodeSkipListEx(iterator->_index->_skiplistEx.uniqueSkiplistEx, interval->_leftEndPoint, iterator->_thisTransID); + } + else { + iterator->_cursor = TRI_NextNodeSkipListExMulti(iterator->_index->_skiplistEx.nonUniqueSkiplistEx, interval->_leftEndPoint, iterator->_thisTransID); + } + + currentNode = (TRI_skiplistEx_node_t*) (iterator->_cursor); + + if (currentNode == NULL) { + return NULL; + } + + if (currentNode == interval->_rightEndPoint) { + + if (iterator->_index->_unique) { + iterator->_cursor = TRI_NextNodeSkipListEx(iterator->_index->_skiplistEx.uniqueSkiplistEx, interval->_leftEndPoint, iterator->_thisTransID); + } + else { + iterator->_cursor = TRI_NextNodeSkipListExMulti(iterator->_index->_skiplistEx.nonUniqueSkiplistEx, interval->_leftEndPoint, iterator->_thisTransID); + } + + currentNode = (TRI_skiplistEx_node_t*) (iterator->_cursor); + return NULL; + } + + return &(currentNode->_element); + } + + + // ............................................................................. + // Obtain the current interval we are at. + // ............................................................................. + + interval = (TRI_skiplistEx_iterator_interval_t*) ( TRI_AtVector(&(iterator->_intervals), iterator->_currentInterval) ); + + // ............................................................................. + // use the current cursor and move jumpSize back. + // ............................................................................. + if (jumpSize < 0) { + jumpSize = -jumpSize; + for (j = 0; j < jumpSize; ++j) { + if (iterator->_cursor == interval->_leftEndPoint) { + if (iterator->_currentInterval == 0) { + return NULL; + } + --iterator->_currentInterval; + interval = (TRI_skiplistEx_iterator_interval_t*) ( TRI_AtVector(&(iterator->_intervals), iterator->_currentInterval) ); + iterator->_cursor = interval->_rightEndPoint; + } + + if (iterator->_index->_unique) { + iterator->_cursor = TRI_PrevNodeSkipListEx(iterator->_index->_skiplistEx.uniqueSkiplistEx, iterator->_cursor, iterator->_thisTransID); + } + else { + iterator->_cursor = TRI_PrevNodeSkipListExMulti(iterator->_index->_skiplistEx.nonUniqueSkiplistEx, iterator->_cursor, iterator->_thisTransID); + } + + } + + if (iterator->_cursor == interval->_leftEndPoint) { + if (iterator->_currentInterval == 0) { + return NULL; + } + --iterator->_currentInterval; + interval = (TRI_skiplistEx_iterator_interval_t*) ( TRI_AtVector(&(iterator->_intervals), iterator->_currentInterval) ); + iterator->_cursor = interval->_rightEndPoint; + + if (iterator->_index->_unique) { + iterator->_cursor = TRI_PrevNodeSkipListEx(iterator->_index->_skiplistEx.uniqueSkiplistEx, iterator->_cursor, iterator->_thisTransID); + } + else { + iterator->_cursor = TRI_PrevNodeSkipListExMulti(iterator->_index->_skiplistEx.nonUniqueSkiplistEx, iterator->_cursor, iterator->_thisTransID); + } + + } + } + + + // ............................................................................. + // use the current cursor and move jumpSize forward. + // ............................................................................. + if (jumpSize > 0) { + + for (j = 0; j < jumpSize; ++j) { + if (iterator->_cursor == interval->_rightEndPoint) { + if (iterator->_currentInterval == (iterator->_intervals._length - 1)) { + return NULL; + } + ++iterator->_currentInterval; + interval = (TRI_skiplistEx_iterator_interval_t*) ( TRI_AtVector(&(iterator->_intervals), iterator->_currentInterval) ); + iterator->_cursor = interval->_leftEndPoint; + } + + if (iterator->_index->_unique) { + iterator->_cursor = TRI_NextNodeSkipListEx(iterator->_index->_skiplistEx.uniqueSkiplistEx, iterator->_cursor, iterator->_thisTransID); + } + else { + iterator->_cursor = TRI_NextNodeSkipListExMulti(iterator->_index->_skiplistEx.nonUniqueSkiplistEx, iterator->_cursor, iterator->_thisTransID); + } + } + + if (iterator->_cursor == interval->_rightEndPoint) { + if (iterator->_currentInterval == (iterator->_intervals._length - 1)) { + return NULL; + } + ++iterator->_currentInterval; + interval = (TRI_skiplistEx_iterator_interval_t*) ( TRI_AtVector(&(iterator->_intervals), iterator->_currentInterval) ); + iterator->_cursor = interval->_leftEndPoint; + + if (iterator->_index->_unique) { + iterator->_cursor = TRI_NextNodeSkipListEx(iterator->_index->_skiplistEx.uniqueSkiplistEx, iterator->_cursor, iterator->_thisTransID); + } + else { + iterator->_cursor = TRI_NextNodeSkipListExMulti(iterator->_index->_skiplistEx.nonUniqueSkiplistEx, iterator->_cursor, iterator->_thisTransID); + } + + } + } + + + currentNode = (TRI_skiplistEx_node_t*) (iterator->_cursor); + if (currentNode == NULL) { + return NULL; + } + return &(currentNode->_element); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief default callback for jumping forward by 1 +//////////////////////////////////////////////////////////////////////////////// + +static void* SkiplistExNextIterationCallback(TRI_skiplistEx_iterator_t* iterator) { + return SkiplistExIteration(iterator,1); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief default callback for jumping forward by jumpSize docs +//////////////////////////////////////////////////////////////////////////////// + +static void* SkiplistExNextsIterationCallback(TRI_skiplistEx_iterator_t* iterator, int64_t jumpSize) { + return SkiplistExIteration(iterator,jumpSize); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief default callback for jumping backwards by 1 +//////////////////////////////////////////////////////////////////////////////// + +static void* SkiplistExPrevIterationCallback(TRI_skiplistEx_iterator_t* iterator) { + return SkiplistExIteration(iterator,-1); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief default callback for jumping backwards by jumpSize docs +//////////////////////////////////////////////////////////////////////////////// + +static void* SkiplistExPrevsIterationCallback(TRI_skiplistEx_iterator_t* iterator, int64_t jumpSize) { + return SkiplistExIteration(iterator,-jumpSize); +} + + + + +// ----------------------------------------------------------------------------- +// --SECTION-- skiplistIndex common public methods +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup skiplistIndex +/// @{ +//////////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief Assigns a static function call to a function pointer used by Query Engine +//////////////////////////////////////////////////////////////////////////////// + +int SkiplistExIndex_assignMethod(void* methodHandle, TRI_index_method_assignment_type_e methodType) { + switch (methodType) { + + case TRI_INDEX_METHOD_ASSIGNMENT_FREE : { + TRI_index_query_free_method_call_t* call = (TRI_index_query_free_method_call_t*)(methodHandle); + *call = SkiplistExIndex_freeMethodCall; + break; + } + + case TRI_INDEX_METHOD_ASSIGNMENT_QUERY : { + TRI_index_query_method_call_t* call = (TRI_index_query_method_call_t*)(methodHandle); + *call = SkiplistExIndex_queryMethodCall; + break; + } + + case TRI_INDEX_METHOD_ASSIGNMENT_RESULT : { + TRI_index_query_result_method_call_t* call = (TRI_index_query_result_method_call_t*)(methodHandle); + *call = SkiplistExIndex_resultMethodCall; + break; + } + + default : { + assert(false); + } + } + + return TRI_ERROR_NO_ERROR; +} + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief Free a skiplist iterator +//////////////////////////////////////////////////////////////////////////////// + +void TRI_FreeSkiplistExIterator (TRI_skiplistEx_iterator_t* const iterator) { + assert(iterator); + + TRI_DestroyVector(&iterator->_intervals); + TRI_Free(TRI_UNKNOWN_MEM_ZONE, iterator); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief destroys a skip list index , but does not free the pointer +//////////////////////////////////////////////////////////////////////////////// + +void SkiplistExIndex_destroy(SkiplistExIndex* slIndex) { + if (slIndex == NULL) { + return; + } + + if (slIndex->_unique) { + TRI_FreeSkipListEx(slIndex->_skiplistEx.uniqueSkiplistEx); + slIndex->_skiplistEx.uniqueSkiplistEx = NULL; + } + else { + TRI_FreeSkipListExMulti(slIndex->_skiplistEx.nonUniqueSkiplistEx); + slIndex->_skiplistEx.nonUniqueSkiplistEx = NULL; + } +} + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief destroys a skip list index and frees the pointer +//////////////////////////////////////////////////////////////////////////////// + +void SkiplistExIndex_free(SkiplistExIndex* slIndex) { + if (slIndex == NULL) { + return; + } + SkiplistExIndex_destroy(slIndex); + TRI_Free(TRI_UNKNOWN_MEM_ZONE, slIndex); +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + + + + + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// Unique Skiplists +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + + +//------------------------------------------------------------------------------ +// Private Methods Unique Skiplists +//------------------------------------------------------------------------------ + + + + +//------------------------------------------------------------------------------ +// Public Methods Unique Skiplists +//------------------------------------------------------------------------------ + +//////////////////////////////////////////////////////////////////////////////// +/// @brief creates a new unique entry skiplist +//////////////////////////////////////////////////////////////////////////////// + +SkiplistExIndex* SkiplistExIndex_new(TRI_transaction_context_t* transactionContext) { + SkiplistExIndex* skiplistExIndex; + int result; + uint64_t lastKnownTransID = 0; + + skiplistExIndex = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(SkiplistExIndex), true); + if (skiplistExIndex == NULL) { + return NULL; + } + + skiplistExIndex->_unique = true; + skiplistExIndex->_skiplistEx.uniqueSkiplistEx = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_skiplistEx_t), true); + if (skiplistExIndex->_skiplistEx.uniqueSkiplistEx == NULL) { + TRI_Free(TRI_UNKNOWN_MEM_ZONE, skiplistExIndex); + return NULL; + } + + skiplistExIndex->_transactionContext = transactionContext; + + // TODO ORESTE: determine the last know transaction id from the transaction context + result = TRI_InitSkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx, + sizeof(SkiplistExIndexElement), + NULL,NULL,TRI_SKIPLIST_EX_PROB_HALF, 40, + lastKnownTransID); + + if (result != TRI_ERROR_NO_ERROR) { + TRI_Free(TRI_UNKNOWN_MEM_ZONE, skiplistExIndex->_skiplistEx.uniqueSkiplistEx); + TRI_Free(TRI_UNKNOWN_MEM_ZONE, skiplistExIndex); + return NULL; + } + + return skiplistExIndex; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief adds (inserts) a data element into a unique skip list +//////////////////////////////////////////////////////////////////////////////// + +int SkiplistExIndex_add(SkiplistExIndex* skiplistExIndex, SkiplistExIndexElement* element, uint64_t thisTransID) { + int result; + result = TRI_InsertKeySkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx, element, element, false, thisTransID); + return result; +} + + + +////////////////////////////////////////////////////////////////////////////////// +/// @brief Locates one or more ranges within the unique skiplist and returns iterator +////////////////////////////////////////////////////////////////////////////////// + +// ............................................................................... +// Tests whether the LeftEndPoint is < than RightEndPoint (-1) +// Tests whether the LeftEndPoint is == to RightEndPoint (0) [empty] +// Tests whether the LeftEndPoint is > than RightEndPoint (1) [undefined] +// ............................................................................... +/* +static void debugElement(SkiplistIndex* skiplistIndex, TRI_skiplist_node_t* node) { + size_t numFields; + SkiplistIndexElement* element = (SkiplistIndexElement*)(&(node->_element)); + TRI_shaper_t* shaper; + size_t j; + + if (node == NULL) { + printf("%s:%u:node null\n",__FILE__,__LINE__); + return; + } + + if (node == TRI_StartNodeSkipList(skiplistIndex->skiplist.uniqueSkiplist)) { + printf("%s:%u:start node\n",__FILE__,__LINE__); + } + + if (node == TRI_EndNodeSkipList(skiplistIndex->skiplist.uniqueSkiplist)) { + printf("%s:%u:end node\n",__FILE__,__LINE__); + } + + if (element == NULL) { + printf("%s:%u:element null\n",__FILE__,__LINE__); + return; + } + + numFields = element->numFields; + shaper = ((TRI_primary_collection_t*)(element->collection))->_shaper; + + for (j = 0; j < numFields; j++) { + printf("%s:%u:!!!:%f:%lu\n",__FILE__,__LINE__, + *((double*)((j + element->fields)->_data.data)), + (long unsigned int)(element->data) ); + } + return; +} +*/ + +static bool skiplistExIndex_findHelperIntervalIntersectionValid(SkiplistExIndex* skiplistExIndex, + TRI_skiplistEx_iterator_interval_t* lInterval, + TRI_skiplistEx_iterator_interval_t* rInterval, + TRI_skiplistEx_iterator_interval_t* interval) { + int compareResult; + TRI_skiplistEx_node_t* lNode; + TRI_skiplistEx_node_t* rNode; + + lNode = (TRI_skiplistEx_node_t*)(lInterval->_leftEndPoint); + rNode = (TRI_skiplistEx_node_t*)(rInterval->_leftEndPoint); + + if (lNode == TRI_EndNodeSkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx) || lNode == NULL || + rNode == TRI_EndNodeSkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx) || rNode == NULL) { + return false; + } + + if (lNode == TRI_StartNodeSkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx)) { + compareResult = -1; + } + else if (rNode == TRI_StartNodeSkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx)) { + compareResult = 1; + } + else { + compareResult = skiplistExIndex->_skiplistEx.uniqueSkiplistEx->compareKeyElement( + skiplistExIndex->_skiplistEx.uniqueSkiplistEx, + &(lNode->_element), &(rNode->_element), 0); + } + + if (compareResult < 1) { + interval->_leftEndPoint = rNode; + } + else { + interval->_leftEndPoint = lNode; + } + + + + lNode = (TRI_skiplistEx_node_t*)(lInterval->_rightEndPoint); + rNode = (TRI_skiplistEx_node_t*)(rInterval->_rightEndPoint); + + if (lNode == TRI_EndNodeSkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx)) { + compareResult = 1; + } + else if (rNode == TRI_EndNodeSkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx)) { + compareResult = -1; + } + else { + compareResult = skiplistExIndex->_skiplistEx.uniqueSkiplistEx->compareKeyElement( + skiplistExIndex->_skiplistEx.uniqueSkiplistEx, + &(lNode->_element), &(rNode->_element), 0); + } + + if (compareResult < 1) { + interval->_rightEndPoint = lNode; + } + else { + interval->_rightEndPoint = rNode; + } + + return skiplistExIndex_findHelperIntervalValid(skiplistExIndex, interval); +} + +static bool skiplistExIndex_findHelperIntervalValid(SkiplistExIndex* skiplistExIndex, TRI_skiplistEx_iterator_interval_t* interval) { + int compareResult; + TRI_skiplistEx_node_t* lNode; + TRI_skiplistEx_node_t* rNode; + + + if ((interval->_leftEndPoint == NULL) || (interval->_rightEndPoint == NULL)) { + return false; + } + + if (interval->_leftEndPoint == interval->_rightEndPoint) { + return false; + } + + if ( (interval->_leftEndPoint == TRI_StartNodeSkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx)) || + (interval->_rightEndPoint == TRI_EndNodeSkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx))) { + return true; + } + + lNode = (TRI_skiplistEx_node_t*)(interval->_leftEndPoint); + rNode = (TRI_skiplistEx_node_t*)(interval->_rightEndPoint); + + + compareResult = skiplistExIndex->_skiplistEx.uniqueSkiplistEx->compareKeyElement( + skiplistExIndex->_skiplistEx.uniqueSkiplistEx, + &(lNode->_element), &(rNode->_element), 0); + return (compareResult == -1); +} + + +static void SkiplistExIndex_findHelper(SkiplistExIndex* skiplistExIndex, + TRI_vector_t* shapeList, + TRI_index_operator_t* indexOperator, + TRI_vector_t* resultIntervalList, + uint64_t thisTransID) { + + SkiplistExIndexElement values; + TRI_vector_t leftResult; + TRI_vector_t rightResult; + TRI_relation_index_operator_t* relationOperator; + TRI_logical_index_operator_t* logicalOperator; + TRI_skiplistEx_iterator_interval_t interval; + TRI_skiplistEx_iterator_interval_t* tempLeftInterval; + TRI_skiplistEx_iterator_interval_t* tempRightInterval; + size_t j; + size_t i; + + TRI_InitVector(&(leftResult), TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_skiplistEx_iterator_interval_t)); + TRI_InitVector(&(rightResult), TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_skiplistEx_iterator_interval_t)); + + relationOperator = (TRI_relation_index_operator_t*)(indexOperator); + logicalOperator = (TRI_logical_index_operator_t*)(indexOperator); + + switch (indexOperator->_type) { + case TRI_EQ_INDEX_OPERATOR: + case TRI_LE_INDEX_OPERATOR: + case TRI_LT_INDEX_OPERATOR: + case TRI_GE_INDEX_OPERATOR: + case TRI_GT_INDEX_OPERATOR: + + values.fields = relationOperator->_fields; + values.numFields = relationOperator->_numFields; + values.collection = relationOperator->_collection; + values.data = 0; // we do not have a document pointer + + default: { + // must not access relationOperator->xxx if the operator is not a relational one + // otherwise we'll get invalid reads and the prog might crash + } + } + + switch (indexOperator->_type) { + + /* + case TRI_SL_OR_OPERATOR: { + SkiplistIndex_findHelper(skiplistIndex,shapeList,logicalOperator->_left,&leftResult); + SkiplistIndex_findHelper(skiplistIndex,shapeList,logicalOperator->_right,&leftResult); + i = 0; + while (i < leftResult._length - 1) { + tempLeftInterval = (TRI_skiplist_iterator_interval_t*) (TRI_AtVector(&leftResult, i)); + tempRightInterval = (TRI_skiplist_iterator_interval_t*) (TRI_AtVector(&leftResult, i + 1)); + // if intervals intersect, optimise and start again + } + assert(0); + } + */ + + case TRI_AND_INDEX_OPERATOR: { + SkiplistExIndex_findHelper(skiplistExIndex,shapeList,logicalOperator->_left,&leftResult, thisTransID); + SkiplistExIndex_findHelper(skiplistExIndex,shapeList,logicalOperator->_right,&rightResult, thisTransID); + + for (i = 0; i < leftResult._length; ++i) { + for (j = 0; j < rightResult._length; ++j) { + tempLeftInterval = (TRI_skiplistEx_iterator_interval_t*) (TRI_AtVector(&leftResult, i)); + tempRightInterval = (TRI_skiplistEx_iterator_interval_t*) (TRI_AtVector(&rightResult, j)); + if (!skiplistExIndex_findHelperIntervalIntersectionValid(skiplistExIndex, tempLeftInterval, + tempRightInterval, &interval)) { + continue; + } + TRI_PushBackVector(resultIntervalList,&interval); + } + } + TRI_DestroyVector(&leftResult); + TRI_DestroyVector(&rightResult); + return; + } + + + case TRI_EQ_INDEX_OPERATOR: { + // ............................................................................ + // The index is constructed from n fields and the client has sent us n values + // ............................................................................ + if (relationOperator->_numFields == shapeList->_length) { + interval._leftEndPoint = TRI_LookupByKeySkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx, &values, thisTransID); + if (interval._leftEndPoint != NULL) { + interval._rightEndPoint = TRI_NextNodeSkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx, interval._leftEndPoint, thisTransID); + interval._leftEndPoint = TRI_PrevNodeSkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx, interval._leftEndPoint, thisTransID); + } + } + // ............................................................................ + // The index is constructed from n fields and the client has sent us m values + // where m < n + // ............................................................................ + else { + interval._leftEndPoint = TRI_LeftLookupByKeySkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx, &values, thisTransID); + interval._rightEndPoint = TRI_RightLookupByKeySkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx, &values, thisTransID); + } + + if (skiplistExIndex_findHelperIntervalValid(skiplistExIndex, &interval)) { + TRI_PushBackVector(resultIntervalList,&interval); + } + return; + } + + case TRI_LE_INDEX_OPERATOR: { + interval._leftEndPoint = TRI_StartNodeSkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx); + interval._rightEndPoint = TRI_RightLookupByKeySkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx, &values, thisTransID); + if (skiplistExIndex_findHelperIntervalValid(skiplistExIndex, &interval)) { + TRI_PushBackVector(resultIntervalList,&interval); + } + return; + } + + + case TRI_LT_INDEX_OPERATOR: { + interval._leftEndPoint = TRI_StartNodeSkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx); + interval._rightEndPoint = TRI_LeftLookupByKeySkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx, &values, thisTransID); + if (interval._rightEndPoint != TRI_EndNodeSkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx)) { + interval._rightEndPoint = TRI_NextNodeSkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx, interval._rightEndPoint, thisTransID); + } + if (skiplistExIndex_findHelperIntervalValid(skiplistExIndex, &interval)) { + TRI_PushBackVector(resultIntervalList, &interval); + } + return; + } + + + case TRI_GE_INDEX_OPERATOR: { + interval._leftEndPoint = TRI_LeftLookupByKeySkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx, &values, thisTransID); + interval._rightEndPoint = TRI_EndNodeSkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx); + if (skiplistExIndex_findHelperIntervalValid(skiplistExIndex, &interval)) { + TRI_PushBackVector(resultIntervalList,&interval); + } + return; + } + + + case TRI_GT_INDEX_OPERATOR: { + interval._leftEndPoint = TRI_RightLookupByKeySkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx, &values, thisTransID); + interval._rightEndPoint = TRI_EndNodeSkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx); + if (interval._leftEndPoint != TRI_StartNodeSkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx)) { + interval._leftEndPoint = TRI_PrevNodeSkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx, interval._leftEndPoint, thisTransID); + } + if (skiplistExIndex_findHelperIntervalValid(skiplistExIndex, &interval)) { + TRI_PushBackVector(resultIntervalList,&interval); + } + return; + } + + default: { + assert(0); + } + + } // end of switch statement + + +} + +TRI_skiplistEx_iterator_t* SkiplistExIndex_find(SkiplistExIndex* skiplistExIndex, TRI_vector_t* shapeList, TRI_index_operator_t* indexOperator, uint64_t thisTransID) { + TRI_skiplistEx_iterator_t* results; + + results = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_skiplistEx_iterator_t), true); + if (results == NULL) { + return NULL; // calling procedure needs to care when the iterator is null + } + results->_index = skiplistExIndex; + TRI_InitVector(&(results->_intervals), TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_skiplistEx_iterator_interval_t)); + results->_currentInterval = 0; + results->_cursor = NULL; + results->_thisTransID = thisTransID; + results->_hasNext = SkiplistExHasNextIterationCallback; + results->_next = SkiplistExNextIterationCallback; + results->_nexts = SkiplistExNextsIterationCallback; + results->_hasPrev = SkiplistExHasPrevIterationCallback; + results->_prev = SkiplistExPrevIterationCallback; + results->_prevs = SkiplistExPrevsIterationCallback; + + SkiplistExIndex_findHelper(skiplistExIndex, shapeList, indexOperator, &(results->_intervals), thisTransID); + + return results; +} + + + +////////////////////////////////////////////////////////////////////////////////// +/// @brief alias for addIndex +////////////////////////////////////////////////////////////////////////////////// + +int SkiplistExIndex_insert(SkiplistExIndex* skiplistExIndex, SkiplistExIndexElement* element, uint64_t thisTransID) { + return SkiplistExIndex_add(skiplistExIndex,element, thisTransID); +} + + + +////////////////////////////////////////////////////////////////////////////////// +/// @brief removes an entry from the skip list +////////////////////////////////////////////////////////////////////////////////// + +int SkiplistExIndex_remove(SkiplistExIndex* skiplistExIndex, SkiplistExIndexElement* element, uint64_t thisTransID) { + int result; + result = TRI_RemoveElementSkipListEx(skiplistExIndex->_skiplistEx.uniqueSkiplistEx, element, NULL, thisTransID); + return result; +} + + + +////////////////////////////////////////////////////////////////////////////////// +/// @brief updates a skiplist entry +////////////////////////////////////////////////////////////////////////////////// + +bool SkiplistExIndex_update(SkiplistExIndex* skiplistExIndex, const SkiplistExIndexElement* beforeElement, const SkiplistExIndexElement* afterElement, uint64_t thisTransID) { + // updates an entry in the skip list, first removes beforeElement, + // then adds the afterElement -- should never be called here + // call SkiplistIndex_remove first and then SkiplistIndex_add + assert(false); + return false; // shuts the VC++ up +} + + + + + + + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// Multi-skiplist non-unique skiplist indexes +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + + +//------------------------------------------------------------------------------ +// Private methods +//------------------------------------------------------------------------------ + + + + + + +//------------------------------------------------------------------------------ +// Public Methods Non-Unique Muilti Skiplists +//------------------------------------------------------------------------------ + + + + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief creates a new non-uniqe (allows duplicates) multi skiplist +//////////////////////////////////////////////////////////////////////////////// + +SkiplistExIndex* MultiSkiplistExIndex_new(TRI_transaction_context_t* transactionContext) { + SkiplistExIndex* skiplistExIndex; + int result; + uint64_t lastKnownTransID = 0; + + skiplistExIndex = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(SkiplistExIndex), true); + if (skiplistExIndex == NULL) { + return NULL; + } + + skiplistExIndex->_unique = false; + skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_skiplistEx_multi_t), true); + if (skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx == NULL) { + TRI_Free(TRI_UNKNOWN_MEM_ZONE, skiplistExIndex); + return NULL; + } + + skiplistExIndex->_transactionContext = transactionContext; + + // TODO ORESTE: determine the last know transaction id from the transaction context + result = TRI_InitSkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx, + sizeof(SkiplistExIndexElement), + NULL, NULL, NULL,TRI_SKIPLIST_EX_PROB_HALF, 40, + lastKnownTransID); + if (result != TRI_ERROR_NO_ERROR) { + TRI_Free(TRI_UNKNOWN_MEM_ZONE, skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx); + TRI_Free(TRI_UNKNOWN_MEM_ZONE, skiplistExIndex); + return NULL; + } + return skiplistExIndex; +} + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief adds (inserts) a data element into a multi skiplist +//////////////////////////////////////////////////////////////////////////////// + + +int MultiSkiplistExIndex_add(SkiplistExIndex* skiplistExIndex, SkiplistExIndexElement* element, uint64_t thisTransID) { + int result; + result = TRI_InsertElementSkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx, element, false, thisTransID); + return result; +} + + + +////////////////////////////////////////////////////////////////////////////////// +/// @brief Locates one or more ranges within the unique skiplist and returns iterator +////////////////////////////////////////////////////////////////////////////////// + +static bool multiSkiplistExIndex_findHelperIntervalIntersectionValid(SkiplistExIndex* skiplistExIndex, + TRI_skiplistEx_iterator_interval_t* lInterval, + TRI_skiplistEx_iterator_interval_t* rInterval, + TRI_skiplistEx_iterator_interval_t* interval) { + int compareResult; + TRI_skiplistEx_node_t* lNode; + TRI_skiplistEx_node_t* rNode; + + lNode = (TRI_skiplistEx_node_t*)(lInterval->_leftEndPoint); + rNode = (TRI_skiplistEx_node_t*)(rInterval->_leftEndPoint); + + if (lNode == TRI_EndNodeSkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx) || lNode == NULL || + rNode == TRI_EndNodeSkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx) || rNode == NULL) { + return false; + } + + if (lNode == TRI_StartNodeSkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx)) { + compareResult = -1; + } + else if (rNode == TRI_StartNodeSkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx)) { + compareResult = 1; + } + else { + compareResult = skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx->compareKeyElement( + skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx, + &(lNode->_element), &(rNode->_element), 0); + } + + if (compareResult < 1) { + interval->_leftEndPoint = rNode; + } + else { + interval->_leftEndPoint = lNode; + } + + + + lNode = (TRI_skiplistEx_node_t*)(lInterval->_rightEndPoint); + rNode = (TRI_skiplistEx_node_t*)(rInterval->_rightEndPoint); + + if (lNode == TRI_EndNodeSkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx)) { + compareResult = 1; + } + else if (rNode == TRI_EndNodeSkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx)) { + compareResult = -1; + } + else { + compareResult = skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx->compareKeyElement( + skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx, + &(lNode->_element), &(rNode->_element), 0); + } + + if (compareResult < 1) { + interval->_rightEndPoint = lNode; + } + else { + interval->_rightEndPoint = rNode; + } + + return multiSkiplistExIndex_findHelperIntervalValid(skiplistExIndex, interval); +} + + + +static bool multiSkiplistExIndex_findHelperIntervalValid(SkiplistExIndex* skiplistExIndex, TRI_skiplistEx_iterator_interval_t* interval) { + int compareResult; + TRI_skiplistEx_node_t* lNode; + TRI_skiplistEx_node_t* rNode; + + + if ((interval->_leftEndPoint == NULL) || (interval->_rightEndPoint == NULL)) { + return 0; + } + + if (interval->_leftEndPoint == interval->_rightEndPoint) { + return 0; + } + + if ( (interval->_leftEndPoint == TRI_StartNodeSkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx)) || + (interval->_rightEndPoint == TRI_EndNodeSkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx))) { + return -1; + } + + lNode = (TRI_skiplistEx_node_t*)(interval->_leftEndPoint); + rNode = (TRI_skiplistEx_node_t*)(interval->_rightEndPoint); + + + compareResult = skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx->compareKeyElement( + skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx, + &(lNode->_element), &(rNode->_element), 0); + + + return (compareResult == -1); +} + + + +static void MultiSkiplistExIndex_findHelper(SkiplistExIndex* skiplistExIndex, + TRI_vector_t* shapeList, + TRI_index_operator_t* indexOperator, + TRI_vector_t* resultIntervalList, + uint64_t thisTransID) { + + SkiplistExIndexElement values; + TRI_vector_t leftResult; + TRI_vector_t rightResult; + TRI_relation_index_operator_t* relationOperator; + TRI_logical_index_operator_t* logicalOperator; + TRI_skiplistEx_iterator_interval_t interval; + TRI_skiplistEx_iterator_interval_t* tempLeftInterval; + TRI_skiplistEx_iterator_interval_t* tempRightInterval; + size_t j; + size_t i; + + TRI_InitVector(&(leftResult), TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_skiplistEx_iterator_interval_t)); + TRI_InitVector(&(rightResult), TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_skiplistEx_iterator_interval_t)); + + logicalOperator = (TRI_logical_index_operator_t*)(indexOperator); + relationOperator = (TRI_relation_index_operator_t*)(indexOperator); + + switch (indexOperator->_type) { + case TRI_EQ_INDEX_OPERATOR: + case TRI_LE_INDEX_OPERATOR: + case TRI_LT_INDEX_OPERATOR: + case TRI_GE_INDEX_OPERATOR: + case TRI_GT_INDEX_OPERATOR: + + values.fields = relationOperator->_fields; + values.numFields = relationOperator->_numFields; + values.collection = relationOperator->_collection; + values.data = 0; // no document pointer available + + default: { + // must not access relationOperator->xxx if the operator is not a relational one + // otherwise we'll get invalid reads and the prog might crash + } + } + + switch (indexOperator->_type) { + + /* + case TRI_SL_OR_OPERATOR: { + todo + } + */ + + case TRI_AND_INDEX_OPERATOR: { + MultiSkiplistExIndex_findHelper(skiplistExIndex,shapeList,logicalOperator->_left,&leftResult, thisTransID); + MultiSkiplistExIndex_findHelper(skiplistExIndex,shapeList,logicalOperator->_right,&rightResult, thisTransID); + + for (i = 0; i < leftResult._length; ++i) { + for (j = 0; j < rightResult._length; ++j) { + tempLeftInterval = (TRI_skiplistEx_iterator_interval_t*) (TRI_AtVector(&leftResult, i)); + tempRightInterval = (TRI_skiplistEx_iterator_interval_t*) (TRI_AtVector(&rightResult, j)); + if (!multiSkiplistExIndex_findHelperIntervalIntersectionValid(skiplistExIndex,tempLeftInterval, + tempRightInterval, &interval)) { + continue; + } + TRI_PushBackVector(resultIntervalList,&interval); + } + } + TRI_DestroyVector(&leftResult); + TRI_DestroyVector(&rightResult); + return; + } + + + case TRI_EQ_INDEX_OPERATOR: { + interval._leftEndPoint = TRI_LeftLookupByKeySkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx, &values, thisTransID); + interval._rightEndPoint = TRI_RightLookupByKeySkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx, &values, thisTransID); + if (multiSkiplistExIndex_findHelperIntervalValid(skiplistExIndex, &interval)) { + TRI_PushBackVector(resultIntervalList, &interval); + } + return; + } + + + case TRI_LE_INDEX_OPERATOR: { + interval._leftEndPoint = TRI_StartNodeSkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx); + interval._rightEndPoint = TRI_RightLookupByKeySkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx, &values, thisTransID); + if (multiSkiplistExIndex_findHelperIntervalValid(skiplistExIndex, &interval)) { + TRI_PushBackVector(resultIntervalList, &interval); + } + return; + } + + + case TRI_LT_INDEX_OPERATOR: { + interval._leftEndPoint = TRI_StartNodeSkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx); + interval._rightEndPoint = TRI_LeftLookupByKeySkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx, &values, thisTransID); + if (interval._rightEndPoint != TRI_EndNodeSkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx)) { + interval._rightEndPoint = TRI_NextNodeSkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx, interval._rightEndPoint, thisTransID); + } + if (multiSkiplistExIndex_findHelperIntervalValid(skiplistExIndex, &interval)) { + TRI_PushBackVector(resultIntervalList,&interval); + } + return; + } + + + case TRI_GE_INDEX_OPERATOR: { + interval._leftEndPoint = TRI_LeftLookupByKeySkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx, &values, thisTransID); + interval._rightEndPoint = TRI_EndNodeSkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx); + if (multiSkiplistExIndex_findHelperIntervalValid(skiplistExIndex, &interval)) { + TRI_PushBackVector(resultIntervalList,&interval); + } + return; + } + + + case TRI_GT_INDEX_OPERATOR: { + interval._leftEndPoint = TRI_RightLookupByKeySkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx, &values, thisTransID); + interval._rightEndPoint = TRI_EndNodeSkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx); + if (interval._leftEndPoint != TRI_StartNodeSkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx)) { + interval._leftEndPoint = TRI_PrevNodeSkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx, interval._leftEndPoint, thisTransID); + } + if (multiSkiplistExIndex_findHelperIntervalValid(skiplistExIndex, &interval)) { + TRI_PushBackVector(resultIntervalList, &interval); + } + return; + } + + default: { + assert(0); + } + + } // end of switch statement +} + + +TRI_skiplistEx_iterator_t* MultiSkiplistExIndex_find(SkiplistExIndex* skiplistExIndex, TRI_vector_t* shapeList, TRI_index_operator_t* indexOperator, uint64_t thisTransID) { + TRI_skiplistEx_iterator_t* results; + + results = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_skiplistEx_iterator_t), false); + if (results == NULL) { + return NULL; + } + + results->_index = skiplistExIndex; + TRI_InitVector(&(results->_intervals), TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_skiplistEx_iterator_interval_t)); + results->_currentInterval = 0; + results->_cursor = NULL; + results->_thisTransID = thisTransID; + results->_hasNext = SkiplistExHasNextIterationCallback; + results->_next = SkiplistExNextIterationCallback; + results->_nexts = SkiplistExNextsIterationCallback; + results->_hasPrev = SkiplistExHasPrevIterationCallback; + results->_prev = SkiplistExPrevIterationCallback; + results->_prevs = SkiplistExPrevsIterationCallback; + + MultiSkiplistExIndex_findHelper(skiplistExIndex, shapeList, indexOperator, &(results->_intervals), thisTransID); + + return results; +} + + + +////////////////////////////////////////////////////////////////////////////////// +/// @brief alias for addIndex +////////////////////////////////////////////////////////////////////////////////// + +int MultiSkiplistExIndex_insert(SkiplistExIndex* skiplistExIndex, SkiplistExIndexElement* element, uint64_t thisTransID) { + return MultiSkiplistExIndex_add(skiplistExIndex, element, thisTransID); +} + + +////////////////////////////////////////////////////////////////////////////////// +/// @brief removes an entry from the skiplist +////////////////////////////////////////////////////////////////////////////////// + +int MultiSkiplistExIndex_remove(SkiplistExIndex* skiplistExIndex, SkiplistExIndexElement* element, uint64_t thisTransID) { + int result; + result = TRI_RemoveElementSkipListExMulti(skiplistExIndex->_skiplistEx.nonUniqueSkiplistEx, element, NULL, thisTransID); + return result; +} + + + +////////////////////////////////////////////////////////////////////////////////// +/// @brief updates and entry in a multi skiplist +////////////////////////////////////////////////////////////////////////////////// + +bool MultiSkiplistExIndex_update(SkiplistExIndex* skiplistExIndex, SkiplistExIndexElement* beforeElement, SkiplistExIndexElement* afterElement, uint64_t thisTransID) { + assert(false); // should never be called directly + return false; // shuts the VC++ up +} + + +//////////////////////////////////////////////////////////////////////////////////////// +// Implementation of forward declared query engine callback functions +//////////////////////////////////////////////////////////////////////////////////////// + +static int SkiplistExIndex_queryMethodCall(void* theIndex, TRI_index_operator_t* indexOperator, + TRI_index_challenge_t* challenge, void* data) { + SkiplistExIndex* slIndex = (SkiplistExIndex*)(theIndex); + if (slIndex == NULL || indexOperator == NULL) { + return TRI_ERROR_INTERNAL; + } + assert(false); + return TRI_ERROR_NO_ERROR; +} + +static TRI_index_iterator_t* SkiplistExIndex_resultMethodCall(void* theIndex, TRI_index_operator_t* indexOperator, + void* data, bool (*filter) (TRI_index_iterator_t*)) { + SkiplistExIndex* slIndex = (SkiplistExIndex*)(theIndex); + if (slIndex == NULL || indexOperator == NULL) { + return NULL; + } + assert(false); + return NULL; +} + +static int SkiplistExIndex_freeMethodCall(void* theIndex, void* data) { + SkiplistExIndex* slIndex = (SkiplistExIndex*)(theIndex); + if (slIndex == NULL) { + return TRI_ERROR_INTERNAL; + } + assert(false); + return TRI_ERROR_NO_ERROR; +} + + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + +// Local Variables: +// mode: outline-minor +// outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)" +// End: + + diff --git a/arangod/SkipListsEx/skiplistExIndex.h b/arangod/SkipListsEx/skiplistExIndex.h new file mode 100644 index 0000000000..b1c3a0f305 --- /dev/null +++ b/arangod/SkipListsEx/skiplistExIndex.h @@ -0,0 +1,208 @@ +//////////////////////////////////////////////////////////////////////////////// +/// @brief unique and non-unique skip lists which support transactions +/// +/// @file +/// +/// DISCLAIMER +/// +/// Copyright by triAGENS GmbH - All rights reserved. +/// +/// The Programs (which include both the software and documentation) +/// contain proprietary information of triAGENS GmbH; they are +/// provided under a license agreement containing restrictions on use and +/// disclosure and are also protected by copyright, patent and other +/// intellectual and industrial property laws. Reverse engineering, +/// disassembly or decompilation of the Programs, except to the extent +/// required to obtain interoperability with other independently created +/// software or as specified by law, is prohibited. +/// +/// The Programs are not intended for use in any nuclear, aviation, mass +/// transit, medical, or other inherently dangerous applications. It shall +/// be the licensee's responsibility to take all appropriate fail-safe, +/// backup, redundancy, and other measures to ensure the safe use of such +/// applications if the Programs are used for such purposes, and triAGENS +/// GmbH disclaims liability for any damages caused by such use of +/// the Programs. +/// +/// This software is the confidential and proprietary information of +/// triAGENS GmbH. You shall not disclose such confidential and +/// proprietary information and shall use it only in accordance with the +/// terms of the license agreement you entered into with triAGENS GmbH. +/// +/// Copyright holder is triAGENS GmbH, Cologne, Germany +/// +/// @author Dr. O +/// @author Copyright 2011, triagens GmbH, Cologne, Germany +//////////////////////////////////////////////////////////////////////////////// + +#ifndef TRIAGENS_DURHAM_VOC_BASE_SKIPLIST_EX_INDEX_H +#define TRIAGENS_DURHAM_VOC_BASE_SKIPLIST_EX_INDEX_H 1 + +#include +#include "SkipListsEx/skiplistEx.h" +#include "IndexIterators/index-iterator.h" +#include "IndexOperators/index-operator.h" +#include "ShapedJson/shaped-json.h" +#include "VocBase/transaction.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// ----------------------------------------------------------------------------- +// --SECTION-- skiplistExIndex public types +// ----------------------------------------------------------------------------- + + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup skiplistExIndex +/// @{ +//////////////////////////////////////////////////////////////////////////////// + + +typedef struct { + union { + TRI_skiplistEx_t* uniqueSkiplistEx; + TRI_skiplistEx_multi_t* nonUniqueSkiplistEx; + } _skiplistEx; + bool _unique; + TRI_transaction_context_t* _transactionContext; +} SkiplistExIndex; + + +typedef struct { + size_t numFields; // the number of fields + TRI_shaped_json_t* fields; // list of shaped json objects which the collection should know about + void* data; // master document pointer + void* collection; // pointer to the collection; +} SkiplistExIndexElement; + +typedef struct { + size_t _numElements; + SkiplistExIndexElement* _elements; // simple list of elements +} SkiplistExIndexElements; + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief Iterator structure for skip list. We require a start and stop node +//////////////////////////////////////////////////////////////////////////////// + +typedef struct TRI_skiplistEx_iterator_interval_s { + void* _leftEndPoint; + void* _rightEndPoint; +} TRI_skiplistEx_iterator_interval_t; + +// ............................................................................ +// The iterator essentially is reading a sequence of documents (which are +// stored in a corresponding sequence of nodes), we require the transaction +// number to which this iterator belongs to, this will ensure that any +// modifications made after this transaction are not 'iterated over' +// ............................................................................ + +typedef struct TRI_skiplistEx_iterator_s { + SkiplistExIndex* _index; + TRI_vector_t _intervals; + size_t _currentInterval; // starts with 0 + void* _cursor; // initially null + uint64_t _thisTransID; // the transaction id to which this iterator belongs to + bool (*_hasNext) (struct TRI_skiplistEx_iterator_s*); + void* (*_next) (struct TRI_skiplistEx_iterator_s*); + void* (*_nexts) (struct TRI_skiplistEx_iterator_s*, int64_t jumpSize); + bool (*_hasPrev) (struct TRI_skiplistEx_iterator_s*); + void* (*_prev) (struct TRI_skiplistEx_iterator_s*); + void* (*_prevs) (struct TRI_skiplistEx_iterator_s*, int64_t jumpSize); +} TRI_skiplistEx_iterator_t; + + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + + + +// ----------------------------------------------------------------------------- +// --SECTION-- skiplistExIndex public methods +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup skiplistExIndex +/// @{ +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @brief Free a skiplistEx iterator +//////////////////////////////////////////////////////////////////////////////// + +void TRI_FreeSkiplistExIterator (TRI_skiplistEx_iterator_t* const); + +//////////////////////////////////////////////////////////////////////////////// +/// @brief destroys a skip list index , but does not free the pointer +//////////////////////////////////////////////////////////////////////////////// + +void SkiplistExIndex_destroy (SkiplistExIndex*); + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief destroys a skip list index and frees the pointer +//////////////////////////////////////////////////////////////////////////////// + +void SkiplistExIndex_free (SkiplistExIndex*); + + + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + +int SkiplistExIndex_assignMethod (void*, TRI_index_method_assignment_type_e); + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// Unique skiplist indexes +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +SkiplistExIndex* SkiplistExIndex_new (TRI_transaction_context_t*); + +int SkiplistExIndex_add (SkiplistExIndex*, SkiplistExIndexElement*, uint64_t); + +TRI_skiplistEx_iterator_t* SkiplistExIndex_find (SkiplistExIndex*, TRI_vector_t*, TRI_index_operator_t*, uint64_t); + +int SkiplistExIndex_insert (SkiplistExIndex*, SkiplistExIndexElement*, uint64_t); + +int SkiplistExIndex_remove (SkiplistExIndex*, SkiplistExIndexElement*, uint64_t); + +bool SkiplistExIndex_update (SkiplistExIndex*, const SkiplistExIndexElement*, const SkiplistExIndexElement*, uint64_t); + + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ +// Multi-skiplist non-unique skiplist indexes +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + + +SkiplistExIndex* MultiSkiplistExIndex_new (TRI_transaction_context_t*); + +int MultiSkiplistExIndex_add (SkiplistExIndex*, SkiplistExIndexElement*, uint64_t); + +TRI_skiplistEx_iterator_t* MultiSkiplistExIndex_find (SkiplistExIndex*, TRI_vector_t*, TRI_index_operator_t*, uint64_t); + +int MultiSkiplistExIndex_insert (SkiplistExIndex*, SkiplistExIndexElement*, uint64_t); + +int MultiSkiplistExIndex_remove (SkiplistExIndex*, SkiplistExIndexElement*, uint64_t); + +bool MultiSkiplistExIndex_update (SkiplistExIndex*, SkiplistExIndexElement*, SkiplistExIndexElement*, uint64_t); + + +#ifdef __cplusplus +} +#endif + +#endif + +// Local Variables: +// mode: outline-minor +// outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)" +// End: + diff --git a/arangod/VocBase/transaction.h b/arangod/VocBase/transaction.h index 69f771c612..c8003e5c2a 100644 --- a/arangod/VocBase/transaction.h +++ b/arangod/VocBase/transaction.h @@ -186,6 +186,9 @@ TRI_transaction_list_t; typedef struct TRI_transaction_context_s { TRI_transaction_id_t _id; // last transaction id assigned + + TRI_read_write_lock_t _rwLock; // lock used to either simulatensously read this structure, + // or uniquely modify this structure #if 0 TRI_mutex_t _lock; // lock used to serialize starting/stopping transactions TRI_mutex_t _collectionLock; // lock used when accessing _collections @@ -193,7 +196,7 @@ typedef struct TRI_transaction_context_s { TRI_transaction_list_t _writeTransactions; // global list of currently ongoing write transactions TRI_associative_pointer_t _collections; // list of collections (TRI_transaction_collection_global_t) #endif - struct TRI_vocbase_s* _vocbase; // pointer to vocbase + struct TRI_vocbase_s* _vocbase; // pointer to vocbase } TRI_transaction_context_t; diff --git a/lib/Basics/Nonce.cpp b/lib/Basics/Nonce.cpp index 7de9f20f3b..c866f23f88 100644 --- a/lib/Basics/Nonce.cpp +++ b/lib/Basics/Nonce.cpp @@ -176,8 +176,8 @@ namespace triagens { for (size_t i = 1; i < N + 1; ++i) { T = T + StatisticsNonces[l2age][i]; coeff = coeff * (N - i + 1) / i; - S0 = S0 * pow(StatisticsNonces[l2age][i] / coeff, (4 * N + 2 - 6 * i) / (N * N - N)); - x = x * pow(StatisticsNonces[l2age][i] / coeff, (12 * i - 6 * N - 6) / (N * N * N - N)); + S0 = S0 * pow((double)(StatisticsNonces[l2age][i] / coeff), (double)((4 * N + 2 - 6 * i) / (N * N - N))); + x = x * pow((double)(StatisticsNonces[l2age][i] / coeff), (double)((12 * i - 6 * N - 6) / (N * N * N - N))); } Statistics current; diff --git a/lib/BasicsC/locks-posix.c b/lib/BasicsC/locks-posix.c index cfe946986b..60eef00e47 100644 --- a/lib/BasicsC/locks-posix.c +++ b/lib/BasicsC/locks-posix.c @@ -551,6 +551,60 @@ void TRI_UnlockCondition (TRI_condition_t* cond) { } } + + + + +// ----------------------------------------------------------------------------- +// COMPARE & SWAP operations below for MAC and GNUC +// ----------------------------------------------------------------------------- + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief atomically compares and swaps 32bit integers +//////////////////////////////////////////////////////////////////////////////// + +bool TRI_CompareAndSwapInteger32 (volatile long* theValue, int32_t oldValue, int32_t newValue) { + #if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1050 + return OSAtomicCompareAndSwap32(oldValue, newValue, theValue); + #elif (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) > 40100 + return __sync_val_compare_and_swap(theValue, oldValue, newValue); + #else + #error No TRI_CompareAndSwapInteger32 implementation defined + #endif +} + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief atomically compares and swaps 64bit integers +//////////////////////////////////////////////////////////////////////////////// + +bool TRI_CompareAndSwapInteger64 (volatile long* theValue, int64_t oldValue, int64_t newValue) { + #if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1050 + return OSAtomicCompareAndSwap64(oldValue, newValue, theValue); + #elif (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) > 40100 + return __sync_val_compare_and_swap(theValue, oldValue, newValue); + #else + #error No TRI_CompareAndSwapInteger64 implementation defined + #endif +} + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief atomically compares and swaps pointers +//////////////////////////////////////////////////////////////////////////////// + +bool TRI_CompareAndSwapPointer(void* volatile* theValue, void* oldValue, void* newValue) { + #if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1050 + return OSAtomicCompareAndSwapPtr(oldValue, newValue, theValue); + #elif (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) > 40100 + return __sync_val_compare_and_swap(theValue, oldValue, newValue); + #else + #error No TRI_CompareAndSwapPointer implementation defined + #endif +} + + //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// diff --git a/lib/BasicsC/locks-win32.c b/lib/BasicsC/locks-win32.c index 5018f7c8e0..2f422fbdea 100644 --- a/lib/BasicsC/locks-win32.c +++ b/lib/BasicsC/locks-win32.c @@ -899,6 +899,40 @@ void TRI_UnlockCondition (TRI_condition_t* cond) { } } + + +// ----------------------------------------------------------------------------- +// COMPARE & SWAP operations below for windows +// ----------------------------------------------------------------------------- + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief atomically compares and swaps 32bit integers +//////////////////////////////////////////////////////////////////////////////// + +bool TRI_CompareAndSwapInteger32 (volatile long* theValue, int32_t oldValue, int32_t newValue) { + return ( InterlockedCompareExchange(theValue, newValue, oldValue) == oldValue ); +} + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief atomically compares and swaps 64bit integers +//////////////////////////////////////////////////////////////////////////////// + +bool TRI_CompareAndSwapInteger64 (volatile long* theValue, int64_t oldValue, int64_t newValue) { + return ( InterlockedCompareExchange64(theValue, newValue, oldValue) == oldValue ); +} + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief atomically compares and swaps pointers +//////////////////////////////////////////////////////////////////////////////// + +bool TRI_CompareAndSwapPointer(void* volatile* theValue, void* oldValue, void* newValue) { + return ( InterlcokedCompareExchangEPointer(theValue, newValue, oldValue) == oldValue ); +} + + //////////////////////////////////////////////////////////////////////////////// /// @} //////////////////////////////////////////////////////////////////////////////// diff --git a/lib/BasicsC/locks.h b/lib/BasicsC/locks.h index d3ab5fefd5..da39b8a797 100644 --- a/lib/BasicsC/locks.h +++ b/lib/BasicsC/locks.h @@ -373,6 +373,68 @@ void TRI_UnlockCondition (TRI_condition_t* cond); /// @} //////////////////////////////////////////////////////////////////////////////// + + + + +// ----------------------------------------------------------------------------- +// --SECTION-- public functions +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @addtogroup CAS operations +/// @{ +//////////////////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief performs an atomic compare and swap operation on a 32bit integer. +//////////////////////////////////////////////////////////////////////////////// + +// ............................................................................. +// The position of 'theValue' must be aligned on a 32 bit boundary. The function +// performs the following atomically: compares the value stored in the position +// pointed to by with the value of . if the value stored +// in position is EQUAL to the value of , then the +// is stored in the position pointed to by (true is +// returned), otherwise no operation is performed (false is returned). +// ............................................................................. + +bool TRI_CompareAndSwapInteger32 (volatile long* theValue, int32_t oldValue, int32_t newValue); + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief performs an atomic compare and swap operation on a 64bit integer. +//////////////////////////////////////////////////////////////////////////////// + +// ............................................................................. +// The position of 'theValue' must be aligned on a 64 bit boundary. This function is +// simply the 64bit equivalent of the function above. +// ............................................................................. + +bool TRI_CompareAndSwapInteger64 (volatile long* theValue, int64_t oldValue, int64_t newValue); + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief performs an atomic compare and swap operation on a pointer. +//////////////////////////////////////////////////////////////////////////////// + +// ............................................................................. +// On a 32bit machine, the position of 'theValue' must be aligned on a 32 bit +// boundary. On a 64bit machine the alignment must be on a 64bit boundary. +// The function performs the following atomically: compares the value stored in +// the position pointed to by with the value of . if the +// value stored in position is EQUAL to the value of , +// then the is stored in the position pointed to by +// (true is returned), otherwise no operation is performed (false is returned). +// ............................................................................. + +bool TRI_CompareAndSwapPointer(void* volatile* theValue, void* oldValue, void* newValue); + +//////////////////////////////////////////////////////////////////////////////// +/// @} +//////////////////////////////////////////////////////////////////////////////// + #ifdef __cplusplus } #endif diff --git a/lib/BasicsC/operating-system.h b/lib/BasicsC/operating-system.h index 1c8e11de08..e1ab25d6ba 100755 --- a/lib/BasicsC/operating-system.h +++ b/lib/BasicsC/operating-system.h @@ -126,6 +126,10 @@ #define TRI_HAVE_GETLINE 1 #endif +#if __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 +#define TRI_HAVE_GETLINE 1 +#endif + #if __WORDSIZE == 64 #define TRI_SIZEOF_SIZE_T 8 #define TRI_ALIGNOF_VOIDP 8 diff --git a/m4/configure.basics b/m4/configure.basics index d85ec04aa2..8bff1826b6 100644 --- a/m4/configure.basics +++ b/m4/configure.basics @@ -41,6 +41,9 @@ esac TRI_BITS="$tr_BITS" AC_SUBST(TRI_BITS) +CFLAGS="${CFLAGS} -DTRI_BITS=${TRI_BITS}" +CXXFLAGS="${CXXFLAGS} -DTRI_BITS=${TRI_BITS}" + dnl ---------------------------------------------------------------------------- dnl use automake to generate Makfile.in dnl ----------------------------------------------------------------------------