mirror of https://gitee.com/bigwinds/arangodb
387 lines
16 KiB
C++
387 lines
16 KiB
C++
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief generic skip list implementation
|
|
///
|
|
/// @file
|
|
///
|
|
/// DISCLAIMER
|
|
///
|
|
/// Copyright 2014 ArangoDB GmbH, Cologne, Germany
|
|
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
|
|
///
|
|
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|
/// you may not use this file except in compliance with the License.
|
|
/// You may obtain a copy of the License at
|
|
///
|
|
/// http://www.apache.org/licenses/LICENSE-2.0
|
|
///
|
|
/// Unless required by applicable law or agreed to in writing, software
|
|
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
/// See the License for the specific language governing permissions and
|
|
/// limitations under the License.
|
|
///
|
|
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
|
///
|
|
/// @author Max Neunhoeffer
|
|
/// @author Copyright 2014, ArangoDB GmbH, Cologne, Germany
|
|
/// @author Copyright 2013-2013, triAGENS GmbH, Cologne, Germany
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#ifndef ARANGODB_BASICS_C_SKIP__LIST_H
|
|
#define ARANGODB_BASICS_C_SKIP__LIST_H 1
|
|
|
|
#include "Basics/Common.h"
|
|
|
|
// We will probably never see more than 2^48 documents in a skip list
|
|
#define TRI_SKIPLIST_MAX_HEIGHT 48
|
|
|
|
namespace triagens {
|
|
namespace basics {
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- SKIP LIST
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- public types
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief type of a skiplist node
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
class SkipListNode {
|
|
friend class SkipList;
|
|
SkipListNode** _next;
|
|
SkipListNode* _prev;
|
|
void* _doc;
|
|
int _height;
|
|
public:
|
|
void* document () const {
|
|
return _doc;
|
|
}
|
|
SkipListNode* nextNode () const {
|
|
return _next[0];
|
|
}
|
|
// Note that the prevNode of the first data node is the artificial
|
|
// _start node not containing data. This is contrary to the prevNode
|
|
// method of the SkipList class, which returns nullptr in that case.
|
|
SkipListNode* prevNode () const {
|
|
return _prev;
|
|
}
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief two possibilities for comparison, see below
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
enum SkipListCmpType {
|
|
SKIPLIST_CMP_PREORDER,
|
|
SKIPLIST_CMP_TOTORDER
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief type of a function pointer to a comparison function for a skiplist.
|
|
///
|
|
/// The last argument is called preorder. If true then the comparison
|
|
/// function must implement a preorder (reflexive and transitive).
|
|
/// If preorder is false, then a proper total order must be
|
|
/// implemented by the comparison function. The proper total order
|
|
/// must refine the preorder in the sense that a < b in the proper order
|
|
/// implies a <= b in the preorder.
|
|
/// The first argument is a data pointer which may contain arbitrary
|
|
/// infrastructure needed for the comparison. See cmpdata field in the
|
|
/// skiplist type.
|
|
/// The cmp_key_elm variant compares a key with an element using the preorder.
|
|
/// The first argument is a data pointer as above, the second is a pointer
|
|
/// to the key and the third is a pointer to an element.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
typedef int (*SkipListCmpElmElm)(void*, void*, void*, SkipListCmpType);
|
|
typedef int (*SkipListCmpKeyElm)(void*, void*, void*);
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief Type of a pointer to a function that is called whenever a
|
|
/// document is removed from a skiplist.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
typedef void (*SkipListFreeFunc)(void*);
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief type of a skiplist
|
|
/// _end always points to the last node in the skiplist, this can be the
|
|
/// same as the _start node. If a node does not have a successor on a certain
|
|
/// level, then the corresponding _next pointer is a nullptr.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
class SkipList {
|
|
SkipListNode* _start;
|
|
SkipListNode* _end;
|
|
SkipListCmpElmElm _cmp_elm_elm;
|
|
SkipListCmpKeyElm _cmp_key_elm;
|
|
void* _cmpdata; // will be the first argument
|
|
SkipListFreeFunc _free;
|
|
bool _unique; // indicates whether multiple entries that
|
|
// are equal in the preorder are allowed in
|
|
uint64_t _nrUsed;
|
|
bool _isArray; // indicates whether this index is used to
|
|
// index arrays.
|
|
size_t _memoryUsed;
|
|
|
|
public:
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- constructors and destructors
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief creates a new skiplist
|
|
///
|
|
/// Returns nullptr if allocation fails and a pointer to the skiplist
|
|
/// otherwise.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
SkipList (SkipListCmpElmElm cmp_elm_elm,
|
|
SkipListCmpKeyElm cmp_key_elm,
|
|
void* cmpdata,
|
|
SkipListFreeFunc freefunc,
|
|
bool unique,
|
|
bool isArray);
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief frees a skiplist and all its documents
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
~SkipList ();
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- public methods
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief return the start node, note that this does not return the first
|
|
/// data node but the (internal) artificial node stored under _start. This
|
|
/// is consistent behaviour with the leftLookup method given a key value
|
|
/// of -infinity.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
SkipListNode* startNode () const {
|
|
return _start;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief return the end node, note that for formal reasons this always
|
|
/// returns a nullptr, which stands for the first value outside, in analogy
|
|
/// to startNode(). One has to use prevNode(nullptr) to get the last node
|
|
/// containing data.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
SkipListNode* endNode () const {
|
|
return nullptr;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief return the successor node or nullptr if last node
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
SkipListNode* nextNode (SkipListNode* node) {
|
|
return node->_next[0];
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief return the predecessor node or _startNode() if first node,
|
|
/// it is legal to call this with the nullptr to find the last node
|
|
/// containing data, if there is one.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
SkipListNode* prevNode (SkipListNode* node) const {
|
|
return nullptr == node ? _end : node->_prev;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief inserts a new document into a skiplist
|
|
///
|
|
/// Comparison is done using proper order comparison. If the skiplist
|
|
/// is unique then no two documents that compare equal in the preorder
|
|
/// can be inserted. Returns 0 if all is well, -1 if allocation failed
|
|
/// and -2 if the unique constraint would have been violated by the
|
|
/// insert. In the latter two cases nothing is inserted.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
int insert (void* doc);
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief removes a document from a skiplist
|
|
///
|
|
/// Comparison is done using proper order comparison. Returns 0 if all
|
|
/// is well and TRI_ERROR_DOCUMENT_NOT_FOUND if the document was not found.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
int remove (void* doc);
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief returns the number of entries in the skiplist.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
uint64_t getNrUsed () const {
|
|
return _nrUsed;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief returns the memory used by the index
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
size_t memoryUsage () const {
|
|
return _memoryUsed;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief returns if this indexed is used for arrays
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool isArray () const {
|
|
return _isArray;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief looks up doc in the skiplist using the proper order
|
|
/// comparison.
|
|
///
|
|
/// Only comparisons using the proper order are done using cmp_elm_elm.
|
|
/// Returns nullptr if doc is not in the skiplist.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
SkipListNode* lookup (void* doc) const;
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief finds the last document that is less to doc in the preorder
|
|
/// comparison or the start node if none is.
|
|
///
|
|
/// Only comparisons using the preorder are done using cmp_elm_elm.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
SkipListNode* leftLookup (void* doc) const;
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief finds the last document that is less or equal to doc in
|
|
/// the preorder comparison or the start node if none is.
|
|
///
|
|
/// Only comparisons using the preorder are done using cmp_elm_elm.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
SkipListNode* rightLookup (void* doc) const;
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief finds the last document whose key is less to key in the preorder
|
|
/// comparison or the start node if none is.
|
|
///
|
|
/// Only comparisons using the preorder are done using cmp_key_elm.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
SkipListNode* leftKeyLookup (void* key) const;
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief finds the last document that is less or equal to doc in
|
|
/// the preorder comparison or the start node if none is.
|
|
///
|
|
/// Only comparisons using the preorder are done using cmp_key_elm.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
SkipListNode* rightKeyLookup (void* key) const;
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- private methods
|
|
// -----------------------------------------------------------------------------
|
|
|
|
private:
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief allocate a new SkipListNode of a certain height. If height is 0,
|
|
/// then a random height is taken.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
SkipListNode* allocNode (int height);
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
///// @brief Free function for a node.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void freeNode (SkipListNode* node);
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief lookupLess
|
|
/// The following function is the main search engine for our skiplists.
|
|
/// It is used in the insertion and removal functions. See below for
|
|
/// a tiny variation which is used in the right lookup function.
|
|
/// This function does the following:
|
|
/// The skiplist sl is searched for the largest document m that is less
|
|
/// than doc. It uses preorder comparison if cmp is SKIPLIST_CMP_PREORDER
|
|
/// and proper order comparison if cmp is SKIPLIST_CMP_TOTORDER. At the end,
|
|
/// (*pos)[0] points to the node containing m and *next points to the
|
|
/// node following (*pos)[0], or is nullptr if there is no such node. The
|
|
/// array *pos contains for each level lev in 0..sl->start->height-1
|
|
/// at (*pos)[lev] the pointer to the node that contains the largest
|
|
/// document that is less than doc amongst those nodes that have height >
|
|
/// lev.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
int lookupLess (void* doc,
|
|
SkipListNode* (*pos)[TRI_SKIPLIST_MAX_HEIGHT],
|
|
SkipListNode** next,
|
|
SkipListCmpType cmptype) const;
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief lookupLessOrEq
|
|
/// The following function is nearly as LookupScript above, but
|
|
/// finds the largest document m that is less than or equal to doc.
|
|
/// It uses preorder comparison if cmp is SKIPLIST_CMP_PREORDER
|
|
/// and proper order comparison if cmp is SKIPLIST_CMP_TOTORDER. At the end,
|
|
/// (*pos)[0] points to the node containing m and *next points to the
|
|
/// node following (*pos)[0], or is nullptr if there is no such node. The
|
|
/// array *pos contains for each level lev in 0.._start->_height-1
|
|
/// at (*pos)[lev] the pointer to the node that contains the largest
|
|
/// document that is less than or equal to doc amongst those nodes
|
|
/// that have height > lev.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
int lookupLessOrEq (void* doc,
|
|
SkipListNode* (*pos)[TRI_SKIPLIST_MAX_HEIGHT],
|
|
SkipListNode** next,
|
|
SkipListCmpType cmptype) const;
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief lookupKeyLess
|
|
/// We have two more very similar functions which look up documents if
|
|
/// only a key is given. This implies using the cmp_key_elm function
|
|
/// and using the preorder only. Otherwise, they behave identically
|
|
/// as the two previous ones.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
int lookupKeyLess (void* key,
|
|
SkipListNode* (*pos)[TRI_SKIPLIST_MAX_HEIGHT],
|
|
SkipListNode** next) const;
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief lookupKeyLessOrEq
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
int lookupKeyLessOrEq (void* key,
|
|
SkipListNode* (*pos)[TRI_SKIPLIST_MAX_HEIGHT],
|
|
SkipListNode** next) const;
|
|
|
|
}; // struct SkipList
|
|
|
|
} // namespace triagens::basics
|
|
} // namespace triagens
|
|
|
|
#endif
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- END-OF-FILE
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// Local Variables:
|
|
// mode: outline-minor
|
|
// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @page\\|// --SECTION--\\|/// @\\}"
|
|
// End:
|