Skip to content
Snippets Groups Projects
Commit a81c084d authored by Nat Goodspeed's avatar Nat Goodspeed
Browse files

Add llsd_equals(), a function whose absence sorely puzzles me

parent 01d39082
No related branches found
No related tags found
No related merge requests found
...@@ -576,3 +576,95 @@ std::string llsd_matches(const LLSD& prototype, const LLSD& data, const std::str ...@@ -576,3 +576,95 @@ std::string llsd_matches(const LLSD& prototype, const LLSD& data, const std::str
// bad LLSD doesn't define isConvertible(Type to, Type from). // bad LLSD doesn't define isConvertible(Type to, Type from).
return match_types(prototype.type(), TypeVector(), data.type(), pfx); return match_types(prototype.type(), TypeVector(), data.type(), pfx);
} }
bool llsd_equals(const LLSD& lhs, const LLSD& rhs)
{
// We're comparing strict equality of LLSD representation rather than
// performing any conversions. So if the types aren't equal, the LLSD
// values aren't equal.
if (lhs.type() != rhs.type())
{
return false;
}
// Here we know both types are equal. Now compare values.
switch (lhs.type())
{
case LLSD::TypeUndefined:
// Both are TypeUndefined. There's nothing more to know.
return true;
#define COMPARE_SCALAR(type) \
case LLSD::Type##type: \
/* LLSD::URI has operator!=() but not operator==() */ \
/* rely on the optimizer for all others */ \
return (! (lhs.as##type() != rhs.as##type()))
COMPARE_SCALAR(Boolean);
COMPARE_SCALAR(Integer);
// The usual caveats about comparing floating-point numbers apply. This is
// only useful when we expect identical bit representation for a given
// Real value, e.g. for integer-valued Reals.
COMPARE_SCALAR(Real);
COMPARE_SCALAR(String);
COMPARE_SCALAR(UUID);
COMPARE_SCALAR(Date);
COMPARE_SCALAR(URI);
COMPARE_SCALAR(Binary);
#undef COMPARE_SCALAR
case LLSD::TypeArray:
{
LLSD::array_const_iterator
lai(lhs.beginArray()), laend(lhs.endArray()),
rai(rhs.beginArray()), raend(rhs.endArray());
// Compare array elements, walking the two arrays in parallel.
for ( ; lai != laend && rai != raend; ++lai, ++rai)
{
// If any one array element is unequal, the arrays are unequal.
if (! llsd_equals(*lai, *rai))
return false;
}
// Here we've reached the end of one or the other array. They're equal
// only if they're BOTH at end: that is, if they have equal length too.
return (lai == laend && rai == raend);
}
case LLSD::TypeMap:
{
// Build a set of all rhs keys.
std::set<LLSD::String> rhskeys;
for (LLSD::map_const_iterator rmi(rhs.beginMap()), rmend(rhs.endMap());
rmi != rmend; ++rmi)
{
rhskeys.insert(rmi->first);
}
// Now walk all the lhs keys.
for (LLSD::map_const_iterator lmi(lhs.beginMap()), lmend(lhs.endMap());
lmi != lmend; ++lmi)
{
// Try to erase this lhs key from the set of rhs keys. If rhs has
// no such key, the maps are unequal. erase(key) returns count of
// items erased.
if (rhskeys.erase(lmi->first) != 1)
return false;
// Both maps have the current key. Compare values.
if (! llsd_equals(lmi->second, rhs[lmi->first]))
return false;
}
// We've now established that all the lhs keys have equal values in
// both maps. The maps are equal unless rhs contains a superset of
// those keys.
return rhskeys.empty();
}
default:
// We expect that every possible type() value is specifically handled
// above. Failing to extend this switch to support a new LLSD type is
// an error that must be brought to the coder's attention.
LL_ERRS("llsd_equals") << "llsd_equals(" << lhs << ", " << rhs << "): "
"unknown type " << lhs.type() << LL_ENDL;
return false; // pacify the compiler
}
}
...@@ -129,6 +129,9 @@ LL_COMMON_API BOOL compare_llsd_with_template( ...@@ -129,6 +129,9 @@ LL_COMMON_API BOOL compare_llsd_with_template(
*/ */
LL_COMMON_API std::string llsd_matches(const LLSD& prototype, const LLSD& data, const std::string& pfx=""); LL_COMMON_API std::string llsd_matches(const LLSD& prototype, const LLSD& data, const std::string& pfx="");
/// Deep equality
bool llsd_equals(const LLSD& lhs, const LLSD& rhs);
// Simple function to copy data out of input & output iterators if // Simple function to copy data out of input & output iterators if
// there is no need for casting. // there is no need for casting.
template<typename Input> LLSD llsd_copy_array(Input iter, Input end) template<typename Input> LLSD llsd_copy_array(Input iter, Input end)
......
...@@ -45,6 +45,7 @@ ...@@ -45,6 +45,7 @@
#include "llquaternion.h" #include "llquaternion.h"
#include "llsdutil.h" #include "llsdutil.h"
#include "llsdutil_math.h" #include "llsdutil_math.h"
#include "stringize.h"
#include <set> #include <set>
#include <boost/range.hpp> #include <boost/range.hpp>
...@@ -207,14 +208,16 @@ namespace tut ...@@ -207,14 +208,16 @@ namespace tut
map.insert("URI", LLSD::URI()); map.insert("URI", LLSD::URI());
map.insert("Binary", LLSD::Binary()); map.insert("Binary", LLSD::Binary());
map.insert("Map", LLSD().insert("foo", LLSD())); map.insert("Map", LLSD().insert("foo", LLSD()));
// array can't be constructed on the fly // Only an empty array can be constructed on the fly
LLSD array; LLSD array;
array.append(LLSD()); array.append(LLSD());
map.insert("Array", array); map.insert("Array", array);
// These iterators are declared outside our various for loops to avoid // These iterators are declared outside our various for loops to avoid
// fatal MSVC warning: "I used to be broken, but I'm all better now!" // fatal MSVC warning: "I used to be broken, but I'm all better now!"
LLSD::map_const_iterator mi(map.beginMap()), mend(map.endMap()); LLSD::map_const_iterator mi, mend(map.endMap());
/*-------------------------- llsd_matches --------------------------*/
// empty prototype matches anything // empty prototype matches anything
for (mi = map.beginMap(); mi != mend; ++mi) for (mi = map.beginMap(); mi != mend; ++mi)
...@@ -337,5 +340,56 @@ namespace tut ...@@ -337,5 +340,56 @@ namespace tut
static const char* matches[] = { "Binary" }; static const char* matches[] = { "Binary" };
test_matches("Binary", map, boost::begin(matches), boost::end(matches)); test_matches("Binary", map, boost::begin(matches), boost::end(matches));
} }
/*-------------------------- llsd_equals ---------------------------*/
// Cross-product of each LLSD type with every other
for (LLSD::map_const_iterator lmi(map.beginMap()), lmend(map.endMap());
lmi != lmend; ++lmi)
{
for (LLSD::map_const_iterator rmi(map.beginMap()), rmend(map.endMap());
rmi != rmend; ++rmi)
{
// Name this test based on the map keys naming the types of
// interest, e.g "String::Integer".
// We expect the values (xmi->second) to be equal if and only
// if the type names (xmi->first) are equal.
ensure(STRINGIZE(lmi->first << "::" << rmi->first),
bool(lmi->first == rmi->first) ==
bool(llsd_equals(lmi->second, rmi->second)));
}
}
// Array cases
LLSD rarray;
rarray.append(1.0);
rarray.append(2);
rarray.append("3");
LLSD larray(rarray);
ensure("llsd_equals(equal arrays)", llsd_equals(larray, rarray));
rarray[2] = "4";
ensure("llsd_equals(different [2])", ! llsd_equals(larray, rarray));
rarray = larray;
rarray.append(LLSD::Date());
ensure("llsd_equals(longer right array)", ! llsd_equals(larray, rarray));
rarray = larray;
rarray.erase(2);
ensure("llsd_equals(shorter right array)", ! llsd_equals(larray, rarray));
// Map cases
LLSD rmap;
rmap["San Francisco"] = 65;
rmap["Phoenix"] = 92;
rmap["Boston"] = 77;
LLSD lmap(rmap);
ensure("llsd_equals(equal maps)", llsd_equals(lmap, rmap));
rmap["Boston"] = 80;
ensure("llsd_equals(different [\"Boston\"])", ! llsd_equals(lmap, rmap));
rmap = lmap;
rmap["Atlanta"] = 95;
ensure("llsd_equals(superset right map)", ! llsd_equals(lmap, rmap));
rmap = lmap;
lmap["Seattle"] = 72;
ensure("llsd_equals(superset left map)", ! llsd_equals(lmap, rmap));
} }
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment