in database/src/desktop/query_params_comparator.cc [137:264]
int QueryParamsComparator::CompareValues(const Variant& variant_a,
const Variant& variant_b) {
const Variant* value_a = GetVariantValue(&variant_a);
const Variant* value_b = GetVariantValue(&variant_b);
// The order of this enum matters. This order matches the order that RTDB
// organizes nodes in if they are different types.
enum Precedence {
kPrecedenceFirst,
kPrecedenceNull,
kPrecedenceBoolean,
kPrecedenceNumber,
kPrecedenceString,
kPrecedenceMap,
kPrecedenceLast,
kPrecedenceSentinel,
kPrecedenceError,
};
// Map the precedence to the equivalent Variant types.
// StaticBlob and MutableBlob values get special treatment here - they are
// used as a speical sentinel values that will always be considered the first
// or last element respectively. The server will never send down a Blob, so it
// can be savely used here with a special meaning.
static const Precedence kPrecedenceLookupTable[] = {
kPrecedenceNull, // kTypeNull
kPrecedenceNumber, // kTypeInt64
kPrecedenceNumber, // kTypeDouble
kPrecedenceBoolean, // kTypeBool
kPrecedenceString, // kTypeStaticString
kPrecedenceString, // kTypeMutableString
kPrecedenceError, // kTypeVector
kPrecedenceMap, // kTypeMap
kPrecedenceSentinel, // kTypeStaticBlob
kPrecedenceError, // kTypeMutableBlob
};
Variant::Type type_a = value_a->type();
Variant::Type type_b = value_b->type();
Precedence precedence_a = kPrecedenceLookupTable[type_a];
Precedence precedence_b = kPrecedenceLookupTable[type_b];
// If we encounted a special sentinel value, figure out what we actually want
// the precendence to be.
if (precedence_a == kPrecedenceSentinel) {
assert(*value_a == kMinVariant || *value_a == kMaxVariant);
precedence_a =
(*value_a == kMinVariant) ? kPrecedenceFirst : kPrecedenceLast;
}
if (precedence_b == kPrecedenceSentinel) {
assert(*value_b == kMinVariant || *value_b == kMaxVariant);
precedence_b =
(*value_b == kMinVariant) ? kPrecedenceFirst : kPrecedenceLast;
}
// Values coming down from the server should never contain blobs or vectors.
assert(precedence_a != kPrecedenceError);
assert(precedence_b != kPrecedenceError);
// If we have different priorities we don't need to compare the values
// themselves. Just return the difference between the priorities.
if (precedence_a != precedence_b) {
return precedence_a - precedence_b;
}
// If the priority is the same we need to compare the value of the types.
switch (precedence_a) {
case kPrecedenceFirst: {
// Two first elements are equal. Once can't be more first than the other.
return 0;
}
case kPrecedenceNull: {
// Null types are always equal to other null types.
return 0;
}
case kPrecedenceBoolean: {
return (value_a->bool_value() == value_b->bool_value())
? 0
: (value_a->bool_value() ? 1 : -1);
}
case kPrecedenceNumber: {
// If they're both integers.
if (type_a == Variant::kTypeInt64 && type_b == Variant::kTypeInt64) {
int64_t int64_a = value_a->int64_value();
int64_t int64_b = value_b->int64_value();
if (int64_a < int64_b) return -1;
if (int64_a > int64_b) return 1;
return 0;
}
// At least one of them is a double, so we treat them both as doubles.
// TODO(amablue): Technically, this comparison is imperfect when comparing
// certain values int64 values that can't be perfectly cast to doubles.
// Look into improving this comparison. This is good enough for now
// though, since it's what the Android imlementation does.
assert(type_a == Variant::kTypeDouble || type_b == Variant::kTypeDouble);
double double_a = (type_a == Variant::kTypeDouble)
? value_a->double_value()
: static_cast<double>(value_a->int64_value());
double double_b = (type_b == Variant::kTypeDouble)
? value_b->double_value()
: static_cast<double>(value_b->int64_value());
if (double_a < double_b) return -1;
if (double_a > double_b) return 1;
return 0;
}
case kPrecedenceString: {
return strcmp(value_a->string_value(), value_b->string_value());
}
case kPrecedenceMap: {
// Maps are not compared against each other. Treat them as equal.
return 0;
}
case kPrecedenceLast: {
// Two last elements are equal. Once can't be more last than the other.
return 0;
}
case kPrecedenceSentinel:
FIREBASE_CASE_FALLTHROUGH;
case kPrecedenceError: {
// Assert for this condition should be caught above, but it's included
// here for completeness sake (and lint).
assert(0);
}
}
return 0;
}