1
0
Fork 0
arangodb/arangod/HashIndex/hash-array.c

1204 lines
41 KiB
C

////////////////////////////////////////////////////////////////////////////////
/// @brief hash array implementation
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2004-2013 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 triAGENS GmbH, Cologne, Germany
///
/// @author Dr. Frank Celler
/// @author Dr. Oreste Costa-Panaia
/// @author Martin Schoenert
/// @author Copyright 2004-2013, triAGENS GmbH, Cologne, Germany
////////////////////////////////////////////////////////////////////////////////
#include "hash-array.h"
#include "BasicsC/hashes.h"
#include "HashIndex/hash-index.h"
#include "VocBase/document-collection.h"
// -----------------------------------------------------------------------------
// --SECTION-- COMPARISON
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup HashArray
/// @{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief returns true if an element is 'empty'
////////////////////////////////////////////////////////////////////////////////
static bool IsEmptyElement (TRI_hash_array_t* array,
TRI_hash_index_element_t* element) {
if (element != NULL) {
if (element->_document == NULL) {
return true;
}
}
return false;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief marks an element in the hash array as being 'cleared' or 'empty'
////////////////////////////////////////////////////////////////////////////////
static void ClearElement (TRI_hash_array_t* array,
TRI_hash_index_element_t* element) {
if (element != NULL) {
element->_document = NULL;
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief destroys an element, removing any allocated memory
////////////////////////////////////////////////////////////////////////////////
static void DestroyElement (TRI_hash_array_t* array,
TRI_hash_index_element_t* element) {
if (element != NULL) {
if (element->_document != NULL) {
TRI_Free(TRI_UNKNOWN_MEM_ZONE, element->_subObjects);
}
}
ClearElement(array, element);
}
////////////////////////////////////////////////////////////////////////////////
/// @brief determines if two elements are equal
///
/// Two elements are 'equal' if the document pointer is the same.
////////////////////////////////////////////////////////////////////////////////
static bool IsEqualElementElement (TRI_hash_array_t* array,
TRI_hash_index_element_t* left,
TRI_hash_index_element_t* right) {
if (left->_document == NULL || right->_document == NULL) {
return false;
}
return left->_document == right->_document;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief determines if a key corresponds to an element
////////////////////////////////////////////////////////////////////////////////
static bool IsEqualKeyElement (TRI_hash_array_t* array,
TRI_index_search_value_t* left,
TRI_hash_index_element_t* right) {
size_t j;
if (right->_document == NULL) {
return false;
}
for (j = 0; j < array->_numFields; ++j) {
TRI_shaped_json_t* leftJson = &left->_values[j];
TRI_shaped_sub_t* rightSub = &right->_subObjects[j];
size_t length;
if (leftJson->_sid != rightSub->_sid) {
return false;
}
length = leftJson->_data.length;
if (length != rightSub->_length) {
return false;
}
if (0 < length) {
char* ptr = ((char*) right->_document->_data) + rightSub->_offset;
if (memcmp(leftJson->_data.data, ptr, length) != 0) {
return false;
}
}
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief given a key generates a hash integer
////////////////////////////////////////////////////////////////////////////////
static uint64_t HashKey (TRI_hash_array_t* array,
TRI_index_search_value_t* key) {
uint64_t hash = TRI_FnvHashBlockInitial();
size_t j;
for (j = 0; j < array->_numFields; ++j) {
// ignore the sid for hashing
hash = TRI_FnvHashBlock(hash, key->_values[j]._data.data, key->_values[j]._data.length);
}
return hash;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief given an element generates a hash integer
////////////////////////////////////////////////////////////////////////////////
static uint64_t HashElement (TRI_hash_array_t* array,
TRI_hash_index_element_t* element) {
uint64_t hash = TRI_FnvHashBlockInitial();
char* ptr;
size_t j;
for (j = 0; j < array->_numFields; j++) {
// ignore the sid for hashing
ptr = ((char*) element->_document->_data) + element->_subObjects[j]._offset;
// only hash the data block
hash = TRI_FnvHashBlock(hash, ptr, element->_subObjects[j]._length);
}
return hash;
}
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// --SECTION-- HASH ARRAY
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// --SECTION-- private defines
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup HashArray
/// @{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief initial preallocation size of the hash table when the table is
/// first created
/// setting this to a high value will waste memory but reduce the number of
/// reallocations/repositionings necessary when the table grows
////////////////////////////////////////////////////////////////////////////////
#define INITIAL_SIZE (256)
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// --SECTION-- private functions
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup HashArray
/// @{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief adds a new element
////////////////////////////////////////////////////////////////////////////////
static void AddNewElement (TRI_hash_array_t* array,
TRI_hash_index_element_t* element) {
uint64_t hash;
uint64_t i;
// ...........................................................................
// compute the hash
// ...........................................................................
hash = HashElement(array, element);
// ...........................................................................
// search the table
// ...........................................................................
i = hash % array->_nrAlloc;
while (! IsEmptyElement(array, &array->_table[i])) {
i = (i + 1) % array->_nrAlloc;
}
// ...........................................................................
// add a new element to the associative array
// memcpy ok here since are simply moving array items internally
// ...........................................................................
memcpy(&array->_table[i], element, sizeof(TRI_hash_index_element_t));
array->_nrUsed++;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief allocate memory for the hash table
///
/// the hash table memory will be aligned on a cache line boundary
////////////////////////////////////////////////////////////////////////////////
static int AllocateTable (TRI_hash_array_t* array, size_t numElements) {
TRI_hash_index_element_t* table;
table = TRI_Allocate(TRI_UNKNOWN_MEM_ZONE,
sizeof(TRI_hash_index_element_t) * numElements,
true);
if (table == NULL) {
return TRI_ERROR_OUT_OF_MEMORY;
}
array->_table = table;
array->_nrAlloc = numElements;
return TRI_ERROR_NO_ERROR;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief resizes the array
////////////////////////////////////////////////////////////////////////////////
static int ResizeHashArray (TRI_hash_array_t* array) {
TRI_hash_index_element_t* oldTable;
uint64_t oldAlloc;
uint64_t j;
int res;
oldTable = array->_table;
oldAlloc = array->_nrAlloc;
res = AllocateTable(array, (size_t) (2 * oldAlloc + 1));
if (res != TRI_ERROR_NO_ERROR) {
return res;
}
array->_nrUsed = 0;
for (j = 0; j < oldAlloc; j++) {
if (! IsEmptyElement(array, &oldTable[j])) {
AddNewElement(array, &oldTable[j]);
}
}
TRI_Free(TRI_UNKNOWN_MEM_ZONE, oldTable);
return TRI_ERROR_NO_ERROR;
}
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// --SECTION-- constructors and destructors
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup HashArray
/// @{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief initialises an array
////////////////////////////////////////////////////////////////////////////////
int TRI_InitHashArray (TRI_hash_array_t* array,
size_t initialDocumentCount,
size_t numFields) {
size_t initialSize;
int res;
// ...........................................................................
// Assign the callback functions
// ...........................................................................
assert(numFields > 0);
array->_numFields = numFields;
array->_table = NULL;
array->_nrUsed = 0;
array->_nrAlloc = 0;
if (initialDocumentCount > 0) {
// use initial document count provided as initial size
initialSize = (size_t) (2.5 * initialDocumentCount);
}
else {
initialSize = INITIAL_SIZE;
}
res = AllocateTable(array, initialSize);
if (res != TRI_ERROR_NO_ERROR) {
return res;
}
// ...........................................................................
// allocate storage for the hash array
// ...........................................................................
array->_nrUsed = 0;
return TRI_ERROR_NO_ERROR;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief destroys an array, but does not free the pointer
////////////////////////////////////////////////////////////////////////////////
void TRI_DestroyHashArray (TRI_hash_array_t* array) {
if (array == NULL) {
return;
}
// ...........................................................................
// Go through each item in the array and remove any internal allocated memory
// ...........................................................................
// array->_table might be NULL if array initialisation fails
if (array->_table != NULL) {
TRI_hash_index_element_t* p;
TRI_hash_index_element_t* e;
p = array->_table;
e = p + array->_nrAlloc;
for (; p < e; ++p) {
DestroyElement(array, p);
}
TRI_Free(TRI_UNKNOWN_MEM_ZONE, array->_table);
}
}
////////////////////////////////////////////////////////////////////////////////
/// @brief destroys an array and frees the pointer
////////////////////////////////////////////////////////////////////////////////
void TRI_FreeHashArray (TRI_hash_array_t* array) {
if (array != NULL) {
TRI_DestroyHashArray(array);
TRI_Free(TRI_UNKNOWN_MEM_ZONE, array);
}
}
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// --SECTION-- public functions
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup Collections
/// @{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief lookups an element given a key
////////////////////////////////////////////////////////////////////////////////
TRI_hash_index_element_t* TRI_LookupByKeyHashArray (TRI_hash_array_t* array,
TRI_index_search_value_t* key) {
uint64_t hash;
uint64_t i;
// ...........................................................................
// compute the hash
// ...........................................................................
hash = HashKey(array, key);
i = hash % array->_nrAlloc;
// ...........................................................................
// search the table
// ...........................................................................
while (! IsEmptyElement(array, &array->_table[i])
&& ! IsEqualKeyElement(array, key, &array->_table[i])) {
i = (i + 1) % array->_nrAlloc;
}
// ...........................................................................
// return whatever we found
// ...........................................................................
return &array->_table[i];
}
////////////////////////////////////////////////////////////////////////////////
/// @brief finds an element given a key, return NULL if not found
////////////////////////////////////////////////////////////////////////////////
TRI_hash_index_element_t* TRI_FindByKeyHashArray (TRI_hash_array_t* array,
TRI_index_search_value_t* key) {
TRI_hash_index_element_t* element;
element = TRI_LookupByKeyHashArray(array, key);
if (! IsEmptyElement(array, element) && IsEqualKeyElement(array, key, element)) {
return element;
}
return NULL;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief lookups an element given an element
////////////////////////////////////////////////////////////////////////////////
TRI_hash_index_element_t* TRI_LookupByElementHashArray (TRI_hash_array_t* array,
TRI_hash_index_element_t* element) {
uint64_t hash;
uint64_t i;
// ...........................................................................
// compute the hash
// ...........................................................................
hash = HashElement(array, element);
i = hash % array->_nrAlloc;
// ...........................................................................
// search the table
// ...........................................................................
while (! IsEmptyElement(array, &array->_table[i])
&& ! IsEqualElementElement(array, element, &array->_table[i])) {
i = (i + 1) % array->_nrAlloc;
}
// ...........................................................................
// return whatever we found
// ...........................................................................
return &array->_table[i];
}
////////////////////////////////////////////////////////////////////////////////
/// @brief finds an element given an element, returns NULL if not found
////////////////////////////////////////////////////////////////////////////////
TRI_hash_index_element_t* TRI_FindByElementHashArray (TRI_hash_array_t* array,
TRI_hash_index_element_t* element) {
TRI_hash_index_element_t* element2;
element2 = TRI_LookupByElementHashArray(array, element);
if (! IsEmptyElement(array, element2) && IsEqualElementElement(array, element2, element)) {
return element2;
}
return NULL;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief adds an element to the array
///
/// This function claims the owenship of the sub-objects in the inserted
/// element.
////////////////////////////////////////////////////////////////////////////////
int TRI_InsertElementHashArray (TRI_hash_array_t* array,
TRI_hash_index_element_t* element,
bool overwrite) {
uint64_t hash;
uint64_t i;
bool found;
TRI_hash_index_element_t* arrayElement;
// ...........................................................................
// compute the hash
// ...........................................................................
hash = HashElement(array, element);
i = hash % array->_nrAlloc;
// ...........................................................................
// search the table
// ...........................................................................
while (! IsEmptyElement(array, &array->_table[i])
&& ! IsEqualElementElement(array, element, &array->_table[i])) {
i = (i + 1) % array->_nrAlloc;
}
arrayElement = &array->_table[i];
// ...........................................................................
// if we found an element, return
// ...........................................................................
found = ! IsEmptyElement(array, arrayElement);
if (found) {
if (overwrite) {
// destroy the underlying element since we are going to stomp on top if it
DestroyElement(array, arrayElement);
*arrayElement = *element;
}
else {
DestroyElement(array, element);
}
return TRI_RESULT_ELEMENT_EXISTS;
}
// ...........................................................................
// add a new element to the hash array (existing item is empty so no need to
// destroy it)
// ...........................................................................
*arrayElement = *element;
array->_nrUsed++;
// ...........................................................................
// we are adding and the table is more than half full, extend it
// ...........................................................................
if (array->_nrAlloc < 2 * array->_nrUsed) {
int res;
res = ResizeHashArray(array);
if (res != TRI_ERROR_NO_ERROR) {
return res;
}
}
return TRI_ERROR_NO_ERROR;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief adds an key/element to the array
///
/// This function claims the owenship of the sub-objects in the inserted
/// element.
////////////////////////////////////////////////////////////////////////////////
int TRI_InsertKeyHashArray (TRI_hash_array_t* array,
TRI_index_search_value_t* key,
TRI_hash_index_element_t* element,
bool overwrite) {
uint64_t hash;
uint64_t i;
bool found;
TRI_hash_index_element_t* arrayElement;
// ...........................................................................
// compute the hash
// ...........................................................................
hash = HashKey(array, key);
i = hash % array->_nrAlloc;
// ...........................................................................
// search the table
// ...........................................................................
while (! IsEmptyElement(array, &array->_table[i])
&& ! IsEqualKeyElement(array, key, &array->_table[i])) {
i = (i + 1) % array->_nrAlloc;
}
arrayElement = &array->_table[i];
// ...........................................................................
// if we found an element, return
// ...........................................................................
found = ! IsEmptyElement(array, arrayElement);
if (found) {
if (overwrite) {
// destroy the underlying element since we are going to stomp on top if it
DestroyElement(array, arrayElement);
*arrayElement = *element;
}
else {
DestroyElement(array, element);
}
return TRI_RESULT_KEY_EXISTS;
}
// ...........................................................................
// add a new element to the associative array
// ...........................................................................
*arrayElement = *element;
array->_nrUsed++;
// ...........................................................................
// we are adding and the table is more than half full, extend it
// ...........................................................................
if (array->_nrAlloc < 2 * array->_nrUsed) {
int res;
res = ResizeHashArray(array);
if (res != TRI_ERROR_NO_ERROR) {
return res;
}
}
return TRI_ERROR_NO_ERROR;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief removes an element from the array
////////////////////////////////////////////////////////////////////////////////
int TRI_RemoveElementHashArray (TRI_hash_array_t* array,
TRI_hash_index_element_t* element) {
uint64_t hash;
uint64_t i;
uint64_t k;
bool found;
void* arrayElement;
// ...........................................................................
// compute the hash
// ...........................................................................
hash = HashElement(array, element);
i = hash % array->_nrAlloc;
// ...........................................................................
// search the table
// ...........................................................................
while (! IsEmptyElement(array, &array->_table[i])
&& ! IsEqualElementElement(array, element, &array->_table[i])) {
i = (i + 1) % array->_nrAlloc;
}
arrayElement = &array->_table[i];
// ...........................................................................
// if we did not find such an item return false
// ...........................................................................
found = ! IsEmptyElement(array, arrayElement);
if (! found) {
return TRI_RESULT_ELEMENT_NOT_FOUND;
}
// ...........................................................................
// remove item - destroy any internal memory associated with the element structure
// ...........................................................................
DestroyElement(array, arrayElement);
array->_nrUsed--;
// ...........................................................................
// and now check the following places for items to move closer together
// so that there are no gaps in the array
// ...........................................................................
k = (i + 1) % array->_nrAlloc;
while (! IsEmptyElement(array, &array->_table[k])) {
uint64_t j = HashElement(array, &array->_table[k]) % array->_nrAlloc;
if ((i < k && !(i < j && j <= k)) || (k < i && !(i < j || j <= k))) {
array->_table[i] = array->_table[k];
ClearElement(array, &array->_table[k]);
i = k;
}
k = (k + 1) % array->_nrAlloc;
}
return TRI_ERROR_NO_ERROR;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief removes an key from the array
////////////////////////////////////////////////////////////////////////////////
int TRI_RemoveKeyHashArray (TRI_hash_array_t* array,
TRI_index_search_value_t* key) {
uint64_t hash;
uint64_t i;
uint64_t k;
bool found;
void* arrayElement;
// ...........................................................................
// compute the hash
// ...........................................................................
hash = HashKey(array, key);
i = hash % array->_nrAlloc;
// ...........................................................................
// search the table
// ...........................................................................
while (! IsEmptyElement(array, &array->_table[i])
&& ! IsEqualKeyElement(array, key, &array->_table[i])) {
i = (i + 1) % array->_nrAlloc;
}
arrayElement = &array->_table[i];
// ...........................................................................
// if we did not find such an item return false
// ...........................................................................
found = ! IsEmptyElement(array, arrayElement);
if (! found) {
return TRI_RESULT_KEY_NOT_FOUND;
}
// ...........................................................................
// remove item
// ...........................................................................
DestroyElement(array, arrayElement);
array->_nrUsed--;
// ...........................................................................
// and now check the following places for items to move here
// ...........................................................................
k = (i + 1) % array->_nrAlloc;
while (! IsEmptyElement(array, &array->_table[k])) {
uint64_t j = HashElement(array, &array->_table[k]) % array->_nrAlloc;
if ((i < k && !(i < j && j <= k)) || (k < i && !(i < j || j <= k))) {
array->_table[i] = array->_table[k];
ClearElement(array, &array->_table[k]);
i = k;
}
k = (k + 1) % array->_nrAlloc;
}
// ...........................................................................
// return success
// ...........................................................................
return TRI_ERROR_NO_ERROR;
}
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// --SECTION-- HASH ARRAY MULTI
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// --SECTION-- public functions
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @addtogroup HashArray
/// @{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief lookups an element given a key
////////////////////////////////////////////////////////////////////////////////
TRI_vector_pointer_t TRI_LookupByKeyHashArrayMulti (TRI_hash_array_t* array,
TRI_index_search_value_t* key) {
TRI_vector_pointer_t result;
uint64_t hash;
uint64_t i;
// ...........................................................................
// initialise the vector which will hold the result if any
// ...........................................................................
TRI_InitVectorPointer(&result, TRI_UNKNOWN_MEM_ZONE);
// ...........................................................................
// compute the hash
// ...........................................................................
hash = HashKey(array, key);
i = hash % array->_nrAlloc;
// ...........................................................................
// search the table
// ...........................................................................
while (! IsEmptyElement(array, &array->_table[i])) {
if (IsEqualKeyElement(array, key, &array->_table[i])) {
TRI_PushBackVectorPointer(&result, &array->_table[i]);
}
i = (i + 1) % array->_nrAlloc;
}
// ...........................................................................
// return whatever we found -- which could be an empty vector list if nothing
// matches.
// ...........................................................................
return result;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief lookups an element given an element
////////////////////////////////////////////////////////////////////////////////
TRI_vector_pointer_t TRI_LookupByElementHashArrayMulti (TRI_hash_array_t* array,
TRI_hash_index_element_t* element) {
TRI_vector_pointer_t result;
uint64_t hash;
uint64_t i;
// ...........................................................................
// initialise the vector which will hold the result if any
// ...........................................................................
TRI_InitVectorPointer(&result, TRI_UNKNOWN_MEM_ZONE);
// ...........................................................................
// compute the hash
// ...........................................................................
hash = HashElement(array, element);
i = hash % array->_nrAlloc;
// ...........................................................................
// search the table
// ...........................................................................
while (! IsEmptyElement(array, &array->_table[i])) {
if (IsEqualElementElement(array, element, &array->_table[i])) {
TRI_PushBackVectorPointer(&result, &array->_table[i]);
}
i = (i + 1) % array->_nrAlloc;
}
// ...........................................................................
// return whatever we found -- which could be an empty vector list if nothing
// matches. Note that we allow multiple elements (compare with pointer impl).
// ...........................................................................
return result;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief adds an element to the array
///
/// This function claims the owenship of the sub-objects in the inserted
/// element.
////////////////////////////////////////////////////////////////////////////////
int TRI_InsertElementHashArrayMulti (TRI_hash_array_t* array,
TRI_hash_index_element_t* element,
bool overwrite) {
uint64_t hash;
uint64_t i;
bool found;
TRI_hash_index_element_t* arrayElement;
// ...........................................................................
// compute the hash
// ...........................................................................
hash = HashElement(array, element);
i = hash % array->_nrAlloc;
// ...........................................................................
// search the table
// ...........................................................................
while (! IsEmptyElement(array, &array->_table[i])
&& ! IsEqualElementElement(array, element, &array->_table[i])) {
i = (i + 1) % array->_nrAlloc;
}
arrayElement = &array->_table[i];
// ...........................................................................
// If we found an element, return. While we allow duplicate entries in the
// hash table, we do not allow duplicate elements. Elements would refer to the
// (for example) an actual row in memory. This is different from the
// TRI_InsertElementMultiArray function below where we only have keys to
// differentiate between elements.
// ...........................................................................
found = ! IsEmptyElement(array, arrayElement);
if (found) {
if (overwrite) {
// destroy the underlying element since we are going to stomp on top if it
DestroyElement(array, arrayElement);
*arrayElement = *element;
}
else {
DestroyElement(array, element);
}
return TRI_RESULT_ELEMENT_EXISTS;
}
// ...........................................................................
// add a new element to the associative array
// ...........................................................................
*arrayElement = *element;
array->_nrUsed++;
// ...........................................................................
// if we were adding and the table is more than half full, extend it
// ...........................................................................
if (array->_nrAlloc < 2 * array->_nrUsed) {
int res;
res = ResizeHashArray(array);
if (res != TRI_ERROR_NO_ERROR) {
return res;
}
}
return TRI_ERROR_NO_ERROR;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief adds a key/element to the array
///
/// This function claims the owenship of the sub-objects in the inserted
/// element.
////////////////////////////////////////////////////////////////////////////////
int TRI_InsertKeyHashArrayMulti (TRI_hash_array_t* array,
TRI_index_search_value_t* key,
TRI_hash_index_element_t* element,
bool overwrite) {
uint64_t hash;
uint64_t i;
TRI_hash_index_element_t* arrayElement;
// ...........................................................................
// compute the hash
// ...........................................................................
hash = HashKey(array, key);
i = hash % array->_nrAlloc;
// ...........................................................................
// search the table
// ...........................................................................
while (! IsEmptyElement(array, &array->_table[i])) {
i = (i + 1) % array->_nrAlloc;
}
arrayElement = &array->_table[i];
// ...........................................................................
// We do not look for an element (as opposed to the function above). So whether
// or not there exists a duplicate we do not care.
// ...........................................................................
// ...........................................................................
// add a new element to the associative array
// ...........................................................................
*arrayElement = * element;
array->_nrUsed++;
// ...........................................................................
// if we were adding and the table is more than half full, extend it
// ...........................................................................
if (array->_nrAlloc < 2 * array->_nrUsed) {
int res;
res = ResizeHashArray(array);
if (res != TRI_ERROR_NO_ERROR) {
return res;
}
}
return TRI_ERROR_NO_ERROR;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief removes an element from the array
////////////////////////////////////////////////////////////////////////////////
int TRI_RemoveElementHashArrayMulti (TRI_hash_array_t* array,
TRI_hash_index_element_t* element) {
uint64_t hash;
uint64_t i;
uint64_t k;
bool found;
TRI_hash_index_element_t* arrayElement;
// ...........................................................................
// Obtain the hash
// ...........................................................................
hash = HashElement(array, element);
i = hash % array->_nrAlloc;
// ...........................................................................
// search the table
// ...........................................................................
while (! IsEmptyElement(array, &array->_table[i])
&& ! IsEqualElementElement(array, element, &array->_table[i])) {
i = (i + 1) % array->_nrAlloc;
}
arrayElement = &array->_table[i];
// ...........................................................................
// if we did not find such an item return false
// ...........................................................................
found = ! IsEmptyElement(array, arrayElement);
if (! found) {
return TRI_RESULT_ELEMENT_NOT_FOUND;
}
// ...........................................................................
// remove item
// ...........................................................................
DestroyElement(array, arrayElement);
array->_nrUsed--;
// ...........................................................................
// and now check the following places for items to move here
// ...........................................................................
k = (i + 1) % array->_nrAlloc;
while (! IsEmptyElement(array, &array->_table[k])) {
uint64_t j = HashElement(array, &array->_table[k]) % array->_nrAlloc;
if ((i < k && !(i < j && j <= k)) || (k < i && !(i < j || j <= k))) {
array->_table[i] = array->_table[k];
ClearElement(array, &array->_table[k]);
i = k;
}
k = (k + 1) % array->_nrAlloc;
}
return TRI_ERROR_NO_ERROR;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief removes an key/element to the array
////////////////////////////////////////////////////////////////////////////////
int TRI_RemoveKeyHashArrayMulti (TRI_hash_array_t* array,
TRI_index_search_value_t* key) {
uint64_t hash;
uint64_t i;
uint64_t k;
bool found;
TRI_hash_index_element_t* arrayElement;
// ...........................................................................
// generate hash using key
// ...........................................................................
hash = HashKey(array, key);
i = hash % array->_nrAlloc;
// ...........................................................................
// search the table -- we can only match keys
// ...........................................................................
while (! IsEmptyElement(array, &array->_table[i])
&& ! IsEqualKeyElement(array, key, &array->_table[i])) {
i = (i + 1) % array->_nrAlloc;
}
arrayElement = &array->_table[i];
// ...........................................................................
// if we did not find such an item return false
// ...........................................................................
found = ! IsEmptyElement(array, arrayElement);
if (! found) {
return TRI_RESULT_KEY_NOT_FOUND;
}
// ...........................................................................
// remove item
// ...........................................................................
DestroyElement(array, arrayElement);
array->_nrUsed--;
// ...........................................................................
// and now check the following places for items to move here
// ...........................................................................
k = (i + 1) % array->_nrAlloc;
while (! IsEmptyElement(array, &array->_table[k])) {
uint64_t j = HashElement(array, &array->_table[k]) % array->_nrAlloc;
if ((i < k && !(i < j && j <= k)) || (k < i && !(i < j || j <= k))) {
array->_table[i] = array->_table[k];
ClearElement(array, &array->_table[k]);
i = k;
}
k = (k + 1) % array->_nrAlloc;
}
return TRI_ERROR_NO_ERROR;
}
////////////////////////////////////////////////////////////////////////////////
/// @}
////////////////////////////////////////////////////////////////////////////////
// -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE
// -----------------------------------------------------------------------------
// Local Variables:
// mode: outline-minor
// outline-regexp: "/// @brief\\|/// {@inheritDoc}\\|/// @addtogroup\\|/// @page\\|// --SECTION--\\|/// @\\}"
// End: