mirror of https://gitee.com/bigwinds/arangodb
move json-utilities to the place where it is solely used
This commit is contained in:
parent
f3d836c86d
commit
eea35223de
|
@ -25,10 +25,10 @@
|
||||||
#include "Basics/Exceptions.h"
|
#include "Basics/Exceptions.h"
|
||||||
#include "Basics/ReadWriteLock.h"
|
#include "Basics/ReadWriteLock.h"
|
||||||
#include "Basics/ReadLocker.h"
|
#include "Basics/ReadLocker.h"
|
||||||
|
#include "Basics/Utf8Helper.h"
|
||||||
#include "Basics/WriteLocker.h"
|
#include "Basics/WriteLocker.h"
|
||||||
#include "Basics/hashes.h"
|
#include "Basics/hashes.h"
|
||||||
#include "Basics/json.h"
|
#include "Basics/json.h"
|
||||||
#include "Basics/json-utilities.h"
|
|
||||||
#include "Basics/tri-strings.h"
|
#include "Basics/tri-strings.h"
|
||||||
#include "VocBase/vocbase.h"
|
#include "VocBase/vocbase.h"
|
||||||
#include "V8/v8-conv.h"
|
#include "V8/v8-conv.h"
|
||||||
|
@ -36,6 +36,403 @@
|
||||||
|
|
||||||
using namespace arangodb;
|
using namespace arangodb;
|
||||||
|
|
||||||
|
int TRI_CompareValuesJson(TRI_json_t const* lhs, TRI_json_t const* rhs,
|
||||||
|
bool useUTF8 = 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_Insert3ObjectJson(zone, r, key->_value._string.data, merged);
|
||||||
|
} else {
|
||||||
|
TRI_json_t* copy = TRI_CopyJson(zone, value);
|
||||||
|
|
||||||
|
if (copy == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
TRI_Insert3ObjectJson(zone, r, key->_value._string.data, copy);
|
||||||
|
}
|
||||||
|
} 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief get type weight of a json value usable for comparison and sorting
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
static int TypeWeight(TRI_json_t const* value) {
|
||||||
|
if (value != nullptr) {
|
||||||
|
switch (value->_type) {
|
||||||
|
case TRI_JSON_BOOLEAN:
|
||||||
|
return 1;
|
||||||
|
case TRI_JSON_NUMBER:
|
||||||
|
return 2;
|
||||||
|
case TRI_JSON_STRING:
|
||||||
|
case TRI_JSON_STRING_REFERENCE:
|
||||||
|
// a string reference has the same weight as a regular string
|
||||||
|
return 3;
|
||||||
|
case TRI_JSON_ARRAY:
|
||||||
|
return 4;
|
||||||
|
case TRI_JSON_OBJECT:
|
||||||
|
return 5;
|
||||||
|
case TRI_JSON_NULL:
|
||||||
|
case TRI_JSON_UNUSED:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief uniquify a sorted json list into a new array
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
static TRI_json_t* 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) {
|
||||||
|
int res = TRI_PushBackArrayJson(TRI_UNKNOWN_MEM_ZONE, result.get(), p);
|
||||||
|
|
||||||
|
if (res != TRI_ERROR_NO_ERROR) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// remember last element
|
||||||
|
last = p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief callback function used for json value sorting
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
static int CompareJson(void const* lhs, void const* rhs) {
|
||||||
|
return TRI_CompareValuesJson(static_cast<TRI_json_t const*>(lhs),
|
||||||
|
static_cast<TRI_json_t const*>(rhs), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief sorts a json array in place
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
static TRI_json_t* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief merge two arrays of array keys, sort them and return a combined array
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
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));
|
||||||
|
int res = TRI_PushBackArrayJson(TRI_UNKNOWN_MEM_ZONE, keys.get(), key);
|
||||||
|
|
||||||
|
if (res != TRI_ERROR_NO_ERROR) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
int res = TRI_PushBackArrayJson(TRI_UNKNOWN_MEM_ZONE, keys.get(), key);
|
||||||
|
|
||||||
|
if (res != TRI_ERROR_NO_ERROR) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort the key array in place
|
||||||
|
SortArrayJson(keys.get());
|
||||||
|
|
||||||
|
// array is now sorted
|
||||||
|
return UniquifyArrayJson(keys.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief compare two json values
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
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(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) {
|
||||||
|
THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/// @brief merge two JSON documents into one
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
TRI_json_t* TRI_MergeJson(TRI_memory_zone_t* zone, TRI_json_t const* lhs,
|
||||||
|
TRI_json_t const* rhs, bool nullMeansRemove,
|
||||||
|
bool mergeObjects) {
|
||||||
|
TRI_ASSERT(lhs->_type == TRI_JSON_OBJECT);
|
||||||
|
TRI_ASSERT(rhs->_type == TRI_JSON_OBJECT);
|
||||||
|
|
||||||
|
return MergeRecursive(zone, lhs, rhs, nullMeansRemove, mergeObjects);
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief converts a TRI_json_t into a V8 object
|
/// @brief converts a TRI_json_t into a V8 object
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -1,427 +0,0 @@
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// DISCLAIMER
|
|
||||||
///
|
|
||||||
/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
|
|
||||||
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
|
|
||||||
///
|
|
||||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
/// you may not use this file except in compliance with the License.
|
|
||||||
/// You may obtain a copy of the License at
|
|
||||||
///
|
|
||||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
///
|
|
||||||
/// Unless required by applicable law or agreed to in writing, software
|
|
||||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
/// See the License for the specific language governing permissions and
|
|
||||||
/// limitations under the License.
|
|
||||||
///
|
|
||||||
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
|
||||||
///
|
|
||||||
/// @author Jan Steemann
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
#include "json-utilities.h"
|
|
||||||
#include "Basics/fasthash.h"
|
|
||||||
#include "Basics/hashes.h"
|
|
||||||
#include "Basics/StringBuffer.h"
|
|
||||||
#include "Basics/Utf8Helper.h"
|
|
||||||
#include "Basics/VelocyPackHelper.h"
|
|
||||||
|
|
||||||
#include <velocypack/Builder.h>
|
|
||||||
#include <velocypack/velocypack-aliases.h>
|
|
||||||
|
|
||||||
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_Insert3ObjectJson(zone, r, key->_value._string.data, merged);
|
|
||||||
} else {
|
|
||||||
TRI_json_t* copy = TRI_CopyJson(zone, value);
|
|
||||||
|
|
||||||
if (copy == nullptr) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
TRI_Insert3ObjectJson(zone, r, key->_value._string.data, copy);
|
|
||||||
}
|
|
||||||
} 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief get type weight of a json value usable for comparison and sorting
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
static int TypeWeight(TRI_json_t const* value) {
|
|
||||||
if (value != nullptr) {
|
|
||||||
switch (value->_type) {
|
|
||||||
case TRI_JSON_BOOLEAN:
|
|
||||||
return 1;
|
|
||||||
case TRI_JSON_NUMBER:
|
|
||||||
return 2;
|
|
||||||
case TRI_JSON_STRING:
|
|
||||||
case TRI_JSON_STRING_REFERENCE:
|
|
||||||
// a string reference has the same weight as a regular string
|
|
||||||
return 3;
|
|
||||||
case TRI_JSON_ARRAY:
|
|
||||||
return 4;
|
|
||||||
case TRI_JSON_OBJECT:
|
|
||||||
return 5;
|
|
||||||
case TRI_JSON_NULL:
|
|
||||||
case TRI_JSON_UNUSED:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief uniquify a sorted json list into a new array
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
static TRI_json_t* 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) {
|
|
||||||
int res = TRI_PushBackArrayJson(TRI_UNKNOWN_MEM_ZONE, result.get(), p);
|
|
||||||
|
|
||||||
if (res != TRI_ERROR_NO_ERROR) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// remember last element
|
|
||||||
last = p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief callback function used for json value sorting
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
static int CompareJson(void const* lhs, void const* rhs) {
|
|
||||||
return TRI_CompareValuesJson(static_cast<TRI_json_t const*>(lhs),
|
|
||||||
static_cast<TRI_json_t const*>(rhs), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief sorts a json array in place
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
static TRI_json_t* 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief merge two arrays of array keys, sort them and return a combined array
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
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));
|
|
||||||
int res = TRI_PushBackArrayJson(TRI_UNKNOWN_MEM_ZONE, keys.get(), key);
|
|
||||||
|
|
||||||
if (res != TRI_ERROR_NO_ERROR) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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));
|
|
||||||
int res = TRI_PushBackArrayJson(TRI_UNKNOWN_MEM_ZONE, keys.get(), key);
|
|
||||||
|
|
||||||
if (res != TRI_ERROR_NO_ERROR) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// sort the key array in place
|
|
||||||
SortArrayJson(keys.get());
|
|
||||||
|
|
||||||
// array is now sorted
|
|
||||||
return UniquifyArrayJson(keys.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief compare two json values
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
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(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) {
|
|
||||||
THROW_ARANGO_EXCEPTION(TRI_ERROR_OUT_OF_MEMORY);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief merge two JSON documents into one
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
TRI_json_t* TRI_MergeJson(TRI_memory_zone_t* zone, TRI_json_t const* lhs,
|
|
||||||
TRI_json_t const* rhs, bool nullMeansRemove,
|
|
||||||
bool mergeObjects) {
|
|
||||||
TRI_ASSERT(lhs->_type == TRI_JSON_OBJECT);
|
|
||||||
TRI_ASSERT(rhs->_type == TRI_JSON_OBJECT);
|
|
||||||
|
|
||||||
return MergeRecursive(zone, lhs, rhs, nullMeansRemove, mergeObjects);
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,60 +0,0 @@
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// DISCLAIMER
|
|
||||||
///
|
|
||||||
/// Copyright 2014-2016 ArangoDB GmbH, Cologne, Germany
|
|
||||||
/// Copyright 2004-2014 triAGENS GmbH, Cologne, Germany
|
|
||||||
///
|
|
||||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
/// you may not use this file except in compliance with the License.
|
|
||||||
/// You may obtain a copy of the License at
|
|
||||||
///
|
|
||||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
///
|
|
||||||
/// Unless required by applicable law or agreed to in writing, software
|
|
||||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
/// See the License for the specific language governing permissions and
|
|
||||||
/// limitations under the License.
|
|
||||||
///
|
|
||||||
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
|
|
||||||
///
|
|
||||||
/// @author Jan Steemann
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
#ifndef ARANGODB_BASICS_JSON__UTILITIES_H
|
|
||||||
#define ARANGODB_BASICS_JSON__UTILITIES_H 1
|
|
||||||
|
|
||||||
#include "Basics/Common.h"
|
|
||||||
#include "Basics/json.h"
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief compare two json values
|
|
||||||
///
|
|
||||||
/// the values are first compared by their types, and only by their values if
|
|
||||||
/// the types are the same
|
|
||||||
/// returns -1 if lhs is smaller than rhs, 0 if lhs == rhs, and 1 if rhs is
|
|
||||||
/// greater than lhs.
|
|
||||||
///
|
|
||||||
/// If useUTF8 is set to true, strings will be converted using proper UTF8,
|
|
||||||
/// otherwise, strcmp is used, so it should only be used to test for equality
|
|
||||||
/// in this case.
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
int TRI_CompareValuesJson(TRI_json_t const*, TRI_json_t const*,
|
|
||||||
bool useUTF8 = true);
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief merge two JSON documents into one
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
TRI_json_t* TRI_MergeJson(TRI_memory_zone_t*, TRI_json_t const*,
|
|
||||||
TRI_json_t const*, bool, bool);
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
/// @brief compute a hash value for a JSON document, using fasthash64.
|
|
||||||
/// This is slightly faster than the FNV-based hashing
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
uint64_t TRI_FastHashJson(TRI_json_t const* json);
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -152,7 +152,6 @@ add_library(${LIB_ARANGO} STATIC
|
||||||
Basics/files.cpp
|
Basics/files.cpp
|
||||||
Basics/fpconv.cpp
|
Basics/fpconv.cpp
|
||||||
Basics/hashes.cpp
|
Basics/hashes.cpp
|
||||||
Basics/json-utilities.cpp
|
|
||||||
Basics/json.cpp
|
Basics/json.cpp
|
||||||
Basics/levenshtein.cpp
|
Basics/levenshtein.cpp
|
||||||
Basics/memory.cpp
|
Basics/memory.cpp
|
||||||
|
|
Loading…
Reference in New Issue