mirror of https://gitee.com/bigwinds/arangodb
1065 lines
34 KiB
C
1065 lines
34 KiB
C
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief priority queue index
|
|
///
|
|
/// @file
|
|
///
|
|
/// DISCLAIMER
|
|
///
|
|
/// Copyright by triAGENS GmbH - All rights reserved.
|
|
///
|
|
/// The Programs (which include both the software and documentation)
|
|
/// contain proprietary information of triAGENS GmbH; they are
|
|
/// provided under a license agreement containing restrictions on use and
|
|
/// disclosure and are also protected by copyright, patent and other
|
|
/// intellectual and industrial property laws. Reverse engineering,
|
|
/// disassembly or decompilation of the Programs, except to the extent
|
|
/// required to obtain interoperability with other independently created
|
|
/// software or as specified by law, is prohibited.
|
|
///
|
|
/// The Programs are not intended for use in any nuclear, aviation, mass
|
|
/// transit, medical, or other inherently dangerous applications. It shall
|
|
/// be the licensee's responsibility to take all appropriate fail-safe,
|
|
/// backup, redundancy, and other measures to ensure the safe use of such
|
|
/// applications if the Programs are used for such purposes, and triAGENS
|
|
/// GmbH disclaims liability for any damages caused by such use of
|
|
/// the Programs.
|
|
///
|
|
/// This software is the confidential and proprietary information of
|
|
/// triAGENS GmbH. You shall not disclose such confidential and
|
|
/// proprietary information and shall use it only in accordance with the
|
|
/// terms of the license agreement you entered into with triAGENS GmbH.
|
|
///
|
|
/// Copyright holder is triAGENS GmbH, Cologne, Germany
|
|
///
|
|
/// @author Dr. O
|
|
/// @author Copyright 2011, triagens GmbH, Cologne, Germany
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "pqueueindex.h"
|
|
#include "ShapedJson/shaped-json.h"
|
|
#include "ShapedJson/json-shaper.h"
|
|
#include "VocBase/document-collection.h"
|
|
#include <BasicsC/hashes.h>
|
|
#include <BasicsC/logging.h>
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- priority queue index some useful forward declarations
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @addtogroup PriorityQueueIndex
|
|
/// @{
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// callbacks for the priority queue
|
|
static void ClearStoragePQIndex(TRI_pqueue_t*, void*);
|
|
static uint64_t GetStoragePQIndex(TRI_pqueue_t*, void*);
|
|
static bool IsLessPQIndex(TRI_pqueue_t*,void*, void*);
|
|
static void UpdateStoragePQIndex(TRI_pqueue_t*, void*, uint64_t);
|
|
|
|
|
|
// callbacks for the associative array
|
|
static void ClearElementPQIndex(TRI_associative_array_t*, void*);
|
|
static uint64_t HashKeyPQIndex(TRI_associative_array_t*, void*);
|
|
static uint64_t HashElementPQIndex(TRI_associative_array_t*, void*);
|
|
static bool IsEmptyElementPQIndex(TRI_associative_array_t*, void*);
|
|
static bool IsEqualElementElementPQIndex(TRI_associative_array_t*, void*, void*);
|
|
static bool IsEqualKeyElementPQIndex(TRI_associative_array_t*, void*, void*);
|
|
|
|
|
|
// comparison helpers
|
|
static int CompareShapedJsonShapedJson (const TRI_shaped_json_t* left, const TRI_shaped_json_t* right,
|
|
TRI_shaper_t* leftShaper, TRI_shaper_t* rightShaper);
|
|
static int CompareShapeTypes (const TRI_shaped_json_t* left, const TRI_shaped_json_t* right,
|
|
TRI_shaper_t* leftShaper, TRI_shaper_t* rightShaper);
|
|
|
|
// ...............................................................................
|
|
// some internal error numbers which can be mapped later to global error numbers
|
|
// ...............................................................................
|
|
enum {
|
|
PQIndex_InvalidIndexError = -1,
|
|
PQIndex_InvalidElementError = -10,
|
|
|
|
PQIndex_ElementMissingAssociativeArrayError = 1,
|
|
PQIndex_DuplicateElement = 10,
|
|
|
|
PQIndex_RemoveInternalError = 1001
|
|
} PQueueIndexErrors;
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- priority queue index constructors and destructors
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @addtogroup PriorityQueueIndex
|
|
/// @{
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief removes any allocated memory internal to the index structure
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void PQueueIndex_destroy(PQIndex* idx) {
|
|
if (idx == NULL) {
|
|
return;
|
|
}
|
|
TRI_FreePQueue(idx->_pq);
|
|
TRI_FreeAssociativeArray(TRI_UNKNOWN_MEM_ZONE, idx->_aa);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief destroy the index and frees any allocated memory
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void PQueueIndex_free(PQIndex* idx) {
|
|
if (idx == NULL) {
|
|
return;
|
|
}
|
|
PQueueIndex_destroy(idx);
|
|
TRI_Free(TRI_UNKNOWN_MEM_ZONE, idx);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief creates a priority queue index
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
PQIndex* PQueueIndex_new (void) {
|
|
|
|
PQIndex* idx;
|
|
bool ok;
|
|
|
|
// ..........................................................................
|
|
// Allocate the Priority Que Index
|
|
// ..........................................................................
|
|
|
|
idx = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(PQIndex), false);
|
|
|
|
if (idx == NULL) {
|
|
TRI_set_errno(TRI_ERROR_OUT_OF_MEMORY);
|
|
LOG_ERROR("out of memory when creating priority queue index");
|
|
return NULL;
|
|
}
|
|
|
|
|
|
// ..........................................................................
|
|
// Allocate the priority que
|
|
// Remember to add any additional structure you need
|
|
// ..........................................................................
|
|
|
|
idx->_pq = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_pqueue_t) + sizeof(void*), false);
|
|
if (idx->_pq == NULL) {
|
|
TRI_Free(TRI_UNKNOWN_MEM_ZONE, idx);
|
|
TRI_set_errno(TRI_ERROR_OUT_OF_MEMORY);
|
|
LOG_ERROR("out of memory when creating priority queue index");
|
|
return NULL;
|
|
}
|
|
|
|
|
|
// ..........................................................................
|
|
// Allocate the associative array
|
|
// ..........................................................................
|
|
|
|
idx->_aa = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(TRI_associative_array_t), false);
|
|
if (idx->_aa == NULL) {
|
|
TRI_Free(TRI_UNKNOWN_MEM_ZONE, idx);
|
|
TRI_Free(TRI_UNKNOWN_MEM_ZONE, idx->_pq);
|
|
TRI_set_errno(TRI_ERROR_OUT_OF_MEMORY);
|
|
LOG_ERROR("out of memory when creating priority queue index");
|
|
return NULL;
|
|
}
|
|
|
|
|
|
// ..........................................................................
|
|
// Initialise the priority que
|
|
// ..........................................................................
|
|
|
|
ok = TRI_InitPQueue(idx->_pq, 100, sizeof(PQIndexElement), false,
|
|
ClearStoragePQIndex,
|
|
GetStoragePQIndex,
|
|
IsLessPQIndex,
|
|
UpdateStoragePQIndex);
|
|
if (! ok) {
|
|
TRI_Free(TRI_UNKNOWN_MEM_ZONE, idx->_pq);
|
|
TRI_Free(TRI_UNKNOWN_MEM_ZONE, idx->_aa);
|
|
TRI_Free(TRI_UNKNOWN_MEM_ZONE, idx);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
// ..........................................................................
|
|
// Initialise the associative array
|
|
// ..........................................................................
|
|
|
|
TRI_InitAssociativeArray(idx->_aa,
|
|
TRI_UNKNOWN_MEM_ZONE,
|
|
sizeof(PQIndexElement),
|
|
HashKeyPQIndex,
|
|
HashElementPQIndex,
|
|
ClearElementPQIndex,
|
|
IsEmptyElementPQIndex,
|
|
IsEqualKeyElementPQIndex,
|
|
IsEqualElementElementPQIndex);
|
|
|
|
|
|
// ..........................................................................
|
|
// Add the associative array at the end of the pq so that we can access later
|
|
//memcpy((char*)(idx->_pq) + sizeof(TRI_pqueue_t),&(idx->_aa),sizeof(TRI_associative_array_t*));
|
|
// ..........................................................................
|
|
|
|
*((TRI_associative_array_t**)((char*)(idx->_pq) + sizeof(TRI_pqueue_t))) = idx->_aa;
|
|
|
|
|
|
return idx;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- Priority Queue Index public methods
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @addtogroup PriorityQueueIndex
|
|
/// @{
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief inserts an item into a priority queue
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
int PQIndex_add(PQIndex* idx, PQIndexElement* element) {
|
|
|
|
if (idx == NULL) {
|
|
return TRI_ERROR_INTERNAL;
|
|
}
|
|
|
|
if (element == NULL) {
|
|
return TRI_ERROR_INTERNAL;
|
|
}
|
|
|
|
// ...........................................................................
|
|
// Check if item is already added to the associative array
|
|
// ...........................................................................
|
|
|
|
if (TRI_FindByKeyAssociativeArray(idx->_aa, element->data) != NULL) {
|
|
// attempt to add duplicate document to the priority queue
|
|
return TRI_ERROR_ARANGO_INDEX_PQ_INSERT_FAILED;
|
|
}
|
|
|
|
|
|
// ...........................................................................
|
|
// Initialise the priority queue array storage pointer
|
|
// ...........................................................................
|
|
element->pqSlot = 0;
|
|
|
|
// ...........................................................................
|
|
// Add item to associative array
|
|
// ...........................................................................
|
|
if (!TRI_InsertElementAssociativeArray(idx->_aa, element, false)) {
|
|
// can not add item to associative array -- give up on insert
|
|
return TRI_ERROR_ARANGO_INDEX_PQ_INSERT_FAILED;
|
|
}
|
|
|
|
|
|
if (!idx->_pq->add(idx->_pq, element)) {
|
|
TRI_RemoveElementAssociativeArray(idx->_aa, element, NULL);
|
|
// can not add item to priority queue array -- give up on insert
|
|
return TRI_ERROR_ARANGO_INDEX_PQ_INSERT_FAILED;
|
|
}
|
|
|
|
return TRI_ERROR_NO_ERROR;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief inserts an item into a priority queue (same as add method above)
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
int PQIndex_insert(PQIndex* idx, PQIndexElement* element) {
|
|
return PQIndex_add(idx, element);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief removes an item from the priority queue (not necessarily the top most)
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
int PQIndex_remove(PQIndex* idx, PQIndexElement* element) {
|
|
|
|
PQIndexElement* item;
|
|
bool ok;
|
|
|
|
if (idx == NULL) {
|
|
return TRI_ERROR_ARANGO_INDEX_PQ_REMOVE_FAILED;
|
|
}
|
|
|
|
if (element == NULL) {
|
|
return TRI_ERROR_ARANGO_INDEX_PQ_REMOVE_FAILED;
|
|
}
|
|
|
|
// ...........................................................................
|
|
// Check if item exists in the associative array.
|
|
// ...........................................................................
|
|
|
|
item = TRI_FindByKeyAssociativeArray(idx->_aa, element->data);
|
|
if (item == NULL) {
|
|
return TRI_ERROR_ARANGO_INDEX_PQ_REMOVE_ITEM_MISSING;
|
|
}
|
|
|
|
|
|
// ...........................................................................
|
|
// Remove item from the priority queue
|
|
// ...........................................................................
|
|
|
|
ok = idx->_pq->remove(idx->_pq,item->pqSlot, true);
|
|
|
|
|
|
// ...........................................................................
|
|
// Remove item from associative array
|
|
// Must come after remove above, since update storage will be called.
|
|
// ...........................................................................
|
|
|
|
ok = TRI_RemoveElementAssociativeArray(idx->_aa,item,NULL) && ok;
|
|
|
|
if (!ok) {
|
|
return TRI_ERROR_ARANGO_INDEX_PQ_REMOVE_FAILED;
|
|
}
|
|
|
|
return TRI_ERROR_NO_ERROR;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief returns the top most item without removing it from the queue
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
PQIndexElements* PQIndex_top(PQIndex* idx, uint64_t numElements) {
|
|
|
|
PQIndexElements* result;
|
|
PQIndexElements tempResult;
|
|
PQIndexElement* element;
|
|
uint64_t j;
|
|
bool ok;
|
|
uint64_t numCopied;
|
|
|
|
|
|
if (idx == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
if (numElements < 1) {
|
|
return NULL;
|
|
}
|
|
|
|
|
|
// .............................................................................
|
|
// Optimise for the common case where we remove only a single element
|
|
// .............................................................................
|
|
|
|
if (numElements == 1) {
|
|
result = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(PQIndexElements), false);
|
|
if (result == NULL) {
|
|
TRI_set_errno(TRI_ERROR_OUT_OF_MEMORY);
|
|
return NULL;
|
|
}
|
|
result->_elements = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(PQIndexElement) * numElements, false);
|
|
if (result->_elements == NULL) {
|
|
TRI_Free(TRI_UNKNOWN_MEM_ZONE, result);
|
|
TRI_set_errno(TRI_ERROR_OUT_OF_MEMORY);
|
|
return NULL;
|
|
}
|
|
result->_numElements = numElements;
|
|
result->_elements[0] = *((PQIndexElement*)(idx->_pq->top(idx->_pq)));
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
// .............................................................................
|
|
// Two or more elements are 'topped'
|
|
// .............................................................................
|
|
|
|
tempResult._elements = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(PQIndexElement) * numElements, false);
|
|
if (tempResult._elements == NULL) {
|
|
TRI_set_errno(TRI_ERROR_OUT_OF_MEMORY);
|
|
return NULL;
|
|
}
|
|
|
|
ok = true;
|
|
numCopied = 0;
|
|
for (j = 0; j < numElements; ++j) {
|
|
element = (PQIndexElement*)(idx->_pq->top(idx->_pq));
|
|
if (element == NULL) {
|
|
break;
|
|
}
|
|
|
|
|
|
tempResult._elements[j] = *element;
|
|
ok = idx->_pq->remove(idx->_pq,element->pqSlot, false);
|
|
if (!ok) {
|
|
break;
|
|
}
|
|
++numCopied;
|
|
}
|
|
|
|
|
|
result = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(PQIndexElements), false);
|
|
if (result == NULL) {
|
|
TRI_Free(TRI_UNKNOWN_MEM_ZONE, tempResult._elements);
|
|
TRI_set_errno(TRI_ERROR_OUT_OF_MEMORY);
|
|
return NULL;
|
|
}
|
|
|
|
result->_elements = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, sizeof(PQIndexElement) * numCopied, false);
|
|
if (result->_elements == NULL) {
|
|
TRI_Free(TRI_UNKNOWN_MEM_ZONE, tempResult._elements);
|
|
TRI_Free(TRI_UNKNOWN_MEM_ZONE, result);
|
|
TRI_set_errno(TRI_ERROR_OUT_OF_MEMORY);
|
|
return NULL;
|
|
}
|
|
|
|
result->_numElements = numCopied;
|
|
|
|
for (j = 0; j < numCopied; ++j) {
|
|
result->_elements[j] = tempResult._elements[j];
|
|
result->_elements[j].pqSlot = 0;
|
|
idx->_pq->add(idx->_pq, &(result->_elements[j]));
|
|
}
|
|
|
|
TRI_Free(TRI_UNKNOWN_MEM_ZONE, tempResult._elements);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief removes an item and inserts a new item
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
bool PQIndex_update(PQIndex* idx, const PQIndexElement* oldElement, const PQIndexElement* newElement) {
|
|
assert(false);
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- priority queue index implementation of callbacks
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @addtogroup PriorityQueueIndex
|
|
/// @{
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// .............................................................................
|
|
// callbacks for the priority queue
|
|
// .............................................................................
|
|
|
|
|
|
static void ClearStoragePQIndex(TRI_pqueue_t* pq, void* item) {
|
|
PQIndexElement* element;
|
|
|
|
element = (PQIndexElement*)(item);
|
|
if (element == 0) {
|
|
return;
|
|
}
|
|
TRI_Free(TRI_UNKNOWN_MEM_ZONE, element->fields);
|
|
return;
|
|
}
|
|
|
|
|
|
static uint64_t GetStoragePQIndex(TRI_pqueue_t* pq, void* item) {
|
|
PQIndexElement* element;
|
|
|
|
element = (PQIndexElement*)(item);
|
|
if (element == 0) {
|
|
return 0;
|
|
}
|
|
return element->pqSlot;
|
|
}
|
|
|
|
|
|
// .............................................................................
|
|
// True if the leftItem is less than the rightItem
|
|
// .............................................................................
|
|
|
|
static bool IsLessPQIndex(TRI_pqueue_t* pq, void* leftItem, void* rightItem) {
|
|
size_t maxNumFields;
|
|
PQIndexElement* leftElement = (PQIndexElement*)(leftItem);
|
|
PQIndexElement* rightElement = (PQIndexElement*)(rightItem);
|
|
TRI_shaper_t* leftShaper;
|
|
TRI_shaper_t* rightShaper;
|
|
size_t j;
|
|
int compareResult;
|
|
|
|
if (leftElement == NULL && rightElement == NULL) {
|
|
return false;
|
|
}
|
|
|
|
if (leftElement == NULL && rightElement != NULL) {
|
|
return true;
|
|
}
|
|
|
|
if (leftElement != NULL && rightElement == NULL) {
|
|
return false;
|
|
}
|
|
|
|
if (leftElement == rightElement) {
|
|
return false;
|
|
}
|
|
|
|
// ............................................................................
|
|
// The document could be the same -- so no further comparison is required.
|
|
// ............................................................................
|
|
if (leftElement->data == rightElement->data) {
|
|
return false;
|
|
}
|
|
|
|
if (leftElement->numFields < rightElement->numFields) {
|
|
maxNumFields = leftElement->numFields;
|
|
}
|
|
else {
|
|
maxNumFields = rightElement->numFields;
|
|
}
|
|
|
|
leftShaper = ((TRI_doc_collection_t*)(leftElement->collection))->_shaper;
|
|
rightShaper = ((TRI_doc_collection_t*)(rightElement->collection))->_shaper;
|
|
|
|
compareResult = 0;
|
|
|
|
leftShaper->lookupShapeId(leftShaper, (leftElement->fields)->_sid);
|
|
|
|
for (j = 0; j < maxNumFields; j++) {
|
|
/*
|
|
printf("%s:%u:%f:%f,%u:%u\n",__FILE__,__LINE__,
|
|
*((double*)((j + leftElement->fields)->_data.data)),
|
|
*((double*)((j + rightElement->fields)->_data.data)),
|
|
(uint64_t)(leftElement->data),
|
|
(uint64_t)(rightElement->data)
|
|
);
|
|
*/
|
|
compareResult = CompareShapedJsonShapedJson((j + leftElement->fields),
|
|
(j + rightElement->fields),
|
|
leftShaper, rightShaper);
|
|
if (compareResult != 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (compareResult < 0) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
static void UpdateStoragePQIndex(TRI_pqueue_t* pq, void* item, uint64_t position) {
|
|
PQIndexElement* element;
|
|
TRI_associative_array_t* aa;
|
|
|
|
element = (PQIndexElement*)(item);
|
|
if (element == 0) {
|
|
LOG_ERROR("invalid priority queue element received");
|
|
return;
|
|
}
|
|
|
|
element->pqSlot = position;
|
|
|
|
// ...........................................................................
|
|
// Since the items stored in the hash array are pointers, we must update these
|
|
// as well. The associative array is stored at the end of the Priority Queue
|
|
// structure.
|
|
// ...........................................................................
|
|
aa = *((TRI_associative_array_t**)((char*)(pq) + sizeof(TRI_pqueue_t)));
|
|
element = (PQIndexElement*)(TRI_FindByElementAssociativeArray(aa, item));
|
|
if (element == 0) {
|
|
LOG_ERROR("invalid priority queue/ associative array element received");
|
|
return;
|
|
}
|
|
element->pqSlot = position;
|
|
}
|
|
|
|
|
|
// .............................................................................
|
|
// callbacks for the associative array
|
|
// .............................................................................
|
|
|
|
|
|
static void ClearElementPQIndex(TRI_associative_array_t* aa, void* item) {
|
|
if (item == NULL) {
|
|
return;
|
|
}
|
|
memset(item, 0, sizeof(PQIndexElement));
|
|
}
|
|
|
|
|
|
static uint64_t HashKeyPQIndex(TRI_associative_array_t* aa, void* key) {
|
|
uint64_t hash;
|
|
|
|
hash = TRI_FnvHashBlockInitial();
|
|
hash = TRI_FnvHashBlock(hash, key, sizeof(void*));
|
|
|
|
return hash;
|
|
}
|
|
|
|
|
|
static uint64_t HashElementPQIndex(TRI_associative_array_t* aa, void* item) {
|
|
PQIndexElement* element;
|
|
uint64_t hash;
|
|
|
|
element = (PQIndexElement*)(item);
|
|
if (element == 0) {
|
|
return 0;
|
|
}
|
|
|
|
hash = TRI_FnvHashBlockInitial();
|
|
hash = TRI_FnvHashBlock(hash, element->data, sizeof(void*));
|
|
|
|
return hash;
|
|
}
|
|
|
|
|
|
static bool IsEmptyElementPQIndex(TRI_associative_array_t* aa, void* item) {
|
|
PQIndexElement* element;
|
|
|
|
if (item == NULL) {
|
|
// should never happen
|
|
return false;
|
|
}
|
|
|
|
element = (PQIndexElement*)(item);
|
|
if (element->data == NULL) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
static bool IsEqualElementElementPQIndex(TRI_associative_array_t* aa, void* leftItem, void* rightItem) {
|
|
PQIndexElement* leftElement;
|
|
PQIndexElement* rightElement;
|
|
|
|
if (leftItem == NULL || rightItem == NULL) {
|
|
// should never happen
|
|
return false;
|
|
}
|
|
|
|
leftElement = (PQIndexElement*)(leftItem);
|
|
rightElement = (PQIndexElement*)(rightItem);
|
|
if (leftElement->data == rightElement->data) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
static bool IsEqualKeyElementPQIndex(TRI_associative_array_t* aa, void* key, void* item) {
|
|
PQIndexElement* element;
|
|
|
|
if (item == NULL) {
|
|
return false; // should never happen
|
|
}
|
|
element = (PQIndexElement*)(item);
|
|
if (element->data == key) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
// .............................................................................
|
|
// implementation of compare functions
|
|
// .............................................................................
|
|
|
|
static int CompareShapeTypes (const TRI_shaped_json_t* left, const TRI_shaped_json_t* right, TRI_shaper_t* leftShaper, TRI_shaper_t* rightShaper) {
|
|
|
|
int result;
|
|
size_t j;
|
|
TRI_shape_type_t leftType;
|
|
TRI_shape_type_t rightType;
|
|
const TRI_shape_t* leftShape;
|
|
const TRI_shape_t* rightShape;
|
|
size_t leftListLength;
|
|
size_t rightListLength;
|
|
size_t listLength;
|
|
TRI_shaped_json_t leftElement;
|
|
TRI_shaped_json_t rightElement;
|
|
char* leftString;
|
|
char* rightString;
|
|
|
|
|
|
leftShape = leftShaper->lookupShapeId(leftShaper, left->_sid);
|
|
rightShape = rightShaper->lookupShapeId(rightShaper, right->_sid);
|
|
leftType = leftShape->_type;
|
|
rightType = rightShape->_type;
|
|
|
|
switch (leftType) {
|
|
|
|
case TRI_SHAPE_ILLEGAL: {
|
|
switch (rightType) {
|
|
case TRI_SHAPE_ILLEGAL:
|
|
{
|
|
return 0;
|
|
}
|
|
case TRI_SHAPE_NULL:
|
|
case TRI_SHAPE_BOOLEAN:
|
|
case TRI_SHAPE_NUMBER:
|
|
case TRI_SHAPE_SHORT_STRING:
|
|
case TRI_SHAPE_LONG_STRING:
|
|
case TRI_SHAPE_ARRAY:
|
|
case TRI_SHAPE_LIST:
|
|
case TRI_SHAPE_HOMOGENEOUS_LIST:
|
|
case TRI_SHAPE_HOMOGENEOUS_SIZED_LIST:
|
|
{
|
|
return -1;
|
|
}
|
|
} // end of switch (rightType)
|
|
}
|
|
|
|
case TRI_SHAPE_NULL: {
|
|
switch (rightType) {
|
|
case TRI_SHAPE_ILLEGAL:
|
|
{
|
|
return 1;
|
|
}
|
|
case TRI_SHAPE_NULL:
|
|
{
|
|
return 0;
|
|
}
|
|
case TRI_SHAPE_BOOLEAN:
|
|
case TRI_SHAPE_NUMBER:
|
|
case TRI_SHAPE_SHORT_STRING:
|
|
case TRI_SHAPE_LONG_STRING:
|
|
case TRI_SHAPE_ARRAY:
|
|
case TRI_SHAPE_LIST:
|
|
case TRI_SHAPE_HOMOGENEOUS_LIST:
|
|
case TRI_SHAPE_HOMOGENEOUS_SIZED_LIST:
|
|
{
|
|
return -1;
|
|
}
|
|
} // end of switch (rightType)
|
|
}
|
|
|
|
case TRI_SHAPE_BOOLEAN: {
|
|
switch (rightType) {
|
|
case TRI_SHAPE_ILLEGAL:
|
|
case TRI_SHAPE_NULL:
|
|
{
|
|
return 1;
|
|
}
|
|
case TRI_SHAPE_BOOLEAN:
|
|
{
|
|
// check which is false and which is true!
|
|
if ( *((TRI_shape_boolean_t*)(left->_data.data)) == *((TRI_shape_boolean_t*)(right->_data.data)) ) {
|
|
return 0;
|
|
}
|
|
if ( *((TRI_shape_boolean_t*)(left->_data.data)) < *((TRI_shape_boolean_t*)(right->_data.data)) ) {
|
|
return -1;
|
|
}
|
|
return 1;
|
|
}
|
|
case TRI_SHAPE_NUMBER:
|
|
case TRI_SHAPE_SHORT_STRING:
|
|
case TRI_SHAPE_LONG_STRING:
|
|
case TRI_SHAPE_ARRAY:
|
|
case TRI_SHAPE_LIST:
|
|
case TRI_SHAPE_HOMOGENEOUS_LIST:
|
|
case TRI_SHAPE_HOMOGENEOUS_SIZED_LIST:
|
|
{
|
|
return -1;
|
|
}
|
|
} // end of switch (rightType)
|
|
}
|
|
|
|
case TRI_SHAPE_NUMBER: {
|
|
switch (rightType) {
|
|
case TRI_SHAPE_ILLEGAL:
|
|
case TRI_SHAPE_NULL:
|
|
case TRI_SHAPE_BOOLEAN:
|
|
{
|
|
return 1;
|
|
}
|
|
case TRI_SHAPE_NUMBER:
|
|
{
|
|
// compare the numbers.
|
|
if ( *((TRI_shape_number_t*)(left->_data.data)) == *((TRI_shape_number_t*)(right->_data.data)) ) {
|
|
return 0;
|
|
}
|
|
if ( *((TRI_shape_number_t*)(left->_data.data)) < *((TRI_shape_number_t*)(right->_data.data)) ) {
|
|
return -1;
|
|
}
|
|
return 1;
|
|
}
|
|
case TRI_SHAPE_SHORT_STRING:
|
|
case TRI_SHAPE_LONG_STRING:
|
|
case TRI_SHAPE_ARRAY:
|
|
case TRI_SHAPE_LIST:
|
|
case TRI_SHAPE_HOMOGENEOUS_LIST:
|
|
case TRI_SHAPE_HOMOGENEOUS_SIZED_LIST:
|
|
{
|
|
return -1;
|
|
}
|
|
} // end of switch (rightType)
|
|
}
|
|
|
|
case TRI_SHAPE_SHORT_STRING:
|
|
case TRI_SHAPE_LONG_STRING:
|
|
{
|
|
switch (rightType) {
|
|
case TRI_SHAPE_ILLEGAL:
|
|
case TRI_SHAPE_NULL:
|
|
case TRI_SHAPE_BOOLEAN:
|
|
case TRI_SHAPE_NUMBER:
|
|
{
|
|
return 1;
|
|
}
|
|
case TRI_SHAPE_SHORT_STRING:
|
|
case TRI_SHAPE_LONG_STRING:
|
|
{
|
|
// compare strings
|
|
// extract the strings
|
|
if (leftType == TRI_SHAPE_SHORT_STRING) {
|
|
leftString = (char*)(sizeof(TRI_shape_length_short_string_t) + left->_data.data);
|
|
}
|
|
else {
|
|
leftString = (char*)(sizeof(TRI_shape_length_long_string_t) + left->_data.data);
|
|
}
|
|
|
|
if (rightType == TRI_SHAPE_SHORT_STRING) {
|
|
rightString = (char*)(sizeof(TRI_shape_length_short_string_t) + right->_data.data);
|
|
}
|
|
else {
|
|
rightString = (char*)(sizeof(TRI_shape_length_long_string_t) + right->_data.data);
|
|
}
|
|
|
|
result = strcmp(leftString,rightString);
|
|
return result;
|
|
}
|
|
case TRI_SHAPE_ARRAY:
|
|
case TRI_SHAPE_LIST:
|
|
case TRI_SHAPE_HOMOGENEOUS_LIST:
|
|
case TRI_SHAPE_HOMOGENEOUS_SIZED_LIST:
|
|
{
|
|
return -1;
|
|
}
|
|
} // end of switch (rightType)
|
|
}
|
|
|
|
case TRI_SHAPE_HOMOGENEOUS_LIST:
|
|
case TRI_SHAPE_HOMOGENEOUS_SIZED_LIST:
|
|
case TRI_SHAPE_LIST:
|
|
{
|
|
switch (rightType) {
|
|
case TRI_SHAPE_ILLEGAL:
|
|
case TRI_SHAPE_NULL:
|
|
case TRI_SHAPE_BOOLEAN:
|
|
case TRI_SHAPE_NUMBER:
|
|
case TRI_SHAPE_SHORT_STRING:
|
|
case TRI_SHAPE_LONG_STRING:
|
|
{
|
|
return 1;
|
|
}
|
|
case TRI_SHAPE_HOMOGENEOUS_LIST:
|
|
case TRI_SHAPE_HOMOGENEOUS_SIZED_LIST:
|
|
case TRI_SHAPE_LIST:
|
|
{
|
|
// unfortunately recursion: check the types of all the entries
|
|
leftListLength = *((TRI_shape_length_list_t*)(left->_data.data));
|
|
rightListLength = *((TRI_shape_length_list_t*)(right->_data.data));
|
|
|
|
// determine the smallest list
|
|
if (leftListLength > rightListLength) {
|
|
listLength = rightListLength;
|
|
}
|
|
else {
|
|
listLength = leftListLength;
|
|
}
|
|
|
|
for (j = 0; j < listLength; ++j) {
|
|
|
|
if (leftType == TRI_SHAPE_HOMOGENEOUS_LIST) {
|
|
TRI_AtHomogeneousListShapedJson((const TRI_homogeneous_list_shape_t*)(leftShape),
|
|
left,j,&leftElement);
|
|
}
|
|
else if (leftType == TRI_SHAPE_HOMOGENEOUS_SIZED_LIST) {
|
|
TRI_AtHomogeneousSizedListShapedJson((const TRI_homogeneous_sized_list_shape_t*)(leftShape),
|
|
left,j,&leftElement);
|
|
}
|
|
else {
|
|
TRI_AtListShapedJson((const TRI_list_shape_t*)(leftShape),left,j,&leftElement);
|
|
}
|
|
|
|
|
|
if (rightType == TRI_SHAPE_HOMOGENEOUS_LIST) {
|
|
TRI_AtHomogeneousListShapedJson((const TRI_homogeneous_list_shape_t*)(rightShape),
|
|
right,j,&rightElement);
|
|
}
|
|
else if (rightType == TRI_SHAPE_HOMOGENEOUS_SIZED_LIST) {
|
|
TRI_AtHomogeneousSizedListShapedJson((const TRI_homogeneous_sized_list_shape_t*)(rightShape),
|
|
right,j,&rightElement);
|
|
}
|
|
else {
|
|
TRI_AtListShapedJson((const TRI_list_shape_t*)(rightShape),right,j,&rightElement);
|
|
}
|
|
|
|
result = CompareShapeTypes (&leftElement, &rightElement, leftShaper, rightShaper);
|
|
if (result != 0) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
// up to listLength everything matches
|
|
if (leftListLength < rightListLength) {
|
|
return -1;
|
|
}
|
|
else if (leftListLength > rightListLength) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
case TRI_SHAPE_ARRAY:
|
|
{
|
|
return -1;
|
|
}
|
|
} // end of switch (rightType)
|
|
}
|
|
|
|
case TRI_SHAPE_ARRAY:
|
|
{
|
|
/* start oreste:
|
|
char* shape = (char*)(leftShape);
|
|
uint64_t fixedEntries;
|
|
uint64_t variableEntries;
|
|
uint64_t ssid;
|
|
uint64_t aaid;
|
|
char* name;
|
|
TRI_shape_t* newShape;
|
|
|
|
shape = shape + sizeof(TRI_shape_t);
|
|
fixedEntries = *((TRI_shape_size_t*)(shape));
|
|
shape = shape + sizeof(TRI_shape_size_t);
|
|
variableEntries = *((TRI_shape_size_t*)(shape));
|
|
shape = shape + sizeof(TRI_shape_size_t);
|
|
ssid = *((TRI_shape_sid_t*)(shape));
|
|
shape = shape + (sizeof(TRI_shape_sid_t) * (fixedEntries + variableEntries));
|
|
aaid = *((TRI_shape_aid_t*)(shape));
|
|
shape = shape + (sizeof(TRI_shape_aid_t) * (fixedEntries + variableEntries));
|
|
|
|
name = leftShaper->lookupAttributeId(leftShaper,aaid);
|
|
newShape = leftShaper->lookupShapeId(leftShaper, ssid);
|
|
|
|
|
|
printf("%s:%u:_fixedEntries:%u\n",__FILE__,__LINE__,fixedEntries);
|
|
printf("%s:%u:_variableEntries:%u\n",__FILE__,__LINE__,variableEntries);
|
|
printf("%s:%u:_sids[0]:%u\n",__FILE__,__LINE__,ssid);
|
|
printf("%s:%u:_aids[0]:%u\n",__FILE__,__LINE__,aaid);
|
|
printf("%s:%u:name:%s\n",__FILE__,__LINE__,name);
|
|
printf("%s:%u:type:%d\n",__FILE__,__LINE__,newShape->_type);
|
|
|
|
end oreste */
|
|
assert(false);
|
|
switch (rightType) {
|
|
case TRI_SHAPE_ILLEGAL:
|
|
case TRI_SHAPE_NULL:
|
|
case TRI_SHAPE_BOOLEAN:
|
|
case TRI_SHAPE_NUMBER:
|
|
case TRI_SHAPE_SHORT_STRING:
|
|
case TRI_SHAPE_LONG_STRING:
|
|
case TRI_SHAPE_HOMOGENEOUS_LIST:
|
|
case TRI_SHAPE_HOMOGENEOUS_SIZED_LIST:
|
|
case TRI_SHAPE_LIST:
|
|
{
|
|
return 1;
|
|
}
|
|
case TRI_SHAPE_ARRAY:
|
|
{
|
|
assert(false);
|
|
result = 0;
|
|
return result;
|
|
}
|
|
} // end of switch (rightType)
|
|
}
|
|
|
|
}
|
|
assert(false);
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief Compare a shapded json object recursively if necessary
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
static int CompareShapedJsonShapedJson (const TRI_shaped_json_t* left, const TRI_shaped_json_t* right, TRI_shaper_t* leftShaper, TRI_shaper_t* rightShaper) {
|
|
|
|
int result;
|
|
|
|
// ............................................................................
|
|
// the following order is currently defined for placing an order on documents
|
|
// undef < null < boolean < number < strings < lists < hash arrays
|
|
// note: undefined will be treated as NULL pointer not NULL JSON OBJECT
|
|
// within each type class we have the following order
|
|
// boolean: false < true
|
|
// number: natural order
|
|
// strings: lexicographical
|
|
// lists: lexicorgraphically and within each slot according to these rules.
|
|
// ............................................................................
|
|
|
|
|
|
if (left == NULL && right == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
if (left == NULL && right != NULL) {
|
|
return -1;
|
|
}
|
|
|
|
if (left != NULL && right == NULL) {
|
|
return 1;
|
|
}
|
|
|
|
result = CompareShapeTypes (left, right, leftShaper, rightShaper);
|
|
|
|
return result;
|
|
|
|
} // end of function CompareShapedJsonShapedJson
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Local Variables:
|
|
// mode: outline-minor
|
|
// outline-regexp: "^\\(/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|// --SECTION--\\|/// @\\}\\)"
|
|
// End:
|
|
|
|
|