mirror of https://gitee.com/bigwinds/arangodb
Moved some functionality from skiplist-helper into the skiplist and C++ified it. Not yet done
This commit is contained in:
parent
cbd1cd7111
commit
cb9e1b0e98
|
@ -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
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue