LLSD-14: Bring over llsd.{h,cpp} enhancements from server-trunk.
Because new enum values have been added to the LLSD type field, a few external switch statements must be adjusted to suppress fatal warnings, even though we never expect to encounter an LLSD instance containing any of the new values.meow-7.2.2
parent
4bcdcd02fb
commit
cc1fb7bcac
|
|
@ -31,6 +31,7 @@
|
|||
#include "../llmath/llmath.h"
|
||||
#include "llformat.h"
|
||||
#include "llsdserialize.h"
|
||||
#include "stringize.h"
|
||||
|
||||
#ifndef LL_RELEASE_FOR_DOWNLOAD
|
||||
#define NAME_UNNAMED_NAMESPACE
|
||||
|
|
@ -50,6 +51,24 @@ namespace
|
|||
using namespace LLSDUnnamedNamespace;
|
||||
#endif
|
||||
|
||||
|
||||
// Normally undefined
|
||||
#ifdef LLSD_DEBUG_INFO
|
||||
|
||||
// statics
|
||||
S32 LLSD::sLLSDAllocationCount = 0;
|
||||
S32 LLSD::sLLSDNetObjects = 0;
|
||||
|
||||
#define ALLOC_LLSD_OBJECT { sLLSDNetObjects++; sLLSDAllocationCount++; }
|
||||
#define FREE_LLSD_OBJECT { sLLSDNetObjects--; }
|
||||
|
||||
#else
|
||||
|
||||
#define ALLOC_LLSD_OBJECT
|
||||
#define FREE_LLSD_OBJECT
|
||||
|
||||
#endif
|
||||
|
||||
class LLSD::Impl
|
||||
/**< This class is the abstract base class of the implementation of LLSD
|
||||
It provides the reference counting implementation, and the default
|
||||
|
|
@ -58,13 +77,10 @@ class LLSD::Impl
|
|||
|
||||
*/
|
||||
{
|
||||
private:
|
||||
U32 mUseCount;
|
||||
|
||||
protected:
|
||||
Impl();
|
||||
|
||||
enum StaticAllocationMarker { STATIC };
|
||||
enum StaticAllocationMarker { STATIC_USAGE_COUNT = 0xFFFFFFFF };
|
||||
Impl(StaticAllocationMarker);
|
||||
///< This constructor is used for static objects and causes the
|
||||
// suppresses adjusting the debugging counters when they are
|
||||
|
|
@ -72,7 +88,9 @@ protected:
|
|||
|
||||
virtual ~Impl();
|
||||
|
||||
bool shared() const { return mUseCount > 1; }
|
||||
bool shared() const { return (mUseCount > 1) && (mUseCount != STATIC_USAGE_COUNT); }
|
||||
|
||||
U32 mUseCount;
|
||||
|
||||
public:
|
||||
static void reset(Impl*& var, Impl* impl);
|
||||
|
|
@ -128,6 +146,9 @@ public:
|
|||
virtual LLSD::array_const_iterator beginArray() const { return endArray(); }
|
||||
virtual LLSD::array_const_iterator endArray() const { static const std::vector<LLSD> empty; return empty.end(); }
|
||||
|
||||
virtual void dumpStats() const;
|
||||
virtual void calcStats(S32 type_counts[], S32 share_counts[]) const;
|
||||
|
||||
static const LLSD& undef();
|
||||
|
||||
static U32 sAllocationCount;
|
||||
|
|
@ -360,6 +381,9 @@ namespace
|
|||
LLSD::map_iterator endMap() { return mData.end(); }
|
||||
virtual LLSD::map_const_iterator beginMap() const { return mData.begin(); }
|
||||
virtual LLSD::map_const_iterator endMap() const { return mData.end(); }
|
||||
|
||||
virtual void dumpStats() const;
|
||||
virtual void calcStats(S32 type_counts[], S32 share_counts[]) const;
|
||||
};
|
||||
|
||||
ImplMap& ImplMap::makeMap(LLSD::Impl*& var)
|
||||
|
|
@ -414,6 +438,36 @@ namespace
|
|||
return i->second;
|
||||
}
|
||||
|
||||
void ImplMap::dumpStats() const
|
||||
{
|
||||
std::cout << "Map size: " << mData.size() << std::endl;
|
||||
|
||||
#ifdef LLSD_DEBUG_INFO
|
||||
std::cout << "LLSD Net Objects: " << LLSD::sLLSDNetObjects << std::endl;
|
||||
std::cout << "LLSD allocations: " << LLSD::sLLSDAllocationCount << std::endl;
|
||||
#endif
|
||||
|
||||
std::cout << "LLSD::Impl Net Objects: " << sOutstandingCount << std::endl;
|
||||
std::cout << "LLSD::Impl allocations: " << sAllocationCount << std::endl;
|
||||
|
||||
Impl::dumpStats();
|
||||
}
|
||||
|
||||
void ImplMap::calcStats(S32 type_counts[], S32 share_counts[]) const
|
||||
{
|
||||
LLSD::map_const_iterator iter = beginMap();
|
||||
while (iter != endMap())
|
||||
{
|
||||
//std::cout << " " << (*iter).first << ": " << (*iter).second << std::endl;
|
||||
(*iter).second.calcStats(type_counts, share_counts);
|
||||
iter++;
|
||||
}
|
||||
|
||||
// Add in the values for this map
|
||||
Impl::calcStats(type_counts, share_counts);
|
||||
}
|
||||
|
||||
|
||||
class ImplArray : public LLSD::Impl
|
||||
{
|
||||
private:
|
||||
|
|
@ -449,6 +503,8 @@ namespace
|
|||
LLSD::array_iterator endArray() { return mData.end(); }
|
||||
virtual LLSD::array_const_iterator beginArray() const { return mData.begin(); }
|
||||
virtual LLSD::array_const_iterator endArray() const { return mData.end(); }
|
||||
|
||||
virtual void calcStats(S32 type_counts[], S32 share_counts[]) const;
|
||||
};
|
||||
|
||||
ImplArray& ImplArray::makeArray(Impl*& var)
|
||||
|
|
@ -490,12 +546,13 @@ namespace
|
|||
|
||||
void ImplArray::insert(LLSD::Integer i, const LLSD& v)
|
||||
{
|
||||
if (i < 0) {
|
||||
if (i < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
DataVector::size_type index = i;
|
||||
|
||||
if (index >= mData.size())
|
||||
if (index >= mData.size()) // tbd - sanity check limit for index ?
|
||||
{
|
||||
mData.resize(index + 1);
|
||||
}
|
||||
|
|
@ -543,6 +600,19 @@ namespace
|
|||
|
||||
return mData[index];
|
||||
}
|
||||
|
||||
void ImplArray::calcStats(S32 type_counts[], S32 share_counts[]) const
|
||||
{
|
||||
LLSD::array_const_iterator iter = beginArray();
|
||||
while (iter != endArray())
|
||||
{ // Add values for all items held in the array
|
||||
(*iter).calcStats(type_counts, share_counts);
|
||||
iter++;
|
||||
}
|
||||
|
||||
// Add in the values for this array
|
||||
Impl::calcStats(type_counts, share_counts);
|
||||
}
|
||||
}
|
||||
|
||||
LLSD::Impl::Impl()
|
||||
|
|
@ -564,8 +634,11 @@ LLSD::Impl::~Impl()
|
|||
|
||||
void LLSD::Impl::reset(Impl*& var, Impl* impl)
|
||||
{
|
||||
if (impl) ++impl->mUseCount;
|
||||
if (var && --var->mUseCount == 0)
|
||||
if (impl && impl->mUseCount != STATIC_USAGE_COUNT)
|
||||
{
|
||||
++impl->mUseCount;
|
||||
}
|
||||
if (var && var->mUseCount != STATIC_USAGE_COUNT && --var->mUseCount == 0)
|
||||
{
|
||||
delete var;
|
||||
}
|
||||
|
|
@ -574,13 +647,13 @@ void LLSD::Impl::reset(Impl*& var, Impl* impl)
|
|||
|
||||
LLSD::Impl& LLSD::Impl::safe(Impl* impl)
|
||||
{
|
||||
static Impl theUndefined(STATIC);
|
||||
static Impl theUndefined(STATIC_USAGE_COUNT);
|
||||
return impl ? *impl : theUndefined;
|
||||
}
|
||||
|
||||
const LLSD::Impl& LLSD::Impl::safe(const Impl* impl)
|
||||
{
|
||||
static Impl theUndefined(STATIC);
|
||||
static Impl theUndefined(STATIC_USAGE_COUNT);
|
||||
return impl ? *impl : theUndefined;
|
||||
}
|
||||
|
||||
|
|
@ -656,6 +729,39 @@ const LLSD& LLSD::Impl::undef()
|
|||
return immutableUndefined;
|
||||
}
|
||||
|
||||
void LLSD::Impl::dumpStats() const
|
||||
{
|
||||
S32 type_counts[LLSD::TypeLLSDNumTypes + 1];
|
||||
memset(&type_counts, 0, LLSD::TypeLLSDNumTypes * sizeof(S32));
|
||||
|
||||
S32 share_counts[LLSD::TypeLLSDNumTypes + 1];
|
||||
memset(&share_counts, 0, LLSD::TypeLLSDNumTypes * sizeof(S32));
|
||||
|
||||
// Add info from all the values this object has
|
||||
calcStats(type_counts, share_counts);
|
||||
|
||||
S32 type_index = LLSD::TypeLLSDTypeBegin;
|
||||
while (type_index != LLSD::TypeLLSDTypeEnd)
|
||||
{
|
||||
std::cout << LLSD::typeString((LLSD::Type)type_index) << " type "
|
||||
<< type_counts[type_index] << " objects, "
|
||||
<< share_counts[type_index] << " shared"
|
||||
<< std::endl;
|
||||
type_index++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void LLSD::Impl::calcStats(S32 type_counts[], S32 share_counts[]) const
|
||||
{
|
||||
type_counts[(S32) type()]++;
|
||||
if (shared())
|
||||
{
|
||||
share_counts[(S32) type()]++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
U32 LLSD::Impl::sAllocationCount = 0;
|
||||
U32 LLSD::Impl::sOutstandingCount = 0;
|
||||
|
||||
|
|
@ -681,10 +787,10 @@ namespace
|
|||
}
|
||||
|
||||
|
||||
LLSD::LLSD() : impl(0) { }
|
||||
LLSD::~LLSD() { Impl::reset(impl, 0); }
|
||||
LLSD::LLSD() : impl(0) { ALLOC_LLSD_OBJECT; }
|
||||
LLSD::~LLSD() { FREE_LLSD_OBJECT; Impl::reset(impl, 0); }
|
||||
|
||||
LLSD::LLSD(const LLSD& other) : impl(0) { assign(other); }
|
||||
LLSD::LLSD(const LLSD& other) : impl(0) { ALLOC_LLSD_OBJECT; assign(other); }
|
||||
void LLSD::assign(const LLSD& other) { Impl::assign(impl, other.impl); }
|
||||
|
||||
|
||||
|
|
@ -693,17 +799,17 @@ void LLSD::clear() { Impl::assignUndefined(impl); }
|
|||
LLSD::Type LLSD::type() const { return safe(impl).type(); }
|
||||
|
||||
// Scaler Constructors
|
||||
LLSD::LLSD(Boolean v) : impl(0) { assign(v); }
|
||||
LLSD::LLSD(Integer v) : impl(0) { assign(v); }
|
||||
LLSD::LLSD(Real v) : impl(0) { assign(v); }
|
||||
LLSD::LLSD(const UUID& v) : impl(0) { assign(v); }
|
||||
LLSD::LLSD(const String& v) : impl(0) { assign(v); }
|
||||
LLSD::LLSD(const Date& v) : impl(0) { assign(v); }
|
||||
LLSD::LLSD(const URI& v) : impl(0) { assign(v); }
|
||||
LLSD::LLSD(const Binary& v) : impl(0) { assign(v); }
|
||||
LLSD::LLSD(Boolean v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); }
|
||||
LLSD::LLSD(Integer v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); }
|
||||
LLSD::LLSD(Real v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); }
|
||||
LLSD::LLSD(const UUID& v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); }
|
||||
LLSD::LLSD(const String& v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); }
|
||||
LLSD::LLSD(const Date& v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); }
|
||||
LLSD::LLSD(const URI& v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); }
|
||||
LLSD::LLSD(const Binary& v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); }
|
||||
|
||||
// Convenience Constructors
|
||||
LLSD::LLSD(F32 v) : impl(0) { assign((Real)v); }
|
||||
LLSD::LLSD(F32 v) : impl(0) { ALLOC_LLSD_OBJECT; assign((Real)v); }
|
||||
|
||||
// Scalar Assignment
|
||||
void LLSD::assign(Boolean v) { safe(impl).assign(impl, v); }
|
||||
|
|
@ -726,7 +832,7 @@ LLSD::URI LLSD::asURI() const { return safe(impl).asURI(); }
|
|||
LLSD::Binary LLSD::asBinary() const { return safe(impl).asBinary(); }
|
||||
|
||||
// const char * helpers
|
||||
LLSD::LLSD(const char* v) : impl(0) { assign(v); }
|
||||
LLSD::LLSD(const char* v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); }
|
||||
void LLSD::assign(const char* v)
|
||||
{
|
||||
if(v) assign(std::string(v));
|
||||
|
|
@ -801,15 +907,9 @@ static const char *llsd_dump(const LLSD &llsd, bool useXMLFormat)
|
|||
{
|
||||
std::ostringstream out;
|
||||
if (useXMLFormat)
|
||||
{
|
||||
LLSDXMLStreamer xml_streamer(llsd);
|
||||
out << xml_streamer;
|
||||
}
|
||||
out << LLSDXMLStreamer(llsd);
|
||||
else
|
||||
{
|
||||
LLSDNotationStreamer notation_streamer(llsd);
|
||||
out << notation_streamer;
|
||||
}
|
||||
out << LLSDNotationStreamer(llsd);
|
||||
out_string = out.str();
|
||||
}
|
||||
int len = out_string.length();
|
||||
|
|
@ -840,3 +940,33 @@ LLSD::array_iterator LLSD::beginArray() { return makeArray(impl).beginArray();
|
|||
LLSD::array_iterator LLSD::endArray() { return makeArray(impl).endArray(); }
|
||||
LLSD::array_const_iterator LLSD::beginArray() const{ return safe(impl).beginArray(); }
|
||||
LLSD::array_const_iterator LLSD::endArray() const { return safe(impl).endArray(); }
|
||||
|
||||
|
||||
// Diagnostic dump of contents in an LLSD object
|
||||
void LLSD::dumpStats() const { safe(impl).dumpStats(); }
|
||||
void LLSD::calcStats(S32 type_counts[], S32 share_counts[]) const
|
||||
{ safe(impl).calcStats(type_counts, share_counts); }
|
||||
|
||||
// static
|
||||
std::string LLSD::typeString(Type type)
|
||||
{
|
||||
static const char * sTypeNameArray[] = {
|
||||
"Undefined",
|
||||
"Boolean",
|
||||
"Integer",
|
||||
"Real",
|
||||
"String",
|
||||
"UUID",
|
||||
"Date",
|
||||
"URI",
|
||||
"Binary",
|
||||
"Map",
|
||||
"Array"
|
||||
};
|
||||
|
||||
if (0 <= type < (sizeof(sTypeNameArray)/sizeof(sTypeNameArray[0])))
|
||||
{
|
||||
return sTypeNameArray[type];
|
||||
}
|
||||
return STRINGIZE("** invalid type value " << type);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,9 +80,73 @@
|
|||
|
||||
An array is a sequence of zero or more LLSD values.
|
||||
|
||||
Thread Safety
|
||||
|
||||
In general, these LLSD classes offer *less* safety than STL container
|
||||
classes. Implementations prior to this one were unsafe even when
|
||||
completely unrelated LLSD trees were in two threads due to reference
|
||||
sharing of special 'undefined' values that participated in the reference
|
||||
counting mechanism.
|
||||
|
||||
The dereference-before-refcount and aggressive tree sharing also make
|
||||
it impractical to share an LLSD across threads. A strategy of passing
|
||||
ownership or a copy to another thread is still difficult due to a lack
|
||||
of a cloning interface but it can be done with some care.
|
||||
|
||||
One way of transferring ownership is as follows:
|
||||
|
||||
void method(const LLSD input) {
|
||||
...
|
||||
LLSD * xfer_tree = new LLSD();
|
||||
{
|
||||
// Top-level values
|
||||
(* xfer_tree)['label'] = "Some text";
|
||||
(* xfer_tree)['mode'] = APP_MODE_CONSTANT;
|
||||
|
||||
// There will be a second-level
|
||||
LLSD subtree(LLSD::emptyMap());
|
||||
(* xfer_tree)['subtree'] = subtree;
|
||||
|
||||
// Do *not* copy from LLSD objects via LLSD
|
||||
// intermediaries. Only use plain-old-data
|
||||
// types as intermediaries to prevent reference
|
||||
// sharing.
|
||||
subtree['value1'] = input['value1'].asInteger();
|
||||
subtree['value2'] = input['value2'].asString();
|
||||
|
||||
// Close scope and drop 'subtree's reference.
|
||||
// Only xfer_tree has a reference to the second
|
||||
// level data.
|
||||
}
|
||||
...
|
||||
// Transfer the LLSD pointer to another thread. Ownership
|
||||
// transfers, this thread no longer has a reference to any
|
||||
// part of the xfer_tree and there's nothing to free or
|
||||
// release here. Receiving thread does need to delete the
|
||||
// pointer when it is done with the LLSD. Transfer
|
||||
// mechanism must perform correct data ordering operations
|
||||
// as dictated by architecture.
|
||||
other_thread.sendMessageAndPointer("Take This", xfer_tree);
|
||||
xfer_tree = NULL;
|
||||
|
||||
|
||||
Avoid this pattern which provides half of a race condition:
|
||||
|
||||
void method(const LLSD input) {
|
||||
...
|
||||
LLSD xfer_tree(LLSD::emptyMap());
|
||||
xfer_tree['label'] = "Some text";
|
||||
xfer_tree['mode'] = APP_MODE_CONSTANT;
|
||||
...
|
||||
other_thread.sendMessageAndPointer("Take This", xfer_tree);
|
||||
|
||||
|
||||
@nosubgrouping
|
||||
*/
|
||||
|
||||
// Normally undefined, used for diagnostics
|
||||
//#define LLSD_DEBUG_INFO 1
|
||||
|
||||
class LL_COMMON_API LLSD
|
||||
{
|
||||
public:
|
||||
|
|
@ -266,7 +330,7 @@ public:
|
|||
/** @name Type Testing */
|
||||
//@{
|
||||
enum Type {
|
||||
TypeUndefined,
|
||||
TypeUndefined = 0,
|
||||
TypeBoolean,
|
||||
TypeInteger,
|
||||
TypeReal,
|
||||
|
|
@ -276,7 +340,10 @@ public:
|
|||
TypeURI,
|
||||
TypeBinary,
|
||||
TypeMap,
|
||||
TypeArray
|
||||
TypeArray,
|
||||
TypeLLSDTypeEnd,
|
||||
TypeLLSDTypeBegin = TypeUndefined,
|
||||
TypeLLSDNumTypes = (TypeLLSDTypeEnd - TypeLLSDTypeBegin)
|
||||
};
|
||||
|
||||
Type type() const;
|
||||
|
|
@ -338,6 +405,17 @@ private:
|
|||
/// Returns Notation version of llsd -- only to be called from debugger
|
||||
static const char *dump(const LLSD &llsd);
|
||||
//@}
|
||||
|
||||
public:
|
||||
void dumpStats() const; // Output information on object and usage
|
||||
void calcStats(S32 type_counts[], S32 share_counts[]) const; // Calculate the number of LLSD objects used by this value
|
||||
|
||||
static std::string typeString(Type type); // Return human-readable type as a string
|
||||
|
||||
#ifdef LLSD_DEBUG_INFO
|
||||
static S32 sLLSDAllocationCount; // Number of LLSD objects ever created
|
||||
static S32 sLLSDNetObjects; // Number of LLSD objects that exist
|
||||
#endif
|
||||
};
|
||||
|
||||
struct llsd_select_bool : public std::unary_function<LLSD, LLSD::Boolean>
|
||||
|
|
|
|||
|
|
@ -291,6 +291,7 @@ S32 getElementSize(const LLSD& llsd)
|
|||
case LLSD::TypeMap:
|
||||
case LLSD::TypeArray:
|
||||
case LLSD::TypeUndefined:
|
||||
default: // TypeLLSDTypeEnd, TypeLLSDNumTypes, etc.
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
#include "llformat.h"
|
||||
#include "llsd.h"
|
||||
#include "lluri.h"
|
||||
#include "stringize.h"
|
||||
|
||||
namespace tut
|
||||
{
|
||||
|
|
@ -144,6 +145,10 @@ namespace tut
|
|||
}
|
||||
return;
|
||||
}
|
||||
default:
|
||||
// should never get here, but compiler produces warning if we
|
||||
// don't cover this case, and at Linden warnings are fatal.
|
||||
throw failure(STRINGIZE("invalid type field " << actual.type()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue