int VelocyPackHelper::compareNumberValues(VPackValueType lhsType, VPackSlice lhs, VPackSlice rhs) { if (lhsType == rhs.type()) { // both types are equal if (lhsType == VPackValueType::Int || lhsType == VPackValueType::SmallInt) { // use exact comparisons. no need to cast to double int64_t l = lhs.getInt(); int64_t r = rhs.getInt(); if (l == r) { return 0; } return (l < r ? -1 : 1); } if (lhsType == VPackValueType::UInt) { // use exact comparisons. no need to cast to double uint64_t l = lhs.getUInt(); uint64_t r = rhs.getUInt(); if (l == r) { return 0; } return (l < r ? -1 : 1); } // fallthrough to double comparison } double left = lhs.getNumericValue<double>(); double right = rhs.getNumericValue<double>(); if (left == right) { return 0; } return (left < right ? -1 : 1); }
double VelocyPackHelper::toDouble(VPackSlice const& slice, bool& failed) { TRI_ASSERT(!slice.isNone()); failed = false; switch (slice.type()) { case VPackValueType::None: case VPackValueType::Null: return 0.0; case VPackValueType::Bool: return (slice.getBoolean() ? 1.0 : 0.0); case VPackValueType::Double: case VPackValueType::Int: case VPackValueType::UInt: case VPackValueType::SmallInt: return slice.getNumericValue<double>(); case VPackValueType::String: { std::string tmp(slice.copyString()); try { // try converting string to number return std::stod(tmp); } catch (...) { if (tmp.empty()) { return 0.0; } // conversion failed } break; } case VPackValueType::Array: { VPackValueLength const n = slice.length(); if (n == 0) { return 0.0; } else if (n == 1) { return VelocyPackHelper::toDouble(slice.at(0).resolveExternal(), failed); } break; } case VPackValueType::External: { return VelocyPackHelper::toDouble(slice.resolveExternal(), failed); } case VPackValueType::Illegal: case VPackValueType::Object: case VPackValueType::UTCDate: case VPackValueType::MinKey: case VPackValueType::MaxKey: case VPackValueType::Binary: case VPackValueType::BCD: case VPackValueType::Custom: break; } failed = true; return 0.0; }
static int TypeWeight(VPackSlice const& slice) { switch (slice.type()) { case VPackValueType::MinKey: return -99; // must be lowest case VPackValueType::Illegal: return -1; case VPackValueType::None: case VPackValueType::Null: return 0; case VPackValueType::Bool: return 1; case VPackValueType::Double: case VPackValueType::Int: case VPackValueType::UInt: case VPackValueType::SmallInt: case VPackValueType::UTCDate: case VPackValueType::BCD: return 2; case VPackValueType::String: case VPackValueType::Binary: case VPackValueType::Custom: // custom type is used for _id (which is a string) return 3; case VPackValueType::Array: return 4; case VPackValueType::Object: return 5; case VPackValueType::External: return TypeWeight(slice.resolveExternal()); case VPackValueType::MaxKey: return 99; // must be highest default: // All other values have equal weight return 0; } }
v8::Handle<v8::Value> TRI_VPackToV8(v8::Isolate* isolate, VPackSlice const& slice, VPackOptions const* options, VPackSlice const* base) { switch (slice.type()) { case VPackValueType::Null: { return v8::Null(isolate); } case VPackValueType::Bool: { return v8::Boolean::New(isolate, slice.getBool()); } case VPackValueType::Double: { // convert NaN, +inf & -inf to null double value = slice.getDouble(); if (std::isnan(value) || !std::isfinite(value) || value == HUGE_VAL || value == -HUGE_VAL) { return v8::Null(isolate); } return v8::Number::New(isolate, slice.getDouble()); } case VPackValueType::Int: { int64_t value = slice.getInt(); if (value >= -2147483648LL && value <= 2147483647LL) { // value is within bounds of an int32_t return v8::Integer::New(isolate, static_cast<int32_t>(value)); } if (value >= 0 && value <= 4294967295LL) { // value is within bounds of a uint32_t return v8::Integer::NewFromUnsigned(isolate, static_cast<uint32_t>(value)); } // must use double to avoid truncation return v8::Number::New(isolate, static_cast<double>(slice.getInt())); } case VPackValueType::UInt: { uint64_t value = slice.getUInt(); if (value <= 4294967295ULL) { // value is within bounds of a uint32_t return v8::Integer::NewFromUnsigned(isolate, static_cast<uint32_t>(value)); } // must use double to avoid truncation return v8::Number::New(isolate, static_cast<double>(slice.getUInt())); } case VPackValueType::SmallInt: { return v8::Integer::New(isolate, slice.getNumericValue<int32_t>()); } case VPackValueType::String: { return ObjectVPackString(isolate, slice); } case VPackValueType::Array: { return ObjectVPackArray(isolate, slice, options, base); } case VPackValueType::Object: { return ObjectVPackObject(isolate, slice, options, base); } case VPackValueType::External: { // resolve external return TRI_VPackToV8(isolate, VPackSlice(slice.getExternal()), options, base); } case VPackValueType::Custom: { if (options == nullptr || options->customTypeHandler == nullptr || base == nullptr) { THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "Could not extract custom attribute."); } std::string id = options->customTypeHandler->toString(slice, options, *base); return TRI_V8_STD_STRING(id); } case VPackValueType::None: default: { return v8::Undefined(isolate); } } }
int VelocyPackHelper::compare(VPackSlice lhs, VPackSlice rhs, bool useUTF8, VPackOptions const* options, VPackSlice const* lhsBase, VPackSlice const* rhsBase) { { // will resolve externals... int lWeight = TypeWeight(lhs); int rWeight = TypeWeight(rhs); if (lWeight < rWeight) { return -1; } if (lWeight > rWeight) { return 1; } TRI_ASSERT(lWeight == rWeight); } lhs = lhs.resolveExternal(); // follow externals rhs = rhs.resolveExternal(); // follow externals // lhs and rhs have equal weights if (lhs.isNone() || rhs.isNone()) { // either lhs or rhs is none. we cannot be sure here that both are // nones. // there can also exist the situation that lhs is a none and rhs is a // null value // (or vice versa). Anyway, the compare value is the same for both, return 0; } auto lhsType = lhs.type(); switch (lhsType) { case VPackValueType::Illegal: case VPackValueType::MinKey: case VPackValueType::MaxKey: case VPackValueType::None: case VPackValueType::Null: return 0; case VPackValueType::Bool: { bool left = lhs.getBoolean(); bool right = rhs.getBoolean(); if (left == right) { return 0; } if (!left && right) { return -1; } return 1; } case VPackValueType::Double: case VPackValueType::Int: case VPackValueType::UInt: case VPackValueType::SmallInt: { return compareNumberValues(lhsType, lhs, rhs); } case VPackValueType::Custom: case VPackValueType::String: { std::string lhsString; VPackValueLength nl; char const* left; if (lhs.isCustom()) { if (lhsBase == nullptr || options == nullptr || options->customTypeHandler == nullptr) { THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "Could not extract custom attribute."); } lhsString.assign(options->customTypeHandler->toString(lhs, options, *lhsBase)); left = lhsString.c_str(); nl = lhsString.size(); } else { left = lhs.getString(nl); } TRI_ASSERT(left != nullptr); std::string rhsString; VPackValueLength nr; char const* right; if (rhs.isCustom()) { if (rhsBase == nullptr || options == nullptr || options->customTypeHandler == nullptr) { THROW_ARANGO_EXCEPTION_MESSAGE(TRI_ERROR_INTERNAL, "Could not extract custom attribute."); } rhsString.assign(options->customTypeHandler->toString(rhs, options, *rhsBase)); right = rhsString.c_str(); nr = rhsString.size(); } else { right = rhs.getString(nr); } TRI_ASSERT(right != nullptr); int res; if (useUTF8) { res = TRI_compare_utf8(left, static_cast<size_t>(nl), right, static_cast<size_t>(nr)); } else { size_t len = static_cast<size_t>(nl < nr ? nl : nr); res = memcmp(left, right, len); } if (res < 0) { return -1; } if (res > 0) { return 1; } // res == 0 if (nl == nr) { return 0; } // res == 0, but different string lengths return nl < nr ? -1 : 1; } case VPackValueType::Array: { VPackValueLength const nl = lhs.length(); VPackValueLength const nr = rhs.length(); VPackValueLength const n = (std::max)(nr, nl); for (VPackValueLength i = 0; i < n; ++i) { VPackSlice lhsValue; if (i < nl) { lhsValue = lhs.at(i).resolveExternal(); } VPackSlice rhsValue; if (i < nr) { rhsValue = rhs.at(i).resolveExternal(); } int result = compare(lhsValue, rhsValue, useUTF8, options, &lhs, &rhs); if (result != 0) { return result; } } return 0; } case VPackValueType::Object: { std::set<std::string, AttributeSorterUTF8> keys; VPackCollection::keys(lhs, keys); VPackCollection::keys(rhs, keys); for (auto const& key : keys) { VPackSlice lhsValue = lhs.get(key).resolveExternal(); if (lhsValue.isNone()) { // not present => null lhsValue = VPackSlice::nullSlice(); } VPackSlice rhsValue = rhs.get(key).resolveExternal(); if (rhsValue.isNone()) { // not present => null rhsValue = VPackSlice::nullSlice(); } int result = compare(lhsValue, rhsValue, useUTF8, options, &lhs, &rhs); if (result != 0) { return result; } } return 0; } default: // Contains all other ValueTypes of VelocyPack. // They are not used in ArangoDB so this cannot occur TRI_ASSERT(false); return 0; } }