1
0
Fork 0

Moved some functionality from skiplist-helper into the skiplist and C++ified it. Not yet done

This commit is contained in:
Michael Hackstein 2015-08-28 15:52:55 +02:00
parent cbd1cd7111
commit cb9e1b0e98
4 changed files with 493 additions and 556 deletions

View File

@ -39,6 +39,177 @@ using namespace triagens::arango;
// --SECTION-- private functions // --SECTION-- private functions
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief frees an element in the skiplist
////////////////////////////////////////////////////////////////////////////////
static void FreeElm (void* e) {
auto element = static_cast<TRI_index_element_t*>(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<triagens::arango::SkiplistIndex2*>(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<SkiplistIndex*>(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, static int FillLookupOperator (TRI_index_operator_t* slOperator,
TRI_document_collection_t* document) { TRI_document_collection_t* document) {
if (slOperator == nullptr) { if (slOperator == nullptr) {
@ -117,6 +288,181 @@ static int FillLookupOperator (TRI_index_operator_t* slOperator,
return TRI_ERROR_NO_ERROR; 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 // --SECTION-- class SkiplistIndex
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -137,11 +483,9 @@ SkiplistIndex2::SkiplistIndex2 (TRI_idx_iid_t iid,
: PathBasedIndex(iid, collection, fields, unique, sparse), : PathBasedIndex(iid, collection, fields, unique, sparse),
_skiplistIndex(nullptr) { _skiplistIndex(nullptr) {
_skiplistIndex = SkiplistIndex_new(collection, _skiplistIndex = new triagens::basics::SkipList(
_paths.size(), CmpElmElm, CmpKeyElm, skiplistIndex,
unique, FreeElm, unique, _useExpansion);
_useExpansion);
if (_skiplistIndex == nullptr) { if (_skiplistIndex == nullptr) {
THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY); THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY);
} }
@ -153,7 +497,7 @@ SkiplistIndex2::SkiplistIndex2 (TRI_idx_iid_t iid,
SkiplistIndex2::~SkiplistIndex2 () { SkiplistIndex2::~SkiplistIndex2 () {
if (_skiplistIndex != nullptr) { if (_skiplistIndex != nullptr) {
SkiplistIndex_free(_skiplistIndex); delete _skiplistIndex;
} }
} }
@ -162,7 +506,12 @@ SkiplistIndex2::~SkiplistIndex2 () {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
size_t SkiplistIndex2::memory () const { size_t SkiplistIndex2::memory () const {
return SkiplistIndex_memoryUsage(_skiplistIndex); return _skiplistIndex->memoryUsage() +
static_cast<size_t>(_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 /// 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) { bool reverse) {
if (slOperator == nullptr) { if (slOperator == nullptr) {
return nullptr; return nullptr;
@ -290,12 +639,31 @@ TRI_skiplist_iterator_t* SkiplistIndex2::lookup (TRI_index_operator_t* slOperato
return nullptr; return nullptr;
} }
std::unique_ptr<SkiplistIterator> results = new SkiplistIterator(_skiplistIndex, reverse);
return SkiplistIndex_find(_skiplistIndex, if (!results) {
slOperator, // Check if we could not get an iterator.
reverse); 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 // --SECTION-- END-OF-FILE
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------

View File

@ -42,9 +42,106 @@
// --SECTION-- class SkiplistIndex // --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 triagens {
namespace arango { 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<TRI_skiplist_index_key_t, TRI_index_element_t> Node;
struct SkiplistIteratorInterval {
Node* _leftEndPoint;
Node* _rightEndPoint;
};
// -----------------------------------------------------------------------------
// --SECTION-- private variables
// -----------------------------------------------------------------------------
triagens::arango::SkiplistIndex2 const* _index;
std::vector<SkiplistIteratorInterval*> _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 { class SkiplistIndex2 : public PathBasedIndex {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -86,6 +183,8 @@ namespace triagens {
int remove (struct TRI_doc_mptr_t const*, bool) override final; 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 /// @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 /// the TRI_index_operator_t* and the TRI_skiplist_iterator_t* results
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
TRI_skiplist_iterator_t* lookup (TRI_index_operator_t*, SkiplistIterator* lookup (TRI_index_operator_t*, bool);
bool);
// -----------------------------------------------------------------------------
// --SECTION-- private methods
// -----------------------------------------------------------------------------
private:
size_t elementSize () const;
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// --SECTION-- private variables // --SECTION-- private variables
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -107,7 +213,7 @@ namespace triagens {
/// @brief the actual skiplist index /// @brief the actual skiplist index
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
SkiplistIndex* _skiplistIndex; triagens::basics::SkipList<TRI_skiplist_index_key_t, TRI_index_element_t>* _skiplistIndex;
}; };

View File

@ -37,429 +37,15 @@
// --SECTION-- private types // --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<TRI_index_element_t const*>(left);
auto rightElement = static_cast<TRI_index_element_t const*>(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<SkiplistIndex*>(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<TRI_skiplist_index_key_t const*>(left);
auto rightElement = static_cast<TRI_index_element_t const*>(right);
TRI_ASSERT(nullptr != left);
TRI_ASSERT(nullptr != right);
SkiplistIndex* skiplistindex = static_cast<SkiplistIndex*>(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<TRI_index_element_t*>(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_skiplist_iterator_interval_t*>(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<TRI_index_element_t*>(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<TRI_index_element_t*>(iterator->_cursor->document());
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// --SECTION-- skiplistIndex common public methods // --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 // 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<SkiplistIndex*>(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 /// @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] // 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) { TRI_skiplist_iterator_interval_t const* interval) {
triagens::basics::SkipListNode* lNode = interval->_leftEndPoint; triagens::basics::SkipListNode* lNode = interval->_leftEndPoint;
@ -581,7 +167,7 @@ static bool skiplistIndex_findHelperIntervalIntersectionValid (
return skiplistIndex_findHelperIntervalValid(skiplistIndex, interval); 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_index_operator_t const* indexOperator,
TRI_vector_t* resultIntervalList) { TRI_vector_t* resultIntervalList) {
TRI_skiplist_index_key_t values; TRI_skiplist_index_key_t values;
@ -723,63 +309,9 @@ static void SkiplistIndex_findHelper (SkiplistIndex* skiplistIndex,
} // end of switch statement } // 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, TRI_index_operator_t const* indexOperator,
bool reverse) { bool reverse) {
auto results = static_cast<TRI_skiplist_iterator_t*>(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_skiplist_iterator_interval_t*>(TRI_AtVector(&results->_intervals, n - 1));
results->_cursor = tmp->_rightEndPoint;
}
else {
// start at first interval, left endpoint
auto tmp = static_cast<TRI_skiplist_iterator_interval_t*>(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<size_t>(skiplistIndex->skiplist->getNrUsed()) * SkiplistIndex_ElementSize(skiplistIndex);
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------

View File

@ -43,91 +43,22 @@
struct TRI_doc_mptr_t; struct TRI_doc_mptr_t;
struct TRI_document_collection_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 // --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 /// @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 /// @brief return the memory size of a skiplist index element
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
inline size_t SkiplistIndex_ElementSize (SkiplistIndex const* idx) { size_t SkiplistIndex_ElementSize (triagens::arango::SkiplistIndex2 const* idx);
return sizeof(TRI_doc_mptr_t*) + (sizeof(TRI_shaped_sub_t) * idx->_numFields);
}
#endif #endif