static uint64_t FastHashJsonRecursive (uint64_t hash, TRI_json_t const* object) { if (nullptr == object) { return fasthash64(static_cast<const void*>("null"), 4, hash); } switch (object->_type) { case TRI_JSON_UNUSED: { return hash; } case TRI_JSON_NULL: { return fasthash64(static_cast<const void*>("null"), 4, hash); } case TRI_JSON_BOOLEAN: { if (object->_value._boolean) { return fasthash64(static_cast<const void*>("true"), 4, hash); } return fasthash64(static_cast<const void*>("false"), 5, hash); } case TRI_JSON_NUMBER: { return fasthash64(static_cast<const void*>(&object->_value._number), sizeof(object->_value._number), hash); } case TRI_JSON_STRING: case TRI_JSON_STRING_REFERENCE: { return fasthash64(static_cast<const void*>(object->_value._string.data), object->_value._string.length, hash); } case TRI_JSON_OBJECT: { hash = fasthash64(static_cast<const void*>("object"), 6, hash); size_t const n = TRI_LengthVector(&object->_value._objects); for (size_t i = 0; i < n; i += 2) { auto subjson = static_cast<TRI_json_t const*>(TRI_AddressVector(&object->_value._objects, i)); TRI_ASSERT(TRI_IsStringJson(subjson)); hash = FastHashJsonRecursive(hash, subjson); subjson = static_cast<TRI_json_t const*>(TRI_AddressVector(&object->_value._objects, i + 1)); hash = FastHashJsonRecursive(hash, subjson); } return hash; } case TRI_JSON_ARRAY: { hash = fasthash64(static_cast<const void*>("array"), 5, hash); size_t const n = TRI_LengthVector(&object->_value._objects); for (size_t i = 0; i < n; ++i) { auto subjson = static_cast<TRI_json_t const*>(TRI_AddressVector(&object->_value._objects, i)); hash = FastHashJsonRecursive(hash, subjson); } } } return hash; // never reached }
void TRI_DestroyJson (TRI_memory_zone_t* zone, TRI_json_t* object) { switch (object->_type) { case TRI_JSON_UNUSED: case TRI_JSON_NULL: case TRI_JSON_BOOLEAN: case TRI_JSON_NUMBER: case TRI_JSON_STRING_REFERENCE: break; case TRI_JSON_STRING: TRI_DestroyBlob(zone, &object->_value._string); break; case TRI_JSON_OBJECT: case TRI_JSON_ARRAY: { size_t const n = TRI_LengthVector(&object->_value._objects); for (size_t i = 0; i < n; ++i) { auto v = static_cast<TRI_json_t*>(TRI_AddressVector(&object->_value._objects, i)); TRI_DestroyJson(zone, v); } TRI_DestroyVector(&object->_value._objects); break; } } }
TRI_json_t* TRI_UniquifyArrayJson (TRI_json_t const* array) { TRI_ASSERT(array != nullptr); TRI_ASSERT(array->_type == TRI_JSON_ARRAY); // create result array std::unique_ptr<TRI_json_t> result(TRI_CreateArrayJson(TRI_UNKNOWN_MEM_ZONE)); if (result == nullptr) { return nullptr; } size_t const n = TRI_LengthVector(&array->_value._objects); TRI_json_t const* last = nullptr; for (size_t i = 0; i < n; ++i) { auto p = static_cast<TRI_json_t const*>(TRI_AtVector(&array->_value._objects, i)); // don't push value if it is the same as the last value if (last == nullptr || TRI_CompareValuesJson(p, last, false) != 0) { TRI_PushBackArrayJson(TRI_UNKNOWN_MEM_ZONE, result.get(), p); // remember last element last = p; } } return result.release(); }
static TRI_json_t* GetMergedKeyArray (TRI_json_t const* lhs, TRI_json_t const* rhs) { TRI_ASSERT(lhs->_type == TRI_JSON_OBJECT); TRI_ASSERT(rhs->_type == TRI_JSON_OBJECT); size_t n = TRI_LengthVector(&lhs->_value._objects) + TRI_LengthVector(&rhs->_value._objects); std::unique_ptr<TRI_json_t> keys(TRI_CreateArrayJson(TRI_UNKNOWN_MEM_ZONE, n)); if (keys == nullptr) { return nullptr; } if (TRI_CapacityVector(&(keys.get()->_value._objects)) < n) { return nullptr; } n = TRI_LengthVector(&lhs->_value._objects); for (size_t i = 0 ; i < n; i += 2) { auto key = static_cast<TRI_json_t const*>(TRI_AtVector(&lhs->_value._objects, i)); TRI_ASSERT(TRI_IsStringJson(key)); TRI_PushBackArrayJson(TRI_UNKNOWN_MEM_ZONE, keys.get(), key); } n = TRI_LengthVector(&rhs->_value._objects); for (size_t i = 0 ; i < n; i += 2) { auto key = static_cast<TRI_json_t const*>(TRI_AtVector(&rhs->_value._objects, i)); TRI_ASSERT(TRI_IsStringJson(key)); TRI_PushBackArrayJson(TRI_UNKNOWN_MEM_ZONE, keys.get(), key); } // sort the key array in place TRI_SortArrayJson(keys.get()); // array is now sorted return TRI_UniquifyArrayJson(keys.get()); }
TRI_json_t* TRI_LookupArrayJson (TRI_json_t const* array, size_t pos) { TRI_ASSERT(array->_type == TRI_JSON_ARRAY); size_t const n = TRI_LengthVector(&array->_value._objects); if (pos >= n) { // out of bounds return nullptr; } return static_cast<TRI_json_t*>(TRI_AtVector(&array->_value._objects, pos)); }
int TRI_CopyToJson (TRI_memory_zone_t* zone, TRI_json_t* dst, TRI_json_t const* src) { dst->_type = src->_type; switch (src->_type) { case TRI_JSON_UNUSED: case TRI_JSON_NULL: break; case TRI_JSON_BOOLEAN: dst->_value._boolean = src->_value._boolean; break; case TRI_JSON_NUMBER: dst->_value._number = src->_value._number; break; case TRI_JSON_STRING: return TRI_CopyToBlob(zone, &dst->_value._string, &src->_value._string); case TRI_JSON_STRING_REFERENCE: return TRI_AssignToBlob(zone, &dst->_value._string, &src->_value._string); case TRI_JSON_OBJECT: case TRI_JSON_ARRAY: { size_t const n = TRI_LengthVector(&src->_value._objects); TRI_InitVector(&dst->_value._objects, zone, sizeof(TRI_json_t)); int res = TRI_ResizeVector(&dst->_value._objects, n); if (res != TRI_ERROR_NO_ERROR) { return res; } for (size_t i = 0; i < n; ++i) { auto const* v = static_cast<TRI_json_t const*>(TRI_AtVector(&src->_value._objects, i)); auto* w = static_cast<TRI_json_t*>(TRI_AtVector(&dst->_value._objects, i)); res = TRI_CopyToJson(zone, w, v); if (res != TRI_ERROR_NO_ERROR) { return res; } } break; } } return TRI_ERROR_NO_ERROR; }
TRI_json_t* TRI_SortArrayJson (TRI_json_t* array) { TRI_ASSERT(array != nullptr); TRI_ASSERT(array->_type == TRI_JSON_ARRAY); size_t const n = TRI_LengthVector(&array->_value._objects); if (n > 1) { // only sort if more than one value in array qsort(TRI_BeginVector(&array->_value._objects), n, sizeof(TRI_json_t), &CompareJson); } return array; }
TRI_json_t* TRI_LookupObjectJson (TRI_json_t const* object, char const* name) { if (object == nullptr) { return nullptr; } TRI_ASSERT(object->_type == TRI_JSON_OBJECT); TRI_ASSERT(name != nullptr); size_t const n = TRI_LengthVector(&object->_value._objects); if (n >= 16) { // if we have many attributes, try an algorithm that compares string length first // calculate string length once size_t const length = strlen(name) + 1; for (size_t i = 0; i < n; i += 2) { auto key = static_cast<TRI_json_t const*>(TRI_AddressVector(&object->_value._objects, i)); if (! IsString(key)) { continue; } // check string length first, and contents only if string lengths are equal if (key->_value._string.length == length && strcmp(key->_value._string.data, name) == 0) { return static_cast<TRI_json_t*>(TRI_AddressVector(&object->_value._objects, i + 1)); } } } else { // simple case for smaller objects for (size_t i = 0; i < n; i += 2) { auto key = static_cast<TRI_json_t const*>(TRI_AddressVector(&object->_value._objects, i)); if (! IsString(key)) { continue; } if (strcmp(key->_value._string.data, name) == 0) { return static_cast<TRI_json_t*>(TRI_AddressVector(&object->_value._objects, i + 1)); } } } return nullptr; }
bool TRI_DeleteObjectJson (TRI_memory_zone_t* zone, TRI_json_t* object, char const* name) { TRI_ASSERT(object->_type == TRI_JSON_OBJECT); TRI_ASSERT(name != nullptr); size_t const n = TRI_LengthVector(&object->_value._objects); for (size_t i = 0; i < n; i += 2) { TRI_json_t* key = static_cast<TRI_json_t*>(TRI_AtVector(&object->_value._objects, i)); if (! IsString(key)) { continue; } if (TRI_EqualString(key->_value._string.data, name)) { // remove key TRI_json_t* old = static_cast<TRI_json_t*>(TRI_AtVector(&object->_value._objects, i)); if (old != nullptr) { TRI_DestroyJson(zone, old); } TRI_RemoveVector(&object->_value._objects, i); // remove value old = static_cast<TRI_json_t*>(TRI_AtVector(&object->_value._objects, i)); if (old != nullptr) { TRI_DestroyJson(zone, old); } TRI_RemoveVector(&object->_value._objects, i); return true; } } return false; }
bool TRI_ReplaceObjectJson (TRI_memory_zone_t* zone, TRI_json_t* object, char const* name, TRI_json_t const* replacement) { TRI_ASSERT(object->_type == TRI_JSON_OBJECT); TRI_ASSERT(name != nullptr); size_t const n = TRI_LengthVector(&object->_value._objects); for (size_t i = 0; i < n; i += 2) { TRI_json_t* key = static_cast<TRI_json_t*>(TRI_AtVector(&object->_value._objects, i)); if (! IsString(key)) { continue; } if (TRI_EqualString(key->_value._string.data, name)) { // retrieve the old element TRI_json_t* old = static_cast<TRI_json_t*>(TRI_AtVector(&object->_value._objects, i + 1)); if (old != nullptr) { TRI_DestroyJson(zone, old); } TRI_json_t copy; TRI_CopyToJson(zone, ©, replacement); TRI_SetVector(&object->_value._objects, i + 1, ©); return true; } } // object not found in array, now simply add it TRI_Insert2ObjectJson(zone, object, name, replacement); return false; }
static bool CompactifyDocumentCollection (TRI_document_collection_t* document) { TRI_primary_collection_t* primary; TRI_vector_t vector; int64_t numAlive; size_t i, n; bool compactNext; compactNext = false; primary = &document->base; // if we cannot acquire the read lock instantly, we will exit directly. // otherwise we'll risk a multi-thread deadlock between synchroniser, // compactor and data-modification threads (e.g. POST /_api/document) if (! TRI_TRY_READ_LOCK_DATAFILES_DOC_COLLECTION(primary)) { return false; } n = primary->base._datafiles._length; if (primary->base._compactors._length > 0 || n == 0) { // we already have created a compactor file in progress. // if this happens, then a previous compaction attempt for this collection failed // additionally, if there are no datafiles, then there's no need to compact TRI_READ_UNLOCK_DATAFILES_DOC_COLLECTION(primary); return false; } // copy datafile information TRI_InitVector(&vector, TRI_UNKNOWN_MEM_ZONE, sizeof(compaction_info_t)); numAlive = 0; for (i = 0; i < n; ++i) { TRI_datafile_t* df; TRI_doc_datafile_info_t* dfi; compaction_info_t compaction; bool shouldCompact; df = primary->base._datafiles._buffer[i]; assert(df != NULL); dfi = TRI_FindDatafileInfoPrimaryCollection(primary, df->_fid, true); if (dfi == NULL) { continue; } shouldCompact = false; if (! compactNext && df->_maximalSize < COMPACTOR_MIN_SIZE && i < n - 1) { // very small datafile. let's compact it so it's merged with others shouldCompact = true; compactNext = true; } else if (numAlive == 0 && dfi->_numberDeletion > 0) { // compact first datafile already if it has got some deletions shouldCompact = true; compactNext = true; } else { // in all other cases, only check the number and size of "dead" objects if (dfi->_sizeDead >= (int64_t) COMPACTOR_DEAD_SIZE_THRESHOLD) { shouldCompact = true; compactNext = true; } else if (dfi->_sizeDead > 0) { // the size of dead objects is above some threshold double share = (double) dfi->_sizeDead / ((double) dfi->_sizeDead + (double) dfi->_sizeAlive); if (share >= COMPACTOR_DEAD_SIZE_SHARE) { // the size of dead objects is above some share shouldCompact = true; compactNext = true; } } } if (! shouldCompact) { // only use those datafiles that contain dead objects if (! compactNext) { numAlive += (int64_t) dfi->_numberAlive; continue; } } LOG_TRACE("found datafile eligible for compaction. fid: %llu, size: %llu " "numberDead: %llu, numberAlive: %llu, numberTransaction: %llu, numberDeletion: %llu, " "sizeDead: %llu, sizeAlive: %llu, sizeTransaction: %llu", (unsigned long long) df->_fid, (unsigned long long) df->_maximalSize, (unsigned long long) dfi->_numberDead, (unsigned long long) dfi->_numberAlive, (unsigned long long) dfi->_numberTransaction, (unsigned long long) dfi->_numberDeletion, (unsigned long long) dfi->_sizeDead, (unsigned long long) dfi->_sizeAlive, (unsigned long long) dfi->_sizeTransaction); compaction._datafile = df; compaction._keepDeletions = (numAlive > 0 && i > 0); TRI_PushBackVector(&vector, &compaction); // we stop at the first few datafiles. // this is better than going over all datafiles in a collection in one go // because the compactor is single-threaded, and collecting all datafiles // might take a long time (it might even be that there is a request to // delete the collection in the middle of compaction, but the compactor // will not pick this up as it is read-locking the collection status) if (TRI_LengthVector(&vector) >= COMPACTOR_MAX_FILES) { // found enough to compact break; } numAlive += (int64_t) dfi->_numberAlive; } // can now continue without the lock TRI_READ_UNLOCK_DATAFILES_DOC_COLLECTION(primary); if (vector._length == 0) { // cleanup local variables TRI_DestroyVector(&vector); return false; } // handle datafiles with dead objects n = vector._length; assert(n >= 1); CompactifyDatafiles(document, &vector); // cleanup local variables TRI_DestroyVector(&vector); return true; }
static TRI_json_t* MergeRecursive (TRI_memory_zone_t* zone, TRI_json_t const* lhs, TRI_json_t const* rhs, bool nullMeansRemove, bool mergeObjects) { TRI_ASSERT(lhs != nullptr); std::unique_ptr<TRI_json_t> result(TRI_CopyJson(zone, lhs)); if (result == nullptr) { return nullptr; } auto r = result.get(); // shortcut variable size_t const n = TRI_LengthVector(&rhs->_value._objects); for (size_t i = 0; i < n; i += 2) { // enumerate all the replacement values auto key = static_cast<TRI_json_t const*>(TRI_AtVector(&rhs->_value._objects, i)); auto value = static_cast<TRI_json_t const*>(TRI_AtVector(&rhs->_value._objects, i + 1)); if (value->_type == TRI_JSON_NULL && nullMeansRemove) { // replacement value is a null and we don't want to store nulls => delete attribute from the result TRI_DeleteObjectJson(zone, r, key->_value._string.data); } else { // replacement value is not a null or we want to store nulls TRI_json_t const* lhsValue = TRI_LookupObjectJson(lhs, key->_value._string.data); if (lhsValue == nullptr) { // existing array does not have the attribute => append new attribute if (value->_type == TRI_JSON_OBJECT && nullMeansRemove) { TRI_json_t empty; TRI_InitObjectJson(TRI_UNKNOWN_MEM_ZONE, &empty); TRI_json_t* merged = MergeRecursive(zone, &empty, value, nullMeansRemove, mergeObjects); if (merged == nullptr) { return nullptr; } TRI_json_t* copy = TRI_CopyJson(zone, value); if (copy == nullptr) { return nullptr; } TRI_Insert3ObjectJson(zone, r, key->_value._string.data, copy); } else { TRI_Insert3ObjectJson(zone, r, key->_value._string.data, TRI_CopyJson(zone, value)); } } else { // existing array already has the attribute => replace attribute if (lhsValue->_type == TRI_JSON_OBJECT && value->_type == TRI_JSON_OBJECT && mergeObjects) { TRI_json_t* merged = MergeRecursive(zone, lhsValue, value, nullMeansRemove, mergeObjects); if (merged == nullptr) { return nullptr; } TRI_ReplaceObjectJson(zone, r, key->_value._string.data, merged); TRI_FreeJson(zone, merged); } else { TRI_ReplaceObjectJson(zone, r, key->_value._string.data, value); } } } } return result.release(); }
size_t TRI_LengthVectorJson (TRI_json_t const* json) { TRI_ASSERT(json != nullptr && (json->_type == TRI_JSON_ARRAY || json->_type == TRI_JSON_OBJECT)); return TRI_LengthVector(&json->_value._objects); }
static int StringifyJson (TRI_memory_zone_t* zone, TRI_string_buffer_t* buffer, TRI_json_t const* object, bool braces) { int res; switch (object->_type) { case TRI_JSON_UNUSED: { break; } case TRI_JSON_NULL: { res = TRI_AppendString2StringBuffer(buffer, "null", 4); // strlen("null") if (res != TRI_ERROR_NO_ERROR) { return res; } break; } case TRI_JSON_BOOLEAN: { if (object->_value._boolean) { res = TRI_AppendString2StringBuffer(buffer, "true", 4); // strlen("true") } else { res = TRI_AppendString2StringBuffer(buffer, "false", 5); // strlen("false") } if (res != TRI_ERROR_NO_ERROR) { return res; } break; } case TRI_JSON_NUMBER: { res = TRI_AppendDoubleStringBuffer(buffer, object->_value._number); if (res != TRI_ERROR_NO_ERROR) { return res; } break; } case TRI_JSON_STRING: case TRI_JSON_STRING_REFERENCE: { res = TRI_AppendCharStringBuffer(buffer, '\"'); if (res != TRI_ERROR_NO_ERROR) { return res; } if (object->_value._string.length > 0) { // optimisation for the empty string res = TRI_AppendJsonEncodedStringStringBuffer(buffer, object->_value._string.data, object->_value._string.length - 1, false); if (res != TRI_ERROR_NO_ERROR) { return TRI_ERROR_OUT_OF_MEMORY; } } res = TRI_AppendCharStringBuffer(buffer, '\"'); if (res != TRI_ERROR_NO_ERROR) { return res; } break; } case TRI_JSON_OBJECT: { if (braces) { res = TRI_AppendCharStringBuffer(buffer, '{'); if (res != TRI_ERROR_NO_ERROR) { return res; } } size_t const n = TRI_LengthVector(&object->_value._objects); for (size_t i = 0; i < n; i += 2) { if (0 < i) { res = TRI_AppendCharStringBuffer(buffer, ','); if (res != TRI_ERROR_NO_ERROR) { return res; } } res = StringifyJson(zone, buffer, static_cast<TRI_json_t const*>(TRI_AtVector(&object->_value._objects, i)), true); if (res != TRI_ERROR_NO_ERROR) { return res; } res = TRI_AppendCharStringBuffer(buffer, ':'); if (res != TRI_ERROR_NO_ERROR) { return res; } res = StringifyJson(zone, buffer, static_cast<TRI_json_t const*>(TRI_AtVector(&object->_value._objects, i + 1)), true); if (res != TRI_ERROR_NO_ERROR) { return res; } } if (braces) { res = TRI_AppendCharStringBuffer(buffer, '}'); if (res != TRI_ERROR_NO_ERROR) { return res; } } break; } case TRI_JSON_ARRAY: { if (braces) { res = TRI_AppendCharStringBuffer(buffer, '['); if (res != TRI_ERROR_NO_ERROR) { return res; } } size_t const n = TRI_LengthVector(&object->_value._objects); for (size_t i = 0; i < n; ++i) { if (0 < i) { res = TRI_AppendCharStringBuffer(buffer, ','); if (res != TRI_ERROR_NO_ERROR) { return res; } } res = StringifyJson(zone, buffer, static_cast<TRI_json_t const*>(TRI_AtVector(&object->_value._objects, i)), true); if (res != TRI_ERROR_NO_ERROR) { return res; } } if (braces) { res = TRI_AppendCharStringBuffer(buffer, ']'); if (res != TRI_ERROR_NO_ERROR) { return res; } } break; } } 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; }
bool TRI_HasDuplicateKeyJson (TRI_json_t const* object) { if (object && object->_type == TRI_JSON_OBJECT) { size_t const n = TRI_LengthVector(&object->_value._objects); const bool hasMultipleElements = (n > 2); // if we don't have attributes, we do not need to check for duplicates // if we only have one attribute, we don't need to check for duplicates in // the array, but we need to recursively validate the array values (if // array value itself is an array) if (n > 0) { TRI_associative_pointer_t hash; size_t i; if (hasMultipleElements) { TRI_InitAssociativePointer(&hash, TRI_UNKNOWN_MEM_ZONE, &TRI_HashStringKeyAssociativePointer, &TRI_HashStringKeyAssociativePointer, &TRI_EqualStringKeyAssociativePointer, 0); } for (i = 0; i < n; i += 2) { auto key = static_cast<TRI_json_t const*>(TRI_AtVector(&object->_value._objects, i)); if (! TRI_IsStringJson(key)) { continue; } auto value = static_cast<TRI_json_t const*>(TRI_AtVector(&object->_value._objects, i + 1)); // recursively check sub-array elements if (value->_type == TRI_JSON_OBJECT && TRI_HasDuplicateKeyJson(value)) { // duplicate found in sub-array if (hasMultipleElements) { TRI_DestroyAssociativePointer(&hash); } return true; } if (hasMultipleElements) { void* previous = TRI_InsertKeyAssociativePointer(&hash, key->_value._string.data, key->_value._string.data, false); if (previous != nullptr) { // duplicate found TRI_DestroyAssociativePointer(&hash); return true; } } } if (hasMultipleElements) { TRI_DestroyAssociativePointer(&hash); } } } // no duplicate found return false; }
static uint64_t HashJsonRecursive (uint64_t hash, TRI_json_t const* object) { if (nullptr == object) { return HashBlock(hash, "null", 4); // strlen("null") } switch (object->_type) { case TRI_JSON_UNUSED: { return hash; } case TRI_JSON_NULL: { return HashBlock(hash, "null", 4); // strlen("null") } case TRI_JSON_BOOLEAN: { if (object->_value._boolean) { return HashBlock(hash, "true", 4); // strlen("true") } else { return HashBlock(hash, "false", 5); // strlen("true") } } case TRI_JSON_NUMBER: { return HashBlock(hash, (char const*) &(object->_value._number), sizeof(object->_value._number)); } case TRI_JSON_STRING: case TRI_JSON_STRING_REFERENCE: { return HashBlock(hash, object->_value._string.data, object->_value._string.length); } case TRI_JSON_OBJECT: { hash = HashBlock(hash, "array", 5); // strlen("array") size_t const n = TRI_LengthVector(&object->_value._objects); uint64_t tmphash = hash; for (size_t i = 0; i < n; i += 2) { auto subjson = static_cast<TRI_json_t const*>(TRI_AddressVector(&object->_value._objects, i)); TRI_ASSERT(TRI_IsStringJson(subjson)); tmphash ^= HashJsonRecursive(hash, subjson); subjson = static_cast<TRI_json_t const*>(TRI_AddressVector(&object->_value._objects, i + 1)); tmphash ^= HashJsonRecursive(hash, subjson); } return tmphash; } case TRI_JSON_ARRAY: { hash = HashBlock(hash, "list", 4); // strlen("list") size_t const n = TRI_LengthVector(&object->_value._objects); for (size_t i = 0; i < n; ++i) { auto subjson = static_cast<TRI_json_t const*>(TRI_AddressVector(&object->_value._objects, i)); hash = HashJsonRecursive(hash, subjson); } return hash; } } return hash; // never reached }