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
Nat Goodspeed 2011-11-17 08:06:31 -05:00
parent 4bcdcd02fb
commit cc1fb7bcac
4 changed files with 248 additions and 34 deletions

View File

@ -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);
}

View File

@ -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>

View File

@ -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;

View File

@ -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()));
}
}