summaryrefslogtreecommitdiff
path: root/indra/llcommon/llsdutil.h
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llcommon/llsdutil.h')
-rw-r--r--indra/llcommon/llsdutil.h1348
1 files changed, 674 insertions, 674 deletions
diff --git a/indra/llcommon/llsdutil.h b/indra/llcommon/llsdutil.h
index aa234e2f62..38bbe19ddd 100644
--- a/indra/llcommon/llsdutil.h
+++ b/indra/llcommon/llsdutil.h
@@ -1,674 +1,674 @@
-/**
- * @file llsdutil.h
- * @author Phoenix
- * @date 2006-05-24
- * @brief Utility classes, functions, etc, for using structured data.
- *
- * $LicenseInfo:firstyear=2006&license=viewerlgpl$
- * Second Life Viewer Source Code
- * Copyright (C) 2010, Linden Research, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation;
- * version 2.1 of the License only.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- *
- * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLSDUTIL_H
-#define LL_LLSDUTIL_H
-
-#include "apply.h" // LL::invoke()
-#include "function_types.h" // LL::function_arity
-#include "llsd.h"
-#include <boost/functional/hash.hpp>
-#include <cassert>
-#include <memory> // std::shared_ptr
-#include <type_traits>
-#include <vector>
-
-// U32
-LL_COMMON_API LLSD ll_sd_from_U32(const U32);
-LL_COMMON_API U32 ll_U32_from_sd(const LLSD& sd);
-
-// U64
-LL_COMMON_API LLSD ll_sd_from_U64(const U64);
-LL_COMMON_API U64 ll_U64_from_sd(const LLSD& sd);
-
-// IP Address
-LL_COMMON_API LLSD ll_sd_from_ipaddr(const U32);
-LL_COMMON_API U32 ll_ipaddr_from_sd(const LLSD& sd);
-
-// Binary to string
-LL_COMMON_API LLSD ll_string_from_binary(const LLSD& sd);
-
-//String to binary
-LL_COMMON_API LLSD ll_binary_from_string(const LLSD& sd);
-
-// Serializes sd to static buffer and returns pointer, useful for gdb debugging.
-LL_COMMON_API char* ll_print_sd(const LLSD& sd);
-
-// Serializes sd to static buffer and returns pointer, using "pretty printing" mode.
-LL_COMMON_API char* ll_pretty_print_sd_ptr(const LLSD* sd);
-LL_COMMON_API char* ll_pretty_print_sd(const LLSD& sd);
-
-LL_COMMON_API std::string ll_stream_notation_sd(const LLSD& sd);
-
-//compares the structure of an LLSD to a template LLSD and stores the
-//"valid" values in a 3rd LLSD. Default values
-//are pulled from the template. Extra keys/values in the test
-//are ignored in the resultant LLSD. Ordering of arrays matters
-//Returns false if the test is of same type but values differ in type
-//Otherwise, returns true
-
-LL_COMMON_API bool compare_llsd_with_template(
- const LLSD& llsd_to_test,
- const LLSD& template_llsd,
- LLSD& resultant_llsd);
-
-// filter_llsd_with_template() is a direct clone (copy-n-paste) of
-// compare_llsd_with_template with the following differences:
-// (1) bool vs BOOL return types
-// (2) A map with the key value "*" is a special value and maps any key in the
-// test llsd that doesn't have an explicitly matching key in the template.
-// (3) The element of an array with exactly one element is taken as a template
-// for *all* the elements of the test array. If the template array is of
-// different size, compare_llsd_with_template() semantics apply.
-bool filter_llsd_with_template(
- const LLSD & llsd_to_test,
- const LLSD & template_llsd,
- LLSD & resultant_llsd);
-
-/**
- * Recursively determine whether a given LLSD data block "matches" another
- * LLSD prototype. The returned string is empty() on success, non-empty() on
- * mismatch.
- *
- * This function tests structure (types) rather than data values. It is
- * intended for when a consumer expects an LLSD block with a particular
- * structure, and must succinctly detect whether the arriving block is
- * well-formed. For instance, a test of the form:
- * @code
- * if (! (data.has("request") && data.has("target") && data.has("modifier") ...))
- * @endcode
- * could instead be expressed by initializing a prototype LLSD map with the
- * required keys and writing:
- * @code
- * if (! llsd_matches(prototype, data).empty())
- * @endcode
- *
- * A non-empty return value is an error-message fragment intended to indicate
- * to (English-speaking) developers where in the prototype structure the
- * mismatch occurred.
- *
- * * If a slot in the prototype isUndefined(), then anything is valid at that
- * place in the real object. (Passing prototype == LLSD() matches anything
- * at all.)
- * * An array in the prototype must match a data array at least that large.
- * (Additional entries in the data array are ignored.) Every isDefined()
- * entry in the prototype array must match the corresponding entry in the
- * data array.
- * * A map in the prototype must match a map in the data. Every key in the
- * prototype map must match a corresponding key in the data map. (Additional
- * keys in the data map are ignored.) Every isDefined() value in the
- * prototype map must match the corresponding key's value in the data map.
- * * Scalar values in the prototype are tested for @em type rather than value.
- * For instance, a String in the prototype matches any String at all. In
- * effect, storing an Integer at a particular place in the prototype asserts
- * that the caller intends to apply asInteger() to the corresponding slot in
- * the data.
- * * A String in the prototype matches String, Boolean, Integer, Real, UUID,
- * Date and URI, because asString() applied to any of these produces a
- * meaningful result.
- * * Similarly, a Boolean, Integer or Real in the prototype can match any of
- * Boolean, Integer or Real in the data -- or even String.
- * * UUID matches UUID or String.
- * * Date matches Date or String.
- * * URI matches URI or String.
- * * Binary in the prototype matches only Binary in the data.
- *
- * @TODO: when a Boolean, Integer or Real in the prototype matches a String in
- * the data, we should examine the String @em value to ensure it can be
- * meaningfully converted to the requested type. The same goes for UUID, Date
- * and URI.
- */
-LL_COMMON_API std::string llsd_matches(const LLSD& prototype, const LLSD& data, const std::string& pfx="");
-
-/// Deep equality. If you want to compare LLSD::Real values for approximate
-/// equality rather than bitwise equality, pass @a bits as for
-/// is_approx_equal_fraction().
-LL_COMMON_API bool llsd_equals(const LLSD& lhs, const LLSD& rhs, int bits=-1);
-/// If you don't care about LLSD::Real equality
-inline bool operator==(const LLSD& lhs, const LLSD& rhs)
-{
- return llsd_equals(lhs, rhs);
-}
-inline bool operator!=(const LLSD& lhs, const LLSD& rhs)
-{
- // operator!=() should always be the negation of operator==()
- return ! (lhs == 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)
-{
- LLSD dest;
- for (; iter != end; ++iter)
- {
- dest.append(*iter);
- }
- return dest;
-}
-
-namespace llsd
-{
-
-/**
- * Drill down to locate an element in 'blob' according to 'path', where 'path'
- * is one of the following:
- *
- * - LLSD::String: 'blob' is an LLSD::Map. Find the entry with key 'path'.
- * - LLSD::Integer: 'blob' is an LLSD::Array. Find the entry with index 'path'.
- * - Any other 'path' type will be interpreted as LLSD::Array, and 'blob' is a
- * nested structure. For each element of 'path':
- * - If it's an LLSD::Integer, select the entry with that index from an
- * LLSD::Array at that level.
- * - If it's an LLSD::String, select the entry with that key from an
- * LLSD::Map at that level.
- * - Anything else is an error.
- *
- * By implication, if path.isUndefined() or otherwise equivalent to an empty
- * LLSD::Array, drill[_ref]() returns 'blob' as is.
- */
-LLSD drill(const LLSD& blob, const LLSD& path);
-LLSD& drill_ref( LLSD& blob, const LLSD& path);
-
-}
-
-namespace llsd
-{
-
-/**
- * Construct an LLSD::Array inline, using modern C++ variadic arguments.
- */
-
-// recursion tail
-inline
-void array_(LLSD&) {}
-
-// recursive call
-template <typename T0, typename... Ts>
-void array_(LLSD& data, T0&& v0, Ts&&... vs)
-{
- data.append(std::forward<T0>(v0));
- array_(data, std::forward<Ts>(vs)...);
-}
-
-// public interface
-template <typename... Ts>
-LLSD array(Ts&&... vs)
-{
- LLSD data;
- array_(data, std::forward<Ts>(vs)...);
- return data;
-}
-
-} // namespace llsd
-
-/*****************************************************************************
-* LLSDMap
-*****************************************************************************/
-/**
- * Construct an LLSD::Map inline, with implicit conversion to LLSD. Usage:
- *
- * @code
- * void somefunc(const LLSD&);
- * ...
- * somefunc(LLSDMap("alpha", "abc")("number", 17)("pi", 3.14));
- * @endcode
- *
- * For completeness, LLSDMap() with no args constructs an empty map, so
- * <tt>LLSDMap()("alpha", "abc")("number", 17)("pi", 3.14)</tt> produces a map
- * equivalent to the above. But for most purposes, LLSD() is already
- * equivalent to an empty map, and if you explicitly want an empty isMap(),
- * there's LLSD::emptyMap(). However, supporting a no-args LLSDMap()
- * constructor follows the principle of least astonishment.
- */
-class LLSDMap
-{
-public:
- LLSDMap():
- _data(LLSD::emptyMap())
- {}
- LLSDMap(const LLSD::String& key, const LLSD& value):
- _data(LLSD::emptyMap())
- {
- _data[key] = value;
- }
-
- LLSDMap& operator()(const LLSD::String& key, const LLSD& value)
- {
- _data[key] = value;
- return *this;
- }
-
- operator LLSD() const { return _data; }
- LLSD get() const { return _data; }
-
-private:
- LLSD _data;
-};
-
-namespace llsd
-{
-
-/**
- * Construct an LLSD::Map inline, using modern C++ variadic arguments.
- */
-
-// recursion tail
-inline
-void map_(LLSD&) {}
-
-// recursive call
-template <typename T0, typename... Ts>
-void map_(LLSD& data, const LLSD::String& k0, T0&& v0, Ts&&... vs)
-{
- data[k0] = v0;
- map_(data, std::forward<Ts>(vs)...);
-}
-
-// public interface
-template <typename... Ts>
-LLSD map(Ts&&... vs)
-{
- LLSD data;
- map_(data, std::forward<Ts>(vs)...);
- return data;
-}
-
-} // namespace llsd
-
-/*****************************************************************************
-* LLSDParam
-*****************************************************************************/
-struct LLSDParamBase
-{
- virtual ~LLSDParamBase() {}
-};
-
-/**
- * LLSDParam is a customization point for passing LLSD values to function
- * parameters of more or less arbitrary type. LLSD provides a small set of
- * native conversions; but if a generic algorithm explicitly constructs an
- * LLSDParam object in the function's argument list, a consumer can provide
- * LLSDParam specializations to support more different parameter types than
- * LLSD's native conversions.
- *
- * Usage:
- *
- * @code
- * void somefunc(const paramtype&);
- * ...
- * somefunc(..., LLSDParam<paramtype>(someLLSD), ...);
- * @endcode
- */
-template <typename T>
-class LLSDParam: public LLSDParamBase
-{
-public:
- /**
- * Default implementation converts to T on construction, saves converted
- * value for later retrieval
- */
- LLSDParam(const LLSD& value):
- value_(value)
- {}
-
- operator T() const { return value_; }
-
-private:
- T value_;
-};
-
-/**
- * LLSDParam<LLSD> is for when you don't already have the target parameter
- * type in hand. Instantiate LLSDParam<LLSD>(your LLSD object), and the
- * templated conversion operator will try to select a more specific LLSDParam
- * specialization.
- */
-template <>
-class LLSDParam<LLSD>: public LLSDParamBase
-{
-private:
- LLSD value_;
- // LLSDParam<LLSD>::operator T() works by instantiating an LLSDParam<T> on
- // demand. Returning that engages LLSDParam<T>::operator T(), producing
- // the desired result. But LLSDParam<const char*> owns a std::string whose
- // c_str() is returned by its operator const char*(). If we return a temp
- // LLSDParam<const char*>, the compiler can destroy it right away, as soon
- // as we've called operator const char*(). That's a problem! That
- // invalidates the const char* we've just passed to the subject function.
- // This LLSDParam<LLSD> is presumably guaranteed to survive until the
- // subject function has returned, so we must ensure that any constructed
- // LLSDParam<T> lives just as long as this LLSDParam<LLSD> does. Putting
- // each LLSDParam<T> on the heap and capturing a smart pointer in a vector
- // works. We would have liked to use std::unique_ptr, but vector entries
- // must be copyable.
- // (Alternatively we could assume that every instance of LLSDParam<LLSD>
- // will be asked for at most ONE conversion. We could store a scalar
- // std::unique_ptr and, when constructing an new LLSDParam<T>, assert that
- // the unique_ptr is empty. But some future change in usage patterns, and
- // consequent failure of that assertion, would be very mysterious. Instead
- // of explaining how to fix it, just fix it now.)
- mutable std::vector<std::shared_ptr<LLSDParamBase>> converters_;
-
-public:
- LLSDParam(const LLSD& value): value_(value) {}
-
- /// if we're literally being asked for an LLSD parameter, avoid infinite
- /// recursion
- operator LLSD() const { return value_; }
-
- /// otherwise, instantiate a more specific LLSDParam<T> to convert; that
- /// preserves the existing customization mechanism
- template <typename T>
- operator T() const
- {
- // capture 'ptr' with the specific subclass type because converters_
- // only stores LLSDParamBase pointers
- auto ptr{ std::make_shared<LLSDParam<std::decay_t<T>>>(value_) };
- // keep the new converter alive until we ourselves are destroyed
- converters_.push_back(ptr);
- return *ptr;
- }
-};
-
-/**
- * Turns out that several target types could accept an LLSD param using any of
- * a few different conversions, e.g. LLUUID's constructor can accept LLUUID or
- * std::string. Therefore, the compiler can't decide which LLSD conversion
- * operator to choose, even though to us it seems obvious. But that's okay, we
- * can specialize LLSDParam for such target types, explicitly specifying the
- * desired conversion -- that's part of what LLSDParam is all about. Turns out
- * we have to do that enough to make it worthwhile generalizing. Use a macro
- * because I need to specify one of the asReal, etc., explicit conversion
- * methods as well as a type. If I'm overlooking a clever way to implement
- * that using a template instead, feel free to reimplement.
- */
-#define LLSDParam_for(T, AS) \
-template <> \
-class LLSDParam<T>: public LLSDParamBase \
-{ \
-public: \
- LLSDParam(const LLSD& value): \
- value_((T)value.AS()) \
- {} \
- \
- operator T() const { return value_; } \
- \
-private: \
- T value_; \
-}
-
-LLSDParam_for(float, asReal);
-LLSDParam_for(LLUUID, asUUID);
-LLSDParam_for(LLDate, asDate);
-LLSDParam_for(LLURI, asURI);
-LLSDParam_for(LLSD::Binary, asBinary);
-
-/**
- * LLSDParam<const char*> is an example of the kind of conversion you can
- * support with LLSDParam beyond native LLSD conversions. Normally you can't
- * pass an LLSD object to a function accepting const char* -- but you can
- * safely pass an LLSDParam<const char*>(yourLLSD).
- */
-template <>
-class LLSDParam<const char*>: public LLSDParamBase
-{
-private:
- // The difference here is that we store a std::string rather than a const
- // char*. It's important that the LLSDParam object own the std::string.
- std::string value_;
- // We don't bother storing the incoming LLSD object, but we do have to
- // distinguish whether value_ is an empty string because the LLSD object
- // contains an empty string or because it's isUndefined().
- bool undefined_;
-
-public:
- LLSDParam(const LLSD& value):
- value_(value),
- undefined_(value.isUndefined())
- {}
-
- // The const char* we retrieve is for storage owned by our value_ member.
- // That's how we guarantee that the const char* is valid for the lifetime
- // of this LLSDParam object. Constructing your LLSDParam in the argument
- // list should ensure that the LLSDParam object will persist for the
- // duration of the function call.
- operator const char*() const
- {
- if (undefined_)
- {
- // By default, an isUndefined() LLSD object's asString() method
- // will produce an empty string. But for a function accepting
- // const char*, it's often important to be able to pass NULL, and
- // isUndefined() seems like the best way. If you want to pass an
- // empty string, you can still pass LLSD(""). Without this special
- // case, though, no LLSD value could pass NULL.
- return NULL;
- }
- return value_.c_str();
- }
-};
-
-namespace llsd
-{
-
-/*****************************************************************************
-* range-based for-loop helpers for LLSD
-*****************************************************************************/
-/// Usage: for (LLSD item : inArray(someLLSDarray)) { ... }
-class inArray
-{
-public:
- inArray(const LLSD& array):
- _array(array)
- {}
-
- typedef LLSD::array_const_iterator const_iterator;
- typedef LLSD::array_iterator iterator;
-
- iterator begin() { return _array.beginArray(); }
- iterator end() { return _array.endArray(); }
- const_iterator begin() const { return _array.beginArray(); }
- const_iterator end() const { return _array.endArray(); }
-
-private:
- LLSD _array;
-};
-
-/// MapEntry is what you get from dereferencing an LLSD::map_[const_]iterator.
-typedef std::map<LLSD::String, LLSD>::value_type MapEntry;
-
-/// Usage: for([const] MapEntry& e : inMap(someLLSDmap)) { ... }
-class inMap
-{
-public:
- inMap(const LLSD& map):
- _map(map)
- {}
-
- typedef LLSD::map_const_iterator const_iterator;
- typedef LLSD::map_iterator iterator;
-
- iterator begin() { return _map.beginMap(); }
- iterator end() { return _map.endMap(); }
- const_iterator begin() const { return _map.beginMap(); }
- const_iterator end() const { return _map.endMap(); }
-
-private:
- LLSD _map;
-};
-
-} // namespace llsd
-
-
-// Creates a deep clone of an LLSD object. Maps, Arrays and binary objects
-// are duplicated, atomic primitives (Boolean, Integer, Real, etc) simply
-// use a shared reference.
-// Optionally a filter may be specified to control what is duplicated. The
-// map takes the form "keyname/boolean".
-// If the value is true the value will be duplicated otherwise it will be skipped
-// when encountered in a map. A key name of "*" can be specified as a wild card
-// and will specify the default behavior. If no wild card is given and the clone
-// encounters a name not in the filter, that value will be skipped.
-LLSD llsd_clone(LLSD value, LLSD filter = LLSD());
-
-// Creates a shallow copy of a map or array. If passed any other type of LLSD
-// object it simply returns that value. See llsd_clone for a description of
-// the filter parameter.
-LLSD llsd_shallow(LLSD value, LLSD filter = LLSD());
-
-namespace llsd
-{
-
-// llsd namespace aliases
-inline
-LLSD clone (LLSD value, LLSD filter=LLSD()) { return llsd_clone (value, filter); }
-inline
-LLSD shallow(LLSD value, LLSD filter=LLSD()) { return llsd_shallow(value, filter); }
-
-} // namespace llsd
-
-// Specialization for generating a hash value from an LLSD block.
-namespace boost
-{
-template <>
-struct hash<LLSD>
-{
- typedef LLSD argument_type;
- typedef std::size_t result_type;
- result_type operator()(argument_type const& s) const
- {
- result_type seed(0);
-
- LLSD::Type stype = s.type();
- boost::hash_combine(seed, (S32)stype);
-
- switch (stype)
- {
- case LLSD::TypeBoolean:
- boost::hash_combine(seed, s.asBoolean());
- break;
- case LLSD::TypeInteger:
- boost::hash_combine(seed, s.asInteger());
- break;
- case LLSD::TypeReal:
- boost::hash_combine(seed, s.asReal());
- break;
- case LLSD::TypeURI:
- case LLSD::TypeString:
- boost::hash_combine(seed, s.asString());
- break;
- case LLSD::TypeUUID:
- boost::hash_combine(seed, s.asUUID());
- break;
- case LLSD::TypeDate:
- boost::hash_combine(seed, s.asDate().secondsSinceEpoch());
- break;
- case LLSD::TypeBinary:
- {
- const LLSD::Binary &b(s.asBinary());
- boost::hash_range(seed, b.begin(), b.end());
- break;
- }
- case LLSD::TypeMap:
- {
- for (LLSD::map_const_iterator itm = s.beginMap(); itm != s.endMap(); ++itm)
- {
- boost::hash_combine(seed, (*itm).first);
- boost::hash_combine(seed, (*itm).second);
- }
- break;
- }
- case LLSD::TypeArray:
- for (LLSD::array_const_iterator ita = s.beginArray(); ita != s.endArray(); ++ita)
- {
- boost::hash_combine(seed, (*ita));
- }
- break;
- case LLSD::TypeUndefined:
- default:
- break;
- }
-
- return seed;
- }
-};
-}
-
-namespace LL
-{
-
-/*****************************************************************************
-* apply(function, LLSD array)
-*****************************************************************************/
-// validate incoming LLSD blob, and return an LLSD array suitable to pass to
-// the function of interest
-LLSD apply_llsd_fix(size_t arity, const LLSD& args);
-
-// Derived from https://stackoverflow.com/a/20441189
-// and https://en.cppreference.com/w/cpp/utility/apply .
-// We can't simply make a tuple from the LLSD array and then apply() that
-// tuple to the function -- how would make_tuple() deduce the correct
-// parameter type for each entry? We must go directly to the target function.
-template <typename CALLABLE, std::size_t... I>
-auto apply_impl(CALLABLE&& func, const LLSD& array, std::index_sequence<I...>)
-{
- // call func(unpacked args), using generic LLSDParam<LLSD> to convert each
- // entry in 'array' to the target parameter type
- return std::forward<CALLABLE>(func)(LLSDParam<LLSD>(array[I])...);
-}
-
-// use apply_n<ARITY>(function, LLSD) to call a specific arity of a variadic
-// function with (that many) items from the passed LLSD array
-template <size_t ARITY, typename CALLABLE>
-auto apply_n(CALLABLE&& func, const LLSD& args)
-{
- return apply_impl(std::forward<CALLABLE>(func),
- apply_llsd_fix(ARITY, args),
- std::make_index_sequence<ARITY>());
-}
-
-/**
- * apply(function, LLSD) goes beyond C++17 std::apply(). For this case
- * @a function @emph cannot be variadic: the compiler must know at compile
- * time how many arguments to pass. This isn't Python. (But see apply_n() to
- * pass a specific number of args to a variadic function.)
- */
-template <typename CALLABLE>
-auto apply(CALLABLE&& func, const LLSD& args)
-{
- // infer arity from the definition of func
- constexpr auto arity = function_arity<
- typename std::remove_reference<CALLABLE>::type>::value;
- // now that we have a compile-time arity, apply_n() works
- return apply_n<arity>(std::forward<CALLABLE>(func), args);
-}
-
-} // namespace LL
-
-#endif // LL_LLSDUTIL_H
+/**
+ * @file llsdutil.h
+ * @author Phoenix
+ * @date 2006-05-24
+ * @brief Utility classes, functions, etc, for using structured data.
+ *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLSDUTIL_H
+#define LL_LLSDUTIL_H
+
+#include "apply.h" // LL::invoke()
+#include "function_types.h" // LL::function_arity
+#include "llsd.h"
+#include <boost/functional/hash.hpp>
+#include <cassert>
+#include <memory> // std::shared_ptr
+#include <type_traits>
+#include <vector>
+
+// U32
+LL_COMMON_API LLSD ll_sd_from_U32(const U32);
+LL_COMMON_API U32 ll_U32_from_sd(const LLSD& sd);
+
+// U64
+LL_COMMON_API LLSD ll_sd_from_U64(const U64);
+LL_COMMON_API U64 ll_U64_from_sd(const LLSD& sd);
+
+// IP Address
+LL_COMMON_API LLSD ll_sd_from_ipaddr(const U32);
+LL_COMMON_API U32 ll_ipaddr_from_sd(const LLSD& sd);
+
+// Binary to string
+LL_COMMON_API LLSD ll_string_from_binary(const LLSD& sd);
+
+//String to binary
+LL_COMMON_API LLSD ll_binary_from_string(const LLSD& sd);
+
+// Serializes sd to static buffer and returns pointer, useful for gdb debugging.
+LL_COMMON_API char* ll_print_sd(const LLSD& sd);
+
+// Serializes sd to static buffer and returns pointer, using "pretty printing" mode.
+LL_COMMON_API char* ll_pretty_print_sd_ptr(const LLSD* sd);
+LL_COMMON_API char* ll_pretty_print_sd(const LLSD& sd);
+
+LL_COMMON_API std::string ll_stream_notation_sd(const LLSD& sd);
+
+//compares the structure of an LLSD to a template LLSD and stores the
+//"valid" values in a 3rd LLSD. Default values
+//are pulled from the template. Extra keys/values in the test
+//are ignored in the resultant LLSD. Ordering of arrays matters
+//Returns false if the test is of same type but values differ in type
+//Otherwise, returns true
+
+LL_COMMON_API bool compare_llsd_with_template(
+ const LLSD& llsd_to_test,
+ const LLSD& template_llsd,
+ LLSD& resultant_llsd);
+
+// filter_llsd_with_template() is a direct clone (copy-n-paste) of
+// compare_llsd_with_template with the following differences:
+// (1) bool vs BOOL return types
+// (2) A map with the key value "*" is a special value and maps any key in the
+// test llsd that doesn't have an explicitly matching key in the template.
+// (3) The element of an array with exactly one element is taken as a template
+// for *all* the elements of the test array. If the template array is of
+// different size, compare_llsd_with_template() semantics apply.
+bool filter_llsd_with_template(
+ const LLSD & llsd_to_test,
+ const LLSD & template_llsd,
+ LLSD & resultant_llsd);
+
+/**
+ * Recursively determine whether a given LLSD data block "matches" another
+ * LLSD prototype. The returned string is empty() on success, non-empty() on
+ * mismatch.
+ *
+ * This function tests structure (types) rather than data values. It is
+ * intended for when a consumer expects an LLSD block with a particular
+ * structure, and must succinctly detect whether the arriving block is
+ * well-formed. For instance, a test of the form:
+ * @code
+ * if (! (data.has("request") && data.has("target") && data.has("modifier") ...))
+ * @endcode
+ * could instead be expressed by initializing a prototype LLSD map with the
+ * required keys and writing:
+ * @code
+ * if (! llsd_matches(prototype, data).empty())
+ * @endcode
+ *
+ * A non-empty return value is an error-message fragment intended to indicate
+ * to (English-speaking) developers where in the prototype structure the
+ * mismatch occurred.
+ *
+ * * If a slot in the prototype isUndefined(), then anything is valid at that
+ * place in the real object. (Passing prototype == LLSD() matches anything
+ * at all.)
+ * * An array in the prototype must match a data array at least that large.
+ * (Additional entries in the data array are ignored.) Every isDefined()
+ * entry in the prototype array must match the corresponding entry in the
+ * data array.
+ * * A map in the prototype must match a map in the data. Every key in the
+ * prototype map must match a corresponding key in the data map. (Additional
+ * keys in the data map are ignored.) Every isDefined() value in the
+ * prototype map must match the corresponding key's value in the data map.
+ * * Scalar values in the prototype are tested for @em type rather than value.
+ * For instance, a String in the prototype matches any String at all. In
+ * effect, storing an Integer at a particular place in the prototype asserts
+ * that the caller intends to apply asInteger() to the corresponding slot in
+ * the data.
+ * * A String in the prototype matches String, Boolean, Integer, Real, UUID,
+ * Date and URI, because asString() applied to any of these produces a
+ * meaningful result.
+ * * Similarly, a Boolean, Integer or Real in the prototype can match any of
+ * Boolean, Integer or Real in the data -- or even String.
+ * * UUID matches UUID or String.
+ * * Date matches Date or String.
+ * * URI matches URI or String.
+ * * Binary in the prototype matches only Binary in the data.
+ *
+ * @TODO: when a Boolean, Integer or Real in the prototype matches a String in
+ * the data, we should examine the String @em value to ensure it can be
+ * meaningfully converted to the requested type. The same goes for UUID, Date
+ * and URI.
+ */
+LL_COMMON_API std::string llsd_matches(const LLSD& prototype, const LLSD& data, const std::string& pfx="");
+
+/// Deep equality. If you want to compare LLSD::Real values for approximate
+/// equality rather than bitwise equality, pass @a bits as for
+/// is_approx_equal_fraction().
+LL_COMMON_API bool llsd_equals(const LLSD& lhs, const LLSD& rhs, int bits=-1);
+/// If you don't care about LLSD::Real equality
+inline bool operator==(const LLSD& lhs, const LLSD& rhs)
+{
+ return llsd_equals(lhs, rhs);
+}
+inline bool operator!=(const LLSD& lhs, const LLSD& rhs)
+{
+ // operator!=() should always be the negation of operator==()
+ return ! (lhs == 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)
+{
+ LLSD dest;
+ for (; iter != end; ++iter)
+ {
+ dest.append(*iter);
+ }
+ return dest;
+}
+
+namespace llsd
+{
+
+/**
+ * Drill down to locate an element in 'blob' according to 'path', where 'path'
+ * is one of the following:
+ *
+ * - LLSD::String: 'blob' is an LLSD::Map. Find the entry with key 'path'.
+ * - LLSD::Integer: 'blob' is an LLSD::Array. Find the entry with index 'path'.
+ * - Any other 'path' type will be interpreted as LLSD::Array, and 'blob' is a
+ * nested structure. For each element of 'path':
+ * - If it's an LLSD::Integer, select the entry with that index from an
+ * LLSD::Array at that level.
+ * - If it's an LLSD::String, select the entry with that key from an
+ * LLSD::Map at that level.
+ * - Anything else is an error.
+ *
+ * By implication, if path.isUndefined() or otherwise equivalent to an empty
+ * LLSD::Array, drill[_ref]() returns 'blob' as is.
+ */
+LLSD drill(const LLSD& blob, const LLSD& path);
+LLSD& drill_ref( LLSD& blob, const LLSD& path);
+
+}
+
+namespace llsd
+{
+
+/**
+ * Construct an LLSD::Array inline, using modern C++ variadic arguments.
+ */
+
+// recursion tail
+inline
+void array_(LLSD&) {}
+
+// recursive call
+template <typename T0, typename... Ts>
+void array_(LLSD& data, T0&& v0, Ts&&... vs)
+{
+ data.append(std::forward<T0>(v0));
+ array_(data, std::forward<Ts>(vs)...);
+}
+
+// public interface
+template <typename... Ts>
+LLSD array(Ts&&... vs)
+{
+ LLSD data;
+ array_(data, std::forward<Ts>(vs)...);
+ return data;
+}
+
+} // namespace llsd
+
+/*****************************************************************************
+* LLSDMap
+*****************************************************************************/
+/**
+ * Construct an LLSD::Map inline, with implicit conversion to LLSD. Usage:
+ *
+ * @code
+ * void somefunc(const LLSD&);
+ * ...
+ * somefunc(LLSDMap("alpha", "abc")("number", 17)("pi", 3.14));
+ * @endcode
+ *
+ * For completeness, LLSDMap() with no args constructs an empty map, so
+ * <tt>LLSDMap()("alpha", "abc")("number", 17)("pi", 3.14)</tt> produces a map
+ * equivalent to the above. But for most purposes, LLSD() is already
+ * equivalent to an empty map, and if you explicitly want an empty isMap(),
+ * there's LLSD::emptyMap(). However, supporting a no-args LLSDMap()
+ * constructor follows the principle of least astonishment.
+ */
+class LLSDMap
+{
+public:
+ LLSDMap():
+ _data(LLSD::emptyMap())
+ {}
+ LLSDMap(const LLSD::String& key, const LLSD& value):
+ _data(LLSD::emptyMap())
+ {
+ _data[key] = value;
+ }
+
+ LLSDMap& operator()(const LLSD::String& key, const LLSD& value)
+ {
+ _data[key] = value;
+ return *this;
+ }
+
+ operator LLSD() const { return _data; }
+ LLSD get() const { return _data; }
+
+private:
+ LLSD _data;
+};
+
+namespace llsd
+{
+
+/**
+ * Construct an LLSD::Map inline, using modern C++ variadic arguments.
+ */
+
+// recursion tail
+inline
+void map_(LLSD&) {}
+
+// recursive call
+template <typename T0, typename... Ts>
+void map_(LLSD& data, const LLSD::String& k0, T0&& v0, Ts&&... vs)
+{
+ data[k0] = v0;
+ map_(data, std::forward<Ts>(vs)...);
+}
+
+// public interface
+template <typename... Ts>
+LLSD map(Ts&&... vs)
+{
+ LLSD data;
+ map_(data, std::forward<Ts>(vs)...);
+ return data;
+}
+
+} // namespace llsd
+
+/*****************************************************************************
+* LLSDParam
+*****************************************************************************/
+struct LLSDParamBase
+{
+ virtual ~LLSDParamBase() {}
+};
+
+/**
+ * LLSDParam is a customization point for passing LLSD values to function
+ * parameters of more or less arbitrary type. LLSD provides a small set of
+ * native conversions; but if a generic algorithm explicitly constructs an
+ * LLSDParam object in the function's argument list, a consumer can provide
+ * LLSDParam specializations to support more different parameter types than
+ * LLSD's native conversions.
+ *
+ * Usage:
+ *
+ * @code
+ * void somefunc(const paramtype&);
+ * ...
+ * somefunc(..., LLSDParam<paramtype>(someLLSD), ...);
+ * @endcode
+ */
+template <typename T>
+class LLSDParam: public LLSDParamBase
+{
+public:
+ /**
+ * Default implementation converts to T on construction, saves converted
+ * value for later retrieval
+ */
+ LLSDParam(const LLSD& value):
+ value_(value)
+ {}
+
+ operator T() const { return value_; }
+
+private:
+ T value_;
+};
+
+/**
+ * LLSDParam<LLSD> is for when you don't already have the target parameter
+ * type in hand. Instantiate LLSDParam<LLSD>(your LLSD object), and the
+ * templated conversion operator will try to select a more specific LLSDParam
+ * specialization.
+ */
+template <>
+class LLSDParam<LLSD>: public LLSDParamBase
+{
+private:
+ LLSD value_;
+ // LLSDParam<LLSD>::operator T() works by instantiating an LLSDParam<T> on
+ // demand. Returning that engages LLSDParam<T>::operator T(), producing
+ // the desired result. But LLSDParam<const char*> owns a std::string whose
+ // c_str() is returned by its operator const char*(). If we return a temp
+ // LLSDParam<const char*>, the compiler can destroy it right away, as soon
+ // as we've called operator const char*(). That's a problem! That
+ // invalidates the const char* we've just passed to the subject function.
+ // This LLSDParam<LLSD> is presumably guaranteed to survive until the
+ // subject function has returned, so we must ensure that any constructed
+ // LLSDParam<T> lives just as long as this LLSDParam<LLSD> does. Putting
+ // each LLSDParam<T> on the heap and capturing a smart pointer in a vector
+ // works. We would have liked to use std::unique_ptr, but vector entries
+ // must be copyable.
+ // (Alternatively we could assume that every instance of LLSDParam<LLSD>
+ // will be asked for at most ONE conversion. We could store a scalar
+ // std::unique_ptr and, when constructing an new LLSDParam<T>, assert that
+ // the unique_ptr is empty. But some future change in usage patterns, and
+ // consequent failure of that assertion, would be very mysterious. Instead
+ // of explaining how to fix it, just fix it now.)
+ mutable std::vector<std::shared_ptr<LLSDParamBase>> converters_;
+
+public:
+ LLSDParam(const LLSD& value): value_(value) {}
+
+ /// if we're literally being asked for an LLSD parameter, avoid infinite
+ /// recursion
+ operator LLSD() const { return value_; }
+
+ /// otherwise, instantiate a more specific LLSDParam<T> to convert; that
+ /// preserves the existing customization mechanism
+ template <typename T>
+ operator T() const
+ {
+ // capture 'ptr' with the specific subclass type because converters_
+ // only stores LLSDParamBase pointers
+ auto ptr{ std::make_shared<LLSDParam<std::decay_t<T>>>(value_) };
+ // keep the new converter alive until we ourselves are destroyed
+ converters_.push_back(ptr);
+ return *ptr;
+ }
+};
+
+/**
+ * Turns out that several target types could accept an LLSD param using any of
+ * a few different conversions, e.g. LLUUID's constructor can accept LLUUID or
+ * std::string. Therefore, the compiler can't decide which LLSD conversion
+ * operator to choose, even though to us it seems obvious. But that's okay, we
+ * can specialize LLSDParam for such target types, explicitly specifying the
+ * desired conversion -- that's part of what LLSDParam is all about. Turns out
+ * we have to do that enough to make it worthwhile generalizing. Use a macro
+ * because I need to specify one of the asReal, etc., explicit conversion
+ * methods as well as a type. If I'm overlooking a clever way to implement
+ * that using a template instead, feel free to reimplement.
+ */
+#define LLSDParam_for(T, AS) \
+template <> \
+class LLSDParam<T>: public LLSDParamBase \
+{ \
+public: \
+ LLSDParam(const LLSD& value): \
+ value_((T)value.AS()) \
+ {} \
+ \
+ operator T() const { return value_; } \
+ \
+private: \
+ T value_; \
+}
+
+LLSDParam_for(float, asReal);
+LLSDParam_for(LLUUID, asUUID);
+LLSDParam_for(LLDate, asDate);
+LLSDParam_for(LLURI, asURI);
+LLSDParam_for(LLSD::Binary, asBinary);
+
+/**
+ * LLSDParam<const char*> is an example of the kind of conversion you can
+ * support with LLSDParam beyond native LLSD conversions. Normally you can't
+ * pass an LLSD object to a function accepting const char* -- but you can
+ * safely pass an LLSDParam<const char*>(yourLLSD).
+ */
+template <>
+class LLSDParam<const char*>: public LLSDParamBase
+{
+private:
+ // The difference here is that we store a std::string rather than a const
+ // char*. It's important that the LLSDParam object own the std::string.
+ std::string value_;
+ // We don't bother storing the incoming LLSD object, but we do have to
+ // distinguish whether value_ is an empty string because the LLSD object
+ // contains an empty string or because it's isUndefined().
+ bool undefined_;
+
+public:
+ LLSDParam(const LLSD& value):
+ value_(value),
+ undefined_(value.isUndefined())
+ {}
+
+ // The const char* we retrieve is for storage owned by our value_ member.
+ // That's how we guarantee that the const char* is valid for the lifetime
+ // of this LLSDParam object. Constructing your LLSDParam in the argument
+ // list should ensure that the LLSDParam object will persist for the
+ // duration of the function call.
+ operator const char*() const
+ {
+ if (undefined_)
+ {
+ // By default, an isUndefined() LLSD object's asString() method
+ // will produce an empty string. But for a function accepting
+ // const char*, it's often important to be able to pass NULL, and
+ // isUndefined() seems like the best way. If you want to pass an
+ // empty string, you can still pass LLSD(""). Without this special
+ // case, though, no LLSD value could pass NULL.
+ return NULL;
+ }
+ return value_.c_str();
+ }
+};
+
+namespace llsd
+{
+
+/*****************************************************************************
+* range-based for-loop helpers for LLSD
+*****************************************************************************/
+/// Usage: for (LLSD item : inArray(someLLSDarray)) { ... }
+class inArray
+{
+public:
+ inArray(const LLSD& array):
+ _array(array)
+ {}
+
+ typedef LLSD::array_const_iterator const_iterator;
+ typedef LLSD::array_iterator iterator;
+
+ iterator begin() { return _array.beginArray(); }
+ iterator end() { return _array.endArray(); }
+ const_iterator begin() const { return _array.beginArray(); }
+ const_iterator end() const { return _array.endArray(); }
+
+private:
+ LLSD _array;
+};
+
+/// MapEntry is what you get from dereferencing an LLSD::map_[const_]iterator.
+typedef std::map<LLSD::String, LLSD>::value_type MapEntry;
+
+/// Usage: for([const] MapEntry& e : inMap(someLLSDmap)) { ... }
+class inMap
+{
+public:
+ inMap(const LLSD& map):
+ _map(map)
+ {}
+
+ typedef LLSD::map_const_iterator const_iterator;
+ typedef LLSD::map_iterator iterator;
+
+ iterator begin() { return _map.beginMap(); }
+ iterator end() { return _map.endMap(); }
+ const_iterator begin() const { return _map.beginMap(); }
+ const_iterator end() const { return _map.endMap(); }
+
+private:
+ LLSD _map;
+};
+
+} // namespace llsd
+
+
+// Creates a deep clone of an LLSD object. Maps, Arrays and binary objects
+// are duplicated, atomic primitives (Boolean, Integer, Real, etc) simply
+// use a shared reference.
+// Optionally a filter may be specified to control what is duplicated. The
+// map takes the form "keyname/boolean".
+// If the value is true the value will be duplicated otherwise it will be skipped
+// when encountered in a map. A key name of "*" can be specified as a wild card
+// and will specify the default behavior. If no wild card is given and the clone
+// encounters a name not in the filter, that value will be skipped.
+LLSD llsd_clone(LLSD value, LLSD filter = LLSD());
+
+// Creates a shallow copy of a map or array. If passed any other type of LLSD
+// object it simply returns that value. See llsd_clone for a description of
+// the filter parameter.
+LLSD llsd_shallow(LLSD value, LLSD filter = LLSD());
+
+namespace llsd
+{
+
+// llsd namespace aliases
+inline
+LLSD clone (LLSD value, LLSD filter=LLSD()) { return llsd_clone (value, filter); }
+inline
+LLSD shallow(LLSD value, LLSD filter=LLSD()) { return llsd_shallow(value, filter); }
+
+} // namespace llsd
+
+// Specialization for generating a hash value from an LLSD block.
+namespace boost
+{
+template <>
+struct hash<LLSD>
+{
+ typedef LLSD argument_type;
+ typedef std::size_t result_type;
+ result_type operator()(argument_type const& s) const
+ {
+ result_type seed(0);
+
+ LLSD::Type stype = s.type();
+ boost::hash_combine(seed, (S32)stype);
+
+ switch (stype)
+ {
+ case LLSD::TypeBoolean:
+ boost::hash_combine(seed, s.asBoolean());
+ break;
+ case LLSD::TypeInteger:
+ boost::hash_combine(seed, s.asInteger());
+ break;
+ case LLSD::TypeReal:
+ boost::hash_combine(seed, s.asReal());
+ break;
+ case LLSD::TypeURI:
+ case LLSD::TypeString:
+ boost::hash_combine(seed, s.asString());
+ break;
+ case LLSD::TypeUUID:
+ boost::hash_combine(seed, s.asUUID());
+ break;
+ case LLSD::TypeDate:
+ boost::hash_combine(seed, s.asDate().secondsSinceEpoch());
+ break;
+ case LLSD::TypeBinary:
+ {
+ const LLSD::Binary &b(s.asBinary());
+ boost::hash_range(seed, b.begin(), b.end());
+ break;
+ }
+ case LLSD::TypeMap:
+ {
+ for (LLSD::map_const_iterator itm = s.beginMap(); itm != s.endMap(); ++itm)
+ {
+ boost::hash_combine(seed, (*itm).first);
+ boost::hash_combine(seed, (*itm).second);
+ }
+ break;
+ }
+ case LLSD::TypeArray:
+ for (LLSD::array_const_iterator ita = s.beginArray(); ita != s.endArray(); ++ita)
+ {
+ boost::hash_combine(seed, (*ita));
+ }
+ break;
+ case LLSD::TypeUndefined:
+ default:
+ break;
+ }
+
+ return seed;
+ }
+};
+}
+
+namespace LL
+{
+
+/*****************************************************************************
+* apply(function, LLSD array)
+*****************************************************************************/
+// validate incoming LLSD blob, and return an LLSD array suitable to pass to
+// the function of interest
+LLSD apply_llsd_fix(size_t arity, const LLSD& args);
+
+// Derived from https://stackoverflow.com/a/20441189
+// and https://en.cppreference.com/w/cpp/utility/apply .
+// We can't simply make a tuple from the LLSD array and then apply() that
+// tuple to the function -- how would make_tuple() deduce the correct
+// parameter type for each entry? We must go directly to the target function.
+template <typename CALLABLE, std::size_t... I>
+auto apply_impl(CALLABLE&& func, const LLSD& array, std::index_sequence<I...>)
+{
+ // call func(unpacked args), using generic LLSDParam<LLSD> to convert each
+ // entry in 'array' to the target parameter type
+ return std::forward<CALLABLE>(func)(LLSDParam<LLSD>(array[I])...);
+}
+
+// use apply_n<ARITY>(function, LLSD) to call a specific arity of a variadic
+// function with (that many) items from the passed LLSD array
+template <size_t ARITY, typename CALLABLE>
+auto apply_n(CALLABLE&& func, const LLSD& args)
+{
+ return apply_impl(std::forward<CALLABLE>(func),
+ apply_llsd_fix(ARITY, args),
+ std::make_index_sequence<ARITY>());
+}
+
+/**
+ * apply(function, LLSD) goes beyond C++17 std::apply(). For this case
+ * @a function @emph cannot be variadic: the compiler must know at compile
+ * time how many arguments to pass. This isn't Python. (But see apply_n() to
+ * pass a specific number of args to a variadic function.)
+ */
+template <typename CALLABLE>
+auto apply(CALLABLE&& func, const LLSD& args)
+{
+ // infer arity from the definition of func
+ constexpr auto arity = function_arity<
+ typename std::remove_reference<CALLABLE>::type>::value;
+ // now that we have a compile-time arity, apply_n() works
+ return apply_n<arity>(std::forward<CALLABLE>(func), args);
+}
+
+} // namespace LL
+
+#endif // LL_LLSDUTIL_H