diff options
Diffstat (limited to 'indra')
| -rw-r--r-- | indra/llcommon/llsdutil.cpp | 92 | ||||
| -rw-r--r-- | indra/llcommon/llsdutil.h | 3 | ||||
| -rw-r--r-- | indra/test/llsdutil_tut.cpp | 58 | 
3 files changed, 151 insertions, 2 deletions
| diff --git a/indra/llcommon/llsdutil.cpp b/indra/llcommon/llsdutil.cpp index 643720cebe..c8d8030e87 100644 --- a/indra/llcommon/llsdutil.cpp +++ b/indra/llcommon/llsdutil.cpp @@ -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).      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 +    } +} diff --git a/indra/llcommon/llsdutil.h b/indra/llcommon/llsdutil.h index a4175be450..31d219da52 100644 --- a/indra/llcommon/llsdutil.h +++ b/indra/llcommon/llsdutil.h @@ -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=""); +/// Deep equality +bool llsd_equals(const LLSD& lhs, const LLSD& rhs); +  // Simple function to copy data out of input & output iterators if  // there is no need for casting.  template<typename Input> LLSD llsd_copy_array(Input iter, Input end) diff --git a/indra/test/llsdutil_tut.cpp b/indra/test/llsdutil_tut.cpp index 35ab80e791..d125bb0005 100644 --- a/indra/test/llsdutil_tut.cpp +++ b/indra/test/llsdutil_tut.cpp @@ -45,6 +45,7 @@  #include "llquaternion.h"  #include "llsdutil.h"  #include "llsdutil_math.h" +#include "stringize.h"  #include <set>  #include <boost/range.hpp> @@ -207,14 +208,16 @@ namespace tut          map.insert("URI",       LLSD::URI());          map.insert("Binary",    LLSD::Binary());          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;          array.append(LLSD());          map.insert("Array",     array);          // 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!" -        LLSD::map_const_iterator mi(map.beginMap()), mend(map.endMap()); +        LLSD::map_const_iterator mi, mend(map.endMap()); + +        /*-------------------------- llsd_matches --------------------------*/          // empty prototype matches anything          for (mi = map.beginMap(); mi != mend; ++mi) @@ -337,5 +340,56 @@ namespace tut              static const char* matches[] = { "Binary" };              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));      }  } | 
