LLCommon cleanup and updating to V2 (V3 now, I guess.)
This commit is contained in:
@@ -19,3 +19,8 @@ set(LLCOMMON_INCLUDE_DIRS
|
||||
)
|
||||
|
||||
set(LLCOMMON_LIBRARIES llcommon)
|
||||
|
||||
set(LLCOMMON_LINK_SHARED ON CACHE BOOL "Build the llcommon target as a shared library.")
|
||||
if(LLCOMMON_LINK_SHARED)
|
||||
add_definitions(-DLL_COMMON_LINK_SHARED=1)
|
||||
endif(LLCOMMON_LINK_SHARED)
|
||||
|
||||
@@ -89,7 +89,7 @@ S32 check_for_invalid_wav_formats(const std::string& in_fname, std::string& erro
|
||||
|
||||
//********************************
|
||||
LLAPRFile infile ;
|
||||
infile.open(in_fname,LL_APR_RB, LLAPRFile::global);
|
||||
infile.open(in_fname,LL_APR_RB);
|
||||
//********************************
|
||||
if (!infile.getFileHandle())
|
||||
{
|
||||
@@ -240,7 +240,7 @@ S32 encode_vorbis_file(const std::string& in_fname, const std::string& out_fname
|
||||
S32 data_left = 0;
|
||||
|
||||
LLAPRFile infile ;
|
||||
infile.open(in_fname,LL_APR_RB, LLAPRFile::global);
|
||||
infile.open(in_fname,LL_APR_RB);
|
||||
if (!infile.getFileHandle())
|
||||
{
|
||||
llwarns << "Couldn't open temporary ogg file for writing: " << in_fname
|
||||
@@ -249,7 +249,7 @@ S32 encode_vorbis_file(const std::string& in_fname, const std::string& out_fname
|
||||
}
|
||||
|
||||
LLAPRFile outfile ;
|
||||
outfile.open(out_fname,LL_APR_WPB, LLAPRFile::global);
|
||||
outfile.open(out_fname,LL_APR_WPB);
|
||||
if (!outfile.getFileHandle())
|
||||
{
|
||||
llwarns << "Couldn't open upload sound file for reading: " << in_fname
|
||||
|
||||
@@ -226,7 +226,7 @@ ELoadStatus LLBVHLoader::loadTranslationTable(const char *fileName)
|
||||
std::string path = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS,fileName);
|
||||
|
||||
LLAPRFile infile ;
|
||||
infile.open(path, LL_APR_R, LLAPRFile::global);
|
||||
infile.open(path, LL_APR_R);
|
||||
apr_file_t *fp = infile.getFileHandle();
|
||||
if (!fp)
|
||||
return E_ST_NO_XLT_FILE;
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
#include "llmotioncontroller.h"
|
||||
#include "llvisualparam.h"
|
||||
#include "string_table.h"
|
||||
#include "llmemory.h"
|
||||
#include "llpointer.h"
|
||||
#include "llthread.h"
|
||||
|
||||
class LLPolyMesh;
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
// Header Files
|
||||
//-----------------------------------------------------------------------------
|
||||
#include "lljoint.h"
|
||||
#include "llmemory.h"
|
||||
#include "llrefcount.h"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// class LLJointState
|
||||
|
||||
@@ -163,8 +163,6 @@ void LLPose::setWeight(F32 weight)
|
||||
// there was a crash here
|
||||
// </edit>
|
||||
llassert_always(iter->second.notNull());
|
||||
if(!iter->second) //uhoh...
|
||||
continue;
|
||||
iter->second->setWeight(weight);
|
||||
}
|
||||
mWeight = weight;
|
||||
|
||||
@@ -40,6 +40,8 @@
|
||||
#include "lljointstate.h"
|
||||
#include "lljoint.h"
|
||||
#include "llmap.h"
|
||||
#include "llpointer.h"
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
|
||||
@@ -211,7 +211,7 @@ LLFSMState* LLStateDiagram::getState(U32 state_id)
|
||||
BOOL LLStateDiagram::saveDotFile(const std::string& filename)
|
||||
{
|
||||
LLAPRFile outfile ;
|
||||
outfile.open(filename, LL_APR_W, LLAPRFile::global);
|
||||
outfile.open(filename, LL_APR_W);
|
||||
apr_file_t* dot_file = outfile.getFileHandle() ;
|
||||
|
||||
if (!dot_file)
|
||||
|
||||
@@ -196,6 +196,7 @@ set(llcommon_HEADER_FILES
|
||||
llsys.h
|
||||
llthread.h
|
||||
lltimer.h
|
||||
lltreeiterators.h
|
||||
lluri.h
|
||||
lluuid.h
|
||||
lluuidhashmap.h
|
||||
@@ -232,6 +233,7 @@ target_link_libraries(
|
||||
${EXPAT_LIBRARIES}
|
||||
${ZLIB_LIBRARIES}
|
||||
${WINDOWS_LIBRARIES}
|
||||
${BOOST_REGEX_LIBRARY}
|
||||
${CWDEBUG_LIBRARIES}
|
||||
${CORESERVICES_LIBRARY}
|
||||
)
|
||||
|
||||
@@ -191,6 +191,10 @@ apr_status_t LLAPRFile::open(std::string const& filename, apr_int32_t flags, acc
|
||||
return status;
|
||||
}
|
||||
|
||||
apr_status_t LLAPRFile::open(const std::string& filename, apr_int32_t flags, BOOL use_global_pool)
|
||||
{
|
||||
return open(filename, flags, use_global_pool ? LLAPRFile::global : LLAPRFile::local);
|
||||
}
|
||||
// File I/O
|
||||
S32 LLAPRFile::read(void *buf, S32 nbytes)
|
||||
{
|
||||
|
||||
@@ -150,10 +150,11 @@ public:
|
||||
};
|
||||
|
||||
LLAPRFile() ;
|
||||
LLAPRFile(const std::string& filename, apr_int32_t flags, access_t access_type);
|
||||
LLAPRFile(const std::string& filename, apr_int32_t flags, access_t access_type = LLAPRFile::global);
|
||||
~LLAPRFile() ;
|
||||
|
||||
apr_status_t open(const std::string& filename, apr_int32_t flags, access_t access_type, S32* sizep = NULL);
|
||||
apr_status_t open(const std::string& filename, apr_int32_t flags, access_t access_type = LLAPRFile::global, S32* sizep = NULL);
|
||||
apr_status_t open(const std::string& filename, apr_int32_t flags, BOOL use_global_pool); //use global pool.
|
||||
apr_status_t close() ;
|
||||
|
||||
// Returns actual offset, -1 if seek fails
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include "llassettype.h"
|
||||
#include "lldictionary.h"
|
||||
#include "llmemory.h"
|
||||
#include "llsingleton.h"
|
||||
|
||||
///----------------------------------------------------------------------------
|
||||
/// Class LLAssetType
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "llfoldertype.h"
|
||||
#include "lldictionary.h"
|
||||
#include "llmemory.h"
|
||||
#include "llsingleton.h"
|
||||
|
||||
///----------------------------------------------------------------------------
|
||||
/// Class LLFolderType
|
||||
@@ -95,7 +96,12 @@ LLFolderDictionary::LLFolderDictionary()
|
||||
addEntry(LLFolderType::FT_CURRENT_OUTFIT, new FolderEntry("current", TRUE));
|
||||
addEntry(LLFolderType::FT_OUTFIT, new FolderEntry("outfit", FALSE));
|
||||
addEntry(LLFolderType::FT_MY_OUTFITS, new FolderEntry("my_otfts", TRUE));
|
||||
|
||||
addEntry(LLFolderType::FT_MESH, new FolderEntry("mesh", TRUE));
|
||||
|
||||
addEntry(LLFolderType::FT_INBOX, new FolderEntry("inbox", TRUE));
|
||||
addEntry(LLFolderType::FT_OUTBOX, new FolderEntry("outbox", TRUE));
|
||||
addEntry(LLFolderType::FT_BASIC_ROOT, new FolderEntry("basic_rt", TRUE));
|
||||
|
||||
addEntry(LLFolderType::FT_NONE, new FolderEntry("-1", FALSE));
|
||||
};
|
||||
|
||||
@@ -87,9 +87,14 @@ public:
|
||||
FT_OUTFIT = 47,
|
||||
FT_MY_OUTFITS = 48,
|
||||
|
||||
FT_INBOX = 49,
|
||||
FT_MESH = 49,
|
||||
|
||||
FT_COUNT = 50,
|
||||
FT_INBOX = 50,
|
||||
FT_OUTBOX = 51,
|
||||
|
||||
FT_BASIC_ROOT = 52,
|
||||
|
||||
FT_COUNT,
|
||||
|
||||
FT_NONE = -1
|
||||
};
|
||||
|
||||
@@ -99,7 +99,7 @@ public:
|
||||
public:
|
||||
typedef boost::iterator_facade<key_iter, KEY, boost::forward_traversal_tag> super_t;
|
||||
|
||||
key_iter(typename InstanceMap::iterator& it)
|
||||
key_iter(typename InstanceMap::iterator it)
|
||||
: mIterator(it)
|
||||
{
|
||||
++sIterationNestDepth;
|
||||
|
||||
@@ -118,6 +118,62 @@ public:
|
||||
THROTTLE_BLOCKED, // rate exceed, block key
|
||||
};
|
||||
|
||||
F64 getActionCount(const T& id)
|
||||
{
|
||||
U64 now = 0;
|
||||
if ( mIsRealtime )
|
||||
{
|
||||
now = LLKeyThrottleImpl<T>::getTime();
|
||||
}
|
||||
else
|
||||
{
|
||||
now = LLKeyThrottleImpl<T>::getFrame();
|
||||
}
|
||||
|
||||
if (now >= (m.startTime + m.intervalLength))
|
||||
{
|
||||
if (now < (m.startTime + 2 * m.intervalLength))
|
||||
{
|
||||
// prune old data
|
||||
delete m.prevMap;
|
||||
m.prevMap = m.currMap;
|
||||
m.currMap = new typename LLKeyThrottleImpl<T>::EntryMap;
|
||||
|
||||
m.startTime += m.intervalLength;
|
||||
}
|
||||
else
|
||||
{
|
||||
// lots of time has passed, all data is stale
|
||||
delete m.prevMap;
|
||||
delete m.currMap;
|
||||
m.prevMap = new typename LLKeyThrottleImpl<T>::EntryMap;
|
||||
m.currMap = new typename LLKeyThrottleImpl<T>::EntryMap;
|
||||
|
||||
m.startTime = now;
|
||||
}
|
||||
}
|
||||
|
||||
U32 prevCount = 0;
|
||||
|
||||
typename LLKeyThrottleImpl<T>::EntryMap::const_iterator prev = m.prevMap->find(id);
|
||||
if (prev != m.prevMap->end())
|
||||
{
|
||||
prevCount = prev->second.count;
|
||||
}
|
||||
|
||||
typename LLKeyThrottleImpl<T>::Entry& curr = (*m.currMap)[id];
|
||||
|
||||
// curr.count is the number of keys in
|
||||
// this current 'time slice' from the beginning of it until now
|
||||
// prevCount is the number of keys in the previous
|
||||
// time slice scaled to be one full time slice back from the current
|
||||
// (now) time.
|
||||
|
||||
// compute current, windowed rate
|
||||
F64 timeInCurrent = ((F64)(now - m.startTime) / m.intervalLength);
|
||||
F64 averageCount = curr.count + prevCount * (1.0 - timeInCurrent);
|
||||
return averageCount;
|
||||
}
|
||||
// call each time the key wants use
|
||||
State noteAction(const T& id, S32 weight = 1)
|
||||
{
|
||||
|
||||
@@ -74,9 +74,3 @@ void LLMortician::setZealous(BOOL b)
|
||||
{
|
||||
sDestroyImmediate = b;
|
||||
}
|
||||
|
||||
// static
|
||||
BOOL LLMortician::getZealous()
|
||||
{
|
||||
return sDestroyImmediate;
|
||||
}
|
||||
|
||||
@@ -46,7 +46,6 @@ public:
|
||||
|
||||
// sets destroy immediate true
|
||||
static void setZealous(BOOL b);
|
||||
static BOOL getZealous();
|
||||
|
||||
private:
|
||||
static BOOL sDestroyImmediate;
|
||||
|
||||
@@ -107,7 +107,17 @@
|
||||
|
||||
#endif
|
||||
|
||||
// Deal with the differences on Windows
|
||||
|
||||
// Static linking with apr on windows needs to be declared.
|
||||
#if LL_WINDOWS && !LL_COMMON_LINK_SHARED
|
||||
#ifndef APR_DECLARE_STATIC
|
||||
#define APR_DECLARE_STATIC // For APR on Windows
|
||||
#endif
|
||||
#ifndef APU_DECLARE_STATIC
|
||||
#define APU_DECLARE_STATIC // For APR util on Windows
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(LL_WINDOWS)
|
||||
#define BOOST_REGEX_NO_LIB 1
|
||||
#define CURL_STATICLIB 1
|
||||
@@ -159,12 +169,19 @@
|
||||
#define LL_DLLIMPORT
|
||||
#endif // LL_WINDOWS
|
||||
|
||||
#ifdef llcommon_EXPORTS
|
||||
// Compiling llcommon (shared)
|
||||
#define LL_COMMON_API LL_DLLEXPORT
|
||||
#else // llcommon_EXPORTS
|
||||
// Using llcommon (shared)
|
||||
#define LL_COMMON_API LL_DLLIMPORT
|
||||
#endif // llcommon_EXPORTS
|
||||
#if LL_COMMON_LINK_SHARED
|
||||
// CMake automagically defines llcommon_EXPORTS only when building llcommon
|
||||
// sources, and only when llcommon is a shared library (i.e. when
|
||||
// LL_COMMON_LINK_SHARED). We must still test LL_COMMON_LINK_SHARED because
|
||||
// otherwise we can't distinguish between (non-llcommon source) and (llcommon
|
||||
// not shared).
|
||||
# if defined(llcommon_EXPORTS)
|
||||
# define LL_COMMON_API LL_DLLEXPORT
|
||||
# else //llcommon_EXPORTS
|
||||
# define LL_COMMON_API LL_DLLIMPORT
|
||||
# endif //llcommon_EXPORTS
|
||||
#else // LL_COMMON_LINK_SHARED
|
||||
# define LL_COMMON_API
|
||||
#endif // LL_COMMON_LINK_SHARED
|
||||
|
||||
#endif // not LL_LINDEN_PREPROCESSOR_H
|
||||
|
||||
@@ -31,8 +31,9 @@
|
||||
|
||||
#include "linden_common.h"
|
||||
#include "llqueuedthread.h"
|
||||
|
||||
#include "llstl.h"
|
||||
#include "lltimer.h"
|
||||
#include "lltimer.h" // ms_sleep()
|
||||
|
||||
//============================================================================
|
||||
|
||||
|
||||
@@ -754,11 +754,7 @@ void LLSD::insert(const String& k, const LLSD& v) { makeMap(impl).insert(k, v);
|
||||
LLSD& LLSD::with(const String& k, const LLSD& v)
|
||||
{
|
||||
makeMap(impl).insert(k, v);
|
||||
#ifdef LL_MSVC7
|
||||
return *dynamic_cast<LLSD*>(this);
|
||||
#else
|
||||
return *this;
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
void LLSD::erase(const String& k) { makeMap(impl).erase(k); }
|
||||
|
||||
@@ -784,11 +780,7 @@ void LLSD::insert(Integer i, const LLSD& v) { makeArray(impl).insert(i, v); }
|
||||
LLSD& LLSD::with(Integer i, const LLSD& v)
|
||||
{
|
||||
makeArray(impl).insert(i, v);
|
||||
#ifdef LL_MSVC7
|
||||
return *dynamic_cast<LLSD*>(this);
|
||||
#else
|
||||
return *this;
|
||||
#endif
|
||||
return *this;
|
||||
}
|
||||
void LLSD::append(const LLSD& v) { makeArray(impl).append(v); }
|
||||
void LLSD::erase(Integer i) { makeArray(impl).erase(i); }
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
|
||||
#include "linden_common.h"
|
||||
#include "llsdserialize.h"
|
||||
#include "llmemory.h"
|
||||
#include "llpointer.h"
|
||||
#include "llstreamtools.h" // for fullread
|
||||
|
||||
#include <iostream>
|
||||
|
||||
@@ -36,8 +36,9 @@
|
||||
#define LL_LLSDSERIALIZE_H
|
||||
|
||||
#include <iosfwd>
|
||||
#include "llpointer.h"
|
||||
#include "llrefcount.h"
|
||||
#include "llsd.h"
|
||||
#include "llmemory.h"
|
||||
|
||||
/**
|
||||
* @class LLSDParser
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include <deque>
|
||||
|
||||
#include "apr_base64.h"
|
||||
#include <boost/regex.hpp>
|
||||
|
||||
extern "C"
|
||||
{
|
||||
@@ -734,6 +735,7 @@ void LLSDXMLParser::Impl::endElementHandler(const XML_Char* name)
|
||||
case ELEMENT_INTEGER:
|
||||
{
|
||||
S32 i;
|
||||
// sscanf okay here with different locales - ints don't change for different locale settings like floats do.
|
||||
if ( sscanf(mCurrentContent.c_str(), "%d", &i ) == 1 )
|
||||
{ // See if sscanf works - it's faster
|
||||
value = i;
|
||||
@@ -747,15 +749,19 @@ void LLSDXMLParser::Impl::endElementHandler(const XML_Char* name)
|
||||
|
||||
case ELEMENT_REAL:
|
||||
{
|
||||
F64 r;
|
||||
if ( sscanf(mCurrentContent.c_str(), "%lf", &r ) == 1 )
|
||||
{ // See if sscanf works - it's faster
|
||||
value = r;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = LLSD(mCurrentContent).asReal();
|
||||
}
|
||||
value = LLSD(mCurrentContent).asReal();
|
||||
// removed since this breaks when locale has decimal separator that isn't '.'
|
||||
// investigated changing local to something compatible each time but deemed higher
|
||||
// risk that just using LLSD.asReal() each time.
|
||||
//F64 r;
|
||||
//if ( sscanf(mCurrentContent.c_str(), "%lf", &r ) == 1 )
|
||||
//{ // See if sscanf works - it's faster
|
||||
// value = r;
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// value = LLSD(mCurrentContent).asReal();
|
||||
//}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -777,10 +783,17 @@ void LLSDXMLParser::Impl::endElementHandler(const XML_Char* name)
|
||||
|
||||
case ELEMENT_BINARY:
|
||||
{
|
||||
S32 len = apr_base64_decode_len(mCurrentContent.c_str());
|
||||
// Regex is expensive, but only fix for whitespace in base64,
|
||||
// created by python and other non-linden systems - DEV-39358
|
||||
// Fortunately we have very little binary passing now,
|
||||
// so performance impact shold be negligible. + poppy 2009-09-04
|
||||
boost::regex r;
|
||||
r.assign("\\s");
|
||||
std::string stripped = boost::regex_replace(mCurrentContent, r, "");
|
||||
S32 len = apr_base64_decode_len(stripped.c_str());
|
||||
std::vector<U8> data;
|
||||
data.resize(len);
|
||||
len = apr_base64_decode_binary(&data[0], mCurrentContent.c_str());
|
||||
len = apr_base64_decode_binary(&data[0], stripped.c_str());
|
||||
data.resize(len);
|
||||
value = data;
|
||||
break;
|
||||
|
||||
@@ -46,6 +46,12 @@
|
||||
#endif
|
||||
|
||||
#include "llsdserialize.h"
|
||||
#include "stringize.h"
|
||||
#include "is_approx_equal_fraction.h"
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <boost/range.hpp>
|
||||
|
||||
// U32
|
||||
LLSD ll_sd_from_U32(const U32 val)
|
||||
@@ -171,6 +177,15 @@ char* ll_print_sd(const LLSD& sd)
|
||||
return buffer;
|
||||
}
|
||||
|
||||
char* ll_pretty_print_sd_ptr(const LLSD* sd)
|
||||
{
|
||||
if (sd)
|
||||
{
|
||||
return ll_pretty_print_sd(*sd);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char* ll_pretty_print_sd(const LLSD& sd)
|
||||
{
|
||||
const U32 bufferSize = 10 * 1024;
|
||||
@@ -304,3 +319,363 @@ BOOL compare_llsd_with_template(
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* Helpers for llsd_matches()
|
||||
*****************************************************************************/
|
||||
// raw data used for LLSD::Type lookup
|
||||
struct Data
|
||||
{
|
||||
LLSD::Type type;
|
||||
const char* name;
|
||||
} typedata[] =
|
||||
{
|
||||
#define def(type) { LLSD::type, #type + 4 }
|
||||
def(TypeUndefined),
|
||||
def(TypeBoolean),
|
||||
def(TypeInteger),
|
||||
def(TypeReal),
|
||||
def(TypeString),
|
||||
def(TypeUUID),
|
||||
def(TypeDate),
|
||||
def(TypeURI),
|
||||
def(TypeBinary),
|
||||
def(TypeMap),
|
||||
def(TypeArray)
|
||||
#undef def
|
||||
};
|
||||
|
||||
// LLSD::Type lookup class into which we load the above static data
|
||||
class TypeLookup
|
||||
{
|
||||
typedef std::map<LLSD::Type, std::string> MapType;
|
||||
|
||||
public:
|
||||
TypeLookup()
|
||||
{
|
||||
for (const Data *di(boost::begin(typedata)), *dend(boost::end(typedata)); di != dend; ++di)
|
||||
{
|
||||
mMap[di->type] = di->name;
|
||||
}
|
||||
}
|
||||
|
||||
std::string lookup(LLSD::Type type) const
|
||||
{
|
||||
MapType::const_iterator found = mMap.find(type);
|
||||
if (found != mMap.end())
|
||||
{
|
||||
return found->second;
|
||||
}
|
||||
return STRINGIZE("<unknown LLSD type " << type << ">");
|
||||
}
|
||||
|
||||
private:
|
||||
MapType mMap;
|
||||
};
|
||||
|
||||
// static instance of the lookup class
|
||||
static const TypeLookup sTypes;
|
||||
|
||||
// describe a mismatch; phrasing may want tweaking
|
||||
const std::string op(" required instead of ");
|
||||
|
||||
// llsd_matches() wants to identify specifically where in a complex prototype
|
||||
// structure the mismatch occurred. This entails passing a prefix string,
|
||||
// empty for the top-level call. If the prototype contains an array of maps,
|
||||
// and the mismatch occurs in the second map in a key 'foo', we want to
|
||||
// decorate the returned string with: "[1]['foo']: etc." On the other hand, we
|
||||
// want to omit the entire prefix -- including colon -- if the mismatch is at
|
||||
// top level. This helper accepts the (possibly empty) recursively-accumulated
|
||||
// prefix string, returning either empty or the original string with colon
|
||||
// appended.
|
||||
static std::string colon(const std::string& pfx)
|
||||
{
|
||||
if (pfx.empty())
|
||||
return pfx;
|
||||
return pfx + ": ";
|
||||
}
|
||||
|
||||
// param type for match_types
|
||||
typedef std::vector<LLSD::Type> TypeVector;
|
||||
|
||||
// The scalar cases in llsd_matches() use this helper. In most cases, we can
|
||||
// accept not only the exact type specified in the prototype, but also other
|
||||
// types convertible to the expected type. That implies looping over an array
|
||||
// of such types. If the actual type doesn't match any of them, we want to
|
||||
// provide a list of acceptable conversions as well as the exact type, e.g.:
|
||||
// "Integer (or Boolean, Real, String) required instead of UUID". Both the
|
||||
// implementation and the calling logic are simplified by separating out the
|
||||
// expected type from the convertible types.
|
||||
static std::string match_types(LLSD::Type expect, // prototype.type()
|
||||
const TypeVector& accept, // types convertible to that type
|
||||
LLSD::Type actual, // type we're checking
|
||||
const std::string& pfx) // as for llsd_matches
|
||||
{
|
||||
// Trivial case: if the actual type is exactly what we expect, we're good.
|
||||
if (actual == expect)
|
||||
return "";
|
||||
|
||||
// For the rest of the logic, build up a suitable error string as we go so
|
||||
// we only have to make a single pass over the list of acceptable types.
|
||||
// If we detect success along the way, we'll simply discard the partial
|
||||
// error string.
|
||||
std::ostringstream out;
|
||||
out << colon(pfx) << sTypes.lookup(expect);
|
||||
|
||||
// If there are any convertible types, append that list.
|
||||
if (! accept.empty())
|
||||
{
|
||||
out << " (";
|
||||
const char* sep = "or ";
|
||||
for (TypeVector::const_iterator ai(accept.begin()), aend(accept.end());
|
||||
ai != aend; ++ai, sep = ", ")
|
||||
{
|
||||
// Don't forget to return success if we match any of those types...
|
||||
if (actual == *ai)
|
||||
return "";
|
||||
out << sep << sTypes.lookup(*ai);
|
||||
}
|
||||
out << ')';
|
||||
}
|
||||
// If we got this far, it's because 'actual' was not one of the acceptable
|
||||
// types, so we must return an error. 'out' already contains colon(pfx)
|
||||
// and the formatted list of acceptable types, so just append the mismatch
|
||||
// phrase and the actual type.
|
||||
out << op << sTypes.lookup(actual);
|
||||
return out.str();
|
||||
}
|
||||
|
||||
// see docstring in .h file
|
||||
std::string llsd_matches(const LLSD& prototype, const LLSD& data, const std::string& pfx)
|
||||
{
|
||||
// An undefined prototype means that any data is valid.
|
||||
// An undefined slot in an array or map prototype means that any data
|
||||
// may fill that slot.
|
||||
if (prototype.isUndefined())
|
||||
return "";
|
||||
// A prototype array must match a data array with at least as many
|
||||
// entries. Moreover, every prototype entry must match the
|
||||
// corresponding data entry.
|
||||
if (prototype.isArray())
|
||||
{
|
||||
if (! data.isArray())
|
||||
{
|
||||
return STRINGIZE(colon(pfx) << "Array" << op << sTypes.lookup(data.type()));
|
||||
}
|
||||
if (data.size() < prototype.size())
|
||||
{
|
||||
return STRINGIZE(colon(pfx) << "Array size " << prototype.size() << op
|
||||
<< "Array size " << data.size());
|
||||
}
|
||||
for (LLSD::Integer i = 0; i < prototype.size(); ++i)
|
||||
{
|
||||
std::string match(llsd_matches(prototype[i], data[i], STRINGIZE('[' << i << ']')));
|
||||
if (! match.empty())
|
||||
{
|
||||
return match;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
// A prototype map must match a data map. Every key in the prototype
|
||||
// must have a corresponding key in the data map; every value in the
|
||||
// prototype must match the corresponding key's value in the data.
|
||||
if (prototype.isMap())
|
||||
{
|
||||
if (! data.isMap())
|
||||
{
|
||||
return STRINGIZE(colon(pfx) << "Map" << op << sTypes.lookup(data.type()));
|
||||
}
|
||||
// If there are a number of keys missing from the data, it would be
|
||||
// frustrating to a coder to discover them one at a time, with a big
|
||||
// build each time. Enumerate all missing keys.
|
||||
std::ostringstream out;
|
||||
out << colon(pfx);
|
||||
const char* init = "Map missing keys: ";
|
||||
const char* sep = init;
|
||||
for (LLSD::map_const_iterator mi = prototype.beginMap(); mi != prototype.endMap(); ++mi)
|
||||
{
|
||||
if (! data.has(mi->first))
|
||||
{
|
||||
out << sep << mi->first;
|
||||
sep = ", ";
|
||||
}
|
||||
}
|
||||
// So... are we missing any keys?
|
||||
if (sep != init)
|
||||
{
|
||||
return out.str();
|
||||
}
|
||||
// Good, the data block contains all the keys required by the
|
||||
// prototype. Now match the prototype entries.
|
||||
for (LLSD::map_const_iterator mi2 = prototype.beginMap(); mi2 != prototype.endMap(); ++mi2)
|
||||
{
|
||||
std::string match(llsd_matches(mi2->second, data[mi2->first],
|
||||
STRINGIZE("['" << mi2->first << "']")));
|
||||
if (! match.empty())
|
||||
{
|
||||
return match;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
// A String prototype can match String, Boolean, Integer, Real, UUID,
|
||||
// Date and URI, because any of these can be converted to String.
|
||||
if (prototype.isString())
|
||||
{
|
||||
static LLSD::Type accept[] =
|
||||
{
|
||||
LLSD::TypeBoolean,
|
||||
LLSD::TypeInteger,
|
||||
LLSD::TypeReal,
|
||||
LLSD::TypeUUID,
|
||||
LLSD::TypeDate,
|
||||
LLSD::TypeURI
|
||||
};
|
||||
return match_types(prototype.type(),
|
||||
TypeVector(boost::begin(accept), boost::end(accept)),
|
||||
data.type(),
|
||||
pfx);
|
||||
}
|
||||
// Boolean, Integer, Real match each other or String. TBD: ensure that
|
||||
// a String value is numeric.
|
||||
if (prototype.isBoolean() || prototype.isInteger() || prototype.isReal())
|
||||
{
|
||||
static LLSD::Type all[] =
|
||||
{
|
||||
LLSD::TypeBoolean,
|
||||
LLSD::TypeInteger,
|
||||
LLSD::TypeReal,
|
||||
LLSD::TypeString
|
||||
};
|
||||
// Funny business: shuffle the set of acceptable types to include all
|
||||
// but the prototype's type. Get the acceptable types in a set.
|
||||
std::set<LLSD::Type> rest(boost::begin(all), boost::end(all));
|
||||
// Remove the prototype's type because we pass that separately.
|
||||
rest.erase(prototype.type());
|
||||
return match_types(prototype.type(),
|
||||
TypeVector(rest.begin(), rest.end()),
|
||||
data.type(),
|
||||
pfx);
|
||||
}
|
||||
// UUID, Date and URI match themselves or String.
|
||||
if (prototype.isUUID() || prototype.isDate() || prototype.isURI())
|
||||
{
|
||||
static LLSD::Type accept[] =
|
||||
{
|
||||
LLSD::TypeString
|
||||
};
|
||||
return match_types(prototype.type(),
|
||||
TypeVector(boost::begin(accept), boost::end(accept)),
|
||||
data.type(),
|
||||
pfx);
|
||||
}
|
||||
// We don't yet know the conversion semantics associated with any new LLSD
|
||||
// data type that might be added, so until we've been extended to handle
|
||||
// them, assume it's strict: the new type matches only itself. (This is
|
||||
// true of Binary, which is why we don't handle that case separately.) Too
|
||||
// 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, unsigned bits)
|
||||
{
|
||||
// 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;
|
||||
|
||||
case LLSD::TypeReal:
|
||||
// This is where the 'bits' argument comes in handy. If passed
|
||||
// explicitly, it means to use is_approx_equal_fraction() to compare.
|
||||
if (bits >= 0)
|
||||
{
|
||||
return is_approx_equal_fraction(lhs.asReal(), rhs.asReal(), bits);
|
||||
}
|
||||
// Otherwise we compare bit representations, and the usual caveats
|
||||
// about comparing floating-point numbers apply. Omitting 'bits' when
|
||||
// comparing Real values is only useful when we expect identical bit
|
||||
// representation for a given Real value, e.g. for integer-valued
|
||||
// Reals.
|
||||
return (lhs.asReal() == rhs.asReal());
|
||||
|
||||
#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);
|
||||
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, bits))
|
||||
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], bits))
|
||||
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 << ", " << bits << "): "
|
||||
"unknown type " << lhs.type() << LL_ENDL;
|
||||
return false; // pacify the compiler
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
#ifndef LL_LLSDUTIL_H
|
||||
#define LL_LLSDUTIL_H
|
||||
|
||||
#include "llsd.h"
|
||||
class LLSD;
|
||||
|
||||
// U32
|
||||
LL_COMMON_API LLSD ll_sd_from_U32(const U32);
|
||||
@@ -59,6 +59,7 @@ LL_COMMON_API LLSD ll_binary_from_string(const LLSD& sd);
|
||||
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);
|
||||
|
||||
//compares the structure of an LLSD to a template LLSD and stores the
|
||||
@@ -73,6 +74,66 @@ LL_COMMON_API BOOL compare_llsd_with_template(
|
||||
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, unsigned bits=-1);
|
||||
|
||||
// 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)
|
||||
@@ -85,4 +146,283 @@ template<typename Input> LLSD llsd_copy_array(Input iter, Input end)
|
||||
return dest;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* LLSDArray
|
||||
*****************************************************************************/
|
||||
/**
|
||||
* Construct an LLSD::Array inline, with implicit conversion to LLSD. Usage:
|
||||
*
|
||||
* @code
|
||||
* void somefunc(const LLSD&);
|
||||
* ...
|
||||
* somefunc(LLSDArray("text")(17)(3.14));
|
||||
* @endcode
|
||||
*
|
||||
* For completeness, LLSDArray() with no args constructs an empty array, so
|
||||
* <tt>LLSDArray()("text")(17)(3.14)</tt> produces an array equivalent to the
|
||||
* above. But for most purposes, LLSD() is already equivalent to an empty
|
||||
* array, and if you explicitly want an empty isArray(), there's
|
||||
* LLSD::emptyArray(). However, supporting a no-args LLSDArray() constructor
|
||||
* follows the principle of least astonishment.
|
||||
*/
|
||||
class LLSDArray
|
||||
{
|
||||
public:
|
||||
LLSDArray():
|
||||
_data(LLSD::emptyArray())
|
||||
{}
|
||||
|
||||
/**
|
||||
* Need an explicit copy constructor. Consider the following:
|
||||
*
|
||||
* @code
|
||||
* LLSD array_of_arrays(LLSDArray(LLSDArray(17)(34))
|
||||
* (LLSDArray("x")("y")));
|
||||
* @endcode
|
||||
*
|
||||
* The coder intends to construct [[17, 34], ["x", "y"]].
|
||||
*
|
||||
* With the compiler's implicit copy constructor, s/he gets instead
|
||||
* [17, 34, ["x", "y"]].
|
||||
*
|
||||
* The expression LLSDArray(17)(34) constructs an LLSDArray with those two
|
||||
* values. The reader assumes it should be converted to LLSD, as we always
|
||||
* want with LLSDArray, before passing it to the @em outer LLSDArray
|
||||
* constructor! This copy constructor makes that happen.
|
||||
*/
|
||||
LLSDArray(const LLSDArray& inner):
|
||||
_data(LLSD::emptyArray())
|
||||
{
|
||||
_data.append(inner);
|
||||
}
|
||||
|
||||
LLSDArray(const LLSD& value):
|
||||
_data(LLSD::emptyArray())
|
||||
{
|
||||
_data.append(value);
|
||||
}
|
||||
|
||||
LLSDArray& operator()(const LLSD& value)
|
||||
{
|
||||
_data.append(value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator LLSD() const { return _data; }
|
||||
LLSD get() const { return _data; }
|
||||
|
||||
private:
|
||||
LLSD _data;
|
||||
};
|
||||
|
||||
/*****************************************************************************
|
||||
* 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;
|
||||
};
|
||||
|
||||
/*****************************************************************************
|
||||
* LLSDParam
|
||||
*****************************************************************************/
|
||||
/**
|
||||
* 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:
|
||||
/**
|
||||
* 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;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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: \
|
||||
LLSDParam(const LLSD& value): \
|
||||
_value(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*>
|
||||
{
|
||||
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
|
||||
{
|
||||
|
||||
/*****************************************************************************
|
||||
* BOOST_FOREACH() helpers for LLSD
|
||||
*****************************************************************************/
|
||||
/// Usage: BOOST_FOREACH(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: BOOST_FOREACH([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
|
||||
|
||||
#endif // LL_LLSDUTIL_H
|
||||
|
||||
@@ -100,12 +100,6 @@ private:
|
||||
DELETED
|
||||
} EInitState;
|
||||
|
||||
static void deleteSingleton()
|
||||
{
|
||||
delete getData().mSingletonInstance;
|
||||
getData().mSingletonInstance = NULL;
|
||||
}
|
||||
|
||||
// stores pointer to singleton instance
|
||||
// and tracks initialization state of singleton
|
||||
struct SingletonInstanceData
|
||||
@@ -120,7 +114,11 @@ private:
|
||||
|
||||
~SingletonInstanceData()
|
||||
{
|
||||
deleteSingleton();
|
||||
SingletonInstanceData& data = getData();
|
||||
if (data.mInitState != DELETED)
|
||||
{
|
||||
deleteSingleton();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -132,6 +130,14 @@ public:
|
||||
data.mInitState = DELETED;
|
||||
}
|
||||
|
||||
// Can be used to control when the singleton is deleted. Not normally needed.
|
||||
static void deleteSingleton()
|
||||
{
|
||||
delete getData().mSingletonInstance;
|
||||
getData().mSingletonInstance = NULL;
|
||||
getData().mInitState = DELETED;
|
||||
}
|
||||
|
||||
static SingletonInstanceData& getData()
|
||||
{
|
||||
// this is static to cache the lookup results
|
||||
|
||||
@@ -43,9 +43,10 @@
|
||||
|
||||
|
||||
// statics
|
||||
BOOL LLPerfBlock::sStatsEnabled = FALSE; // Flag for detailed information
|
||||
S32 LLPerfBlock::sStatsFlags = LLPerfBlock::LLSTATS_NO_OPTIONAL_STATS; // Control what is being recorded
|
||||
LLPerfBlock::stat_map_t LLPerfBlock::sStatMap; // Map full path string to LLStatTime objects, tracks all active objects
|
||||
std::string LLPerfBlock::sCurrentStatPath = ""; // Something like "/total_time/physics/physics step"
|
||||
LLStat::stat_map_t LLStat::sStatList;
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// Live config file to trigger stats logging
|
||||
@@ -129,6 +130,7 @@ bool LLStatsConfigFile::loadFile()
|
||||
|
||||
F32 duration = 0.f;
|
||||
F32 interval = 0.f;
|
||||
S32 flags = LLPerfBlock::LLSTATS_BASIC_STATS;
|
||||
|
||||
const char * w = "duration";
|
||||
if (stats_config.has(w))
|
||||
@@ -140,8 +142,18 @@ bool LLStatsConfigFile::loadFile()
|
||||
{
|
||||
interval = (F32)stats_config[w].asReal();
|
||||
}
|
||||
w = "flags";
|
||||
if (stats_config.has(w))
|
||||
{
|
||||
flags = (S32)stats_config[w].asInteger();
|
||||
if (flags == LLPerfBlock::LLSTATS_NO_OPTIONAL_STATS &&
|
||||
duration > 0)
|
||||
{ // No flags passed in, but have a duration, so reset to basic stats
|
||||
flags = LLPerfBlock::LLSTATS_BASIC_STATS;
|
||||
}
|
||||
}
|
||||
|
||||
mStatsp->setReportPerformanceDuration( duration );
|
||||
mStatsp->setReportPerformanceDuration( duration, flags );
|
||||
mStatsp->setReportPerformanceInterval( interval );
|
||||
|
||||
if ( duration > 0 )
|
||||
@@ -253,13 +265,14 @@ void LLPerfStats::dumpIntervalPerformanceStats()
|
||||
}
|
||||
}
|
||||
|
||||
// Set length of performance stat recording
|
||||
void LLPerfStats::setReportPerformanceDuration( F32 seconds )
|
||||
// Set length of performance stat recording.
|
||||
// If turning stats on, caller must provide flags
|
||||
void LLPerfStats::setReportPerformanceDuration( F32 seconds, S32 flags /* = LLSTATS_NO_OPTIONAL_STATS */ )
|
||||
{
|
||||
if ( seconds <= 0.f )
|
||||
{
|
||||
mReportPerformanceStatEnd = 0.0;
|
||||
LLPerfBlock::setStatsEnabled( FALSE );
|
||||
LLPerfBlock::setStatsFlags(LLPerfBlock::LLSTATS_NO_OPTIONAL_STATS); // Make sure all recording is off
|
||||
mFrameStatsFile.close();
|
||||
LLPerfBlock::clearDynamicStats();
|
||||
}
|
||||
@@ -268,8 +281,8 @@ void LLPerfStats::setReportPerformanceDuration( F32 seconds )
|
||||
mReportPerformanceStatEnd = LLFrameTimer::getElapsedSeconds() + ((F64) seconds);
|
||||
// Clear failure flag to try and create the log file once
|
||||
mFrameStatsFileFailure = FALSE;
|
||||
LLPerfBlock::setStatsEnabled( TRUE );
|
||||
mSkipFirstFrameStats = TRUE; // Skip the first report (at the end of this frame)
|
||||
LLPerfBlock::setStatsFlags(flags);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -611,11 +624,26 @@ LLPerfBlock::LLPerfBlock(LLStatTime* stat ) : mPredefinedStat(stat), mDynamicSta
|
||||
}
|
||||
}
|
||||
|
||||
// Use this constructor for dynamically created LLStatTime objects (not pre-defined) with a multi-part key.
|
||||
// These are also turned on or off via the switch passed in
|
||||
LLPerfBlock::LLPerfBlock( const char* key1, const char* key2 ) : mPredefinedStat(NULL), mDynamicStat(NULL)
|
||||
// Use this constructor for normal, optional LLPerfBlock time slices
|
||||
LLPerfBlock::LLPerfBlock( const char* key ) : mPredefinedStat(NULL), mDynamicStat(NULL)
|
||||
{
|
||||
if (!sStatsEnabled) return;
|
||||
if ((sStatsFlags & LLSTATS_BASIC_STATS) == 0)
|
||||
{ // These are off unless the base set is enabled
|
||||
return;
|
||||
}
|
||||
|
||||
initDynamicStat(key);
|
||||
}
|
||||
|
||||
|
||||
// Use this constructor for dynamically created LLPerfBlock time slices
|
||||
// that are only enabled by specific control flags
|
||||
LLPerfBlock::LLPerfBlock( const char* key1, const char* key2, S32 flags ) : mPredefinedStat(NULL), mDynamicStat(NULL)
|
||||
{
|
||||
if ((sStatsFlags & flags) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (NULL == key2 || strlen(key2) == 0)
|
||||
{
|
||||
@@ -629,10 +657,12 @@ LLPerfBlock::LLPerfBlock( const char* key1, const char* key2 ) : mPredefinedStat
|
||||
}
|
||||
}
|
||||
|
||||
// Set up the result data map if dynamic stats are enabled
|
||||
void LLPerfBlock::initDynamicStat(const std::string& key)
|
||||
{
|
||||
// Early exit if dynamic stats aren't enabled.
|
||||
if (!sStatsEnabled) return;
|
||||
if (sStatsFlags == LLSTATS_NO_OPTIONAL_STATS)
|
||||
return;
|
||||
|
||||
mLastPath = sCurrentStatPath; // Save and restore current path
|
||||
sCurrentStatPath += "/" + key; // Add key to current path
|
||||
@@ -713,7 +743,7 @@ void LLPerfBlock::addStatsToLLSDandReset( LLSD & stats,
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // WTF? Shouldn't have a NULL pointer in the map.
|
||||
{ // Shouldn't have a NULL pointer in the map.
|
||||
llwarns << "Unexpected NULL dynamic stat at '" << stats_full_path << "'" << llendl;
|
||||
}
|
||||
}
|
||||
@@ -725,14 +755,12 @@ void LLPerfBlock::addStatsToLLSDandReset( LLSD & stats,
|
||||
LLTimer LLStat::sTimer;
|
||||
LLFrameTimer LLStat::sFrameTimer;
|
||||
|
||||
LLStat::LLStat(const U32 num_bins, const BOOL use_frame_timer)
|
||||
void LLStat::init()
|
||||
{
|
||||
llassert(num_bins > 0);
|
||||
mUseFrameTimer = use_frame_timer;
|
||||
llassert(mNumBins > 0);
|
||||
mNumValues = 0;
|
||||
mLastValue = 0.f;
|
||||
mLastTime = 0.f;
|
||||
mNumBins = num_bins;
|
||||
mCurBin = (mNumBins-1);
|
||||
mNextBin = 0;
|
||||
mBins = new F32[mNumBins];
|
||||
@@ -746,6 +774,29 @@ LLStat::LLStat(const U32 num_bins, const BOOL use_frame_timer)
|
||||
mTime[i] = 0.0;
|
||||
mDT[i] = 0.f;
|
||||
}
|
||||
|
||||
if (!mName.empty())
|
||||
{
|
||||
stat_map_t::iterator iter = sStatList.find(mName);
|
||||
if (iter != sStatList.end())
|
||||
llwarns << "LLStat with duplicate name: " << mName << llendl;
|
||||
sStatList.insert(std::make_pair(mName, this));
|
||||
}
|
||||
}
|
||||
|
||||
LLStat::LLStat(const U32 num_bins, const BOOL use_frame_timer)
|
||||
: mUseFrameTimer(use_frame_timer),
|
||||
mNumBins(num_bins)
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
LLStat::LLStat(std::string name, U32 num_bins, BOOL use_frame_timer)
|
||||
: mUseFrameTimer(use_frame_timer),
|
||||
mNumBins(num_bins),
|
||||
mName(name)
|
||||
{
|
||||
init();
|
||||
}
|
||||
|
||||
LLStat::~LLStat()
|
||||
@@ -754,6 +805,15 @@ LLStat::~LLStat()
|
||||
delete[] mBeginTime;
|
||||
delete[] mTime;
|
||||
delete[] mDT;
|
||||
|
||||
if (!mName.empty())
|
||||
{
|
||||
// handle multiple entries with the same name
|
||||
stat_map_t::iterator iter = sStatList.find(mName);
|
||||
while (iter != sStatList.end() && iter->second != this)
|
||||
++iter;
|
||||
sStatList.erase(iter);
|
||||
}
|
||||
}
|
||||
|
||||
void LLStat::reset()
|
||||
|
||||
@@ -192,14 +192,23 @@ public:
|
||||
// Use this constructor for pre-defined LLStatTime objects
|
||||
LLPerfBlock(LLStatTime* stat);
|
||||
|
||||
// Use this constructor for dynamically created LLStatTime objects (not pre-defined) with a multi-part key
|
||||
LLPerfBlock( const char* key1, const char* key2 = NULL);
|
||||
// Use this constructor for normal, optional LLPerfBlock time slices
|
||||
LLPerfBlock( const char* key );
|
||||
|
||||
// Use this constructor for dynamically created LLPerfBlock time slices
|
||||
// that are only enabled by specific control flags
|
||||
LLPerfBlock( const char* key1, const char* key2, S32 flags = LLSTATS_BASIC_STATS );
|
||||
|
||||
~LLPerfBlock();
|
||||
|
||||
static void setStatsEnabled( BOOL enable ) { sStatsEnabled = enable; };
|
||||
static S32 getStatsEnabled() { return sStatsEnabled; };
|
||||
enum
|
||||
{ // Stats bitfield flags
|
||||
LLSTATS_NO_OPTIONAL_STATS = 0x00, // No optional stats gathering, just pre-defined LLStatTime objects
|
||||
LLSTATS_BASIC_STATS = 0x01, // Gather basic optional runtime stats
|
||||
LLSTATS_SCRIPT_FUNCTIONS = 0x02, // Include LSL function calls
|
||||
};
|
||||
static void setStatsFlags( S32 flags ) { sStatsFlags = flags; };
|
||||
static S32 getStatsFlags() { return sStatsFlags; };
|
||||
|
||||
static void clearDynamicStats(); // Reset maps to clear out dynamic objects
|
||||
static void addStatsToLLSDandReset( LLSD & stats, // Get current information and clear time bin
|
||||
@@ -213,7 +222,7 @@ private:
|
||||
LLStatTime * mPredefinedStat; // LLStatTime object to get data
|
||||
StatEntry * mDynamicStat; // StatEntryobject to get data
|
||||
|
||||
static BOOL sStatsEnabled; // Normally FALSE
|
||||
static S32 sStatsFlags; // Control what is being recorded
|
||||
static stat_map_t sStatMap; // Map full path string to LLStatTime objects
|
||||
static std::string sCurrentStatPath; // Something like "frame/physics/physics step"
|
||||
};
|
||||
@@ -236,7 +245,7 @@ public:
|
||||
BOOL frameStatsIsRunning() { return (mReportPerformanceStatEnd > 0.); };
|
||||
F32 getReportPerformanceInterval() const { return mReportPerformanceStatInterval; };
|
||||
void setReportPerformanceInterval( F32 interval ) { mReportPerformanceStatInterval = interval; };
|
||||
void setReportPerformanceDuration( F32 seconds );
|
||||
void setReportPerformanceDuration( F32 seconds, S32 flags = LLPerfBlock::LLSTATS_NO_OPTIONAL_STATS );
|
||||
void setProcessName(const std::string& process_name) { mProcessName = process_name; }
|
||||
void setProcessPID(S32 process_pid) { mProcessPID = process_pid; }
|
||||
|
||||
@@ -258,8 +267,15 @@ private:
|
||||
// ----------------------------------------------------------------------------
|
||||
class LL_COMMON_API LLStat
|
||||
{
|
||||
private:
|
||||
typedef std::multimap<std::string, LLStat*> stat_map_t;
|
||||
static stat_map_t sStatList;
|
||||
|
||||
void init();
|
||||
|
||||
public:
|
||||
LLStat(const U32 num_bins = 32, BOOL use_frame_timer = FALSE);
|
||||
LLStat(U32 num_bins = 32, BOOL use_frame_timer = FALSE);
|
||||
LLStat(std::string name, U32 num_bins = 32, BOOL use_frame_timer = FALSE);
|
||||
~LLStat();
|
||||
|
||||
void reset();
|
||||
@@ -322,8 +338,22 @@ private:
|
||||
F32 *mDT;
|
||||
S32 mCurBin;
|
||||
S32 mNextBin;
|
||||
|
||||
std::string mName;
|
||||
|
||||
static LLTimer sTimer;
|
||||
static LLFrameTimer sFrameTimer;
|
||||
|
||||
public:
|
||||
static LLStat* getStat(const std::string& name)
|
||||
{
|
||||
// return the first stat that matches 'name'
|
||||
stat_map_t::iterator iter = sStatList.find(name);
|
||||
if (iter != sStatList.end())
|
||||
return iter->second;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif // LL_STAT_
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
// skips spaces and tabs
|
||||
bool skip_whitespace(std::istream& input_stream)
|
||||
{
|
||||
char c = input_stream.peek();
|
||||
int c = input_stream.peek();
|
||||
while (('\t' == c || ' ' == c) && input_stream.good())
|
||||
{
|
||||
input_stream.get();
|
||||
@@ -57,7 +57,7 @@ bool skip_whitespace(std::istream& input_stream)
|
||||
// skips whitespace, newlines, and carriage returns
|
||||
bool skip_emptyspace(std::istream& input_stream)
|
||||
{
|
||||
char c = input_stream.peek();
|
||||
int c = input_stream.peek();
|
||||
while ( input_stream.good()
|
||||
&& ('\t' == c || ' ' == c || '\n' == c || '\r' == c) )
|
||||
{
|
||||
@@ -72,7 +72,7 @@ bool skip_comments_and_emptyspace(std::istream& input_stream)
|
||||
{
|
||||
while (skip_emptyspace(input_stream))
|
||||
{
|
||||
char c = input_stream.peek();
|
||||
int c = input_stream.peek();
|
||||
if ('#' == c )
|
||||
{
|
||||
while ('\n' != c && input_stream.good())
|
||||
@@ -90,7 +90,7 @@ bool skip_comments_and_emptyspace(std::istream& input_stream)
|
||||
|
||||
bool skip_line(std::istream& input_stream)
|
||||
{
|
||||
char c;
|
||||
int c;
|
||||
do
|
||||
{
|
||||
c = input_stream.get();
|
||||
@@ -100,7 +100,7 @@ bool skip_line(std::istream& input_stream)
|
||||
|
||||
bool skip_to_next_word(std::istream& input_stream)
|
||||
{
|
||||
char c = input_stream.peek();
|
||||
int c = input_stream.peek();
|
||||
while ( input_stream.good()
|
||||
&& ( (c >= 'a' && c <= 'z')
|
||||
|| (c >= 'A' && c <= 'Z')
|
||||
@@ -132,7 +132,7 @@ bool skip_to_end_of_next_keyword(const char* keyword, std::istream& input_stream
|
||||
while (input_stream.good())
|
||||
{
|
||||
skip_emptyspace(input_stream);
|
||||
char c = input_stream.get();
|
||||
int c = input_stream.get();
|
||||
if (keyword[0] != c)
|
||||
{
|
||||
skip_line(input_stream);
|
||||
@@ -181,7 +181,7 @@ bool skip_to_start_of_next_keyword(const char* keyword, std::istream& input_stre
|
||||
while (input_stream.good())
|
||||
{
|
||||
skip_emptyspace(input_stream);
|
||||
char c = input_stream.get();
|
||||
int c = input_stream.get();
|
||||
if (keyword[0] != c)
|
||||
{
|
||||
skip_line(input_stream);
|
||||
@@ -229,7 +229,7 @@ bool skip_to_start_of_next_keyword(const char* keyword, std::istream& input_stre
|
||||
bool get_word(std::string& output_string, std::istream& input_stream)
|
||||
{
|
||||
skip_emptyspace(input_stream);
|
||||
char c = input_stream.peek();
|
||||
int c = input_stream.peek();
|
||||
while ( !isspace(c)
|
||||
&& '\n' != c
|
||||
&& '\r' != c
|
||||
@@ -246,7 +246,7 @@ bool get_word(std::string& output_string, std::istream& input_stream, int n)
|
||||
{
|
||||
skip_emptyspace(input_stream);
|
||||
int char_count = 0;
|
||||
char c = input_stream.peek();
|
||||
int c = input_stream.peek();
|
||||
while (!isspace(c)
|
||||
&& '\n' != c
|
||||
&& '\r' != c
|
||||
@@ -265,7 +265,7 @@ bool get_word(std::string& output_string, std::istream& input_stream, int n)
|
||||
bool get_line(std::string& output_string, std::istream& input_stream)
|
||||
{
|
||||
output_string.clear();
|
||||
char c = input_stream.get();
|
||||
int c = input_stream.get();
|
||||
while (input_stream.good())
|
||||
{
|
||||
output_string += c;
|
||||
@@ -285,7 +285,7 @@ bool get_line(std::string& output_string, std::istream& input_stream, int n)
|
||||
{
|
||||
output_string.clear();
|
||||
int char_count = 0;
|
||||
char c = input_stream.get();
|
||||
int c = input_stream.get();
|
||||
while (input_stream.good() && char_count < n)
|
||||
{
|
||||
char_count++;
|
||||
|
||||
@@ -36,6 +36,8 @@
|
||||
|
||||
#include "u64.h"
|
||||
|
||||
#include "lldate.h"
|
||||
|
||||
#if LL_WINDOWS
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# include <winsock2.h>
|
||||
|
||||
@@ -39,8 +39,6 @@
|
||||
#include <limits.h>
|
||||
|
||||
#include "stdtypes.h"
|
||||
#include "llpreprocessor.h"
|
||||
#include "lldate.h"
|
||||
|
||||
#include <string>
|
||||
#include <list>
|
||||
@@ -55,8 +53,6 @@ const U32 USEC_PER_HOUR = USEC_PER_MIN * MIN_PER_HOUR;
|
||||
const U32 SEC_PER_HOUR = SEC_PER_MIN * MIN_PER_HOUR;
|
||||
const F64 SEC_PER_USEC = 1.0 / (F64) USEC_PER_SEC;
|
||||
|
||||
LL_COMMON_API U64 totalTime(); // Returns current system time in microseconds
|
||||
|
||||
class LL_COMMON_API LLTimer
|
||||
{
|
||||
public:
|
||||
@@ -175,4 +171,5 @@ LL_COMMON_API void secondsToTimecodeString(F32 current_time, std::string& tcstri
|
||||
LL_COMMON_API void timeToFormattedString(time_t time, std::string format, std::string ×tr);
|
||||
LL_COMMON_API void timeStructToFormattedString(struct tm * time, std::string format, std::string ×tr);
|
||||
|
||||
U64 LL_COMMON_API totalTime(); // Returns current system time in microseconds
|
||||
#endif
|
||||
|
||||
705
indra/llcommon/lltreeiterators.h
Normal file
705
indra/llcommon/lltreeiterators.h
Normal file
@@ -0,0 +1,705 @@
|
||||
/**
|
||||
* @file lltreeiterators.h
|
||||
* @author Nat Goodspeed
|
||||
* @date 2008-08-19
|
||||
* @brief This file defines iterators useful for traversing arbitrary node
|
||||
* classes, potentially polymorphic, linked into strict tree
|
||||
* structures.
|
||||
*
|
||||
* Dereferencing any one of these iterators actually yields a @em
|
||||
* pointer to the node in question. For example, given an
|
||||
* LLLinkedIter<MyNode> <tt>li</tt>, <tt>*li</tt> gets you a pointer
|
||||
* to MyNode, and <tt>**li</tt> gets you the MyNode instance itself.
|
||||
* More commonly, instead of writing <tt>li->member</tt>, you write
|
||||
* <tt>(*li)->member</tt> -- as you would if you were traversing an
|
||||
* STL container of MyNode pointers.
|
||||
*
|
||||
* It would certainly be possible to build these iterators so that
|
||||
* <tt>*iterator</tt> would return a reference to the node itself
|
||||
* rather than a pointer to the node, and for many purposes it would
|
||||
* even be more convenient. However, that would be insufficiently
|
||||
* flexible. If you want to use an iterator range to (e.g.) initialize
|
||||
* a std::vector collecting results -- you rarely want to actually @em
|
||||
* copy the nodes in question. You're much more likely to want to copy
|
||||
* <i>pointers to</i> the traversed nodes. Hence these iterators
|
||||
* produce pointers.
|
||||
*
|
||||
* Though you specify the actual NODE class as the template parameter,
|
||||
* these iterators internally use LLPtrTo<> to discover whether to
|
||||
* store and return an LLPointer<NODE> or a simple NODE*.
|
||||
*
|
||||
* By strict tree structures, we mean that each child must have
|
||||
* exactly one parent. This forbids a child claiming any ancestor as a
|
||||
* child of its own. Child nodes with multiple parents will be visited
|
||||
* once for each parent. Cycles in the graph will result in either an
|
||||
* infinite loop or an out-of-memory crash. You Have Been Warned.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2008&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$
|
||||
*/
|
||||
|
||||
#if ! defined(LL_LLTREEITERATORS_H)
|
||||
#define LL_LLTREEITERATORS_H
|
||||
|
||||
#include "llptrto.h"
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <boost/iterator/iterator_facade.hpp>
|
||||
#include <boost/function.hpp>
|
||||
#include <boost/static_assert.hpp>
|
||||
|
||||
namespace LLTreeIter
|
||||
{
|
||||
/// Discriminator between LLTreeUpIter and LLTreeDownIter
|
||||
enum RootIter { UP, DOWN };
|
||||
/// Discriminator between LLTreeDFSIter, LLTreeDFSPostIter and LLTreeBFSIter
|
||||
enum WalkIter { DFS_PRE, DFS_POST, BFS };
|
||||
}
|
||||
|
||||
/**
|
||||
* LLBaseIter defines some machinery common to all these iterators. We use
|
||||
* boost::iterator_facade to define the iterator boilerplate: the conventional
|
||||
* operators and methods necessary to implement a standards-conforming
|
||||
* iterator. That allows us to specify the actual iterator semantics in terms
|
||||
* of equal(), dereference() and increment() methods.
|
||||
*/
|
||||
template <class SELFTYPE, class NODE>
|
||||
class LLBaseIter: public boost::iterator_facade<SELFTYPE,
|
||||
// use pointer type as the
|
||||
// reference type
|
||||
typename LLPtrTo<NODE>::type,
|
||||
boost::forward_traversal_tag>
|
||||
{
|
||||
protected:
|
||||
/// LLPtrTo<NODE>::type is either NODE* or LLPointer<NODE>, as appropriate
|
||||
typedef typename LLPtrTo<NODE>::type ptr_type;
|
||||
/// function that advances from this node to next accepts a node pointer
|
||||
/// and returns another
|
||||
typedef boost::function<ptr_type(const ptr_type&)> func_type;
|
||||
typedef SELFTYPE self_type;
|
||||
};
|
||||
|
||||
/// Functor returning NULL, suitable for an end-iterator's 'next' functor
|
||||
template <class NODE>
|
||||
typename LLPtrTo<NODE>::type LLNullNextFunctor(const typename LLPtrTo<NODE>::type&)
|
||||
{
|
||||
return typename LLPtrTo<NODE>::type();
|
||||
}
|
||||
|
||||
/**
|
||||
* LLLinkedIter is an iterator over an intrusive singly-linked list. The
|
||||
* beginning of the list is represented by LLLinkedIter(list head); the end is
|
||||
* represented by LLLinkedIter().
|
||||
*
|
||||
* The begin LLLinkedIter must be instantiated with a functor to extract the
|
||||
* 'next' pointer from the current node. Supposing that the link pointer is @c
|
||||
* public, something like:
|
||||
*
|
||||
* @code
|
||||
* NODE* mNext;
|
||||
* @endcode
|
||||
*
|
||||
* you can use (e.g.) <tt>boost::bind(&NODE::mNext, _1)</tt> for the purpose.
|
||||
* Alternatively, you can bind whatever accessor method is normally used to
|
||||
* advance to the next node, e.g. for:
|
||||
*
|
||||
* @code
|
||||
* NODE* next() const;
|
||||
* @endcode
|
||||
*
|
||||
* you can use <tt>boost::bind(&NODE::next, _1)</tt>.
|
||||
*/
|
||||
template <class NODE>
|
||||
class LLLinkedIter: public LLBaseIter<LLLinkedIter<NODE>, NODE>
|
||||
{
|
||||
typedef LLBaseIter<LLLinkedIter<NODE>, NODE> super;
|
||||
protected:
|
||||
/// some methods need to return a reference to self
|
||||
typedef typename super::self_type self_type;
|
||||
typedef typename super::ptr_type ptr_type;
|
||||
typedef typename super::func_type func_type;
|
||||
public:
|
||||
/// Instantiate an LLLinkedIter to start a range, or to end a range before
|
||||
/// a particular list entry. Pass a functor to extract the 'next' pointer
|
||||
/// from the current node.
|
||||
LLLinkedIter(const ptr_type& entry, const func_type& nextfunc):
|
||||
mCurrent(entry),
|
||||
mNextFunc(nextfunc)
|
||||
{}
|
||||
/// Instantiate an LLLinkedIter to end a range at the end of the list
|
||||
LLLinkedIter():
|
||||
mCurrent(),
|
||||
mNextFunc(LLNullNextFunctor<NODE>)
|
||||
{}
|
||||
|
||||
private:
|
||||
/// leverage boost::iterator_facade
|
||||
friend class boost::iterator_core_access;
|
||||
|
||||
/// advance
|
||||
void increment()
|
||||
{
|
||||
mCurrent = mNextFunc(mCurrent);
|
||||
}
|
||||
/// equality
|
||||
bool equal(const self_type& that) const { return this->mCurrent == that.mCurrent; }
|
||||
/// dereference
|
||||
ptr_type& dereference() const { return const_cast<ptr_type&>(mCurrent); }
|
||||
|
||||
ptr_type mCurrent;
|
||||
func_type mNextFunc;
|
||||
};
|
||||
|
||||
/**
|
||||
* LLTreeUpIter walks from the node in hand to the root of the tree. The term
|
||||
* "up" is applied to a tree visualized with the root at the top.
|
||||
*
|
||||
* LLTreeUpIter is an alias for LLLinkedIter, since any linked tree that you
|
||||
* can navigate that way at all contains parent pointers.
|
||||
*/
|
||||
template <class NODE>
|
||||
class LLTreeUpIter: public LLLinkedIter<NODE>
|
||||
{
|
||||
typedef LLLinkedIter<NODE> super;
|
||||
public:
|
||||
/// Instantiate an LLTreeUpIter to start from a particular tree node, or
|
||||
/// to end a parent traversal before reaching a particular ancestor. Pass
|
||||
/// a functor to extract the 'parent' pointer from the current node.
|
||||
LLTreeUpIter(const typename super::ptr_type& node,
|
||||
const typename super::func_type& parentfunc):
|
||||
super(node, parentfunc)
|
||||
{}
|
||||
/// Instantiate an LLTreeUpIter to end a range at the root of the tree
|
||||
LLTreeUpIter():
|
||||
super()
|
||||
{}
|
||||
};
|
||||
|
||||
/**
|
||||
* LLTreeDownIter walks from the root of the tree to the node in hand. The
|
||||
* term "down" is applied to a tree visualized with the root at the top.
|
||||
*
|
||||
* Though you instantiate the begin() LLTreeDownIter with a pointer to some
|
||||
* node at an arbitrary location in the tree, the root will be the first node
|
||||
* you dereference and the passed node will be the last node you dereference.
|
||||
*
|
||||
* On construction, LLTreeDownIter walks from the current node to the root,
|
||||
* capturing the path. Then in use, it replays that walk in reverse. As with
|
||||
* all traversals of interesting data structures, it is actively dangerous to
|
||||
* modify the tree during an LLTreeDownIter walk.
|
||||
*/
|
||||
template <class NODE>
|
||||
class LLTreeDownIter: public LLBaseIter<LLTreeDownIter<NODE>, NODE>
|
||||
{
|
||||
typedef LLBaseIter<LLTreeDownIter<NODE>, NODE> super;
|
||||
typedef typename super::self_type self_type;
|
||||
protected:
|
||||
typedef typename super::ptr_type ptr_type;
|
||||
typedef typename super::func_type func_type;
|
||||
private:
|
||||
typedef std::vector<ptr_type> list_type;
|
||||
public:
|
||||
/// Instantiate an LLTreeDownIter to end at a particular tree node. Pass a
|
||||
/// functor to extract the 'parent' pointer from the current node.
|
||||
LLTreeDownIter(const ptr_type& node,
|
||||
const func_type& parentfunc)
|
||||
{
|
||||
for (ptr_type n = node; n; n = parentfunc(n))
|
||||
mParents.push_back(n);
|
||||
}
|
||||
/// Instantiate an LLTreeDownIter representing "here", the end of the loop
|
||||
LLTreeDownIter() {}
|
||||
|
||||
private:
|
||||
/// leverage boost::iterator_facade
|
||||
friend class boost::iterator_core_access;
|
||||
|
||||
/// advance
|
||||
void increment()
|
||||
{
|
||||
mParents.pop_back();
|
||||
}
|
||||
/// equality
|
||||
bool equal(const self_type& that) const { return this->mParents == that.mParents; }
|
||||
/// implement dereference/indirection operators
|
||||
ptr_type& dereference() const { return const_cast<ptr_type&>(mParents.back()); }
|
||||
|
||||
list_type mParents;
|
||||
};
|
||||
|
||||
/**
|
||||
* When you want to select between LLTreeUpIter and LLTreeDownIter with a
|
||||
* compile-time discriminator, use LLTreeRootIter with an LLTreeIter::RootIter
|
||||
* template arg.
|
||||
*/
|
||||
template <LLTreeIter::RootIter DISCRIM, class NODE>
|
||||
class LLTreeRootIter
|
||||
{
|
||||
enum { use_a_valid_LLTreeIter_RootIter_value = false };
|
||||
public:
|
||||
/// Bogus constructors for default (unrecognized discriminator) case
|
||||
template <typename TYPE1, typename TYPE2>
|
||||
LLTreeRootIter(TYPE1, TYPE2)
|
||||
{
|
||||
BOOST_STATIC_ASSERT(use_a_valid_LLTreeIter_RootIter_value);
|
||||
}
|
||||
LLTreeRootIter()
|
||||
{
|
||||
BOOST_STATIC_ASSERT(use_a_valid_LLTreeIter_RootIter_value);
|
||||
}
|
||||
};
|
||||
|
||||
/// Specialize for LLTreeIter::UP
|
||||
template <class NODE>
|
||||
class LLTreeRootIter<LLTreeIter::UP, NODE>: public LLTreeUpIter<NODE>
|
||||
{
|
||||
typedef LLTreeUpIter<NODE> super;
|
||||
public:
|
||||
/// forward begin ctor
|
||||
LLTreeRootIter(const typename super::ptr_type& node,
|
||||
const typename super::func_type& parentfunc):
|
||||
super(node, parentfunc)
|
||||
{}
|
||||
/// forward end ctor
|
||||
LLTreeRootIter():
|
||||
super()
|
||||
{}
|
||||
};
|
||||
|
||||
/// Specialize for LLTreeIter::DOWN
|
||||
template <class NODE>
|
||||
class LLTreeRootIter<LLTreeIter::DOWN, NODE>: public LLTreeDownIter<NODE>
|
||||
{
|
||||
typedef LLTreeDownIter<NODE> super;
|
||||
public:
|
||||
/// forward begin ctor
|
||||
LLTreeRootIter(const typename super::ptr_type& node,
|
||||
const typename super::func_type& parentfunc):
|
||||
super(node, parentfunc)
|
||||
{}
|
||||
/// forward end ctor
|
||||
LLTreeRootIter():
|
||||
super()
|
||||
{}
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiated with a tree node, typically the root, LLTreeDFSIter "flattens"
|
||||
* a depth-first tree walk through that node and all its descendants.
|
||||
*
|
||||
* The begin() LLTreeDFSIter must be instantiated with functors to obtain from
|
||||
* a given node begin() and end() iterators for that node's children. For this
|
||||
* reason, you must specify the type of the node's child iterator as an
|
||||
* additional template parameter.
|
||||
*
|
||||
* Specifically, the begin functor must return an iterator whose dereferenced
|
||||
* value is a @em pointer to a child tree node. For instance, if each node
|
||||
* tracks its children in an STL container of node* pointers, you can simply
|
||||
* return that container's begin() iterator.
|
||||
*
|
||||
* Alternatively, if a node tracks its children with a classic linked list,
|
||||
* write a functor returning LLLinkedIter<NODE>.
|
||||
*
|
||||
* The end() LLTreeDFSIter must, of course, match the begin() iterator's
|
||||
* template parameters, but is constructed without runtime parameters.
|
||||
*/
|
||||
template <class NODE, typename CHILDITER>
|
||||
class LLTreeDFSIter: public LLBaseIter<LLTreeDFSIter<NODE, CHILDITER>, NODE>
|
||||
{
|
||||
typedef LLBaseIter<LLTreeDFSIter<NODE, CHILDITER>, NODE> super;
|
||||
typedef typename super::self_type self_type;
|
||||
protected:
|
||||
typedef typename super::ptr_type ptr_type;
|
||||
// The func_type is different for this: from a NODE pointer, we must
|
||||
// obtain a CHILDITER.
|
||||
typedef boost::function<CHILDITER(const ptr_type&)> func_type;
|
||||
private:
|
||||
typedef std::vector<ptr_type> list_type;
|
||||
public:
|
||||
/// Instantiate an LLTreeDFSIter to start a depth-first walk. Pass
|
||||
/// functors to extract the 'child begin' and 'child end' iterators from
|
||||
/// each node.
|
||||
LLTreeDFSIter(const ptr_type& node, const func_type& beginfunc, const func_type& endfunc)
|
||||
: mBeginFunc(beginfunc),
|
||||
mEndFunc(endfunc),
|
||||
mSkipChildren(false)
|
||||
{
|
||||
// Only push back this node if it's non-NULL!
|
||||
if (node)
|
||||
mPending.push_back(node);
|
||||
}
|
||||
/// Instantiate an LLTreeDFSIter to mark the end of the walk
|
||||
LLTreeDFSIter() : mSkipChildren(false) {}
|
||||
|
||||
/// flags iterator logic to skip traversing children of current node on next increment
|
||||
void skipDescendants(bool skip = true) { mSkipChildren = skip; }
|
||||
|
||||
private:
|
||||
/// leverage boost::iterator_facade
|
||||
friend class boost::iterator_core_access;
|
||||
|
||||
/// advance
|
||||
/// This implementation is due to http://en.wikipedia.org/wiki/Depth-first_search
|
||||
void increment()
|
||||
{
|
||||
// Capture the node we were just looking at
|
||||
ptr_type current = mPending.back();
|
||||
// Remove it from mPending so we don't process it again later
|
||||
mPending.pop_back();
|
||||
if (!mSkipChildren)
|
||||
{
|
||||
// Add all its children to mPending
|
||||
addChildren(current);
|
||||
}
|
||||
// reset flag after each step
|
||||
mSkipChildren = false;
|
||||
}
|
||||
/// equality
|
||||
bool equal(const self_type& that) const { return this->mPending == that.mPending; }
|
||||
/// implement dereference/indirection operators
|
||||
ptr_type& dereference() const { return const_cast<ptr_type&>(mPending.back()); }
|
||||
|
||||
/// Add the direct children of the specified node to mPending
|
||||
void addChildren(const ptr_type& node)
|
||||
{
|
||||
// If we just use push_back() for each child in turn, we'll end up
|
||||
// processing children in reverse order. We don't want to assume
|
||||
// CHILDITER is reversible: some of the linked trees we'll be
|
||||
// processing manage their children using singly-linked lists. So
|
||||
// figure out how many children there are, grow mPending by that size
|
||||
// and reverse-copy the children into the new space.
|
||||
CHILDITER chi = mBeginFunc(node), chend = mEndFunc(node);
|
||||
// grow mPending by the number of children
|
||||
mPending.resize(mPending.size() + std::distance(chi, chend));
|
||||
// reverse-copy the children into the newly-expanded space
|
||||
std::copy(chi, chend, mPending.rbegin());
|
||||
}
|
||||
|
||||
/// list of the nodes yet to be processed
|
||||
list_type mPending;
|
||||
/// functor to extract begin() child iterator
|
||||
func_type mBeginFunc;
|
||||
/// functor to extract end() child iterator
|
||||
func_type mEndFunc;
|
||||
/// flag which controls traversal of children (skip children of current node if true)
|
||||
bool mSkipChildren;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiated with a tree node, typically the root, LLTreeDFSPostIter
|
||||
* "flattens" a depth-first tree walk through that node and all its
|
||||
* descendants. Whereas LLTreeDFSIter visits each node before visiting any of
|
||||
* its children, LLTreeDFSPostIter visits all of a node's children before
|
||||
* visiting the node itself.
|
||||
*
|
||||
* The begin() LLTreeDFSPostIter must be instantiated with functors to obtain
|
||||
* from a given node begin() and end() iterators for that node's children. For
|
||||
* this reason, you must specify the type of the node's child iterator as an
|
||||
* additional template parameter.
|
||||
*
|
||||
* Specifically, the begin functor must return an iterator whose dereferenced
|
||||
* value is a @em pointer to a child tree node. For instance, if each node
|
||||
* tracks its children in an STL container of node* pointers, you can simply
|
||||
* return that container's begin() iterator.
|
||||
*
|
||||
* Alternatively, if a node tracks its children with a classic linked list,
|
||||
* write a functor returning LLLinkedIter<NODE>.
|
||||
*
|
||||
* The end() LLTreeDFSPostIter must, of course, match the begin() iterator's
|
||||
* template parameters, but is constructed without runtime parameters.
|
||||
*/
|
||||
template <class NODE, typename CHILDITER>
|
||||
class LLTreeDFSPostIter: public LLBaseIter<LLTreeDFSPostIter<NODE, CHILDITER>, NODE>
|
||||
{
|
||||
typedef LLBaseIter<LLTreeDFSPostIter<NODE, CHILDITER>, NODE> super;
|
||||
typedef typename super::self_type self_type;
|
||||
protected:
|
||||
typedef typename super::ptr_type ptr_type;
|
||||
// The func_type is different for this: from a NODE pointer, we must
|
||||
// obtain a CHILDITER.
|
||||
typedef boost::function<CHILDITER(const ptr_type&)> func_type;
|
||||
private:
|
||||
// Upon reaching a given node in our pending list, we need to know whether
|
||||
// we've already pushed that node's children, so we must associate a bool
|
||||
// with each node pointer.
|
||||
typedef std::vector< std::pair<ptr_type, bool> > list_type;
|
||||
public:
|
||||
/// Instantiate an LLTreeDFSPostIter to start a depth-first walk. Pass
|
||||
/// functors to extract the 'child begin' and 'child end' iterators from
|
||||
/// each node.
|
||||
LLTreeDFSPostIter(const ptr_type& node, const func_type& beginfunc, const func_type& endfunc)
|
||||
: mBeginFunc(beginfunc),
|
||||
mEndFunc(endfunc),
|
||||
mSkipAncestors(false)
|
||||
{
|
||||
if (! node)
|
||||
return;
|
||||
mPending.push_back(typename list_type::value_type(node, false));
|
||||
makeCurrent();
|
||||
}
|
||||
/// Instantiate an LLTreeDFSPostIter to mark the end of the walk
|
||||
LLTreeDFSPostIter() : mSkipAncestors(false) {}
|
||||
|
||||
/// flags iterator logic to skip traversing ancestors of current node on next increment
|
||||
void skipAncestors(bool skip = true) { mSkipAncestors = skip; }
|
||||
|
||||
private:
|
||||
/// leverage boost::iterator_facade
|
||||
friend class boost::iterator_core_access;
|
||||
|
||||
/// advance
|
||||
/// This implementation is due to http://en.wikipedia.org/wiki/Depth-first_search
|
||||
void increment()
|
||||
{
|
||||
// Pop the previous current node
|
||||
mPending.pop_back();
|
||||
makeCurrent();
|
||||
}
|
||||
/// equality
|
||||
bool equal(const self_type& that) const { return this->mPending == that.mPending; }
|
||||
/// implement dereference/indirection operators
|
||||
ptr_type& dereference() const { return const_cast<ptr_type&>(mPending.back().first); }
|
||||
|
||||
struct isOpen
|
||||
{
|
||||
bool operator()(const typename list_type::value_type& item)
|
||||
{
|
||||
return item.second;
|
||||
}
|
||||
};
|
||||
|
||||
/// Call this each time we change mPending.back() -- that is, every time
|
||||
/// we're about to change the value returned by dereference(). If we
|
||||
/// haven't yet pushed the new node's children, do so now.
|
||||
void makeCurrent()
|
||||
{
|
||||
if (mSkipAncestors)
|
||||
{
|
||||
mPending.erase(std::remove_if(mPending.begin(), mPending.end(), isOpen()), mPending.end());
|
||||
mSkipAncestors = false;
|
||||
}
|
||||
|
||||
// Once we've popped the last node, this becomes a no-op.
|
||||
if (mPending.empty())
|
||||
return;
|
||||
// Here mPending.back() holds the node pointer we're proposing to
|
||||
// dereference next. Have we pushed that node's children yet?
|
||||
if (mPending.back().second)
|
||||
return; // if so, it's okay to visit this node now
|
||||
// We haven't yet pushed this node's children. Do so now. Remember
|
||||
// that we did -- while the node in question is still back().
|
||||
mPending.back().second = true;
|
||||
addChildren(mPending.back().first);
|
||||
// Now, because we've just changed mPending.back(), make that new node
|
||||
// current.
|
||||
makeCurrent();
|
||||
}
|
||||
|
||||
/// Add the direct children of the specified node to mPending
|
||||
void addChildren(const ptr_type& node)
|
||||
{
|
||||
// If we just use push_back() for each child in turn, we'll end up
|
||||
// processing children in reverse order. We don't want to assume
|
||||
// CHILDITER is reversible: some of the linked trees we'll be
|
||||
// processing manage their children using singly-linked lists. So
|
||||
// figure out how many children there are, grow mPending by that size
|
||||
// and reverse-copy the children into the new space.
|
||||
CHILDITER chi = mBeginFunc(node), chend = mEndFunc(node);
|
||||
// grow mPending by the number of children
|
||||
mPending.resize(mPending.size() + std::distance(chi, chend));
|
||||
// Reverse-copy the children into the newly-expanded space. We can't
|
||||
// just use std::copy() because the source is a ptr_type, whereas the
|
||||
// dest is a pair of (ptr_type, bool).
|
||||
for (typename list_type::reverse_iterator pi = mPending.rbegin(); chi != chend; ++chi, ++pi)
|
||||
{
|
||||
pi->first = *chi; // copy the child pointer
|
||||
pi->second = false; // we haven't yet pushed this child's chldren
|
||||
}
|
||||
}
|
||||
|
||||
/// list of the nodes yet to be processed
|
||||
list_type mPending;
|
||||
/// functor to extract begin() child iterator
|
||||
func_type mBeginFunc;
|
||||
/// functor to extract end() child iterator
|
||||
func_type mEndFunc;
|
||||
/// flags logic to skip traversal of ancestors of current node
|
||||
bool mSkipAncestors;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiated with a tree node, typically the root, LLTreeBFSIter "flattens"
|
||||
* a breadth-first tree walk through that node and all its descendants.
|
||||
*
|
||||
* The begin() LLTreeBFSIter must be instantiated with functors to obtain from
|
||||
* a given node the begin() and end() iterators of that node's children. For
|
||||
* this reason, you must specify the type of the node's child iterator as an
|
||||
* additional template parameter.
|
||||
*
|
||||
* Specifically, the begin functor must return an iterator whose dereferenced
|
||||
* value is a @em pointer to a child tree node. For instance, if each node
|
||||
* tracks its children in an STL container of node* pointers, you can simply
|
||||
* return that container's begin() iterator.
|
||||
*
|
||||
* Alternatively, if a node tracks its children with a classic linked list,
|
||||
* write a functor returning LLLinkedIter<NODE>.
|
||||
*
|
||||
* The end() LLTreeBFSIter must, of course, match the begin() iterator's
|
||||
* template parameters, but is constructed without runtime parameters.
|
||||
*/
|
||||
template <class NODE, typename CHILDITER>
|
||||
class LLTreeBFSIter: public LLBaseIter<LLTreeBFSIter<NODE, CHILDITER>, NODE>
|
||||
{
|
||||
typedef LLBaseIter<LLTreeBFSIter<NODE, CHILDITER>, NODE> super;
|
||||
typedef typename super::self_type self_type;
|
||||
protected:
|
||||
typedef typename super::ptr_type ptr_type;
|
||||
// The func_type is different for this: from a NODE pointer, we must
|
||||
// obtain a CHILDITER.
|
||||
typedef boost::function<CHILDITER(const ptr_type&)> func_type;
|
||||
private:
|
||||
// We need a FIFO queue rather than a LIFO stack. Use a deque rather than
|
||||
// a vector, since vector can't implement pop_front() efficiently.
|
||||
typedef std::deque<ptr_type> list_type;
|
||||
public:
|
||||
/// Instantiate an LLTreeBFSIter to start a depth-first walk. Pass
|
||||
/// functors to extract the 'child begin' and 'child end' iterators from
|
||||
/// each node.
|
||||
LLTreeBFSIter(const ptr_type& node, const func_type& beginfunc, const func_type& endfunc):
|
||||
mBeginFunc(beginfunc),
|
||||
mEndFunc(endfunc)
|
||||
{
|
||||
if (node)
|
||||
mPending.push_back(node);
|
||||
}
|
||||
/// Instantiate an LLTreeBFSIter to mark the end of the walk
|
||||
LLTreeBFSIter() {}
|
||||
|
||||
private:
|
||||
/// leverage boost::iterator_facade
|
||||
friend class boost::iterator_core_access;
|
||||
|
||||
/// advance
|
||||
/// This implementation is due to http://en.wikipedia.org/wiki/Breadth-first_search
|
||||
void increment()
|
||||
{
|
||||
// Capture the node we were just looking at
|
||||
ptr_type current = mPending.front();
|
||||
// Remove it from mPending so we don't process it again later
|
||||
mPending.pop_front();
|
||||
// Add all its children to mPending
|
||||
CHILDITER chend = mEndFunc(current);
|
||||
for (CHILDITER chi = mBeginFunc(current); chi != chend; ++chi)
|
||||
mPending.push_back(*chi);
|
||||
}
|
||||
/// equality
|
||||
bool equal(const self_type& that) const { return this->mPending == that.mPending; }
|
||||
/// implement dereference/indirection operators
|
||||
ptr_type& dereference() const { return const_cast<ptr_type&>(mPending.front()); }
|
||||
|
||||
/// list of the nodes yet to be processed
|
||||
list_type mPending;
|
||||
/// functor to extract begin() child iterator
|
||||
func_type mBeginFunc;
|
||||
/// functor to extract end() child iterator
|
||||
func_type mEndFunc;
|
||||
};
|
||||
|
||||
/**
|
||||
* When you want to select between LLTreeDFSIter, LLTreeDFSPostIter and
|
||||
* LLTreeBFSIter with a compile-time discriminator, use LLTreeWalkIter with an
|
||||
* LLTreeIter::WalkIter template arg.
|
||||
*/
|
||||
template <LLTreeIter::WalkIter DISCRIM, class NODE, typename CHILDITER>
|
||||
class LLTreeWalkIter
|
||||
{
|
||||
enum { use_a_valid_LLTreeIter_WalkIter_value = false };
|
||||
public:
|
||||
/// Bogus constructors for default (unrecognized discriminator) case
|
||||
template <typename TYPE1, typename TYPE2>
|
||||
LLTreeWalkIter(TYPE1, TYPE2)
|
||||
{
|
||||
BOOST_STATIC_ASSERT(use_a_valid_LLTreeIter_WalkIter_value);
|
||||
}
|
||||
LLTreeWalkIter()
|
||||
{
|
||||
BOOST_STATIC_ASSERT(use_a_valid_LLTreeIter_WalkIter_value);
|
||||
}
|
||||
};
|
||||
|
||||
/// Specialize for LLTreeIter::DFS_PRE
|
||||
template <class NODE, typename CHILDITER>
|
||||
class LLTreeWalkIter<LLTreeIter::DFS_PRE, NODE, CHILDITER>:
|
||||
public LLTreeDFSIter<NODE, CHILDITER>
|
||||
{
|
||||
typedef LLTreeDFSIter<NODE, CHILDITER> super;
|
||||
public:
|
||||
/// forward begin ctor
|
||||
LLTreeWalkIter(const typename super::ptr_type& node,
|
||||
const typename super::func_type& beginfunc,
|
||||
const typename super::func_type& endfunc):
|
||||
super(node, beginfunc, endfunc)
|
||||
{}
|
||||
/// forward end ctor
|
||||
LLTreeWalkIter():
|
||||
super()
|
||||
{}
|
||||
};
|
||||
|
||||
/// Specialize for LLTreeIter::DFS_POST
|
||||
template <class NODE, typename CHILDITER>
|
||||
class LLTreeWalkIter<LLTreeIter::DFS_POST, NODE, CHILDITER>:
|
||||
public LLTreeDFSPostIter<NODE, CHILDITER>
|
||||
{
|
||||
typedef LLTreeDFSPostIter<NODE, CHILDITER> super;
|
||||
public:
|
||||
/// forward begin ctor
|
||||
LLTreeWalkIter(const typename super::ptr_type& node,
|
||||
const typename super::func_type& beginfunc,
|
||||
const typename super::func_type& endfunc):
|
||||
super(node, beginfunc, endfunc)
|
||||
{}
|
||||
/// forward end ctor
|
||||
LLTreeWalkIter():
|
||||
super()
|
||||
{}
|
||||
};
|
||||
|
||||
/// Specialize for LLTreeIter::BFS
|
||||
template <class NODE, typename CHILDITER>
|
||||
class LLTreeWalkIter<LLTreeIter::BFS, NODE, CHILDITER>:
|
||||
public LLTreeBFSIter<NODE, CHILDITER>
|
||||
{
|
||||
typedef LLTreeBFSIter<NODE, CHILDITER> super;
|
||||
public:
|
||||
/// forward begin ctor
|
||||
LLTreeWalkIter(const typename super::ptr_type& node,
|
||||
const typename super::func_type& beginfunc,
|
||||
const typename super::func_type& endfunc):
|
||||
super(node, beginfunc, endfunc)
|
||||
{}
|
||||
/// forward end ctor
|
||||
LLTreeWalkIter():
|
||||
super()
|
||||
{}
|
||||
};
|
||||
|
||||
#endif /* ! defined(LL_LLTREEITERATORS_H) */
|
||||
@@ -32,7 +32,7 @@
|
||||
#ifndef LL_LLECONOMY_H
|
||||
#define LL_LLECONOMY_H
|
||||
|
||||
#include "llmemory.h"
|
||||
#include "llsingleton.h"
|
||||
|
||||
class LLMessageSystem;
|
||||
class LLVector3;
|
||||
|
||||
@@ -36,10 +36,9 @@
|
||||
#include "lldarray.h"
|
||||
#include "llfoldertype.h"
|
||||
#include "llinventorytype.h"
|
||||
#include "llinventorydefines.h"
|
||||
#include "llmemtype.h"
|
||||
#include "llpermissions.h"
|
||||
#include "llmemory.h"
|
||||
#include "llrefcount.h"
|
||||
#include "llsaleinfo.h"
|
||||
#include "llsd.h"
|
||||
#include "lluuid.h"
|
||||
@@ -304,84 +303,11 @@ protected:
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Useful bits
|
||||
// Convertors
|
||||
//
|
||||
// These functions convert between structured data and an inventory
|
||||
// item, appropriate for serialization.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// This functor tests if an item is transferrable and returns true if
|
||||
// it is. Derived from unary_function<> so that the object can be used
|
||||
// in stl-compliant adaptable predicates (eg, not1<>). You might want
|
||||
// to use this in std::partition() or similar logic.
|
||||
struct IsItemTransferable : public std::unary_function<LLInventoryItem*, bool>
|
||||
{
|
||||
LLUUID mDestID;
|
||||
IsItemTransferable(const LLUUID& dest_id) : mDestID(dest_id) {}
|
||||
bool operator()(const LLInventoryItem* item) const
|
||||
{
|
||||
return (item->getPermissions().allowTransferTo(mDestID)) ? true:false;
|
||||
}
|
||||
};
|
||||
|
||||
// This functor is used to set the owner and group of inventory items,
|
||||
// for example, in a simple std::for_each() loop. Note that the call
|
||||
// to setOwnerAndGroup can fail if authority_id != LLUUID::null.
|
||||
struct SetItemOwnerAndGroup
|
||||
{
|
||||
LLUUID mAuthorityID;
|
||||
LLUUID mOwnerID;
|
||||
LLUUID mGroupID;
|
||||
SetItemOwnerAndGroup(const LLUUID& authority_id,
|
||||
const LLUUID& owner_id,
|
||||
const LLUUID& group_id) :
|
||||
mAuthorityID(authority_id), mOwnerID(owner_id), mGroupID(group_id) {}
|
||||
void operator()(LLInventoryItem* item) const
|
||||
{
|
||||
LLPermissions perm = item->getPermissions();
|
||||
bool is_atomic = (LLAssetType::AT_OBJECT == item->getType()) ? false : true;
|
||||
perm.setOwnerAndGroup(mAuthorityID, mOwnerID, mGroupID, is_atomic);
|
||||
// If no owner id is set, this is equivalent to a deed action.
|
||||
// Clear 'share with group'.
|
||||
if (mOwnerID.isNull())
|
||||
{
|
||||
perm.setMaskGroup(PERM_NONE);
|
||||
}
|
||||
item->setPermissions(perm);
|
||||
}
|
||||
};
|
||||
|
||||
// This functor is used to unset the share with group, everyone perms, and
|
||||
// for sale info for objects being sold through contents.
|
||||
struct SetNotForSale
|
||||
{
|
||||
LLUUID mAgentID;
|
||||
LLUUID mGroupID;
|
||||
SetNotForSale(const LLUUID& agent_id,
|
||||
const LLUUID& group_id) :
|
||||
mAgentID(agent_id), mGroupID(group_id) {}
|
||||
void operator()(LLInventoryItem* item) const
|
||||
{
|
||||
// Clear group & everyone permissions.
|
||||
LLPermissions perm = item->getPermissions();
|
||||
perm.setGroupBits(mAgentID, mGroupID, FALSE, PERM_MODIFY | PERM_MOVE | PERM_COPY);
|
||||
perm.setEveryoneBits(mAgentID, mGroupID, FALSE, PERM_MOVE | PERM_COPY);
|
||||
item->setPermissions(perm);
|
||||
|
||||
// Mark group & everyone permissions for overwrite on the next
|
||||
// rez if it is an object.
|
||||
if(LLAssetType::AT_OBJECT == item->getType())
|
||||
{
|
||||
U32 flags = item->getFlags();
|
||||
flags |= LLInventoryItemFlags::II_FLAGS_OBJECT_PERM_OVERWRITE_GROUP;
|
||||
flags |= LLInventoryItemFlags::II_FLAGS_OBJECT_PERM_OVERWRITE_EVERYONE;
|
||||
item->setFlags(flags);
|
||||
}
|
||||
|
||||
// Clear for sale info.
|
||||
item->setSaleInfo(LLSaleInfo::DEFAULT);
|
||||
}
|
||||
};
|
||||
|
||||
// These functions convert between structured data and an inventroy
|
||||
// item, appropriate for serialization.
|
||||
LLSD ll_create_sd_from_inventory_item(LLPointer<LLInventoryItem> item);
|
||||
LLPointer<LLInventoryItem> ll_create_item_from_sd(const LLSD& sd_item);
|
||||
LLSD ll_create_sd_from_inventory_category(LLPointer<LLInventoryCategory> cat);
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include "llinventorytype.h"
|
||||
#include "lldictionary.h"
|
||||
#include "llmemory.h"
|
||||
#include "llsingleton.h"
|
||||
|
||||
static const std::string empty_string;
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
#ifndef LL_NOTECARD_H
|
||||
#define LL_NOTECARD_H
|
||||
|
||||
#include "llmemory.h"
|
||||
#include "llpointer.h"
|
||||
#include "llinventory.h"
|
||||
|
||||
class LLNotecard
|
||||
|
||||
@@ -177,7 +177,7 @@ void LLParcel::init(const LLUUID &owner_id,
|
||||
mSaleTimerExpires.stop();
|
||||
mGraceExtension = 0;
|
||||
//mExpireAction = STEA_REVERT;
|
||||
mRecordTransaction = FALSE;
|
||||
//mRecordTransaction = FALSE;
|
||||
|
||||
mAuctionID = 0;
|
||||
mInEscrow = false;
|
||||
|
||||
@@ -432,13 +432,10 @@ public:
|
||||
void completeSale(U32& type, U8& flags, LLUUID& to_id);
|
||||
void clearSale();
|
||||
|
||||
// this function returns TRUE if the parcel needs conversion to a
|
||||
// lease from a non-owned-status state.
|
||||
BOOL getRecordTransaction() const { return mRecordTransaction; }
|
||||
void setRecordTransaction(BOOL record) { mRecordTransaction = record; }
|
||||
|
||||
BOOL isMediaResetTimerExpired(const U64& time);
|
||||
|
||||
|
||||
// more accessors
|
||||
U32 getParcelFlags() const { return mParcelFlags; }
|
||||
|
||||
@@ -473,7 +470,9 @@ public:
|
||||
{ return (mParcelFlags & PF_ALLOW_FLY) ? TRUE : FALSE; }
|
||||
|
||||
BOOL getAllowLandmark() const
|
||||
{ return (mParcelFlags & PF_ALLOW_LANDMARK) ? TRUE : FALSE; }
|
||||
{ return TRUE; }
|
||||
//Perhaps revert for opensim?
|
||||
//{ return (mParcelFlags & PF_ALLOW_LANDMARK) ? TRUE : FALSE; }
|
||||
|
||||
BOOL getAllowGroupScripts() const
|
||||
{ return (mParcelFlags & PF_ALLOW_GROUP_SCRIPTS) ? TRUE : FALSE; }
|
||||
@@ -619,8 +618,6 @@ protected:
|
||||
LLTimer mMediaResetTimer;
|
||||
|
||||
S32 mGraceExtension;
|
||||
BOOL mRecordTransaction;
|
||||
|
||||
|
||||
// This value is non-zero if there is an auction associated with
|
||||
// the parcel.
|
||||
|
||||
@@ -132,5 +132,7 @@ const S32 PARCEL_DETAILS_DESC = 1;
|
||||
const S32 PARCEL_DETAILS_OWNER = 2;
|
||||
const S32 PARCEL_DETAILS_GROUP = 3;
|
||||
const S32 PARCEL_DETAILS_AREA = 4;
|
||||
const S32 PARCEL_DETAILS_ID = 5;
|
||||
const S32 PARCEL_DETAILS_SEE_AVATARS = 6;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -30,8 +30,8 @@
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#include "linden_common.h"
|
||||
#include <iostream>
|
||||
#include "linden_common.h"
|
||||
|
||||
#include "llsaleinfo.h"
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include "llcompilequeue.h"
|
||||
#include "llfloaterbuycurrency.h"
|
||||
#include "llnotify.h"
|
||||
#include "llinventorydefines.h"
|
||||
#include "llinventorymodel.h"
|
||||
#include "llinventoryview.h"
|
||||
#include "llpermissionsflags.h"
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include "llviewerprecompiledheaders.h"
|
||||
#include "llfloaterbulkpermission.h"
|
||||
#include "llfloaterperms.h" // for utilities
|
||||
#include "llinventorydefines.h"
|
||||
#include "llagent.h"
|
||||
#include "llchat.h"
|
||||
#include "llviewerwindow.h"
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
#include "llagent.h" // for agent id
|
||||
#include "llalertdialog.h"
|
||||
#include "llinventorymodel.h" // for gInventory
|
||||
#include "llinventorydefines.h"
|
||||
#include "llinventoryview.h" // for get_item_icon
|
||||
#include "llselectmgr.h"
|
||||
#include "llscrolllistctrl.h"
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
#include "llalertdialog.h"
|
||||
#include "llcheckboxctrl.h"
|
||||
#include "llinventorymodel.h" // for gInventory
|
||||
#include "llinventorydefines.h"
|
||||
#include "llinventoryview.h" // for get_item_icon
|
||||
#include "llselectmgr.h"
|
||||
#include "llscrolllistctrl.h"
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include "llcachename.h"
|
||||
#include "lldbstrings.h"
|
||||
#include "llinventory.h"
|
||||
#include "llinventorydefines.h"
|
||||
|
||||
#include "llagent.h"
|
||||
#include "llbutton.h"
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
|
||||
#include "llinventoryview.h"
|
||||
#include "llinventorybridge.h"
|
||||
#include "llinventorydefines.h"
|
||||
|
||||
#include "message.h"
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
|
||||
#include "llinventoryview.h"
|
||||
#include "llinventorybridge.h"
|
||||
#include "llinventorydefines.h"
|
||||
|
||||
#include "message.h"
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
#include "llpermissionsflags.h"
|
||||
#include "lleconomy.h"
|
||||
#include "material_codes.h"
|
||||
#include "llinventorydefines.h"
|
||||
|
||||
// project includes
|
||||
#include "llui.h"
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include "llinventory.h"
|
||||
#include "llviewerinventory.h"
|
||||
#include "llinventorymodel.h"
|
||||
#include "llinventorydefines.h"
|
||||
#include "llinventoryview.h"
|
||||
#include "llagent.h"
|
||||
#include "lltooldraganddrop.h"
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
#include "llfontgl.h"
|
||||
#include "llassetstorage.h"
|
||||
#include "llinventory.h"
|
||||
#include "llinventorydefines.h"
|
||||
|
||||
#include "llagent.h"
|
||||
#include "llcallbacklist.h"
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "llpreview.h"
|
||||
#include "lllineeditor.h"
|
||||
#include "llinventory.h"
|
||||
#include "llinventorydefines.h"
|
||||
#include "llinventorymodel.h"
|
||||
#include "llresmgr.h"
|
||||
#include "lltextbox.h"
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
// libraries
|
||||
#include "lldatapacker.h"
|
||||
#include "lldarray.h"
|
||||
#include "llinventorydefines.h"
|
||||
#include "llstring.h"
|
||||
#include "lldir.h"
|
||||
#include "llmultigesture.h"
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include "llpreviewnotecard.h"
|
||||
|
||||
#include "llinventory.h"
|
||||
#include "llinventorydefines.h"
|
||||
|
||||
#include "llagent.h"
|
||||
#include "llagentcamera.h"
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#include "llcheckboxctrl.h"
|
||||
#include "llcombobox.h"
|
||||
#include "lldir.h"
|
||||
#include "llinventorydefines.h"
|
||||
#include "llinventorymodel.h"
|
||||
#include "llkeyboard.h"
|
||||
#include "lllineeditor.h"
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
#include "llhudeffecttrail.h"
|
||||
#include "llhudmanager.h"
|
||||
#include "llinventorybridge.h"
|
||||
#include "llinventorydefines.h"
|
||||
#include "llinventorymodel.h"
|
||||
#include "llinventoryview.h"
|
||||
#include "llmutelist.h"
|
||||
|
||||
@@ -56,6 +56,7 @@
|
||||
#include "llfloaterworldmap.h"
|
||||
#include "llhudtext.h"
|
||||
#include "llhudview.h"
|
||||
#include "llinventorydefines.h"
|
||||
#include "llinventorymodel.h"
|
||||
#include "lllandmarklist.h"
|
||||
#include "llsky.h"
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
#include "llgesturemgr.h"
|
||||
|
||||
#include "llinventorybridge.h"
|
||||
#include "llinventorydefines.h"
|
||||
#include "llinventoryview.h"
|
||||
|
||||
#include "llviewerregion.h"
|
||||
|
||||
@@ -167,6 +167,7 @@
|
||||
#include "llimagebmp.h"
|
||||
#include "llimagej2c.h"
|
||||
#include "llimagetga.h"
|
||||
#include "llinventorydefines.h"
|
||||
#include "llinventorymodel.h"
|
||||
#include "llinventoryview.h"
|
||||
#include "llkeyboard.h"
|
||||
|
||||
@@ -95,6 +95,7 @@
|
||||
#include "llhudeffecttrail.h"
|
||||
#include "llhudmanager.h"
|
||||
#include "llimpanel.h"
|
||||
#include "llinventorydefines.h"
|
||||
#include "llinventorymodel.h"
|
||||
#include "llinventoryview.h"
|
||||
#include "llmenugl.h"
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
#include "llfontgl.h"
|
||||
#include "llframetimer.h"
|
||||
#include "llinventory.h"
|
||||
#include "llinventorydefines.h"
|
||||
#include "llmaterialtable.h"
|
||||
#include "llmutelist.h"
|
||||
#include "llnamevalue.h"
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
#include "llsdutil.h"
|
||||
#include "llsdutil_math.h"
|
||||
#include "lltransactiontypes.h"
|
||||
#include "llinventorydefines.h"
|
||||
|
||||
// newview includes
|
||||
#include "llagent.h"
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "llaudioengine.h"
|
||||
#include "llagent.h"
|
||||
#include "llinventory.h"
|
||||
#include "llinventorydefines.h"
|
||||
#include "llinventorymodel.h"
|
||||
#include "llinventoryview.h"
|
||||
#include "llinventorybridge.h" // for landmark prefix string
|
||||
|
||||
Reference in New Issue
Block a user