From cb9e1b0e985bf1e162a69312b7bed80146f29250 Mon Sep 17 00:00:00 2001 From: Michael Hackstein Date: Fri, 28 Aug 2015 15:52:55 +0200 Subject: [PATCH] Moved some functionality from skiplist-helper into the skiplist and C++ified it. Not yet done --- arangod/Indexes/SkiplistIndex2.cpp | 390 ++++++++++++++++++++++- arangod/Indexes/SkiplistIndex2.h | 112 ++++++- arangod/Indexes/skiplist-helper.cpp | 474 +--------------------------- arangod/Indexes/skiplist-helper.h | 73 +---- 4 files changed, 493 insertions(+), 556 deletions(-) diff --git a/arangod/Indexes/SkiplistIndex2.cpp b/arangod/Indexes/SkiplistIndex2.cpp index 6be3fe5c84..bf34eda2ef 100644 --- a/arangod/Indexes/SkiplistIndex2.cpp +++ b/arangod/Indexes/SkiplistIndex2.cpp @@ -39,6 +39,177 @@ using namespace triagens::arango; // --SECTION-- private functions // ----------------------------------------------------------------------------- + + + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief frees an element in the skiplist +//////////////////////////////////////////////////////////////////////////////// + +static void FreeElm (void* e) { + auto element = static_cast(e); + TRI_index_element_t::free(element); +} + + +// ............................................................................. +// recall for all of the following comparison functions: +// +// left < right return -1 +// left > right return 1 +// left == right return 0 +// +// furthermore: +// +// 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. +// ........................................................................... + +//////////////////////////////////////////////////////////////////////////////// +/// @brief compares a key with an element, version with proper types +//////////////////////////////////////////////////////////////////////////////// + +static int CompareKeyElement (TRI_shaped_json_t const* left, + TRI_index_element_t const* right, + size_t rightPosition, + VocShaper* shaper) { + TRI_ASSERT(nullptr != left); + TRI_ASSERT(nullptr != right); + + auto rightSubobjects = right->subObjects(); + + return TRI_CompareShapeTypes(nullptr, + nullptr, + left, + shaper, + right->document()->getShapedJsonPtr(), + &rightSubobjects[rightPosition], + nullptr, + shaper); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief compares a key with an element in a skip list, generic callback +//////////////////////////////////////////////////////////////////////////////// + +static int CmpKeyElm (void* sli, + TRI_skiplist_index_key_t const* leftKey, + TRI_index_element_t const* rightElement) { + + TRI_ASSERT(nullptr != left); + TRI_ASSERT(nullptr != right); + + triagens::arango::SkiplistIndex2* skiplistindex = static_cast(sli); + auto shaper = skiplistindex->collection()->getShaper(); // ONLY IN INDEX, PROTECTED by RUNTIME + + // Note that the key might contain fewer fields than there are indexed + // attributes, therefore we only run the following loop to + // leftKey->_numFields. + for (size_t j = 0; j < leftKey->_numFields; j++) { + int compareResult = CompareKeyElement(&leftKey->_fields[j], rightElement, j, shaper); + + if (compareResult != 0) { + return compareResult; + } + } + + return 0; +} + + +//////////////////////////////////////////////////////////////////////////////// +/// @brief compares elements, version with proper types +//////////////////////////////////////////////////////////////////////////////// + +static int CompareElementElement (TRI_index_element_t const* left, + size_t leftPosition, + TRI_index_element_t const* right, + size_t rightPosition, + VocShaper* shaper) { + TRI_ASSERT(nullptr != left); + TRI_ASSERT(nullptr != right); + + auto leftSubobjects = left->subObjects(); + auto rightSubobjects = right->subObjects(); + + return TRI_CompareShapeTypes(left->document()->getShapedJsonPtr(), + &leftSubobjects[leftPosition], + nullptr, + shaper, + right->document()->getShapedJsonPtr(), + &rightSubobjects[rightPosition], + nullptr, + shaper); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief compares two elements in a skip list, this is the generic callback +//////////////////////////////////////////////////////////////////////////////// + +static int CmpElmElm (void* sli, + TRI_index_element_t const* leftElement, + TRI_index_element_t const* rightElement, + triagens::basics::SkipListCmpType cmptype) { + + TRI_ASSERT(nullptr != leftElement); + TRI_ASSERT(nullptr != rightElement); + + // .......................................................................... + // The document could be the same -- so no further comparison is required. + // .......................................................................... + + SkiplistIndex* skiplistindex = static_cast(sli); + + if (leftElement == rightElement || + (! skiplistindex->skiplist->isArray() && leftElement->document() == rightElement->document())) { + return 0; + } + + auto shaper = skiplistindex->_collection->getShaper(); // ONLY IN INDEX, PROTECTED by RUNTIME + for (size_t j = 0; j < skiplistindex->_numFields; j++) { + int compareResult = CompareElementElement(leftElement, + j, + rightElement, + j, + shaper); + + if (compareResult != 0) { + return compareResult; + } + } + + // ........................................................................... + // This is where the difference between the preorder and the proper total + // order 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 0 if we use the preorder and look at the _key attribute + // otherwise. + // ........................................................................... + + if (triagens::basics::SKIPLIST_CMP_PREORDER == cmptype) { + return 0; + } + + // We break this tie in the key comparison by looking at the key: + int compareResult = strcmp(TRI_EXTRACT_MARKER_KEY(leftElement->document()), // ONLY IN INDEX, PROTECTED by RUNTIME + TRI_EXTRACT_MARKER_KEY(rightElement->document())); // ONLY IN INDEX, PROTECTED by RUNTIME + + if (compareResult < 0) { + return -1; + } + else if (compareResult > 0) { + return 1; + } + return 0; +} + static int FillLookupOperator (TRI_index_operator_t* slOperator, TRI_document_collection_t* document) { if (slOperator == nullptr) { @@ -117,6 +288,181 @@ static int FillLookupOperator (TRI_index_operator_t* slOperator, return TRI_ERROR_NO_ERROR; } +// ----------------------------------------------------------------------------- +// --SECTION-- class SkiplistIterator +// ----------------------------------------------------------------------------- + +// ----------------------------------------------------------------------------- +// --SECTION-- public methods +// ----------------------------------------------------------------------------- + +size_t SkiplistIterator::size () { + return _intervals.size(); +} + +void SkiplistIterator::initCursor () { + size_t const n = _intervals.size(); + if (0 < n) { + if (_reverse) { + // start at last interval, right endpoint + _currentInterval = n - 1; + _cursor = _intervals[n -1]->_rightEndPoint; + } + else { + // start at first interval, left endpoint + _currentInterval = 0; + _cursor = _intervals[0]->_leftEndPoint; + } + } + else { + cursor = nullptr; + } +} + +bool SkiplistIterator::hasNext () { + if (_reverse) { + return hasPrevIteration(); + } + return hasNextIteration(); +} + +bool SkiplistIterator::next () { + if (_reverse) { + return prevIteration(); + } + return nextIteration(); +} + +// ----------------------------------------------------------------------------- +// --SECTION-- private methods +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @brief Attempts to determine if there is a previous document within an +/// interval or before it - without advancing the iterator. +//////////////////////////////////////////////////////////////////////////////// + +bool SkiplistIterator::hasPrevIteration () { + // ........................................................................... + // if we have more intervals than the one we are currently working + // on then of course we have a previous doc, because intervals are nonempty. + // ........................................................................... + if (_currentInterval > 0) { + return true; + } + + Node const* leftNode = _index->prevNode(_cursor); + + // Note that leftNode can be nullptr here! + // ........................................................................... + // If the leftNode == left end point AND there are no more intervals + // then we have no next. + // ........................................................................... + return leftNode != _intervals[_currentInterval]->_leftEndPoint; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief Attempts to determine if there is a next document within an +/// interval - without advancing the iterator. +//////////////////////////////////////////////////////////////////////////////// + +bool SkiplistIterator::hasNextIteration () { + if (_cursor == nullptr) { + return false; + } + + // ........................................................................... + // if we have more intervals than the one we are currently working + // on then of course we have a next doc, since intervals are nonempty. + // ........................................................................... + if (_intervals.size() - 1 > _currentInterval) { + return true; + } + + Node const* leftNode = _cursor->nextNode(); + + // Note that leftNode can be nullptr here! + // ........................................................................... + // If the left == right end point AND there are no more intervals then we have + // no next. + // ........................................................................... + return leftNode != _intervals[_currentInterval]->_rightEndPoint; +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief Jumps backwards by jumpSize and returns the document +//////////////////////////////////////////////////////////////////////////////// + +TRI_index_element_t* SkiplistIterator::prevIteration () { + static const int64_t jumpSize = 1; + + TRI_skiplist_iterator_interval_t* interval = _intervals[_currentInterval]; + + if (interval == nullptr) { + return nullptr; + } + + // ........................................................................... + // use the current cursor and move jumpSize backward + // ........................................................................... + + Node* result = nullptr; + + result = _index->prevNode(_cursor); + + if (result == interval->_leftEndPoint) { + if (_currentInterval == 0) { + _cursor = nullptr; // exhausted + return nullptr; + } + --_currentInterval; + interval = _intervals[_currentInterval]; + TRI_ASSERT(interval != nullptr); + _cursor = _rightEndPoint; + result = _index->prevNode(_cursor); + } + _cursor = result; + + TRI_ASSERT(result != nullptr); + return result->document(); +} + +//////////////////////////////////////////////////////////////////////////////// +/// @brief Jumps forwards by jumpSize and returns the document +//////////////////////////////////////////////////////////////////////////////// + +TRI_index_element_t* SkiplistIterator::nextIteration () { + + if (_cursor == nullptr) { + // In this case the iterator is exhausted or does not even have intervals. + return nullptr; + } + + TRI_skiplist_iterator_interval_t* interval = _intervals[_currentInterval]; + + if (interval == nullptr) { + return nullptr; + } + + while (true) { // will be left by break + _cursor = _cursor->nextNode(); + if (_cursor != _rightEndPoint) { + // Note that _cursor can be nullptr here! + break; // we found a next one + } + if (_currentInterval == _intervals.size() - 1) { + _cursor = nullptr; // exhausted + return nullptr; + } + ++_currentInterval; + interval = _intervals[_currentInterval]; + TRI_ASSERT(interval != nullptr); + _cursor = interval->_leftEndPoint; + } + + return _cursor->document(); +} + // ----------------------------------------------------------------------------- // --SECTION-- class SkiplistIndex // ----------------------------------------------------------------------------- @@ -137,11 +483,9 @@ SkiplistIndex2::SkiplistIndex2 (TRI_idx_iid_t iid, : PathBasedIndex(iid, collection, fields, unique, sparse), _skiplistIndex(nullptr) { - _skiplistIndex = SkiplistIndex_new(collection, - _paths.size(), - unique, - _useExpansion); - + _skiplistIndex = new triagens::basics::SkipList( + CmpElmElm, CmpKeyElm, skiplistIndex, + FreeElm, unique, _useExpansion); if (_skiplistIndex == nullptr) { THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY); } @@ -153,7 +497,7 @@ SkiplistIndex2::SkiplistIndex2 (TRI_idx_iid_t iid, SkiplistIndex2::~SkiplistIndex2 () { if (_skiplistIndex != nullptr) { - SkiplistIndex_free(_skiplistIndex); + delete _skiplistIndex; } } @@ -162,7 +506,12 @@ SkiplistIndex2::~SkiplistIndex2 () { // ----------------------------------------------------------------------------- size_t SkiplistIndex2::memory () const { - return SkiplistIndex_memoryUsage(_skiplistIndex); + return _skiplistIndex->memoryUsage() + + static_cast(_skiplistIndex->getNrUsed()) * elementSize(); +} + +size_t SkiplistIndex2::numFields () const { + return _fields.size(); } //////////////////////////////////////////////////////////////////////////////// @@ -271,7 +620,7 @@ int SkiplistIndex2::remove (TRI_doc_mptr_t const* doc, /// the TRI_index_operator_t* and the TRI_skiplist_iterator_t* results //////////////////////////////////////////////////////////////////////////////// -TRI_skiplist_iterator_t* SkiplistIndex2::lookup (TRI_index_operator_t* slOperator, +SkiplistIterator* SkiplistIndex2::lookup (TRI_index_operator_t* slOperator, bool reverse) { if (slOperator == nullptr) { return nullptr; @@ -290,12 +639,31 @@ TRI_skiplist_iterator_t* SkiplistIndex2::lookup (TRI_index_operator_t* slOperato return nullptr; } + std::unique_ptr results = new SkiplistIterator(_skiplistIndex, reverse); - return SkiplistIndex_find(_skiplistIndex, - slOperator, - reverse); + if (!results) { + // Check if we could not get an iterator. + return nullptr; // calling procedure needs to care when the iterator is null + } + + result->findHelper(slOperator, &(results->_intervals)); + + results->initCursor(); + + // Finally initialise _cursor if the result is not empty: + return results; } +// ----------------------------------------------------------------------------- +// --SECTION-- private methods +// ----------------------------------------------------------------------------- + +size_t SkiplistIndex::elementSize () const { + return sizeof(TRI_doc_mptr_t*) + (sizeof(TRI_shaped_sub_t) * numFields()); +} + + + // ----------------------------------------------------------------------------- // --SECTION-- END-OF-FILE // ----------------------------------------------------------------------------- diff --git a/arangod/Indexes/SkiplistIndex2.h b/arangod/Indexes/SkiplistIndex2.h index eaacc66df5..32a429f374 100644 --- a/arangod/Indexes/SkiplistIndex2.h +++ b/arangod/Indexes/SkiplistIndex2.h @@ -42,9 +42,106 @@ // --SECTION-- class SkiplistIndex // ----------------------------------------------------------------------------- +typedef struct { + TRI_shaped_json_t* _fields; // list of shaped json objects which the + // collection should know about + size_t _numFields; // Note that the number of fields coming from + // a query can be smaller than the number of + // fields indexed +} +TRI_skiplist_index_key_t; + namespace triagens { namespace arango { +//////////////////////////////////////////////////////////////////////////////// +/// @brief Iterator structure for skip list. We require a start and stop node +/// +/// Intervals are open in the sense that both end points are not members +/// of the interval. This means that one has to use SkipList::nextNode +/// on the start node to get the first element and that the stop node +/// can be NULL. Note that it is ensured that all intervals in an iterator +/// are non-empty. +//////////////////////////////////////////////////////////////////////////////// + + class SkiplistIterator { + private: + +// ----------------------------------------------------------------------------- +// --SECTION-- private structs +// ----------------------------------------------------------------------------- + // Shorthand for the skiplist node + typedef triagens::basics::SkipListNode Node; + + struct SkiplistIteratorInterval { + Node* _leftEndPoint; + Node* _rightEndPoint; + }; + +// ----------------------------------------------------------------------------- +// --SECTION-- private variables +// ----------------------------------------------------------------------------- + triagens::arango::SkiplistIndex2 const* _index; + std::vector _invervals; + size_t _currentInterval; // starts with 0, current interval used + bool _reverse; + Node* _cursor; + + public: + +// ----------------------------------------------------------------------------- +// --SECTION-- constructors / destructors +// ----------------------------------------------------------------------------- + + SkiplistIterator ( + triagens::arango::SkiplistIndex2 const* idx, + bool reverse + ) : _index(idx) { + _currentInterval = 0; + _cursor = nullptr; + }; + + ~SkiplistIterator () {}; + + // always holds the last node returned, initially equal to + // the _leftEndPoint of the first interval (or the + // _rightEndPoint of the last interval in the reverse + // case), can be nullptr if there are no intervals + // (yet), or, in the reverse case, if the cursor is + // at the end of the last interval. Additionally + // in the non-reverse case _cursor is set to nullptr + // if the cursor is exhausted. + // See SkiplistNextIterationCallback and + // SkiplistPrevIterationCallback for the exact + // condition for the iterator to be exhausted. + +// ----------------------------------------------------------------------------- +// --SECTION-- public methods +// ----------------------------------------------------------------------------- + + size_t size (); + + bool hasNext (); + + TRI_index_element_t* next (); + + void initCursor (); + +// ----------------------------------------------------------------------------- +// --SECTION-- private methods +// ----------------------------------------------------------------------------- + + + private: + bool hasPrevIteration (); + TRI_index_element_t* prevIteration (); + + bool hasNextIteration (); + TRI_index_element_t* nextIteration (); + + + }; + class SkiplistIndex2 : public PathBasedIndex { // ----------------------------------------------------------------------------- @@ -86,6 +183,8 @@ namespace triagens { int remove (struct TRI_doc_mptr_t const*, bool) override final; + size_t numFields () const; + //////////////////////////////////////////////////////////////////////////////// /// @brief attempts to locate an entry in the skip list index /// @@ -94,9 +193,16 @@ namespace triagens { /// the TRI_index_operator_t* and the TRI_skiplist_iterator_t* results //////////////////////////////////////////////////////////////////////////////// - TRI_skiplist_iterator_t* lookup (TRI_index_operator_t*, - bool); + SkiplistIterator* lookup (TRI_index_operator_t*, bool); +// ----------------------------------------------------------------------------- +// --SECTION-- private methods +// ----------------------------------------------------------------------------- + + private: + size_t elementSize () const; + + // ----------------------------------------------------------------------------- // --SECTION-- private variables // ----------------------------------------------------------------------------- @@ -107,7 +213,7 @@ namespace triagens { /// @brief the actual skiplist index //////////////////////////////////////////////////////////////////////////////// - SkiplistIndex* _skiplistIndex; + triagens::basics::SkipList* _skiplistIndex; }; diff --git a/arangod/Indexes/skiplist-helper.cpp b/arangod/Indexes/skiplist-helper.cpp index 4e62c24def..d35cced4b0 100644 --- a/arangod/Indexes/skiplist-helper.cpp +++ b/arangod/Indexes/skiplist-helper.cpp @@ -37,429 +37,15 @@ // --SECTION-- private types // ----------------------------------------------------------------------------- -typedef struct { - TRI_shaped_json_t* _fields; // list of shaped json objects which the - // collection should know about - size_t _numFields; // Note that the number of fields coming from - // a query can be smaller than the number of - // fields indexed -} -TRI_skiplist_index_key_t; - -typedef struct TRI_skiplist_iterator_interval_s { - triagens::basics::SkipListNode* _leftEndPoint; - triagens::basics::SkipListNode* _rightEndPoint; -} -TRI_skiplist_iterator_interval_t; - -// ----------------------------------------------------------------------------- -// --SECTION-- private functions -// ----------------------------------------------------------------------------- - -// ............................................................................. -// recall for all of the following comparison functions: -// -// left < right return -1 -// left > right return 1 -// left == right return 0 -// -// furthermore: -// -// 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. -// ........................................................................... - -//////////////////////////////////////////////////////////////////////////////// -/// @brief compares a key with an element, version with proper types -//////////////////////////////////////////////////////////////////////////////// - -static int CompareKeyElement (TRI_shaped_json_t const* left, - TRI_index_element_t const* right, - size_t rightPosition, - VocShaper* shaper) { - TRI_ASSERT(nullptr != left); - TRI_ASSERT(nullptr != right); - - auto rightSubobjects = right->subObjects(); - - return TRI_CompareShapeTypes(nullptr, - nullptr, - left, - shaper, - right->document()->getShapedJsonPtr(), - &rightSubobjects[rightPosition], - nullptr, - shaper); -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief compares elements, version with proper types -//////////////////////////////////////////////////////////////////////////////// - -static int CompareElementElement (TRI_index_element_t const* left, - size_t leftPosition, - TRI_index_element_t const* right, - size_t rightPosition, - VocShaper* shaper) { - TRI_ASSERT(nullptr != left); - TRI_ASSERT(nullptr != right); - - auto leftSubobjects = left->subObjects(); - auto rightSubobjects = right->subObjects(); - - return TRI_CompareShapeTypes(left->document()->getShapedJsonPtr(), - &leftSubobjects[leftPosition], - nullptr, - shaper, - right->document()->getShapedJsonPtr(), - &rightSubobjects[rightPosition], - nullptr, - shaper); -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief compares two elements in a skip list, this is the generic callback -//////////////////////////////////////////////////////////////////////////////// - -static int CmpElmElm (void* sli, - void* left, - void* right, - triagens::basics::SkipListCmpType cmptype) { - - auto leftElement = static_cast(left); - auto rightElement = static_cast(right); - - TRI_ASSERT(nullptr != left); - TRI_ASSERT(nullptr != right); - - // .......................................................................... - // The document could be the same -- so no further comparison is required. - // .......................................................................... - - SkiplistIndex* skiplistindex = static_cast(sli); - - if (leftElement == rightElement || - (! skiplistindex->skiplist->isArray() && leftElement->document() == rightElement->document())) { - return 0; - } - - auto shaper = skiplistindex->_collection->getShaper(); // ONLY IN INDEX, PROTECTED by RUNTIME - for (size_t j = 0; j < skiplistindex->_numFields; j++) { - int compareResult = CompareElementElement(leftElement, - j, - rightElement, - j, - shaper); - - if (compareResult != 0) { - return compareResult; - } - } - - // ........................................................................... - // This is where the difference between the preorder and the proper total - // order 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 0 if we use the preorder and look at the _key attribute - // otherwise. - // ........................................................................... - - if (triagens::basics::SKIPLIST_CMP_PREORDER == cmptype) { - return 0; - } - - // We break this tie in the key comparison by looking at the key: - int compareResult = strcmp(TRI_EXTRACT_MARKER_KEY(leftElement->document()), // ONLY IN INDEX, PROTECTED by RUNTIME - TRI_EXTRACT_MARKER_KEY(rightElement->document())); // ONLY IN INDEX, PROTECTED by RUNTIME - - if (compareResult < 0) { - return -1; - } - else if (compareResult > 0) { - return 1; - } - return 0; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief compares a key with an element in a skip list, generic callback -//////////////////////////////////////////////////////////////////////////////// - -static int CmpKeyElm (void* sli, - void* left, - void* right) { - auto leftKey = static_cast(left); - auto rightElement = static_cast(right); - - TRI_ASSERT(nullptr != left); - TRI_ASSERT(nullptr != right); - - SkiplistIndex* skiplistindex = static_cast(sli); - auto shaper = skiplistindex->_collection->getShaper(); // ONLY IN INDEX, PROTECTED by RUNTIME - - // Note that the key might contain fewer fields than there are indexed - // attributes, therefore we only run the following loop to - // leftKey->_numFields. - for (size_t j = 0; j < leftKey->_numFields; j++) { - int compareResult = CompareKeyElement(&leftKey->_fields[j], rightElement, j, shaper); - - if (compareResult != 0) { - return compareResult; - } - } - - return 0; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief frees an element in the skiplist -//////////////////////////////////////////////////////////////////////////////// - -static void FreeElm (void* e) { - auto element = static_cast(e); - TRI_index_element_t::free(element); -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief return the current interval that the iterator points at -//////////////////////////////////////////////////////////////////////////////// - -static inline TRI_skiplist_iterator_interval_t* GetInterval (TRI_skiplist_iterator_t const* iterator) { - return static_cast(TRI_AtVector(&iterator->_intervals, iterator->_currentInterval)); -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief Attempts to determine if there is a previous document within an -/// interval or before it - without advancing the iterator. -//////////////////////////////////////////////////////////////////////////////// - -static bool HasPrevIterationCallback (TRI_skiplist_iterator_t const* iterator) { - // Note that iterator->_cursor == nullptr if we are before the largest - // document (i.e. the first one in the iterator)! - if (iterator == nullptr) { - return false; - } - - // ........................................................................... - // if we have more intervals than the one we are currently working - // on then of course we have a previous doc, because intervals are nonempty. - // ........................................................................... - if (iterator->_currentInterval > 0) { - return true; - } - - void const* leftNode - = iterator->_index->skiplist->prevNode(iterator->_cursor); - - // Note that leftNode can be nullptr here! - // ........................................................................... - // If the leftNode == left end point AND there are no more intervals - // then we have no next. - // ........................................................................... - if (leftNode == GetInterval(iterator)->_leftEndPoint) { - return false; - } - - return true; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief Attempts to determine if there is a next document within an -/// interval - without advancing the iterator. -//////////////////////////////////////////////////////////////////////////////// - -static bool HasNextIterationCallback (TRI_skiplist_iterator_t const* iterator) { - if (iterator == nullptr || - iterator->_cursor == nullptr) { - return false; - } - - // ........................................................................... - // if we have more intervals than the one we are currently working - // on then of course we have a next doc, since intervals are nonempty. - // ........................................................................... - if (TRI_LengthVector(&iterator->_intervals) - 1 > iterator->_currentInterval) { - return true; - } - - void const* leftNode = iterator->_cursor->nextNode(); - - // Note that leftNode can be nullptr here! - // ........................................................................... - // If the left == right end point AND there are no more intervals then we have - // no next. - // ........................................................................... - if (leftNode == GetInterval(iterator)->_rightEndPoint) { - return false; - } - - return true; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief Jumps backwards by jumpSize and returns the document -//////////////////////////////////////////////////////////////////////////////// - -static TRI_index_element_t* PrevIterationCallback (TRI_skiplist_iterator_t* iterator) { - static const int64_t jumpSize = 1; - - TRI_ASSERT(jumpSize > 0); - - if (iterator == nullptr) { - // In this case the iterator does not even have intervals. - return nullptr; - } - - TRI_skiplist_iterator_interval_t* interval = GetInterval(iterator); - - if (interval == nullptr) { - return nullptr; - } - - // ........................................................................... - // use the current cursor and move jumpSize backward - // ........................................................................... - - triagens::basics::SkipListNode* result = nullptr; - - for (int64_t j = 0; j < jumpSize; ++j) { - while (true) { // will be left by break - result = iterator->_index->skiplist->prevNode(iterator->_cursor); - - if (result == interval->_leftEndPoint) { - if (iterator->_currentInterval == 0) { - iterator->_cursor = nullptr; // exhausted - return nullptr; - } - --iterator->_currentInterval; - interval = GetInterval(iterator); - TRI_ASSERT(interval != nullptr); - iterator->_cursor = interval->_rightEndPoint; - result = iterator->_index->skiplist->prevNode(iterator->_cursor); - } - - iterator->_cursor = result; - break; // we found a prev one - } - } - - TRI_ASSERT(result != nullptr); - return static_cast(result->document()); //iterator->_cursor->document()); -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief Jumps forwards by jumpSize and returns the document -//////////////////////////////////////////////////////////////////////////////// - -static TRI_index_element_t* NextIterationCallback (TRI_skiplist_iterator_t* iterator) { - static const int64_t jumpSize = 1; - - TRI_ASSERT(jumpSize > 0); - - if (iterator == nullptr || - iterator->_cursor == nullptr) { - // In this case the iterator is exhausted or does not even have intervals. - return nullptr; - } - - TRI_skiplist_iterator_interval_t* interval = GetInterval(iterator); - - if (interval == nullptr) { - return nullptr; - } - - // ........................................................................... - // use the current cursor and move jumpSize forward - // ........................................................................... - - for (int64_t j = 0; j < jumpSize; ++j) { - while (true) { // will be left by break - iterator->_cursor = iterator->_cursor->nextNode(); - if (iterator->_cursor != interval->_rightEndPoint) { - // Note that _cursor can be nullptr here! - break; // we found a next one - } - if (iterator->_currentInterval == (TRI_LengthVector(&iterator->_intervals) - 1)) { - iterator->_cursor = nullptr; // exhausted - return nullptr; - } - ++iterator->_currentInterval; - interval = GetInterval(iterator); - TRI_ASSERT(interval != nullptr); - iterator->_cursor = interval->_leftEndPoint; - } - } - - return static_cast(iterator->_cursor->document()); -} // ----------------------------------------------------------------------------- // --SECTION-- skiplistIndex common public methods // ----------------------------------------------------------------------------- -//////////////////////////////////////////////////////////////////////////////// -/// @brief Free a skiplist iterator -//////////////////////////////////////////////////////////////////////////////// - -void TRI_FreeSkiplistIterator (TRI_skiplist_iterator_t* iterator) { - TRI_ASSERT(nullptr != iterator); - - TRI_DestroyVector(&iterator->_intervals); - TRI_Free(TRI_UNKNOWN_MEM_ZONE, iterator); -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief destroys a skip list index and frees the pointer -//////////////////////////////////////////////////////////////////////////////// - -void SkiplistIndex_free (SkiplistIndex* slIndex) { - if (slIndex == nullptr) { - return; - } - delete slIndex->skiplist; - TRI_Free(TRI_UNKNOWN_MEM_ZONE, slIndex); -} - //------------------------------------------------------------------------------ // Public Methods Skiplist Indices //------------------------------------------------------------------------------ -//////////////////////////////////////////////////////////////////////////////// -/// @brief creates a new skiplist index -//////////////////////////////////////////////////////////////////////////////// - -SkiplistIndex* SkiplistIndex_new (TRI_document_collection_t* document, - size_t numFields, - bool unique, - bool isArray) { - SkiplistIndex* skiplistIndex = static_cast(TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(SkiplistIndex), true)); - - if (skiplistIndex == nullptr) { - return nullptr; - } - - skiplistIndex->_collection = document; - skiplistIndex->_numFields = numFields; - skiplistIndex->unique = unique; - try { - skiplistIndex->skiplist = new triagens::basics::SkipList( - CmpElmElm, CmpKeyElm, skiplistIndex, - FreeElm, unique, isArray); - } - catch (...) { - TRI_Free(TRI_UNKNOWN_MEM_ZONE, skiplistIndex); - return nullptr; - } - - return skiplistIndex; -} - //////////////////////////////////////////////////////////////////////////////// /// @brief Locates one or more ranges within the skiplist and returns iterator //////////////////////////////////////////////////////////////////////////////// @@ -470,7 +56,7 @@ SkiplistIndex* SkiplistIndex_new (TRI_document_collection_t* document, // Tests whether the LeftEndPoint is > than RightEndPoint (1) [undefined] // ............................................................................. -static bool skiplistIndex_findHelperIntervalValid (SkiplistIndex* skiplistIndex, +static bool skiplistIndex_findHelperIntervalValid (triagens::arango::SkiplistIndex2* skiplistIndex, TRI_skiplist_iterator_interval_t const* interval) { triagens::basics::SkipListNode* lNode = interval->_leftEndPoint; @@ -581,7 +167,7 @@ static bool skiplistIndex_findHelperIntervalIntersectionValid ( return skiplistIndex_findHelperIntervalValid(skiplistIndex, interval); } -static void SkiplistIndex_findHelper (SkiplistIndex* skiplistIndex, +static void SkiplistIndex_findHelper (triagens::arango::SkiplistIndex2* skiplistIndex, TRI_index_operator_t const* indexOperator, TRI_vector_t* resultIntervalList) { TRI_skiplist_index_key_t values; @@ -723,63 +309,9 @@ static void SkiplistIndex_findHelper (SkiplistIndex* skiplistIndex, } // end of switch statement } -TRI_skiplist_iterator_t* SkiplistIndex_find (SkiplistIndex* skiplistIndex, +TRI_skiplist_iterator_t* SkiplistIndex_find (triagens::arango::SkiplistIndex2* skiplistIndex, TRI_index_operator_t const* indexOperator, bool reverse) { - auto results = static_cast(TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_skiplist_iterator_t), true)); - - if (results == nullptr) { - return nullptr; // calling procedure needs to care when the iterator is null - } - - results->_index = skiplistIndex; - TRI_InitVector(&(results->_intervals), TRI_UNKNOWN_MEM_ZONE, - sizeof(TRI_skiplist_iterator_interval_t)); - results->_currentInterval = 0; - results->_cursor = nullptr; - - if (reverse) { - // reverse iteration intentionally assigns the reverse traversal - // methods to hasNext() and next() so the interface remains the same - // for the caller! - results->hasNext = HasPrevIterationCallback; - results->next = PrevIterationCallback; - } - else { - results->hasNext = HasNextIterationCallback; - results->next = NextIterationCallback; - } - - SkiplistIndex_findHelper(skiplistIndex, indexOperator, &(results->_intervals)); - - size_t const n = TRI_LengthVector(&results->_intervals); - - // Finally initialise _cursor if the result is not empty: - if (0 < n) { - if (reverse) { - // start at last interval, right endpoint - results->_currentInterval = n - 1; - auto tmp = static_cast(TRI_AtVector(&results->_intervals, n - 1)); - results->_cursor = tmp->_rightEndPoint; - } - else { - // start at first interval, left endpoint - auto tmp = static_cast(TRI_AtVector(&results->_intervals, 0)); - results->_cursor = tmp->_leftEndPoint; - } - } - - return results; -} - -//////////////////////////////////////////////////////////////////////////////// -/// @brief returns the memory used by the index -//////////////////////////////////////////////////////////////////////////////// - -size_t SkiplistIndex_memoryUsage (SkiplistIndex const* skiplistIndex) { - return sizeof(SkiplistIndex) + - skiplistIndex->skiplist->memoryUsage() + - static_cast(skiplistIndex->skiplist->getNrUsed()) * SkiplistIndex_ElementSize(skiplistIndex); } // ----------------------------------------------------------------------------- diff --git a/arangod/Indexes/skiplist-helper.h b/arangod/Indexes/skiplist-helper.h index 7682f4ae46..fc4a9ea501 100644 --- a/arangod/Indexes/skiplist-helper.h +++ b/arangod/Indexes/skiplist-helper.h @@ -43,91 +43,22 @@ struct TRI_doc_mptr_t; struct TRI_document_collection_t; -// ----------------------------------------------------------------------------- -// --SECTION-- skiplistIndex public types -// ----------------------------------------------------------------------------- - -typedef struct { - triagens::basics::SkipList* skiplist; - bool unique; - struct TRI_document_collection_t* _collection; - size_t _numFields; -} -SkiplistIndex; - -//////////////////////////////////////////////////////////////////////////////// -/// @brief Iterator structure for skip list. We require a start and stop node -/// -/// Intervals are open in the sense that both end points are not members -/// of the interval. This means that one has to use SkipList::nextNode -/// on the start node to get the first element and that the stop node -/// can be NULL. Note that it is ensured that all intervals in an iterator -/// are non-empty. -//////////////////////////////////////////////////////////////////////////////// - -typedef struct TRI_skiplist_iterator_s { - SkiplistIndex* _index; - TRI_vector_t _intervals; - size_t _currentInterval; // starts with 0, current interval used - triagens::basics::SkipListNode* _cursor; - // always holds the last node returned, initially equal to - // the _leftEndPoint of the first interval (or the - // _rightEndPoint of the last interval in the reverse - // case), can be nullptr if there are no intervals - // (yet), or, in the reverse case, if the cursor is - // at the end of the last interval. Additionally - // in the non-reverse case _cursor is set to nullptr - // if the cursor is exhausted. - // See SkiplistNextIterationCallback and - // SkiplistPrevIterationCallback for the exact - // condition for the iterator to be exhausted. - bool (*hasNext) (struct TRI_skiplist_iterator_s const*); - TRI_index_element_t* (*next)(struct TRI_skiplist_iterator_s*); -} -TRI_skiplist_iterator_t; // ----------------------------------------------------------------------------- // --SECTION-- skiplistIndex public methods // ----------------------------------------------------------------------------- -//////////////////////////////////////////////////////////////////////////////// -/// @brief Free a skiplist iterator -//////////////////////////////////////////////////////////////////////////////// - -void TRI_FreeSkiplistIterator (TRI_skiplist_iterator_t* const); - -//////////////////////////////////////////////////////////////////////////////// -/// @brief destroys a skip list index and frees the pointer -//////////////////////////////////////////////////////////////////////////////// - -void SkiplistIndex_free (SkiplistIndex*); - -//------------------------------------------------------------------------------ -//------------------------------------------------------------------------------ -// Skiplist indices, both unique and non-unique -//------------------------------------------------------------------------------ -//------------------------------------------------------------------------------ - -SkiplistIndex* SkiplistIndex_new (struct TRI_document_collection_t*, - size_t, bool, bool); - -TRI_skiplist_iterator_t* SkiplistIndex_find (SkiplistIndex*, - TRI_index_operator_t const*, - bool); - //////////////////////////////////////////////////////////////////////////////// /// @brief return the memory used by the index //////////////////////////////////////////////////////////////////////////////// -size_t SkiplistIndex_memoryUsage (SkiplistIndex const*); +size_t SkiplistIndex_memoryUsage (triagens::arango::SkiplistIndex2 const*); //////////////////////////////////////////////////////////////////////////////// /// @brief return the memory size of a skiplist index element //////////////////////////////////////////////////////////////////////////////// -inline size_t SkiplistIndex_ElementSize (SkiplistIndex const* idx) { - return sizeof(TRI_doc_mptr_t*) + (sizeof(TRI_shaped_sub_t) * idx->_numFields); -} +size_t SkiplistIndex_ElementSize (triagens::arango::SkiplistIndex2 const* idx); #endif