SL-11216: Add llsd::drill() function to drill into an LLSD blob.

We include both const and non-const overloads. The latter returns LLSD&, so
you can assign to the located element.

In fact we already implemented the non-const logic in a less public form as
storeToLLSDPath() in lleventcoro.cpp. Reimplement the latter to use the new
llsd::drill() function.
meow-7.2.2
Nat Goodspeed 2019-05-30 15:35:54 -04:00
parent 0bb6f754a1
commit 712a03300b
3 changed files with 97 additions and 45 deletions

View File

@ -36,6 +36,7 @@
// external library headers
// other Linden headers
#include "llsdserialize.h"
#include "llsdutil.h"
#include "llerror.h"
#include "llcoros.h"
#include "llmake.h"
@ -92,57 +93,16 @@ std::string listenerNameForCoro()
* In the degenerate case in which @a path is an empty array, @a dest will
* @em become @a value rather than @em containing it.
*/
void storeToLLSDPath(LLSD& dest, const LLSD& rawPath, const LLSD& value)
void storeToLLSDPath(LLSD& dest, const LLSD& path, const LLSD& value)
{
if (rawPath.isUndefined())
if (path.isUndefined())
{
// no-op case
return;
}
// Arrange to treat rawPath uniformly as an array. If it's not already an
// array, store it as the only entry in one.
LLSD path;
if (rawPath.isArray())
{
path = rawPath;
}
else
{
path.append(rawPath);
}
// Need to indicate a current destination -- but that current destination
// needs to change as we step through the path array. Where normally we'd
// use an LLSD& to capture a subscripted LLSD lvalue, this time we must
// instead use a pointer -- since it must be reassigned.
LLSD* pdest = &dest;
// Now loop through that array
for (LLSD::Integer i = 0; i < path.size(); ++i)
{
if (path[i].isString())
{
// *pdest is an LLSD map
pdest = &((*pdest)[path[i].asString()]);
}
else if (path[i].isInteger())
{
// *pdest is an LLSD array
pdest = &((*pdest)[path[i].asInteger()]);
}
else
{
// What do we do with Real or Array or Map or ...?
// As it's a coder error -- not a user error -- rub the coder's
// face in it so it gets fixed.
LL_ERRS("lleventcoro") << "storeToLLSDPath(" << dest << ", " << rawPath << ", " << value
<< "): path[" << i << "] bad type " << path[i].type() << LL_ENDL;
}
}
// Here *pdest is where we should store value.
*pdest = value;
// Drill down to where we should store 'value'.
llsd::drill(dest, path) = value;
}
/// For LLCoros::Future<LLSD>::make_callback(), the callback has a signature

View File

@ -681,3 +681,70 @@ bool llsd_equals(const LLSD& lhs, const LLSD& rhs, int bits)
return false; // pacify the compiler
}
}
/*****************************************************************************
* llsd::drill()
*****************************************************************************/
namespace llsd
{
LLSD& drill(LLSD& blob, const LLSD& rawPath)
{
// Treat rawPath uniformly as an array. If it's not already an array,
// store it as the only entry in one.
LLSD path;
if (rawPath.isArray())
{
path = rawPath;
}
else
{
path.append(rawPath);
}
// Need to indicate a current destination -- but that current destination
// must change as we step through the path array. Where normally we'd use
// an LLSD& to capture a subscripted LLSD lvalue, this time we must
// instead use a pointer -- since it must be reassigned.
// Start by pointing to the input blob exactly as is.
LLSD* located{&blob};
// Extract the element of interest by walking path. Use an explicit index
// so that, in case of a bogus type in path, we can identify the specific
// path entry that's bad.
for (LLSD::Integer i = 0; i < path.size(); ++i)
{
const LLSD& key{path[i]};
if (key.isString())
{
// a string path element is a map key
located = &((*located)[key.asString()]);
}
else if (key.isInteger())
{
// an integer path element is an array index
located = &((*located)[key.asInteger()]);
}
else
{
// What do we do with Real or Array or Map or ...?
// As it's a coder error -- not a user error -- rub the coder's
// face in it so it gets fixed.
LL_ERRS("llsdutil") << "drill(" << blob << ", " << rawPath
<< "): path[" << i << "] bad type "
<< sTypes.lookup(key.type()) << LL_ENDL;
}
}
// dereference the pointer to return a reference to the element we found
return *located;
}
LLSD drill(const LLSD& blob, const LLSD& path)
{
// non-const drill() does exactly what we want. Temporarily cast away
// const-ness and use that.
return drill(const_cast<LLSD&>(blob), path);
}
} // namespace llsd

View File

@ -142,6 +142,31 @@ template<typename Input> LLSD llsd_copy_array(Input iter, Input end)
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() returns 'blob' as is.
*/
LLSD drill(const LLSD& blob, const LLSD& path);
LLSD& drill( LLSD& blob, const LLSD& path);
}
/*****************************************************************************
* LLSDArray
*****************************************************************************/