int TRI_LookupByKeyHashArrayMulti (TRI_hash_array_multi_t const* array, TRI_index_search_value_t const* key, std::vector<TRI_doc_mptr_copy_t>& result) { TRI_ASSERT_EXPENSIVE(array->_nrUsed < array->_nrAlloc); uint64_t const n = array->_nrAlloc; uint64_t i, k; i = k = HashKey(array, key) % n; for (; i < n && array->_table[i]._document != nullptr && ! IsEqualKeyElement(array, key, &array->_table[i]); ++i); if (i == n) { for (i = 0; i < k && array->_table[i]._document != nullptr && ! IsEqualKeyElement(array, key, &array->_table[i]); ++i); } TRI_ASSERT_EXPENSIVE(i < n); if (array->_table[i]._document != nullptr) { // add the element itself result.emplace_back(*(array->_table[i]._document)); // add the overflow elements auto current = array->_table[i]._next; while (current != nullptr) { result.emplace_back(*(current->_document)); current = current->_next; } } return TRI_ERROR_NO_ERROR; }
void* TRI_LookupByKeyPrimaryIndex (TRI_primary_index_t* idx, char const* key) { if (idx->_nrUsed == 0) { return nullptr; } // compute the hash uint64_t const hash = TRI_HashKeyPrimaryIndex(key); uint64_t const n = idx->_nrAlloc; uint64_t i, k; i = k = hash % n; TRI_ASSERT_EXPENSIVE(n > 0); // search the table for (; i < n && idx->_table[i] != nullptr && IsDifferentHashElement(key, hash, idx->_table[i]); ++i); if (i == n) { for (i = 0; i < k && idx->_table[i] != nullptr && IsDifferentHashElement(key, hash, idx->_table[i]); ++i); } TRI_ASSERT_EXPENSIVE(i < n); // return whatever we found return idx->_table[i]; }
static void DestroyElement (TRI_hash_array_multi_t* array, TRI_hash_index_element_multi_t* element) { TRI_ASSERT_EXPENSIVE(element != nullptr); TRI_ASSERT_EXPENSIVE(element->_document != nullptr); if (element->_subObjects != nullptr) { TRI_Free(TRI_UNKNOWN_MEM_ZONE, element->_subObjects); } element->_document = nullptr; element->_subObjects = nullptr; element->_next = nullptr; }
static bool IsEqualKeyElement (TRI_hash_array_multi_t const* array, TRI_index_search_value_t const* left, TRI_hash_index_element_multi_t const* right) { TRI_ASSERT_EXPENSIVE(right->_document != nullptr); for (size_t j = 0; j < array->_numFields; ++j) { TRI_shaped_json_t* leftJson = &left->_values[j]; TRI_shaped_sub_t* rightSub = &right->_subObjects[j]; if (leftJson->_sid != rightSub->_sid) { return false; } auto length = leftJson->_data.length; char const* rightData; size_t rightLength; TRI_InspectShapedSub(rightSub, right->_document, rightData, rightLength); if (length != rightLength) { return false; } if (length > 0 && memcmp(leftJson->_data.data, rightData, length) != 0) { return false; } } return true; }
int TRI_LookupByKeyHashArrayMulti (TRI_hash_array_multi_t const* array, TRI_index_search_value_t const* key, std::vector<TRI_doc_mptr_copy_t>& result, TRI_hash_index_element_multi_t*& next, size_t batchSize) { size_t const initialSize = result.size(); TRI_ASSERT_EXPENSIVE(array->_nrUsed < array->_nrAlloc); TRI_ASSERT(batchSize > 0); if (next == nullptr) { // no previous state. start at the beginning uint64_t const n = array->_nrAlloc; uint64_t i, k; i = k = HashKey(array, key) % n; for (; i < n && array->_table[i]._document != nullptr && ! IsEqualKeyElement(array, key, &array->_table[i]); ++i); if (i == n) { for (i = 0; i < k && array->_table[i]._document != nullptr && ! IsEqualKeyElement(array, key, &array->_table[i]); ++i); } TRI_ASSERT_EXPENSIVE(i < n); if (array->_table[i]._document != nullptr) { result.emplace_back(*(array->_table[i]._document)); } next = array->_table[i]._next; } if (next != nullptr) { // we already had a state size_t total = result.size() - initialSize; while (next != nullptr && total < batchSize) { result.emplace_back(*(next->_document)); next = next->_next; ++total; } } return TRI_ERROR_NO_ERROR; }
int TRI_InsertKeyPrimaryIndex (TRI_primary_index_t* idx, TRI_doc_mptr_t const* header, void const** found) { *found = nullptr; if (idx->_nrAlloc < 2 * idx->_nrUsed) { // check for out-of-memory if (! ResizePrimaryIndex(idx, (uint64_t) (2 * idx->_nrAlloc + 1), false)) { return TRI_ERROR_OUT_OF_MEMORY; } } uint64_t const n = idx->_nrAlloc; uint64_t i, k; TRI_ASSERT_EXPENSIVE(n > 0); i = k = header->_hash % n; for (; i < n && idx->_table[i] != nullptr && IsDifferentKeyElement(header, idx->_table[i]); ++i); if (i == n) { for (i = 0; i < k && idx->_table[i] != nullptr && IsDifferentKeyElement(header, idx->_table[i]); ++i); } TRI_ASSERT_EXPENSIVE(i < n); void* old = idx->_table[i]; // if we found an element, return if (old != nullptr) { *found = old; return TRI_ERROR_NO_ERROR; } // add a new element to the associative idx idx->_table[i] = (void*) header; ++idx->_nrUsed; return TRI_ERROR_NO_ERROR; }
static int ResizeHashArray (TRI_hash_array_multi_t* array, uint64_t targetSize, bool allowShrink) { if (array->_nrAlloc >= targetSize && ! allowShrink) { return TRI_ERROR_NO_ERROR; } TRI_hash_index_element_multi_t* oldTable = array->_table; TRI_hash_index_element_multi_t* oldTablePtr = array->_tablePtr; uint64_t oldAlloc = array->_nrAlloc; TRI_ASSERT(targetSize > 0); int res = AllocateTable(array, targetSize); if (res != TRI_ERROR_NO_ERROR) { return res; } if (array->_nrUsed > 0) { uint64_t const n = array->_nrAlloc; for (uint64_t j = 0; j < oldAlloc; j++) { TRI_hash_index_element_multi_t* element = &oldTable[j]; if (element->_document != nullptr) { uint64_t i, k; i = k = HashElement(array, element) % n; for (; i < n && array->_table[i]._document != nullptr; ++i); if (i == n) { for (i = 0; i < k && array->_table[i]._document != nullptr; ++i); } TRI_ASSERT_EXPENSIVE(i < n); // ........................................................................... // add a new element to the associative array // memcpy ok here since are simply moving array items internally // ........................................................................... memcpy(&array->_table[i], element, TableEntrySize()); } } } TRI_Free(TRI_UNKNOWN_MEM_ZONE, oldTablePtr); return TRI_ERROR_NO_ERROR; }
TRI_vector_pointer_t TRI_LookupByKeyHashArrayMulti (TRI_hash_array_multi_t const* array, TRI_index_search_value_t const* key) { TRI_ASSERT_EXPENSIVE(array->_nrUsed < array->_nrAlloc); // ........................................................................... // initialise the vector which will hold the result if any // ........................................................................... TRI_vector_pointer_t result; TRI_InitVectorPointer(&result, TRI_UNKNOWN_MEM_ZONE); uint64_t const n = array->_nrAlloc; uint64_t i, k; i = k = HashKey(array, key) % n; for (; i < n && array->_table[i]._document != nullptr && ! IsEqualKeyElement(array, key, &array->_table[i]); ++i); if (i == n) { for (i = 0; i < k && array->_table[i]._document != nullptr && ! IsEqualKeyElement(array, key, &array->_table[i]); ++i); } TRI_ASSERT_EXPENSIVE(i < n); if (array->_table[i]._document != nullptr) { // add the element itself TRI_PushBackVectorPointer(&result, array->_table[i]._document); // add the overflow elements auto current = array->_table[i]._next; while (current != nullptr) { TRI_PushBackVectorPointer(&result, current->_document); current = current->_next; } } return result; }
static bool ResizePrimaryIndex (TRI_primary_index_t* idx, uint64_t targetSize, bool allowShrink) { TRI_ASSERT(targetSize > 0); if (idx->_nrAlloc >= targetSize && ! allowShrink) { return true; } void** oldTable = idx->_table; idx->_table = static_cast<void**>(TRI_Allocate(TRI_UNKNOWN_MEM_ZONE, (size_t) (targetSize * sizeof(void*)), true)); if (idx->_table == nullptr) { idx->_table = oldTable; return false; } if (idx->_nrUsed > 0) { uint64_t const oldAlloc = idx->_nrAlloc; // table is already cleared by allocate, now copy old data for (uint64_t j = 0; j < oldAlloc; j++) { TRI_doc_mptr_t const* element = static_cast<TRI_doc_mptr_t const*>(oldTable[j]); if (element != nullptr) { uint64_t const hash = element->_hash; uint64_t i, k; i = k = hash % targetSize; for (; i < targetSize && idx->_table[i] != nullptr; ++i); if (i == targetSize) { for (i = 0; i < k && idx->_table[i] != nullptr; ++i); } TRI_ASSERT_EXPENSIVE(i < targetSize); idx->_table[i] = (void*) element; } } } TRI_Free(TRI_UNKNOWN_MEM_ZONE, oldTable); idx->_nrAlloc = targetSize; return true; }
void* TRI_RemoveKeyPrimaryIndex (TRI_primary_index_t* idx, char const* key) { uint64_t const hash = TRI_HashKeyPrimaryIndex(key); uint64_t const n = idx->_nrAlloc; uint64_t i, k; i = k = hash % n; // search the table for (; i < n && idx->_table[i] != nullptr && IsDifferentHashElement(key, hash, idx->_table[i]); ++i); if (i == n) { for (i = 0; i < k && idx->_table[i] != nullptr && IsDifferentHashElement(key, hash, idx->_table[i]); ++i); } TRI_ASSERT_EXPENSIVE(i < n); // if we did not find such an item return false if (idx->_table[i] == nullptr) { return nullptr; } // remove item void* old = idx->_table[i]; idx->_table[i] = nullptr; idx->_nrUsed--; // and now check the following places for items to move here k = TRI_IncModU64(i, n); while (idx->_table[k] != nullptr) { uint64_t j = (static_cast<TRI_doc_mptr_t const*>(idx->_table[k])->_hash) % n; if ((i < k && ! (i < j && j <= k)) || (k < i && ! (i < j || j <= k))) { idx->_table[i] = idx->_table[k]; idx->_table[k] = nullptr; i = k; } k = TRI_IncModU64(k, n); } if (idx->_nrUsed == 0) { ResizePrimaryIndex(idx, InitialSize(), true); } // return success return old; }
int TRI_RemoveElementHashArrayMulti (TRI_hash_array_multi_t* array, TRI_index_search_value_t const* key, TRI_hash_index_element_multi_t* element) { uint64_t const n = array->_nrAlloc; uint64_t i, k; i = k = HashKey(array, key) % n; for (; i < n && array->_table[i]._document != nullptr && ! IsEqualKeyElement(array, key, &array->_table[i]); ++i); if (i == n) { for (i = 0; i < k && array->_table[i]._document != nullptr && ! IsEqualKeyElement(array, key, &array->_table[i]); ++i); } TRI_ASSERT_EXPENSIVE(i < n); TRI_hash_index_element_multi_t* arrayElement = &array->_table[i]; bool found = (arrayElement->_document != nullptr); if (! found) { return TRI_RESULT_ELEMENT_NOT_FOUND; } if (arrayElement->_document != element->_document) { // look in the overflow list for the sought document auto next = &(arrayElement->_next); while (*next != nullptr) { if ((*next)->_document == element->_document) { auto ptr = (*next)->_next; DestroyElement(array, *next); ReturnToFreelist(array, *next); *next = ptr; return TRI_ERROR_NO_ERROR; } next = &((*next)->_next); } return TRI_RESULT_ELEMENT_NOT_FOUND; } // the element itself is the document to remove TRI_ASSERT(arrayElement->_document == element->_document); if (arrayElement->_next != nullptr) { auto next = arrayElement->_next; // destroy our own data first, otherwise we'll leak TRI_ASSERT(arrayElement->_subObjects != nullptr); TRI_Free(TRI_UNKNOWN_MEM_ZONE, arrayElement->_subObjects); // copy data from first overflow element into ourselves arrayElement->_document = next->_document; arrayElement->_subObjects = next->_subObjects; arrayElement->_next = next->_next; // and remove the first overflow element next->_subObjects = nullptr; DestroyElement(array, next); ReturnToFreelist(array, next); return TRI_ERROR_NO_ERROR; } TRI_ASSERT(arrayElement->_next == nullptr); DestroyElement(array, arrayElement); array->_nrUsed--; // ........................................................................... // and now check the following places for items to move here // ........................................................................... k = TRI_IncModU64(i, n); while (array->_table[k]._document != nullptr) { uint64_t j = HashElement(array, &array->_table[k]) % n; if ((i < k && ! (i < j && j <= k)) || (k < i && ! (i < j || j <= k))) { array->_table[i] = array->_table[k]; array->_table[k]._document = nullptr; array->_table[k]._next = nullptr; array->_table[k]._subObjects = nullptr; i = k; } k = TRI_IncModU64(k, n); } if (array->_nrUsed == 0) { TRI_ASSERT(array->_nrOverflowUsed == 0); ResizeHashArray(array, InitialSize(), true); } return TRI_ERROR_NO_ERROR; }
int TRI_InsertElementHashArrayMulti (TRI_hash_array_multi_t* array, TRI_index_search_value_t const* key, TRI_hash_index_element_multi_t* element, bool isRollback) { if (! CheckResize(array)) { return TRI_ERROR_OUT_OF_MEMORY; } uint64_t const n = array->_nrAlloc; uint64_t i, k; i = k = HashKey(array, key) % n; for (; i < n && array->_table[i]._document != nullptr && ! IsEqualKeyElement(array, key, &array->_table[i]); ++i); if (i == n) { for (i = 0; i < k && array->_table[i]._document != nullptr && ! IsEqualKeyElement(array, key, &array->_table[i]); ++i); } TRI_ASSERT_EXPENSIVE(i < n); TRI_hash_index_element_multi_t* 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. // ........................................................................... bool found = (arrayElement->_document != nullptr); if (found) { if (isRollback) { if (arrayElement->_document == element->_document) { DestroyElement(array, element); return TRI_RESULT_ELEMENT_EXISTS; } auto current = arrayElement->_next; while (current != nullptr) { if (current->_document == element->_document) { DestroyElement(array, element); return TRI_RESULT_ELEMENT_EXISTS; } current = current->_next; } } auto ptr = GetFromFreelist(array); if (ptr == nullptr) { return TRI_ERROR_OUT_OF_MEMORY; } // link our element at the list head ptr->_document = element->_document; ptr->_subObjects = element->_subObjects; ptr->_next = arrayElement->_next; arrayElement->_next = ptr; // it is ok to destroy the element here, because we have copied its internal before! element->_subObjects = nullptr; DestroyElement(array, element); return TRI_ERROR_NO_ERROR; } TRI_ASSERT(arrayElement->_next == nullptr); // not found in list, now insert element->_next = nullptr; *arrayElement = *element; array->_nrUsed++; TRI_ASSERT(arrayElement->_next == nullptr); return TRI_ERROR_NO_ERROR; }
int TRI_CompareValuesJson (TRI_json_t const* lhs, TRI_json_t const* rhs, bool useUTF8) { // note: both lhs and rhs may be NULL! { int lWeight = TypeWeight(lhs); int rWeight = TypeWeight(rhs); if (lWeight < rWeight) { return -1; } if (lWeight > rWeight) { return 1; } TRI_ASSERT_EXPENSIVE(lWeight == rWeight); } // lhs and rhs have equal weights if (lhs == nullptr || rhs == nullptr) { // either lhs or rhs is a nullptr. we cannot be sure here that both are nullptrs. // there can also exist the situation that lhs is a nullptr and rhs is a JSON null value // (or vice versa). Anyway, the compare value is the same for both, return 0; } switch (lhs->_type) { case TRI_JSON_UNUSED: case TRI_JSON_NULL: { return 0; // null == null; } case TRI_JSON_BOOLEAN: { if (lhs->_value._boolean == rhs->_value._boolean) { return 0; } if (! lhs->_value._boolean && rhs->_value._boolean) { return -1; } return 1; } case TRI_JSON_NUMBER: { if (lhs->_value._number == rhs->_value._number) { return 0; } if (lhs->_value._number < rhs->_value._number) { return -1; } return 1; } case TRI_JSON_STRING: case TRI_JSON_STRING_REFERENCE: { // same for STRING and STRING_REFERENCE TRI_ASSERT(lhs->_value._string.data != nullptr); TRI_ASSERT(rhs->_value._string.data != nullptr); int res; size_t const nl = lhs->_value._string.length - 1; size_t const nr = rhs->_value._string.length - 1; if (useUTF8) { res = TRI_compare_utf8(lhs->_value._string.data, nl, rhs->_value._string.data, nr); } else { // beware of strings containing NUL bytes size_t len = nl < nr ? nl : nr; res = memcmp(lhs->_value._string.data, rhs->_value._string.data, len); } if (res < 0) { return -1; } else if (res > 0) { return 1; } // res == 0 if (nl == nr) { return 0; } // res == 0, but different string lengths return nl < nr ? -1 : 1; } case TRI_JSON_ARRAY: { size_t const nl = TRI_LengthVector(&lhs->_value._objects); size_t const nr = TRI_LengthVector(&rhs->_value._objects); size_t n; if (nl > nr) { n = nl; } else { n = nr; } for (size_t i = 0; i < n; ++i) { auto lhsValue = (i >= nl) ? nullptr : static_cast<TRI_json_t const*>(TRI_AtVector(&lhs->_value._objects, i)); auto rhsValue = (i >= nr) ? nullptr : static_cast<TRI_json_t const*>(TRI_AtVector(&rhs->_value._objects, i)); int result = TRI_CompareValuesJson(lhsValue, rhsValue, useUTF8); if (result != 0) { return result; } } return 0; } case TRI_JSON_OBJECT: { TRI_ASSERT(lhs->_type == TRI_JSON_OBJECT); TRI_ASSERT(rhs->_type == TRI_JSON_OBJECT); std::unique_ptr<TRI_json_t> keys(GetMergedKeyArray(lhs, rhs)); if (keys != nullptr) { auto json = keys.get(); size_t const n = TRI_LengthVector(&json->_value._objects); for (size_t i = 0; i < n; ++i) { auto keyElement = static_cast<TRI_json_t const*>(TRI_AtVector(&json->_value._objects, i)); TRI_ASSERT(TRI_IsStringJson(keyElement)); TRI_json_t const* lhsValue = TRI_LookupObjectJson(lhs, keyElement->_value._string.data); // may be NULL TRI_json_t const* rhsValue = TRI_LookupObjectJson(rhs, keyElement->_value._string.data); // may be NULL int result = TRI_CompareValuesJson(lhsValue, rhsValue, useUTF8); if (result != 0) { return result; } } } // fall-through to returning 0 } } return 0; }