summaryrefslogtreecommitdiff
path: root/indra/llcommon
diff options
context:
space:
mode:
authorPalmer <palmer@lindenlab.com>2009-11-02 14:16:08 -0800
committerPalmer <palmer@lindenlab.com>2009-11-02 14:16:08 -0800
commitb81feb6ae7fd9d01b63999e53c5c231551d53200 (patch)
tree527df9920d7d27000981fe726e5fbed9deccadbd /indra/llcommon
parent3e80fa3dbc943de9b784fedc202ba38cf238f46d (diff)
parent74dda61dfbccfd9ab5ef309aeb4b2b8c86e06cd6 (diff)
Merge of viewer-2-0 changes
Diffstat (limited to 'indra/llcommon')
-rw-r--r--indra/llcommon/CMakeLists.txt65
-rw-r--r--indra/llcommon/linden_common.h8
-rw-r--r--indra/llcommon/llallocator.h126
-rw-r--r--indra/llcommon/llallocator_heap_profile.cpp1
-rw-r--r--indra/llcommon/llapp.h2
-rw-r--r--indra/llcommon/llapr.h518
-rw-r--r--indra/llcommon/llassettype.cpp1
-rw-r--r--indra/llcommon/llassettype.h3
-rw-r--r--indra/llcommon/llbase32.h4
-rw-r--r--indra/llcommon/llbase64.h4
-rw-r--r--indra/llcommon/llcommon.h2
-rw-r--r--indra/llcommon/llcoros.cpp137
-rw-r--r--indra/llcommon/llcoros.h149
-rw-r--r--indra/llcommon/llcrc.h2
-rw-r--r--indra/llcommon/llcriticaldamp.h2
-rw-r--r--indra/llcommon/llcursortypes.h2
-rw-r--r--indra/llcommon/lldate.h6
-rw-r--r--indra/llcommon/lldependencies.h2
-rw-r--r--indra/llcommon/llerror.h6
-rw-r--r--indra/llcommon/llerrorcontrol.h50
-rw-r--r--indra/llcommon/llerrorthread.h2
-rw-r--r--indra/llcommon/llevent.h10
-rw-r--r--indra/llcommon/lleventcoro.cpp129
-rw-r--r--indra/llcommon/lleventcoro.h549
-rw-r--r--indra/llcommon/lleventdispatcher.cpp133
-rw-r--r--indra/llcommon/lleventdispatcher.h130
-rw-r--r--indra/llcommon/lleventfilter.cpp149
-rw-r--r--indra/llcommon/lleventfilter.h186
-rw-r--r--indra/llcommon/llevents.cpp71
-rw-r--r--indra/llcommon/llevents.h1774
-rw-r--r--indra/llcommon/llfasttimer.h634
-rw-r--r--indra/llcommon/llfile.h14
-rw-r--r--indra/llcommon/llfindlocale.h4
-rw-r--r--indra/llcommon/llfixedbuffer.h2
-rw-r--r--indra/llcommon/llformat.h2
-rw-r--r--indra/llcommon/llframetimer.h2
-rw-r--r--indra/llcommon/llheartbeat.h2
-rw-r--r--indra/llcommon/llkeythrottle.h57
-rw-r--r--indra/llcommon/llliveappconfig.h2
-rw-r--r--indra/llcommon/lllivefile.h2
-rw-r--r--indra/llcommon/lllog.h2
-rw-r--r--indra/llcommon/llmd5.h2
-rw-r--r--indra/llcommon/llmemory.h130
-rw-r--r--indra/llcommon/llmemorystream.h4
-rw-r--r--indra/llcommon/llmemtype.h496
-rw-r--r--indra/llcommon/llmetrics.h2
-rw-r--r--indra/llcommon/llmortician.h2
-rw-r--r--indra/llcommon/llpreprocessor.h310
-rw-r--r--indra/llcommon/llprocesslauncher.h2
-rw-r--r--indra/llcommon/llqueuedthread.h5
-rw-r--r--indra/llcommon/llrand.h12
-rw-r--r--indra/llcommon/llrefcount.h4
-rw-r--r--indra/llcommon/llrun.h4
-rw-r--r--indra/llcommon/llsd.h4
-rw-r--r--indra/llcommon/llsdserialize.h28
-rw-r--r--indra/llcommon/llsdserialize_xml.cpp12
-rw-r--r--indra/llcommon/llsdutil.cpp355
-rw-r--r--indra/llcommon/llsdutil.h114
-rw-r--r--indra/llcommon/llsecondlifeurls.h30
-rw-r--r--indra/llcommon/llsimplehash.h2
-rw-r--r--indra/llcommon/llsingleton.cpp38
-rw-r--r--indra/llcommon/llsingleton.h47
-rw-r--r--indra/llcommon/llstacktrace.cpp283
-rw-r--r--indra/llcommon/llstacktrace.h88
-rw-r--r--indra/llcommon/llstat.cpp51
-rw-r--r--indra/llcommon/llstat.h35
-rw-r--r--indra/llcommon/llstreamtools.h36
-rw-r--r--indra/llcommon/llstring.cpp45
-rw-r--r--indra/llcommon/llstring.h2590
-rw-r--r--indra/llcommon/llstringtable.h18
-rw-r--r--indra/llcommon/llsys.cpp8
-rw-r--r--indra/llcommon/llsys.h18
-rw-r--r--indra/llcommon/llthread.h10
-rw-r--r--indra/llcommon/lltimer.h28
-rw-r--r--indra/llcommon/lluri.h4
-rw-r--r--indra/llcommon/lluuid.h9
-rw-r--r--indra/llcommon/llversionserver.h4
-rw-r--r--indra/llcommon/llversionviewer.h2
-rw-r--r--indra/llcommon/llworkerthread.h4
-rw-r--r--indra/llcommon/metaclass.h2
-rw-r--r--indra/llcommon/metaproperty.h2
-rw-r--r--indra/llcommon/metapropertyt.h13
-rw-r--r--indra/llcommon/reflective.h2
-rw-r--r--indra/llcommon/stringize.h73
-rw-r--r--indra/llcommon/tests/listener.h139
-rw-r--r--indra/llcommon/tests/lleventcoro_test.cpp782
-rw-r--r--indra/llcommon/tests/lleventfilter_test.cpp276
-rw-r--r--indra/llcommon/tests/llsdserialize_test.cpp74
-rw-r--r--indra/llcommon/tests/llstring_test.cpp1
-rw-r--r--indra/llcommon/tests/wrapllerrs.h56
-rw-r--r--indra/llcommon/timing.h3
-rw-r--r--indra/llcommon/u64.h10
92 files changed, 7483 insertions, 3732 deletions
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt
index 7468e3dde4..e7aaf3c984 100644
--- a/indra/llcommon/CMakeLists.txt
+++ b/indra/llcommon/CMakeLists.txt
@@ -4,12 +4,19 @@ project(llcommon)
include(00-Common)
include(LLCommon)
+include(Linking)
include(Boost)
+include (Pth)
+
+if (WINDOWS)
+ include(CopyWinLibs)
+endif (WINDOWS)
include_directories(
${EXPAT_INCLUDE_DIRS}
${LLCOMMON_INCLUDE_DIRS}
${ZLIB_INCLUDE_DIRS}
+ ${PTH_INCLUDE_DIRS}
)
# add_executable(lltreeiterators lltreeiterators.cpp)
@@ -26,6 +33,7 @@ set(llcommon_SOURCE_FILES
llbase32.cpp
llbase64.cpp
llcommon.cpp
+ llcoros.cpp
llcrc.cpp
llcriticaldamp.cpp
llcursortypes.cpp
@@ -34,6 +42,9 @@ set(llcommon_SOURCE_FILES
llerror.cpp
llerrorthread.cpp
llevent.cpp
+ lleventcoro.cpp
+ lleventdispatcher.cpp
+ lleventfilter.cpp
llevents.cpp
llfasttimer.cpp
llfile.cpp
@@ -64,6 +75,7 @@ set(llcommon_SOURCE_FILES
llsdserialize_xml.cpp
llsdutil.cpp
llsecondlifeurls.cpp
+ llsingleton.cpp
llstat.cpp
llstacktrace.cpp
llstreamtools.cpp
@@ -106,6 +118,7 @@ set(llcommon_HEADER_FILES
llchat.h
llclickaction.h
llcommon.h
+ llcoros.h
llcrc.h
llcriticaldamp.h
llcursortypes.h
@@ -127,6 +140,9 @@ set(llcommon_HEADER_FILES
llerrorlegacy.h
llerrorthread.h
llevent.h
+ lleventcoro.h
+ lleventdispatcher.h
+ lleventfilter.h
llevents.h
lleventemitter.h
llextendedstatus.h
@@ -141,6 +157,7 @@ set(llcommon_HEADER_FILES
llhttpstatuscodes.h
llindexedqueue.h
llinstancetracker.h
+ llinstancetracker.h
llkeythrottle.h
lllazy.h
lllinkedqueue.h
@@ -222,25 +239,63 @@ set_source_files_properties(${llcommon_HEADER_FILES}
list(APPEND llcommon_SOURCE_FILES ${llcommon_HEADER_FILES})
-add_library (llcommon ${llcommon_SOURCE_FILES})
-target_link_libraries(llcommon
+if(LLCOMMON_LINK_SHARED)
+ add_library (llcommon SHARED ${llcommon_SOURCE_FILES})
+
+ if(SHARED_LIB_STAGING_DIR)
+ # *FIX:Mani ---
+ # llcommon.dll get written to the DLL staging directory.
+ # Also this directory is shared with RunBuildTest.cmake, y'know, for the tests.
+ set_target_properties(llcommon PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${SHARED_LIB_STAGING_DIR})
+ if(NOT WINDOWS)
+ get_target_property(LLCOMMON_PATH llcommon LOCATION)
+ get_filename_component(LLCOMMON_FILE ${LLCOMMON_PATH} NAME)
+ add_custom_command(
+ TARGET llcommon POST_BUILD
+ COMMAND ${CMAKE_COMMAND}
+ ARGS
+ -E
+ copy_if_different
+ ${LLCOMMON_PATH}
+ ${SHARED_LIB_STAGING_DIR}/${CMAKE_CFG_INTDIR}/${LLCOMMON_FILE}
+ COMMENT "Copying llcommon to the staging folder."
+ )
+ endif(NOT WINDOWS)
+ endif(SHARED_LIB_STAGING_DIR)
+
+ if (DARWIN)
+ set_target_properties(llcommon PROPERTIES
+ BUILD_WITH_INSTALL_RPATH 1
+ INSTALL_NAME_DIR "@executable_path/../Resources"
+ )
+ endif(DARWIN)
+
+else(LLCOMMON_LINK_SHARED)
+ add_library (llcommon ${llcommon_SOURCE_FILES})
+endif(LLCOMMON_LINK_SHARED)
+
+target_link_libraries(
+ llcommon
${APRUTIL_LIBRARIES}
${APR_LIBRARIES}
${EXPAT_LIBRARIES}
${ZLIB_LIBRARIES}
+ ${WINDOWS_LIBRARIES}
${BOOST_PROGRAM_OPTIONS_LIBRARY}
${BOOST_REGEX_LIBRARY}
+ ${PTH_LIBRARIES}
)
-#add unit tests
-INCLUDE(LLAddBuildTest)
+add_dependencies(llcommon stage_third_party_libs)
+
+include(LLAddBuildTest)
SET(llcommon_TEST_SOURCE_FILES
# unit-testing llcommon is not possible right now as the test-harness *itself* depends upon llcommon, causing a circular dependency. Add your 'unit' tests as integration tests for now.
)
LL_ADD_PROJECT_UNIT_TESTS(llcommon "${llcommon_TEST_SOURCE_FILES}")
#set(TEST_DEBUG on)
-set(test_libs llcommon ${LLCOMMON_LIBRARIES} ${WINDOWS_LIBRARIES})
+set(test_libs llcommon ${LLCOMMON_LIBRARIES} ${WINDOWS_LIBRARIES} ${GOOGLEMOCK_LIBRARIES})
LL_ADD_INTEGRATION_TEST(commonmisc "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(bitpack "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llbase64 "" "${test_libs}")
diff --git a/indra/llcommon/linden_common.h b/indra/llcommon/linden_common.h
index 8687a24655..c2eb867795 100644
--- a/indra/llcommon/linden_common.h
+++ b/indra/llcommon/linden_common.h
@@ -72,13 +72,7 @@
#ifdef LL_WINDOWS
// Reenable warnings we disabled above
#pragma warning (3 : 4702) // unreachable code, we like level 3, not 4
-// level 4 warnings that we need to disable:
-#pragma warning (disable : 4100) // unreferenced formal parameter
-#pragma warning (disable : 4127) // conditional expression is constant (e.g. while(1) )
-#pragma warning (disable : 4244) // possible loss of data on conversions
-#pragma warning (disable : 4396) // the inline specifier cannot be used when a friend declaration refers to a specialization of a function template
-#pragma warning (disable : 4512) // assignment operator could not be generated
-#pragma warning (disable : 4706) // assignment within conditional (even if((x = y)) )
+// moved msvc warnings to llpreprocessor.h *TODO - delete this comment after merge conflicts are unlikely -brad
#endif // LL_WINDOWS
// Linden only libs in alpha-order other than stdtypes.h
diff --git a/indra/llcommon/llallocator.h b/indra/llcommon/llallocator.h
index 2b70fee0b8..0d6f18c5d4 100644
--- a/indra/llcommon/llallocator.h
+++ b/indra/llcommon/llallocator.h
@@ -1,63 +1,63 @@
-/**
- * @file llallocator.h
- * @brief Declaration of the LLAllocator class.
- *
- * $LicenseInfo:firstyear=2009&license=viewergpl$
- *
- * Copyright (c) 2009-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLALLOCATOR_H
-#define LL_LLALLOCATOR_H
-
-#include <string>
-
-#include "llmemtype.h"
-#include "llallocator_heap_profile.h"
-
-class LLAllocator {
- friend class LLMemoryView;
- friend class LLMemType;
-
-private:
- static void pushMemType(S32 type);
- static S32 popMemType();
-
-public:
- void setProfilingEnabled(bool should_enable);
-
- static bool isProfiling();
-
- LLAllocatorHeapProfile const & getProfile();
-
-private:
- std::string getRawProfile();
-
-private:
- LLAllocatorHeapProfile mProf;
-};
-
-#endif // LL_LLALLOCATOR_H
+/**
+ * @file llallocator.h
+ * @brief Declaration of the LLAllocator class.
+ *
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ *
+ * Copyright (c) 2009-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLALLOCATOR_H
+#define LL_LLALLOCATOR_H
+
+#include <string>
+
+#include "llmemtype.h"
+#include "llallocator_heap_profile.h"
+
+class LL_COMMON_API LLAllocator {
+ friend class LLMemoryView;
+ friend class LLMemType;
+
+private:
+ static void pushMemType(S32 type);
+ static S32 popMemType();
+
+public:
+ void setProfilingEnabled(bool should_enable);
+
+ static bool isProfiling();
+
+ LLAllocatorHeapProfile const & getProfile();
+
+private:
+ std::string getRawProfile();
+
+private:
+ LLAllocatorHeapProfile mProf;
+};
+
+#endif // LL_LLALLOCATOR_H
diff --git a/indra/llcommon/llallocator_heap_profile.cpp b/indra/llcommon/llallocator_heap_profile.cpp
index d82ee9ed81..0a807702d0 100644
--- a/indra/llcommon/llallocator_heap_profile.cpp
+++ b/indra/llcommon/llallocator_heap_profile.cpp
@@ -38,6 +38,7 @@
// disable warning about boost::lexical_cast returning uninitialized data
// when it fails to parse the string
#pragma warning (disable:4701)
+#pragma warning (disable:4702)
#endif
#include <boost/algorithm/string/split.hpp>
diff --git a/indra/llcommon/llapp.h b/indra/llcommon/llapp.h
index e32a293f1c..1a052ce62d 100644
--- a/indra/llcommon/llapp.h
+++ b/indra/llcommon/llapp.h
@@ -62,7 +62,7 @@ public:
};
#endif
-class LLApp : public LLOptionInterface
+class LL_COMMON_API LLApp : public LLOptionInterface
{
friend class LLErrorThread;
public:
diff --git a/indra/llcommon/llapr.h b/indra/llcommon/llapr.h
index 63130a89fc..0898aeec47 100644
--- a/indra/llcommon/llapr.h
+++ b/indra/llcommon/llapr.h
@@ -1,259 +1,259 @@
-/**
- * @file llapr.h
- * @author Phoenix
- * @date 2004-11-28
- * @brief Helper functions for using the apache portable runtime library.
- *
- * $LicenseInfo:firstyear=2004&license=viewergpl$
- *
- * Copyright (c) 2004-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLAPR_H
-#define LL_LLAPR_H
-
-#if LL_LINUX || LL_SOLARIS
-#include <sys/param.h> // Need PATH_MAX in APR headers...
-#endif
-
-#include <boost/noncopyable.hpp>
-
-#include "apr_thread_proc.h"
-#include "apr_thread_mutex.h"
-#include "apr_getopt.h"
-#include "apr_signal.h"
-#include "apr_atomic.h"
-#include "llstring.h"
-
-extern apr_thread_mutex_t* gLogMutexp;
-extern apr_thread_mutex_t* gCallStacksLogMutexp;
-
-/**
- * @brief initialize the common apr constructs -- apr itself, the
- * global pool, and a mutex.
- */
-void ll_init_apr();
-
-/**
- * @brief Cleanup those common apr constructs.
- */
-void ll_cleanup_apr();
-
-//
-//LL apr_pool
-//manage apr_pool_t, destroy allocated apr_pool in the destruction function.
-//
-class LLAPRPool
-{
-public:
- LLAPRPool(apr_pool_t *parent = NULL, apr_size_t size = 0, BOOL releasePoolFlag = TRUE) ;
- ~LLAPRPool() ;
-
- apr_pool_t* getAPRPool() ;
- apr_status_t getStatus() {return mStatus ; }
-
-protected:
- void releaseAPRPool() ;
- void createAPRPool() ;
-
-protected:
- apr_pool_t* mPool ; //pointing to an apr_pool
- apr_pool_t* mParent ; //parent pool
- apr_size_t mMaxSize ; //max size of mPool, mPool should return memory to system if allocated memory beyond this limit. However it seems not to work.
- apr_status_t mStatus ; //status when creating the pool
- BOOL mReleasePoolFlag ; //if set, mPool is destroyed when LLAPRPool is deleted. default value is true.
-};
-
-//
-//volatile LL apr_pool
-//which clears memory automatically.
-//so it can not hold static data or data after memory is cleared
-//
-class LLVolatileAPRPool : public LLAPRPool
-{
-public:
- LLVolatileAPRPool(apr_pool_t *parent = NULL, apr_size_t size = 0, BOOL releasePoolFlag = TRUE);
- ~LLVolatileAPRPool(){}
-
- apr_pool_t* getVolatileAPRPool() ;
-
- void clearVolatileAPRPool() ;
-
- BOOL isFull() ;
- BOOL isEmpty() {return !mNumActiveRef ;}
-private:
- S32 mNumActiveRef ; //number of active pointers pointing to the apr_pool.
- S32 mNumTotalRef ; //number of total pointers pointing to the apr_pool since last creating.
-} ;
-
-/**
- * @class LLScopedLock
- * @brief Small class to help lock and unlock mutexes.
- *
- * This class is used to have a stack level lock once you already have
- * an apr mutex handy. The constructor handles the lock, and the
- * destructor handles the unlock. Instances of this class are
- * <b>not</b> thread safe.
- */
-class LLScopedLock : private boost::noncopyable
-{
-public:
- /**
- * @brief Constructor which accepts a mutex, and locks it.
- *
- * @param mutex An allocated APR mutex. If you pass in NULL,
- * this wrapper will not lock.
- */
- LLScopedLock(apr_thread_mutex_t* mutex);
-
- /**
- * @brief Destructor which unlocks the mutex if still locked.
- */
- ~LLScopedLock();
-
- /**
- * @brief Check lock.
- */
- bool isLocked() const { return mLocked; }
-
- /**
- * @brief This method unlocks the mutex.
- */
- void unlock();
-
-protected:
- bool mLocked;
- apr_thread_mutex_t* mMutex;
-};
-
-template <typename Type> class LLAtomic32
-{
-public:
- LLAtomic32<Type>() {};
- LLAtomic32<Type>(Type x) {apr_atomic_set32(&mData, apr_uint32_t(x)); };
- ~LLAtomic32<Type>() {};
-
- operator const Type() { apr_uint32_t data = apr_atomic_read32(&mData); return Type(data); }
- Type operator =(const Type& x) { apr_atomic_set32(&mData, apr_uint32_t(x)); return Type(mData); }
- void operator -=(Type x) { apr_atomic_sub32(&mData, apr_uint32_t(x)); }
- void operator +=(Type x) { apr_atomic_add32(&mData, apr_uint32_t(x)); }
- Type operator ++(int) { return apr_atomic_inc32(&mData); } // Type++
- Type operator --(int) { return apr_atomic_dec32(&mData); } // Type--
-
-private:
- apr_uint32_t mData;
-};
-
-typedef LLAtomic32<U32> LLAtomicU32;
-typedef LLAtomic32<S32> LLAtomicS32;
-
-// File IO convenience functions.
-// Returns NULL if the file fails to openm sets *sizep to file size of not NULL
-// abbreviated flags
-#define LL_APR_R (APR_READ) // "r"
-#define LL_APR_W (APR_CREATE|APR_TRUNCATE|APR_WRITE) // "w"
-#define LL_APR_RB (APR_READ|APR_BINARY) // "rb"
-#define LL_APR_WB (APR_CREATE|APR_TRUNCATE|APR_WRITE|APR_BINARY) // "wb"
-#define LL_APR_RPB (APR_READ|APR_WRITE|APR_BINARY) // "r+b"
-#define LL_APR_WPB (APR_CREATE|APR_TRUNCATE|APR_READ|APR_WRITE|APR_BINARY) // "w+b"
-
-//
-//apr_file manager
-//which: 1)only keeps one file open;
-// 2)closes the open file in the destruction function
-// 3)informs the apr_pool to clean the memory when the file is closed.
-//Note: please close an open file at the earliest convenience.
-// especially do not put some time-costly operations between open() and close().
-// otherwise it might lock the APRFilePool.
-//there are two different apr_pools the APRFile can use:
-// 1, a temperary pool passed to an APRFile function, which is used within this function and only once.
-// 2, a global pool.
-//
-class LLAPRFile
-{
-private:
- apr_file_t* mFile ;
- LLVolatileAPRPool *mCurrentFilePoolp ; //currently in use apr_pool, could be one of them: sAPRFilePoolp, or a temp pool.
-
-public:
- LLAPRFile() ;
- ~LLAPRFile() ;
-
- apr_status_t open(LLVolatileAPRPool* pool, const std::string& filename, apr_int32_t flags, S32* sizep = NULL);
- apr_status_t open(const std::string& filename, apr_int32_t flags, apr_pool_t* pool = NULL, S32* sizep = NULL);
- apr_status_t close() ;
-
- // Returns actual offset, -1 if seek fails
- S32 seek(apr_seek_where_t where, S32 offset);
- apr_status_t eof() { return apr_file_eof(mFile);}
-
- // Returns bytes read/written, 0 if read/write fails:
- S32 read(void* buf, S32 nbytes);
- S32 write(const void* buf, S32 nbytes);
-
- apr_file_t* getFileHandle() {return mFile;}
-
-private:
- apr_pool_t* getAPRFilePool(apr_pool_t* pool) ;
-
-//
-//*******************************************************************************************************************************
-//static components
-//
-public:
- static LLVolatileAPRPool *sAPRFilePoolp ; //a global apr_pool for APRFile, which is used only when local pool does not exist.
-
-private:
- static apr_file_t* open(const std::string& filename, LLVolatileAPRPool* pool, apr_int32_t flags);
- static apr_status_t close(apr_file_t* file, LLVolatileAPRPool* pool) ;
- static S32 seek(apr_file_t* file, apr_seek_where_t where, S32 offset);
-public:
- // returns false if failure:
- static bool remove(const std::string& filename, LLVolatileAPRPool* pool = NULL);
- static bool rename(const std::string& filename, const std::string& newname, LLVolatileAPRPool* pool = NULL);
- static bool isExist(const std::string& filename, LLVolatileAPRPool* pool = NULL, apr_int32_t flags = APR_READ);
- static S32 size(const std::string& filename, LLVolatileAPRPool* pool = NULL);
- static bool makeDir(const std::string& dirname, LLVolatileAPRPool* pool = NULL);
- static bool removeDir(const std::string& dirname, LLVolatileAPRPool* pool = NULL);
-
- // Returns bytes read/written, 0 if read/write fails:
- static S32 readEx(const std::string& filename, void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool = NULL);
- static S32 writeEx(const std::string& filename, void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool = NULL);
-//*******************************************************************************************************************************
-};
-
-/**
- * @brief Function which approprately logs error or remains quiet on
- * APR_SUCCESS.
- * @return Returns <code>true</code> if status is an error condition.
- */
-bool ll_apr_warn_status(apr_status_t status);
-
-void ll_apr_assert_status(apr_status_t status);
-
-extern "C" apr_pool_t* gAPRPoolp; // Global APR memory pool
-
-#endif // LL_LLAPR_H
+/**
+ * @file llapr.h
+ * @author Phoenix
+ * @date 2004-11-28
+ * @brief Helper functions for using the apache portable runtime library.
+ *
+ * $LicenseInfo:firstyear=2004&license=viewergpl$
+ *
+ * Copyright (c) 2004-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLAPR_H
+#define LL_LLAPR_H
+
+#if LL_LINUX || LL_SOLARIS
+#include <sys/param.h> // Need PATH_MAX in APR headers...
+#endif
+
+#include <boost/noncopyable.hpp>
+
+#include "apr_thread_proc.h"
+#include "apr_thread_mutex.h"
+#include "apr_getopt.h"
+#include "apr_signal.h"
+#include "apr_atomic.h"
+#include "llstring.h"
+
+extern LL_COMMON_API apr_thread_mutex_t* gLogMutexp;
+extern apr_thread_mutex_t* gCallStacksLogMutexp;
+
+/**
+ * @brief initialize the common apr constructs -- apr itself, the
+ * global pool, and a mutex.
+ */
+void LL_COMMON_API ll_init_apr();
+
+/**
+ * @brief Cleanup those common apr constructs.
+ */
+void LL_COMMON_API ll_cleanup_apr();
+
+//
+//LL apr_pool
+//manage apr_pool_t, destroy allocated apr_pool in the destruction function.
+//
+class LL_COMMON_API LLAPRPool
+{
+public:
+ LLAPRPool(apr_pool_t *parent = NULL, apr_size_t size = 0, BOOL releasePoolFlag = TRUE) ;
+ ~LLAPRPool() ;
+
+ apr_pool_t* getAPRPool() ;
+ apr_status_t getStatus() {return mStatus ; }
+
+protected:
+ void releaseAPRPool() ;
+ void createAPRPool() ;
+
+protected:
+ apr_pool_t* mPool ; //pointing to an apr_pool
+ apr_pool_t* mParent ; //parent pool
+ apr_size_t mMaxSize ; //max size of mPool, mPool should return memory to system if allocated memory beyond this limit. However it seems not to work.
+ apr_status_t mStatus ; //status when creating the pool
+ BOOL mReleasePoolFlag ; //if set, mPool is destroyed when LLAPRPool is deleted. default value is true.
+};
+
+//
+//volatile LL apr_pool
+//which clears memory automatically.
+//so it can not hold static data or data after memory is cleared
+//
+class LL_COMMON_API LLVolatileAPRPool : public LLAPRPool
+{
+public:
+ LLVolatileAPRPool(apr_pool_t *parent = NULL, apr_size_t size = 0, BOOL releasePoolFlag = TRUE);
+ ~LLVolatileAPRPool(){}
+
+ apr_pool_t* getVolatileAPRPool() ;
+
+ void clearVolatileAPRPool() ;
+
+ BOOL isFull() ;
+ BOOL isEmpty() {return !mNumActiveRef ;}
+private:
+ S32 mNumActiveRef ; //number of active pointers pointing to the apr_pool.
+ S32 mNumTotalRef ; //number of total pointers pointing to the apr_pool since last creating.
+} ;
+
+/**
+ * @class LLScopedLock
+ * @brief Small class to help lock and unlock mutexes.
+ *
+ * This class is used to have a stack level lock once you already have
+ * an apr mutex handy. The constructor handles the lock, and the
+ * destructor handles the unlock. Instances of this class are
+ * <b>not</b> thread safe.
+ */
+class LL_COMMON_API LLScopedLock : private boost::noncopyable
+{
+public:
+ /**
+ * @brief Constructor which accepts a mutex, and locks it.
+ *
+ * @param mutex An allocated APR mutex. If you pass in NULL,
+ * this wrapper will not lock.
+ */
+ LLScopedLock(apr_thread_mutex_t* mutex);
+
+ /**
+ * @brief Destructor which unlocks the mutex if still locked.
+ */
+ ~LLScopedLock();
+
+ /**
+ * @brief Check lock.
+ */
+ bool isLocked() const { return mLocked; }
+
+ /**
+ * @brief This method unlocks the mutex.
+ */
+ void unlock();
+
+protected:
+ bool mLocked;
+ apr_thread_mutex_t* mMutex;
+};
+
+template <typename Type> class LLAtomic32
+{
+public:
+ LLAtomic32<Type>() {};
+ LLAtomic32<Type>(Type x) {apr_atomic_set32(&mData, apr_uint32_t(x)); };
+ ~LLAtomic32<Type>() {};
+
+ operator const Type() { apr_uint32_t data = apr_atomic_read32(&mData); return Type(data); }
+ Type operator =(const Type& x) { apr_atomic_set32(&mData, apr_uint32_t(x)); return Type(mData); }
+ void operator -=(Type x) { apr_atomic_sub32(&mData, apr_uint32_t(x)); }
+ void operator +=(Type x) { apr_atomic_add32(&mData, apr_uint32_t(x)); }
+ Type operator ++(int) { return apr_atomic_inc32(&mData); } // Type++
+ Type operator --(int) { return apr_atomic_dec32(&mData); } // Type--
+
+private:
+ apr_uint32_t mData;
+};
+
+typedef LLAtomic32<U32> LLAtomicU32;
+typedef LLAtomic32<S32> LLAtomicS32;
+
+// File IO convenience functions.
+// Returns NULL if the file fails to openm sets *sizep to file size of not NULL
+// abbreviated flags
+#define LL_APR_R (APR_READ) // "r"
+#define LL_APR_W (APR_CREATE|APR_TRUNCATE|APR_WRITE) // "w"
+#define LL_APR_RB (APR_READ|APR_BINARY) // "rb"
+#define LL_APR_WB (APR_CREATE|APR_TRUNCATE|APR_WRITE|APR_BINARY) // "wb"
+#define LL_APR_RPB (APR_READ|APR_WRITE|APR_BINARY) // "r+b"
+#define LL_APR_WPB (APR_CREATE|APR_TRUNCATE|APR_READ|APR_WRITE|APR_BINARY) // "w+b"
+
+//
+//apr_file manager
+//which: 1)only keeps one file open;
+// 2)closes the open file in the destruction function
+// 3)informs the apr_pool to clean the memory when the file is closed.
+//Note: please close an open file at the earliest convenience.
+// especially do not put some time-costly operations between open() and close().
+// otherwise it might lock the APRFilePool.
+//there are two different apr_pools the APRFile can use:
+// 1, a temperary pool passed to an APRFile function, which is used within this function and only once.
+// 2, a global pool.
+//
+class LL_COMMON_API LLAPRFile
+{
+private:
+ apr_file_t* mFile ;
+ LLVolatileAPRPool *mCurrentFilePoolp ; //currently in use apr_pool, could be one of them: sAPRFilePoolp, or a temp pool.
+
+public:
+ LLAPRFile() ;
+ ~LLAPRFile() ;
+
+ apr_status_t open(LLVolatileAPRPool* pool, const std::string& filename, apr_int32_t flags, S32* sizep = NULL);
+ apr_status_t open(const std::string& filename, apr_int32_t flags, apr_pool_t* pool = NULL, S32* sizep = NULL);
+ apr_status_t close() ;
+
+ // Returns actual offset, -1 if seek fails
+ S32 seek(apr_seek_where_t where, S32 offset);
+ apr_status_t eof() { return apr_file_eof(mFile);}
+
+ // Returns bytes read/written, 0 if read/write fails:
+ S32 read(void* buf, S32 nbytes);
+ S32 write(const void* buf, S32 nbytes);
+
+ apr_file_t* getFileHandle() {return mFile;}
+
+private:
+ apr_pool_t* getAPRFilePool(apr_pool_t* pool) ;
+
+//
+//*******************************************************************************************************************************
+//static components
+//
+public:
+ static LLVolatileAPRPool *sAPRFilePoolp ; //a global apr_pool for APRFile, which is used only when local pool does not exist.
+
+private:
+ static apr_file_t* open(const std::string& filename, LLVolatileAPRPool* pool, apr_int32_t flags);
+ static apr_status_t close(apr_file_t* file, LLVolatileAPRPool* pool) ;
+ static S32 seek(apr_file_t* file, apr_seek_where_t where, S32 offset);
+public:
+ // returns false if failure:
+ static bool remove(const std::string& filename, LLVolatileAPRPool* pool = NULL);
+ static bool rename(const std::string& filename, const std::string& newname, LLVolatileAPRPool* pool = NULL);
+ static bool isExist(const std::string& filename, LLVolatileAPRPool* pool = NULL, apr_int32_t flags = APR_READ);
+ static S32 size(const std::string& filename, LLVolatileAPRPool* pool = NULL);
+ static bool makeDir(const std::string& dirname, LLVolatileAPRPool* pool = NULL);
+ static bool removeDir(const std::string& dirname, LLVolatileAPRPool* pool = NULL);
+
+ // Returns bytes read/written, 0 if read/write fails:
+ static S32 readEx(const std::string& filename, void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool = NULL);
+ static S32 writeEx(const std::string& filename, void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool = NULL);
+//*******************************************************************************************************************************
+};
+
+/**
+ * @brief Function which approprately logs error or remains quiet on
+ * APR_SUCCESS.
+ * @return Returns <code>true</code> if status is an error condition.
+ */
+bool LL_COMMON_API ll_apr_warn_status(apr_status_t status);
+
+void LL_COMMON_API ll_apr_assert_status(apr_status_t status);
+
+extern "C" LL_COMMON_API apr_pool_t* gAPRPoolp; // Global APR memory pool
+
+#endif // LL_LLAPR_H
diff --git a/indra/llcommon/llassettype.cpp b/indra/llcommon/llassettype.cpp
index d57494b444..f3aff332ba 100644
--- a/indra/llcommon/llassettype.cpp
+++ b/indra/llcommon/llassettype.cpp
@@ -290,6 +290,7 @@ bool LLAssetType::lookupIsEnsembleCategoryType(EType asset_type)
asset_type <= AT_FOLDER_ENSEMBLE_END);
}
+
// static. Generate a good default description
void LLAssetType::generateDescriptionFor(LLAssetType::EType asset_type,
std::string& description)
diff --git a/indra/llcommon/llassettype.h b/indra/llcommon/llassettype.h
index a1154d16e3..973b10ea6f 100644
--- a/indra/llcommon/llassettype.h
+++ b/indra/llcommon/llassettype.h
@@ -37,7 +37,7 @@
#include "stdenums.h" // for EDragAndDropType
-class LLAssetType
+class LL_COMMON_API LLAssetType
{
public:
enum EType
@@ -152,6 +152,7 @@ public:
// Mesh data in our proprietary SLM format
AT_COUNT = 50,
+
// +*********************************************************+
// | TO ADD AN ELEMENT TO THIS ENUM: |
// +*********************************************************+
diff --git a/indra/llcommon/llbase32.h b/indra/llcommon/llbase32.h
index 63a93e11ab..0697f7b8e2 100644
--- a/indra/llcommon/llbase32.h
+++ b/indra/llcommon/llbase32.h
@@ -32,9 +32,9 @@
*/
#ifndef LLBASE32_H
-#define LLBASE32_h
+#define LLBASE32_H
-class LLBase32
+class LL_COMMON_API LLBase32
{
public:
static std::string encode(const U8* input, size_t input_size);
diff --git a/indra/llcommon/llbase64.h b/indra/llcommon/llbase64.h
index 58414bba8b..c48fea2478 100644
--- a/indra/llcommon/llbase64.h
+++ b/indra/llcommon/llbase64.h
@@ -32,9 +32,9 @@
*/
#ifndef LLBASE64_H
-#define LLBASE64_h
+#define LLBASE64_H
-class LLBase64
+class LL_COMMON_API LLBase64
{
public:
static std::string encode(const U8* input, size_t input_size);
diff --git a/indra/llcommon/llcommon.h b/indra/llcommon/llcommon.h
index a1808e8a6c..b36471f9f8 100644
--- a/indra/llcommon/llcommon.h
+++ b/indra/llcommon/llcommon.h
@@ -37,7 +37,7 @@
#include "lltimer.h"
#include "llfile.h"
-class LLCommon
+class LL_COMMON_API LLCommon
{
public:
static void initClass();
diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp
new file mode 100644
index 0000000000..377bfaa247
--- /dev/null
+++ b/indra/llcommon/llcoros.cpp
@@ -0,0 +1,137 @@
+/**
+ * @file llcoros.cpp
+ * @author Nat Goodspeed
+ * @date 2009-06-03
+ * @brief Implementation for llcoros.
+ *
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ * Copyright (c) 2009, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+#include "llcoros.h"
+// STL headers
+// std headers
+// external library headers
+#include <boost/bind.hpp>
+// other Linden headers
+#include "llevents.h"
+#include "llerror.h"
+#include "stringize.h"
+
+LLCoros::LLCoros()
+{
+ // Register our cleanup() method for "mainloop" ticks
+ LLEventPumps::instance().obtain("mainloop").listen(
+ "LLCoros", boost::bind(&LLCoros::cleanup, this, _1));
+}
+
+bool LLCoros::cleanup(const LLSD&)
+{
+ // Walk the mCoros map, checking and removing completed coroutines.
+ for (CoroMap::iterator mi(mCoros.begin()), mend(mCoros.end()); mi != mend; )
+ {
+ // Has this coroutine exited (normal return, exception, exit() call)
+ // since last tick?
+ if (mi->second->exited())
+ {
+ LL_INFOS("LLCoros") << "LLCoros: cleaning up coroutine " << mi->first << LL_ENDL;
+ // The erase() call will invalidate its passed iterator value --
+ // so increment mi FIRST -- but pass its original value to
+ // erase(). This is what postincrement is all about.
+ mCoros.erase(mi++);
+ }
+ else
+ {
+ // Still live, just skip this entry as if incrementing at the top
+ // of the loop as usual.
+ ++mi;
+ }
+ }
+ return false;
+}
+
+std::string LLCoros::generateDistinctName(const std::string& prefix) const
+{
+ // Allowing empty name would make getName()'s not-found return ambiguous.
+ if (prefix.empty())
+ {
+ LL_ERRS("LLCoros") << "LLCoros::launch(): pass non-empty name string" << LL_ENDL;
+ }
+
+ // If the specified name isn't already in the map, just use that.
+ std::string name(prefix);
+
+ // Find the lowest numeric suffix that doesn't collide with an existing
+ // entry. Start with 2 just to make it more intuitive for any interested
+ // parties: e.g. "joe", "joe2", "joe3"...
+ for (int i = 2; ; name = STRINGIZE(prefix << i++))
+ {
+ if (mCoros.find(name) == mCoros.end())
+ {
+ LL_INFOS("LLCoros") << "LLCoros: launching coroutine " << name << LL_ENDL;
+ return name;
+ }
+ }
+}
+
+bool LLCoros::kill(const std::string& name)
+{
+ CoroMap::iterator found = mCoros.find(name);
+ if (found == mCoros.end())
+ {
+ return false;
+ }
+ // Because this is a boost::ptr_map, erasing the map entry also destroys
+ // the referenced heap object, in this case the boost::coroutine object,
+ // which will terminate the coroutine.
+ mCoros.erase(found);
+ return true;
+}
+
+std::string LLCoros::getNameByID(const void* self_id) const
+{
+ // Walk the existing coroutines, looking for one from which the 'self_id'
+ // passed to us comes.
+ for (CoroMap::const_iterator mi(mCoros.begin()), mend(mCoros.end()); mi != mend; ++mi)
+ {
+ namespace coro_private = boost::coroutines::detail;
+ if (static_cast<void*>(coro_private::coroutine_accessor::get_impl(const_cast<coro&>(*mi->second)).get())
+ == self_id)
+ {
+ return mi->first;
+ }
+ }
+ return "";
+}
+
+/*****************************************************************************
+* MUST BE LAST
+*****************************************************************************/
+// Turn off MSVC optimizations for just LLCoros::launchImpl() -- see
+// DEV-32777. But MSVC doesn't support push/pop for optimization flags as it
+// does for warning suppression, and we really don't want to force
+// optimization ON for other code even in Debug or RelWithDebInfo builds.
+
+#if LL_MSVC
+// work around broken optimizations
+#pragma warning(disable: 4748)
+#pragma optimize("", off)
+#endif // LL_MSVC
+
+std::string LLCoros::launchImpl(const std::string& prefix, coro* newCoro)
+{
+ std::string name(generateDistinctName(prefix));
+ mCoros.insert(name, newCoro);
+ /* Run the coroutine until its first wait, then return here */
+ (*newCoro)(std::nothrow);
+ return name;
+}
+
+#if LL_MSVC
+// reenable optimizations
+#pragma optimize("", on)
+#endif // LL_MSVC
diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h
new file mode 100644
index 0000000000..6c5fa5af6d
--- /dev/null
+++ b/indra/llcommon/llcoros.h
@@ -0,0 +1,149 @@
+/**
+ * @file llcoros.h
+ * @author Nat Goodspeed
+ * @date 2009-06-02
+ * @brief Manage running boost::coroutine instances
+ *
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ * Copyright (c) 2009, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#if ! defined(LL_LLCOROS_H)
+#define LL_LLCOROS_H
+
+#include <boost/coroutine/coroutine.hpp>
+#include "llsingleton.h"
+#include <boost/ptr_container/ptr_map.hpp>
+#include <string>
+#include <boost/preprocessor/repetition/enum_params.hpp>
+#include <boost/preprocessor/repetition/enum_binary_params.hpp>
+#include <boost/preprocessor/iteration/local.hpp>
+#include <stdexcept>
+
+/**
+ * Registry of named Boost.Coroutine instances
+ *
+ * The Boost.Coroutine library supports the general case of a coroutine
+ * accepting arbitrary parameters and yielding multiple (sets of) results. For
+ * such use cases, it's natural for the invoking code to retain the coroutine
+ * instance: the consumer repeatedly calls into the coroutine, perhaps passing
+ * new parameter values, prompting it to yield its next result.
+ *
+ * Our typical coroutine usage is different, though. For us, coroutines
+ * provide an alternative to the @c Responder pattern. Our typical coroutine
+ * has @c void return, invoked in fire-and-forget mode: the handler for some
+ * user gesture launches the coroutine and promptly returns to the main loop.
+ * The coroutine initiates some action that will take multiple frames (e.g. a
+ * capability request), waits for its result, processes it and silently steals
+ * away.
+ *
+ * This usage poses two (related) problems:
+ *
+ * # Who should own the coroutine instance? If it's simply local to the
+ * handler code that launches it, return from the handler will destroy the
+ * coroutine object, terminating the coroutine.
+ * # Once the coroutine terminates, in whatever way, who's responsible for
+ * cleaning up the coroutine object?
+ *
+ * LLCoros is a Singleton collection of currently-active coroutine instances.
+ * Each has a name. You ask LLCoros to launch a new coroutine with a suggested
+ * name prefix; from your prefix it generates a distinct name, registers the
+ * new coroutine and returns the actual name.
+ *
+ * The name can be used to kill off the coroutine prematurely, if needed. It
+ * can also provide diagnostic info: we can look up the name of the
+ * currently-running coroutine.
+ *
+ * Finally, the next frame ("mainloop" event) after the coroutine terminates,
+ * LLCoros will notice its demise and destroy it.
+ */
+class LL_COMMON_API LLCoros: public LLSingleton<LLCoros>
+{
+public:
+ /// Canonical boost::coroutines::coroutine signature we use
+ typedef boost::coroutines::coroutine<void()> coro;
+ /// Canonical 'self' type
+ typedef coro::self self;
+
+ /**
+ * Create and start running a new coroutine with specified name. The name
+ * string you pass is a suggestion; it will be tweaked for uniqueness. The
+ * actual name is returned to you.
+ *
+ * Usage looks like this, for (e.g.) two coroutine parameters:
+ * @code
+ * class MyClass
+ * {
+ * public:
+ * ...
+ * // Do NOT NOT NOT accept reference params other than 'self'!
+ * // Pass by value only!
+ * void myCoroutineMethod(LLCoros::self& self, std::string, LLSD);
+ * ...
+ * };
+ * ...
+ * std::string name = LLCoros::instance().launch(
+ * "mycoro", boost::bind(&MyClass::myCoroutineMethod, this, _1,
+ * "somestring", LLSD(17));
+ * @endcode
+ *
+ * Your function/method must accept LLCoros::self& as its first parameter.
+ * It can accept any other parameters you want -- but ONLY BY VALUE!
+ * Other reference parameters are a BAD IDEA! You Have Been Warned. See
+ * DEV-32777 comments for an explanation.
+ *
+ * Pass a callable that accepts the single LLCoros::self& parameter. It
+ * may work to pass a free function whose only parameter is 'self'; for
+ * all other cases use boost::bind(). Of course, for a non-static class
+ * method, the first parameter must be the class instance. Use the
+ * placeholder _1 for the 'self' parameter. Any other parameters should be
+ * passed via the bind() expression.
+ *
+ * launch() tweaks the suggested name so it won't collide with any
+ * existing coroutine instance, creates the coroutine instance, registers
+ * it with the tweaked name and runs it until its first wait. At that
+ * point it returns the tweaked name.
+ */
+ template <typename CALLABLE>
+ std::string launch(const std::string& prefix, const CALLABLE& callable)
+ {
+ return launchImpl(prefix, new coro(callable));
+ }
+
+ /**
+ * Abort a running coroutine by name. Normally, when a coroutine either
+ * runs to completion or terminates with an exception, LLCoros quietly
+ * cleans it up. This is for use only when you must explicitly interrupt
+ * one prematurely. Returns @c true if the specified name was found and
+ * still running at the time.
+ */
+ bool kill(const std::string& name);
+
+ /**
+ * From within a coroutine, pass its @c self object to look up the
+ * (tweaked) name string by which this coroutine is registered. Returns
+ * the empty string if not found (e.g. if the coroutine was launched by
+ * hand rather than using LLCoros::launch()).
+ */
+ template <typename COROUTINE_SELF>
+ std::string getName(const COROUTINE_SELF& self) const
+ {
+ return getNameByID(self.get_id());
+ }
+
+ /// getName() by self.get_id()
+ std::string getNameByID(const void* self_id) const;
+
+private:
+ friend class LLSingleton<LLCoros>;
+ LLCoros();
+ std::string launchImpl(const std::string& prefix, coro* newCoro);
+ std::string generateDistinctName(const std::string& prefix) const;
+ bool cleanup(const LLSD&);
+
+ typedef boost::ptr_map<std::string, coro> CoroMap;
+ CoroMap mCoros;
+};
+
+#endif /* ! defined(LL_LLCOROS_H) */
diff --git a/indra/llcommon/llcrc.h b/indra/llcommon/llcrc.h
index 27fae7d269..74369062cc 100644
--- a/indra/llcommon/llcrc.h
+++ b/indra/llcommon/llcrc.h
@@ -50,7 +50,7 @@
// llinfos << "File crc: " << crc.getCRC() << llendl;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-class LLCRC
+class LL_COMMON_API LLCRC
{
protected:
U32 mCurrent;
diff --git a/indra/llcommon/llcriticaldamp.h b/indra/llcommon/llcriticaldamp.h
index ad98284a6c..1ea5914b5b 100644
--- a/indra/llcommon/llcriticaldamp.h
+++ b/indra/llcommon/llcriticaldamp.h
@@ -38,7 +38,7 @@
#include "llframetimer.h"
-class LLCriticalDamp
+class LL_COMMON_API LLCriticalDamp
{
public:
LLCriticalDamp();
diff --git a/indra/llcommon/llcursortypes.h b/indra/llcommon/llcursortypes.h
index 35dbeaf16e..a1b8178bfe 100644
--- a/indra/llcommon/llcursortypes.h
+++ b/indra/llcommon/llcursortypes.h
@@ -71,6 +71,6 @@ enum ECursorType {
UI_CURSOR_COUNT // Number of elements in this enum (NOT a cursor)
};
-ECursorType getCursorFromString(const std::string& cursor_string);
+LL_COMMON_API ECursorType getCursorFromString(const std::string& cursor_string);
#endif // LL_LLCURSORTYPES_H
diff --git a/indra/llcommon/lldate.h b/indra/llcommon/lldate.h
index 40b5f782d6..f8b2f2f163 100644
--- a/indra/llcommon/lldate.h
+++ b/indra/llcommon/lldate.h
@@ -46,7 +46,7 @@
*
* The date class represents a point in time after epoch - 1970-01-01.
*/
-class LLDate
+class LL_COMMON_API LLDate
{
public:
/**
@@ -156,10 +156,10 @@ private:
};
// Helper function to stream out a date
-std::ostream& operator<<(std::ostream& s, const LLDate& date);
+LL_COMMON_API std::ostream& operator<<(std::ostream& s, const LLDate& date);
// Helper function to stream in a date
-std::istream& operator>>(std::istream& s, LLDate& date);
+LL_COMMON_API std::istream& operator>>(std::istream& s, LLDate& date);
diff --git a/indra/llcommon/lldependencies.h b/indra/llcommon/lldependencies.h
index 82f53c6e17..e6229db834 100644
--- a/indra/llcommon/lldependencies.h
+++ b/indra/llcommon/lldependencies.h
@@ -81,7 +81,7 @@ struct instance_from_range: public TYPE
* LLDependencies components that should not be reinstantiated for each KEY,
* NODE specialization
*/
-class LLDependenciesBase
+class LL_COMMON_API LLDependenciesBase
{
public:
virtual ~LLDependenciesBase() {}
diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h
index 37e922d4b7..5a4c644859 100644
--- a/indra/llcommon/llerror.h
+++ b/indra/llcommon/llerror.h
@@ -131,7 +131,7 @@ namespace LLError
class CallSite;
- class Log
+ class LL_COMMON_API Log
{
public:
static bool shouldLog(CallSite&);
@@ -140,7 +140,7 @@ namespace LLError
static void flush(std::ostringstream*, const CallSite&);
};
- class CallSite
+ class LL_COMMON_API CallSite
{
// Represents a specific place in the code where a message is logged
// This is public because it is used by the macros below. It is not
@@ -189,7 +189,7 @@ namespace LLError
//LLCallStacks is designed not to be thread-safe.
//so try not to use it in multiple parallel threads at same time.
//Used in a single thread at a time is fine.
- class LLCallStacks
+ class LL_COMMON_API LLCallStacks
{
private:
static char** sBuffer ;
diff --git a/indra/llcommon/llerrorcontrol.h b/indra/llcommon/llerrorcontrol.h
index fab0a1ef9f..233e9d3389 100644
--- a/indra/llcommon/llerrorcontrol.h
+++ b/indra/llcommon/llerrorcontrol.h
@@ -63,12 +63,12 @@ public:
namespace LLError
{
- void initForServer(const std::string& identity);
+ LL_COMMON_API void initForServer(const std::string& identity);
// resets all logging settings to defaults needed by server processes
// logs to stderr, syslog, and windows debug log
// the identity string is used for in the syslog
- void initForApplication(const std::string& dir);
+ LL_COMMON_API void initForApplication(const std::string& dir);
// resets all logging settings to defaults needed by applicaitons
// logs to stderr and windows debug log
// sets up log configuration from the file logcontrol.xml in dir
@@ -79,14 +79,14 @@ namespace LLError
Setting a level means log messages at that level or above.
*/
- void setPrintLocation(bool);
- void setDefaultLevel(LLError::ELevel);
- void setFunctionLevel(const std::string& function_name, LLError::ELevel);
- void setClassLevel(const std::string& class_name, LLError::ELevel);
- void setFileLevel(const std::string& file_name, LLError::ELevel);
- void setTagLevel(const std::string& file_name, LLError::ELevel);
+ LL_COMMON_API void setPrintLocation(bool);
+ LL_COMMON_API void setDefaultLevel(LLError::ELevel);
+ LL_COMMON_API void setFunctionLevel(const std::string& function_name, LLError::ELevel);
+ LL_COMMON_API void setClassLevel(const std::string& class_name, LLError::ELevel);
+ LL_COMMON_API void setFileLevel(const std::string& file_name, LLError::ELevel);
+ LL_COMMON_API void setTagLevel(const std::string& file_name, LLError::ELevel);
- void configure(const LLSD&);
+ LL_COMMON_API void configure(const LLSD&);
// the LLSD can configure all of the settings
// usually read automatically from the live errorlog.xml file
@@ -96,21 +96,21 @@ namespace LLError
*/
typedef boost::function<void(const std::string&)> FatalFunction;
- void crashAndLoop(const std::string& message);
+ LL_COMMON_API void crashAndLoop(const std::string& message);
// Default fatal function: access null pointer and loops forever
- void setFatalFunction(const FatalFunction&);
+ LL_COMMON_API void setFatalFunction(const FatalFunction&);
// The fatal function will be called when an message of LEVEL_ERROR
// is logged. Note: supressing a LEVEL_ERROR message from being logged
// (by, for example, setting a class level to LEVEL_NONE), will keep
// the that message from causing the fatal funciton to be invoked.
- FatalFunction getFatalFunction();
+ LL_COMMON_API FatalFunction getFatalFunction();
// Retrieve the previously-set FatalFunction
/// temporarily override the FatalFunction for the duration of a
/// particular scope, e.g. for unit tests
- class OverrideFatalFunction
+ class LL_COMMON_API OverrideFatalFunction
{
public:
OverrideFatalFunction(const FatalFunction& func):
@@ -128,15 +128,15 @@ namespace LLError
};
typedef std::string (*TimeFunction)();
- std::string utcTime();
+ LL_COMMON_API std::string utcTime();
- void setTimeFunction(TimeFunction);
+ LL_COMMON_API void setTimeFunction(TimeFunction);
// The function is use to return the current time, formatted for
// display by those error recorders that want the time included.
- class Recorder
+ class LL_COMMON_API Recorder
{
// An object that handles the actual output or error messages.
public:
@@ -150,17 +150,17 @@ namespace LLError
// included in the text of the message
};
- void addRecorder(Recorder*);
- void removeRecorder(Recorder*);
+ LL_COMMON_API void addRecorder(Recorder*);
+ LL_COMMON_API void removeRecorder(Recorder*);
// each error message is passed to each recorder via recordMessage()
- void logToFile(const std::string& filename);
- void logToFixedBuffer(LLLineBuffer*);
+ LL_COMMON_API void logToFile(const std::string& filename);
+ LL_COMMON_API void logToFixedBuffer(LLLineBuffer*);
// Utilities to add recorders for logging to a file or a fixed buffer
// A second call to the same function will remove the logger added
// with the first.
// Passing the empty string or NULL to just removes any prior.
- std::string logFileName();
+ LL_COMMON_API std::string logFileName();
// returns name of current logging file, empty string if none
@@ -169,11 +169,11 @@ namespace LLError
*/
class Settings;
- Settings* saveAndResetSettings();
- void restoreSettings(Settings *);
+ LL_COMMON_API Settings* saveAndResetSettings();
+ LL_COMMON_API void restoreSettings(Settings *);
- std::string abbreviateFile(const std::string& filePath);
- int shouldLogCallCount();
+ LL_COMMON_API std::string abbreviateFile(const std::string& filePath);
+ LL_COMMON_API int shouldLogCallCount();
};
diff --git a/indra/llcommon/llerrorthread.h b/indra/llcommon/llerrorthread.h
index f1d6ffc34f..3121d29675 100644
--- a/indra/llcommon/llerrorthread.h
+++ b/indra/llcommon/llerrorthread.h
@@ -35,7 +35,7 @@
#include "llthread.h"
-class LLErrorThread : public LLThread
+class LL_COMMON_API LLErrorThread : public LLThread
{
public:
LLErrorThread();
diff --git a/indra/llcommon/llevent.h b/indra/llcommon/llevent.h
index 2cc8577219..0ea7cf4ae8 100644
--- a/indra/llcommon/llevent.h
+++ b/indra/llcommon/llevent.h
@@ -47,7 +47,7 @@ class LLEventDispatcher;
class LLObservable;
// Abstract event. All events derive from LLEvent
-class LLEvent : public LLThreadSafeRefCount
+class LL_COMMON_API LLEvent : public LLThreadSafeRefCount
{
protected:
virtual ~LLEvent();
@@ -75,7 +75,7 @@ private:
};
// Abstract listener. All listeners derive from LLEventListener
-class LLEventListener : public LLThreadSafeRefCount
+class LL_COMMON_API LLEventListener : public LLThreadSafeRefCount
{
protected:
virtual ~LLEventListener();
@@ -92,7 +92,7 @@ public:
};
// A listener which tracks references to it and cleans up when it's deallocated
-class LLSimpleListener : public LLEventListener
+class LL_COMMON_API LLSimpleListener : public LLEventListener
{
public:
void clearDispatchers();
@@ -117,7 +117,7 @@ struct LLListenerEntry
// Base class for a dispatcher - an object which listens
// to events being fired and relays them to their
// appropriate destinations.
-class LLEventDispatcher : public LLThreadSafeRefCount
+class LL_COMMON_API LLEventDispatcher : public LLThreadSafeRefCount
{
protected:
virtual ~LLEventDispatcher();
@@ -160,7 +160,7 @@ private:
// In order for this class to work properly, it needs
// an instance of an LLEventDispatcher to route events to their
// listeners.
-class LLObservable
+class LL_COMMON_API LLObservable
{
public:
// Initialize with the default Dispatcher
diff --git a/indra/llcommon/lleventcoro.cpp b/indra/llcommon/lleventcoro.cpp
new file mode 100644
index 0000000000..d598f1cc4a
--- /dev/null
+++ b/indra/llcommon/lleventcoro.cpp
@@ -0,0 +1,129 @@
+/**
+ * @file lleventcoro.cpp
+ * @author Nat Goodspeed
+ * @date 2009-04-29
+ * @brief Implementation for lleventcoro.
+ *
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ * Copyright (c) 2009, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+#include "lleventcoro.h"
+// STL headers
+#include <map>
+// std headers
+// external library headers
+// other Linden headers
+#include "llsdserialize.h"
+#include "llerror.h"
+#include "llcoros.h"
+
+std::string LLEventDetail::listenerNameForCoroImpl(const void* self_id)
+{
+ // First, if this coroutine was launched by LLCoros::launch(), find that name.
+ std::string name(LLCoros::instance().getNameByID(self_id));
+ if (! name.empty())
+ {
+ return name;
+ }
+ // Apparently this coroutine wasn't launched by LLCoros::launch(). Check
+ // whether we have a memo for this self_id.
+ typedef std::map<const void*, std::string> MapType;
+ static MapType memo;
+ MapType::const_iterator found = memo.find(self_id);
+ if (found != memo.end())
+ {
+ // this coroutine instance has called us before, reuse same name
+ return found->second;
+ }
+ // this is the first time we've been called for this coroutine instance
+ name = LLEventPump::inventName("coro");
+ memo[self_id] = name;
+ LL_INFOS("LLEventCoro") << "listenerNameForCoroImpl(" << self_id << "): inventing coro name '"
+ << name << "'" << LL_ENDL;
+ return name;
+}
+
+void LLEventDetail::storeToLLSDPath(LLSD& dest, const LLSD& rawPath, const LLSD& value)
+{
+ if (rawPath.isUndefined())
+ {
+ // no-op case
+ return;
+ }
+
+ // Arrange to treat rawPath uniformly as an array. If it's not already an
+ // array, store it as the only entry in one.
+ LLSD path;
+ if (rawPath.isArray())
+ {
+ path = rawPath;
+ }
+ else
+ {
+ path.append(rawPath);
+ }
+
+ // Need to indicate a current destination -- but that current destination
+ // needs to change as we step through the path array. Where normally we'd
+ // use an LLSD& to capture a subscripted LLSD lvalue, this time we must
+ // instead use a pointer -- since it must be reassigned.
+ LLSD* pdest = &dest;
+
+ // Now loop through that array
+ for (LLSD::Integer i = 0; i < path.size(); ++i)
+ {
+ if (path[i].isString())
+ {
+ // *pdest is an LLSD map
+ pdest = &((*pdest)[path[i].asString()]);
+ }
+ else if (path[i].isInteger())
+ {
+ // *pdest is an LLSD array
+ pdest = &((*pdest)[path[i].asInteger()]);
+ }
+ else
+ {
+ // What do we do with Real or Array or Map or ...?
+ // As it's a coder error -- not a user error -- rub the coder's
+ // face in it so it gets fixed.
+ LL_ERRS("lleventcoro") << "storeToLLSDPath(" << dest << ", " << rawPath << ", " << value
+ << "): path[" << i << "] bad type " << path[i].type() << LL_ENDL;
+ }
+ }
+
+ // Here *pdest is where we should store value.
+ *pdest = value;
+}
+
+LLSD errorException(const LLEventWithID& result, const std::string& desc)
+{
+ // If the result arrived on the error pump (pump 1), instead of
+ // returning it, deliver it via exception.
+ if (result.second)
+ {
+ throw LLErrorEvent(desc, result.first);
+ }
+ // That way, our caller knows a simple return must be from the reply
+ // pump (pump 0).
+ return result.first;
+}
+
+LLSD errorLog(const LLEventWithID& result, const std::string& desc)
+{
+ // If the result arrived on the error pump (pump 1), log it as a fatal
+ // error.
+ if (result.second)
+ {
+ LL_ERRS("errorLog") << desc << ":" << std::endl;
+ LLSDSerialize::toPrettyXML(result.first, LL_CONT);
+ LL_CONT << LL_ENDL;
+ }
+ // A simple return must therefore be from the reply pump (pump 0).
+ return result.first;
+}
diff --git a/indra/llcommon/lleventcoro.h b/indra/llcommon/lleventcoro.h
new file mode 100644
index 0000000000..c6d9de171d
--- /dev/null
+++ b/indra/llcommon/lleventcoro.h
@@ -0,0 +1,549 @@
+/**
+ * @file lleventcoro.h
+ * @author Nat Goodspeed
+ * @date 2009-04-29
+ * @brief Utilities to interface between coroutines and events.
+ *
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ * Copyright (c) 2009, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#if ! defined(LL_LLEVENTCORO_H)
+#define LL_LLEVENTCORO_H
+
+#include <boost/coroutine/coroutine.hpp>
+#include <boost/coroutine/future.hpp>
+#include <boost/optional.hpp>
+#include <string>
+#include <stdexcept>
+#include "llevents.h"
+#include "llerror.h"
+
+/**
+ * Like LLListenerOrPumpName, this is a class intended for parameter lists:
+ * accept a <tt>const LLEventPumpOrPumpName&</tt> and you can accept either an
+ * <tt>LLEventPump&</tt> or its string name. For a single parameter that could
+ * be either, it's not hard to overload the function -- but as soon as you
+ * want to accept two such parameters, this is cheaper than four overloads.
+ */
+class LLEventPumpOrPumpName
+{
+public:
+ /// Pass an actual LLEventPump&
+ LLEventPumpOrPumpName(LLEventPump& pump):
+ mPump(pump)
+ {}
+ /// Pass the string name of an LLEventPump
+ LLEventPumpOrPumpName(const std::string& pumpname):
+ mPump(LLEventPumps::instance().obtain(pumpname))
+ {}
+ /// Pass string constant name of an LLEventPump. This override must be
+ /// explicit, since otherwise passing <tt>const char*</tt> to a function
+ /// accepting <tt>const LLEventPumpOrPumpName&</tt> would require two
+ /// different implicit conversions: <tt>const char*</tt> -> <tt>const
+ /// std::string&</tt> -> <tt>const LLEventPumpOrPumpName&</tt>.
+ LLEventPumpOrPumpName(const char* pumpname):
+ mPump(LLEventPumps::instance().obtain(pumpname))
+ {}
+ /// Unspecified: "I choose not to identify an LLEventPump."
+ LLEventPumpOrPumpName() {}
+ operator LLEventPump& () const { return *mPump; }
+ LLEventPump& getPump() const { return *mPump; }
+ operator bool() const { return mPump; }
+ bool operator!() const { return ! mPump; }
+
+private:
+ boost::optional<LLEventPump&> mPump;
+};
+
+/// This is an adapter for a signature like void LISTENER(const LLSD&), which
+/// isn't a valid LLEventPump listener: such listeners should return bool.
+template <typename LISTENER>
+class LLVoidListener
+{
+public:
+ LLVoidListener(const LISTENER& listener):
+ mListener(listener)
+ {}
+ bool operator()(const LLSD& event)
+ {
+ mListener(event);
+ // don't swallow the event, let other listeners see it
+ return false;
+ }
+private:
+ LISTENER mListener;
+};
+
+/// LLVoidListener helper function to infer the type of the LISTENER
+template <typename LISTENER>
+LLVoidListener<LISTENER> voidlistener(const LISTENER& listener)
+{
+ return LLVoidListener<LISTENER>(listener);
+}
+
+namespace LLEventDetail
+{
+ /**
+ * waitForEventOn() permits a coroutine to temporarily listen on an
+ * LLEventPump any number of times. We don't really want to have to ask
+ * the caller to label each such call with a distinct string; the whole
+ * point of waitForEventOn() is to present a nice sequential interface to
+ * the underlying LLEventPump-with-named-listeners machinery. So we'll use
+ * LLEventPump::inventName() to generate a distinct name for each
+ * temporary listener. On the other hand, because a given coroutine might
+ * call waitForEventOn() any number of times, we don't really want to
+ * consume an arbitrary number of generated inventName()s: that namespace,
+ * though large, is nonetheless finite. So we memoize an invented name for
+ * each distinct coroutine instance (each different 'self' object). We
+ * can't know the type of 'self', because it depends on the coroutine
+ * body's signature. So we cast its address to void*, looking for distinct
+ * pointer values. Yes, that means that an early coroutine could cache a
+ * value here, then be destroyed, only to be supplanted by a later
+ * coroutine (of the same or different type), and we'll end up
+ * "recognizing" the second one and reusing the listener name -- but
+ * that's okay, since it won't collide with any listener name used by the
+ * earlier coroutine since that earlier coroutine no longer exists.
+ */
+ template <typename COROUTINE_SELF>
+ std::string listenerNameForCoro(COROUTINE_SELF& self)
+ {
+ return listenerNameForCoroImpl(self.get_id());
+ }
+
+ /// Implementation for listenerNameForCoro()
+ LL_COMMON_API std::string listenerNameForCoroImpl(const void* self_id);
+
+ /**
+ * Implement behavior described for postAndWait()'s @a replyPumpNamePath
+ * parameter:
+ *
+ * * If <tt>path.isUndefined()</tt>, do nothing.
+ * * If <tt>path.isString()</tt>, @a dest is an LLSD map: store @a value
+ * into <tt>dest[path.asString()]</tt>.
+ * * If <tt>path.isInteger()</tt>, @a dest is an LLSD array: store @a
+ * value into <tt>dest[path.asInteger()]</tt>.
+ * * If <tt>path.isArray()</tt>, iteratively apply the rules above to step
+ * down through the structure of @a dest. The last array entry in @a
+ * path specifies the entry in the lowest-level structure in @a dest
+ * into which to store @a value.
+ *
+ * @note
+ * In the degenerate case in which @a path is an empty array, @a dest will
+ * @em become @a value rather than @em containing it.
+ */
+ LL_COMMON_API void storeToLLSDPath(LLSD& dest, const LLSD& path, const LLSD& value);
+} // namespace LLEventDetail
+
+/**
+ * Post specified LLSD event on the specified LLEventPump, then wait for a
+ * response on specified other LLEventPump. This is more than mere
+ * convenience: the difference between this function and the sequence
+ * @code
+ * requestPump.post(myEvent);
+ * LLSD reply = waitForEventOn(self, replyPump);
+ * @endcode
+ * is that the sequence above fails if the reply is posted immediately on
+ * @a replyPump, that is, before <tt>requestPump.post()</tt> returns. In the
+ * sequence above, the running coroutine isn't even listening on @a replyPump
+ * until <tt>requestPump.post()</tt> returns and @c waitForEventOn() is
+ * entered. Therefore, the coroutine completely misses an immediate reply
+ * event, making it wait indefinitely.
+ *
+ * By contrast, postAndWait() listens on the @a replyPump @em before posting
+ * the specified LLSD event on the specified @a requestPump.
+ *
+ * @param self The @c self object passed into a coroutine
+ * @param event LLSD data to be posted on @a requestPump
+ * @param requestPump an LLEventPump on which to post @a event. Pass either
+ * the LLEventPump& or its string name. However, if you pass a
+ * default-constructed @c LLEventPumpOrPumpName, we skip the post() call.
+ * @param replyPump an LLEventPump on which postAndWait() will listen for a
+ * reply. Pass either the LLEventPump& or its string name. The calling
+ * coroutine will wait until that reply arrives. (If you're concerned about a
+ * reply that might not arrive, please see also LLEventTimeout.)
+ * @param replyPumpNamePath specifies the location within @a event in which to
+ * store <tt>replyPump.getName()</tt>. This is a strictly optional convenience
+ * feature; obviously you can store the name in @a event "by hand" if desired.
+ * @a replyPumpNamePath can be specified in any of four forms:
+ * * @c isUndefined() (default-constructed LLSD object): do nothing. This is
+ * the default behavior if you omit @a replyPumpNamePath.
+ * * @c isInteger(): @a event is an array. Store <tt>replyPump.getName()</tt>
+ * in <tt>event[replyPumpNamePath.asInteger()]</tt>.
+ * * @c isString(): @a event is a map. Store <tt>replyPump.getName()</tt> in
+ * <tt>event[replyPumpNamePath.asString()]</tt>.
+ * * @c isArray(): @a event has several levels of structure, e.g. map of
+ * maps, array of arrays, array of maps, map of arrays, ... Store
+ * <tt>replyPump.getName()</tt> in
+ * <tt>event[replyPumpNamePath[0]][replyPumpNamePath[1]]...</tt> In other
+ * words, examine each array entry in @a replyPumpNamePath in turn. If it's an
+ * <tt>LLSD::String</tt>, the current level of @a event is a map; step down to
+ * that map entry. If it's an <tt>LLSD::Integer</tt>, the current level of @a
+ * event is an array; step down to that array entry. The last array entry in
+ * @a replyPumpNamePath specifies the entry in the lowest-level structure in
+ * @a event into which to store <tt>replyPump.getName()</tt>.
+ */
+template <typename SELF>
+LLSD postAndWait(SELF& self, const LLSD& event, const LLEventPumpOrPumpName& requestPump,
+ const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath=LLSD())
+{
+ // declare the future
+ boost::coroutines::future<LLSD> future(self);
+ // make a callback that will assign a value to the future, and listen on
+ // the specified LLEventPump with that callback
+ std::string listenerName(LLEventDetail::listenerNameForCoro(self));
+ LLTempBoundListener connection(
+ replyPump.getPump().listen(listenerName,
+ voidlistener(boost::coroutines::make_callback(future))));
+ // skip the "post" part if requestPump is default-constructed
+ if (requestPump)
+ {
+ // If replyPumpNamePath is non-empty, store the replyPump name in the
+ // request event.
+ LLSD modevent(event);
+ LLEventDetail::storeToLLSDPath(modevent, replyPumpNamePath, replyPump.getPump().getName());
+ LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName
+ << " posting to " << requestPump.getPump().getName()
+ << ": " << modevent << LL_ENDL;
+ requestPump.getPump().post(modevent);
+ }
+ LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName
+ << " about to wait on LLEventPump " << replyPump.getPump().getName()
+ << LL_ENDL;
+ // trying to dereference ("resolve") the future makes us wait for it
+ LLSD value(*future);
+ LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName
+ << " resuming with " << value << LL_ENDL;
+ // returning should disconnect the connection
+ return value;
+}
+
+/// Wait for the next event on the specified LLEventPump. Pass either the
+/// LLEventPump& or its string name.
+template <typename SELF>
+LLSD waitForEventOn(SELF& self, const LLEventPumpOrPumpName& pump)
+{
+ // This is now a convenience wrapper for postAndWait().
+ return postAndWait(self, LLSD(), LLEventPumpOrPumpName(), pump);
+}
+
+/// return type for two-pump variant of waitForEventOn()
+typedef std::pair<LLSD, int> LLEventWithID;
+
+namespace LLEventDetail
+{
+ /**
+ * This helper is specifically for the two-pump version of waitForEventOn().
+ * We use a single future object, but we want to listen on two pumps with it.
+ * Since we must still adapt from (the callable constructed by)
+ * boost::coroutines::make_callback() (void return) to provide an event
+ * listener (bool return), we've adapted LLVoidListener for the purpose. The
+ * basic idea is that we construct a distinct instance of WaitForEventOnHelper
+ * -- binding different instance data -- for each of the pumps. Then, when a
+ * pump delivers an LLSD value to either WaitForEventOnHelper, it can combine
+ * that LLSD with its discriminator to feed the future object.
+ */
+ template <typename LISTENER>
+ class WaitForEventOnHelper
+ {
+ public:
+ WaitForEventOnHelper(const LISTENER& listener, int discriminator):
+ mListener(listener),
+ mDiscrim(discriminator)
+ {}
+ // this signature is required for an LLEventPump listener
+ bool operator()(const LLSD& event)
+ {
+ // our future object is defined to accept LLEventWithID
+ mListener(LLEventWithID(event, mDiscrim));
+ // don't swallow the event, let other listeners see it
+ return false;
+ }
+ private:
+ LISTENER mListener;
+ const int mDiscrim;
+ };
+
+ /// WaitForEventOnHelper type-inference helper
+ template <typename LISTENER>
+ WaitForEventOnHelper<LISTENER> wfeoh(const LISTENER& listener, int discriminator)
+ {
+ return WaitForEventOnHelper<LISTENER>(listener, discriminator);
+ }
+} // namespace LLEventDetail
+
+/**
+ * This function waits for a reply on either of two specified LLEventPumps.
+ * Otherwise, it closely resembles postAndWait(); please see the documentation
+ * for that function for detailed parameter info.
+ *
+ * While we could have implemented the single-pump variant in terms of this
+ * one, there's enough added complexity here to make it worthwhile to give the
+ * single-pump variant its own straightforward implementation. Conversely,
+ * though we could use preprocessor logic to generate n-pump overloads up to
+ * BOOST_COROUTINE_WAIT_MAX, we don't foresee a use case. This two-pump
+ * overload exists because certain event APIs are defined in terms of a reply
+ * LLEventPump and an error LLEventPump.
+ *
+ * The LLEventWithID return value provides not only the received event, but
+ * the index of the pump on which it arrived (0 or 1).
+ *
+ * @note
+ * I'd have preferred to overload the name postAndWait() for both signatures.
+ * But consider the following ambiguous call:
+ * @code
+ * postAndWait(self, LLSD(), requestPump, replyPump, "someString");
+ * @endcode
+ * "someString" could be converted to either LLSD (@a replyPumpNamePath for
+ * the single-pump function) or LLEventOrPumpName (@a replyPump1 for two-pump
+ * function).
+ *
+ * It seems less burdensome to write postAndWait2() than to write either
+ * LLSD("someString") or LLEventOrPumpName("someString").
+ */
+template <typename SELF>
+LLEventWithID postAndWait2(SELF& self, const LLSD& event,
+ const LLEventPumpOrPumpName& requestPump,
+ const LLEventPumpOrPumpName& replyPump0,
+ const LLEventPumpOrPumpName& replyPump1,
+ const LLSD& replyPump0NamePath=LLSD(),
+ const LLSD& replyPump1NamePath=LLSD())
+{
+ // declare the future
+ boost::coroutines::future<LLEventWithID> future(self);
+ // either callback will assign a value to this future; listen on
+ // each specified LLEventPump with a callback
+ std::string name(LLEventDetail::listenerNameForCoro(self));
+ LLTempBoundListener connection0(
+ replyPump0.getPump().listen(name + "a",
+ LLEventDetail::wfeoh(boost::coroutines::make_callback(future), 0)));
+ LLTempBoundListener connection1(
+ replyPump1.getPump().listen(name + "b",
+ LLEventDetail::wfeoh(boost::coroutines::make_callback(future), 1)));
+ // skip the "post" part if requestPump is default-constructed
+ if (requestPump)
+ {
+ // If either replyPumpNamePath is non-empty, store the corresponding
+ // replyPump name in the request event.
+ LLSD modevent(event);
+ LLEventDetail::storeToLLSDPath(modevent, replyPump0NamePath,
+ replyPump0.getPump().getName());
+ LLEventDetail::storeToLLSDPath(modevent, replyPump1NamePath,
+ replyPump1.getPump().getName());
+ LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name
+ << " posting to " << requestPump.getPump().getName()
+ << ": " << modevent << LL_ENDL;
+ requestPump.getPump().post(modevent);
+ }
+ LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name
+ << " about to wait on LLEventPumps " << replyPump0.getPump().getName()
+ << ", " << replyPump1.getPump().getName() << LL_ENDL;
+ // trying to dereference ("resolve") the future makes us wait for it
+ LLEventWithID value(*future);
+ LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << name
+ << " resuming with (" << value.first << ", " << value.second << ")"
+ << LL_ENDL;
+ // returning should disconnect both connections
+ return value;
+}
+
+/**
+ * Wait for the next event on either of two specified LLEventPumps.
+ */
+template <typename SELF>
+LLEventWithID
+waitForEventOn(SELF& self,
+ const LLEventPumpOrPumpName& pump0, const LLEventPumpOrPumpName& pump1)
+{
+ // This is now a convenience wrapper for postAndWait2().
+ return postAndWait2(self, LLSD(), LLEventPumpOrPumpName(), pump0, pump1);
+}
+
+/**
+ * Helper for the two-pump variant of waitForEventOn(), e.g.:
+ *
+ * @code
+ * LLSD reply = errorException(waitForEventOn(self, replyPump, errorPump),
+ * "error response from login.cgi");
+ * @endcode
+ *
+ * Examines an LLEventWithID, assuming that the second pump (pump 1) is
+ * listening for an error indication. If the incoming data arrived on pump 1,
+ * throw an LLErrorEvent exception. If the incoming data arrived on pump 0,
+ * just return it. Since a normal return can only be from pump 0, we no longer
+ * need the LLEventWithID's discriminator int; we can just return the LLSD.
+ *
+ * @note I'm not worried about introducing the (fairly generic) name
+ * errorException() into global namespace, because how many other overloads of
+ * the same name are going to accept an LLEventWithID parameter?
+ */
+LLSD errorException(const LLEventWithID& result, const std::string& desc);
+
+/**
+ * Exception thrown by errorException(). We don't call this LLEventError
+ * because it's not an error in event processing: rather, this exception
+ * announces an event that bears error information (for some other API).
+ */
+class LL_COMMON_API LLErrorEvent: public std::runtime_error
+{
+public:
+ LLErrorEvent(const std::string& what, const LLSD& data):
+ std::runtime_error(what),
+ mData(data)
+ {}
+ virtual ~LLErrorEvent() throw() {}
+
+ LLSD getData() const { return mData; }
+
+private:
+ LLSD mData;
+};
+
+/**
+ * Like errorException(), save that this trips a fatal error using LL_ERRS
+ * rather than throwing an exception.
+ */
+LL_COMMON_API LLSD errorLog(const LLEventWithID& result, const std::string& desc);
+
+/**
+ * Certain event APIs require the name of an LLEventPump on which they should
+ * post results. While it works to invent a distinct name and let
+ * LLEventPumps::obtain() instantiate the LLEventPump as a "named singleton,"
+ * in a certain sense it's more robust to instantiate a local LLEventPump and
+ * provide its name instead. This class packages the following idiom:
+ *
+ * 1. Instantiate a local LLCoroEventPump, with an optional name prefix.
+ * 2. Provide its actual name to the event API in question as the name of the
+ * reply LLEventPump.
+ * 3. Initiate the request to the event API.
+ * 4. Call your LLEventTempStream's wait() method to wait for the reply.
+ * 5. Let the LLCoroEventPump go out of scope.
+ */
+class LL_COMMON_API LLCoroEventPump
+{
+public:
+ LLCoroEventPump(const std::string& name="coro"):
+ mPump(name, true) // allow tweaking the pump instance name
+ {}
+ /// It's typical to request the LLEventPump name to direct an event API to
+ /// send its response to this pump.
+ std::string getName() const { return mPump.getName(); }
+ /// Less typically, we'd request the pump itself for some reason.
+ LLEventPump& getPump() { return mPump; }
+
+ /**
+ * Wait for an event on this LLEventPump.
+ *
+ * @note
+ * The other major usage pattern we considered was to bind @c self at
+ * LLCoroEventPump construction time, which would avoid passing the
+ * parameter to each wait() call. But if we were going to bind @c self as
+ * a class member, we'd need to specify a class template parameter
+ * indicating its type. The big advantage of passing it to the wait() call
+ * is that the type can be implicit.
+ */
+ template <typename SELF>
+ LLSD wait(SELF& self)
+ {
+ return waitForEventOn(self, mPump);
+ }
+
+ template <typename SELF>
+ LLSD postAndWait(SELF& self, const LLSD& event, const LLEventPumpOrPumpName& requestPump,
+ const LLSD& replyPumpNamePath=LLSD())
+ {
+ return ::postAndWait(self, event, requestPump, mPump, replyPumpNamePath);
+ }
+
+private:
+ LLEventStream mPump;
+};
+
+/**
+ * Other event APIs require the names of two different LLEventPumps: one for
+ * success response, the other for error response. Extend LLCoroEventPump
+ * for the two-pump use case.
+ */
+class LL_COMMON_API LLCoroEventPumps
+{
+public:
+ LLCoroEventPumps(const std::string& name="coro",
+ const std::string& suff0="Reply",
+ const std::string& suff1="Error"):
+ mPump0(name + suff0, true), // allow tweaking the pump instance name
+ mPump1(name + suff1, true)
+ {}
+ /// request pump 0's name
+ std::string getName0() const { return mPump0.getName(); }
+ /// request pump 1's name
+ std::string getName1() const { return mPump1.getName(); }
+ /// request both names
+ std::pair<std::string, std::string> getNames() const
+ {
+ return std::pair<std::string, std::string>(mPump0.getName(), mPump1.getName());
+ }
+
+ /// request pump 0
+ LLEventPump& getPump0() { return mPump0; }
+ /// request pump 1
+ LLEventPump& getPump1() { return mPump1; }
+
+ /// waitForEventOn(self, either of our two LLEventPumps)
+ template <typename SELF>
+ LLEventWithID wait(SELF& self)
+ {
+ return waitForEventOn(self, mPump0, mPump1);
+ }
+
+ /// errorException(wait(self))
+ template <typename SELF>
+ LLSD waitWithException(SELF& self)
+ {
+ return errorException(wait(self), std::string("Error event on ") + getName1());
+ }
+
+ /// errorLog(wait(self))
+ template <typename SELF>
+ LLSD waitWithLog(SELF& self)
+ {
+ return errorLog(wait(self), std::string("Error event on ") + getName1());
+ }
+
+ template <typename SELF>
+ LLEventWithID postAndWait(SELF& self, const LLSD& event,
+ const LLEventPumpOrPumpName& requestPump,
+ const LLSD& replyPump0NamePath=LLSD(),
+ const LLSD& replyPump1NamePath=LLSD())
+ {
+ return postAndWait2(self, event, requestPump, mPump0, mPump1,
+ replyPump0NamePath, replyPump1NamePath);
+ }
+
+ template <typename SELF>
+ LLSD postAndWaitWithException(SELF& self, const LLSD& event,
+ const LLEventPumpOrPumpName& requestPump,
+ const LLSD& replyPump0NamePath=LLSD(),
+ const LLSD& replyPump1NamePath=LLSD())
+ {
+ return errorException(postAndWait(self, event, requestPump,
+ replyPump0NamePath, replyPump1NamePath),
+ std::string("Error event on ") + getName1());
+ }
+
+ template <typename SELF>
+ LLSD postAndWaitWithLog(SELF& self, const LLSD& event,
+ const LLEventPumpOrPumpName& requestPump,
+ const LLSD& replyPump0NamePath=LLSD(),
+ const LLSD& replyPump1NamePath=LLSD())
+ {
+ return errorLog(postAndWait(self, event, requestPump,
+ replyPump0NamePath, replyPump1NamePath),
+ std::string("Error event on ") + getName1());
+ }
+
+private:
+ LLEventStream mPump0, mPump1;
+};
+
+#endif /* ! defined(LL_LLEVENTCORO_H) */
diff --git a/indra/llcommon/lleventdispatcher.cpp b/indra/llcommon/lleventdispatcher.cpp
new file mode 100644
index 0000000000..6b1413d054
--- /dev/null
+++ b/indra/llcommon/lleventdispatcher.cpp
@@ -0,0 +1,133 @@
+/**
+ * @file lleventdispatcher.cpp
+ * @author Nat Goodspeed
+ * @date 2009-06-18
+ * @brief Implementation for lleventdispatcher.
+ *
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ * Copyright (c) 2009, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#if LL_WINDOWS
+#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally
+#endif
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+#include "lleventdispatcher.h"
+// STL headers
+// std headers
+// external library headers
+// other Linden headers
+#include "llevents.h"
+#include "llerror.h"
+#include "llsdutil.h"
+
+LLEventDispatcher::LLEventDispatcher(const std::string& desc, const std::string& key):
+ mDesc(desc),
+ mKey(key)
+{
+}
+
+LLEventDispatcher::~LLEventDispatcher()
+{
+}
+
+/// Register a callable by name
+void LLEventDispatcher::add(const std::string& name, const Callable& callable, const LLSD& required)
+{
+ mDispatch[name] = DispatchMap::mapped_type(callable, required);
+}
+
+void LLEventDispatcher::addFail(const std::string& name, const std::string& classname) const
+{
+ LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << ")::add(" << name
+ << "): " << classname << " is not a subclass "
+ << "of LLEventDispatcher" << LL_ENDL;
+}
+
+/// Unregister a callable
+bool LLEventDispatcher::remove(const std::string& name)
+{
+ DispatchMap::iterator found = mDispatch.find(name);
+ if (found == mDispatch.end())
+ {
+ return false;
+ }
+ mDispatch.erase(found);
+ return true;
+}
+
+/// Call a registered callable with an explicitly-specified name. If no
+/// such callable exists, die with LL_ERRS.
+void LLEventDispatcher::operator()(const std::string& name, const LLSD& event) const
+{
+ if (! attemptCall(name, event))
+ {
+ LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << "): '" << name
+ << "' not found" << LL_ENDL;
+ }
+}
+
+/// Extract the @a key value from the incoming @a event, and call the
+/// callable whose name is specified by that map @a key. If no such
+/// callable exists, die with LL_ERRS.
+void LLEventDispatcher::operator()(const LLSD& event) const
+{
+ // This could/should be implemented in terms of the two-arg overload.
+ // However -- we can produce a more informative error message.
+ std::string name(event[mKey]);
+ if (! attemptCall(name, event))
+ {
+ LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << "): bad " << mKey
+ << " value '" << name << "'" << LL_ENDL;
+ }
+}
+
+bool LLEventDispatcher::attemptCall(const std::string& name, const LLSD& event) const
+{
+ DispatchMap::const_iterator found = mDispatch.find(name);
+ if (found == mDispatch.end())
+ {
+ // The reason we only return false, leaving it up to our caller to die
+ // with LL_ERRS, is that different callers have different amounts of
+ // available information.
+ return false;
+ }
+ // Found the name, so it's plausible to even attempt the call. But first,
+ // validate the syntax of the event itself.
+ std::string mismatch(llsd_matches(found->second.second, event));
+ if (! mismatch.empty())
+ {
+ LL_ERRS("LLEventDispatcher") << "LLEventDispatcher(" << mDesc << ") calling '" << name
+ << "': bad request: " << mismatch << LL_ENDL;
+ }
+ // Event syntax looks good, go for it!
+ (found->second.first)(event);
+ return true; // tell caller we were able to call
+}
+
+LLEventDispatcher::Callable LLEventDispatcher::get(const std::string& name) const
+{
+ DispatchMap::const_iterator found = mDispatch.find(name);
+ if (found == mDispatch.end())
+ {
+ return Callable();
+ }
+ return found->second.first;
+}
+
+LLDispatchListener::LLDispatchListener(const std::string& pumpname, const std::string& key):
+ LLEventDispatcher(pumpname, key),
+ mPump(pumpname, true), // allow tweaking for uniqueness
+ mBoundListener(mPump.listen("self", boost::bind(&LLDispatchListener::process, this, _1)))
+{
+}
+
+bool LLDispatchListener::process(const LLSD& event)
+{
+ (*this)(event);
+ return false;
+}
diff --git a/indra/llcommon/lleventdispatcher.h b/indra/llcommon/lleventdispatcher.h
new file mode 100644
index 0000000000..671f2a4d1c
--- /dev/null
+++ b/indra/llcommon/lleventdispatcher.h
@@ -0,0 +1,130 @@
+/**
+ * @file lleventdispatcher.h
+ * @author Nat Goodspeed
+ * @date 2009-06-18
+ * @brief Central mechanism for dispatching events by string name. This is
+ * useful when you have a single LLEventPump listener on which you can
+ * request different operations, vs. instantiating a different
+ * LLEventPump for each such operation.
+ *
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ * Copyright (c) 2009, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#if ! defined(LL_LLEVENTDISPATCHER_H)
+#define LL_LLEVENTDISPATCHER_H
+
+#include <string>
+#include <map>
+#include <boost/function.hpp>
+#include <boost/bind.hpp>
+#include <typeinfo>
+#include "llevents.h"
+
+class LLSD;
+
+/**
+ * Given an LLSD map, examine a string-valued key and call a corresponding
+ * callable. This class is designed to be contained by an LLEventPump
+ * listener class that will register some of its own methods, though any
+ * callable can be used.
+ */
+class LL_COMMON_API LLEventDispatcher
+{
+public:
+ LLEventDispatcher(const std::string& desc, const std::string& key);
+ virtual ~LLEventDispatcher();
+
+ /// Accept any C++ callable, typically a boost::bind() expression
+ typedef boost::function<void(const LLSD&)> Callable;
+
+ /**
+ * Register a @a callable by @a name. The optional @a required parameter
+ * is used to validate the structure of each incoming event (see
+ * llsd_matches()).
+ */
+ void add(const std::string& name, const Callable& callable, const LLSD& required=LLSD());
+
+ /**
+ * Special case: a subclass of this class can pass an unbound member
+ * function pointer without explicitly specifying the
+ * <tt>boost::bind()</tt> expression.
+ */
+ template <class CLASS>
+ void add(const std::string& name, void (CLASS::*method)(const LLSD&),
+ const LLSD& required=LLSD())
+ {
+ addMethod<CLASS>(name, method, required);
+ }
+
+ /// Overload for both const and non-const methods
+ template <class CLASS>
+ void add(const std::string& name, void (CLASS::*method)(const LLSD&) const,
+ const LLSD& required=LLSD())
+ {
+ addMethod<CLASS>(name, method, required);
+ }
+
+ /// Unregister a callable
+ bool remove(const std::string& name);
+
+ /// Call a registered callable with an explicitly-specified name. If no
+ /// such callable exists, die with LL_ERRS. If the @a event fails to match
+ /// the @a required prototype specified at add() time, die with LL_ERRS.
+ void operator()(const std::string& name, const LLSD& event) const;
+
+ /// Extract the @a key value from the incoming @a event, and call the
+ /// callable whose name is specified by that map @a key. If no such
+ /// callable exists, die with LL_ERRS. If the @a event fails to match the
+ /// @a required prototype specified at add() time, die with LL_ERRS.
+ void operator()(const LLSD& event) const;
+
+ /// Fetch the Callable for the specified name. If no such name was
+ /// registered, return an empty() Callable.
+ Callable get(const std::string& name) const;
+
+private:
+ template <class CLASS, typename METHOD>
+ void addMethod(const std::string& name, const METHOD& method, const LLSD& required)
+ {
+ CLASS* downcast = dynamic_cast<CLASS*>(this);
+ if (! downcast)
+ {
+ addFail(name, typeid(CLASS).name());
+ }
+ else
+ {
+ add(name, boost::bind(method, downcast, _1), required);
+ }
+ }
+ void addFail(const std::string& name, const std::string& classname) const;
+ /// try to dispatch, return @c true if success
+ bool attemptCall(const std::string& name, const LLSD& event) const;
+
+ std::string mDesc, mKey;
+ typedef std::map<std::string, std::pair<Callable, LLSD> > DispatchMap;
+ DispatchMap mDispatch;
+};
+
+/**
+ * Bundle an LLEventPump and a listener with an LLEventDispatcher. A class
+ * that contains (or derives from) LLDispatchListener need only specify the
+ * LLEventPump name and dispatch key, and add() its methods. Incoming events
+ * will automatically be dispatched.
+ */
+class LL_COMMON_API LLDispatchListener: public LLEventDispatcher
+{
+public:
+ LLDispatchListener(const std::string& pumpname, const std::string& key);
+
+ std::string getPumpName() const { return mPump.getName(); }
+
+private:
+ bool process(const LLSD& event);
+
+ LLEventStream mPump;
+ LLTempBoundListener mBoundListener;
+};
+
+#endif /* ! defined(LL_LLEVENTDISPATCHER_H) */
diff --git a/indra/llcommon/lleventfilter.cpp b/indra/llcommon/lleventfilter.cpp
new file mode 100644
index 0000000000..74133781be
--- /dev/null
+++ b/indra/llcommon/lleventfilter.cpp
@@ -0,0 +1,149 @@
+/**
+ * @file lleventfilter.cpp
+ * @author Nat Goodspeed
+ * @date 2009-03-05
+ * @brief Implementation for lleventfilter.
+ *
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ * Copyright (c) 2009, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+#include "lleventfilter.h"
+// STL headers
+// std headers
+// external library headers
+#include <boost/bind.hpp>
+// other Linden headers
+#include "llerror.h" // LL_ERRS
+#include "llsdutil.h" // llsd_matches()
+
+LLEventFilter::LLEventFilter(LLEventPump& source, const std::string& name, bool tweak):
+ LLEventStream(name, tweak)
+{
+ source.listen(getName(), boost::bind(&LLEventFilter::post, this, _1));
+}
+
+LLEventMatching::LLEventMatching(const LLSD& pattern):
+ LLEventFilter("matching"),
+ mPattern(pattern)
+{
+}
+
+LLEventMatching::LLEventMatching(LLEventPump& source, const LLSD& pattern):
+ LLEventFilter(source, "matching"),
+ mPattern(pattern)
+{
+}
+
+bool LLEventMatching::post(const LLSD& event)
+{
+ if (! llsd_matches(mPattern, event).empty())
+ return false;
+
+ return LLEventStream::post(event);
+}
+
+LLEventTimeoutBase::LLEventTimeoutBase():
+ LLEventFilter("timeout")
+{
+}
+
+LLEventTimeoutBase::LLEventTimeoutBase(LLEventPump& source):
+ LLEventFilter(source, "timeout")
+{
+}
+
+void LLEventTimeoutBase::actionAfter(F32 seconds, const Action& action)
+{
+ setCountdown(seconds);
+ mAction = action;
+ if (! mMainloop.connected())
+ {
+ LLEventPump& mainloop(LLEventPumps::instance().obtain("mainloop"));
+ mMainloop = mainloop.listen(getName(), boost::bind(&LLEventTimeoutBase::tick, this, _1));
+ }
+}
+
+class ErrorAfter
+{
+public:
+ ErrorAfter(const std::string& message): mMessage(message) {}
+
+ void operator()()
+ {
+ LL_ERRS("LLEventTimeout") << mMessage << LL_ENDL;
+ }
+
+private:
+ std::string mMessage;
+};
+
+void LLEventTimeoutBase::errorAfter(F32 seconds, const std::string& message)
+{
+ actionAfter(seconds, ErrorAfter(message));
+}
+
+class EventAfter
+{
+public:
+ EventAfter(LLEventPump& pump, const LLSD& event):
+ mPump(pump),
+ mEvent(event)
+ {}
+
+ void operator()()
+ {
+ mPump.post(mEvent);
+ }
+
+private:
+ LLEventPump& mPump;
+ LLSD mEvent;
+};
+
+void LLEventTimeoutBase::eventAfter(F32 seconds, const LLSD& event)
+{
+ actionAfter(seconds, EventAfter(*this, event));
+}
+
+bool LLEventTimeoutBase::post(const LLSD& event)
+{
+ cancel();
+ return LLEventStream::post(event);
+}
+
+void LLEventTimeoutBase::cancel()
+{
+ mMainloop.disconnect();
+}
+
+bool LLEventTimeoutBase::tick(const LLSD&)
+{
+ if (countdownElapsed())
+ {
+ cancel();
+ mAction();
+ }
+ return false; // show event to other listeners
+}
+
+LLEventTimeout::LLEventTimeout() {}
+
+LLEventTimeout::LLEventTimeout(LLEventPump& source):
+ LLEventTimeoutBase(source)
+{
+}
+
+void LLEventTimeout::setCountdown(F32 seconds)
+{
+ mTimer.setTimerExpirySec(seconds);
+}
+
+bool LLEventTimeout::countdownElapsed() const
+{
+ return mTimer.hasExpired();
+}
diff --git a/indra/llcommon/lleventfilter.h b/indra/llcommon/lleventfilter.h
new file mode 100644
index 0000000000..89f0c7ea43
--- /dev/null
+++ b/indra/llcommon/lleventfilter.h
@@ -0,0 +1,186 @@
+/**
+ * @file lleventfilter.h
+ * @author Nat Goodspeed
+ * @date 2009-03-05
+ * @brief Define LLEventFilter: LLEventStream subclass with conditions
+ *
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ * Copyright (c) 2009, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#if ! defined(LL_LLEVENTFILTER_H)
+#define LL_LLEVENTFILTER_H
+
+#include "llevents.h"
+#include "stdtypes.h"
+#include "lltimer.h"
+#include <boost/function.hpp>
+
+/**
+ * Generic base class
+ */
+class LL_COMMON_API LLEventFilter: public LLEventStream
+{
+public:
+ /// construct a standalone LLEventFilter
+ LLEventFilter(const std::string& name="filter", bool tweak=true):
+ LLEventStream(name, tweak)
+ {}
+ /// construct LLEventFilter and connect it to the specified LLEventPump
+ LLEventFilter(LLEventPump& source, const std::string& name="filter", bool tweak=true);
+
+ /// Post an event to all listeners
+ virtual bool post(const LLSD& event) = 0;
+};
+
+/**
+ * Pass through only events matching a specified pattern
+ */
+class LLEventMatching: public LLEventFilter
+{
+public:
+ /// Pass an LLSD map with keys and values the incoming event must match
+ LLEventMatching(const LLSD& pattern);
+ /// instantiate and connect
+ LLEventMatching(LLEventPump& source, const LLSD& pattern);
+
+ /// Only pass through events matching the pattern
+ virtual bool post(const LLSD& event);
+
+private:
+ LLSD mPattern;
+};
+
+/**
+ * Wait for an event to be posted. If no such event arrives within a specified
+ * time, take a specified action. See LLEventTimeout for production
+ * implementation.
+ *
+ * @NOTE This is an abstract base class so that, for testing, we can use an
+ * alternate "timer" that doesn't actually consume real time.
+ */
+class LL_COMMON_API LLEventTimeoutBase: public LLEventFilter
+{
+public:
+ /// construct standalone
+ LLEventTimeoutBase();
+ /// construct and connect
+ LLEventTimeoutBase(LLEventPump& source);
+
+ /// Callable, can be constructed with boost::bind()
+ typedef boost::function<void()> Action;
+
+ /**
+ * Start countdown timer for the specified number of @a seconds. Forward
+ * all events. If any event arrives before timer expires, cancel timer. If
+ * no event arrives before timer expires, take specified @a action.
+ *
+ * This is a one-shot timer. Once it has either expired or been canceled,
+ * it is inert until another call to actionAfter().
+ *
+ * Calling actionAfter() while an existing timer is running cheaply
+ * replaces that original timer. Thus, a valid use case is to detect
+ * idleness of some event source by calling actionAfter() on each new
+ * event. A rapid sequence of events will keep the timer from expiring;
+ * the first gap in events longer than the specified timer will fire the
+ * specified Action.
+ *
+ * Any post() call cancels the timer. To be satisfied with only a
+ * particular event, chain on an LLEventMatching that only passes such
+ * events:
+ *
+ * @code
+ * event ultimate
+ * source ---> LLEventMatching ---> LLEventTimeout ---> listener
+ * @endcode
+ *
+ * @NOTE
+ * The implementation relies on frequent events on the LLEventPump named
+ * "mainloop".
+ */
+ void actionAfter(F32 seconds, const Action& action);
+
+ /**
+ * Like actionAfter(), but where the desired Action is LL_ERRS
+ * termination. Pass the timeout time and the desired LL_ERRS @a message.
+ *
+ * This method is useful when, for instance, some async API guarantees an
+ * event, whether success or failure, within a stated time window.
+ * Instantiate an LLEventTimeout listening to that API and call
+ * errorAfter() on each async request with a timeout comfortably longer
+ * than the API's time guarantee (much longer than the anticipated
+ * "mainloop" granularity).
+ *
+ * Then if the async API breaks its promise, the program terminates with
+ * the specified LL_ERRS @a message. The client of the async API can
+ * therefore assume the guarantee is upheld.
+ *
+ * @NOTE
+ * errorAfter() is implemented in terms of actionAfter(), so all remarks
+ * about calling actionAfter() also apply to errorAfter().
+ */
+ void errorAfter(F32 seconds, const std::string& message);
+
+ /**
+ * Like actionAfter(), but where the desired Action is a particular event
+ * for all listeners. Pass the timeout time and the desired @a event data.
+ *
+ * Suppose the timeout should only be satisfied by a particular event, but
+ * the ultimate listener must see all other incoming events as well, plus
+ * the timeout @a event if any:
+ *
+ * @code
+ * some LLEventMatching LLEventMatching
+ * event ---> for particular ---> LLEventTimeout ---> for timeout
+ * source event event \
+ * \ \ ultimate
+ * `-----------------------------------------------------> listener
+ * @endcode
+ *
+ * Since a given listener can listen on more than one LLEventPump, we can
+ * set things up so it sees the set union of events from LLEventTimeout
+ * and the original event source. However, as LLEventTimeout passes
+ * through all incoming events, the "particular event" that satisfies the
+ * left LLEventMatching would reach the ultimate listener twice. So we add
+ * an LLEventMatching that only passes timeout events.
+ *
+ * @NOTE
+ * eventAfter() is implemented in terms of actionAfter(), so all remarks
+ * about calling actionAfter() also apply to eventAfter().
+ */
+ void eventAfter(F32 seconds, const LLSD& event);
+
+ /// Pass event through, canceling the countdown timer
+ virtual bool post(const LLSD& event);
+
+ /// Cancel timer without event
+ void cancel();
+
+protected:
+ virtual void setCountdown(F32 seconds) = 0;
+ virtual bool countdownElapsed() const = 0;
+
+private:
+ bool tick(const LLSD&);
+
+ LLBoundListener mMainloop;
+ Action mAction;
+};
+
+/// Production implementation of LLEventTimoutBase
+class LL_COMMON_API LLEventTimeout: public LLEventTimeoutBase
+{
+public:
+ LLEventTimeout();
+ LLEventTimeout(LLEventPump& source);
+
+protected:
+ virtual void setCountdown(F32 seconds);
+ virtual bool countdownElapsed() const;
+
+private:
+ LLTimer mTimer;
+};
+
+#endif /* ! defined(LL_LLEVENTFILTER_H) */
diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp
index eb380ba7c8..4bdfe5a867 100644
--- a/indra/llcommon/llevents.cpp
+++ b/indra/llcommon/llevents.cpp
@@ -38,6 +38,12 @@
#pragma warning (pop)
#endif
// other Linden headers
+#include "stringize.h"
+#include "llerror.h"
+#include "llsdutil.h"
+#if LL_MSVC
+#pragma warning (disable : 4702)
+#endif
/*****************************************************************************
* queue_names: specify LLEventPump names that should be instantiated as
@@ -56,14 +62,12 @@ const char* queue_names[] =
/*****************************************************************************
* If there's a "mainloop" pump, listen on that to flush all LLEventQueues
*****************************************************************************/
-struct RegisterFlush
+struct RegisterFlush : public LLEventTrackable
{
RegisterFlush():
- pumps(LLEventPumps::instance()),
- mainloop(pumps.obtain("mainloop")),
- name("flushLLEventQueues")
+ pumps(LLEventPumps::instance())
{
- mainloop.listen(name, boost::bind(&RegisterFlush::flush, this, _1));
+ pumps.obtain("mainloop").listen("flushLLEventQueues", boost::bind(&RegisterFlush::flush, this, _1));
}
bool flush(const LLSD&)
{
@@ -72,11 +76,9 @@ struct RegisterFlush
}
~RegisterFlush()
{
- mainloop.stopListening(name);
+ // LLEventTrackable handles stopListening for us.
}
LLEventPumps& pumps;
- LLEventPump& mainloop;
- const std::string name;
};
static RegisterFlush registerFlush;
@@ -124,6 +126,16 @@ void LLEventPumps::flush()
}
}
+void LLEventPumps::reset()
+{
+ // Reset every known LLEventPump instance. Leave it up to each instance to
+ // decide what to do with the reset() call.
+ for (PumpMap::iterator pmi = mPumpMap.begin(), pmend = mPumpMap.end(); pmi != pmend; ++pmi)
+ {
+ pmi->second->reset();
+ }
+}
+
std::string LLEventPumps::registerNew(const LLEventPump& pump, const std::string& name, bool tweak)
{
std::pair<PumpMap::iterator, bool> inserted =
@@ -240,6 +252,7 @@ LLEventPumps::~LLEventPumps()
LLEventPump::LLEventPump(const std::string& name, bool tweak):
// Register every new instance with LLEventPumps
mName(LLEventPumps::instance().registerNew(*this, name, tweak)),
+ mSignal(new LLStandardSignal()),
mEnabled(true)
{}
@@ -256,6 +269,19 @@ LLEventPump::~LLEventPump()
// static data member
const LLEventPump::NameList LLEventPump::empty;
+std::string LLEventPump::inventName(const std::string& pfx)
+{
+ static long suffix = 0;
+ return STRINGIZE(pfx << suffix++);
+}
+
+void LLEventPump::reset()
+{
+ mSignal.reset();
+ mConnections.clear();
+ //mDeps.clear();
+}
+
LLBoundListener LLEventPump::listen_impl(const std::string& name, const LLEventListener& listener,
const NameList& after,
const NameList& before)
@@ -397,7 +423,7 @@ LLBoundListener LLEventPump::listen_impl(const std::string& name, const LLEventL
}
// Now that newNode has a value that places it appropriately in mSignal,
// connect it.
- LLBoundListener bound = mSignal.connect(newNode, listener);
+ LLBoundListener bound = mSignal->connect(newNode, listener);
mConnections[name] = bound;
return bound;
}
@@ -437,7 +463,7 @@ bool LLEventStream::post(const LLSD& event)
// Let caller know if any one listener handled the event. This is mostly
// useful when using LLEventStream as a listener for an upstream
// LLEventPump.
- return mSignal(event);
+ return (*mSignal)(event);
}
/*****************************************************************************
@@ -468,7 +494,7 @@ void LLEventQueue::flush()
mEventQueue.clear();
for ( ; ! queue.empty(); queue.pop_front())
{
- mSignal(queue.front());
+ (*mSignal)(queue.front());
}
}
@@ -499,3 +525,26 @@ bool LLListenerOrPumpName::operator()(const LLSD& event) const
}
return (*mListener)(event);
}
+
+void LLReqID::stamp(LLSD& response) const
+{
+ if (! (response.isUndefined() || response.isMap()))
+ {
+ // If 'response' was previously completely empty, it's okay to
+ // turn it into a map. If it was already a map, then it should be
+ // okay to add a key. But if it was anything else (e.g. a scalar),
+ // assigning a ["reqid"] key will DISCARD the previous value,
+ // replacing it with a map. That would be Bad.
+ LL_INFOS("LLReqID") << "stamp(" << mReqid << ") leaving non-map response unmodified: "
+ << response << LL_ENDL;
+ return;
+ }
+ LLSD oldReqid(response["reqid"]);
+ if (! (oldReqid.isUndefined() || llsd_equals(oldReqid, mReqid)))
+ {
+ LL_INFOS("LLReqID") << "stamp(" << mReqid << ") preserving existing [\"reqid\"] value "
+ << oldReqid << " in response: " << response << LL_ENDL;
+ return;
+ }
+ response["reqid"] = mReqid;
+}
diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h
index 240adcdd41..64e5cb5da7 100644
--- a/indra/llcommon/llevents.h
+++ b/indra/llcommon/llevents.h
@@ -1,831 +1,943 @@
-/**
- * @file llevents.h
- * @author Kent Quirk, Nat Goodspeed
- * @date 2008-09-11
- * @brief This is an implementation of the event system described at
- * https://wiki.lindenlab.com/wiki/Viewer:Messaging/Event_System,
- * originally introduced in llnotifications.h. It has nothing
- * whatsoever to do with the older system in llevent.h.
- *
- * $LicenseInfo:firstyear=2008&license=viewergpl$
- * Copyright (c) 2008, Linden Research, Inc.
- * $/LicenseInfo$
- */
-
-#if ! defined(LL_LLEVENTS_H)
-#define LL_LLEVENTS_H
-
-#include <string>
-#include <map>
-#include <set>
-#include <vector>
-#include <list>
-#include <deque>
-#include <stdexcept>
-#if LL_WINDOWS
- #pragma warning (push)
- #pragma warning (disable : 4263) // boost::signals2::expired_slot::what() has const mismatch
- #pragma warning (disable : 4264)
-#endif
-#include <boost/signals2.hpp>
-#if LL_WINDOWS
- #pragma warning (pop)
-#endif
-
-#include <boost/bind.hpp>
-#include <boost/shared_ptr.hpp>
-#include <boost/enable_shared_from_this.hpp>
-#include <boost/utility.hpp> // noncopyable
-#include <boost/optional/optional.hpp>
-#include <boost/ptr_container/ptr_vector.hpp>
-#include <boost/visit_each.hpp>
-#include <boost/ref.hpp> // reference_wrapper
-#include <boost/type_traits/is_pointer.hpp>
-#include <boost/utility/addressof.hpp>
-#include <boost/preprocessor/repetition/enum_params.hpp>
-#include <boost/preprocessor/iteration/local.hpp>
-#include <boost/function.hpp>
-#include <boost/static_assert.hpp>
-#include "llsd.h"
-#include "llsingleton.h"
-#include "lldependencies.h"
-
-// override this to allow binding free functions with more parameters
-#ifndef LLEVENTS_LISTENER_ARITY
-#define LLEVENTS_LISTENER_ARITY 10
-#endif
-
-// hack for testing
-#ifndef testable
-#define testable private
-#endif
-
-/*****************************************************************************
-* Signal and handler declarations
-* Using a single handler signature means that we can have a common handler
-* type, rather than needing a distinct one for each different handler.
-*****************************************************************************/
-
-/**
- * A boost::signals Combiner that stops the first time a handler returns true
- * We need this because we want to have our handlers return bool, so that
- * we have the option to cause a handler to stop further processing. The
- * default handler fails when the signal returns a value but has no slots.
- */
-struct LLStopWhenHandled
-{
- typedef bool result_type;
-
- template<typename InputIterator>
- result_type operator()(InputIterator first, InputIterator last) const
- {
- for (InputIterator si = first; si != last; ++si)
- {
- if (*si)
- {
- return true;
- }
- }
- return false;
- }
-};
-
-/**
- * We want to have a standard signature for all signals; this way,
- * we can easily document a protocol for communicating across
- * dlls and into scripting languages someday.
- *
- * We want to return a bool to indicate whether the signal has been
- * handled and should NOT be passed on to other listeners.
- * Return true to stop further handling of the signal, and false
- * to continue.
- *
- * We take an LLSD because this way the contents of the signal
- * are independent of the API used to communicate it.
- * It is const ref because then there's low cost to pass it;
- * if you only need to inspect it, it's very cheap.
- *
- * @internal
- * The @c float template parameter indicates that we will internally use @c
- * float to indicate relative listener order on a given LLStandardSignal.
- * Don't worry, the @c float values are strictly internal! They are not part
- * of the interface, for the excellent reason that requiring the caller to
- * specify a numeric key to establish order means that the caller must know
- * the universe of possible values. We use LLDependencies for that instead.
- */
-typedef boost::signals2::signal<bool(const LLSD&), LLStopWhenHandled, float> LLStandardSignal;
-/// Methods that forward listeners (e.g. constructed with
-/// <tt>boost::bind()</tt>) should accept (const LLEventListener&)
-typedef LLStandardSignal::slot_type LLEventListener;
-/// Result of registering a listener, supports <tt>connected()</tt>,
-/// <tt>disconnect()</tt> and <tt>blocked()</tt>
-typedef boost::signals2::connection LLBoundListener;
-
-/**
- * A common idiom for event-based code is to accept either a callable --
- * directly called on completion -- or the string name of an LLEventPump on
- * which to post the completion event. Specifying a parameter as <tt>const
- * LLListenerOrPumpName&</tt> allows either.
- *
- * Calling a validly-constructed LLListenerOrPumpName, passing the LLSD
- * 'event' object, either calls the callable or posts the event to the named
- * LLEventPump.
- *
- * A default-constructed LLListenerOrPumpName is 'empty'. (This is useful as
- * the default value of an optional method parameter.) Calling it throws
- * LLListenerOrPumpName::Empty. Test for this condition beforehand using
- * either <tt>if (param)</tt> or <tt>if (! param)</tt>.
- */
-class LLListenerOrPumpName
-{
-public:
- /// passing string name of LLEventPump
- LLListenerOrPumpName(const std::string& pumpname);
- /// passing string literal (overload so compiler isn't forced to infer
- /// double conversion)
- LLListenerOrPumpName(const char* pumpname);
- /// passing listener -- the "anything else" catch-all case. The type of an
- /// object constructed by boost::bind() isn't intended to be written out.
- /// Normally we'd just accept 'const LLEventListener&', but that would
- /// require double implicit conversion: boost::bind() object to
- /// LLEventListener, LLEventListener to LLListenerOrPumpName. So use a
- /// template to forward anything.
- template<typename T>
- LLListenerOrPumpName(const T& listener): mListener(listener) {}
-
- /// for omitted method parameter: uninitialized mListener
- LLListenerOrPumpName() {}
-
- /// test for validity
- operator bool() const { return bool(mListener); }
- bool operator! () const { return ! mListener; }
-
- /// explicit accessor
- const LLEventListener& getListener() const { return *mListener; }
-
- /// implicit conversion to LLEventListener
- operator LLEventListener() const { return *mListener; }
-
- /// allow calling directly
- bool operator()(const LLSD& event) const;
-
- /// exception if you try to call when empty
- struct Empty: public std::runtime_error
- {
- Empty(const std::string& what):
- std::runtime_error(std::string("LLListenerOrPumpName::Empty: ") + what) {}
- };
-
-private:
- boost::optional<LLEventListener> mListener;
-};
-
-/*****************************************************************************
-* LLEventPumps
-*****************************************************************************/
-class LLEventPump;
-
-/**
- * LLEventPumps is a Singleton manager through which one typically accesses
- * this subsystem.
- */
-class LLEventPumps: public LLSingleton<LLEventPumps>
-{
- friend class LLSingleton<LLEventPumps>;
-public:
- /**
- * Find or create an LLEventPump instance with a specific name. We return
- * a reference so there's no question about ownership. obtain() @em finds
- * an instance without conferring @em ownership.
- */
- LLEventPump& obtain(const std::string& name);
- /**
- * Flush all known LLEventPump instances
- */
- void flush();
-
-private:
- friend class LLEventPump;
- /**
- * Register a new LLEventPump instance (internal)
- */
- std::string registerNew(const LLEventPump&, const std::string& name, bool tweak);
- /**
- * Unregister a doomed LLEventPump instance (internal)
- */
- void unregister(const LLEventPump&);
-
-private:
- LLEventPumps();
- ~LLEventPumps();
-
-testable:
- // Map of all known LLEventPump instances, whether or not we instantiated
- // them. We store a plain old LLEventPump* because this map doesn't claim
- // ownership of the instances. Though the common usage pattern is to
- // request an instance using obtain(), it's fair to instantiate an
- // LLEventPump subclass statically, as a class member, on the stack or on
- // the heap. In such cases, the instantiating party is responsible for its
- // lifespan.
- typedef std::map<std::string, LLEventPump*> PumpMap;
- PumpMap mPumpMap;
- // Set of all LLEventPumps we instantiated. Membership in this set means
- // we claim ownership, and will delete them when this LLEventPumps is
- // destroyed.
- typedef std::set<LLEventPump*> PumpSet;
- PumpSet mOurPumps;
- // LLEventPump names that should be instantiated as LLEventQueue rather
- // than as LLEventStream
- typedef std::set<std::string> PumpNames;
- PumpNames mQueueNames;
-};
-
-/*****************************************************************************
-* details
-*****************************************************************************/
-namespace LLEventDetail
-{
- /// Any callable capable of connecting an LLEventListener to an
- /// LLStandardSignal to produce an LLBoundListener can be mapped to this
- /// signature.
- typedef boost::function<LLBoundListener(const LLEventListener&)> ConnectFunc;
-
- /**
- * Utility template function to use Visitor appropriately
- *
- * @param listener Callable to connect, typically a boost::bind()
- * expression. This will be visited by Visitor using boost::visit_each().
- * @param connect_func Callable that will connect() @a listener to an
- * LLStandardSignal, returning LLBoundListener.
- */
- template <typename LISTENER>
- LLBoundListener visit_and_connect(const LISTENER& listener,
- const ConnectFunc& connect_func);
-} // namespace LLEventDetail
-
-/*****************************************************************************
-* LLEventPump
-*****************************************************************************/
-/**
- * LLEventPump is the base class interface through which we access the
- * concrete subclasses LLEventStream and LLEventQueue.
- */
-class LLEventPump: boost::noncopyable
-{
-public:
- /**
- * Exception thrown by LLEventPump(). You are trying to instantiate an
- * LLEventPump (subclass) using the same name as some other instance, and
- * you didn't pass <tt>tweak=true</tt> to permit it to generate a unique
- * variant.
- */
- struct DupPumpName: public std::runtime_error
- {
- DupPumpName(const std::string& what):
- std::runtime_error(std::string("DupPumpName: ") + what) {}
- };
-
- /**
- * Instantiate an LLEventPump (subclass) with the string name by which it
- * can be found using LLEventPumps::obtain().
- *
- * If you pass (or default) @a tweak to @c false, then a duplicate name
- * will throw DupPumpName. This won't happen if LLEventPumps::obtain()
- * instantiates the LLEventPump, because obtain() uses find-or-create
- * logic. It can only happen if you instantiate an LLEventPump in your own
- * code -- and a collision with the name of some other LLEventPump is
- * likely to cause much more subtle problems!
- *
- * When you hand-instantiate an LLEventPump, consider passing @a tweak as
- * @c true. This directs LLEventPump() to append a suffix to the passed @a
- * name to make it unique. You can retrieve the adjusted name by calling
- * getName() on your new instance.
- */
- LLEventPump(const std::string& name, bool tweak=false);
- virtual ~LLEventPump();
-
- /// group exceptions thrown by listen(). We use exceptions because these
- /// particular errors are likely to be coding errors, found and fixed by
- /// the developer even before preliminary checkin.
- struct ListenError: public std::runtime_error
- {
- ListenError(const std::string& what): std::runtime_error(what) {}
- };
- /**
- * exception thrown by listen(). You are attempting to register a
- * listener on this LLEventPump using the same listener name as an
- * already-registered listener.
- */
- struct DupListenerName: public ListenError
- {
- DupListenerName(const std::string& what):
- ListenError(std::string("DupListenerName: ") + what)
- {}
- };
- /**
- * exception thrown by listen(). The order dependencies specified for your
- * listener are incompatible with existing listeners.
- *
- * Consider listener "a" which specifies before "b" and "b" which
- * specifies before "c". You are now attempting to register "c" before
- * "a". There is no order that can satisfy all constraints.
- */
- struct Cycle: public ListenError
- {
- Cycle(const std::string& what): ListenError(std::string("Cycle: ") + what) {}
- };
- /**
- * exception thrown by listen(). This one means that your new listener
- * would force a change to the order of previously-registered listeners,
- * and we don't have a good way to implement that.
- *
- * Consider listeners "some", "other" and "third". "some" and "other" are
- * registered earlier without specifying relative order, so "other"
- * happens to be first. Now you attempt to register "third" after "some"
- * and before "other". Whoops, that would require swapping "some" and
- * "other", which we can't do. Instead we throw this exception.
- *
- * It may not be possible to change the registration order so we already
- * know "third"s order requirement by the time we register the second of
- * "some" and "other". A solution would be to specify that "some" must
- * come before "other", or equivalently that "other" must come after
- * "some".
- */
- struct OrderChange: public ListenError
- {
- OrderChange(const std::string& what): ListenError(std::string("OrderChange: ") + what) {}
- };
-
- /// used by listen()
- typedef std::vector<std::string> NameList;
- /// convenience placeholder for when you explicitly want to pass an empty
- /// NameList
- const static NameList empty;
-
- /// Get this LLEventPump's name
- std::string getName() const { return mName; }
-
- /**
- * Register a new listener with a unique name. Specify an optional list
- * of other listener names after which this one must be called, likewise
- * an optional list of other listener names before which this one must be
- * called. The other listeners mentioned need not yet be registered
- * themselves. listen() can throw any ListenError; see ListenError
- * subclasses.
- *
- * If (as is typical) you pass a <tt>boost::bind()</tt> expression,
- * listen() will inspect the components of that expression. If a bound
- * object matches any of several cases, the connection will automatically
- * be disconnected when that object is destroyed.
- *
- * * You bind a <tt>boost::weak_ptr</tt>.
- * * Binding a <tt>boost::shared_ptr</tt> that way would ensure that the
- * referenced object would @em never be destroyed, since the @c
- * shared_ptr stored in the LLEventPump would remain an outstanding
- * reference. Use the weaken() function to convert your @c shared_ptr to
- * @c weak_ptr. Because this is easy to forget, binding a @c shared_ptr
- * will produce a compile error (@c BOOST_STATIC_ASSERT failure).
- * * You bind a simple pointer or reference to an object derived from
- * <tt>boost::enable_shared_from_this</tt>. (UNDER CONSTRUCTION)
- * * You bind a simple pointer or reference to an object derived from
- * LLEventTrackable. Unlike the cases described above, though, this is
- * vulnerable to a couple of cross-thread race conditions, as described
- * in the LLEventTrackable documentation.
- */
- template <typename LISTENER>
- LLBoundListener listen(const std::string& name, const LISTENER& listener,
- const NameList& after=NameList(),
- const NameList& before=NameList())
- {
- // Examine listener, using our listen_impl() method to make the
- // actual connection.
- // This is why listen() is a template. Conversion from boost::bind()
- // to LLEventListener performs type erasure, so it's important to look
- // at the boost::bind object itself before that happens.
- return LLEventDetail::visit_and_connect(listener,
- boost::bind(&LLEventPump::listen_impl,
- this,
- name,
- _1,
- after,
- before));
- }
-
- /// Get the LLBoundListener associated with the passed name (dummy
- /// LLBoundListener if not found)
- virtual LLBoundListener getListener(const std::string& name) const;
- /**
- * Instantiate one of these to block an existing connection:
- * @code
- * { // in some local scope
- * LLEventPump::Blocker block(someLLBoundListener);
- * // code that needs the connection blocked
- * } // unblock the connection again
- * @endcode
- */
- typedef boost::signals2::shared_connection_block Blocker;
- /// Unregister a listener by name. Prefer this to
- /// <tt>getListener(name).disconnect()</tt> because stopListening() also
- /// forgets this name.
- virtual void stopListening(const std::string& name);
- /// Post an event to all listeners. The @c bool return is only meaningful
- /// if the underlying leaf class is LLEventStream -- beware of relying on
- /// it too much! Truthfully, we return @c bool mostly to permit chaining
- /// one LLEventPump as a listener on another.
- virtual bool post(const LLSD&) = 0;
- /// Enable/disable: while disabled, silently ignore all post() calls
- virtual void enable(bool enabled=true) { mEnabled = enabled; }
- /// query
- virtual bool enabled() const { return mEnabled; }
-
-private:
- friend class LLEventPumps;
- /// flush queued events
- virtual void flush() {}
-
-private:
- virtual LLBoundListener listen_impl(const std::string& name, const LLEventListener&,
- const NameList& after,
- const NameList& before);
- std::string mName;
-
-protected:
- /// implement the dispatching
- LLStandardSignal mSignal;
- /// valve open?
- bool mEnabled;
- /// Map of named listeners. This tracks the listeners that actually exist
- /// at this moment. When we stopListening(), we discard the entry from
- /// this map.
- typedef std::map<std::string, boost::signals2::connection> ConnectionMap;
- ConnectionMap mConnections;
- typedef LLDependencies<std::string, float> DependencyMap;
- /// Dependencies between listeners. For each listener, track the float
- /// used to establish its place in mSignal's order. This caches all the
- /// listeners that have ever registered; stopListening() does not discard
- /// the entry from this map. This is to avoid a new dependency sort if the
- /// same listener with the same dependencies keeps hopping on and off this
- /// LLEventPump.
- DependencyMap mDeps;
-};
-
-/*****************************************************************************
-* LLEventStream
-*****************************************************************************/
-/**
- * LLEventStream is a thin wrapper around LLStandardSignal. Posting an
- * event immediately calls all registered listeners.
- */
-class LLEventStream: public LLEventPump
-{
-public:
- LLEventStream(const std::string& name, bool tweak=false): LLEventPump(name, tweak) {}
- virtual ~LLEventStream() {}
-
- /// Post an event to all listeners
- virtual bool post(const LLSD& event);
-};
-
-/*****************************************************************************
-* LLEventQueue
-*****************************************************************************/
-/**
- * LLEventQueue isa LLEventPump whose post() method defers calling registered
- * listeners until flush() is called.
- */
-class LLEventQueue: public LLEventPump
-{
-public:
- LLEventQueue(const std::string& name, bool tweak=false): LLEventPump(name, tweak) {}
- virtual ~LLEventQueue() {}
-
- /// Post an event to all listeners
- virtual bool post(const LLSD& event);
-
-private:
- /// flush queued events
- virtual void flush();
-
-private:
- typedef std::deque<LLSD> EventQueue;
- EventQueue mEventQueue;
-};
-
-/*****************************************************************************
-* LLEventTrackable and underpinnings
-*****************************************************************************/
-/**
- * LLEventTrackable wraps boost::signals2::trackable, which resembles
- * boost::trackable. Derive your listener class from LLEventTrackable instead,
- * and use something like
- * <tt>LLEventPump::listen(boost::bind(&YourTrackableSubclass::method,
- * instance, _1))</tt>. This will implicitly disconnect when the object
- * referenced by @c instance is destroyed.
- *
- * @note
- * LLEventTrackable doesn't address a couple of cases:
- * * Object destroyed during call
- * - You enter a slot call in thread A.
- * - Thread B destroys the object, which of course disconnects it from any
- * future slot calls.
- * - Thread A's call uses 'this', which now refers to a defunct object.
- * Undefined behavior results.
- * * Call during destruction
- * - @c MySubclass is derived from LLEventTrackable.
- * - @c MySubclass registers one of its own methods using
- * <tt>LLEventPump::listen()</tt>.
- * - The @c MySubclass object begins destruction. <tt>~MySubclass()</tt>
- * runs, destroying state specific to the subclass. (For instance, a
- * <tt>Foo*</tt> data member is <tt>delete</tt>d but not zeroed.)
- * - The listening method will not be disconnected until
- * <tt>~LLEventTrackable()</tt> runs.
- * - Before we get there, another thread posts data to the @c LLEventPump
- * instance, calling the @c MySubclass method.
- * - The method in question relies on valid @c MySubclass state. (For
- * instance, it attempts to dereference the <tt>Foo*</tt> pointer that was
- * <tt>delete</tt>d but not zeroed.)
- * - Undefined behavior results.
- * If you suspect you may encounter any such scenario, you're better off
- * managing the lifespan of your object with <tt>boost::shared_ptr</tt>.
- * Passing <tt>LLEventPump::listen()</tt> a <tt>boost::bind()</tt> expression
- * involving a <tt>boost::weak_ptr<Foo></tt> is recognized specially, engaging
- * thread-safe Boost.Signals2 machinery.
- */
-typedef boost::signals2::trackable LLEventTrackable;
-
-/**
- * We originally provided a suite of overloaded
- * LLEventTrackable::listenTo(LLEventPump&, ...) methods that would call
- * LLEventPump::listen(...) and then pass the returned LLBoundListener to
- * LLEventTrackable::track(). This was workable but error-prone: the coder
- * must remember to call listenTo() rather than the more straightforward
- * listen() method.
- *
- * Now we publish only the single canonical listen() method, so there's a
- * uniform mechanism. Having a single way to do this is good, in that there's
- * no question in the coder's mind which of several alternatives to choose.
- *
- * To support automatic connection management, we use boost::visit_each
- * (http://www.boost.org/doc/libs/1_37_0/doc/html/boost/visit_each.html) to
- * inspect each argument of a boost::bind expression. (Although the visit_each
- * mechanism was first introduced with the original Boost.Signals library, it
- * was only later documented.)
- *
- * Cases:
- * * At least one of the function's arguments is a boost::weak_ptr<T>. Pass
- * the corresponding shared_ptr to slot_type::track(). Ideally that would be
- * the object whose method we want to call, but in fact we do the same for
- * any weak_ptr we might find among the bound arguments. If we're passing
- * our bound method a weak_ptr to some object, wouldn't the destruction of
- * that object invalidate the call? So we disconnect automatically when any
- * such object is destroyed. This is the mechanism preferred by boost::
- * signals2.
- * * One of the functions's arguments is a boost::shared_ptr<T>. This produces
- * a compile error: the bound copy of the shared_ptr stored in the
- * boost_bind object stored in the signal object would make the referenced
- * T object immortal. We provide a weaken() function. Pass
- * weaken(your_shared_ptr) instead. (We can inspect, but not modify, the
- * boost::bind object. Otherwise we'd replace the shared_ptr with weak_ptr
- * implicitly and just proceed.)
- * * One of the function's arguments is a plain pointer/reference to an object
- * derived from boost::enable_shared_from_this. We assume that this object
- * is managed using boost::shared_ptr, so we implicitly extract a shared_ptr
- * and track that. (UNDER CONSTRUCTION)
- * * One of the function's arguments is derived from LLEventTrackable. Pass
- * the LLBoundListener to its LLEventTrackable::track(). This is vulnerable
- * to a couple different race conditions, as described in LLEventTrackable
- * documentation. (NOTE: Now that LLEventTrackable is a typedef for
- * boost::signals2::trackable, the Signals2 library handles this itself, so
- * our visitor needs no special logic for this case.)
- * * Any other argument type is irrelevant to automatic connection management.
- */
-
-namespace LLEventDetail
-{
- template <typename F>
- const F& unwrap(const F& f) { return f; }
-
- template <typename F>
- const F& unwrap(const boost::reference_wrapper<F>& f) { return f.get(); }
-
- // Most of the following is lifted from the Boost.Signals use of
- // visit_each.
- template<bool Cond> struct truth {};
-
- /**
- * boost::visit_each() Visitor, used on a template argument <tt>const F&
- * f</tt> as follows (see visit_and_connect()):
- * @code
- * LLEventListener listener(f);
- * Visitor visitor(listener); // bind listener so it can track() shared_ptrs
- * using boost::visit_each; // allow unqualified visit_each() call for ADL
- * visit_each(visitor, unwrap(f));
- * @endcode
- */
- class Visitor
- {
- public:
- /**
- * Visitor binds a reference to LLEventListener so we can track() any
- * shared_ptrs we find in the argument list.
- */
- Visitor(LLEventListener& listener):
- mListener(listener)
- {
- }
-
- /**
- * boost::visit_each() calls this method for each component of a
- * boost::bind() expression.
- */
- template <typename T>
- void operator()(const T& t) const
- {
- decode(t, 0);
- }
-
- private:
- // decode() decides between a reference wrapper and anything else
- // boost::ref() variant
- template<typename T>
- void decode(const boost::reference_wrapper<T>& t, int) const
- {
-// add_if_trackable(t.get_pointer());
- }
-
- // decode() anything else
- template<typename T>
- void decode(const T& t, long) const
- {
- typedef truth<(boost::is_pointer<T>::value)> is_a_pointer;
- maybe_get_pointer(t, is_a_pointer());
- }
-
- // maybe_get_pointer() decides between a pointer and a non-pointer
- // plain pointer variant
- template<typename T>
- void maybe_get_pointer(const T& t, truth<true>) const
- {
-// add_if_trackable(t);
- }
-
- // shared_ptr variant
- template<typename T>
- void maybe_get_pointer(const boost::shared_ptr<T>& t, truth<false>) const
- {
- // If we have a shared_ptr to this object, it doesn't matter
- // whether the object is derived from LLEventTrackable, so no
- // further analysis of T is needed.
-// mListener.track(t);
-
- // Make this case illegal. Passing a bound shared_ptr to
- // slot_type::track() is useless, since the bound shared_ptr will
- // keep the object alive anyway! Force the coder to cast to weak_ptr.
-
- // Trivial as it is, make the BOOST_STATIC_ASSERT() condition
- // dependent on template param so the macro is only evaluated if
- // this method is in fact instantiated, as described here:
- // http://www.boost.org/doc/libs/1_34_1/doc/html/boost_staticassert.html
-
- // ATTENTION: Don't bind a shared_ptr<anything> using
- // LLEventPump::listen(boost::bind()). Doing so captures a copy of
- // the shared_ptr, making the referenced object effectively
- // immortal. Use the weaken() function, e.g.:
- // somepump.listen(boost::bind(...weaken(my_shared_ptr)...));
- // This lets us automatically disconnect when the referenced
- // object is destroyed.
- BOOST_STATIC_ASSERT(sizeof(T) == 0);
- }
-
- // weak_ptr variant
- template<typename T>
- void maybe_get_pointer(const boost::weak_ptr<T>& t, truth<false>) const
- {
- // If we have a weak_ptr to this object, it doesn't matter
- // whether the object is derived from LLEventTrackable, so no
- // further analysis of T is needed.
- mListener.track(t);
-// std::cout << "Found weak_ptr<" << typeid(T).name() << ">!\n";
- }
-
-#if 0
- // reference to anything derived from boost::enable_shared_from_this
- template <typename T>
- inline void maybe_get_pointer(const boost::enable_shared_from_this<T>& ct,
- truth<false>) const
- {
- // Use the slot_type::track(shared_ptr) mechanism. Cast away
- // const-ness because (in our code base anyway) it's unusual
- // to find shared_ptr<const T>.
- boost::enable_shared_from_this<T>&
- t(const_cast<boost::enable_shared_from_this<T>&>(ct));
- std::cout << "Capturing shared_from_this()" << std::endl;
- boost::shared_ptr<T> sp(t.shared_from_this());
-/*==========================================================================*|
- std::cout << "Capturing weak_ptr" << std::endl;
- boost::weak_ptr<T> wp(sp);
-|*==========================================================================*/
- std::cout << "Tracking shared__ptr" << std::endl;
- mListener.track(sp);
- }
-#endif
-
- // non-pointer variant
- template<typename T>
- void maybe_get_pointer(const T& t, truth<false>) const
- {
- // Take the address of this object, because the object itself may be
- // trackable
-// add_if_trackable(boost::addressof(t));
- }
-
-/*==========================================================================*|
- // add_if_trackable() adds LLEventTrackable objects to mTrackables
- inline void add_if_trackable(const LLEventTrackable* t) const
- {
- if (t)
- {
- }
- }
-
- // pointer to anything not an LLEventTrackable subclass
- inline void add_if_trackable(const void*) const
- {
- }
-
- // pointer to free function
- // The following construct uses the preprocessor to generate
- // add_if_trackable() overloads accepting pointer-to-function taking
- // 0, 1, ..., LLEVENTS_LISTENER_ARITY parameters of arbitrary type.
-#define BOOST_PP_LOCAL_MACRO(n) \
- template <typename R \
- BOOST_PP_COMMA_IF(n) \
- BOOST_PP_ENUM_PARAMS(n, typename T)> \
- inline void \
- add_if_trackable(R (*)(BOOST_PP_ENUM_PARAMS(n, T))) const \
- { \
- }
-#define BOOST_PP_LOCAL_LIMITS (0, LLEVENTS_LISTENER_ARITY)
-#include BOOST_PP_LOCAL_ITERATE()
-#undef BOOST_PP_LOCAL_MACRO
-#undef BOOST_PP_LOCAL_LIMITS
-|*==========================================================================*/
-
- /// Bind a reference to the LLEventListener to call its track() method.
- LLEventListener& mListener;
- };
-
- /**
- * Utility template function to use Visitor appropriately
- *
- * @param raw_listener Callable to connect, typically a boost::bind()
- * expression. This will be visited by Visitor using boost::visit_each().
- * @param connect_funct Callable that will connect() @a raw_listener to an
- * LLStandardSignal, returning LLBoundListener.
- */
- template <typename LISTENER>
- LLBoundListener visit_and_connect(const LISTENER& raw_listener,
- const ConnectFunc& connect_func)
- {
- // Capture the listener
- LLEventListener listener(raw_listener);
- // Define our Visitor, binding the listener so we can call
- // listener.track() if we discover any shared_ptr<Foo>.
- LLEventDetail::Visitor visitor(listener);
- // Allow unqualified visit_each() call for ADL
- using boost::visit_each;
- // Visit each component of a boost::bind() expression. Pass
- // 'raw_listener', our template argument, rather than 'listener' from
- // which type details have been erased. unwrap() comes from
- // Boost.Signals, in case we were passed a boost::ref().
- visit_each(visitor, LLEventDetail::unwrap(raw_listener));
- // Make the connection using passed function. At present, wrapping
- // this functionality into this function is a bit silly: we don't
- // really need a visit_and_connect() function any more, just a visit()
- // function. The definition of this function dates from when, after
- // visit_each(), after establishing the connection, we had to
- // postprocess the new connection with the visitor object. That's no
- // longer necessary.
- return connect_func(listener);
- }
-} // namespace LLEventDetail
-
-// Somewhat to my surprise, passing boost::bind(...boost::weak_ptr<T>...) to
-// listen() fails in Boost code trying to instantiate LLEventListener (i.e.
-// LLStandardSignal::slot_type) because the boost::get_pointer() utility function isn't
-// specialized for boost::weak_ptr. This remedies that omission.
-namespace boost
-{
- template <typename T>
- T* get_pointer(const weak_ptr<T>& ptr) { return shared_ptr<T>(ptr).get(); }
-}
-
-/// Since we forbid use of listen(boost::bind(...shared_ptr<T>...)), provide an
-/// easy way to cast to the corresponding weak_ptr.
-template <typename T>
-boost::weak_ptr<T> weaken(const boost::shared_ptr<T>& ptr)
-{
- return boost::weak_ptr<T>(ptr);
-}
-
-#endif /* ! defined(LL_LLEVENTS_H) */
+/**
+ * @file llevents.h
+ * @author Kent Quirk, Nat Goodspeed
+ * @date 2008-09-11
+ * @brief This is an implementation of the event system described at
+ * https://wiki.lindenlab.com/wiki/Viewer:Messaging/Event_System,
+ * originally introduced in llnotifications.h. It has nothing
+ * whatsoever to do with the older system in llevent.h.
+ *
+ * $LicenseInfo:firstyear=2008&license=viewergpl$
+ * Copyright (c) 2008, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#if ! defined(LL_LLEVENTS_H)
+#define LL_LLEVENTS_H
+
+#include <string>
+#include <map>
+#include <set>
+#include <vector>
+#include <deque>
+#include <stdexcept>
+#if LL_WINDOWS
+ #pragma warning (push)
+ #pragma warning (disable : 4263) // boost::signals2::expired_slot::what() has const mismatch
+ #pragma warning (disable : 4264)
+#endif
+#include <boost/signals2.hpp>
+#if LL_WINDOWS
+ #pragma warning (pop)
+#endif
+
+#include <boost/bind.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/enable_shared_from_this.hpp>
+#include <boost/utility.hpp> // noncopyable
+#include <boost/optional/optional.hpp>
+#include <boost/visit_each.hpp>
+#include <boost/ref.hpp> // reference_wrapper
+#include <boost/type_traits/is_pointer.hpp>
+#include <boost/function.hpp>
+#include <boost/static_assert.hpp>
+#include "llsd.h"
+#include "llsingleton.h"
+#include "lldependencies.h"
+
+// override this to allow binding free functions with more parameters
+#ifndef LLEVENTS_LISTENER_ARITY
+#define LLEVENTS_LISTENER_ARITY 10
+#endif
+
+// hack for testing
+#ifndef testable
+#define testable private
+#endif
+
+/*****************************************************************************
+* Signal and handler declarations
+* Using a single handler signature means that we can have a common handler
+* type, rather than needing a distinct one for each different handler.
+*****************************************************************************/
+
+/**
+ * A boost::signals Combiner that stops the first time a handler returns true
+ * We need this because we want to have our handlers return bool, so that
+ * we have the option to cause a handler to stop further processing. The
+ * default handler fails when the signal returns a value but has no slots.
+ */
+struct LLStopWhenHandled
+{
+ typedef bool result_type;
+
+ template<typename InputIterator>
+ result_type operator()(InputIterator first, InputIterator last) const
+ {
+ for (InputIterator si = first; si != last; ++si)
+ {
+ if (*si)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+};
+
+/**
+ * We want to have a standard signature for all signals; this way,
+ * we can easily document a protocol for communicating across
+ * dlls and into scripting languages someday.
+ *
+ * We want to return a bool to indicate whether the signal has been
+ * handled and should NOT be passed on to other listeners.
+ * Return true to stop further handling of the signal, and false
+ * to continue.
+ *
+ * We take an LLSD because this way the contents of the signal
+ * are independent of the API used to communicate it.
+ * It is const ref because then there's low cost to pass it;
+ * if you only need to inspect it, it's very cheap.
+ *
+ * @internal
+ * The @c float template parameter indicates that we will internally use @c
+ * float to indicate relative listener order on a given LLStandardSignal.
+ * Don't worry, the @c float values are strictly internal! They are not part
+ * of the interface, for the excellent reason that requiring the caller to
+ * specify a numeric key to establish order means that the caller must know
+ * the universe of possible values. We use LLDependencies for that instead.
+ */
+typedef boost::signals2::signal<bool(const LLSD&), LLStopWhenHandled, float> LLStandardSignal;
+/// Methods that forward listeners (e.g. constructed with
+/// <tt>boost::bind()</tt>) should accept (const LLEventListener&)
+typedef LLStandardSignal::slot_type LLEventListener;
+/// Result of registering a listener, supports <tt>connected()</tt>,
+/// <tt>disconnect()</tt> and <tt>blocked()</tt>
+typedef boost::signals2::connection LLBoundListener;
+/// Storing an LLBoundListener in LLTempBoundListener will disconnect the
+/// referenced listener when the LLTempBoundListener instance is destroyed.
+typedef boost::signals2::scoped_connection LLTempBoundListener;
+
+/**
+ * A common idiom for event-based code is to accept either a callable --
+ * directly called on completion -- or the string name of an LLEventPump on
+ * which to post the completion event. Specifying a parameter as <tt>const
+ * LLListenerOrPumpName&</tt> allows either.
+ *
+ * Calling a validly-constructed LLListenerOrPumpName, passing the LLSD
+ * 'event' object, either calls the callable or posts the event to the named
+ * LLEventPump.
+ *
+ * A default-constructed LLListenerOrPumpName is 'empty'. (This is useful as
+ * the default value of an optional method parameter.) Calling it throws
+ * LLListenerOrPumpName::Empty. Test for this condition beforehand using
+ * either <tt>if (param)</tt> or <tt>if (! param)</tt>.
+ */
+class LL_COMMON_API LLListenerOrPumpName
+{
+public:
+ /// passing string name of LLEventPump
+ LLListenerOrPumpName(const std::string& pumpname);
+ /// passing string literal (overload so compiler isn't forced to infer
+ /// double conversion)
+ LLListenerOrPumpName(const char* pumpname);
+ /// passing listener -- the "anything else" catch-all case. The type of an
+ /// object constructed by boost::bind() isn't intended to be written out.
+ /// Normally we'd just accept 'const LLEventListener&', but that would
+ /// require double implicit conversion: boost::bind() object to
+ /// LLEventListener, LLEventListener to LLListenerOrPumpName. So use a
+ /// template to forward anything.
+ template<typename T>
+ LLListenerOrPumpName(const T& listener): mListener(listener) {}
+
+ /// for omitted method parameter: uninitialized mListener
+ LLListenerOrPumpName() {}
+
+ /// test for validity
+ operator bool() const { return bool(mListener); }
+ bool operator! () const { return ! mListener; }
+
+ /// explicit accessor
+ const LLEventListener& getListener() const { return *mListener; }
+
+ /// implicit conversion to LLEventListener
+ operator LLEventListener() const { return *mListener; }
+
+ /// allow calling directly
+ bool operator()(const LLSD& event) const;
+
+ /// exception if you try to call when empty
+ struct Empty: public std::runtime_error
+ {
+ Empty(const std::string& what):
+ std::runtime_error(std::string("LLListenerOrPumpName::Empty: ") + what) {}
+ };
+
+private:
+ boost::optional<LLEventListener> mListener;
+};
+
+/*****************************************************************************
+* LLEventPumps
+*****************************************************************************/
+class LLEventPump;
+
+/**
+ * LLEventPumps is a Singleton manager through which one typically accesses
+ * this subsystem.
+ */
+class LL_COMMON_API LLEventPumps: public LLSingleton<LLEventPumps>
+{
+ friend class LLSingleton<LLEventPumps>;
+public:
+ /**
+ * Find or create an LLEventPump instance with a specific name. We return
+ * a reference so there's no question about ownership. obtain() @em finds
+ * an instance without conferring @em ownership.
+ */
+ LLEventPump& obtain(const std::string& name);
+ /**
+ * Flush all known LLEventPump instances
+ */
+ void flush();
+
+ /**
+ * Reset all known LLEventPump instances
+ * workaround for DEV-35406 crash on shutdown
+ */
+ void reset();
+
+private:
+ friend class LLEventPump;
+ /**
+ * Register a new LLEventPump instance (internal)
+ */
+ std::string registerNew(const LLEventPump&, const std::string& name, bool tweak);
+ /**
+ * Unregister a doomed LLEventPump instance (internal)
+ */
+ void unregister(const LLEventPump&);
+
+private:
+ LLEventPumps();
+ ~LLEventPumps();
+
+testable:
+ // Map of all known LLEventPump instances, whether or not we instantiated
+ // them. We store a plain old LLEventPump* because this map doesn't claim
+ // ownership of the instances. Though the common usage pattern is to
+ // request an instance using obtain(), it's fair to instantiate an
+ // LLEventPump subclass statically, as a class member, on the stack or on
+ // the heap. In such cases, the instantiating party is responsible for its
+ // lifespan.
+ typedef std::map<std::string, LLEventPump*> PumpMap;
+ PumpMap mPumpMap;
+ // Set of all LLEventPumps we instantiated. Membership in this set means
+ // we claim ownership, and will delete them when this LLEventPumps is
+ // destroyed.
+ typedef std::set<LLEventPump*> PumpSet;
+ PumpSet mOurPumps;
+ // LLEventPump names that should be instantiated as LLEventQueue rather
+ // than as LLEventStream
+ typedef std::set<std::string> PumpNames;
+ PumpNames mQueueNames;
+};
+
+/*****************************************************************************
+* details
+*****************************************************************************/
+namespace LLEventDetail
+{
+ /// Any callable capable of connecting an LLEventListener to an
+ /// LLStandardSignal to produce an LLBoundListener can be mapped to this
+ /// signature.
+ typedef boost::function<LLBoundListener(const LLEventListener&)> ConnectFunc;
+
+ /**
+ * Utility template function to use Visitor appropriately
+ *
+ * @param listener Callable to connect, typically a boost::bind()
+ * expression. This will be visited by Visitor using boost::visit_each().
+ * @param connect_func Callable that will connect() @a listener to an
+ * LLStandardSignal, returning LLBoundListener.
+ */
+ template <typename LISTENER>
+ LLBoundListener visit_and_connect(const LISTENER& listener,
+ const ConnectFunc& connect_func);
+} // namespace LLEventDetail
+
+/*****************************************************************************
+* LLEventTrackable
+*****************************************************************************/
+/**
+ * LLEventTrackable wraps boost::signals2::trackable, which resembles
+ * boost::trackable. Derive your listener class from LLEventTrackable instead,
+ * and use something like
+ * <tt>LLEventPump::listen(boost::bind(&YourTrackableSubclass::method,
+ * instance, _1))</tt>. This will implicitly disconnect when the object
+ * referenced by @c instance is destroyed.
+ *
+ * @note
+ * LLEventTrackable doesn't address a couple of cases:
+ * * Object destroyed during call
+ * - You enter a slot call in thread A.
+ * - Thread B destroys the object, which of course disconnects it from any
+ * future slot calls.
+ * - Thread A's call uses 'this', which now refers to a defunct object.
+ * Undefined behavior results.
+ * * Call during destruction
+ * - @c MySubclass is derived from LLEventTrackable.
+ * - @c MySubclass registers one of its own methods using
+ * <tt>LLEventPump::listen()</tt>.
+ * - The @c MySubclass object begins destruction. <tt>~MySubclass()</tt>
+ * runs, destroying state specific to the subclass. (For instance, a
+ * <tt>Foo*</tt> data member is <tt>delete</tt>d but not zeroed.)
+ * - The listening method will not be disconnected until
+ * <tt>~LLEventTrackable()</tt> runs.
+ * - Before we get there, another thread posts data to the @c LLEventPump
+ * instance, calling the @c MySubclass method.
+ * - The method in question relies on valid @c MySubclass state. (For
+ * instance, it attempts to dereference the <tt>Foo*</tt> pointer that was
+ * <tt>delete</tt>d but not zeroed.)
+ * - Undefined behavior results.
+ * If you suspect you may encounter any such scenario, you're better off
+ * managing the lifespan of your object with <tt>boost::shared_ptr</tt>.
+ * Passing <tt>LLEventPump::listen()</tt> a <tt>boost::bind()</tt> expression
+ * involving a <tt>boost::weak_ptr<Foo></tt> is recognized specially, engaging
+ * thread-safe Boost.Signals2 machinery.
+ */
+typedef boost::signals2::trackable LLEventTrackable;
+
+/*****************************************************************************
+* LLEventPump
+*****************************************************************************/
+/**
+ * LLEventPump is the base class interface through which we access the
+ * concrete subclasses LLEventStream and LLEventQueue.
+ *
+ * @NOTE
+ * LLEventPump derives from LLEventTrackable so that when you "chain"
+ * LLEventPump instances together, they will automatically disconnect on
+ * destruction. Please see LLEventTrackable documentation for situations in
+ * which this may be perilous across threads.
+ */
+class LL_COMMON_API LLEventPump: public LLEventTrackable
+{
+public:
+ /**
+ * Exception thrown by LLEventPump(). You are trying to instantiate an
+ * LLEventPump (subclass) using the same name as some other instance, and
+ * you didn't pass <tt>tweak=true</tt> to permit it to generate a unique
+ * variant.
+ */
+ struct DupPumpName: public std::runtime_error
+ {
+ DupPumpName(const std::string& what):
+ std::runtime_error(std::string("DupPumpName: ") + what) {}
+ };
+
+ /**
+ * Instantiate an LLEventPump (subclass) with the string name by which it
+ * can be found using LLEventPumps::obtain().
+ *
+ * If you pass (or default) @a tweak to @c false, then a duplicate name
+ * will throw DupPumpName. This won't happen if LLEventPumps::obtain()
+ * instantiates the LLEventPump, because obtain() uses find-or-create
+ * logic. It can only happen if you instantiate an LLEventPump in your own
+ * code -- and a collision with the name of some other LLEventPump is
+ * likely to cause much more subtle problems!
+ *
+ * When you hand-instantiate an LLEventPump, consider passing @a tweak as
+ * @c true. This directs LLEventPump() to append a suffix to the passed @a
+ * name to make it unique. You can retrieve the adjusted name by calling
+ * getName() on your new instance.
+ */
+ LLEventPump(const std::string& name, bool tweak=false);
+ virtual ~LLEventPump();
+
+ /// group exceptions thrown by listen(). We use exceptions because these
+ /// particular errors are likely to be coding errors, found and fixed by
+ /// the developer even before preliminary checkin.
+ struct ListenError: public std::runtime_error
+ {
+ ListenError(const std::string& what): std::runtime_error(what) {}
+ };
+ /**
+ * exception thrown by listen(). You are attempting to register a
+ * listener on this LLEventPump using the same listener name as an
+ * already-registered listener.
+ */
+ struct DupListenerName: public ListenError
+ {
+ DupListenerName(const std::string& what):
+ ListenError(std::string("DupListenerName: ") + what)
+ {}
+ };
+ /**
+ * exception thrown by listen(). The order dependencies specified for your
+ * listener are incompatible with existing listeners.
+ *
+ * Consider listener "a" which specifies before "b" and "b" which
+ * specifies before "c". You are now attempting to register "c" before
+ * "a". There is no order that can satisfy all constraints.
+ */
+ struct Cycle: public ListenError
+ {
+ Cycle(const std::string& what): ListenError(std::string("Cycle: ") + what) {}
+ };
+ /**
+ * exception thrown by listen(). This one means that your new listener
+ * would force a change to the order of previously-registered listeners,
+ * and we don't have a good way to implement that.
+ *
+ * Consider listeners "some", "other" and "third". "some" and "other" are
+ * registered earlier without specifying relative order, so "other"
+ * happens to be first. Now you attempt to register "third" after "some"
+ * and before "other". Whoops, that would require swapping "some" and
+ * "other", which we can't do. Instead we throw this exception.
+ *
+ * It may not be possible to change the registration order so we already
+ * know "third"s order requirement by the time we register the second of
+ * "some" and "other". A solution would be to specify that "some" must
+ * come before "other", or equivalently that "other" must come after
+ * "some".
+ */
+ struct OrderChange: public ListenError
+ {
+ OrderChange(const std::string& what): ListenError(std::string("OrderChange: ") + what) {}
+ };
+
+ /// used by listen()
+ typedef std::vector<std::string> NameList;
+ /// convenience placeholder for when you explicitly want to pass an empty
+ /// NameList
+ const static NameList empty;
+
+ /// Get this LLEventPump's name
+ std::string getName() const { return mName; }
+
+ /**
+ * Register a new listener with a unique name. Specify an optional list
+ * of other listener names after which this one must be called, likewise
+ * an optional list of other listener names before which this one must be
+ * called. The other listeners mentioned need not yet be registered
+ * themselves. listen() can throw any ListenError; see ListenError
+ * subclasses.
+ *
+ * The listener name must be unique among active listeners for this
+ * LLEventPump, else you get DupListenerName. If you don't care to invent
+ * a name yourself, use inventName(). (I was tempted to recognize e.g. ""
+ * and internally generate a distinct name for that case. But that would
+ * handle badly the scenario in which you want to add, remove, re-add,
+ * etc. the same listener: each new listen() call would necessarily
+ * perform a new dependency sort. Assuming you specify the same
+ * after/before lists each time, using inventName() when you first
+ * instantiate your listener, then passing the same name on each listen()
+ * call, allows us to optimize away the second and subsequent dependency
+ * sorts.
+ *
+ * If (as is typical) you pass a <tt>boost::bind()</tt> expression as @a
+ * listener, listen() will inspect the components of that expression. If a
+ * bound object matches any of several cases, the connection will
+ * automatically be disconnected when that object is destroyed.
+ *
+ * * You bind a <tt>boost::weak_ptr</tt>.
+ * * Binding a <tt>boost::shared_ptr</tt> that way would ensure that the
+ * referenced object would @em never be destroyed, since the @c
+ * shared_ptr stored in the LLEventPump would remain an outstanding
+ * reference. Use the weaken() function to convert your @c shared_ptr to
+ * @c weak_ptr. Because this is easy to forget, binding a @c shared_ptr
+ * will produce a compile error (@c BOOST_STATIC_ASSERT failure).
+ * * You bind a simple pointer or reference to an object derived from
+ * <tt>boost::enable_shared_from_this</tt>. (UNDER CONSTRUCTION)
+ * * You bind a simple pointer or reference to an object derived from
+ * LLEventTrackable. Unlike the cases described above, though, this is
+ * vulnerable to a couple of cross-thread race conditions, as described
+ * in the LLEventTrackable documentation.
+ */
+ template <typename LISTENER>
+ LLBoundListener listen(const std::string& name, const LISTENER& listener,
+ const NameList& after=NameList(),
+ const NameList& before=NameList())
+ {
+ // Examine listener, using our listen_impl() method to make the
+ // actual connection.
+ // This is why listen() is a template. Conversion from boost::bind()
+ // to LLEventListener performs type erasure, so it's important to look
+ // at the boost::bind object itself before that happens.
+ return LLEventDetail::visit_and_connect(listener,
+ boost::bind(&LLEventPump::listen_impl,
+ this,
+ name,
+ _1,
+ after,
+ before));
+ }
+
+ /// Get the LLBoundListener associated with the passed name (dummy
+ /// LLBoundListener if not found)
+ virtual LLBoundListener getListener(const std::string& name) const;
+ /**
+ * Instantiate one of these to block an existing connection:
+ * @code
+ * { // in some local scope
+ * LLEventPump::Blocker block(someLLBoundListener);
+ * // code that needs the connection blocked
+ * } // unblock the connection again
+ * @endcode
+ */
+ typedef boost::signals2::shared_connection_block Blocker;
+ /// Unregister a listener by name. Prefer this to
+ /// <tt>getListener(name).disconnect()</tt> because stopListening() also
+ /// forgets this name.
+ virtual void stopListening(const std::string& name);
+ /// Post an event to all listeners. The @c bool return is only meaningful
+ /// if the underlying leaf class is LLEventStream -- beware of relying on
+ /// it too much! Truthfully, we return @c bool mostly to permit chaining
+ /// one LLEventPump as a listener on another.
+ virtual bool post(const LLSD&) = 0;
+ /// Enable/disable: while disabled, silently ignore all post() calls
+ virtual void enable(bool enabled=true) { mEnabled = enabled; }
+ /// query
+ virtual bool enabled() const { return mEnabled; }
+
+ /// Generate a distinct name for a listener -- see listen()
+ static std::string inventName(const std::string& pfx="listener");
+
+private:
+ friend class LLEventPumps;
+ /// flush queued events
+ virtual void flush() {}
+
+ virtual void reset();
+
+private:
+ virtual LLBoundListener listen_impl(const std::string& name, const LLEventListener&,
+ const NameList& after,
+ const NameList& before);
+ std::string mName;
+
+protected:
+ /// implement the dispatching
+ boost::scoped_ptr<LLStandardSignal> mSignal;
+
+ /// valve open?
+ bool mEnabled;
+ /// Map of named listeners. This tracks the listeners that actually exist
+ /// at this moment. When we stopListening(), we discard the entry from
+ /// this map.
+ typedef std::map<std::string, boost::signals2::connection> ConnectionMap;
+ ConnectionMap mConnections;
+ typedef LLDependencies<std::string, float> DependencyMap;
+ /// Dependencies between listeners. For each listener, track the float
+ /// used to establish its place in mSignal's order. This caches all the
+ /// listeners that have ever registered; stopListening() does not discard
+ /// the entry from this map. This is to avoid a new dependency sort if the
+ /// same listener with the same dependencies keeps hopping on and off this
+ /// LLEventPump.
+ DependencyMap mDeps;
+};
+
+/*****************************************************************************
+* LLEventStream
+*****************************************************************************/
+/**
+ * LLEventStream is a thin wrapper around LLStandardSignal. Posting an
+ * event immediately calls all registered listeners.
+ */
+class LL_COMMON_API LLEventStream: public LLEventPump
+{
+public:
+ LLEventStream(const std::string& name, bool tweak=false): LLEventPump(name, tweak) {}
+ virtual ~LLEventStream() {}
+
+ /// Post an event to all listeners
+ virtual bool post(const LLSD& event);
+};
+
+/*****************************************************************************
+* LLEventQueue
+*****************************************************************************/
+/**
+ * LLEventQueue isa LLEventPump whose post() method defers calling registered
+ * listeners until flush() is called.
+ */
+class LL_COMMON_API LLEventQueue: public LLEventPump
+{
+public:
+ LLEventQueue(const std::string& name, bool tweak=false): LLEventPump(name, tweak) {}
+ virtual ~LLEventQueue() {}
+
+ /// Post an event to all listeners
+ virtual bool post(const LLSD& event);
+
+private:
+ /// flush queued events
+ virtual void flush();
+
+private:
+ typedef std::deque<LLSD> EventQueue;
+ EventQueue mEventQueue;
+};
+
+/*****************************************************************************
+* LLReqID
+*****************************************************************************/
+/**
+ * This class helps the implementer of a given event API to honor the
+ * ["reqid"] convention. By this convention, each event API stamps into its
+ * response LLSD a ["reqid"] key whose value echoes the ["reqid"] value, if
+ * any, from the corresponding request.
+ *
+ * This supports an (atypical, but occasionally necessary) use case in which
+ * two or more asynchronous requests are multiplexed onto the same ["reply"]
+ * LLEventPump. Since the response events could arrive in arbitrary order, the
+ * caller must be able to demux them. It does so by matching the ["reqid"]
+ * value in each response with the ["reqid"] value in the corresponding
+ * request.
+ *
+ * It is the caller's responsibility to ensure distinct ["reqid"] values for
+ * that case. Though LLSD::UUID is guaranteed to work, it might be overkill:
+ * the "namespace" of unique ["reqid"] values is simply the set of requests
+ * specifying the same ["reply"] LLEventPump name.
+ *
+ * Making a given event API echo the request's ["reqid"] into the response is
+ * nearly trivial. This helper is mostly for mnemonic purposes, to serve as a
+ * place to put these comments. We hope that each time a coder implements a
+ * new event API based on some existing one, s/he will say, "Huh, what's an
+ * LLReqID?" and look up this material.
+ *
+ * The hardest part about the convention is deciding where to store the
+ * ["reqid"] value. Ironically, LLReqID can't help with that: you must store
+ * an LLReqID instance in whatever storage will persist until the reply is
+ * sent. For example, if the request ultimately ends up using a Responder
+ * subclass, storing an LLReqID instance in the Responder works.
+ *
+ * @note
+ * The @em implementer of an event API must honor the ["reqid"] convention.
+ * However, the @em caller of an event API need only use it if s/he is sharing
+ * the same ["reply"] LLEventPump for two or more asynchronous event API
+ * requests.
+ *
+ * In most cases, it's far easier for the caller to instantiate a local
+ * LLEventStream and pass its name to the event API in question. Then it's
+ * perfectly reasonable not to set a ["reqid"] key in the request, ignoring
+ * the @c isUndefined() ["reqid"] value in the response.
+ */
+class LL_COMMON_API LLReqID
+{
+public:
+ /**
+ * If you have the request in hand at the time you instantiate the
+ * LLReqID, pass that request to extract its ["reqid"].
+ */
+ LLReqID(const LLSD& request):
+ mReqid(request["reqid"])
+ {}
+ /// If you don't yet have the request, use setFrom() later.
+ LLReqID() {}
+
+ /// Extract and store the ["reqid"] value from an incoming request.
+ void setFrom(const LLSD& request)
+ {
+ mReqid = request["reqid"];
+ }
+
+ /// Set ["reqid"] key into a pending response LLSD object.
+ void stamp(LLSD& response) const;
+
+ /// Make a whole new response LLSD object with our ["reqid"].
+ LLSD makeResponse() const
+ {
+ LLSD response;
+ stamp(response);
+ return response;
+ }
+
+ /// Not really sure of a use case for this accessor...
+ LLSD getReqID() const { return mReqid; }
+
+private:
+ LLSD mReqid;
+};
+
+/*****************************************************************************
+* Underpinnings
+*****************************************************************************/
+/**
+ * We originally provided a suite of overloaded
+ * LLEventTrackable::listenTo(LLEventPump&, ...) methods that would call
+ * LLEventPump::listen(...) and then pass the returned LLBoundListener to
+ * LLEventTrackable::track(). This was workable but error-prone: the coder
+ * must remember to call listenTo() rather than the more straightforward
+ * listen() method.
+ *
+ * Now we publish only the single canonical listen() method, so there's a
+ * uniform mechanism. Having a single way to do this is good, in that there's
+ * no question in the coder's mind which of several alternatives to choose.
+ *
+ * To support automatic connection management, we use boost::visit_each
+ * (http://www.boost.org/doc/libs/1_37_0/doc/html/boost/visit_each.html) to
+ * inspect each argument of a boost::bind expression. (Although the visit_each
+ * mechanism was first introduced with the original Boost.Signals library, it
+ * was only later documented.)
+ *
+ * Cases:
+ * * At least one of the function's arguments is a boost::weak_ptr<T>. Pass
+ * the corresponding shared_ptr to slot_type::track(). Ideally that would be
+ * the object whose method we want to call, but in fact we do the same for
+ * any weak_ptr we might find among the bound arguments. If we're passing
+ * our bound method a weak_ptr to some object, wouldn't the destruction of
+ * that object invalidate the call? So we disconnect automatically when any
+ * such object is destroyed. This is the mechanism preferred by boost::
+ * signals2.
+ * * One of the functions's arguments is a boost::shared_ptr<T>. This produces
+ * a compile error: the bound copy of the shared_ptr stored in the
+ * boost_bind object stored in the signal object would make the referenced
+ * T object immortal. We provide a weaken() function. Pass
+ * weaken(your_shared_ptr) instead. (We can inspect, but not modify, the
+ * boost::bind object. Otherwise we'd replace the shared_ptr with weak_ptr
+ * implicitly and just proceed.)
+ * * One of the function's arguments is a plain pointer/reference to an object
+ * derived from boost::enable_shared_from_this. We assume that this object
+ * is managed using boost::shared_ptr, so we implicitly extract a shared_ptr
+ * and track that. (UNDER CONSTRUCTION)
+ * * One of the function's arguments is derived from LLEventTrackable. Pass
+ * the LLBoundListener to its LLEventTrackable::track(). This is vulnerable
+ * to a couple different race conditions, as described in LLEventTrackable
+ * documentation. (NOTE: Now that LLEventTrackable is a typedef for
+ * boost::signals2::trackable, the Signals2 library handles this itself, so
+ * our visitor needs no special logic for this case.)
+ * * Any other argument type is irrelevant to automatic connection management.
+ */
+
+namespace LLEventDetail
+{
+ template <typename F>
+ const F& unwrap(const F& f) { return f; }
+
+ template <typename F>
+ const F& unwrap(const boost::reference_wrapper<F>& f) { return f.get(); }
+
+ // Most of the following is lifted from the Boost.Signals use of
+ // visit_each.
+ template<bool Cond> struct truth {};
+
+ /**
+ * boost::visit_each() Visitor, used on a template argument <tt>const F&
+ * f</tt> as follows (see visit_and_connect()):
+ * @code
+ * LLEventListener listener(f);
+ * Visitor visitor(listener); // bind listener so it can track() shared_ptrs
+ * using boost::visit_each; // allow unqualified visit_each() call for ADL
+ * visit_each(visitor, unwrap(f));
+ * @endcode
+ */
+ class Visitor
+ {
+ public:
+ /**
+ * Visitor binds a reference to LLEventListener so we can track() any
+ * shared_ptrs we find in the argument list.
+ */
+ Visitor(LLEventListener& listener):
+ mListener(listener)
+ {
+ }
+
+ /**
+ * boost::visit_each() calls this method for each component of a
+ * boost::bind() expression.
+ */
+ template <typename T>
+ void operator()(const T& t) const
+ {
+ decode(t, 0);
+ }
+
+ private:
+ // decode() decides between a reference wrapper and anything else
+ // boost::ref() variant
+ template<typename T>
+ void decode(const boost::reference_wrapper<T>& t, int) const
+ {
+// add_if_trackable(t.get_pointer());
+ }
+
+ // decode() anything else
+ template<typename T>
+ void decode(const T& t, long) const
+ {
+ typedef truth<(boost::is_pointer<T>::value)> is_a_pointer;
+ maybe_get_pointer(t, is_a_pointer());
+ }
+
+ // maybe_get_pointer() decides between a pointer and a non-pointer
+ // plain pointer variant
+ template<typename T>
+ void maybe_get_pointer(const T& t, truth<true>) const
+ {
+// add_if_trackable(t);
+ }
+
+ // shared_ptr variant
+ template<typename T>
+ void maybe_get_pointer(const boost::shared_ptr<T>& t, truth<false>) const
+ {
+ // If we have a shared_ptr to this object, it doesn't matter
+ // whether the object is derived from LLEventTrackable, so no
+ // further analysis of T is needed.
+// mListener.track(t);
+
+ // Make this case illegal. Passing a bound shared_ptr to
+ // slot_type::track() is useless, since the bound shared_ptr will
+ // keep the object alive anyway! Force the coder to cast to weak_ptr.
+
+ // Trivial as it is, make the BOOST_STATIC_ASSERT() condition
+ // dependent on template param so the macro is only evaluated if
+ // this method is in fact instantiated, as described here:
+ // http://www.boost.org/doc/libs/1_34_1/doc/html/boost_staticassert.html
+
+ // ATTENTION: Don't bind a shared_ptr<anything> using
+ // LLEventPump::listen(boost::bind()). Doing so captures a copy of
+ // the shared_ptr, making the referenced object effectively
+ // immortal. Use the weaken() function, e.g.:
+ // somepump.listen(boost::bind(...weaken(my_shared_ptr)...));
+ // This lets us automatically disconnect when the referenced
+ // object is destroyed.
+ BOOST_STATIC_ASSERT(sizeof(T) == 0);
+ }
+
+ // weak_ptr variant
+ template<typename T>
+ void maybe_get_pointer(const boost::weak_ptr<T>& t, truth<false>) const
+ {
+ // If we have a weak_ptr to this object, it doesn't matter
+ // whether the object is derived from LLEventTrackable, so no
+ // further analysis of T is needed.
+ mListener.track(t);
+// std::cout << "Found weak_ptr<" << typeid(T).name() << ">!\n";
+ }
+
+#if 0
+ // reference to anything derived from boost::enable_shared_from_this
+ template <typename T>
+ inline void maybe_get_pointer(const boost::enable_shared_from_this<T>& ct,
+ truth<false>) const
+ {
+ // Use the slot_type::track(shared_ptr) mechanism. Cast away
+ // const-ness because (in our code base anyway) it's unusual
+ // to find shared_ptr<const T>.
+ boost::enable_shared_from_this<T>&
+ t(const_cast<boost::enable_shared_from_this<T>&>(ct));
+ std::cout << "Capturing shared_from_this()" << std::endl;
+ boost::shared_ptr<T> sp(t.shared_from_this());
+/*==========================================================================*|
+ std::cout << "Capturing weak_ptr" << std::endl;
+ boost::weak_ptr<T> wp(sp);
+|*==========================================================================*/
+ std::cout << "Tracking shared__ptr" << std::endl;
+ mListener.track(sp);
+ }
+#endif
+
+ // non-pointer variant
+ template<typename T>
+ void maybe_get_pointer(const T& t, truth<false>) const
+ {
+ // Take the address of this object, because the object itself may be
+ // trackable
+// add_if_trackable(boost::addressof(t));
+ }
+
+/*==========================================================================*|
+ // add_if_trackable() adds LLEventTrackable objects to mTrackables
+ inline void add_if_trackable(const LLEventTrackable* t) const
+ {
+ if (t)
+ {
+ }
+ }
+
+ // pointer to anything not an LLEventTrackable subclass
+ inline void add_if_trackable(const void*) const
+ {
+ }
+
+ // pointer to free function
+ // The following construct uses the preprocessor to generate
+ // add_if_trackable() overloads accepting pointer-to-function taking
+ // 0, 1, ..., LLEVENTS_LISTENER_ARITY parameters of arbitrary type.
+#define BOOST_PP_LOCAL_MACRO(n) \
+ template <typename R \
+ BOOST_PP_COMMA_IF(n) \
+ BOOST_PP_ENUM_PARAMS(n, typename T)> \
+ inline void \
+ add_if_trackable(R (*)(BOOST_PP_ENUM_PARAMS(n, T))) const \
+ { \
+ }
+#define BOOST_PP_LOCAL_LIMITS (0, LLEVENTS_LISTENER_ARITY)
+#include BOOST_PP_LOCAL_ITERATE()
+#undef BOOST_PP_LOCAL_MACRO
+#undef BOOST_PP_LOCAL_LIMITS
+|*==========================================================================*/
+
+ /// Bind a reference to the LLEventListener to call its track() method.
+ LLEventListener& mListener;
+ };
+
+ /**
+ * Utility template function to use Visitor appropriately
+ *
+ * @param raw_listener Callable to connect, typically a boost::bind()
+ * expression. This will be visited by Visitor using boost::visit_each().
+ * @param connect_funct Callable that will connect() @a raw_listener to an
+ * LLStandardSignal, returning LLBoundListener.
+ */
+ template <typename LISTENER>
+ LLBoundListener visit_and_connect(const LISTENER& raw_listener,
+ const ConnectFunc& connect_func)
+ {
+ // Capture the listener
+ LLEventListener listener(raw_listener);
+ // Define our Visitor, binding the listener so we can call
+ // listener.track() if we discover any shared_ptr<Foo>.
+ LLEventDetail::Visitor visitor(listener);
+ // Allow unqualified visit_each() call for ADL
+ using boost::visit_each;
+ // Visit each component of a boost::bind() expression. Pass
+ // 'raw_listener', our template argument, rather than 'listener' from
+ // which type details have been erased. unwrap() comes from
+ // Boost.Signals, in case we were passed a boost::ref().
+ visit_each(visitor, LLEventDetail::unwrap(raw_listener));
+ // Make the connection using passed function. At present, wrapping
+ // this functionality into this function is a bit silly: we don't
+ // really need a visit_and_connect() function any more, just a visit()
+ // function. The definition of this function dates from when, after
+ // visit_each(), after establishing the connection, we had to
+ // postprocess the new connection with the visitor object. That's no
+ // longer necessary.
+ return connect_func(listener);
+ }
+} // namespace LLEventDetail
+
+// Somewhat to my surprise, passing boost::bind(...boost::weak_ptr<T>...) to
+// listen() fails in Boost code trying to instantiate LLEventListener (i.e.
+// LLStandardSignal::slot_type) because the boost::get_pointer() utility function isn't
+// specialized for boost::weak_ptr. This remedies that omission.
+namespace boost
+{
+ template <typename T>
+ T* get_pointer(const weak_ptr<T>& ptr) { return shared_ptr<T>(ptr).get(); }
+}
+
+/// Since we forbid use of listen(boost::bind(...shared_ptr<T>...)), provide an
+/// easy way to cast to the corresponding weak_ptr.
+template <typename T>
+boost::weak_ptr<T> weaken(const boost::shared_ptr<T>& ptr)
+{
+ return boost::weak_ptr<T>(ptr);
+}
+
+#endif /* ! defined(LL_LLEVENTS_H) */
diff --git a/indra/llcommon/llfasttimer.h b/indra/llcommon/llfasttimer.h
index 576e45d2ae..905d736d62 100644
--- a/indra/llcommon/llfasttimer.h
+++ b/indra/llcommon/llfasttimer.h
@@ -1,317 +1,317 @@
-/**
- * @file llfasttimer.h
- * @brief Declaration of a fast timer.
- *
- * $LicenseInfo:firstyear=2004&license=viewergpl$
- *
- * Copyright (c) 2004-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_FASTTIMER_H
-#define LL_FASTTIMER_H
-
-#include "llinstancetracker.h"
-
-#define FAST_TIMER_ON 1
-
-#if LL_WINDOWS
-
-// shift off lower 8 bits for lower resolution but longer term timing
-// on 1Ghz machine, a 32-bit word will hold ~1000 seconds of timing
-inline U32 get_cpu_clock_count_32()
-{
- U32 ret_val;
- __asm
- {
- _emit 0x0f
- _emit 0x31
- shr eax,8
- shl edx,24
- or eax, edx
- mov dword ptr [ret_val], eax
- }
- return ret_val;
-}
-
-// return full timer value, still shifted by 8 bits
-inline U64 get_cpu_clock_count_64()
-{
- U64 ret_val;
- __asm
- {
- _emit 0x0f
- _emit 0x31
- mov eax,eax
- mov edx,edx
- mov dword ptr [ret_val+4], edx
- mov dword ptr [ret_val], eax
- }
- return ret_val >> 8;
-}
-
-#endif // LL_WINDOWS
-
-#if (LL_LINUX || LL_SOLARIS || LL_DARWIN) && (defined(__i386__) || defined(__amd64__))
-inline U32 get_cpu_clock_count_32()
-{
- U64 x;
- __asm__ volatile (".byte 0x0f, 0x31": "=A"(x));
- return (U32)x >> 8;
-}
-
-inline U32 get_cpu_clock_count_64()
-{
- U64 x;
- __asm__ volatile (".byte 0x0f, 0x31": "=A"(x));
- return x >> 8;
-}
-#endif
-
-#if ( LL_DARWIN && !(defined(__i386__) || defined(__amd64__))) || (LL_SOLARIS && defined(__sparc__))
-//
-// Mac PPC (deprecated) & Solaris SPARC implementation of CPU clock
-//
-// Just use gettimeofday implementation for now
-
-inline U32 get_cpu_clock_count_32()
-{
- return (U32)get_clock_count();
-}
-
-inline U32 get_cpu_clock_count_64()
-{
- return get_clock_count();
-}
-#endif
-
-class LLMutex;
-
-#include <queue>
-#include "llsd.h"
-
-
-class LLFastTimer
-{
-public:
- // stores a "named" timer instance to be reused via multiple LLFastTimer stack instances
- class NamedTimer
- : public LLInstanceTracker<NamedTimer>
- {
- friend class DeclareTimer;
- public:
- ~NamedTimer();
-
- enum { HISTORY_NUM = 60 };
-
- const std::string& getName() const { return mName; }
- NamedTimer* getParent() const { return mParent; }
- void setParent(NamedTimer* parent);
- S32 getDepth();
- std::string getToolTip(S32 history_index = -1);
-
- typedef std::vector<NamedTimer*>::const_iterator child_const_iter;
- child_const_iter beginChildren();
- child_const_iter endChildren();
- std::vector<NamedTimer*>& getChildren();
-
- void setCollapsed(bool collapsed) { mCollapsed = collapsed; }
- bool getCollapsed() const { return mCollapsed; }
-
- U32 getCountAverage() const { return mCountAverage; }
- U32 getCallAverage() const { return mCallAverage; }
-
- U32 getHistoricalCount(S32 history_index = 0) const;
- U32 getHistoricalCalls(S32 history_index = 0) const;
-
- static NamedTimer& getRootNamedTimer();
-
- struct FrameState
- {
- FrameState(NamedTimer* timerp);
-
- U32 mSelfTimeCounter;
- U32 mCalls;
- FrameState* mParent; // info for caller timer
- FrameState* mLastCaller; // used to bootstrap tree construction
- NamedTimer* mTimer;
- U16 mActiveCount; // number of timers with this ID active on stack
- bool mMoveUpTree; // needs to be moved up the tree of timers at the end of frame
- };
-
- S32 getFrameStateIndex() const { return mFrameStateIndex; }
-
- FrameState& getFrameState() const;
-
-
- private:
- friend class LLFastTimer;
- friend class NamedTimerFactory;
-
- //
- // methods
- //
- NamedTimer(const std::string& name);
- // recursive call to gather total time from children
- static void accumulateTimings();
-
- // updates cumulative times and hierarchy,
- // can be called multiple times in a frame, at any point
- static void processTimes();
-
- static void buildHierarchy();
- static void resetFrame();
- static void reset();
-
-
- //
- // members
- //
- S32 mFrameStateIndex;
-
- std::string mName;
-
- U32 mTotalTimeCounter;
-
- U32 mCountAverage;
- U32 mCallAverage;
-
- U32* mCountHistory;
- U32* mCallHistory;
-
- // tree structure
- NamedTimer* mParent; // NamedTimer of caller(parent)
- std::vector<NamedTimer*> mChildren;
- bool mCollapsed; // don't show children
- bool mNeedsSorting; // sort children whenever child added
-
- };
-
- // used to statically declare a new named timer
- class DeclareTimer
- : public LLInstanceTracker<DeclareTimer>
- {
- public:
- DeclareTimer(const std::string& name, bool open);
- DeclareTimer(const std::string& name);
-
- static void updateCachedPointers();
-
- // convertable to NamedTimer::FrameState for convenient usage of LLFastTimer(declared_timer)
- operator NamedTimer::FrameState&() { return *mFrameState; }
- private:
- NamedTimer& mTimer;
- NamedTimer::FrameState* mFrameState;
- };
-
-
-public:
- static LLMutex* sLogLock;
- static std::queue<LLSD> sLogQueue;
- static BOOL sLog;
- static BOOL sMetricLog;
-
- typedef std::vector<NamedTimer::FrameState> info_list_t;
- static info_list_t& getFrameStateList();
-
- enum RootTimerMarker { ROOT };
- LLFastTimer(RootTimerMarker);
-
- LLFastTimer(NamedTimer::FrameState& timer)
- : mFrameState(&timer)
- {
-#if FAST_TIMER_ON
- NamedTimer::FrameState* frame_state = &timer;
- U32 cur_time = get_cpu_clock_count_32();
- mStartSelfTime = cur_time;
- mStartTotalTime = cur_time;
-
- frame_state->mActiveCount++;
- frame_state->mCalls++;
- // keep current parent as long as it is active when we are
- frame_state->mMoveUpTree |= (frame_state->mParent->mActiveCount == 0);
-
- mLastTimer = sCurTimer;
- sCurTimer = this;
-#endif
- }
-
- ~LLFastTimer()
- {
-#if FAST_TIMER_ON
- NamedTimer::FrameState* frame_state = mFrameState;
- U32 cur_time = get_cpu_clock_count_32();
- frame_state->mSelfTimeCounter += cur_time - mStartSelfTime;
-
- frame_state->mActiveCount--;
- LLFastTimer* last_timer = mLastTimer;
- sCurTimer = last_timer;
-
- // store last caller to bootstrap tree creation
- frame_state->mLastCaller = last_timer->mFrameState;
-
- // we are only tracking self time, so subtract our total time delta from parents
- U32 total_time = cur_time - mStartTotalTime;
- last_timer->mStartSelfTime += total_time;
-#endif
- }
-
-
- // call this once a frame to reset timers
- static void nextFrame();
-
- // dumps current cumulative frame stats to log
- // call nextFrame() to reset timers
- static void dumpCurTimes();
-
- // call this to reset timer hierarchy, averages, etc.
- static void reset();
-
- static U64 countsPerSecond();
- static S32 getLastFrameIndex() { return sLastFrameIndex; }
- static S32 getCurFrameIndex() { return sCurFrameIndex; }
-
- static void writeLog(std::ostream& os);
- static const NamedTimer* getTimerByName(const std::string& name);
-
-public:
- static bool sPauseHistory;
- static bool sResetHistory;
-
-private:
- typedef std::vector<LLFastTimer*> timer_stack_t;
- static LLFastTimer* sCurTimer;
- static S32 sCurFrameIndex;
- static S32 sLastFrameIndex;
- static U64 sLastFrameTime;
- static info_list_t* sTimerInfos;
-
- U32 mStartSelfTime; // start time + time of all child timers
- U32 mStartTotalTime; // start time + time of all child timers
- NamedTimer::FrameState* mFrameState;
- LLFastTimer* mLastTimer;
-};
-
-#endif // LL_LLFASTTIMER_H
+/**
+ * @file llfasttimer.h
+ * @brief Declaration of a fast timer.
+ *
+ * $LicenseInfo:firstyear=2004&license=viewergpl$
+ *
+ * Copyright (c) 2004-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_FASTTIMER_H
+#define LL_FASTTIMER_H
+
+#include "llinstancetracker.h"
+
+#define FAST_TIMER_ON 1
+
+#if LL_WINDOWS
+
+// shift off lower 8 bits for lower resolution but longer term timing
+// on 1Ghz machine, a 32-bit word will hold ~1000 seconds of timing
+inline U32 get_cpu_clock_count_32()
+{
+ U32 ret_val;
+ __asm
+ {
+ _emit 0x0f
+ _emit 0x31
+ shr eax,8
+ shl edx,24
+ or eax, edx
+ mov dword ptr [ret_val], eax
+ }
+ return ret_val;
+}
+
+// return full timer value, still shifted by 8 bits
+inline U64 get_cpu_clock_count_64()
+{
+ U64 ret_val;
+ __asm
+ {
+ _emit 0x0f
+ _emit 0x31
+ mov eax,eax
+ mov edx,edx
+ mov dword ptr [ret_val+4], edx
+ mov dword ptr [ret_val], eax
+ }
+ return ret_val >> 8;
+}
+
+#endif // LL_WINDOWS
+
+#if (LL_LINUX || LL_SOLARIS || LL_DARWIN) && (defined(__i386__) || defined(__amd64__))
+inline U32 get_cpu_clock_count_32()
+{
+ U64 x;
+ __asm__ volatile (".byte 0x0f, 0x31": "=A"(x));
+ return (U32)x >> 8;
+}
+
+inline U32 get_cpu_clock_count_64()
+{
+ U64 x;
+ __asm__ volatile (".byte 0x0f, 0x31": "=A"(x));
+ return x >> 8;
+}
+#endif
+
+#if ( LL_DARWIN && !(defined(__i386__) || defined(__amd64__))) || (LL_SOLARIS && defined(__sparc__))
+//
+// Mac PPC (deprecated) & Solaris SPARC implementation of CPU clock
+//
+// Just use gettimeofday implementation for now
+
+inline U32 get_cpu_clock_count_32()
+{
+ return (U32)get_clock_count();
+}
+
+inline U32 get_cpu_clock_count_64()
+{
+ return get_clock_count();
+}
+#endif
+
+class LLMutex;
+
+#include <queue>
+#include "llsd.h"
+
+
+class LL_COMMON_API LLFastTimer
+{
+public:
+ // stores a "named" timer instance to be reused via multiple LLFastTimer stack instances
+ class LL_COMMON_API NamedTimer
+ : public LLInstanceTracker<NamedTimer>
+ {
+ friend class DeclareTimer;
+ public:
+ ~NamedTimer();
+
+ enum { HISTORY_NUM = 60 };
+
+ const std::string& getName() const { return mName; }
+ NamedTimer* getParent() const { return mParent; }
+ void setParent(NamedTimer* parent);
+ S32 getDepth();
+ std::string getToolTip(S32 history_index = -1);
+
+ typedef std::vector<NamedTimer*>::const_iterator child_const_iter;
+ child_const_iter beginChildren();
+ child_const_iter endChildren();
+ std::vector<NamedTimer*>& getChildren();
+
+ void setCollapsed(bool collapsed) { mCollapsed = collapsed; }
+ bool getCollapsed() const { return mCollapsed; }
+
+ U32 getCountAverage() const { return mCountAverage; }
+ U32 getCallAverage() const { return mCallAverage; }
+
+ U32 getHistoricalCount(S32 history_index = 0) const;
+ U32 getHistoricalCalls(S32 history_index = 0) const;
+
+ static NamedTimer& getRootNamedTimer();
+
+ struct FrameState
+ {
+ FrameState(NamedTimer* timerp);
+
+ U32 mSelfTimeCounter;
+ U32 mCalls;
+ FrameState* mParent; // info for caller timer
+ FrameState* mLastCaller; // used to bootstrap tree construction
+ NamedTimer* mTimer;
+ U16 mActiveCount; // number of timers with this ID active on stack
+ bool mMoveUpTree; // needs to be moved up the tree of timers at the end of frame
+ };
+
+ S32 getFrameStateIndex() const { return mFrameStateIndex; }
+
+ FrameState& getFrameState() const;
+
+
+ private:
+ friend class LLFastTimer;
+ friend class NamedTimerFactory;
+
+ //
+ // methods
+ //
+ NamedTimer(const std::string& name);
+ // recursive call to gather total time from children
+ static void accumulateTimings();
+
+ // updates cumulative times and hierarchy,
+ // can be called multiple times in a frame, at any point
+ static void processTimes();
+
+ static void buildHierarchy();
+ static void resetFrame();
+ static void reset();
+
+
+ //
+ // members
+ //
+ S32 mFrameStateIndex;
+
+ std::string mName;
+
+ U32 mTotalTimeCounter;
+
+ U32 mCountAverage;
+ U32 mCallAverage;
+
+ U32* mCountHistory;
+ U32* mCallHistory;
+
+ // tree structure
+ NamedTimer* mParent; // NamedTimer of caller(parent)
+ std::vector<NamedTimer*> mChildren;
+ bool mCollapsed; // don't show children
+ bool mNeedsSorting; // sort children whenever child added
+
+ };
+
+ // used to statically declare a new named timer
+ class LL_COMMON_API DeclareTimer
+ : public LLInstanceTracker<DeclareTimer>
+ {
+ public:
+ DeclareTimer(const std::string& name, bool open);
+ DeclareTimer(const std::string& name);
+
+ static void updateCachedPointers();
+
+ // convertable to NamedTimer::FrameState for convenient usage of LLFastTimer(declared_timer)
+ operator NamedTimer::FrameState&() { return *mFrameState; }
+ private:
+ NamedTimer& mTimer;
+ NamedTimer::FrameState* mFrameState;
+ };
+
+
+public:
+ static LLMutex* sLogLock;
+ static std::queue<LLSD> sLogQueue;
+ static BOOL sLog;
+ static BOOL sMetricLog;
+
+ typedef std::vector<NamedTimer::FrameState> info_list_t;
+ static info_list_t& getFrameStateList();
+
+ enum RootTimerMarker { ROOT };
+ LLFastTimer(RootTimerMarker);
+
+ LLFastTimer(NamedTimer::FrameState& timer)
+ : mFrameState(&timer)
+ {
+#if FAST_TIMER_ON
+ NamedTimer::FrameState* frame_state = &timer;
+ U32 cur_time = get_cpu_clock_count_32();
+ mStartSelfTime = cur_time;
+ mStartTotalTime = cur_time;
+
+ frame_state->mActiveCount++;
+ frame_state->mCalls++;
+ // keep current parent as long as it is active when we are
+ frame_state->mMoveUpTree |= (frame_state->mParent->mActiveCount == 0);
+
+ mLastTimer = sCurTimer;
+ sCurTimer = this;
+#endif
+ }
+
+ ~LLFastTimer()
+ {
+#if FAST_TIMER_ON
+ NamedTimer::FrameState* frame_state = mFrameState;
+ U32 cur_time = get_cpu_clock_count_32();
+ frame_state->mSelfTimeCounter += cur_time - mStartSelfTime;
+
+ frame_state->mActiveCount--;
+ LLFastTimer* last_timer = mLastTimer;
+ sCurTimer = last_timer;
+
+ // store last caller to bootstrap tree creation
+ frame_state->mLastCaller = last_timer->mFrameState;
+
+ // we are only tracking self time, so subtract our total time delta from parents
+ U32 total_time = cur_time - mStartTotalTime;
+ last_timer->mStartSelfTime += total_time;
+#endif
+ }
+
+
+ // call this once a frame to reset timers
+ static void nextFrame();
+
+ // dumps current cumulative frame stats to log
+ // call nextFrame() to reset timers
+ static void dumpCurTimes();
+
+ // call this to reset timer hierarchy, averages, etc.
+ static void reset();
+
+ static U64 countsPerSecond();
+ static S32 getLastFrameIndex() { return sLastFrameIndex; }
+ static S32 getCurFrameIndex() { return sCurFrameIndex; }
+
+ static void writeLog(std::ostream& os);
+ static const NamedTimer* getTimerByName(const std::string& name);
+
+public:
+ static bool sPauseHistory;
+ static bool sResetHistory;
+
+private:
+ typedef std::vector<LLFastTimer*> timer_stack_t;
+ static LLFastTimer* sCurTimer;
+ static S32 sCurFrameIndex;
+ static S32 sLastFrameIndex;
+ static U64 sLastFrameTime;
+ static info_list_t* sTimerInfos;
+
+ U32 mStartSelfTime; // start time + time of all child timers
+ U32 mStartTotalTime; // start time + time of all child timers
+ NamedTimer::FrameState* mFrameState;
+ LLFastTimer* mLastTimer;
+};
+
+#endif // LL_LLFASTTIMER_H
diff --git a/indra/llcommon/llfile.h b/indra/llcommon/llfile.h
index c6092f7b9c..fea5d3ed2b 100644
--- a/indra/llcommon/llfile.h
+++ b/indra/llcommon/llfile.h
@@ -70,7 +70,7 @@ typedef struct stat llstat;
#include "llstring.h" // safe char* -> std::string conversion
-class LLFile
+class LL_COMMON_API LLFile
{
public:
// All these functions take UTF8 path/filenames.
@@ -95,7 +95,7 @@ public:
#if USE_LLFILESTREAMS
-class llifstream : public std::basic_istream < char , std::char_traits < char > >
+class LL_COMMON_API llifstream : public std::basic_istream < char , std::char_traits < char > >
{
// input stream associated with a C stream
public:
@@ -136,7 +136,7 @@ private:
};
-class llofstream : public std::basic_ostream< char , std::char_traits < char > >
+class LL_COMMON_API llofstream : public std::basic_ostream< char , std::char_traits < char > >
{
public:
typedef std::basic_ostream< char , std::char_traits < char > > _Myt;
@@ -185,7 +185,7 @@ private:
//#define llifstream std::ifstream
//#define llofstream std::ofstream
-class llifstream : public std::ifstream
+class LL_COMMON_API llifstream : public std::ifstream
{
public:
llifstream() : std::ifstream()
@@ -203,7 +203,7 @@ public:
};
-class llofstream : public std::ofstream
+class LL_COMMON_API llofstream : public std::ofstream
{
public:
llofstream() : std::ofstream()
@@ -231,7 +231,7 @@ public:
* and should only be used for config files and the like -- not in a
* loop.
*/
-std::streamsize llifstream_size(llifstream& fstr);
-std::streamsize llofstream_size(llofstream& fstr);
+std::streamsize LL_COMMON_API llifstream_size(llifstream& fstr);
+std::streamsize LL_COMMON_API llofstream_size(llofstream& fstr);
#endif // not LL_LLFILE_H
diff --git a/indra/llcommon/llfindlocale.h b/indra/llcommon/llfindlocale.h
index f17c7740f3..b812a065db 100644
--- a/indra/llcommon/llfindlocale.h
+++ b/indra/llcommon/llfindlocale.h
@@ -59,8 +59,8 @@ typedef enum {
/* This allocates/fills in a FL_Locale structure with pointers to
strings (which should be treated as static), or NULL for inappropriate /
undetected fields. */
-FL_Success FL_FindLocale(FL_Locale **locale, FL_Domain domain);
+LL_COMMON_API FL_Success FL_FindLocale(FL_Locale **locale, FL_Domain domain);
/* This should be used to free the struct written by FL_FindLocale */
-void FL_FreeLocale(FL_Locale **locale);
+LL_COMMON_API void FL_FreeLocale(FL_Locale **locale);
#endif /*__findlocale_h_*/
diff --git a/indra/llcommon/llfixedbuffer.h b/indra/llcommon/llfixedbuffer.h
index 01b46d327a..17fdef27d7 100644
--- a/indra/llcommon/llfixedbuffer.h
+++ b/indra/llcommon/llfixedbuffer.h
@@ -41,7 +41,7 @@
#include "llerrorcontrol.h"
// fixed buffer implementation
-class LLFixedBuffer : public LLLineBuffer
+class LL_COMMON_API LLFixedBuffer : public LLLineBuffer
{
public:
LLFixedBuffer(const U32 max_lines = 20);
diff --git a/indra/llcommon/llformat.h b/indra/llcommon/llformat.h
index 44c62d9710..dc64edb26d 100644
--- a/indra/llcommon/llformat.h
+++ b/indra/llcommon/llformat.h
@@ -40,6 +40,6 @@
// *NOTE: buffer limited to 1024, (but vsnprintf prevents overrun)
// should perhaps be replaced with boost::format.
-std::string llformat(const char *fmt, ...);
+std::string LL_COMMON_API llformat(const char *fmt, ...);
#endif // LL_LLFORMAT_H
diff --git a/indra/llcommon/llframetimer.h b/indra/llcommon/llframetimer.h
index 8f51272af2..be2d9b0703 100644
--- a/indra/llcommon/llframetimer.h
+++ b/indra/llcommon/llframetimer.h
@@ -43,7 +43,7 @@
#include "lltimer.h"
#include "timing.h"
-class LLFrameTimer
+class LL_COMMON_API LLFrameTimer
{
public:
LLFrameTimer() : mStartTime( sFrameTime ), mExpiry(0), mStarted(TRUE) {}
diff --git a/indra/llcommon/llheartbeat.h b/indra/llcommon/llheartbeat.h
index fecb5b1e54..6f7026970f 100644
--- a/indra/llcommon/llheartbeat.h
+++ b/indra/llcommon/llheartbeat.h
@@ -40,7 +40,7 @@
// Note: Win32 does not support the heartbeat/smackdown system;
// heartbeat-delivery turns into a no-op there.
-class LLHeartbeat
+class LL_COMMON_API LLHeartbeat
{
public:
// secs_between_heartbeat: after a heartbeat is successfully delivered,
diff --git a/indra/llcommon/llkeythrottle.h b/indra/llcommon/llkeythrottle.h
index 873f50a65e..7544ab1d11 100644
--- a/indra/llcommon/llkeythrottle.h
+++ b/indra/llcommon/llkeythrottle.h
@@ -118,6 +118,63 @@ 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)
{
diff --git a/indra/llcommon/llliveappconfig.h b/indra/llcommon/llliveappconfig.h
index a6ece6e8b3..73b3a23352 100644
--- a/indra/llcommon/llliveappconfig.h
+++ b/indra/llcommon/llliveappconfig.h
@@ -45,7 +45,7 @@
* loop. The traditional name for it is live_config. Be sure to call
* <code>live_config.checkAndReload()</code> periodically.
*/
-class LLLiveAppConfig : public LLLiveFile
+class LL_COMMON_API LLLiveAppConfig : public LLLiveFile
{
public:
diff --git a/indra/llcommon/lllivefile.h b/indra/llcommon/lllivefile.h
index 89b5d95e44..2453d7a125 100644
--- a/indra/llcommon/lllivefile.h
+++ b/indra/llcommon/lllivefile.h
@@ -36,7 +36,7 @@
extern const F32 DEFAULT_CONFIG_FILE_REFRESH;
-class LLLiveFile
+class LL_COMMON_API LLLiveFile
{
public:
LLLiveFile(const std::string& filename, const F32 refresh_period = 5.f);
diff --git a/indra/llcommon/lllog.h b/indra/llcommon/lllog.h
index 7ac6c8aa42..4b6777bb9c 100644
--- a/indra/llcommon/lllog.h
+++ b/indra/llcommon/lllog.h
@@ -41,7 +41,7 @@ class LLLogImpl;
class LLApp;
class LLSD;
-class LLLog
+class LL_COMMON_API LLLog
{
public:
LLLog(LLApp* app);
diff --git a/indra/llcommon/llmd5.h b/indra/llcommon/llmd5.h
index d8bca03e4e..df9d7324ab 100644
--- a/indra/llcommon/llmd5.h
+++ b/indra/llcommon/llmd5.h
@@ -80,7 +80,7 @@ const int MD5RAW_BYTES = 16;
const int MD5HEX_STR_SIZE = 33; // char hex[MD5HEX_STR_SIZE]; with null
const int MD5HEX_STR_BYTES = 32; // message system fixed size
-class LLMD5 {
+class LL_COMMON_API LLMD5 {
// first, some types:
typedef unsigned int uint4; // assumes integer is 4 words long
typedef unsigned short int uint2; // assumes short integer is 2 words long
diff --git a/indra/llcommon/llmemory.h b/indra/llcommon/llmemory.h
index f41da37ba6..09f19532b7 100644
--- a/indra/llcommon/llmemory.h
+++ b/indra/llcommon/llmemory.h
@@ -1,65 +1,65 @@
-/**
- * @file llmemory.h
- * @brief Memory allocation/deallocation header-stuff goes here.
- *
- * $LicenseInfo:firstyear=2002&license=viewergpl$
- *
- * Copyright (c) 2002-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-#ifndef LLMEMORY_H
-#define LLMEMORY_H
-
-
-
-extern S32 gTotalDAlloc;
-extern S32 gTotalDAUse;
-extern S32 gDACount;
-
-extern void* ll_allocate (size_t size);
-extern void ll_release (void *p);
-
-class LLMemory
-{
-public:
- static void initClass();
- static void cleanupClass();
- static void freeReserve();
- // Return the resident set size of the current process, in bytes.
- // Return value is zero if not known.
- static U64 getCurrentRSS();
-private:
- static char* reserveMem;
-};
-
-// LLRefCount moved to llrefcount.h
-
-// LLPointer moved to llpointer.h
-
-// LLSafeHandle moved to llsafehandle.h
-
-// LLSingleton moved to llsingleton.h
-
-#endif
+/**
+ * @file llmemory.h
+ * @brief Memory allocation/deallocation header-stuff goes here.
+ *
+ * $LicenseInfo:firstyear=2002&license=viewergpl$
+ *
+ * Copyright (c) 2002-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+#ifndef LLMEMORY_H
+#define LLMEMORY_H
+
+
+
+extern S32 gTotalDAlloc;
+extern S32 gTotalDAUse;
+extern S32 gDACount;
+
+extern void* ll_allocate (size_t size);
+extern void ll_release (void *p);
+
+class LL_COMMON_API LLMemory
+{
+public:
+ static void initClass();
+ static void cleanupClass();
+ static void freeReserve();
+ // Return the resident set size of the current process, in bytes.
+ // Return value is zero if not known.
+ static U64 getCurrentRSS();
+private:
+ static char* reserveMem;
+};
+
+// LLRefCount moved to llrefcount.h
+
+// LLPointer moved to llpointer.h
+
+// LLSafeHandle moved to llsafehandle.h
+
+// LLSingleton moved to llsingleton.h
+
+#endif
diff --git a/indra/llcommon/llmemorystream.h b/indra/llcommon/llmemorystream.h
index f3486324c5..fa0f5d22f2 100644
--- a/indra/llcommon/llmemorystream.h
+++ b/indra/llcommon/llmemorystream.h
@@ -52,7 +52,7 @@
* be careful to always pass in a valid memory location that exists
* for at least as long as this streambuf.
*/
-class LLMemoryStreamBuf : public std::streambuf
+class LL_COMMON_API LLMemoryStreamBuf : public std::streambuf
{
public:
LLMemoryStreamBuf(const U8* start, S32 length);
@@ -74,7 +74,7 @@ protected:
* be careful to always pass in a valid memory location that exists
* for at least as long as this streambuf.
*/
-class LLMemoryStream : public std::istream
+class LL_COMMON_API LLMemoryStream : public std::istream
{
public:
LLMemoryStream(const U8* start, S32 length);
diff --git a/indra/llcommon/llmemtype.h b/indra/llcommon/llmemtype.h
index 12310fcdb4..5952a3a7c5 100644
--- a/indra/llcommon/llmemtype.h
+++ b/indra/llcommon/llmemtype.h
@@ -1,248 +1,248 @@
-/**
- * @file llmemtype.h
- * @brief Runtime memory usage debugging utilities.
- *
- * $LicenseInfo:firstyear=2005&license=viewergpl$
- *
- * Copyright (c) 2005-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_MEMTYPE_H
-#define LL_MEMTYPE_H
-
-//----------------------------------------------------------------------------
-//----------------------------------------------------------------------------
-
-//----------------------------------------------------------------------------
-
-#include "linden_common.h"
-//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-// WARNING: Never commit with MEM_TRACK_MEM == 1
-//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-#define MEM_TRACK_MEM (0 && LL_WINDOWS)
-
-#include <vector>
-
-#define MEM_TYPE_NEW(T)
-
-class LLMemType
-{
-public:
-
- // class we'll initialize all instances of as
- // static members of MemType. Then use
- // to construct any new mem type.
- class DeclareMemType
- {
- public:
- DeclareMemType(char const * st);
- ~DeclareMemType();
-
- S32 mID;
- char const * mName;
-
- // array so we can map an index ID to Name
- static std::vector<char const *> mNameList;
- };
-
- LLMemType(DeclareMemType& dt);
- ~LLMemType();
-
- static char const * getNameFromID(S32 id);
-
- static DeclareMemType MTYPE_INIT;
- static DeclareMemType MTYPE_STARTUP;
- static DeclareMemType MTYPE_MAIN;
- static DeclareMemType MTYPE_FRAME;
-
- static DeclareMemType MTYPE_GATHER_INPUT;
- static DeclareMemType MTYPE_JOY_KEY;
-
- static DeclareMemType MTYPE_IDLE;
- static DeclareMemType MTYPE_IDLE_PUMP;
- static DeclareMemType MTYPE_IDLE_NETWORK;
- static DeclareMemType MTYPE_IDLE_UPDATE_REGIONS;
- static DeclareMemType MTYPE_IDLE_UPDATE_VIEWER_REGION;
- static DeclareMemType MTYPE_IDLE_UPDATE_SURFACE;
- static DeclareMemType MTYPE_IDLE_UPDATE_PARCEL_OVERLAY;
- static DeclareMemType MTYPE_IDLE_AUDIO;
-
- static DeclareMemType MTYPE_CACHE_PROCESS_PENDING;
- static DeclareMemType MTYPE_CACHE_PROCESS_PENDING_ASKS;
- static DeclareMemType MTYPE_CACHE_PROCESS_PENDING_REPLIES;
-
- static DeclareMemType MTYPE_MESSAGE_CHECK_ALL;
- static DeclareMemType MTYPE_MESSAGE_PROCESS_ACKS;
-
- static DeclareMemType MTYPE_RENDER;
- static DeclareMemType MTYPE_SLEEP;
-
- static DeclareMemType MTYPE_NETWORK;
- static DeclareMemType MTYPE_PHYSICS;
- static DeclareMemType MTYPE_INTERESTLIST;
-
- static DeclareMemType MTYPE_IMAGEBASE;
- static DeclareMemType MTYPE_IMAGERAW;
- static DeclareMemType MTYPE_IMAGEFORMATTED;
-
- static DeclareMemType MTYPE_APPFMTIMAGE;
- static DeclareMemType MTYPE_APPRAWIMAGE;
- static DeclareMemType MTYPE_APPAUXRAWIMAGE;
-
- static DeclareMemType MTYPE_DRAWABLE;
-
- static DeclareMemType MTYPE_OBJECT;
- static DeclareMemType MTYPE_OBJECT_PROCESS_UPDATE;
- static DeclareMemType MTYPE_OBJECT_PROCESS_UPDATE_CORE;
-
- static DeclareMemType MTYPE_DISPLAY;
- static DeclareMemType MTYPE_DISPLAY_UPDATE;
- static DeclareMemType MTYPE_DISPLAY_UPDATE_CAMERA;
- static DeclareMemType MTYPE_DISPLAY_UPDATE_GEOM;
- static DeclareMemType MTYPE_DISPLAY_SWAP;
- static DeclareMemType MTYPE_DISPLAY_UPDATE_HUD;
- static DeclareMemType MTYPE_DISPLAY_GEN_REFLECTION;
- static DeclareMemType MTYPE_DISPLAY_IMAGE_UPDATE;
- static DeclareMemType MTYPE_DISPLAY_STATE_SORT;
- static DeclareMemType MTYPE_DISPLAY_SKY;
- static DeclareMemType MTYPE_DISPLAY_RENDER_GEOM;
- static DeclareMemType MTYPE_DISPLAY_RENDER_FLUSH;
- static DeclareMemType MTYPE_DISPLAY_RENDER_UI;
- static DeclareMemType MTYPE_DISPLAY_RENDER_ATTACHMENTS;
-
- static DeclareMemType MTYPE_VERTEX_DATA;
- static DeclareMemType MTYPE_VERTEX_CONSTRUCTOR;
- static DeclareMemType MTYPE_VERTEX_DESTRUCTOR;
- static DeclareMemType MTYPE_VERTEX_CREATE_VERTICES;
- static DeclareMemType MTYPE_VERTEX_CREATE_INDICES;
- static DeclareMemType MTYPE_VERTEX_DESTROY_BUFFER;
- static DeclareMemType MTYPE_VERTEX_DESTROY_INDICES;
- static DeclareMemType MTYPE_VERTEX_UPDATE_VERTS;
- static DeclareMemType MTYPE_VERTEX_UPDATE_INDICES;
- static DeclareMemType MTYPE_VERTEX_ALLOCATE_BUFFER;
- static DeclareMemType MTYPE_VERTEX_RESIZE_BUFFER;
- static DeclareMemType MTYPE_VERTEX_MAP_BUFFER;
- static DeclareMemType MTYPE_VERTEX_MAP_BUFFER_VERTICES;
- static DeclareMemType MTYPE_VERTEX_MAP_BUFFER_INDICES;
- static DeclareMemType MTYPE_VERTEX_UNMAP_BUFFER;
- static DeclareMemType MTYPE_VERTEX_SET_STRIDE;
- static DeclareMemType MTYPE_VERTEX_SET_BUFFER;
- static DeclareMemType MTYPE_VERTEX_SETUP_VERTEX_BUFFER;
- static DeclareMemType MTYPE_VERTEX_CLEANUP_CLASS;
-
- static DeclareMemType MTYPE_SPACE_PARTITION;
-
- static DeclareMemType MTYPE_PIPELINE;
- static DeclareMemType MTYPE_PIPELINE_INIT;
- static DeclareMemType MTYPE_PIPELINE_CREATE_BUFFERS;
- static DeclareMemType MTYPE_PIPELINE_RESTORE_GL;
- static DeclareMemType MTYPE_PIPELINE_UNLOAD_SHADERS;
- static DeclareMemType MTYPE_PIPELINE_LIGHTING_DETAIL;
- static DeclareMemType MTYPE_PIPELINE_GET_POOL_TYPE;
- static DeclareMemType MTYPE_PIPELINE_ADD_POOL;
- static DeclareMemType MTYPE_PIPELINE_ALLOCATE_DRAWABLE;
- static DeclareMemType MTYPE_PIPELINE_ADD_OBJECT;
- static DeclareMemType MTYPE_PIPELINE_CREATE_OBJECTS;
- static DeclareMemType MTYPE_PIPELINE_UPDATE_MOVE;
- static DeclareMemType MTYPE_PIPELINE_UPDATE_GEOM;
- static DeclareMemType MTYPE_PIPELINE_MARK_VISIBLE;
- static DeclareMemType MTYPE_PIPELINE_MARK_MOVED;
- static DeclareMemType MTYPE_PIPELINE_MARK_SHIFT;
- static DeclareMemType MTYPE_PIPELINE_SHIFT_OBJECTS;
- static DeclareMemType MTYPE_PIPELINE_MARK_TEXTURED;
- static DeclareMemType MTYPE_PIPELINE_MARK_REBUILD;
- static DeclareMemType MTYPE_PIPELINE_UPDATE_CULL;
- static DeclareMemType MTYPE_PIPELINE_STATE_SORT;
- static DeclareMemType MTYPE_PIPELINE_POST_SORT;
-
- static DeclareMemType MTYPE_PIPELINE_RENDER_HUD_ELS;
- static DeclareMemType MTYPE_PIPELINE_RENDER_HL;
- static DeclareMemType MTYPE_PIPELINE_RENDER_GEOM;
- static DeclareMemType MTYPE_PIPELINE_RENDER_GEOM_DEFFERRED;
- static DeclareMemType MTYPE_PIPELINE_RENDER_GEOM_POST_DEF;
- static DeclareMemType MTYPE_PIPELINE_RENDER_GEOM_SHADOW;
- static DeclareMemType MTYPE_PIPELINE_RENDER_SELECT;
- static DeclareMemType MTYPE_PIPELINE_REBUILD_POOLS;
- static DeclareMemType MTYPE_PIPELINE_QUICK_LOOKUP;
- static DeclareMemType MTYPE_PIPELINE_RENDER_OBJECTS;
- static DeclareMemType MTYPE_PIPELINE_GENERATE_IMPOSTOR;
- static DeclareMemType MTYPE_PIPELINE_RENDER_BLOOM;
-
- static DeclareMemType MTYPE_UPKEEP_POOLS;
-
- static DeclareMemType MTYPE_AVATAR;
- static DeclareMemType MTYPE_AVATAR_MESH;
- static DeclareMemType MTYPE_PARTICLES;
- static DeclareMemType MTYPE_REGIONS;
-
- static DeclareMemType MTYPE_INVENTORY;
- static DeclareMemType MTYPE_INVENTORY_DRAW;
- static DeclareMemType MTYPE_INVENTORY_BUILD_NEW_VIEWS;
- static DeclareMemType MTYPE_INVENTORY_DO_FOLDER;
- static DeclareMemType MTYPE_INVENTORY_POST_BUILD;
- static DeclareMemType MTYPE_INVENTORY_FROM_XML;
- static DeclareMemType MTYPE_INVENTORY_CREATE_NEW_ITEM;
- static DeclareMemType MTYPE_INVENTORY_VIEW_INIT;
- static DeclareMemType MTYPE_INVENTORY_VIEW_SHOW;
- static DeclareMemType MTYPE_INVENTORY_VIEW_TOGGLE;
-
- static DeclareMemType MTYPE_ANIMATION;
- static DeclareMemType MTYPE_VOLUME;
- static DeclareMemType MTYPE_PRIMITIVE;
-
- static DeclareMemType MTYPE_SCRIPT;
- static DeclareMemType MTYPE_SCRIPT_RUN;
- static DeclareMemType MTYPE_SCRIPT_BYTECODE;
-
- static DeclareMemType MTYPE_IO_PUMP;
- static DeclareMemType MTYPE_IO_TCP;
- static DeclareMemType MTYPE_IO_BUFFER;
- static DeclareMemType MTYPE_IO_HTTP_SERVER;
- static DeclareMemType MTYPE_IO_SD_SERVER;
- static DeclareMemType MTYPE_IO_SD_CLIENT;
- static DeclareMemType MTYPE_IO_URL_REQUEST;
-
- static DeclareMemType MTYPE_DIRECTX_INIT;
-
- static DeclareMemType MTYPE_TEMP1;
- static DeclareMemType MTYPE_TEMP2;
- static DeclareMemType MTYPE_TEMP3;
- static DeclareMemType MTYPE_TEMP4;
- static DeclareMemType MTYPE_TEMP5;
- static DeclareMemType MTYPE_TEMP6;
- static DeclareMemType MTYPE_TEMP7;
- static DeclareMemType MTYPE_TEMP8;
- static DeclareMemType MTYPE_TEMP9;
-
- static DeclareMemType MTYPE_OTHER; // Special; used by display code
-
- S32 mTypeIndex;
-};
-
-//----------------------------------------------------------------------------
-
-#endif
-
+/**
+ * @file llmemtype.h
+ * @brief Runtime memory usage debugging utilities.
+ *
+ * $LicenseInfo:firstyear=2005&license=viewergpl$
+ *
+ * Copyright (c) 2005-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_MEMTYPE_H
+#define LL_MEMTYPE_H
+
+//----------------------------------------------------------------------------
+//----------------------------------------------------------------------------
+
+//----------------------------------------------------------------------------
+
+#include "linden_common.h"
+//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+// WARNING: Never commit with MEM_TRACK_MEM == 1
+//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+#define MEM_TRACK_MEM (0 && LL_WINDOWS)
+
+#include <vector>
+
+#define MEM_TYPE_NEW(T)
+
+class LL_COMMON_API LLMemType
+{
+public:
+
+ // class we'll initialize all instances of as
+ // static members of MemType. Then use
+ // to construct any new mem type.
+ class LL_COMMON_API DeclareMemType
+ {
+ public:
+ DeclareMemType(char const * st);
+ ~DeclareMemType();
+
+ S32 mID;
+ char const * mName;
+
+ // array so we can map an index ID to Name
+ static std::vector<char const *> mNameList;
+ };
+
+ LLMemType(DeclareMemType& dt);
+ ~LLMemType();
+
+ static char const * getNameFromID(S32 id);
+
+ static DeclareMemType MTYPE_INIT;
+ static DeclareMemType MTYPE_STARTUP;
+ static DeclareMemType MTYPE_MAIN;
+ static DeclareMemType MTYPE_FRAME;
+
+ static DeclareMemType MTYPE_GATHER_INPUT;
+ static DeclareMemType MTYPE_JOY_KEY;
+
+ static DeclareMemType MTYPE_IDLE;
+ static DeclareMemType MTYPE_IDLE_PUMP;
+ static DeclareMemType MTYPE_IDLE_NETWORK;
+ static DeclareMemType MTYPE_IDLE_UPDATE_REGIONS;
+ static DeclareMemType MTYPE_IDLE_UPDATE_VIEWER_REGION;
+ static DeclareMemType MTYPE_IDLE_UPDATE_SURFACE;
+ static DeclareMemType MTYPE_IDLE_UPDATE_PARCEL_OVERLAY;
+ static DeclareMemType MTYPE_IDLE_AUDIO;
+
+ static DeclareMemType MTYPE_CACHE_PROCESS_PENDING;
+ static DeclareMemType MTYPE_CACHE_PROCESS_PENDING_ASKS;
+ static DeclareMemType MTYPE_CACHE_PROCESS_PENDING_REPLIES;
+
+ static DeclareMemType MTYPE_MESSAGE_CHECK_ALL;
+ static DeclareMemType MTYPE_MESSAGE_PROCESS_ACKS;
+
+ static DeclareMemType MTYPE_RENDER;
+ static DeclareMemType MTYPE_SLEEP;
+
+ static DeclareMemType MTYPE_NETWORK;
+ static DeclareMemType MTYPE_PHYSICS;
+ static DeclareMemType MTYPE_INTERESTLIST;
+
+ static DeclareMemType MTYPE_IMAGEBASE;
+ static DeclareMemType MTYPE_IMAGERAW;
+ static DeclareMemType MTYPE_IMAGEFORMATTED;
+
+ static DeclareMemType MTYPE_APPFMTIMAGE;
+ static DeclareMemType MTYPE_APPRAWIMAGE;
+ static DeclareMemType MTYPE_APPAUXRAWIMAGE;
+
+ static DeclareMemType MTYPE_DRAWABLE;
+
+ static DeclareMemType MTYPE_OBJECT;
+ static DeclareMemType MTYPE_OBJECT_PROCESS_UPDATE;
+ static DeclareMemType MTYPE_OBJECT_PROCESS_UPDATE_CORE;
+
+ static DeclareMemType MTYPE_DISPLAY;
+ static DeclareMemType MTYPE_DISPLAY_UPDATE;
+ static DeclareMemType MTYPE_DISPLAY_UPDATE_CAMERA;
+ static DeclareMemType MTYPE_DISPLAY_UPDATE_GEOM;
+ static DeclareMemType MTYPE_DISPLAY_SWAP;
+ static DeclareMemType MTYPE_DISPLAY_UPDATE_HUD;
+ static DeclareMemType MTYPE_DISPLAY_GEN_REFLECTION;
+ static DeclareMemType MTYPE_DISPLAY_IMAGE_UPDATE;
+ static DeclareMemType MTYPE_DISPLAY_STATE_SORT;
+ static DeclareMemType MTYPE_DISPLAY_SKY;
+ static DeclareMemType MTYPE_DISPLAY_RENDER_GEOM;
+ static DeclareMemType MTYPE_DISPLAY_RENDER_FLUSH;
+ static DeclareMemType MTYPE_DISPLAY_RENDER_UI;
+ static DeclareMemType MTYPE_DISPLAY_RENDER_ATTACHMENTS;
+
+ static DeclareMemType MTYPE_VERTEX_DATA;
+ static DeclareMemType MTYPE_VERTEX_CONSTRUCTOR;
+ static DeclareMemType MTYPE_VERTEX_DESTRUCTOR;
+ static DeclareMemType MTYPE_VERTEX_CREATE_VERTICES;
+ static DeclareMemType MTYPE_VERTEX_CREATE_INDICES;
+ static DeclareMemType MTYPE_VERTEX_DESTROY_BUFFER;
+ static DeclareMemType MTYPE_VERTEX_DESTROY_INDICES;
+ static DeclareMemType MTYPE_VERTEX_UPDATE_VERTS;
+ static DeclareMemType MTYPE_VERTEX_UPDATE_INDICES;
+ static DeclareMemType MTYPE_VERTEX_ALLOCATE_BUFFER;
+ static DeclareMemType MTYPE_VERTEX_RESIZE_BUFFER;
+ static DeclareMemType MTYPE_VERTEX_MAP_BUFFER;
+ static DeclareMemType MTYPE_VERTEX_MAP_BUFFER_VERTICES;
+ static DeclareMemType MTYPE_VERTEX_MAP_BUFFER_INDICES;
+ static DeclareMemType MTYPE_VERTEX_UNMAP_BUFFER;
+ static DeclareMemType MTYPE_VERTEX_SET_STRIDE;
+ static DeclareMemType MTYPE_VERTEX_SET_BUFFER;
+ static DeclareMemType MTYPE_VERTEX_SETUP_VERTEX_BUFFER;
+ static DeclareMemType MTYPE_VERTEX_CLEANUP_CLASS;
+
+ static DeclareMemType MTYPE_SPACE_PARTITION;
+
+ static DeclareMemType MTYPE_PIPELINE;
+ static DeclareMemType MTYPE_PIPELINE_INIT;
+ static DeclareMemType MTYPE_PIPELINE_CREATE_BUFFERS;
+ static DeclareMemType MTYPE_PIPELINE_RESTORE_GL;
+ static DeclareMemType MTYPE_PIPELINE_UNLOAD_SHADERS;
+ static DeclareMemType MTYPE_PIPELINE_LIGHTING_DETAIL;
+ static DeclareMemType MTYPE_PIPELINE_GET_POOL_TYPE;
+ static DeclareMemType MTYPE_PIPELINE_ADD_POOL;
+ static DeclareMemType MTYPE_PIPELINE_ALLOCATE_DRAWABLE;
+ static DeclareMemType MTYPE_PIPELINE_ADD_OBJECT;
+ static DeclareMemType MTYPE_PIPELINE_CREATE_OBJECTS;
+ static DeclareMemType MTYPE_PIPELINE_UPDATE_MOVE;
+ static DeclareMemType MTYPE_PIPELINE_UPDATE_GEOM;
+ static DeclareMemType MTYPE_PIPELINE_MARK_VISIBLE;
+ static DeclareMemType MTYPE_PIPELINE_MARK_MOVED;
+ static DeclareMemType MTYPE_PIPELINE_MARK_SHIFT;
+ static DeclareMemType MTYPE_PIPELINE_SHIFT_OBJECTS;
+ static DeclareMemType MTYPE_PIPELINE_MARK_TEXTURED;
+ static DeclareMemType MTYPE_PIPELINE_MARK_REBUILD;
+ static DeclareMemType MTYPE_PIPELINE_UPDATE_CULL;
+ static DeclareMemType MTYPE_PIPELINE_STATE_SORT;
+ static DeclareMemType MTYPE_PIPELINE_POST_SORT;
+
+ static DeclareMemType MTYPE_PIPELINE_RENDER_HUD_ELS;
+ static DeclareMemType MTYPE_PIPELINE_RENDER_HL;
+ static DeclareMemType MTYPE_PIPELINE_RENDER_GEOM;
+ static DeclareMemType MTYPE_PIPELINE_RENDER_GEOM_DEFFERRED;
+ static DeclareMemType MTYPE_PIPELINE_RENDER_GEOM_POST_DEF;
+ static DeclareMemType MTYPE_PIPELINE_RENDER_GEOM_SHADOW;
+ static DeclareMemType MTYPE_PIPELINE_RENDER_SELECT;
+ static DeclareMemType MTYPE_PIPELINE_REBUILD_POOLS;
+ static DeclareMemType MTYPE_PIPELINE_QUICK_LOOKUP;
+ static DeclareMemType MTYPE_PIPELINE_RENDER_OBJECTS;
+ static DeclareMemType MTYPE_PIPELINE_GENERATE_IMPOSTOR;
+ static DeclareMemType MTYPE_PIPELINE_RENDER_BLOOM;
+
+ static DeclareMemType MTYPE_UPKEEP_POOLS;
+
+ static DeclareMemType MTYPE_AVATAR;
+ static DeclareMemType MTYPE_AVATAR_MESH;
+ static DeclareMemType MTYPE_PARTICLES;
+ static DeclareMemType MTYPE_REGIONS;
+
+ static DeclareMemType MTYPE_INVENTORY;
+ static DeclareMemType MTYPE_INVENTORY_DRAW;
+ static DeclareMemType MTYPE_INVENTORY_BUILD_NEW_VIEWS;
+ static DeclareMemType MTYPE_INVENTORY_DO_FOLDER;
+ static DeclareMemType MTYPE_INVENTORY_POST_BUILD;
+ static DeclareMemType MTYPE_INVENTORY_FROM_XML;
+ static DeclareMemType MTYPE_INVENTORY_CREATE_NEW_ITEM;
+ static DeclareMemType MTYPE_INVENTORY_VIEW_INIT;
+ static DeclareMemType MTYPE_INVENTORY_VIEW_SHOW;
+ static DeclareMemType MTYPE_INVENTORY_VIEW_TOGGLE;
+
+ static DeclareMemType MTYPE_ANIMATION;
+ static DeclareMemType MTYPE_VOLUME;
+ static DeclareMemType MTYPE_PRIMITIVE;
+
+ static DeclareMemType MTYPE_SCRIPT;
+ static DeclareMemType MTYPE_SCRIPT_RUN;
+ static DeclareMemType MTYPE_SCRIPT_BYTECODE;
+
+ static DeclareMemType MTYPE_IO_PUMP;
+ static DeclareMemType MTYPE_IO_TCP;
+ static DeclareMemType MTYPE_IO_BUFFER;
+ static DeclareMemType MTYPE_IO_HTTP_SERVER;
+ static DeclareMemType MTYPE_IO_SD_SERVER;
+ static DeclareMemType MTYPE_IO_SD_CLIENT;
+ static DeclareMemType MTYPE_IO_URL_REQUEST;
+
+ static DeclareMemType MTYPE_DIRECTX_INIT;
+
+ static DeclareMemType MTYPE_TEMP1;
+ static DeclareMemType MTYPE_TEMP2;
+ static DeclareMemType MTYPE_TEMP3;
+ static DeclareMemType MTYPE_TEMP4;
+ static DeclareMemType MTYPE_TEMP5;
+ static DeclareMemType MTYPE_TEMP6;
+ static DeclareMemType MTYPE_TEMP7;
+ static DeclareMemType MTYPE_TEMP8;
+ static DeclareMemType MTYPE_TEMP9;
+
+ static DeclareMemType MTYPE_OTHER; // Special; used by display code
+
+ S32 mTypeIndex;
+};
+
+//----------------------------------------------------------------------------
+
+#endif
+
diff --git a/indra/llcommon/llmetrics.h b/indra/llcommon/llmetrics.h
index 1d91e8c8a2..f6f49eb456 100644
--- a/indra/llcommon/llmetrics.h
+++ b/indra/llcommon/llmetrics.h
@@ -38,7 +38,7 @@
class LLMetricsImpl;
class LLSD;
-class LLMetrics
+class LL_COMMON_API LLMetrics
{
public:
LLMetrics();
diff --git a/indra/llcommon/llmortician.h b/indra/llcommon/llmortician.h
index fcda3df58e..27bd8cd9b5 100644
--- a/indra/llcommon/llmortician.h
+++ b/indra/llcommon/llmortician.h
@@ -35,7 +35,7 @@
#include "stdtypes.h"
-class LLMortician
+class LL_COMMON_API LLMortician
{
public:
LLMortician() { mIsDead = FALSE; }
diff --git a/indra/llcommon/llpreprocessor.h b/indra/llcommon/llpreprocessor.h
index bb598a2be1..48baa50edb 100644
--- a/indra/llcommon/llpreprocessor.h
+++ b/indra/llcommon/llpreprocessor.h
@@ -1,142 +1,168 @@
-/**
- * @file llpreprocessor.h
- * @brief This file should be included in all Linden Lab files and
- * should only contain special preprocessor directives
- *
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LLPREPROCESSOR_H
-#define LLPREPROCESSOR_H
-
-// Figure out endianness of platform
-#ifdef LL_LINUX
-#define __ENABLE_WSTRING
-#include <endian.h>
-#endif // LL_LINUX
-
-#if LL_SOLARIS
-# ifdef __sparc // Since we're talking Solaris 10 and up, only 64 bit is supported.
-# define LL_BIG_ENDIAN 1
-# define LL_SOLARIS_ALIGNED_CPU 1 // used to designate issues where SPARC alignment is addressed
-# define LL_SOLARIS_NON_MESA_GL 1 // The SPARC GL does not provide a MESA-based GL API
-# endif
-# include <sys/isa_defs.h> // ensure we know which end is up
-#endif // LL_SOLARIS
-
-#if (defined(LL_WINDOWS) || (defined(LL_LINUX) && (__BYTE_ORDER == __LITTLE_ENDIAN)) || (defined(LL_DARWIN) && defined(__LITTLE_ENDIAN__)) || (defined(LL_SOLARIS) && defined(__i386)))
-#define LL_LITTLE_ENDIAN 1
-#else
-#define LL_BIG_ENDIAN 1
-#endif
-
-// Per-compiler switches
-#ifdef __GNUC__
-#define LL_FORCE_INLINE inline __attribute__((always_inline))
-#else
-#define LL_FORCE_INLINE __forceinline
-#endif
-
-// Figure out differences between compilers
-#if defined(__GNUC__)
- #define GCC_VERSION (__GNUC__ * 10000 \
- + __GNUC_MINOR__ * 100 \
- + __GNUC_PATCHLEVEL__)
- #ifndef LL_GNUC
- #define LL_GNUC 1
- #endif
-#elif defined(__MSVC_VER__) || defined(_MSC_VER)
- #ifndef LL_MSVC
- #define LL_MSVC 1
- #endif
- #if _MSC_VER < 1400
- #define LL_MSVC7 //Visual C++ 2003 or earlier
- #endif
-#endif
-
-// Deal with minor differences on Unixy OSes.
-#if LL_DARWIN || LL_LINUX
- // Different name, same functionality.
- #define stricmp strcasecmp
- #define strnicmp strncasecmp
-
- // Not sure why this is different, but...
- #ifndef MAX_PATH
- #define MAX_PATH PATH_MAX
- #endif // not MAX_PATH
-
-#endif
-
-
-// Deal with the differeneces on Windows
-#if LL_MSVC
-namespace snprintf_hack
-{
- int snprintf(char *str, size_t size, const char *format, ...);
-}
-
-// #define snprintf safe_snprintf /* Flawfinder: ignore */
-using snprintf_hack::snprintf;
-#endif // LL_MSVC
-
-// Static linking with apr on windows needs to be declared.
-#ifdef LL_WINDOWS
-#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
-#ifndef XML_STATIC
-#define XML_STATIC
-#endif
-#endif // LL_WINDOWS
-
-
-// Deal with VC6 problems
-#if LL_MSVC
-#pragma warning( 3 : 4701 ) // "local variable used without being initialized" Treat this as level 3, not level 4.
-#pragma warning( 3 : 4702 ) // "unreachable code" Treat this as level 3, not level 4.
-#pragma warning( 3 : 4189 ) // "local variable initialized but not referenced" Treat this as level 3, not level 4.
-//#pragma warning( 3 : 4018 ) // "signed/unsigned mismatch" Treat this as level 3, not level 4.
-#pragma warning( 3 : 4263 ) // 'function' : member function does not override any base class virtual member function
-#pragma warning( 3 : 4264 ) // "'virtual_function' : no override available for virtual member function from base 'class'; function is hidden"
-#pragma warning( 3 : 4265 ) // "class has virtual functions, but destructor is not virtual"
-#pragma warning( 3 : 4266 ) // 'function' : no override available for virtual member function from base 'type'; function is hidden
-#pragma warning( disable : 4284 ) // silly MS warning deep inside their <map> include file
-#pragma warning( disable : 4503 ) // 'decorated name length exceeded, name was truncated'. Does not seem to affect compilation.
-#pragma warning( disable : 4800 ) // 'BOOL' : forcing value to bool 'true' or 'false' (performance warning)
-#pragma warning( disable : 4996 ) // warning: deprecated
-#endif // LL_MSVC
-
-#endif // not LL_LINDEN_PREPROCESSOR_H
+/**
+ * @file llpreprocessor.h
+ * @brief This file should be included in all Linden Lab files and
+ * should only contain special preprocessor directives
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$
+ *
+ * Copyright (c) 2001-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LLPREPROCESSOR_H
+#define LLPREPROCESSOR_H
+
+// Figure out endianness of platform
+#ifdef LL_LINUX
+#define __ENABLE_WSTRING
+#include <endian.h>
+#endif // LL_LINUX
+
+#if LL_SOLARIS
+# ifdef __sparc // Since we're talking Solaris 10 and up, only 64 bit is supported.
+# define LL_BIG_ENDIAN 1
+# define LL_SOLARIS_ALIGNED_CPU 1 // used to designate issues where SPARC alignment is addressed
+# define LL_SOLARIS_NON_MESA_GL 1 // The SPARC GL does not provide a MESA-based GL API
+# endif
+# include <sys/isa_defs.h> // ensure we know which end is up
+#endif // LL_SOLARIS
+
+#if (defined(LL_WINDOWS) || (defined(LL_LINUX) && (__BYTE_ORDER == __LITTLE_ENDIAN)) || (defined(LL_DARWIN) && defined(__LITTLE_ENDIAN__)) || (defined(LL_SOLARIS) && defined(__i386)))
+#define LL_LITTLE_ENDIAN 1
+#else
+#define LL_BIG_ENDIAN 1
+#endif
+
+// Per-compiler switches
+#ifdef __GNUC__
+#define LL_FORCE_INLINE inline __attribute__((always_inline))
+#else
+#define LL_FORCE_INLINE __forceinline
+#endif
+
+// Figure out differences between compilers
+#if defined(__GNUC__)
+ #define GCC_VERSION (__GNUC__ * 10000 \
+ + __GNUC_MINOR__ * 100 \
+ + __GNUC_PATCHLEVEL__)
+ #ifndef LL_GNUC
+ #define LL_GNUC 1
+ #endif
+#elif defined(__MSVC_VER__) || defined(_MSC_VER)
+ #ifndef LL_MSVC
+ #define LL_MSVC 1
+ #endif
+ #if _MSC_VER < 1400
+ #define LL_MSVC7 //Visual C++ 2003 or earlier
+ #endif
+#endif
+
+// Deal with minor differences on Unixy OSes.
+#if LL_DARWIN || LL_LINUX
+ // Different name, same functionality.
+ #define stricmp strcasecmp
+ #define strnicmp strncasecmp
+
+ // Not sure why this is different, but...
+ #ifndef MAX_PATH
+ #define MAX_PATH PATH_MAX
+ #endif // not MAX_PATH
+
+#endif
+
+
+// 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
+#ifndef XML_STATIC
+#define XML_STATIC
+#endif
+#endif // LL_WINDOWS
+
+
+// Deal with VC6 problems
+#if LL_MSVC
+#pragma warning( 3 : 4701 ) // "local variable used without being initialized" Treat this as level 3, not level 4.
+#pragma warning( 3 : 4702 ) // "unreachable code" Treat this as level 3, not level 4.
+#pragma warning( 3 : 4189 ) // "local variable initialized but not referenced" Treat this as level 3, not level 4.
+//#pragma warning( 3 : 4018 ) // "signed/unsigned mismatch" Treat this as level 3, not level 4.
+#pragma warning( 3 : 4263 ) // 'function' : member function does not override any base class virtual member function
+#pragma warning( 3 : 4264 ) // "'virtual_function' : no override available for virtual member function from base 'class'; function is hidden"
+#pragma warning( 3 : 4265 ) // "class has virtual functions, but destructor is not virtual"
+#pragma warning( 3 : 4266 ) // 'function' : no override available for virtual member function from base 'type'; function is hidden
+#pragma warning( disable : 4284 ) // silly MS warning deep inside their <map> include file
+#pragma warning( disable : 4503 ) // 'decorated name length exceeded, name was truncated'. Does not seem to affect compilation.
+#pragma warning( disable : 4800 ) // 'BOOL' : forcing value to bool 'true' or 'false' (performance warning)
+#pragma warning( disable : 4996 ) // warning: deprecated
+
+// level 4 warnings that we need to disable:
+#pragma warning (disable : 4100) // unreferenced formal parameter
+#pragma warning (disable : 4127) // conditional expression is constant (e.g. while(1) )
+#pragma warning (disable : 4244) // possible loss of data on conversions
+#pragma warning (disable : 4396) // the inline specifier cannot be used when a friend declaration refers to a specialization of a function template
+#pragma warning (disable : 4512) // assignment operator could not be generated
+#pragma warning (disable : 4706) // assignment within conditional (even if((x = y)) )
+
+#pragma warning (disable : 4251) // member needs to have dll-interface to be used by clients of class
+#pragma warning (disable : 4275) // non dll-interface class used as base for dll-interface class
+#endif // LL_MSVC
+
+#if LL_WINDOWS
+#define LL_DLLEXPORT __declspec(dllexport)
+#define LL_DLLIMPORT __declspec(dllimport)
+#elif LL_LINUX
+#define LL_DLLEXPORT __attribute__ ((visibility("default")))
+#define LL_DLLIMPORT
+#else
+#define LL_DLLEXPORT
+#define LL_DLLIMPORT
+#endif // LL_WINDOWS
+
+#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
diff --git a/indra/llcommon/llprocesslauncher.h b/indra/llcommon/llprocesslauncher.h
index a1b8e22691..880562157f 100644
--- a/indra/llcommon/llprocesslauncher.h
+++ b/indra/llcommon/llprocesslauncher.h
@@ -42,7 +42,7 @@
It also keeps track of whether the process is still running, and can kill it if required.
*/
-class LLProcessLauncher
+class LL_COMMON_API LLProcessLauncher
{
LOG_CLASS(LLProcessLauncher);
public:
diff --git a/indra/llcommon/llqueuedthread.h b/indra/llcommon/llqueuedthread.h
index 3ba43e1e07..8bfa5632a1 100644
--- a/indra/llcommon/llqueuedthread.h
+++ b/indra/llcommon/llqueuedthread.h
@@ -47,7 +47,7 @@
// Note: ~LLQueuedThread is O(N) N=# of queued threads, assumed to be small
// It is assumed that LLQueuedThreads are rarely created/destroyed.
-class LLQueuedThread : public LLThread
+class LL_COMMON_API LLQueuedThread : public LLThread
{
//------------------------------------------------------------------------
public:
@@ -80,7 +80,7 @@ public:
//------------------------------------------------------------------------
public:
- class QueuedRequest : public LLSimpleHashEntry<handle_t>
+ class LL_COMMON_API QueuedRequest : public LLSimpleHashEntry<handle_t>
{
friend class LLQueuedThread;
@@ -148,6 +148,7 @@ protected:
}
};
+
//------------------------------------------------------------------------
public:
diff --git a/indra/llcommon/llrand.h b/indra/llcommon/llrand.h
index d12597bb53..30fec9b982 100644
--- a/indra/llcommon/llrand.h
+++ b/indra/llcommon/llrand.h
@@ -65,32 +65,32 @@
/**
*@brief Generate a float from [0, RAND_MAX).
*/
-S32 ll_rand();
+S32 LL_COMMON_API ll_rand();
/**
*@brief Generate a float from [0, val) or (val, 0].
*/
-S32 ll_rand(S32 val);
+S32 LL_COMMON_API ll_rand(S32 val);
/**
*@brief Generate a float from [0, 1.0).
*/
-F32 ll_frand();
+F32 LL_COMMON_API ll_frand();
/**
*@brief Generate a float from [0, val) or (val, 0].
*/
-F32 ll_frand(F32 val);
+F32 LL_COMMON_API ll_frand(F32 val);
/**
*@brief Generate a double from [0, 1.0).
*/
-F64 ll_drand();
+F64 LL_COMMON_API ll_drand();
/**
*@brief Generate a double from [0, val) or (val, 0].
*/
-F64 ll_drand(F64 val);
+F64 LL_COMMON_API ll_drand(F64 val);
/**
* @brief typedefs for good boost lagged fibonacci.
diff --git a/indra/llcommon/llrefcount.h b/indra/llcommon/llrefcount.h
index d3597b454c..9ab844eb22 100644
--- a/indra/llcommon/llrefcount.h
+++ b/indra/llcommon/llrefcount.h
@@ -39,9 +39,9 @@
// see llthread.h for LLThreadSafeRefCount
//----------------------------------------------------------------------------
-class LLRefCount
+class LL_COMMON_API LLRefCount
{
-protected:
+private:
LLRefCount(const LLRefCount& other); // no implementation
private:
LLRefCount& operator=(const LLRefCount&); // no implementation
diff --git a/indra/llcommon/llrun.h b/indra/llcommon/llrun.h
index 77b23d9051..1fc9925df9 100644
--- a/indra/llcommon/llrun.h
+++ b/indra/llcommon/llrun.h
@@ -48,7 +48,7 @@ class LLRunnable;
* which are scheduled to run on a repeating or one time basis.
* @see LLRunnable
*/
-class LLRunner
+class LL_COMMON_API LLRunner
{
public:
/**
@@ -149,7 +149,7 @@ protected:
* something useful.
* @see LLRunner
*/
-class LLRunnable
+class LL_COMMON_API LLRunnable
{
public:
LLRunnable();
diff --git a/indra/llcommon/llsd.h b/indra/llcommon/llsd.h
index d2845a3757..552bb57498 100644
--- a/indra/llcommon/llsd.h
+++ b/indra/llcommon/llsd.h
@@ -89,7 +89,7 @@
@nosubgrouping
*/
-class LLSD
+class LL_COMMON_API LLSD
{
public:
LLSD(); ///< initially Undefined
@@ -387,7 +387,7 @@ struct llsd_select_string : public std::unary_function<LLSD, LLSD::String>
}
};
-std::ostream& operator<<(std::ostream& s, const LLSD& llsd);
+LL_COMMON_API std::ostream& operator<<(std::ostream& s, const LLSD& llsd);
/** QUESTIONS & TO DOS
- Would Binary be more convenient as usigned char* buffer semantics?
diff --git a/indra/llcommon/llsdserialize.h b/indra/llcommon/llsdserialize.h
index 7463d1e5dd..2f2b292189 100644
--- a/indra/llcommon/llsdserialize.h
+++ b/indra/llcommon/llsdserialize.h
@@ -44,7 +44,7 @@
* @class LLSDParser
* @brief Abstract base class for LLSD parsers.
*/
-class LLSDParser : public LLRefCount
+class LL_COMMON_API LLSDParser : public LLRefCount
{
protected:
/**
@@ -221,7 +221,7 @@ protected:
* @class LLSDNotationParser
* @brief Parser which handles the original notation format for LLSD.
*/
-class LLSDNotationParser : public LLSDParser
+class LL_COMMON_API LLSDNotationParser : public LLSDParser
{
protected:
/**
@@ -294,7 +294,7 @@ private:
* @class LLSDXMLParser
* @brief Parser which handles XML format LLSD.
*/
-class LLSDXMLParser : public LLSDParser
+class LL_COMMON_API LLSDXMLParser : public LLSDParser
{
protected:
/**
@@ -342,7 +342,7 @@ private:
* @class LLSDBinaryParser
* @brief Parser which handles binary formatted LLSD.
*/
-class LLSDBinaryParser : public LLSDParser
+class LL_COMMON_API LLSDBinaryParser : public LLSDParser
{
protected:
/**
@@ -407,7 +407,7 @@ private:
* @class LLSDFormatter
* @brief Abstract base class for formatting LLSD.
*/
-class LLSDFormatter : public LLRefCount
+class LL_COMMON_API LLSDFormatter : public LLRefCount
{
protected:
/**
@@ -479,7 +479,7 @@ protected:
* @class LLSDNotationFormatter
* @brief Formatter which outputs the original notation format for LLSD.
*/
-class LLSDNotationFormatter : public LLSDFormatter
+class LL_COMMON_API LLSDNotationFormatter : public LLSDFormatter
{
protected:
/**
@@ -520,7 +520,7 @@ public:
* @class LLSDXMLFormatter
* @brief Formatter which outputs the LLSD as XML.
*/
-class LLSDXMLFormatter : public LLSDFormatter
+class LL_COMMON_API LLSDXMLFormatter : public LLSDFormatter
{
protected:
/**
@@ -588,7 +588,7 @@ protected:
* Map: '{' + 4 byte integer size every(key + value) + '}'<br>
* map keys are serialized as 'k' + 4 byte integer size + string
*/
-class LLSDBinaryFormatter : public LLSDFormatter
+class LL_COMMON_API LLSDBinaryFormatter : public LLSDFormatter
{
protected:
/**
@@ -638,9 +638,14 @@ protected:
* params << "[{'version':i1}," << LLSDOStreamer<LLSDNotationFormatter>(sd)
* << "]";
* </code>
+ *
+ * *NOTE - formerly this class inherited from its template parameter Formatter,
+ * but all insnatiations passed in LLRefCount subclasses. This conflicted with
+ * the auto allocation intended for this class template (demonstrated in the
+ * example above). -brad
*/
template <class Formatter>
-class LLSDOStreamer : public Formatter
+class LLSDOStreamer
{
public:
/**
@@ -661,7 +666,8 @@ public:
std::ostream& str,
const LLSDOStreamer<Formatter>& formatter)
{
- formatter.format(formatter.mSD, str, formatter.mOptions);
+ LLPointer<Formatter> f = new Formatter;
+ f->format(formatter.mSD, str, formatter.mOptions);
return str;
}
@@ -677,7 +683,7 @@ typedef LLSDOStreamer<LLSDXMLFormatter> LLSDXMLStreamer;
* @class LLSDSerialize
* @brief Serializer / deserializer for the various LLSD formats
*/
-class LLSDSerialize
+class LL_COMMON_API LLSDSerialize
{
public:
enum ELLSD_Serialize
diff --git a/indra/llcommon/llsdserialize_xml.cpp b/indra/llcommon/llsdserialize_xml.cpp
index c12ca350de..7e1c2e35e0 100644
--- a/indra/llcommon/llsdserialize_xml.cpp
+++ b/indra/llcommon/llsdserialize_xml.cpp
@@ -37,6 +37,7 @@
#include <deque>
#include "apr_base64.h"
+#include <boost/regex.hpp>
extern "C"
{
@@ -777,10 +778,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;
diff --git a/indra/llcommon/llsdutil.cpp b/indra/llcommon/llsdutil.cpp
index 0202a033c3..c8d8030e87 100644
--- a/indra/llcommon/llsdutil.cpp
+++ b/indra/llcommon/llsdutil.cpp
@@ -46,6 +46,11 @@
#endif
#include "llsdserialize.h"
+#include "stringize.h"
+
+#include <map>
+#include <set>
+#include <boost/range.hpp>
// U32
LLSD ll_sd_from_U32(const U32 val)
@@ -313,3 +318,353 @@ 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)
+{
+ // 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;
+
+#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);
+ // The usual caveats about comparing floating-point numbers apply. This is
+ // only useful when we expect identical bit representation for a given
+ // Real value, e.g. for integer-valued Reals.
+ COMPARE_SCALAR(Real);
+ 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))
+ 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]))
+ 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 << "): "
+ "unknown type " << lhs.type() << LL_ENDL;
+ return false; // pacify the compiler
+ }
+}
diff --git a/indra/llcommon/llsdutil.h b/indra/llcommon/llsdutil.h
index 501600f1d9..6a6c396687 100644
--- a/indra/llcommon/llsdutil.h
+++ b/indra/llcommon/llsdutil.h
@@ -35,62 +35,32 @@
#ifndef LL_LLSDUTIL_H
#define LL_LLSDUTIL_H
-#include "llsd.h"
-
-// vector3
-class LLVector3;
-LLSD ll_sd_from_vector3(const LLVector3& vec);
-LLVector3 ll_vector3_from_sd(const LLSD& sd, S32 start_index = 0);
-
-// vector4
-class LLVector4;
-LLSD ll_sd_from_vector4(const LLVector4& vec);
-LLVector4 ll_vector4_from_sd(const LLSD& sd, S32 start_index = 0);
-
-// vector3d (double)
-class LLVector3d;
-LLSD ll_sd_from_vector3d(const LLVector3d& vec);
-LLVector3d ll_vector3d_from_sd(const LLSD& sd, S32 start_index = 0);
-
-// vector2
-class LLVector2;
-LLSD ll_sd_from_vector2(const LLVector2& vec);
-LLVector2 ll_vector2_from_sd(const LLSD& sd);
-
-// Quaternion
-class LLQuaternion;
-LLSD ll_sd_from_quaternion(const LLQuaternion& quat);
-LLQuaternion ll_quaternion_from_sd(const LLSD& sd);
-
-// color4
-class LLColor4;
-LLSD ll_sd_from_color4(const LLColor4& c);
-LLColor4 ll_color4_from_sd(const LLSD& sd);
+class LLSD;
// U32
-LLSD ll_sd_from_U32(const U32);
-U32 ll_U32_from_sd(const LLSD& sd);
+LL_COMMON_API LLSD ll_sd_from_U32(const U32);
+LL_COMMON_API U32 ll_U32_from_sd(const LLSD& sd);
// U64
-LLSD ll_sd_from_U64(const U64);
-U64 ll_U64_from_sd(const LLSD& sd);
+LL_COMMON_API LLSD ll_sd_from_U64(const U64);
+LL_COMMON_API U64 ll_U64_from_sd(const LLSD& sd);
// IP Address
-LLSD ll_sd_from_ipaddr(const U32);
-U32 ll_ipaddr_from_sd(const LLSD& sd);
+LL_COMMON_API LLSD ll_sd_from_ipaddr(const U32);
+LL_COMMON_API U32 ll_ipaddr_from_sd(const LLSD& sd);
// Binary to string
-LLSD ll_string_from_binary(const LLSD& sd);
+LL_COMMON_API LLSD ll_string_from_binary(const LLSD& sd);
//String to binary
-LLSD ll_binary_from_string(const LLSD& sd);
+LL_COMMON_API LLSD ll_binary_from_string(const LLSD& sd);
// Serializes sd to static buffer and returns pointer, useful for gdb debugging.
-char* ll_print_sd(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.
-char* ll_pretty_print_sd_ptr(const LLSD* sd);
-char* ll_pretty_print_sd(const LLSD& sd);
+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
//"valid" values in a 3rd LLSD. Default values
@@ -99,11 +69,69 @@ char* ll_pretty_print_sd(const LLSD& sd);
//Returns false if the test is of same type but values differ in type
//Otherwise, returns true
-BOOL compare_llsd_with_template(
+LL_COMMON_API BOOL compare_llsd_with_template(
const LLSD& llsd_to_test,
const LLSD& template_llsd,
LLSD& resultant_llsd);
+/**
+ * Recursively determine whether a given LLSD data block "matches" another
+ * LLSD prototype. The returned string is empty() on success, non-empty() on
+ * mismatch.
+ *
+ * This function tests structure (types) rather than data values. It is
+ * intended for when a consumer expects an LLSD block with a particular
+ * structure, and must succinctly detect whether the arriving block is
+ * well-formed. For instance, a test of the form:
+ * @code
+ * if (! (data.has("request") && data.has("target") && data.has("modifier") ...))
+ * @endcode
+ * could instead be expressed by initializing a prototype LLSD map with the
+ * required keys and writing:
+ * @code
+ * if (! llsd_matches(prototype, data).empty())
+ * @endcode
+ *
+ * A non-empty return value is an error-message fragment intended to indicate
+ * to (English-speaking) developers where in the prototype structure the
+ * mismatch occurred.
+ *
+ * * If a slot in the prototype isUndefined(), then anything is valid at that
+ * place in the real object. (Passing prototype == LLSD() matches anything
+ * at all.)
+ * * An array in the prototype must match a data array at least that large.
+ * (Additional entries in the data array are ignored.) Every isDefined()
+ * entry in the prototype array must match the corresponding entry in the
+ * data array.
+ * * A map in the prototype must match a map in the data. Every key in the
+ * prototype map must match a corresponding key in the data map. (Additional
+ * keys in the data map are ignored.) Every isDefined() value in the
+ * prototype map must match the corresponding key's value in the data map.
+ * * Scalar values in the prototype are tested for @em type rather than value.
+ * For instance, a String in the prototype matches any String at all. In
+ * effect, storing an Integer at a particular place in the prototype asserts
+ * that the caller intends to apply asInteger() to the corresponding slot in
+ * the data.
+ * * A String in the prototype matches String, Boolean, Integer, Real, UUID,
+ * Date and URI, because asString() applied to any of these produces a
+ * meaningful result.
+ * * Similarly, a Boolean, Integer or Real in the prototype can match any of
+ * Boolean, Integer or Real in the data -- or even String.
+ * * UUID matches UUID or String.
+ * * Date matches Date or String.
+ * * URI matches URI or String.
+ * * Binary in the prototype matches only Binary in the data.
+ *
+ * @TODO: when a Boolean, Integer or Real in the prototype matches a String in
+ * the data, we should examine the String @em value to ensure it can be
+ * meaningfully converted to the requested type. The same goes for UUID, Date
+ * and URI.
+ */
+LL_COMMON_API std::string llsd_matches(const LLSD& prototype, const LLSD& data, const std::string& pfx="");
+
+/// Deep equality
+LL_COMMON_API bool llsd_equals(const LLSD& lhs, const LLSD& rhs);
+
// Simple function to copy data out of input & output iterators if
// there is no need for casting.
template<typename Input> LLSD llsd_copy_array(Input iter, Input end)
diff --git a/indra/llcommon/llsecondlifeurls.h b/indra/llcommon/llsecondlifeurls.h
index a2e5f0b9c6..bd2f9f7604 100644
--- a/indra/llcommon/llsecondlifeurls.h
+++ b/indra/llcommon/llsecondlifeurls.h
@@ -34,49 +34,49 @@
#define LL_LLSECONDLIFEURLS_H
/*
// Account registration web page
-extern const std::string CREATE_ACCOUNT_URL;
+LL_COMMON_API extern const std::string CREATE_ACCOUNT_URL;
// Manage Account
-extern const std::string MANAGE_ACCOUNT;
+LL_COMMON_API extern const std::string MANAGE_ACCOUNT;
-extern const std::string AUCTION_URL;
+LL_COMMON_API extern const std::string AUCTION_URL;
-extern const std::string EVENTS_URL;
+LL_COMMON_API extern const std::string EVENTS_URL;
*/
// Tier up to a new land level.
-extern const std::string TIER_UP_URL;
+LL_COMMON_API extern const std::string TIER_UP_URL;
// Tier up to a new land level.
-extern const std::string LAND_URL;
+LL_COMMON_API extern const std::string LAND_URL;
// How to get DirectX 9
-extern const std::string DIRECTX_9_URL;
+LL_COMMON_API extern const std::string DIRECTX_9_URL;
/*
// Upgrade from basic membership to premium membership
-extern const std::string UPGRADE_TO_PREMIUM_URL;
+LL_COMMON_API extern const std::string UPGRADE_TO_PREMIUM_URL;
// Out of date VIA chipset
-extern const std::string VIA_URL;
+LL_COMMON_API extern const std::string VIA_URL;
// Support URL
-extern const std::string SUPPORT_URL;
+LL_COMMON_API extern const std::string SUPPORT_URL;
// Linden Blogs page
-extern const std::string BLOGS_URL;
+LL_COMMON_API extern const std::string BLOGS_URL;
// Currency page
-extern const std::string BUY_CURRENCY_URL;
+LL_COMMON_API extern const std::string BUY_CURRENCY_URL;
// LSL script wiki
-extern const std::string LSL_DOC_URL;
+LL_COMMON_API extern const std::string LSL_DOC_URL;
// SL KnowledgeBase page
-extern const std::string SL_KB_URL;
+LL_COMMON_API extern const std::string SL_KB_URL;
// Release Notes Redirect URL for Server and Viewer
-extern const std::string RELEASE_NOTES_BASE_URL;
+LL_COMMON_API extern const std::string RELEASE_NOTES_BASE_URL;
*/
#endif
diff --git a/indra/llcommon/llsimplehash.h b/indra/llcommon/llsimplehash.h
index 0ba2a3014c..5df93b646e 100644
--- a/indra/llcommon/llsimplehash.h
+++ b/indra/llcommon/llsimplehash.h
@@ -64,7 +64,7 @@ public:
};
template <typename HASH_KEY_TYPE, int TABLE_SIZE>
-class LLSimpleHash
+class LL_COMMON_API LLSimpleHash
{
public:
LLSimpleHash()
diff --git a/indra/llcommon/llsingleton.cpp b/indra/llcommon/llsingleton.cpp
new file mode 100644
index 0000000000..6b5feaf1c4
--- /dev/null
+++ b/indra/llcommon/llsingleton.cpp
@@ -0,0 +1,38 @@
+/**
+ * @file llsingleton.cpp
+ * @author Brad Kittenbrink
+ *
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ *
+ * Copyright (c) 2009-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llsingleton.h"
+
+std::map<std::string, void *> * LLSingletonRegistry::sSingletonMap = NULL;
+
diff --git a/indra/llcommon/llsingleton.h b/indra/llcommon/llsingleton.h
index 2e7d845bf7..f55fafadd8 100644
--- a/indra/llcommon/llsingleton.h
+++ b/indra/llcommon/llsingleton.h
@@ -33,7 +33,41 @@
#include "llerror.h" // *TODO: eliminate this
+#include <typeinfo>
#include <boost/noncopyable.hpp>
+#include <boost/any.hpp>
+
+/// @brief A global registry of all singletons to prevent duplicate allocations
+/// across shared library boundaries
+class LL_COMMON_API LLSingletonRegistry {
+ private:
+ typedef std::map<std::string, void *> TypeMap;
+ static TypeMap * sSingletonMap;
+
+ static void checkInit()
+ {
+ if(sSingletonMap == NULL)
+ {
+ sSingletonMap = new TypeMap();
+ }
+ }
+
+ public:
+ template<typename T> static void * & get()
+ {
+ std::string name(typeid(T).name());
+
+ checkInit();
+
+ // the first entry of the pair returned by insert will be either the existing
+ // iterator matching our key, or the newly inserted NULL initialized entry
+ // see "Insert element" in http://www.sgi.com/tech/stl/UniqueAssociativeContainer.html
+ TypeMap::iterator result =
+ sSingletonMap->insert(std::make_pair(name, (void*)NULL)).first;
+
+ return result->second;
+ }
+};
// LLSingleton implements the getInstance() method part of the Singleton
// pattern. It can't make the derived class constructors protected, though, so
@@ -107,8 +141,17 @@ public:
static SingletonInstanceData& getData()
{
- static SingletonInstanceData data;
- return data;
+ // this is static to cache the lookup results
+ static void * & registry = LLSingletonRegistry::get<DERIVED_TYPE>();
+
+ // *TODO - look into making this threadsafe
+ if(NULL == registry)
+ {
+ static SingletonInstanceData data;
+ registry = &data;
+ }
+
+ return *static_cast<SingletonInstanceData *>(registry);
}
static DERIVED_TYPE* getInstance()
diff --git a/indra/llcommon/llstacktrace.cpp b/indra/llcommon/llstacktrace.cpp
index 4be91b5b11..3cb074257b 100644
--- a/indra/llcommon/llstacktrace.cpp
+++ b/indra/llcommon/llstacktrace.cpp
@@ -1,141 +1,142 @@
-/**
- * @file llstacktrace.cpp
- * @brief stack tracing functionality
- *
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#include "llstacktrace.h"
-
-#ifdef LL_WINDOWS
-
-#include <iostream>
-#include <sstream>
-
-#include "windows.h"
-#include "Dbghelp.h"
-
-typedef USHORT NTAPI RtlCaptureStackBackTrace_Function(
- IN ULONG frames_to_skip,
- IN ULONG frames_to_capture,
- OUT PVOID *backtrace,
- OUT PULONG backtrace_hash);
-
-static RtlCaptureStackBackTrace_Function* const RtlCaptureStackBackTrace_fn =
- (RtlCaptureStackBackTrace_Function*)
- GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlCaptureStackBackTrace");
-
-bool ll_get_stack_trace(std::vector<std::string>& lines)
-{
- const S32 MAX_STACK_DEPTH = 32;
- const S32 STRING_NAME_LENGTH = 200;
- const S32 FRAME_SKIP = 2;
- static BOOL symbolsLoaded = false;
- static BOOL firstCall = true;
-
- HANDLE hProc = GetCurrentProcess();
-
- // load the symbols if they're not loaded
- if(!symbolsLoaded && firstCall)
- {
- symbolsLoaded = SymInitialize(hProc, NULL, true);
- firstCall = false;
- }
-
- // if loaded, get the call stack
- if(symbolsLoaded)
- {
- // create the frames to hold the addresses
- void* frames[MAX_STACK_DEPTH];
- memset(frames, 0, sizeof(void*)*MAX_STACK_DEPTH);
- S32 depth = 0;
-
- // get the addresses
- depth = RtlCaptureStackBackTrace_fn(FRAME_SKIP, MAX_STACK_DEPTH, frames, NULL);
-
- IMAGEHLP_LINE64 line;
- memset(&line, 0, sizeof(IMAGEHLP_LINE64));
- line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
-
- // create something to hold address info
- PIMAGEHLP_SYMBOL64 pSym;
- pSym = (PIMAGEHLP_SYMBOL64)malloc(sizeof(IMAGEHLP_SYMBOL64) + STRING_NAME_LENGTH);
- memset(pSym, 0, sizeof(IMAGEHLP_SYMBOL64) + STRING_NAME_LENGTH);
- pSym->MaxNameLength = STRING_NAME_LENGTH;
- pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
-
- // get address info for each address frame
- // and store
- for(S32 i=0; i < depth; i++)
- {
- std::stringstream stack_line;
- BOOL ret;
-
- DWORD64 addr = (DWORD64)frames[i];
- ret = SymGetSymFromAddr64(hProc, addr, 0, pSym);
- if(ret)
- {
- stack_line << pSym->Name << " ";
- }
-
- DWORD dummy;
- ret = SymGetLineFromAddr64(hProc, addr, &dummy, &line);
- if(ret)
- {
- std::string file_name = line.FileName;
- std::string::size_type index = file_name.rfind("\\");
- stack_line << file_name.substr(index + 1, file_name.size()) << ":" << line.LineNumber;
- }
-
- lines.push_back(stack_line.str());
- }
-
- free(pSym);
-
- // TODO: figure out a way to cleanup symbol loading
- // Not hugely necessary, however.
- //SymCleanup(hProc);
- return true;
- }
- else
- {
- lines.push_back("Stack Trace Failed. PDB symbol info not loaded");
- }
-
- return false;
-}
-
-#else
-
-bool ll_get_stack_trace(std::vector<std::string>& lines)
-{
- return false;
-}
-
-#endif
-
+/**
+ * @file llstacktrace.cpp
+ * @brief stack tracing functionality
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$
+ *
+ * Copyright (c) 2001-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+#include "llstacktrace.h"
+
+#ifdef LL_WINDOWS
+
+#include <iostream>
+#include <sstream>
+
+#include "windows.h"
+#include "Dbghelp.h"
+
+typedef USHORT NTAPI RtlCaptureStackBackTrace_Function(
+ IN ULONG frames_to_skip,
+ IN ULONG frames_to_capture,
+ OUT PVOID *backtrace,
+ OUT PULONG backtrace_hash);
+
+static RtlCaptureStackBackTrace_Function* const RtlCaptureStackBackTrace_fn =
+ (RtlCaptureStackBackTrace_Function*)
+ GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlCaptureStackBackTrace");
+
+bool ll_get_stack_trace(std::vector<std::string>& lines)
+{
+ const S32 MAX_STACK_DEPTH = 32;
+ const S32 STRING_NAME_LENGTH = 200;
+ const S32 FRAME_SKIP = 2;
+ static BOOL symbolsLoaded = false;
+ static BOOL firstCall = true;
+
+ HANDLE hProc = GetCurrentProcess();
+
+ // load the symbols if they're not loaded
+ if(!symbolsLoaded && firstCall)
+ {
+ symbolsLoaded = SymInitialize(hProc, NULL, true);
+ firstCall = false;
+ }
+
+ // if loaded, get the call stack
+ if(symbolsLoaded)
+ {
+ // create the frames to hold the addresses
+ void* frames[MAX_STACK_DEPTH];
+ memset(frames, 0, sizeof(void*)*MAX_STACK_DEPTH);
+ S32 depth = 0;
+
+ // get the addresses
+ depth = RtlCaptureStackBackTrace_fn(FRAME_SKIP, MAX_STACK_DEPTH, frames, NULL);
+
+ IMAGEHLP_LINE64 line;
+ memset(&line, 0, sizeof(IMAGEHLP_LINE64));
+ line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
+
+ // create something to hold address info
+ PIMAGEHLP_SYMBOL64 pSym;
+ pSym = (PIMAGEHLP_SYMBOL64)malloc(sizeof(IMAGEHLP_SYMBOL64) + STRING_NAME_LENGTH);
+ memset(pSym, 0, sizeof(IMAGEHLP_SYMBOL64) + STRING_NAME_LENGTH);
+ pSym->MaxNameLength = STRING_NAME_LENGTH;
+ pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
+
+ // get address info for each address frame
+ // and store
+ for(S32 i=0; i < depth; i++)
+ {
+ std::stringstream stack_line;
+ BOOL ret;
+
+ DWORD64 addr = (DWORD64)frames[i];
+ ret = SymGetSymFromAddr64(hProc, addr, 0, pSym);
+ if(ret)
+ {
+ stack_line << pSym->Name << " ";
+ }
+
+ DWORD dummy;
+ ret = SymGetLineFromAddr64(hProc, addr, &dummy, &line);
+ if(ret)
+ {
+ std::string file_name = line.FileName;
+ std::string::size_type index = file_name.rfind("\\");
+ stack_line << file_name.substr(index + 1, file_name.size()) << ":" << line.LineNumber;
+ }
+
+ lines.push_back(stack_line.str());
+ }
+
+ free(pSym);
+
+ // TODO: figure out a way to cleanup symbol loading
+ // Not hugely necessary, however.
+ //SymCleanup(hProc);
+ return true;
+ }
+ else
+ {
+ lines.push_back("Stack Trace Failed. PDB symbol info not loaded");
+ }
+
+ return false;
+}
+
+#else
+
+bool ll_get_stack_trace(std::vector<std::string>& lines)
+{
+ return false;
+}
+
+#endif
+
diff --git a/indra/llcommon/llstacktrace.h b/indra/llcommon/llstacktrace.h
index 609b934a97..b84b1aa6ad 100644
--- a/indra/llcommon/llstacktrace.h
+++ b/indra/llcommon/llstacktrace.h
@@ -1,44 +1,44 @@
-/**
- * @file llstacktrace.h
- * @brief stack trace functions
- *
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-
-#ifndef LL_LLSTACKTRACE_H
-#define LL_LLSTACKTRACE_H
-
-#include "stdtypes.h"
-#include <vector>
-#include <string>
-
-bool ll_get_stack_trace(std::vector<std::string>& lines);
-
-#endif
-
+/**
+ * @file llstacktrace.h
+ * @brief stack trace functions
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$
+ *
+ * Copyright (c) 2001-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+
+#ifndef LL_LLSTACKTRACE_H
+#define LL_LLSTACKTRACE_H
+
+#include "stdtypes.h"
+#include <vector>
+#include <string>
+
+LL_COMMON_API bool ll_get_stack_trace(std::vector<std::string>& lines);
+
+#endif
+
diff --git a/indra/llcommon/llstat.cpp b/indra/llcommon/llstat.cpp
index 90dae11793..0bd2609f4a 100644
--- a/indra/llcommon/llstat.cpp
+++ b/indra/llcommon/llstat.cpp
@@ -43,7 +43,7 @@
// 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;
@@ -130,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))
@@ -141,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 )
@@ -254,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();
}
@@ -269,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);
}
}
@@ -612,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)
{
@@ -630,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
diff --git a/indra/llcommon/llstat.h b/indra/llcommon/llstat.h
index bad18f46a0..bd73c9a6bb 100644
--- a/indra/llcommon/llstat.h
+++ b/indra/llcommon/llstat.h
@@ -52,7 +52,7 @@ class LLSD;
// amounts of time with very low memory cost.
//
-class LLStatAccum
+class LL_COMMON_API LLStatAccum
{
protected:
LLStatAccum(bool use_frame_timer);
@@ -116,7 +116,7 @@ public:
F64 mLastSampleValue;
};
-class LLStatMeasure : public LLStatAccum
+class LL_COMMON_API LLStatMeasure : public LLStatAccum
// gathers statistics about things that are measured
// ex.: tempature, time dilation
{
@@ -131,7 +131,7 @@ public:
};
-class LLStatRate : public LLStatAccum
+class LL_COMMON_API LLStatRate : public LLStatAccum
// gathers statistics about things that can be counted over time
// ex.: LSL instructions executed, messages sent, simulator frames completed
// renders it in terms of rate of thing per second
@@ -147,7 +147,7 @@ public:
};
-class LLStatTime : public LLStatAccum
+class LL_COMMON_API LLStatTime : public LLStatAccum
// gathers statistics about time spent in a block of code
// measure average duration per second in the block
{
@@ -178,7 +178,7 @@ private:
// Use this class on the stack to record statistics about an area of code
-class LLPerfBlock
+class LL_COMMON_API LLPerfBlock
{
public:
struct StatEntry
@@ -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,14 +222,14 @@ 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"
};
// ----------------------------------------------------------------------------
-class LLPerfStats
+class LL_COMMON_API LLPerfStats
{
public:
LLPerfStats(const std::string& process_name = "unknown", S32 process_pid = 0);
@@ -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; }
@@ -256,7 +265,7 @@ private:
};
// ----------------------------------------------------------------------------
-class LLStat
+class LL_COMMON_API LLStat
{
private:
typedef std::multimap<std::string, LLStat*> stat_map_t;
diff --git a/indra/llcommon/llstreamtools.h b/indra/llcommon/llstreamtools.h
index a6dc4d51e2..f64e761409 100644
--- a/indra/llcommon/llstreamtools.h
+++ b/indra/llcommon/llstreamtools.h
@@ -39,23 +39,23 @@
// unless specifed otherwise these all return input_stream.good()
// skips spaces and tabs
-bool skip_whitespace(std::istream& input_stream);
+LL_COMMON_API bool skip_whitespace(std::istream& input_stream);
// skips whitespace and newlines
-bool skip_emptyspace(std::istream& input_stream);
+LL_COMMON_API bool skip_emptyspace(std::istream& input_stream);
// skips emptyspace and lines that start with a #
-bool skip_comments_and_emptyspace(std::istream& input_stream);
+LL_COMMON_API bool skip_comments_and_emptyspace(std::istream& input_stream);
// skips to character after next newline
-bool skip_line(std::istream& input_stream);
+LL_COMMON_API bool skip_line(std::istream& input_stream);
// skips to beginning of next non-emptyspace
-bool skip_to_next_word(std::istream& input_stream);
+LL_COMMON_API bool skip_to_next_word(std::istream& input_stream);
// skips to character after the end of next keyword
// a 'keyword' is defined as the first word on a line
-bool skip_to_end_of_next_keyword(const char* keyword, std::istream& input_stream);
+LL_COMMON_API bool skip_to_end_of_next_keyword(const char* keyword, std::istream& input_stream);
// skip_to_start_of_next_keyword() is disabled -- might tickle corruption bug
// in windows iostream
@@ -65,14 +65,14 @@ bool skip_to_end_of_next_keyword(const char* keyword, std::istream& input_stream
// characters are pulled out of input_stream and appended to output_string
// returns result of input_stream.good() after characters are pulled
-bool get_word(std::string& output_string, std::istream& input_stream);
-bool get_line(std::string& output_string, std::istream& input_stream);
+LL_COMMON_API bool get_word(std::string& output_string, std::istream& input_stream);
+LL_COMMON_API bool get_line(std::string& output_string, std::istream& input_stream);
// characters are pulled out of input_stream (up to a max of 'n')
// and appended to output_string
// returns result of input_stream.good() after characters are pulled
-bool get_word(std::string& output_string, std::istream& input_stream, int n);
-bool get_line(std::string& output_string, std::istream& input_stream, int n);
+LL_COMMON_API bool get_word(std::string& output_string, std::istream& input_stream, int n);
+LL_COMMON_API bool get_line(std::string& output_string, std::istream& input_stream, int n);
// unget_line() is disabled -- might tickle corruption bug in windows iostream
//// backs up the input_stream by line_size + 1 characters
@@ -82,28 +82,28 @@ bool get_line(std::string& output_string, std::istream& input_stream, int n);
// removes the last char in 'line' if it matches 'c'
// returns true if removed last char
-bool remove_last_char(char c, std::string& line);
+LL_COMMON_API bool remove_last_char(char c, std::string& line);
// replaces escaped characters with the correct characters from left to right
// "\\" ---> '\\'
// "\n" ---> '\n'
-void unescape_string(std::string& line);
+LL_COMMON_API void unescape_string(std::string& line);
// replaces unescaped characters with expanded equivalents from left to right
// '\\' ---> "\\"
// '\n' ---> "\n"
-void escape_string(std::string& line);
+LL_COMMON_API void escape_string(std::string& line);
// replaces each '\n' character with ' '
-void replace_newlines_with_whitespace(std::string& line);
+LL_COMMON_API void replace_newlines_with_whitespace(std::string& line);
// erases any double-quote characters in line
-void remove_double_quotes(std::string& line);
+LL_COMMON_API void remove_double_quotes(std::string& line);
// the 'keyword' is defined as the first word on a line
// the 'value' is everything after the keyword on the same line
// starting at the first non-whitespace and ending right before the newline
-void get_keyword_and_value(std::string& keyword,
+LL_COMMON_API void get_keyword_and_value(std::string& keyword,
std::string& value,
const std::string& line);
@@ -111,13 +111,13 @@ void get_keyword_and_value(std::string& keyword,
// read anymore or until we hit the count. Some istream
// implimentations have a max that they will read.
// Returns the number of bytes read.
-std::streamsize fullread(
+LL_COMMON_API std::streamsize fullread(
std::istream& istr,
char* buf,
std::streamsize requested);
-std::istream& operator>>(std::istream& str, const char *tocheck);
+LL_COMMON_API std::istream& operator>>(std::istream& str, const char *tocheck);
#endif
diff --git a/indra/llcommon/llstring.cpp b/indra/llcommon/llstring.cpp
index f2edd5c559..c027aa7bdd 100644
--- a/indra/llcommon/llstring.cpp
+++ b/indra/llcommon/llstring.cpp
@@ -671,9 +671,9 @@ std::string ll_convert_wide_to_string(const wchar_t* in)
}
#endif // LL_WINDOWS
-long LLStringOps::sltOffset;
-long LLStringOps::localTimeOffset;
-bool LLStringOps::daylightSavings;
+long LLStringOps::sPacificTimeOffset = 0;
+long LLStringOps::sLocalTimeOffset = 0;
+bool LLStringOps::sPacificDaylightTime = 0;
std::map<std::string, std::string> LLStringOps::datetimeToCodes;
S32 LLStringOps::collate(const llwchar* a, const llwchar* b)
@@ -700,11 +700,11 @@ void LLStringOps::setupDatetimeInfo (bool daylight)
tmpT = gmtime (&nowT);
gmtT = mktime (tmpT);
- localTimeOffset = (long) (gmtT - localT);
+ sLocalTimeOffset = (long) (gmtT - localT);
- daylightSavings = daylight;
- sltOffset = (daylightSavings? 7 : 8 ) * 60 * 60;
+ sPacificDaylightTime = daylight;
+ sPacificTimeOffset = (sPacificDaylightTime? 7 : 8 ) * 60 * 60;
datetimeToCodes["wkday"] = "%a"; // Thu
datetimeToCodes["weekday"] = "%A"; // Thursday
@@ -957,36 +957,35 @@ bool LLStringUtil::formatDatetime(std::string& replacement, std::string token,
}
else if (param != "utc") // slt
{
- secFromEpoch -= LLStringOps::getSltOffset();
+ secFromEpoch -= LLStringOps::getPacificTimeOffset();
}
// if never fell into those two ifs above, param must be utc
if (secFromEpoch < 0) secFromEpoch = 0;
- LLDate * datetime = new LLDate((F64)secFromEpoch);
+ LLDate datetime((F64)secFromEpoch);
std::string code = LLStringOps::getDatetimeCode (token);
// special case to handle timezone
if (code == "%Z") {
if (param == "utc")
+ {
replacement = "GMT";
- else if (param == "slt")
- replacement = "SLT";
- else if (param != "local") // *TODO Vadim: not local? then what?
- replacement = LLStringOps::getDaylightSavings() ? "PDT" : "PST";
-
- return true;
- }
- replacement = datetime->toHTTPDateString(code);
-
- if (code.empty())
- {
- return false;
- }
- else
- {
+ }
+ else if (param == "local")
+ {
+ replacement = ""; // user knows their own timezone
+ }
+ else
+ {
+ // "slt" = Second Life Time, which is deprecated.
+ // If not utc or user local time, fallback to Pacific time
+ replacement = LLStringOps::getPacificDaylightTime() ? "PDT" : "PST";
+ }
return true;
}
+ replacement = datetime.toHTTPDateString(code);
+ return !code.empty();
}
// LLStringUtil::format recogizes the following patterns.
diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h
index eca7e922fd..edbb007f61 100644
--- a/indra/llcommon/llstring.h
+++ b/indra/llcommon/llstring.h
@@ -1,1290 +1,1300 @@
-/**
- * @file llstring.h
- * @brief String utility functions and std::string class.
- *
- * $LicenseInfo:firstyear=2001&license=viewergpl$
- *
- * Copyright (c) 2001-2009, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
-
-#ifndef LL_LLSTRING_H
-#define LL_LLSTRING_H
-
-#include <string>
-#include <cstdio>
-#include <locale>
-#include <iomanip>
-#include "llsd.h"
-#include "llfasttimer.h"
-
-#if LL_LINUX || LL_SOLARIS
-#include <wctype.h>
-#include <wchar.h>
-#endif
-
-#include <string.h>
-
-#if LL_SOLARIS
-// stricmp and strnicmp do not exist on Solaris:
-#define stricmp strcasecmp
-#define strnicmp strncasecmp
-#endif
-
-const char LL_UNKNOWN_CHAR = '?';
-
-#if LL_DARWIN || LL_LINUX || LL_SOLARIS
-// Template specialization of char_traits for U16s. Only necessary on Mac and Linux (exists on Windows already)
-#include <cstring>
-
-namespace std
-{
-template<>
-struct char_traits<U16>
-{
- typedef U16 char_type;
- typedef int int_type;
- typedef streampos pos_type;
- typedef streamoff off_type;
- typedef mbstate_t state_type;
-
- static void
- assign(char_type& __c1, const char_type& __c2)
- { __c1 = __c2; }
-
- static bool
- eq(const char_type& __c1, const char_type& __c2)
- { return __c1 == __c2; }
-
- static bool
- lt(const char_type& __c1, const char_type& __c2)
- { return __c1 < __c2; }
-
- static int
- compare(const char_type* __s1, const char_type* __s2, size_t __n)
- { return memcmp(__s1, __s2, __n * sizeof(char_type)); }
-
- static size_t
- length(const char_type* __s)
- {
- const char_type *cur_char = __s;
- while (*cur_char != 0)
- {
- ++cur_char;
- }
- return cur_char - __s;
- }
-
- static const char_type*
- find(const char_type* __s, size_t __n, const char_type& __a)
- { return static_cast<const char_type*>(memchr(__s, __a, __n * sizeof(char_type))); }
-
- static char_type*
- move(char_type* __s1, const char_type* __s2, size_t __n)
- { return static_cast<char_type*>(memmove(__s1, __s2, __n * sizeof(char_type))); }
-
- static char_type*
- copy(char_type* __s1, const char_type* __s2, size_t __n)
- { return static_cast<char_type*>(memcpy(__s1, __s2, __n * sizeof(char_type))); } /* Flawfinder: ignore */
-
- static char_type*
- assign(char_type* __s, size_t __n, char_type __a)
- {
- // This isn't right.
- //return static_cast<char_type*>(memset(__s, __a, __n * sizeof(char_type)));
-
- // I don't think there's a standard 'memset' for 16-bit values.
- // Do this the old-fashioned way.
-
- size_t __i;
- for(__i = 0; __i < __n; __i++)
- {
- __s[__i] = __a;
- }
- return __s;
- }
-
- static char_type
- to_char_type(const int_type& __c)
- { return static_cast<char_type>(__c); }
-
- static int_type
- to_int_type(const char_type& __c)
- { return static_cast<int_type>(__c); }
-
- static bool
- eq_int_type(const int_type& __c1, const int_type& __c2)
- { return __c1 == __c2; }
-
- static int_type
- eof() { return static_cast<int_type>(EOF); }
-
- static int_type
- not_eof(const int_type& __c)
- { return (__c == eof()) ? 0 : __c; }
- };
-};
-#endif
-
-class LLStringOps
-{
-private:
- static long sltOffset;
- static long localTimeOffset;
- static bool daylightSavings;
- static std::map<std::string, std::string> datetimeToCodes;
-
-public:
- static char toUpper(char elem) { return toupper((unsigned char)elem); }
- static llwchar toUpper(llwchar elem) { return towupper(elem); }
-
- static char toLower(char elem) { return tolower((unsigned char)elem); }
- static llwchar toLower(llwchar elem) { return towlower(elem); }
-
- static bool isSpace(char elem) { return isspace((unsigned char)elem) != 0; }
- static bool isSpace(llwchar elem) { return iswspace(elem) != 0; }
-
- static bool isUpper(char elem) { return isupper((unsigned char)elem) != 0; }
- static bool isUpper(llwchar elem) { return iswupper(elem) != 0; }
-
- static bool isLower(char elem) { return islower((unsigned char)elem) != 0; }
- static bool isLower(llwchar elem) { return iswlower(elem) != 0; }
-
- static bool isDigit(char a) { return isdigit((unsigned char)a) != 0; }
- static bool isDigit(llwchar a) { return iswdigit(a) != 0; }
-
- static bool isPunct(char a) { return ispunct((unsigned char)a) != 0; }
- static bool isPunct(llwchar a) { return iswpunct(a) != 0; }
-
- static bool isAlnum(char a) { return isalnum((unsigned char)a) != 0; }
- static bool isAlnum(llwchar a) { return iswalnum(a) != 0; }
-
- static S32 collate(const char* a, const char* b) { return strcoll(a, b); }
- static S32 collate(const llwchar* a, const llwchar* b);
-
- static void setupDatetimeInfo (bool daylight);
- static long getSltOffset (void) {return sltOffset;}
- static long getLocalTimeOffset (void) {return localTimeOffset;}
- static bool getDaylightSavings (void) {return daylightSavings;}
- static std::string getDatetimeCode (std::string key);
-};
-
-/**
- * @brief Return a string constructed from in without crashing if the
- * pointer is NULL.
- */
-std::string ll_safe_string(const char* in);
-std::string ll_safe_string(const char* in, S32 maxlen);
-
-
-// Allowing assignments from non-strings into format_map_t is apparently
-// *really* error-prone, so subclass std::string with just basic c'tors.
-class LLFormatMapString
-{
-public:
- LLFormatMapString() {};
- LLFormatMapString(const char* s) : mString(ll_safe_string(s)) {};
- LLFormatMapString(const std::string& s) : mString(s) {};
- operator std::string() const { return mString; }
- bool operator<(const LLFormatMapString& rhs) const { return mString < rhs.mString; }
- std::size_t length() const { return mString.length(); }
-
-private:
- std::string mString;
-};
-
-template <class T>
-class LLStringUtilBase
-{
-private:
- static std::string sLocale;
-
-public:
- typedef typename std::basic_string<T>::size_type size_type;
-
-public:
- /////////////////////////////////////////////////////////////////////////////////////////
- // Static Utility functions that operate on std::strings
-
- static std::basic_string<T> null;
-
- typedef std::map<LLFormatMapString, LLFormatMapString> format_map_t;
- static void getTokens(const std::basic_string<T>& instr, std::vector<std::basic_string<T> >& tokens, const std::basic_string<T>& delims);
- static void formatNumber(std::basic_string<T>& numStr, std::basic_string<T> decimals);
- static bool formatDatetime(std::basic_string<T>& replacement, std::basic_string<T> token, std::basic_string<T> param, S32 secFromEpoch);
- static S32 format(std::basic_string<T>& s, const format_map_t& substitutions);
- static S32 format(std::basic_string<T>& s, const LLSD& substitutions);
- static bool simpleReplacement(std::basic_string<T>& replacement, std::basic_string<T> token, const format_map_t& substitutions);
- static bool simpleReplacement(std::basic_string<T>& replacement, std::basic_string<T> token, const LLSD& substitutions);
- static void setLocale (std::string inLocale) {sLocale = inLocale;};
- static std::string getLocale (void) {return sLocale;};
-
- static bool isValidIndex(const std::basic_string<T>& string, size_type i)
- {
- return !string.empty() && (0 <= i) && (i <= string.size());
- }
-
- static void trimHead(std::basic_string<T>& string);
- static void trimTail(std::basic_string<T>& string);
- static void trim(std::basic_string<T>& string) { trimHead(string); trimTail(string); }
- static void truncate(std::basic_string<T>& string, size_type count);
-
- static void toUpper(std::basic_string<T>& string);
- static void toLower(std::basic_string<T>& string);
-
- // True if this is the head of s.
- static BOOL isHead( const std::basic_string<T>& string, const T* s );
-
- /**
- * @brief Returns true if string starts with substr
- *
- * If etither string or substr are empty, this method returns false.
- */
- static bool startsWith(
- const std::basic_string<T>& string,
- const std::basic_string<T>& substr);
-
- /**
- * @brief Returns true if string ends in substr
- *
- * If etither string or substr are empty, this method returns false.
- */
- static bool endsWith(
- const std::basic_string<T>& string,
- const std::basic_string<T>& substr);
-
- static void addCRLF(std::basic_string<T>& string);
- static void removeCRLF(std::basic_string<T>& string);
-
- static void replaceTabsWithSpaces( std::basic_string<T>& string, size_type spaces_per_tab );
- static void replaceNonstandardASCII( std::basic_string<T>& string, T replacement );
- static void replaceChar( std::basic_string<T>& string, T target, T replacement );
- static void replaceString( std::basic_string<T>& string, std::basic_string<T> target, std::basic_string<T> replacement );
-
- static BOOL containsNonprintable(const std::basic_string<T>& string);
- static void stripNonprintable(std::basic_string<T>& string);
-
- /**
- * @brief Unsafe way to make ascii characters. You should probably
- * only call this when interacting with the host operating system.
- * The 1 byte std::string does not work correctly.
- * The 2 and 4 byte std::string probably work, so LLWStringUtil::_makeASCII
- * should work.
- */
- static void _makeASCII(std::basic_string<T>& string);
-
- // Conversion to other data types
- static BOOL convertToBOOL(const std::basic_string<T>& string, BOOL& value);
- static BOOL convertToU8(const std::basic_string<T>& string, U8& value);
- static BOOL convertToS8(const std::basic_string<T>& string, S8& value);
- static BOOL convertToS16(const std::basic_string<T>& string, S16& value);
- static BOOL convertToU16(const std::basic_string<T>& string, U16& value);
- static BOOL convertToU32(const std::basic_string<T>& string, U32& value);
- static BOOL convertToS32(const std::basic_string<T>& string, S32& value);
- static BOOL convertToF32(const std::basic_string<T>& string, F32& value);
- static BOOL convertToF64(const std::basic_string<T>& string, F64& value);
-
- /////////////////////////////////////////////////////////////////////////////////////////
- // Utility functions for working with char*'s and strings
-
- // Like strcmp but also handles empty strings. Uses
- // current locale.
- static S32 compareStrings(const T* lhs, const T* rhs);
- static S32 compareStrings(const std::basic_string<T>& lhs, const std::basic_string<T>& rhs);
-
- // case insensitive version of above. Uses current locale on
- // Win32, and falls back to a non-locale aware comparison on
- // Linux.
- static S32 compareInsensitive(const T* lhs, const T* rhs);
- static S32 compareInsensitive(const std::basic_string<T>& lhs, const std::basic_string<T>& rhs);
-
- // Case sensitive comparison with good handling of numbers. Does not use current locale.
- // a.k.a. strdictcmp()
- static S32 compareDict(const std::basic_string<T>& a, const std::basic_string<T>& b);
-
- // Case *in*sensitive comparison with good handling of numbers. Does not use current locale.
- // a.k.a. strdictcmp()
- static S32 compareDictInsensitive(const std::basic_string<T>& a, const std::basic_string<T>& b);
-
- // Puts compareDict() in a form appropriate for LL container classes to use for sorting.
- static BOOL precedesDict( const std::basic_string<T>& a, const std::basic_string<T>& b );
-
- // A replacement for strncpy.
- // If the dst buffer is dst_size bytes long or more, ensures that dst is null terminated and holds
- // up to dst_size-1 characters of src.
- static void copy(T* dst, const T* src, size_type dst_size);
-
- // Copies src into dst at a given offset.
- static void copyInto(std::basic_string<T>& dst, const std::basic_string<T>& src, size_type offset);
-
- static bool isPartOfWord(T c) { return (c == (T)'_') || LLStringOps::isAlnum(c); }
-
-
-#ifdef _DEBUG
- static void testHarness();
-#endif
-
-private:
- static size_type getSubstitution(const std::basic_string<T>& instr, size_type& start, std::vector<std::basic_string<T> >& tokens);
-};
-
-template<class T> std::basic_string<T> LLStringUtilBase<T>::null;
-template<class T> std::string LLStringUtilBase<T>::sLocale;
-
-typedef LLStringUtilBase<char> LLStringUtil;
-typedef LLStringUtilBase<llwchar> LLWStringUtil;
-typedef std::basic_string<llwchar> LLWString;
-
-//@ Use this where we want to disallow input in the form of "foo"
-// This is used to catch places where english text is embedded in the code
-// instead of in a translatable XUI file.
-class LLStringExplicit : public std::string
-{
-public:
- explicit LLStringExplicit(const char* s) : std::string(s) {}
- LLStringExplicit(const std::string& s) : std::string(s) {}
- LLStringExplicit(const std::string& s, size_type pos, size_type n = std::string::npos) : std::string(s, pos, n) {}
-};
-
-struct LLDictionaryLess
-{
-public:
- bool operator()(const std::string& a, const std::string& b)
- {
- return (LLStringUtil::precedesDict(a, b) ? true : false);
- }
-};
-
-
-/**
- * Simple support functions
- */
-
-/**
- * @brief chop off the trailing characters in a string.
- *
- * This function works on bytes rather than glyphs, so this will
- * incorrectly truncate non-single byte strings.
- * Use utf8str_truncate() for utf8 strings
- * @return a copy of in string minus the trailing count bytes.
- */
-inline std::string chop_tail_copy(
- const std::string& in,
- std::string::size_type count)
-{
- return std::string(in, 0, in.length() - count);
-}
-
-/**
- * @brief This translates a nybble stored as a hex value from 0-f back
- * to a nybble in the low order bits of the return byte.
- */
-U8 hex_as_nybble(char hex);
-
-/**
- * @brief read the contents of a file into a string.
- *
- * Since this function has no concept of character encoding, most
- * anything you do with this method ill-advised. Please avoid.
- * @param str [out] The string which will have.
- * @param filename The full name of the file to read.
- * @return Returns true on success. If false, str is unmodified.
- */
-bool _read_file_into_string(std::string& str, const std::string& filename);
-bool iswindividual(llwchar elem);
-
-/**
- * Unicode support
- */
-
-// Make the incoming string a utf8 string. Replaces any unknown glyph
-// with the UNKOWN_CHARACTER. Once any unknown glph is found, the rest
-// of the data may not be recovered.
-std::string rawstr_to_utf8(const std::string& raw);
-
-//
-// We should never use UTF16 except when communicating with Win32!
-//
-typedef std::basic_string<U16> llutf16string;
-
-LLWString utf16str_to_wstring(const llutf16string &utf16str, S32 len);
-LLWString utf16str_to_wstring(const llutf16string &utf16str);
-
-llutf16string wstring_to_utf16str(const LLWString &utf32str, S32 len);
-llutf16string wstring_to_utf16str(const LLWString &utf32str);
-
-llutf16string utf8str_to_utf16str ( const std::string& utf8str, S32 len);
-llutf16string utf8str_to_utf16str ( const std::string& utf8str );
-
-LLWString utf8str_to_wstring(const std::string &utf8str, S32 len);
-LLWString utf8str_to_wstring(const std::string &utf8str);
-// Same function, better name. JC
-inline LLWString utf8string_to_wstring(const std::string& utf8_string) { return utf8str_to_wstring(utf8_string); }
-
-//
-S32 wchar_to_utf8chars(llwchar inchar, char* outchars);
-
-std::string wstring_to_utf8str(const LLWString &utf32str, S32 len);
-std::string wstring_to_utf8str(const LLWString &utf32str);
-
-std::string utf16str_to_utf8str(const llutf16string &utf16str, S32 len);
-std::string utf16str_to_utf8str(const llutf16string &utf16str);
-
-// Length of this UTF32 string in bytes when transformed to UTF8
-S32 wstring_utf8_length(const LLWString& wstr);
-
-// Length in bytes of this wide char in a UTF8 string
-S32 wchar_utf8_length(const llwchar wc);
-
-std::string utf8str_tolower(const std::string& utf8str);
-
-// Length in llwchar (UTF-32) of the first len units (16 bits) of the given UTF-16 string.
-S32 utf16str_wstring_length(const llutf16string &utf16str, S32 len);
-
-// Length in utf16string (UTF-16) of wlen wchars beginning at woffset.
-S32 wstring_utf16_length(const LLWString & wstr, S32 woffset, S32 wlen);
-
-// Length in wstring (i.e., llwchar count) of a part of a wstring specified by utf16 length (i.e., utf16 units.)
-S32 wstring_wstring_length_from_utf16_length(const LLWString & wstr, S32 woffset, S32 utf16_length, BOOL *unaligned = NULL);
-
-/**
- * @brief Properly truncate a utf8 string to a maximum byte count.
- *
- * The returned string may be less than max_len if the truncation
- * happens in the middle of a glyph. If max_len is longer than the
- * string passed in, the return value == utf8str.
- * @param utf8str A valid utf8 string to truncate.
- * @param max_len The maximum number of bytes in the return value.
- * @return Returns a valid utf8 string with byte count <= max_len.
- */
-std::string utf8str_truncate(const std::string& utf8str, const S32 max_len);
-
-std::string utf8str_trim(const std::string& utf8str);
-
-S32 utf8str_compare_insensitive(
- const std::string& lhs,
- const std::string& rhs);
-
-/**
- * @brief Replace all occurences of target_char with replace_char
- *
- * @param utf8str A utf8 string to process.
- * @param target_char The wchar to be replaced
- * @param replace_char The wchar which is written on replace
- */
-std::string utf8str_substChar(
- const std::string& utf8str,
- const llwchar target_char,
- const llwchar replace_char);
-
-std::string utf8str_makeASCII(const std::string& utf8str);
-
-// Hack - used for evil notecards.
-std::string mbcsstring_makeASCII(const std::string& str);
-
-std::string utf8str_removeCRLF(const std::string& utf8str);
-
-
-#if LL_WINDOWS
-/* @name Windows string helpers
- */
-//@{
-
-/**
- * @brief Implementation the expected snprintf interface.
- *
- * If the size of the passed in buffer is not large enough to hold the string,
- * two bad things happen:
- * 1. resulting formatted string is NOT null terminated
- * 2. Depending on the platform, the return value could be a) the required
- * size of the buffer to copy the entire formatted string or b) -1.
- * On Windows with VS.Net 2003, it returns -1 e.g.
- *
- * safe_snprintf always adds a NULL terminator so that the caller does not
- * need to check for return value or need to add the NULL terminator.
- * It does not, however change the return value - to let the caller know
- * that the passed in buffer size was not large enough to hold the
- * formatted string.
- *
- */
-int safe_snprintf(char* str, size_t size, const char* format, ...);
-
-/**
- * @brief Convert a wide string to std::string
- *
- * This replaces the unsafe W2A macro from ATL.
- */
-std::string ll_convert_wide_to_string(const wchar_t* in);
-
-//@}
-#endif // LL_WINDOWS
-
-/**
- * Many of the 'strip' and 'replace' methods of LLStringUtilBase need
- * specialization to work with the signed char type.
- * Sadly, it is not possible (AFAIK) to specialize a single method of
- * a template class.
- * That stuff should go here.
- */
-namespace LLStringFn
-{
- /**
- * @brief Replace all non-printable characters with replacement in
- * string.
- * NOTE - this will zap non-ascii
- *
- * @param [in,out] string the to modify. out value is the string
- * with zero non-printable characters.
- * @param The replacement character. use LL_UNKNOWN_CHAR if unsure.
- */
- void replace_nonprintable_in_ascii(
- std::basic_string<char>& string,
- char replacement);
-
-
- /**
- * @brief Replace all non-printable characters and pipe characters
- * with replacement in a string.
- * NOTE - this will zap non-ascii
- *
- * @param [in,out] the string to modify. out value is the string
- * with zero non-printable characters and zero pipe characters.
- * @param The replacement character. use LL_UNKNOWN_CHAR if unsure.
- */
- void replace_nonprintable_and_pipe_in_ascii(std::basic_string<char>& str,
- char replacement);
-
-
- /**
- * @brief Remove all characters that are not allowed in XML 1.0.
- * Returns a copy of the string with those characters removed.
- * Works with US ASCII and UTF-8 encoded strings. JC
- */
- std::string strip_invalid_xml(const std::string& input);
-
-
- /**
- * @brief Replace all control characters (0 <= c < 0x20) with replacement in
- * string. This is safe for utf-8
- *
- * @param [in,out] string the to modify. out value is the string
- * with zero non-printable characters.
- * @param The replacement character. use LL_UNKNOWN_CHAR if unsure.
- */
- void replace_ascii_controlchars(
- std::basic_string<char>& string,
- char replacement);
-}
-
-////////////////////////////////////////////////////////////
-// NOTE: LLStringUtil::format, getTokens, and support functions moved to llstring.cpp.
-// There is no LLWStringUtil::format implementation currently.
-// Calling thse for anything other than LLStringUtil will produce link errors.
-
-////////////////////////////////////////////////////////////
-
-
-// static
-template<class T>
-S32 LLStringUtilBase<T>::compareStrings(const T* lhs, const T* rhs)
-{
- S32 result;
- if( lhs == rhs )
- {
- result = 0;
- }
- else
- if ( !lhs || !lhs[0] )
- {
- result = ((!rhs || !rhs[0]) ? 0 : 1);
- }
- else
- if ( !rhs || !rhs[0])
- {
- result = -1;
- }
- else
- {
- result = LLStringOps::collate(lhs, rhs);
- }
- return result;
-}
-
-//static
-template<class T>
-S32 LLStringUtilBase<T>::compareStrings(const std::basic_string<T>& lhs, const std::basic_string<T>& rhs)
-{
- return LLStringOps::collate(lhs.c_str(), rhs.c_str());
-}
-
-// static
-template<class T>
-S32 LLStringUtilBase<T>::compareInsensitive(const T* lhs, const T* rhs )
-{
- S32 result;
- if( lhs == rhs )
- {
- result = 0;
- }
- else
- if ( !lhs || !lhs[0] )
- {
- result = ((!rhs || !rhs[0]) ? 0 : 1);
- }
- else
- if ( !rhs || !rhs[0] )
- {
- result = -1;
- }
- else
- {
- std::basic_string<T> lhs_string(lhs);
- std::basic_string<T> rhs_string(rhs);
- LLStringUtilBase<T>::toUpper(lhs_string);
- LLStringUtilBase<T>::toUpper(rhs_string);
- result = LLStringOps::collate(lhs_string.c_str(), rhs_string.c_str());
- }
- return result;
-}
-
-//static
-template<class T>
-S32 LLStringUtilBase<T>::compareInsensitive(const std::basic_string<T>& lhs, const std::basic_string<T>& rhs)
-{
- std::basic_string<T> lhs_string(lhs);
- std::basic_string<T> rhs_string(rhs);
- LLStringUtilBase<T>::toUpper(lhs_string);
- LLStringUtilBase<T>::toUpper(rhs_string);
- return LLStringOps::collate(lhs_string.c_str(), rhs_string.c_str());
-}
-
-// Case sensitive comparison with good handling of numbers. Does not use current locale.
-// a.k.a. strdictcmp()
-
-//static
-template<class T>
-S32 LLStringUtilBase<T>::compareDict(const std::basic_string<T>& astr, const std::basic_string<T>& bstr)
-{
- const T* a = astr.c_str();
- const T* b = bstr.c_str();
- T ca, cb;
- S32 ai, bi, cnt = 0;
- S32 bias = 0;
-
- ca = *(a++);
- cb = *(b++);
- while( ca && cb ){
- if( bias==0 ){
- if( LLStringOps::isUpper(ca) ){ ca = LLStringOps::toLower(ca); bias--; }
- if( LLStringOps::isUpper(cb) ){ cb = LLStringOps::toLower(cb); bias++; }
- }else{
- if( LLStringOps::isUpper(ca) ){ ca = LLStringOps::toLower(ca); }
- if( LLStringOps::isUpper(cb) ){ cb = LLStringOps::toLower(cb); }
- }
- if( LLStringOps::isDigit(ca) ){
- if( cnt-->0 ){
- if( cb!=ca ) break;
- }else{
- if( !LLStringOps::isDigit(cb) ) break;
- for(ai=0; LLStringOps::isDigit(a[ai]); ai++);
- for(bi=0; LLStringOps::isDigit(b[bi]); bi++);
- if( ai<bi ){ ca=0; break; }
- if( bi<ai ){ cb=0; break; }
- if( ca!=cb ) break;
- cnt = ai;
- }
- }else if( ca!=cb ){ break;
- }
- ca = *(a++);
- cb = *(b++);
- }
- if( ca==cb ) ca += bias;
- return ca-cb;
-}
-
-// static
-template<class T>
-S32 LLStringUtilBase<T>::compareDictInsensitive(const std::basic_string<T>& astr, const std::basic_string<T>& bstr)
-{
- const T* a = astr.c_str();
- const T* b = bstr.c_str();
- T ca, cb;
- S32 ai, bi, cnt = 0;
-
- ca = *(a++);
- cb = *(b++);
- while( ca && cb ){
- if( LLStringOps::isUpper(ca) ){ ca = LLStringOps::toLower(ca); }
- if( LLStringOps::isUpper(cb) ){ cb = LLStringOps::toLower(cb); }
- if( LLStringOps::isDigit(ca) ){
- if( cnt-->0 ){
- if( cb!=ca ) break;
- }else{
- if( !LLStringOps::isDigit(cb) ) break;
- for(ai=0; LLStringOps::isDigit(a[ai]); ai++);
- for(bi=0; LLStringOps::isDigit(b[bi]); bi++);
- if( ai<bi ){ ca=0; break; }
- if( bi<ai ){ cb=0; break; }
- if( ca!=cb ) break;
- cnt = ai;
- }
- }else if( ca!=cb ){ break;
- }
- ca = *(a++);
- cb = *(b++);
- }
- return ca-cb;
-}
-
-// Puts compareDict() in a form appropriate for LL container classes to use for sorting.
-// static
-template<class T>
-BOOL LLStringUtilBase<T>::precedesDict( const std::basic_string<T>& a, const std::basic_string<T>& b )
-{
- if( a.size() && b.size() )
- {
- return (LLStringUtilBase<T>::compareDict(a.c_str(), b.c_str()) < 0);
- }
- else
- {
- return (!b.empty());
- }
-}
-
-//static
-template<class T>
-void LLStringUtilBase<T>::toUpper(std::basic_string<T>& string)
-{
- if( !string.empty() )
- {
- std::transform(
- string.begin(),
- string.end(),
- string.begin(),
- (T(*)(T)) &LLStringOps::toUpper);
- }
-}
-
-//static
-template<class T>
-void LLStringUtilBase<T>::toLower(std::basic_string<T>& string)
-{
- if( !string.empty() )
- {
- std::transform(
- string.begin(),
- string.end(),
- string.begin(),
- (T(*)(T)) &LLStringOps::toLower);
- }
-}
-
-//static
-template<class T>
-void LLStringUtilBase<T>::trimHead(std::basic_string<T>& string)
-{
- if( !string.empty() )
- {
- size_type i = 0;
- while( i < string.length() && LLStringOps::isSpace( string[i] ) )
- {
- i++;
- }
- string.erase(0, i);
- }
-}
-
-//static
-template<class T>
-void LLStringUtilBase<T>::trimTail(std::basic_string<T>& string)
-{
- if( string.size() )
- {
- size_type len = string.length();
- size_type i = len;
- while( i > 0 && LLStringOps::isSpace( string[i-1] ) )
- {
- i--;
- }
-
- string.erase( i, len - i );
- }
-}
-
-
-// Replace line feeds with carriage return-line feed pairs.
-//static
-template<class T>
-void LLStringUtilBase<T>::addCRLF(std::basic_string<T>& string)
-{
- const T LF = 10;
- const T CR = 13;
-
- // Count the number of line feeds
- size_type count = 0;
- size_type len = string.size();
- size_type i;
- for( i = 0; i < len; i++ )
- {
- if( string[i] == LF )
- {
- count++;
- }
- }
-
- // Insert a carriage return before each line feed
- if( count )
- {
- size_type size = len + count;
- T *t = new T[size];
- size_type j = 0;
- for( i = 0; i < len; ++i )
- {
- if( string[i] == LF )
- {
- t[j] = CR;
- ++j;
- }
- t[j] = string[i];
- ++j;
- }
-
- string.assign(t, size);
- }
-}
-
-// Remove all carriage returns
-//static
-template<class T>
-void LLStringUtilBase<T>::removeCRLF(std::basic_string<T>& string)
-{
- const T CR = 13;
-
- size_type cr_count = 0;
- size_type len = string.size();
- size_type i;
- for( i = 0; i < len - cr_count; i++ )
- {
- if( string[i+cr_count] == CR )
- {
- cr_count++;
- }
-
- string[i] = string[i+cr_count];
- }
- string.erase(i, cr_count);
-}
-
-//static
-template<class T>
-void LLStringUtilBase<T>::replaceChar( std::basic_string<T>& string, T target, T replacement )
-{
- size_type found_pos = 0;
- while( (found_pos = string.find(target, found_pos)) != std::basic_string<T>::npos )
- {
- string[found_pos] = replacement;
- found_pos++; // avoid infinite defeat if target == replacement
- }
-}
-
-//static
-template<class T>
-void LLStringUtilBase<T>::replaceString( std::basic_string<T>& string, std::basic_string<T> target, std::basic_string<T> replacement )
-{
- size_type found_pos = 0;
- while( (found_pos = string.find(target, found_pos)) != std::basic_string<T>::npos )
- {
- string.replace( found_pos, target.length(), replacement );
- found_pos += replacement.length(); // avoid infinite defeat if replacement contains target
- }
-}
-
-//static
-template<class T>
-void LLStringUtilBase<T>::replaceNonstandardASCII( std::basic_string<T>& string, T replacement )
-{
- const char LF = 10;
- const S8 MIN = 32;
-// const S8 MAX = 127;
-
- size_type len = string.size();
- for( size_type i = 0; i < len; i++ )
- {
- // No need to test MAX < mText[i] because we treat mText[i] as a signed char,
- // which has a max value of 127.
- if( ( S8(string[i]) < MIN ) && (string[i] != LF) )
- {
- string[i] = replacement;
- }
- }
-}
-
-//static
-template<class T>
-void LLStringUtilBase<T>::replaceTabsWithSpaces( std::basic_string<T>& str, size_type spaces_per_tab )
-{
- const T TAB = '\t';
- const T SPACE = ' ';
-
- std::basic_string<T> out_str;
- // Replace tabs with spaces
- for (size_type i = 0; i < str.length(); i++)
- {
- if (str[i] == TAB)
- {
- for (size_type j = 0; j < spaces_per_tab; j++)
- out_str += SPACE;
- }
- else
- {
- out_str += str[i];
- }
- }
- str = out_str;
-}
-
-//static
-template<class T>
-BOOL LLStringUtilBase<T>::containsNonprintable(const std::basic_string<T>& string)
-{
- const char MIN = 32;
- BOOL rv = FALSE;
- for (size_type i = 0; i < string.size(); i++)
- {
- if(string[i] < MIN)
- {
- rv = TRUE;
- break;
- }
- }
- return rv;
-}
-
-//static
-template<class T>
-void LLStringUtilBase<T>::stripNonprintable(std::basic_string<T>& string)
-{
- const char MIN = 32;
- size_type j = 0;
- if (string.empty())
- {
- return;
- }
- size_t src_size = string.size();
- char* c_string = new char[src_size + 1];
- if(c_string == NULL)
- {
- return;
- }
- copy(c_string, string.c_str(), src_size+1);
- char* write_head = &c_string[0];
- for (size_type i = 0; i < src_size; i++)
- {
- char* read_head = &string[i];
- write_head = &c_string[j];
- if(!(*read_head < MIN))
- {
- *write_head = *read_head;
- ++j;
- }
- }
- c_string[j]= '\0';
- string = c_string;
- delete []c_string;
-}
-
-template<class T>
-void LLStringUtilBase<T>::_makeASCII(std::basic_string<T>& string)
-{
- // Replace non-ASCII chars with LL_UNKNOWN_CHAR
- for (size_type i = 0; i < string.length(); i++)
- {
- if (string[i] > 0x7f)
- {
- string[i] = LL_UNKNOWN_CHAR;
- }
- }
-}
-
-// static
-template<class T>
-void LLStringUtilBase<T>::copy( T* dst, const T* src, size_type dst_size )
-{
- if( dst_size > 0 )
- {
- size_type min_len = 0;
- if( src )
- {
- min_len = llmin( dst_size - 1, strlen( src ) ); /* Flawfinder: ignore */
- memcpy(dst, src, min_len * sizeof(T)); /* Flawfinder: ignore */
- }
- dst[min_len] = '\0';
- }
-}
-
-// static
-template<class T>
-void LLStringUtilBase<T>::copyInto(std::basic_string<T>& dst, const std::basic_string<T>& src, size_type offset)
-{
- if ( offset == dst.length() )
- {
- // special case - append to end of string and avoid expensive
- // (when strings are large) string manipulations
- dst += src;
- }
- else
- {
- std::basic_string<T> tail = dst.substr(offset);
-
- dst = dst.substr(0, offset);
- dst += src;
- dst += tail;
- };
-}
-
-// True if this is the head of s.
-//static
-template<class T>
-BOOL LLStringUtilBase<T>::isHead( const std::basic_string<T>& string, const T* s )
-{
- if( string.empty() )
- {
- // Early exit
- return FALSE;
- }
- else
- {
- return (strncmp( s, string.c_str(), string.size() ) == 0);
- }
-}
-
-// static
-template<class T>
-bool LLStringUtilBase<T>::startsWith(
- const std::basic_string<T>& string,
- const std::basic_string<T>& substr)
-{
- if(string.empty() || (substr.empty())) return false;
- if(0 == string.find(substr)) return true;
- return false;
-}
-
-// static
-template<class T>
-bool LLStringUtilBase<T>::endsWith(
- const std::basic_string<T>& string,
- const std::basic_string<T>& substr)
-{
- if(string.empty() || (substr.empty())) return false;
- std::string::size_type idx = string.rfind(substr);
- if(std::string::npos == idx) return false;
- return (idx == (string.size() - substr.size()));
-}
-
-
-template<class T>
-BOOL LLStringUtilBase<T>::convertToBOOL(const std::basic_string<T>& string, BOOL& value)
-{
- if( string.empty() )
- {
- return FALSE;
- }
-
- std::basic_string<T> temp( string );
- trim(temp);
- if(
- (temp == "1") ||
- (temp == "T") ||
- (temp == "t") ||
- (temp == "TRUE") ||
- (temp == "true") ||
- (temp == "True") )
- {
- value = TRUE;
- return TRUE;
- }
- else
- if(
- (temp == "0") ||
- (temp == "F") ||
- (temp == "f") ||
- (temp == "FALSE") ||
- (temp == "false") ||
- (temp == "False") )
- {
- value = FALSE;
- return TRUE;
- }
-
- return FALSE;
-}
-
-template<class T>
-BOOL LLStringUtilBase<T>::convertToU8(const std::basic_string<T>& string, U8& value)
-{
- S32 value32 = 0;
- BOOL success = convertToS32(string, value32);
- if( success && (U8_MIN <= value32) && (value32 <= U8_MAX) )
- {
- value = (U8) value32;
- return TRUE;
- }
- return FALSE;
-}
-
-template<class T>
-BOOL LLStringUtilBase<T>::convertToS8(const std::basic_string<T>& string, S8& value)
-{
- S32 value32 = 0;
- BOOL success = convertToS32(string, value32);
- if( success && (S8_MIN <= value32) && (value32 <= S8_MAX) )
- {
- value = (S8) value32;
- return TRUE;
- }
- return FALSE;
-}
-
-template<class T>
-BOOL LLStringUtilBase<T>::convertToS16(const std::basic_string<T>& string, S16& value)
-{
- S32 value32 = 0;
- BOOL success = convertToS32(string, value32);
- if( success && (S16_MIN <= value32) && (value32 <= S16_MAX) )
- {
- value = (S16) value32;
- return TRUE;
- }
- return FALSE;
-}
-
-template<class T>
-BOOL LLStringUtilBase<T>::convertToU16(const std::basic_string<T>& string, U16& value)
-{
- S32 value32 = 0;
- BOOL success = convertToS32(string, value32);
- if( success && (U16_MIN <= value32) && (value32 <= U16_MAX) )
- {
- value = (U16) value32;
- return TRUE;
- }
- return FALSE;
-}
-
-template<class T>
-BOOL LLStringUtilBase<T>::convertToU32(const std::basic_string<T>& string, U32& value)
-{
- if( string.empty() )
- {
- return FALSE;
- }
-
- std::basic_string<T> temp( string );
- trim(temp);
- U32 v;
- std::basic_istringstream<T> i_stream((std::basic_string<T>)temp);
- if(i_stream >> v)
- {
- value = v;
- return TRUE;
- }
- return FALSE;
-}
-
-template<class T>
-BOOL LLStringUtilBase<T>::convertToS32(const std::basic_string<T>& string, S32& value)
-{
- if( string.empty() )
- {
- return FALSE;
- }
-
- std::basic_string<T> temp( string );
- trim(temp);
- S32 v;
- std::basic_istringstream<T> i_stream((std::basic_string<T>)temp);
- if(i_stream >> v)
- {
- //TODO: figure out overflow and underflow reporting here
- //if((LONG_MAX == v) || (LONG_MIN == v))
- //{
- // // Underflow or overflow
- // return FALSE;
- //}
-
- value = v;
- return TRUE;
- }
- return FALSE;
-}
-
-template<class T>
-BOOL LLStringUtilBase<T>::convertToF32(const std::basic_string<T>& string, F32& value)
-{
- F64 value64 = 0.0;
- BOOL success = convertToF64(string, value64);
- if( success && (-F32_MAX <= value64) && (value64 <= F32_MAX) )
- {
- value = (F32) value64;
- return TRUE;
- }
- return FALSE;
-}
-
-template<class T>
-BOOL LLStringUtilBase<T>::convertToF64(const std::basic_string<T>& string, F64& value)
-{
- if( string.empty() )
- {
- return FALSE;
- }
-
- std::basic_string<T> temp( string );
- trim(temp);
- F64 v;
- std::basic_istringstream<T> i_stream((std::basic_string<T>)temp);
- if(i_stream >> v)
- {
- //TODO: figure out overflow and underflow reporting here
- //if( ((-HUGE_VAL == v) || (HUGE_VAL == v))) )
- //{
- // // Underflow or overflow
- // return FALSE;
- //}
-
- value = v;
- return TRUE;
- }
- return FALSE;
-}
-
-template<class T>
-void LLStringUtilBase<T>::truncate(std::basic_string<T>& string, size_type count)
-{
- size_type cur_size = string.size();
- string.resize(count < cur_size ? count : cur_size);
-}
-
-#endif // LL_STRING_H
+/**
+ * @file llstring.h
+ * @brief String utility functions and std::string class.
+ *
+ * $LicenseInfo:firstyear=2001&license=viewergpl$
+ *
+ * Copyright (c) 2001-2009, Linden Research, Inc.
+ *
+ * Second Life Viewer Source Code
+ * The source code in this file ("Source Code") is provided by Linden Lab
+ * to you under the terms of the GNU General Public License, version 2.0
+ * ("GPL"), unless you have obtained a separate licensing agreement
+ * ("Other License"), formally executed by you and Linden Lab. Terms of
+ * the GPL can be found in doc/GPL-license.txt in this distribution, or
+ * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
+ *
+ * There are special exceptions to the terms and conditions of the GPL as
+ * it is applied to this Source Code. View the full text of the exception
+ * in the file doc/FLOSS-exception.txt in this software distribution, or
+ * online at
+ * http://secondlifegrid.net/programs/open_source/licensing/flossexception
+ *
+ * By copying, modifying or distributing this software, you acknowledge
+ * that you have read and understood your obligations described above,
+ * and agree to abide by those obligations.
+ *
+ * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
+ * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
+ * COMPLETENESS OR PERFORMANCE.
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLSTRING_H
+#define LL_LLSTRING_H
+
+#include <string>
+#include <cstdio>
+#include <locale>
+#include <iomanip>
+#include "llsd.h"
+#include "llfasttimer.h"
+
+#if LL_LINUX || LL_SOLARIS
+#include <wctype.h>
+#include <wchar.h>
+#endif
+
+#include <string.h>
+
+#if LL_SOLARIS
+// stricmp and strnicmp do not exist on Solaris:
+#define stricmp strcasecmp
+#define strnicmp strncasecmp
+#endif
+
+const char LL_UNKNOWN_CHAR = '?';
+
+#if LL_DARWIN || LL_LINUX || LL_SOLARIS
+// Template specialization of char_traits for U16s. Only necessary on Mac and Linux (exists on Windows already)
+#include <cstring>
+
+namespace std
+{
+template<>
+struct char_traits<U16>
+{
+ typedef U16 char_type;
+ typedef int int_type;
+ typedef streampos pos_type;
+ typedef streamoff off_type;
+ typedef mbstate_t state_type;
+
+ static void
+ assign(char_type& __c1, const char_type& __c2)
+ { __c1 = __c2; }
+
+ static bool
+ eq(const char_type& __c1, const char_type& __c2)
+ { return __c1 == __c2; }
+
+ static bool
+ lt(const char_type& __c1, const char_type& __c2)
+ { return __c1 < __c2; }
+
+ static int
+ compare(const char_type* __s1, const char_type* __s2, size_t __n)
+ { return memcmp(__s1, __s2, __n * sizeof(char_type)); }
+
+ static size_t
+ length(const char_type* __s)
+ {
+ const char_type *cur_char = __s;
+ while (*cur_char != 0)
+ {
+ ++cur_char;
+ }
+ return cur_char - __s;
+ }
+
+ static const char_type*
+ find(const char_type* __s, size_t __n, const char_type& __a)
+ { return static_cast<const char_type*>(memchr(__s, __a, __n * sizeof(char_type))); }
+
+ static char_type*
+ move(char_type* __s1, const char_type* __s2, size_t __n)
+ { return static_cast<char_type*>(memmove(__s1, __s2, __n * sizeof(char_type))); }
+
+ static char_type*
+ copy(char_type* __s1, const char_type* __s2, size_t __n)
+ { return static_cast<char_type*>(memcpy(__s1, __s2, __n * sizeof(char_type))); } /* Flawfinder: ignore */
+
+ static char_type*
+ assign(char_type* __s, size_t __n, char_type __a)
+ {
+ // This isn't right.
+ //return static_cast<char_type*>(memset(__s, __a, __n * sizeof(char_type)));
+
+ // I don't think there's a standard 'memset' for 16-bit values.
+ // Do this the old-fashioned way.
+
+ size_t __i;
+ for(__i = 0; __i < __n; __i++)
+ {
+ __s[__i] = __a;
+ }
+ return __s;
+ }
+
+ static char_type
+ to_char_type(const int_type& __c)
+ { return static_cast<char_type>(__c); }
+
+ static int_type
+ to_int_type(const char_type& __c)
+ { return static_cast<int_type>(__c); }
+
+ static bool
+ eq_int_type(const int_type& __c1, const int_type& __c2)
+ { return __c1 == __c2; }
+
+ static int_type
+ eof() { return static_cast<int_type>(EOF); }
+
+ static int_type
+ not_eof(const int_type& __c)
+ { return (__c == eof()) ? 0 : __c; }
+ };
+};
+#endif
+
+class LL_COMMON_API LLStringOps
+{
+private:
+ static long sPacificTimeOffset;
+ static long sLocalTimeOffset;
+ static bool sPacificDaylightTime;
+ static std::map<std::string, std::string> datetimeToCodes;
+
+public:
+ static char toUpper(char elem) { return toupper((unsigned char)elem); }
+ static llwchar toUpper(llwchar elem) { return towupper(elem); }
+
+ static char toLower(char elem) { return tolower((unsigned char)elem); }
+ static llwchar toLower(llwchar elem) { return towlower(elem); }
+
+ static bool isSpace(char elem) { return isspace((unsigned char)elem) != 0; }
+ static bool isSpace(llwchar elem) { return iswspace(elem) != 0; }
+
+ static bool isUpper(char elem) { return isupper((unsigned char)elem) != 0; }
+ static bool isUpper(llwchar elem) { return iswupper(elem) != 0; }
+
+ static bool isLower(char elem) { return islower((unsigned char)elem) != 0; }
+ static bool isLower(llwchar elem) { return iswlower(elem) != 0; }
+
+ static bool isDigit(char a) { return isdigit((unsigned char)a) != 0; }
+ static bool isDigit(llwchar a) { return iswdigit(a) != 0; }
+
+ static bool isPunct(char a) { return ispunct((unsigned char)a) != 0; }
+ static bool isPunct(llwchar a) { return iswpunct(a) != 0; }
+
+ static bool isAlnum(char a) { return isalnum((unsigned char)a) != 0; }
+ static bool isAlnum(llwchar a) { return iswalnum(a) != 0; }
+
+ static S32 collate(const char* a, const char* b) { return strcoll(a, b); }
+ static S32 collate(const llwchar* a, const llwchar* b);
+
+ static void setupDatetimeInfo(bool pacific_daylight_time);
+ static long getPacificTimeOffset(void) { return sPacificTimeOffset;}
+ static long getLocalTimeOffset(void) { return sLocalTimeOffset;}
+ // Is the Pacific time zone (aka server time zone)
+ // currently in daylight savings time?
+ static bool getPacificDaylightTime(void) { return sPacificDaylightTime;}
+
+ static std::string getDatetimeCode (std::string key);
+};
+
+/**
+ * @brief Return a string constructed from in without crashing if the
+ * pointer is NULL.
+ */
+LL_COMMON_API std::string ll_safe_string(const char* in);
+LL_COMMON_API std::string ll_safe_string(const char* in, S32 maxlen);
+
+
+// Allowing assignments from non-strings into format_map_t is apparently
+// *really* error-prone, so subclass std::string with just basic c'tors.
+class LLFormatMapString
+{
+public:
+ LLFormatMapString() {};
+ LLFormatMapString(const char* s) : mString(ll_safe_string(s)) {};
+ LLFormatMapString(const std::string& s) : mString(s) {};
+ operator std::string() const { return mString; }
+ bool operator<(const LLFormatMapString& rhs) const { return mString < rhs.mString; }
+ std::size_t length() const { return mString.length(); }
+
+private:
+ std::string mString;
+};
+
+template <class T>
+class LLStringUtilBase
+{
+private:
+ static std::string sLocale;
+
+public:
+ typedef typename std::basic_string<T>::size_type size_type;
+
+public:
+ /////////////////////////////////////////////////////////////////////////////////////////
+ // Static Utility functions that operate on std::strings
+
+ static std::basic_string<T> null;
+
+ typedef std::map<LLFormatMapString, LLFormatMapString> format_map_t;
+ LL_COMMON_API static void getTokens(const std::basic_string<T>& instr, std::vector<std::basic_string<T> >& tokens, const std::basic_string<T>& delims);
+ LL_COMMON_API static void formatNumber(std::basic_string<T>& numStr, std::basic_string<T> decimals);
+ LL_COMMON_API static bool formatDatetime(std::basic_string<T>& replacement, std::basic_string<T> token, std::basic_string<T> param, S32 secFromEpoch);
+ LL_COMMON_API static S32 format(std::basic_string<T>& s, const format_map_t& substitutions);
+ LL_COMMON_API static S32 format(std::basic_string<T>& s, const LLSD& substitutions);
+ LL_COMMON_API static bool simpleReplacement(std::basic_string<T>& replacement, std::basic_string<T> token, const format_map_t& substitutions);
+ LL_COMMON_API static bool simpleReplacement(std::basic_string<T>& replacement, std::basic_string<T> token, const LLSD& substitutions);
+ static void setLocale (std::string inLocale) {sLocale = inLocale;};
+ static std::string getLocale (void) {return sLocale;};
+
+ static bool isValidIndex(const std::basic_string<T>& string, size_type i)
+ {
+ return !string.empty() && (0 <= i) && (i <= string.size());
+ }
+
+ static void trimHead(std::basic_string<T>& string);
+ static void trimTail(std::basic_string<T>& string);
+ static void trim(std::basic_string<T>& string) { trimHead(string); trimTail(string); }
+ static void truncate(std::basic_string<T>& string, size_type count);
+
+ static void toUpper(std::basic_string<T>& string);
+ static void toLower(std::basic_string<T>& string);
+
+ // True if this is the head of s.
+ static BOOL isHead( const std::basic_string<T>& string, const T* s );
+
+ /**
+ * @brief Returns true if string starts with substr
+ *
+ * If etither string or substr are empty, this method returns false.
+ */
+ static bool startsWith(
+ const std::basic_string<T>& string,
+ const std::basic_string<T>& substr);
+
+ /**
+ * @brief Returns true if string ends in substr
+ *
+ * If etither string or substr are empty, this method returns false.
+ */
+ static bool endsWith(
+ const std::basic_string<T>& string,
+ const std::basic_string<T>& substr);
+
+ static void addCRLF(std::basic_string<T>& string);
+ static void removeCRLF(std::basic_string<T>& string);
+
+ static void replaceTabsWithSpaces( std::basic_string<T>& string, size_type spaces_per_tab );
+ static void replaceNonstandardASCII( std::basic_string<T>& string, T replacement );
+ static void replaceChar( std::basic_string<T>& string, T target, T replacement );
+ static void replaceString( std::basic_string<T>& string, std::basic_string<T> target, std::basic_string<T> replacement );
+
+ static BOOL containsNonprintable(const std::basic_string<T>& string);
+ static void stripNonprintable(std::basic_string<T>& string);
+
+ /**
+ * @brief Unsafe way to make ascii characters. You should probably
+ * only call this when interacting with the host operating system.
+ * The 1 byte std::string does not work correctly.
+ * The 2 and 4 byte std::string probably work, so LLWStringUtil::_makeASCII
+ * should work.
+ */
+ static void _makeASCII(std::basic_string<T>& string);
+
+ // Conversion to other data types
+ static BOOL convertToBOOL(const std::basic_string<T>& string, BOOL& value);
+ static BOOL convertToU8(const std::basic_string<T>& string, U8& value);
+ static BOOL convertToS8(const std::basic_string<T>& string, S8& value);
+ static BOOL convertToS16(const std::basic_string<T>& string, S16& value);
+ static BOOL convertToU16(const std::basic_string<T>& string, U16& value);
+ static BOOL convertToU32(const std::basic_string<T>& string, U32& value);
+ static BOOL convertToS32(const std::basic_string<T>& string, S32& value);
+ static BOOL convertToF32(const std::basic_string<T>& string, F32& value);
+ static BOOL convertToF64(const std::basic_string<T>& string, F64& value);
+
+ /////////////////////////////////////////////////////////////////////////////////////////
+ // Utility functions for working with char*'s and strings
+
+ // Like strcmp but also handles empty strings. Uses
+ // current locale.
+ static S32 compareStrings(const T* lhs, const T* rhs);
+ static S32 compareStrings(const std::basic_string<T>& lhs, const std::basic_string<T>& rhs);
+
+ // case insensitive version of above. Uses current locale on
+ // Win32, and falls back to a non-locale aware comparison on
+ // Linux.
+ static S32 compareInsensitive(const T* lhs, const T* rhs);
+ static S32 compareInsensitive(const std::basic_string<T>& lhs, const std::basic_string<T>& rhs);
+
+ // Case sensitive comparison with good handling of numbers. Does not use current locale.
+ // a.k.a. strdictcmp()
+ static S32 compareDict(const std::basic_string<T>& a, const std::basic_string<T>& b);
+
+ // Case *in*sensitive comparison with good handling of numbers. Does not use current locale.
+ // a.k.a. strdictcmp()
+ static S32 compareDictInsensitive(const std::basic_string<T>& a, const std::basic_string<T>& b);
+
+ // Puts compareDict() in a form appropriate for LL container classes to use for sorting.
+ static BOOL precedesDict( const std::basic_string<T>& a, const std::basic_string<T>& b );
+
+ // A replacement for strncpy.
+ // If the dst buffer is dst_size bytes long or more, ensures that dst is null terminated and holds
+ // up to dst_size-1 characters of src.
+ static void copy(T* dst, const T* src, size_type dst_size);
+
+ // Copies src into dst at a given offset.
+ static void copyInto(std::basic_string<T>& dst, const std::basic_string<T>& src, size_type offset);
+
+ static bool isPartOfWord(T c) { return (c == (T)'_') || LLStringOps::isAlnum(c); }
+
+
+#ifdef _DEBUG
+ LL_COMMON_API static void testHarness();
+#endif
+
+private:
+ LL_COMMON_API static size_type getSubstitution(const std::basic_string<T>& instr, size_type& start, std::vector<std::basic_string<T> >& tokens);
+};
+
+template<class T> std::basic_string<T> LLStringUtilBase<T>::null;
+template<class T> std::string LLStringUtilBase<T>::sLocale;
+
+typedef LLStringUtilBase<char> LLStringUtil;
+typedef LLStringUtilBase<llwchar> LLWStringUtil;
+typedef std::basic_string<llwchar> LLWString;
+
+//@ Use this where we want to disallow input in the form of "foo"
+// This is used to catch places where english text is embedded in the code
+// instead of in a translatable XUI file.
+class LLStringExplicit : public std::string
+{
+public:
+ explicit LLStringExplicit(const char* s) : std::string(s) {}
+ LLStringExplicit(const std::string& s) : std::string(s) {}
+ LLStringExplicit(const std::string& s, size_type pos, size_type n = std::string::npos) : std::string(s, pos, n) {}
+};
+
+struct LLDictionaryLess
+{
+public:
+ bool operator()(const std::string& a, const std::string& b)
+ {
+ return (LLStringUtil::precedesDict(a, b) ? true : false);
+ }
+};
+
+
+/**
+ * Simple support functions
+ */
+
+/**
+ * @brief chop off the trailing characters in a string.
+ *
+ * This function works on bytes rather than glyphs, so this will
+ * incorrectly truncate non-single byte strings.
+ * Use utf8str_truncate() for utf8 strings
+ * @return a copy of in string minus the trailing count bytes.
+ */
+inline std::string chop_tail_copy(
+ const std::string& in,
+ std::string::size_type count)
+{
+ return std::string(in, 0, in.length() - count);
+}
+
+/**
+ * @brief This translates a nybble stored as a hex value from 0-f back
+ * to a nybble in the low order bits of the return byte.
+ */
+LL_COMMON_API U8 hex_as_nybble(char hex);
+
+/**
+ * @brief read the contents of a file into a string.
+ *
+ * Since this function has no concept of character encoding, most
+ * anything you do with this method ill-advised. Please avoid.
+ * @param str [out] The string which will have.
+ * @param filename The full name of the file to read.
+ * @return Returns true on success. If false, str is unmodified.
+ */
+LL_COMMON_API bool _read_file_into_string(std::string& str, const std::string& filename);
+LL_COMMON_API bool iswindividual(llwchar elem);
+
+/**
+ * Unicode support
+ */
+
+// Make the incoming string a utf8 string. Replaces any unknown glyph
+// with the UNKOWN_CHARACTER. Once any unknown glph is found, the rest
+// of the data may not be recovered.
+LL_COMMON_API std::string rawstr_to_utf8(const std::string& raw);
+
+//
+// We should never use UTF16 except when communicating with Win32!
+//
+typedef std::basic_string<U16> llutf16string;
+
+LL_COMMON_API LLWString utf16str_to_wstring(const llutf16string &utf16str, S32 len);
+LL_COMMON_API LLWString utf16str_to_wstring(const llutf16string &utf16str);
+
+LL_COMMON_API llutf16string wstring_to_utf16str(const LLWString &utf32str, S32 len);
+LL_COMMON_API llutf16string wstring_to_utf16str(const LLWString &utf32str);
+
+LL_COMMON_API llutf16string utf8str_to_utf16str ( const std::string& utf8str, S32 len);
+LL_COMMON_API llutf16string utf8str_to_utf16str ( const std::string& utf8str );
+
+LL_COMMON_API LLWString utf8str_to_wstring(const std::string &utf8str, S32 len);
+LL_COMMON_API LLWString utf8str_to_wstring(const std::string &utf8str);
+// Same function, better name. JC
+inline LLWString utf8string_to_wstring(const std::string& utf8_string) { return utf8str_to_wstring(utf8_string); }
+
+//
+LL_COMMON_API S32 wchar_to_utf8chars(llwchar inchar, char* outchars);
+
+LL_COMMON_API std::string wstring_to_utf8str(const LLWString &utf32str, S32 len);
+LL_COMMON_API std::string wstring_to_utf8str(const LLWString &utf32str);
+
+LL_COMMON_API std::string utf16str_to_utf8str(const llutf16string &utf16str, S32 len);
+LL_COMMON_API std::string utf16str_to_utf8str(const llutf16string &utf16str);
+
+// Length of this UTF32 string in bytes when transformed to UTF8
+LL_COMMON_API S32 wstring_utf8_length(const LLWString& wstr);
+
+// Length in bytes of this wide char in a UTF8 string
+LL_COMMON_API S32 wchar_utf8_length(const llwchar wc);
+
+LL_COMMON_API std::string utf8str_tolower(const std::string& utf8str);
+
+// Length in llwchar (UTF-32) of the first len units (16 bits) of the given UTF-16 string.
+LL_COMMON_API S32 utf16str_wstring_length(const llutf16string &utf16str, S32 len);
+
+// Length in utf16string (UTF-16) of wlen wchars beginning at woffset.
+LL_COMMON_API S32 wstring_utf16_length(const LLWString & wstr, S32 woffset, S32 wlen);
+
+// Length in wstring (i.e., llwchar count) of a part of a wstring specified by utf16 length (i.e., utf16 units.)
+LL_COMMON_API S32 wstring_wstring_length_from_utf16_length(const LLWString & wstr, S32 woffset, S32 utf16_length, BOOL *unaligned = NULL);
+
+/**
+ * @brief Properly truncate a utf8 string to a maximum byte count.
+ *
+ * The returned string may be less than max_len if the truncation
+ * happens in the middle of a glyph. If max_len is longer than the
+ * string passed in, the return value == utf8str.
+ * @param utf8str A valid utf8 string to truncate.
+ * @param max_len The maximum number of bytes in the return value.
+ * @return Returns a valid utf8 string with byte count <= max_len.
+ */
+LL_COMMON_API std::string utf8str_truncate(const std::string& utf8str, const S32 max_len);
+
+LL_COMMON_API std::string utf8str_trim(const std::string& utf8str);
+
+LL_COMMON_API S32 utf8str_compare_insensitive(
+ const std::string& lhs,
+ const std::string& rhs);
+
+/**
+ * @brief Replace all occurences of target_char with replace_char
+ *
+ * @param utf8str A utf8 string to process.
+ * @param target_char The wchar to be replaced
+ * @param replace_char The wchar which is written on replace
+ */
+LL_COMMON_API std::string utf8str_substChar(
+ const std::string& utf8str,
+ const llwchar target_char,
+ const llwchar replace_char);
+
+LL_COMMON_API std::string utf8str_makeASCII(const std::string& utf8str);
+
+// Hack - used for evil notecards.
+LL_COMMON_API std::string mbcsstring_makeASCII(const std::string& str);
+
+LL_COMMON_API std::string utf8str_removeCRLF(const std::string& utf8str);
+
+
+#if LL_WINDOWS
+/* @name Windows string helpers
+ */
+//@{
+
+/**
+ * @brief Implementation the expected snprintf interface.
+ *
+ * If the size of the passed in buffer is not large enough to hold the string,
+ * two bad things happen:
+ * 1. resulting formatted string is NOT null terminated
+ * 2. Depending on the platform, the return value could be a) the required
+ * size of the buffer to copy the entire formatted string or b) -1.
+ * On Windows with VS.Net 2003, it returns -1 e.g.
+ *
+ * safe_snprintf always adds a NULL terminator so that the caller does not
+ * need to check for return value or need to add the NULL terminator.
+ * It does not, however change the return value - to let the caller know
+ * that the passed in buffer size was not large enough to hold the
+ * formatted string.
+ *
+ */
+
+// Deal with the differeneces on Windows
+namespace snprintf_hack
+{
+ LL_COMMON_API int snprintf(char *str, size_t size, const char *format, ...);
+}
+
+using snprintf_hack::snprintf;
+
+/**
+ * @brief Convert a wide string to std::string
+ *
+ * This replaces the unsafe W2A macro from ATL.
+ */
+LL_COMMON_API std::string ll_convert_wide_to_string(const wchar_t* in);
+
+//@}
+#endif // LL_WINDOWS
+
+/**
+ * Many of the 'strip' and 'replace' methods of LLStringUtilBase need
+ * specialization to work with the signed char type.
+ * Sadly, it is not possible (AFAIK) to specialize a single method of
+ * a template class.
+ * That stuff should go here.
+ */
+namespace LLStringFn
+{
+ /**
+ * @brief Replace all non-printable characters with replacement in
+ * string.
+ * NOTE - this will zap non-ascii
+ *
+ * @param [in,out] string the to modify. out value is the string
+ * with zero non-printable characters.
+ * @param The replacement character. use LL_UNKNOWN_CHAR if unsure.
+ */
+ LL_COMMON_API void replace_nonprintable_in_ascii(
+ std::basic_string<char>& string,
+ char replacement);
+
+
+ /**
+ * @brief Replace all non-printable characters and pipe characters
+ * with replacement in a string.
+ * NOTE - this will zap non-ascii
+ *
+ * @param [in,out] the string to modify. out value is the string
+ * with zero non-printable characters and zero pipe characters.
+ * @param The replacement character. use LL_UNKNOWN_CHAR if unsure.
+ */
+ LL_COMMON_API void replace_nonprintable_and_pipe_in_ascii(std::basic_string<char>& str,
+ char replacement);
+
+
+ /**
+ * @brief Remove all characters that are not allowed in XML 1.0.
+ * Returns a copy of the string with those characters removed.
+ * Works with US ASCII and UTF-8 encoded strings. JC
+ */
+ LL_COMMON_API std::string strip_invalid_xml(const std::string& input);
+
+
+ /**
+ * @brief Replace all control characters (0 <= c < 0x20) with replacement in
+ * string. This is safe for utf-8
+ *
+ * @param [in,out] string the to modify. out value is the string
+ * with zero non-printable characters.
+ * @param The replacement character. use LL_UNKNOWN_CHAR if unsure.
+ */
+ LL_COMMON_API void replace_ascii_controlchars(
+ std::basic_string<char>& string,
+ char replacement);
+}
+
+////////////////////////////////////////////////////////////
+// NOTE: LLStringUtil::format, getTokens, and support functions moved to llstring.cpp.
+// There is no LLWStringUtil::format implementation currently.
+// Calling thse for anything other than LLStringUtil will produce link errors.
+
+////////////////////////////////////////////////////////////
+
+
+// static
+template<class T>
+S32 LLStringUtilBase<T>::compareStrings(const T* lhs, const T* rhs)
+{
+ S32 result;
+ if( lhs == rhs )
+ {
+ result = 0;
+ }
+ else
+ if ( !lhs || !lhs[0] )
+ {
+ result = ((!rhs || !rhs[0]) ? 0 : 1);
+ }
+ else
+ if ( !rhs || !rhs[0])
+ {
+ result = -1;
+ }
+ else
+ {
+ result = LLStringOps::collate(lhs, rhs);
+ }
+ return result;
+}
+
+//static
+template<class T>
+S32 LLStringUtilBase<T>::compareStrings(const std::basic_string<T>& lhs, const std::basic_string<T>& rhs)
+{
+ return LLStringOps::collate(lhs.c_str(), rhs.c_str());
+}
+
+// static
+template<class T>
+S32 LLStringUtilBase<T>::compareInsensitive(const T* lhs, const T* rhs )
+{
+ S32 result;
+ if( lhs == rhs )
+ {
+ result = 0;
+ }
+ else
+ if ( !lhs || !lhs[0] )
+ {
+ result = ((!rhs || !rhs[0]) ? 0 : 1);
+ }
+ else
+ if ( !rhs || !rhs[0] )
+ {
+ result = -1;
+ }
+ else
+ {
+ std::basic_string<T> lhs_string(lhs);
+ std::basic_string<T> rhs_string(rhs);
+ LLStringUtilBase<T>::toUpper(lhs_string);
+ LLStringUtilBase<T>::toUpper(rhs_string);
+ result = LLStringOps::collate(lhs_string.c_str(), rhs_string.c_str());
+ }
+ return result;
+}
+
+//static
+template<class T>
+S32 LLStringUtilBase<T>::compareInsensitive(const std::basic_string<T>& lhs, const std::basic_string<T>& rhs)
+{
+ std::basic_string<T> lhs_string(lhs);
+ std::basic_string<T> rhs_string(rhs);
+ LLStringUtilBase<T>::toUpper(lhs_string);
+ LLStringUtilBase<T>::toUpper(rhs_string);
+ return LLStringOps::collate(lhs_string.c_str(), rhs_string.c_str());
+}
+
+// Case sensitive comparison with good handling of numbers. Does not use current locale.
+// a.k.a. strdictcmp()
+
+//static
+template<class T>
+S32 LLStringUtilBase<T>::compareDict(const std::basic_string<T>& astr, const std::basic_string<T>& bstr)
+{
+ const T* a = astr.c_str();
+ const T* b = bstr.c_str();
+ T ca, cb;
+ S32 ai, bi, cnt = 0;
+ S32 bias = 0;
+
+ ca = *(a++);
+ cb = *(b++);
+ while( ca && cb ){
+ if( bias==0 ){
+ if( LLStringOps::isUpper(ca) ){ ca = LLStringOps::toLower(ca); bias--; }
+ if( LLStringOps::isUpper(cb) ){ cb = LLStringOps::toLower(cb); bias++; }
+ }else{
+ if( LLStringOps::isUpper(ca) ){ ca = LLStringOps::toLower(ca); }
+ if( LLStringOps::isUpper(cb) ){ cb = LLStringOps::toLower(cb); }
+ }
+ if( LLStringOps::isDigit(ca) ){
+ if( cnt-->0 ){
+ if( cb!=ca ) break;
+ }else{
+ if( !LLStringOps::isDigit(cb) ) break;
+ for(ai=0; LLStringOps::isDigit(a[ai]); ai++);
+ for(bi=0; LLStringOps::isDigit(b[bi]); bi++);
+ if( ai<bi ){ ca=0; break; }
+ if( bi<ai ){ cb=0; break; }
+ if( ca!=cb ) break;
+ cnt = ai;
+ }
+ }else if( ca!=cb ){ break;
+ }
+ ca = *(a++);
+ cb = *(b++);
+ }
+ if( ca==cb ) ca += bias;
+ return ca-cb;
+}
+
+// static
+template<class T>
+S32 LLStringUtilBase<T>::compareDictInsensitive(const std::basic_string<T>& astr, const std::basic_string<T>& bstr)
+{
+ const T* a = astr.c_str();
+ const T* b = bstr.c_str();
+ T ca, cb;
+ S32 ai, bi, cnt = 0;
+
+ ca = *(a++);
+ cb = *(b++);
+ while( ca && cb ){
+ if( LLStringOps::isUpper(ca) ){ ca = LLStringOps::toLower(ca); }
+ if( LLStringOps::isUpper(cb) ){ cb = LLStringOps::toLower(cb); }
+ if( LLStringOps::isDigit(ca) ){
+ if( cnt-->0 ){
+ if( cb!=ca ) break;
+ }else{
+ if( !LLStringOps::isDigit(cb) ) break;
+ for(ai=0; LLStringOps::isDigit(a[ai]); ai++);
+ for(bi=0; LLStringOps::isDigit(b[bi]); bi++);
+ if( ai<bi ){ ca=0; break; }
+ if( bi<ai ){ cb=0; break; }
+ if( ca!=cb ) break;
+ cnt = ai;
+ }
+ }else if( ca!=cb ){ break;
+ }
+ ca = *(a++);
+ cb = *(b++);
+ }
+ return ca-cb;
+}
+
+// Puts compareDict() in a form appropriate for LL container classes to use for sorting.
+// static
+template<class T>
+BOOL LLStringUtilBase<T>::precedesDict( const std::basic_string<T>& a, const std::basic_string<T>& b )
+{
+ if( a.size() && b.size() )
+ {
+ return (LLStringUtilBase<T>::compareDict(a.c_str(), b.c_str()) < 0);
+ }
+ else
+ {
+ return (!b.empty());
+ }
+}
+
+//static
+template<class T>
+void LLStringUtilBase<T>::toUpper(std::basic_string<T>& string)
+{
+ if( !string.empty() )
+ {
+ std::transform(
+ string.begin(),
+ string.end(),
+ string.begin(),
+ (T(*)(T)) &LLStringOps::toUpper);
+ }
+}
+
+//static
+template<class T>
+void LLStringUtilBase<T>::toLower(std::basic_string<T>& string)
+{
+ if( !string.empty() )
+ {
+ std::transform(
+ string.begin(),
+ string.end(),
+ string.begin(),
+ (T(*)(T)) &LLStringOps::toLower);
+ }
+}
+
+//static
+template<class T>
+void LLStringUtilBase<T>::trimHead(std::basic_string<T>& string)
+{
+ if( !string.empty() )
+ {
+ size_type i = 0;
+ while( i < string.length() && LLStringOps::isSpace( string[i] ) )
+ {
+ i++;
+ }
+ string.erase(0, i);
+ }
+}
+
+//static
+template<class T>
+void LLStringUtilBase<T>::trimTail(std::basic_string<T>& string)
+{
+ if( string.size() )
+ {
+ size_type len = string.length();
+ size_type i = len;
+ while( i > 0 && LLStringOps::isSpace( string[i-1] ) )
+ {
+ i--;
+ }
+
+ string.erase( i, len - i );
+ }
+}
+
+
+// Replace line feeds with carriage return-line feed pairs.
+//static
+template<class T>
+void LLStringUtilBase<T>::addCRLF(std::basic_string<T>& string)
+{
+ const T LF = 10;
+ const T CR = 13;
+
+ // Count the number of line feeds
+ size_type count = 0;
+ size_type len = string.size();
+ size_type i;
+ for( i = 0; i < len; i++ )
+ {
+ if( string[i] == LF )
+ {
+ count++;
+ }
+ }
+
+ // Insert a carriage return before each line feed
+ if( count )
+ {
+ size_type size = len + count;
+ T *t = new T[size];
+ size_type j = 0;
+ for( i = 0; i < len; ++i )
+ {
+ if( string[i] == LF )
+ {
+ t[j] = CR;
+ ++j;
+ }
+ t[j] = string[i];
+ ++j;
+ }
+
+ string.assign(t, size);
+ }
+}
+
+// Remove all carriage returns
+//static
+template<class T>
+void LLStringUtilBase<T>::removeCRLF(std::basic_string<T>& string)
+{
+ const T CR = 13;
+
+ size_type cr_count = 0;
+ size_type len = string.size();
+ size_type i;
+ for( i = 0; i < len - cr_count; i++ )
+ {
+ if( string[i+cr_count] == CR )
+ {
+ cr_count++;
+ }
+
+ string[i] = string[i+cr_count];
+ }
+ string.erase(i, cr_count);
+}
+
+//static
+template<class T>
+void LLStringUtilBase<T>::replaceChar( std::basic_string<T>& string, T target, T replacement )
+{
+ size_type found_pos = 0;
+ while( (found_pos = string.find(target, found_pos)) != std::basic_string<T>::npos )
+ {
+ string[found_pos] = replacement;
+ found_pos++; // avoid infinite defeat if target == replacement
+ }
+}
+
+//static
+template<class T>
+void LLStringUtilBase<T>::replaceString( std::basic_string<T>& string, std::basic_string<T> target, std::basic_string<T> replacement )
+{
+ size_type found_pos = 0;
+ while( (found_pos = string.find(target, found_pos)) != std::basic_string<T>::npos )
+ {
+ string.replace( found_pos, target.length(), replacement );
+ found_pos += replacement.length(); // avoid infinite defeat if replacement contains target
+ }
+}
+
+//static
+template<class T>
+void LLStringUtilBase<T>::replaceNonstandardASCII( std::basic_string<T>& string, T replacement )
+{
+ const char LF = 10;
+ const S8 MIN = 32;
+// const S8 MAX = 127;
+
+ size_type len = string.size();
+ for( size_type i = 0; i < len; i++ )
+ {
+ // No need to test MAX < mText[i] because we treat mText[i] as a signed char,
+ // which has a max value of 127.
+ if( ( S8(string[i]) < MIN ) && (string[i] != LF) )
+ {
+ string[i] = replacement;
+ }
+ }
+}
+
+//static
+template<class T>
+void LLStringUtilBase<T>::replaceTabsWithSpaces( std::basic_string<T>& str, size_type spaces_per_tab )
+{
+ const T TAB = '\t';
+ const T SPACE = ' ';
+
+ std::basic_string<T> out_str;
+ // Replace tabs with spaces
+ for (size_type i = 0; i < str.length(); i++)
+ {
+ if (str[i] == TAB)
+ {
+ for (size_type j = 0; j < spaces_per_tab; j++)
+ out_str += SPACE;
+ }
+ else
+ {
+ out_str += str[i];
+ }
+ }
+ str = out_str;
+}
+
+//static
+template<class T>
+BOOL LLStringUtilBase<T>::containsNonprintable(const std::basic_string<T>& string)
+{
+ const char MIN = 32;
+ BOOL rv = FALSE;
+ for (size_type i = 0; i < string.size(); i++)
+ {
+ if(string[i] < MIN)
+ {
+ rv = TRUE;
+ break;
+ }
+ }
+ return rv;
+}
+
+//static
+template<class T>
+void LLStringUtilBase<T>::stripNonprintable(std::basic_string<T>& string)
+{
+ const char MIN = 32;
+ size_type j = 0;
+ if (string.empty())
+ {
+ return;
+ }
+ size_t src_size = string.size();
+ char* c_string = new char[src_size + 1];
+ if(c_string == NULL)
+ {
+ return;
+ }
+ copy(c_string, string.c_str(), src_size+1);
+ char* write_head = &c_string[0];
+ for (size_type i = 0; i < src_size; i++)
+ {
+ char* read_head = &string[i];
+ write_head = &c_string[j];
+ if(!(*read_head < MIN))
+ {
+ *write_head = *read_head;
+ ++j;
+ }
+ }
+ c_string[j]= '\0';
+ string = c_string;
+ delete []c_string;
+}
+
+template<class T>
+void LLStringUtilBase<T>::_makeASCII(std::basic_string<T>& string)
+{
+ // Replace non-ASCII chars with LL_UNKNOWN_CHAR
+ for (size_type i = 0; i < string.length(); i++)
+ {
+ if (string[i] > 0x7f)
+ {
+ string[i] = LL_UNKNOWN_CHAR;
+ }
+ }
+}
+
+// static
+template<class T>
+void LLStringUtilBase<T>::copy( T* dst, const T* src, size_type dst_size )
+{
+ if( dst_size > 0 )
+ {
+ size_type min_len = 0;
+ if( src )
+ {
+ min_len = llmin( dst_size - 1, strlen( src ) ); /* Flawfinder: ignore */
+ memcpy(dst, src, min_len * sizeof(T)); /* Flawfinder: ignore */
+ }
+ dst[min_len] = '\0';
+ }
+}
+
+// static
+template<class T>
+void LLStringUtilBase<T>::copyInto(std::basic_string<T>& dst, const std::basic_string<T>& src, size_type offset)
+{
+ if ( offset == dst.length() )
+ {
+ // special case - append to end of string and avoid expensive
+ // (when strings are large) string manipulations
+ dst += src;
+ }
+ else
+ {
+ std::basic_string<T> tail = dst.substr(offset);
+
+ dst = dst.substr(0, offset);
+ dst += src;
+ dst += tail;
+ };
+}
+
+// True if this is the head of s.
+//static
+template<class T>
+BOOL LLStringUtilBase<T>::isHead( const std::basic_string<T>& string, const T* s )
+{
+ if( string.empty() )
+ {
+ // Early exit
+ return FALSE;
+ }
+ else
+ {
+ return (strncmp( s, string.c_str(), string.size() ) == 0);
+ }
+}
+
+// static
+template<class T>
+bool LLStringUtilBase<T>::startsWith(
+ const std::basic_string<T>& string,
+ const std::basic_string<T>& substr)
+{
+ if(string.empty() || (substr.empty())) return false;
+ if(0 == string.find(substr)) return true;
+ return false;
+}
+
+// static
+template<class T>
+bool LLStringUtilBase<T>::endsWith(
+ const std::basic_string<T>& string,
+ const std::basic_string<T>& substr)
+{
+ if(string.empty() || (substr.empty())) return false;
+ std::string::size_type idx = string.rfind(substr);
+ if(std::string::npos == idx) return false;
+ return (idx == (string.size() - substr.size()));
+}
+
+
+template<class T>
+BOOL LLStringUtilBase<T>::convertToBOOL(const std::basic_string<T>& string, BOOL& value)
+{
+ if( string.empty() )
+ {
+ return FALSE;
+ }
+
+ std::basic_string<T> temp( string );
+ trim(temp);
+ if(
+ (temp == "1") ||
+ (temp == "T") ||
+ (temp == "t") ||
+ (temp == "TRUE") ||
+ (temp == "true") ||
+ (temp == "True") )
+ {
+ value = TRUE;
+ return TRUE;
+ }
+ else
+ if(
+ (temp == "0") ||
+ (temp == "F") ||
+ (temp == "f") ||
+ (temp == "FALSE") ||
+ (temp == "false") ||
+ (temp == "False") )
+ {
+ value = FALSE;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+template<class T>
+BOOL LLStringUtilBase<T>::convertToU8(const std::basic_string<T>& string, U8& value)
+{
+ S32 value32 = 0;
+ BOOL success = convertToS32(string, value32);
+ if( success && (U8_MIN <= value32) && (value32 <= U8_MAX) )
+ {
+ value = (U8) value32;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+template<class T>
+BOOL LLStringUtilBase<T>::convertToS8(const std::basic_string<T>& string, S8& value)
+{
+ S32 value32 = 0;
+ BOOL success = convertToS32(string, value32);
+ if( success && (S8_MIN <= value32) && (value32 <= S8_MAX) )
+ {
+ value = (S8) value32;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+template<class T>
+BOOL LLStringUtilBase<T>::convertToS16(const std::basic_string<T>& string, S16& value)
+{
+ S32 value32 = 0;
+ BOOL success = convertToS32(string, value32);
+ if( success && (S16_MIN <= value32) && (value32 <= S16_MAX) )
+ {
+ value = (S16) value32;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+template<class T>
+BOOL LLStringUtilBase<T>::convertToU16(const std::basic_string<T>& string, U16& value)
+{
+ S32 value32 = 0;
+ BOOL success = convertToS32(string, value32);
+ if( success && (U16_MIN <= value32) && (value32 <= U16_MAX) )
+ {
+ value = (U16) value32;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+template<class T>
+BOOL LLStringUtilBase<T>::convertToU32(const std::basic_string<T>& string, U32& value)
+{
+ if( string.empty() )
+ {
+ return FALSE;
+ }
+
+ std::basic_string<T> temp( string );
+ trim(temp);
+ U32 v;
+ std::basic_istringstream<T> i_stream((std::basic_string<T>)temp);
+ if(i_stream >> v)
+ {
+ value = v;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+template<class T>
+BOOL LLStringUtilBase<T>::convertToS32(const std::basic_string<T>& string, S32& value)
+{
+ if( string.empty() )
+ {
+ return FALSE;
+ }
+
+ std::basic_string<T> temp( string );
+ trim(temp);
+ S32 v;
+ std::basic_istringstream<T> i_stream((std::basic_string<T>)temp);
+ if(i_stream >> v)
+ {
+ //TODO: figure out overflow and underflow reporting here
+ //if((LONG_MAX == v) || (LONG_MIN == v))
+ //{
+ // // Underflow or overflow
+ // return FALSE;
+ //}
+
+ value = v;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+template<class T>
+BOOL LLStringUtilBase<T>::convertToF32(const std::basic_string<T>& string, F32& value)
+{
+ F64 value64 = 0.0;
+ BOOL success = convertToF64(string, value64);
+ if( success && (-F32_MAX <= value64) && (value64 <= F32_MAX) )
+ {
+ value = (F32) value64;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+template<class T>
+BOOL LLStringUtilBase<T>::convertToF64(const std::basic_string<T>& string, F64& value)
+{
+ if( string.empty() )
+ {
+ return FALSE;
+ }
+
+ std::basic_string<T> temp( string );
+ trim(temp);
+ F64 v;
+ std::basic_istringstream<T> i_stream((std::basic_string<T>)temp);
+ if(i_stream >> v)
+ {
+ //TODO: figure out overflow and underflow reporting here
+ //if( ((-HUGE_VAL == v) || (HUGE_VAL == v))) )
+ //{
+ // // Underflow or overflow
+ // return FALSE;
+ //}
+
+ value = v;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+template<class T>
+void LLStringUtilBase<T>::truncate(std::basic_string<T>& string, size_type count)
+{
+ size_type cur_size = string.size();
+ string.resize(count < cur_size ? count : cur_size);
+}
+
+#endif // LL_STRING_H
diff --git a/indra/llcommon/llstringtable.h b/indra/llcommon/llstringtable.h
index 888361b0b9..d40c9d8dfd 100644
--- a/indra/llcommon/llstringtable.h
+++ b/indra/llcommon/llstringtable.h
@@ -48,15 +48,17 @@
//# define STRING_TABLE_HASH_MAP 1
#endif
-#if LL_WINDOWS
-#include <hash_map>
-#else
-#include <ext/hash_map>
+#if STRING_TABLE_HASH_MAP
+# if LL_WINDOWS
+# include <hash_map>
+# else
+# include <ext/hash_map>
+# endif
#endif
const U32 MAX_STRINGS_LENGTH = 256;
-class LLStringTableEntry
+class LL_COMMON_API LLStringTableEntry
{
public:
LLStringTableEntry(const char *str);
@@ -69,7 +71,7 @@ public:
S32 mCount;
};
-class LLStringTable
+class LL_COMMON_API LLStringTable
{
public:
LLStringTable(int tablesize);
@@ -103,7 +105,7 @@ public:
#endif
};
-extern LLStringTable gStringTable;
+extern LL_COMMON_API LLStringTable gStringTable;
//============================================================================
@@ -113,7 +115,7 @@ extern LLStringTable gStringTable;
typedef const std::string* LLStdStringHandle;
-class LLStdStringTable
+class LL_COMMON_API LLStdStringTable
{
public:
LLStdStringTable(S32 tablesize = 0)
diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp
index 4737421289..3652eeba72 100644
--- a/indra/llcommon/llsys.cpp
+++ b/indra/llcommon/llsys.cpp
@@ -161,8 +161,16 @@ LLOSInfo::LLOSInfo() :
mOSStringSimple = "Microsoft Windows Vista Server ";
}
}
+ else if(osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 1)
+ {
+ if(osvi.wProductType == VER_NT_WORKSTATION)
+ mOSStringSimple = "Microsoft Windows 7 ";
+ else mOSStringSimple = "Microsoft Windows 7 Server ";
+ }
else // Use the registry on early versions of Windows NT.
{
+ mOSStringSimple = "Microsoft Windows (unrecognized) ";
+
HKEY hKey;
WCHAR szProductType[80];
DWORD dwBufLen;
diff --git a/indra/llcommon/llsys.h b/indra/llcommon/llsys.h
index 03f48ca018..c2c45bec9a 100644
--- a/indra/llcommon/llsys.h
+++ b/indra/llcommon/llsys.h
@@ -45,7 +45,7 @@
#include <iosfwd>
#include <string>
-class LLOSInfo
+class LL_COMMON_API LLOSInfo
{
public:
LLOSInfo();
@@ -70,7 +70,7 @@ private:
};
-class LLCPUInfo
+class LL_COMMON_API LLCPUInfo
{
public:
LLCPUInfo();
@@ -99,7 +99,7 @@ private:
//
// CLASS LLMemoryInfo
-class LLMemoryInfo
+class LL_COMMON_API LLMemoryInfo
/*! @brief Class to query the memory subsystem
@@ -123,15 +123,15 @@ public:
};
-std::ostream& operator<<(std::ostream& s, const LLOSInfo& info);
-std::ostream& operator<<(std::ostream& s, const LLCPUInfo& info);
-std::ostream& operator<<(std::ostream& s, const LLMemoryInfo& info);
+LL_COMMON_API std::ostream& operator<<(std::ostream& s, const LLOSInfo& info);
+LL_COMMON_API std::ostream& operator<<(std::ostream& s, const LLCPUInfo& info);
+LL_COMMON_API std::ostream& operator<<(std::ostream& s, const LLMemoryInfo& info);
// gunzip srcfile into dstfile. Returns FALSE on error.
-BOOL gunzip_file(const std::string& srcfile, const std::string& dstfile);
+BOOL LL_COMMON_API gunzip_file(const std::string& srcfile, const std::string& dstfile);
// gzip srcfile into dstfile. Returns FALSE on error.
-BOOL gzip_file(const std::string& srcfile, const std::string& dstfile);
+BOOL LL_COMMON_API gzip_file(const std::string& srcfile, const std::string& dstfile);
-extern LLCPUInfo gSysCPU;
+extern LL_COMMON_API LLCPUInfo gSysCPU;
#endif // LL_LLSYS_H
diff --git a/indra/llcommon/llthread.h b/indra/llcommon/llthread.h
index f25339f48d..c3d7650bd9 100644
--- a/indra/llcommon/llthread.h
+++ b/indra/llcommon/llthread.h
@@ -42,7 +42,7 @@ class LLThread;
class LLMutex;
class LLCondition;
-class LLThread
+class LL_COMMON_API LLThread
{
public:
typedef enum e_thread_status
@@ -130,7 +130,7 @@ protected:
//============================================================================
-class LLMutex
+class LL_COMMON_API LLMutex
{
public:
LLMutex(apr_pool_t *apr_poolp); // NULL pool constructs a new pool for the mutex
@@ -147,7 +147,7 @@ protected:
};
// Actually a condition/mutex pair (since each condition needs to be associated with a mutex).
-class LLCondition : public LLMutex
+class LL_COMMON_API LLCondition : public LLMutex
{
public:
LLCondition(apr_pool_t *apr_poolp); // Defaults to global pool, could use the thread pool as well.
@@ -194,7 +194,7 @@ void LLThread::unlockData()
// see llmemory.h for LLPointer<> definition
-class LLThreadSafeRefCount
+class LL_COMMON_API LLThreadSafeRefCount
{
public:
static void initThreadSafeRefCount(); // creates sMutex
@@ -246,7 +246,7 @@ private:
// Simple responder for self destructing callbacks
// Pure virtual class
-class LLResponder : public LLThreadSafeRefCount
+class LL_COMMON_API LLResponder : public LLThreadSafeRefCount
{
protected:
virtual ~LLResponder();
diff --git a/indra/llcommon/lltimer.h b/indra/llcommon/lltimer.h
index 0319bec45b..d009c0f5f7 100644
--- a/indra/llcommon/lltimer.h
+++ b/indra/llcommon/lltimer.h
@@ -55,7 +55,7 @@ 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;
-class LLTimer
+class LL_COMMON_API LLTimer
{
public:
static LLTimer *sTimer; // global timer
@@ -114,17 +114,17 @@ public:
//
// Various functions for initializing/accessing clock and timing stuff. Don't use these without REALLY knowing how they work.
//
-U64 get_clock_count();
-F64 calc_clock_frequency(U32 msecs);
-void update_clock_frequencies();
+LL_COMMON_API U64 get_clock_count();
+LL_COMMON_API F64 calc_clock_frequency(U32 msecs);
+LL_COMMON_API void update_clock_frequencies();
// Sleep for milliseconds
-void ms_sleep(U32 ms);
-U32 micro_sleep(U64 us, U32 max_yields = 0xFFFFFFFF);
+LL_COMMON_API void ms_sleep(U32 ms);
+LL_COMMON_API U32 micro_sleep(U64 us, U32 max_yields = 0xFFFFFFFF);
// Returns the correct UTC time in seconds, like time(NULL).
// Useful on the viewer, which may have its local clock set wrong.
-time_t time_corrected();
+LL_COMMON_API time_t time_corrected();
static inline time_t time_min()
{
@@ -155,24 +155,24 @@ static inline time_t time_max()
}
// Correction factor used by time_corrected() above.
-extern S32 gUTCOffset;
+extern LL_COMMON_API S32 gUTCOffset;
// Is the current computer (in its current time zone)
// observing daylight savings time?
-BOOL is_daylight_savings();
+LL_COMMON_API BOOL is_daylight_savings();
// Converts internal "struct tm" time buffer to Pacific Standard/Daylight Time
// Usage:
// S32 utc_time;
// utc_time = time_corrected();
// struct tm* internal_time = utc_to_pacific_time(utc_time, gDaylight);
-struct tm* utc_to_pacific_time(time_t utc_time, BOOL pacific_daylight_time);
+LL_COMMON_API struct tm* utc_to_pacific_time(time_t utc_time, BOOL pacific_daylight_time);
-void microsecondsToTimecodeString(U64 current_time, std::string& tcstring);
-void secondsToTimecodeString(F32 current_time, std::string& tcstring);
+LL_COMMON_API void microsecondsToTimecodeString(U64 current_time, std::string& tcstring);
+LL_COMMON_API void secondsToTimecodeString(F32 current_time, std::string& tcstring);
// class for scheduling a function to be called at a given frequency (approximate, inprecise)
-class LLEventTimer : protected LLInstanceTracker<LLEventTimer>
+class LL_COMMON_API LLEventTimer : protected LLInstanceTracker<LLEventTimer>
{
public:
LLEventTimer(F32 period); // period is the amount of time between each call to tick() in seconds
@@ -190,4 +190,6 @@ protected:
F32 mPeriod;
};
+U64 LL_COMMON_API totalTime(); // Returns current system time in microseconds
+
#endif
diff --git a/indra/llcommon/lluri.h b/indra/llcommon/lluri.h
index 8e46e2e89e..8e69e8558a 100644
--- a/indra/llcommon/lluri.h
+++ b/indra/llcommon/lluri.h
@@ -47,7 +47,7 @@ class LLApp;
* See: http://www.ietf.org/rfc/rfc3986.txt
*
*/
-class LLURI
+class LL_COMMON_API LLURI
{
public:
LLURI();
@@ -178,6 +178,6 @@ private:
};
// this operator required for tut
-bool operator!=(const LLURI& first, const LLURI& second);
+LL_COMMON_API bool operator!=(const LLURI& first, const LLURI& second);
#endif // LL_LLURI_H
diff --git a/indra/llcommon/lluuid.h b/indra/llcommon/lluuid.h
index 4b32138a06..c78fb12018 100644
--- a/indra/llcommon/lluuid.h
+++ b/indra/llcommon/lluuid.h
@@ -35,6 +35,7 @@
#include <iostream>
#include <set>
#include "stdtypes.h"
+#include "llpreprocessor.h"
const S32 UUID_BYTES = 16;
const S32 UUID_WORDS = 4;
@@ -47,7 +48,7 @@ struct uuid_time_t {
U32 low;
};
-class LLUUID
+class LL_COMMON_API LLUUID
{
public:
//
@@ -106,8 +107,8 @@ public:
LLUUID combine(const LLUUID& other) const;
void combine(const LLUUID& other, LLUUID& result) const;
- friend std::ostream& operator<<(std::ostream& s, const LLUUID &uuid);
- friend std::istream& operator>>(std::istream& s, LLUUID &uuid);
+ friend LL_COMMON_API std::ostream& operator<<(std::ostream& s, const LLUUID &uuid);
+ friend LL_COMMON_API std::istream& operator>>(std::istream& s, LLUUID &uuid);
void toString(char *out) const; // Does not allocate memory, needs 36 characters (including \0)
void toString(std::string& out) const;
@@ -323,7 +324,7 @@ typedef std::set<LLUUID, lluuid_less> uuid_list_t;
*/
typedef LLUUID LLAssetID;
-class LLTransactionID : public LLUUID
+class LL_COMMON_API LLTransactionID : public LLUUID
{
public:
LLTransactionID() : LLUUID() { }
diff --git a/indra/llcommon/llversionserver.h b/indra/llcommon/llversionserver.h
index 23e39ceb08..71c6fc0591 100644
--- a/indra/llcommon/llversionserver.h
+++ b/indra/llcommon/llversionserver.h
@@ -34,9 +34,9 @@
#define LL_LLVERSIONSERVER_H
const S32 LL_VERSION_MAJOR = 1;
-const S32 LL_VERSION_MINOR = 29;
+const S32 LL_VERSION_MINOR = 31;
const S32 LL_VERSION_PATCH = 0;
-const S32 LL_VERSION_BUILD = 0;
+const S32 LL_VERSION_BUILD = 3256;
const char * const LL_CHANNEL = "Second Life Server";
diff --git a/indra/llcommon/llversionviewer.h b/indra/llcommon/llversionviewer.h
index 2c3e9c7333..082d054ba2 100644
--- a/indra/llcommon/llversionviewer.h
+++ b/indra/llcommon/llversionviewer.h
@@ -36,7 +36,7 @@
const S32 LL_VERSION_MAJOR = 2;
const S32 LL_VERSION_MINOR = 0;
const S32 LL_VERSION_PATCH = 0;
-const S32 LL_VERSION_BUILD = 0;
+const S32 LL_VERSION_BUILD = 3256;
const char * const LL_CHANNEL = "Second Life Developer";
diff --git a/indra/llcommon/llworkerthread.h b/indra/llcommon/llworkerthread.h
index 19407f4463..a12bd52a64 100644
--- a/indra/llcommon/llworkerthread.h
+++ b/indra/llcommon/llworkerthread.h
@@ -50,7 +50,7 @@ class LLWorkerClass;
// Note: ~LLWorkerThread is O(N) N=# of worker threads, assumed to be small
// It is assumed that LLWorkerThreads are rarely created/destroyed.
-class LLWorkerThread : public LLQueuedThread
+class LL_COMMON_API LLWorkerThread : public LLQueuedThread
{
public:
class WorkRequest : public LLQueuedThread::QueuedRequest
@@ -113,7 +113,7 @@ public:
// Only one background task can be active at a time (per instance).
// i.e. don't call addWork() if haveWork() returns true
-class LLWorkerClass
+class LL_COMMON_API LLWorkerClass
{
friend class LLWorkerThread;
friend class LLWorkerThread::WorkRequest;
diff --git a/indra/llcommon/metaclass.h b/indra/llcommon/metaclass.h
index cc10f1675f..f38bcd2d57 100644
--- a/indra/llcommon/metaclass.h
+++ b/indra/llcommon/metaclass.h
@@ -43,7 +43,7 @@
class LLReflective;
class LLMetaProperty;
class LLMetaMethod;
-class LLMetaClass
+class LL_COMMON_API LLMetaClass
{
public:
diff --git a/indra/llcommon/metaproperty.h b/indra/llcommon/metaproperty.h
index e5ac35907c..6c016c56dd 100644
--- a/indra/llcommon/metaproperty.h
+++ b/indra/llcommon/metaproperty.h
@@ -41,7 +41,7 @@
class LLMetaClass;
class LLReflective;
-class LLMetaProperty
+class LL_COMMON_API LLMetaProperty
{
public:
LLMetaProperty(const std::string& name, const LLMetaClass& object_class);
diff --git a/indra/llcommon/metapropertyt.h b/indra/llcommon/metapropertyt.h
index 79a536a224..5ad230d1d5 100644
--- a/indra/llcommon/metapropertyt.h
+++ b/indra/llcommon/metapropertyt.h
@@ -94,6 +94,13 @@ inline const LLReflective* LLMetaPropertyT<LLUUID>::get(const LLReflective* obje
}
template <>
+inline const LLReflective* LLMetaPropertyT<bool>::get(const LLReflective* object) const
+{
+ checkObjectClass(object);
+ return NULL;
+}
+
+template <>
inline LLSD LLMetaPropertyT<S32>::getLLSD(const LLReflective* object) const
{
return *(getProperty(object));
@@ -111,6 +118,12 @@ inline LLSD LLMetaPropertyT<LLUUID>::getLLSD(const LLReflective* object) const
return *(getProperty(object));
}
+template <>
+inline LLSD LLMetaPropertyT<bool>::getLLSD(const LLReflective* object) const
+{
+ return *(getProperty(object));
+}
+
template<class TObject, class TProperty>
class LLMetaPropertyTT : public LLMetaPropertyT<TProperty>
{
diff --git a/indra/llcommon/reflective.h b/indra/llcommon/reflective.h
index e2c18ebc6d..a13537681d 100644
--- a/indra/llcommon/reflective.h
+++ b/indra/llcommon/reflective.h
@@ -36,7 +36,7 @@
#define LL_REFLECTIVE_H
class LLMetaClass;
-class LLReflective
+class LL_COMMON_API LLReflective
{
public:
LLReflective();
diff --git a/indra/llcommon/stringize.h b/indra/llcommon/stringize.h
index 1b2958020f..6399547f5e 100644
--- a/indra/llcommon/stringize.h
+++ b/indra/llcommon/stringize.h
@@ -13,6 +13,7 @@
#define LL_STRINGIZE_H
#include <sstream>
+#include <boost/lambda/lambda.hpp>
/**
* stringize(item) encapsulates an idiom we use constantly, using
@@ -28,6 +29,17 @@ std::string stringize(const T& item)
}
/**
+ * stringize_f(functor)
+ */
+template <typename Functor>
+std::string stringize_f(Functor const & f)
+{
+ std::ostringstream out;
+ f(out);
+ return out.str();
+}
+
+/**
* STRINGIZE(item1 << item2 << item3 ...) effectively expands to the
* following:
* @code
@@ -36,40 +48,43 @@ std::string stringize(const T& item)
* return out.str();
* @endcode
*/
-#define STRINGIZE(EXPRESSION) (static_cast<std::ostringstream&>(Stringize() << EXPRESSION).str())
+#define STRINGIZE(EXPRESSION) (stringize_f(boost::lambda::_1 << EXPRESSION))
+
/**
- * Helper class for STRINGIZE() macro. Ideally the body of
- * STRINGIZE(EXPRESSION) would look something like this:
+ * destringize(str)
+ * defined for symmetry with stringize
+ * *NOTE - this has distinct behavior from boost::lexical_cast<T> regarding
+ * leading/trailing whitespace and handling of bad_lexical_cast exceptions
+ */
+template <typename T>
+T destringize(std::string const & str)
+{
+ T val;
+ std::istringstream in(str);
+ in >> val;
+ return val;
+}
+
+/**
+ * destringize_f(str, functor)
+ */
+template <typename Functor>
+void destringize_f(std::string const & str, Functor const & f)
+{
+ std::istringstream in(str);
+ f(in);
+}
+
+/**
+ * DESTRINGIZE(str, item1 >> item2 >> item3 ...) effectively expands to the
+ * following:
* @code
- * (std::ostringstream() << EXPRESSION).str()
+ * std::istringstream in(str);
+ * in >> item1 >> item2 >> item3 ... ;
* @endcode
- * That doesn't work because each of the relevant operator<<() functions
- * accepts a non-const std::ostream&, to which you can't pass a temp instance
- * of std::ostringstream. Stringize plays the necessary const tricks to make
- * the whole thing work.
*/
-class Stringize
-{
-public:
- /**
- * This is the essence of Stringize. The leftmost << operator (the one
- * coded in the STRINGIZE() macro) engages this operator<<() const method
- * on the temp Stringize instance. Every other << operator (ones embedded
- * in EXPRESSION) simply sees the std::ostream& returned by the first one.
- *
- * Finally, the STRINGIZE() macro downcasts that std::ostream& to
- * std::ostringstream&.
- */
- template <typename T>
- std::ostream& operator<<(const T& item) const
- {
- mOut << item;
- return mOut;
- }
+#define DESTRINGIZE(STR, EXPRESSION) (destringize_f((STR), (boost::lambda::_1 >> EXPRESSION)))
-private:
- mutable std::ostringstream mOut;
-};
#endif /* ! defined(LL_STRINGIZE_H) */
diff --git a/indra/llcommon/tests/listener.h b/indra/llcommon/tests/listener.h
new file mode 100644
index 0000000000..fa12f944ef
--- /dev/null
+++ b/indra/llcommon/tests/listener.h
@@ -0,0 +1,139 @@
+/**
+ * @file listener.h
+ * @author Nat Goodspeed
+ * @date 2009-03-06
+ * @brief Useful for tests of the LLEventPump family of classes
+ *
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ * Copyright (c) 2009, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#if ! defined(LL_LISTENER_H)
+#define LL_LISTENER_H
+
+#include "llsd.h"
+#include <iostream>
+
+/*****************************************************************************
+* test listener class
+*****************************************************************************/
+class Listener;
+std::ostream& operator<<(std::ostream&, const Listener&);
+
+/// Bear in mind that this is strictly for testing
+class Listener
+{
+public:
+ /// Every Listener is instantiated with a name
+ Listener(const std::string& name):
+ mName(name)
+ {
+// std::cout << *this << ": ctor\n";
+ }
+/*==========================================================================*|
+ // These methods are only useful when trying to track Listener instance
+ // lifespan
+ Listener(const Listener& that):
+ mName(that.mName),
+ mLastEvent(that.mLastEvent)
+ {
+ std::cout << *this << ": copy\n";
+ }
+ virtual ~Listener()
+ {
+ std::cout << *this << ": dtor\n";
+ }
+|*==========================================================================*/
+ /// You can request the name
+ std::string getName() const { return mName; }
+ /// This is a typical listener method that returns 'false' when done,
+ /// allowing subsequent listeners on the LLEventPump to process the
+ /// incoming event.
+ bool call(const LLSD& event)
+ {
+// std::cout << *this << "::call(" << event << ")\n";
+ mLastEvent = event;
+ return false;
+ }
+ /// This is an alternate listener that returns 'true' when done, which
+ /// stops processing of the incoming event.
+ bool callstop(const LLSD& event)
+ {
+// std::cout << *this << "::callstop(" << event << ")\n";
+ mLastEvent = event;
+ return true;
+ }
+ /// ListenMethod can represent either call() or callstop().
+ typedef bool (Listener::*ListenMethod)(const LLSD&);
+ /**
+ * This helper method is only because our test code makes so many
+ * repetitive listen() calls to ListenerMethods. In real code, you should
+ * call LLEventPump::listen() directly so it can examine the specific
+ * object you pass to boost::bind().
+ */
+ LLBoundListener listenTo(LLEventPump& pump,
+ ListenMethod method=&Listener::call,
+ const LLEventPump::NameList& after=LLEventPump::empty,
+ const LLEventPump::NameList& before=LLEventPump::empty)
+ {
+ return pump.listen(getName(), boost::bind(method, this, _1), after, before);
+ }
+ /// Both call() and callstop() set mLastEvent. Retrieve it.
+ LLSD getLastEvent() const
+ {
+// std::cout << *this << "::getLastEvent() -> " << mLastEvent << "\n";
+ return mLastEvent;
+ }
+ /// Reset mLastEvent to a known state.
+ void reset(const LLSD& to = LLSD())
+ {
+// std::cout << *this << "::reset(" << to << ")\n";
+ mLastEvent = to;
+ }
+
+private:
+ std::string mName;
+ LLSD mLastEvent;
+};
+
+std::ostream& operator<<(std::ostream& out, const Listener& listener)
+{
+ out << "Listener(" << listener.getName() /* << "@" << &listener */ << ')';
+ return out;
+}
+
+/**
+ * This class tests the relative order in which various listeners on a given
+ * LLEventPump are called. Each listen() call binds a particular string, which
+ * we collect for later examination. The actual event is ignored.
+ */
+struct Collect
+{
+ bool add(const std::string& bound, const LLSD& event)
+ {
+ result.push_back(bound);
+ return false;
+ }
+ void clear() { result.clear(); }
+ typedef std::vector<std::string> StringList;
+ StringList result;
+};
+
+std::ostream& operator<<(std::ostream& out, const Collect::StringList& strings)
+{
+ out << '(';
+ Collect::StringList::const_iterator begin(strings.begin()), end(strings.end());
+ if (begin != end)
+ {
+ out << '"' << *begin << '"';
+ while (++begin != end)
+ {
+ out << ", \"" << *begin << '"';
+ }
+ }
+ out << ')';
+ return out;
+}
+
+#endif /* ! defined(LL_LISTENER_H) */
diff --git a/indra/llcommon/tests/lleventcoro_test.cpp b/indra/llcommon/tests/lleventcoro_test.cpp
new file mode 100644
index 0000000000..3a2cda7735
--- /dev/null
+++ b/indra/llcommon/tests/lleventcoro_test.cpp
@@ -0,0 +1,782 @@
+/**
+ * @file coroutine_test.cpp
+ * @author Nat Goodspeed
+ * @date 2009-04-22
+ * @brief Test for coroutine.
+ *
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ * Copyright (c) 2009, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+/*****************************************************************************/
+// test<1>() is cloned from a Boost.Coroutine example program whose copyright
+// info is reproduced here:
+/*---------------------------------------------------------------------------*/
+// Copyright (c) 2006, Giovanni P. Deretta
+//
+// This code may be used under either of the following two licences:
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE. OF SUCH DAMAGE.
+//
+// Or:
+//
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+/*****************************************************************************/
+
+// On some platforms, Boost.Coroutine must #define magic symbols before
+// #including platform-API headers. Naturally, that's ineffective unless the
+// Boost.Coroutine #include is the *first* #include of the platform header.
+// That means that client code must generally #include Boost.Coroutine headers
+// before anything else.
+#include <boost/coroutine/coroutine.hpp>
+// Normally, lleventcoro.h obviates future.hpp. We only include this because
+// we implement a "by hand" test of future functionality.
+#include <boost/coroutine/future.hpp>
+#include <boost/bind.hpp>
+#include <boost/range.hpp>
+
+#include "linden_common.h"
+
+#include <iostream>
+#include <string>
+
+#include "../test/lltut.h"
+#include "llsd.h"
+#include "llevents.h"
+#include "tests/wrapllerrs.h"
+#include "stringize.h"
+#include "lleventcoro.h"
+#include "../test/debug.h"
+
+/*****************************************************************************
+* from the banana.cpp example program borrowed for test<1>()
+*****************************************************************************/
+namespace coroutines = boost::coroutines;
+using coroutines::coroutine;
+
+template<typename Iter>
+bool match(Iter first, Iter last, std::string match) {
+ std::string::iterator i = match.begin();
+ i != match.end();
+ for(; (first != last) && (i != match.end()); ++i) {
+ if (*first != *i)
+ return false;
+ ++first;
+ }
+ return i == match.end();
+}
+
+template<typename BidirectionalIterator>
+BidirectionalIterator
+match_substring(BidirectionalIterator begin,
+ BidirectionalIterator end,
+ std::string xmatch,
+ BOOST_DEDUCED_TYPENAME coroutine<BidirectionalIterator(void)>::self& self) {
+ BidirectionalIterator begin_ = begin;
+ for(; begin != end; ++begin)
+ if(match(begin, end, xmatch)) {
+ self.yield(begin);
+ }
+ return end;
+}
+
+typedef coroutine<std::string::iterator(void)> match_coroutine_type;
+
+/*****************************************************************************
+* Test helpers
+*****************************************************************************/
+// I suspect this will be typical of coroutines used in Linden software
+typedef boost::coroutines::coroutine<void()> coroutine_type;
+
+/// Simulate an event API whose response is immediate: sent on receipt of the
+/// initial request, rather than after some delay. This is the case that
+/// distinguishes postAndWait() from calling post(), then calling
+/// waitForEventOn().
+class ImmediateAPI
+{
+public:
+ ImmediateAPI():
+ mPump("immediate", true)
+ {
+ mPump.listen("API", boost::bind(&ImmediateAPI::operator(), this, _1));
+ }
+
+ LLEventPump& getPump() { return mPump; }
+
+ // Invoke this with an LLSD map containing:
+ // ["value"]: Integer value. We will reply with ["value"] + 1.
+ // ["reply"]: Name of LLEventPump on which to send success response.
+ // ["error"]: Name of LLEventPump on which to send error response.
+ // ["fail"]: Presence of this key selects ["error"], else ["success"] as
+ // the name of the pump on which to send the response.
+ bool operator()(const LLSD& event) const
+ {
+ LLSD::Integer value(event["value"]);
+ LLSD::String replyPumpName(event.has("fail")? "error" : "reply");
+ LLEventPumps::instance().obtain(event[replyPumpName]).post(value + 1);
+ return false;
+ }
+
+private:
+ LLEventStream mPump;
+};
+
+/*****************************************************************************
+* TUT
+*****************************************************************************/
+namespace tut
+{
+ struct coroutine_data
+ {
+ // Define coroutine bodies as methods here so they can use ensure*()
+
+ void explicit_wait(coroutine_type::self& self)
+ {
+ BEGIN
+ {
+ // ... do whatever preliminary stuff must happen ...
+
+ // declare the future
+ boost::coroutines::future<LLSD> future(self);
+ // tell the future what to wait for
+ LLTempBoundListener connection(
+ LLEventPumps::instance().obtain("source").listen("coro", voidlistener(boost::coroutines::make_callback(future))));
+ ensure("Not yet", ! future);
+ // attempting to dereference ("resolve") the future causes the calling
+ // coroutine to wait for it
+ debug("about to wait");
+ result = *future;
+ ensure("Got it", future);
+ }
+ END
+ }
+
+ void waitForEventOn1(coroutine_type::self& self)
+ {
+ BEGIN
+ {
+ result = waitForEventOn(self, "source");
+ }
+ END
+ }
+
+ void waitForEventOn2(coroutine_type::self& self)
+ {
+ BEGIN
+ {
+ LLEventWithID pair = waitForEventOn(self, "reply", "error");
+ result = pair.first;
+ which = pair.second;
+ debug(STRINGIZE("result = " << result << ", which = " << which));
+ }
+ END
+ }
+
+ void postAndWait1(coroutine_type::self& self)
+ {
+ BEGIN
+ {
+ result = postAndWait(self,
+ LLSD().insert("value", 17), // request event
+ immediateAPI.getPump(), // requestPump
+ "reply1", // replyPump
+ "reply"); // request["reply"] = name
+ }
+ END
+ }
+
+ void postAndWait2(coroutine_type::self& self)
+ {
+ BEGIN
+ {
+ LLEventWithID pair = ::postAndWait2(self,
+ LLSD().insert("value", 18),
+ immediateAPI.getPump(),
+ "reply2",
+ "error2",
+ "reply",
+ "error");
+ result = pair.first;
+ which = pair.second;
+ debug(STRINGIZE("result = " << result << ", which = " << which));
+ }
+ END
+ }
+
+ void postAndWait2_1(coroutine_type::self& self)
+ {
+ BEGIN
+ {
+ LLEventWithID pair = ::postAndWait2(self,
+ LLSD().insert("value", 18).insert("fail", LLSD()),
+ immediateAPI.getPump(),
+ "reply2",
+ "error2",
+ "reply",
+ "error");
+ result = pair.first;
+ which = pair.second;
+ debug(STRINGIZE("result = " << result << ", which = " << which));
+ }
+ END
+ }
+
+ void coroPump(coroutine_type::self& self)
+ {
+ BEGIN
+ {
+ LLCoroEventPump waiter;
+ replyName = waiter.getName();
+ result = waiter.wait(self);
+ }
+ END
+ }
+
+ void coroPumpPost(coroutine_type::self& self)
+ {
+ BEGIN
+ {
+ LLCoroEventPump waiter;
+ result = waiter.postAndWait(self, LLSD().insert("value", 17),
+ immediateAPI.getPump(), "reply");
+ }
+ END
+ }
+
+ void coroPumps(coroutine_type::self& self)
+ {
+ BEGIN
+ {
+ LLCoroEventPumps waiter;
+ replyName = waiter.getName0();
+ errorName = waiter.getName1();
+ LLEventWithID pair(waiter.wait(self));
+ result = pair.first;
+ which = pair.second;
+ }
+ END
+ }
+
+ void coroPumpsNoEx(coroutine_type::self& self)
+ {
+ BEGIN
+ {
+ LLCoroEventPumps waiter;
+ replyName = waiter.getName0();
+ errorName = waiter.getName1();
+ result = waiter.waitWithException(self);
+ }
+ END
+ }
+
+ void coroPumpsEx(coroutine_type::self& self)
+ {
+ BEGIN
+ {
+ LLCoroEventPumps waiter;
+ replyName = waiter.getName0();
+ errorName = waiter.getName1();
+ try
+ {
+ result = waiter.waitWithException(self);
+ debug("no exception");
+ }
+ catch (const LLErrorEvent& e)
+ {
+ debug(STRINGIZE("exception " << e.what()));
+ errordata = e.getData();
+ }
+ }
+ END
+ }
+
+ void coroPumpsNoLog(coroutine_type::self& self)
+ {
+ BEGIN
+ {
+ LLCoroEventPumps waiter;
+ replyName = waiter.getName0();
+ errorName = waiter.getName1();
+ result = waiter.waitWithLog(self);
+ }
+ END
+ }
+
+ void coroPumpsLog(coroutine_type::self& self)
+ {
+ BEGIN
+ {
+ LLCoroEventPumps waiter;
+ replyName = waiter.getName0();
+ errorName = waiter.getName1();
+ WrapLL_ERRS capture;
+ try
+ {
+ result = waiter.waitWithLog(self);
+ debug("no exception");
+ }
+ catch (const WrapLL_ERRS::FatalException& e)
+ {
+ debug(STRINGIZE("exception " << e.what()));
+ threw = e.what();
+ }
+ }
+ END
+ }
+
+ void coroPumpsPost(coroutine_type::self& self)
+ {
+ BEGIN
+ {
+ LLCoroEventPumps waiter;
+ LLEventWithID pair(waiter.postAndWait(self, LLSD().insert("value", 23),
+ immediateAPI.getPump(), "reply", "error"));
+ result = pair.first;
+ which = pair.second;
+ }
+ END
+ }
+
+ void coroPumpsPost_1(coroutine_type::self& self)
+ {
+ BEGIN
+ {
+ LLCoroEventPumps waiter;
+ LLEventWithID pair(
+ waiter.postAndWait(self, LLSD().insert("value", 23).insert("fail", LLSD()),
+ immediateAPI.getPump(), "reply", "error"));
+ result = pair.first;
+ which = pair.second;
+ }
+ END
+ }
+
+ void coroPumpsPostNoEx(coroutine_type::self& self)
+ {
+ BEGIN
+ {
+ LLCoroEventPumps waiter;
+ result = waiter.postAndWaitWithException(self, LLSD().insert("value", 8),
+ immediateAPI.getPump(), "reply", "error");
+ }
+ END
+ }
+
+ void coroPumpsPostEx(coroutine_type::self& self)
+ {
+ BEGIN
+ {
+ LLCoroEventPumps waiter;
+ try
+ {
+ result = waiter.postAndWaitWithException(self,
+ LLSD().insert("value", 9).insert("fail", LLSD()),
+ immediateAPI.getPump(), "reply", "error");
+ debug("no exception");
+ }
+ catch (const LLErrorEvent& e)
+ {
+ debug(STRINGIZE("exception " << e.what()));
+ errordata = e.getData();
+ }
+ }
+ END
+ }
+
+ void coroPumpsPostNoLog(coroutine_type::self& self)
+ {
+ BEGIN
+ {
+ LLCoroEventPumps waiter;
+ result = waiter.postAndWaitWithLog(self, LLSD().insert("value", 30),
+ immediateAPI.getPump(), "reply", "error");
+ }
+ END
+ }
+
+ void coroPumpsPostLog(coroutine_type::self& self)
+ {
+ BEGIN
+ {
+ LLCoroEventPumps waiter;
+ WrapLL_ERRS capture;
+ try
+ {
+ result = waiter.postAndWaitWithLog(self,
+ LLSD().insert("value", 31).insert("fail", LLSD()),
+ immediateAPI.getPump(), "reply", "error");
+ debug("no exception");
+ }
+ catch (const WrapLL_ERRS::FatalException& e)
+ {
+ debug(STRINGIZE("exception " << e.what()));
+ threw = e.what();
+ }
+ }
+ END
+ }
+
+ void ensure_done(coroutine_type& coro)
+ {
+ ensure("coroutine complete", ! coro);
+ }
+
+ ImmediateAPI immediateAPI;
+ std::string replyName, errorName, threw;
+ LLSD result, errordata;
+ int which;
+ };
+ typedef test_group<coroutine_data> coroutine_group;
+ typedef coroutine_group::object object;
+ coroutine_group coroutinegrp("coroutine");
+
+ template<> template<>
+ void object::test<1>()
+ {
+ set_test_name("From banana.cpp example program in Boost.Coroutine distro");
+ std::string buffer = "banananana";
+ std::string match = "nana";
+ std::string::iterator begin = buffer.begin();
+ std::string::iterator end = buffer.end();
+
+#if defined(BOOST_CORO_POSIX_IMPL)
+// std::cout << "Using Boost.Coroutine " << BOOST_CORO_POSIX_IMPL << '\n';
+#else
+// std::cout << "Using non-Posix Boost.Coroutine implementation" << std::endl;
+#endif
+
+ typedef std::string::iterator signature(std::string::iterator,
+ std::string::iterator,
+ std::string,
+ match_coroutine_type::self&);
+
+ coroutine<std::string::iterator(void)> matcher
+ (boost::bind(static_cast<signature*>(match_substring),
+ begin,
+ end,
+ match,
+ _1));
+
+ std::string::iterator i = matcher();
+/*==========================================================================*|
+ while(matcher && i != buffer.end()) {
+ std::cout <<"Match at: "<< std::distance(buffer.begin(), i)<<'\n';
+ i = matcher();
+ }
+|*==========================================================================*/
+ size_t matches[] = { 2, 4, 6 };
+ for (size_t *mi(boost::begin(matches)), *mend(boost::end(matches));
+ mi != mend; ++mi, i = matcher())
+ {
+ ensure("more", matcher);
+ ensure("found", i != buffer.end());
+ ensure_equals("value", std::distance(buffer.begin(), i), *mi);
+ }
+ ensure("done", ! matcher);
+ }
+
+ template<> template<>
+ void object::test<2>()
+ {
+ set_test_name("explicit_wait");
+ DEBUG;
+
+ // Construct the coroutine instance that will run explicit_wait.
+ // Pass the ctor a callable that accepts the coroutine_type::self
+ // param passed by the library.
+ coroutine_type coro(boost::bind(&coroutine_data::explicit_wait, this, _1));
+ // Start the coroutine
+ coro(std::nothrow);
+ // When the coroutine waits for the event pump, it returns here.
+ debug("about to send");
+ // Satisfy the wait.
+ LLEventPumps::instance().obtain("source").post("received");
+ // Now wait for the coroutine to complete.
+ ensure_done(coro);
+ // ensure the coroutine ran and woke up again with the intended result
+ ensure_equals(result.asString(), "received");
+ }
+
+ template<> template<>
+ void object::test<3>()
+ {
+ set_test_name("waitForEventOn1");
+ DEBUG;
+ coroutine_type coro(boost::bind(&coroutine_data::waitForEventOn1, this, _1));
+ coro(std::nothrow);
+ debug("about to send");
+ LLEventPumps::instance().obtain("source").post("received");
+ debug("back from send");
+ ensure_done(coro);
+ ensure_equals(result.asString(), "received");
+ }
+
+ template<> template<>
+ void object::test<4>()
+ {
+ set_test_name("waitForEventOn2 reply");
+ {
+ DEBUG;
+ coroutine_type coro(boost::bind(&coroutine_data::waitForEventOn2, this, _1));
+ coro(std::nothrow);
+ debug("about to send");
+ LLEventPumps::instance().obtain("reply").post("received");
+ debug("back from send");
+ ensure_done(coro);
+ }
+ ensure_equals(result.asString(), "received");
+ ensure_equals("which pump", which, 0);
+ }
+
+ template<> template<>
+ void object::test<5>()
+ {
+ set_test_name("waitForEventOn2 error");
+ DEBUG;
+ coroutine_type coro(boost::bind(&coroutine_data::waitForEventOn2, this, _1));
+ coro(std::nothrow);
+ debug("about to send");
+ LLEventPumps::instance().obtain("error").post("badness");
+ debug("back from send");
+ ensure_done(coro);
+ ensure_equals(result.asString(), "badness");
+ ensure_equals("which pump", which, 1);
+ }
+
+ template<> template<>
+ void object::test<6>()
+ {
+ set_test_name("coroPump");
+ DEBUG;
+ coroutine_type coro(boost::bind(&coroutine_data::coroPump, this, _1));
+ coro(std::nothrow);
+ debug("about to send");
+ LLEventPumps::instance().obtain(replyName).post("received");
+ debug("back from send");
+ ensure_done(coro);
+ ensure_equals(result.asString(), "received");
+ }
+
+ template<> template<>
+ void object::test<7>()
+ {
+ set_test_name("coroPumps reply");
+ DEBUG;
+ coroutine_type coro(boost::bind(&coroutine_data::coroPumps, this, _1));
+ coro(std::nothrow);
+ debug("about to send");
+ LLEventPumps::instance().obtain(replyName).post("received");
+ debug("back from send");
+ ensure_done(coro);
+ ensure_equals(result.asString(), "received");
+ ensure_equals("which pump", which, 0);
+ }
+
+ template<> template<>
+ void object::test<8>()
+ {
+ set_test_name("coroPumps error");
+ DEBUG;
+ coroutine_type coro(boost::bind(&coroutine_data::coroPumps, this, _1));
+ coro(std::nothrow);
+ debug("about to send");
+ LLEventPumps::instance().obtain(errorName).post("badness");
+ debug("back from send");
+ ensure_done(coro);
+ ensure_equals(result.asString(), "badness");
+ ensure_equals("which pump", which, 1);
+ }
+
+ template<> template<>
+ void object::test<9>()
+ {
+ set_test_name("coroPumpsNoEx");
+ DEBUG;
+ coroutine_type coro(boost::bind(&coroutine_data::coroPumpsNoEx, this, _1));
+ coro(std::nothrow);
+ debug("about to send");
+ LLEventPumps::instance().obtain(replyName).post("received");
+ debug("back from send");
+ ensure_done(coro);
+ ensure_equals(result.asString(), "received");
+ }
+
+ template<> template<>
+ void object::test<10>()
+ {
+ set_test_name("coroPumpsEx");
+ DEBUG;
+ coroutine_type coro(boost::bind(&coroutine_data::coroPumpsEx, this, _1));
+ coro(std::nothrow);
+ debug("about to send");
+ LLEventPumps::instance().obtain(errorName).post("badness");
+ debug("back from send");
+ ensure_done(coro);
+ ensure("no result", result.isUndefined());
+ ensure_equals("got error", errordata.asString(), "badness");
+ }
+
+ template<> template<>
+ void object::test<11>()
+ {
+ set_test_name("coroPumpsNoLog");
+ DEBUG;
+ coroutine_type coro(boost::bind(&coroutine_data::coroPumpsNoLog, this, _1));
+ coro(std::nothrow);
+ debug("about to send");
+ LLEventPumps::instance().obtain(replyName).post("received");
+ debug("back from send");
+ ensure_done(coro);
+ ensure_equals(result.asString(), "received");
+ }
+
+ template<> template<>
+ void object::test<12>()
+ {
+ set_test_name("coroPumpsLog");
+ DEBUG;
+ coroutine_type coro(boost::bind(&coroutine_data::coroPumpsLog, this, _1));
+ coro(std::nothrow);
+ debug("about to send");
+ LLEventPumps::instance().obtain(errorName).post("badness");
+ debug("back from send");
+ ensure_done(coro);
+ ensure("no result", result.isUndefined());
+ ensure_contains("got error", threw, "badness");
+ }
+
+ template<> template<>
+ void object::test<13>()
+ {
+ set_test_name("postAndWait1");
+ DEBUG;
+ coroutine_type coro(boost::bind(&coroutine_data::postAndWait1, this, _1));
+ coro(std::nothrow);
+ ensure_done(coro);
+ ensure_equals(result.asInteger(), 18);
+ }
+
+ template<> template<>
+ void object::test<14>()
+ {
+ set_test_name("postAndWait2");
+ DEBUG;
+ coroutine_type coro(boost::bind(&coroutine_data::postAndWait2, this, _1));
+ coro(std::nothrow);
+ ensure_done(coro);
+ ensure_equals(result.asInteger(), 19);
+ ensure_equals(which, 0);
+ }
+
+ template<> template<>
+ void object::test<15>()
+ {
+ set_test_name("postAndWait2_1");
+ DEBUG;
+ coroutine_type coro(boost::bind(&coroutine_data::postAndWait2_1, this, _1));
+ coro(std::nothrow);
+ ensure_done(coro);
+ ensure_equals(result.asInteger(), 19);
+ ensure_equals(which, 1);
+ }
+
+ template<> template<>
+ void object::test<16>()
+ {
+ set_test_name("coroPumpPost");
+ DEBUG;
+ coroutine_type coro(boost::bind(&coroutine_data::coroPumpPost, this, _1));
+ coro(std::nothrow);
+ ensure_done(coro);
+ ensure_equals(result.asInteger(), 18);
+ }
+
+ template<> template<>
+ void object::test<17>()
+ {
+ set_test_name("coroPumpsPost reply");
+ DEBUG;
+ coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPost, this, _1));
+ coro(std::nothrow);
+ ensure_done(coro);
+ ensure_equals(result.asInteger(), 24);
+ ensure_equals("which pump", which, 0);
+ }
+
+ template<> template<>
+ void object::test<18>()
+ {
+ set_test_name("coroPumpsPost error");
+ DEBUG;
+ coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPost_1, this, _1));
+ coro(std::nothrow);
+ ensure_done(coro);
+ ensure_equals(result.asInteger(), 24);
+ ensure_equals("which pump", which, 1);
+ }
+
+ template<> template<>
+ void object::test<19>()
+ {
+ set_test_name("coroPumpsPostNoEx");
+ DEBUG;
+ coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPostNoEx, this, _1));
+ coro(std::nothrow);
+ ensure_done(coro);
+ ensure_equals(result.asInteger(), 9);
+ }
+
+ template<> template<>
+ void object::test<20>()
+ {
+ set_test_name("coroPumpsPostEx");
+ DEBUG;
+ coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPostEx, this, _1));
+ coro(std::nothrow);
+ ensure_done(coro);
+ ensure("no result", result.isUndefined());
+ ensure_equals("got error", errordata.asInteger(), 10);
+ }
+
+ template<> template<>
+ void object::test<21>()
+ {
+ set_test_name("coroPumpsPostNoLog");
+ DEBUG;
+ coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPostNoLog, this, _1));
+ coro(std::nothrow);
+ ensure_done(coro);
+ ensure_equals(result.asInteger(), 31);
+ }
+
+ template<> template<>
+ void object::test<22>()
+ {
+ set_test_name("coroPumpsPostLog");
+ DEBUG;
+ coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPostLog, this, _1));
+ coro(std::nothrow);
+ ensure_done(coro);
+ ensure("no result", result.isUndefined());
+ ensure_contains("got error", threw, "32");
+ }
+} // namespace tut
diff --git a/indra/llcommon/tests/lleventfilter_test.cpp b/indra/llcommon/tests/lleventfilter_test.cpp
new file mode 100644
index 0000000000..28b909298e
--- /dev/null
+++ b/indra/llcommon/tests/lleventfilter_test.cpp
@@ -0,0 +1,276 @@
+/**
+ * @file lleventfilter_test.cpp
+ * @author Nat Goodspeed
+ * @date 2009-03-06
+ * @brief Test for lleventfilter.
+ *
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ * Copyright (c) 2009, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+#include "lleventfilter.h"
+// STL headers
+// std headers
+// external library headers
+// other Linden headers
+#include "../test/lltut.h"
+#include "stringize.h"
+#include "listener.h"
+#include "tests/wrapllerrs.h"
+
+/*****************************************************************************
+* Test classes
+*****************************************************************************/
+// Strictly speaking, we're testing LLEventTimeoutBase rather than the
+// production LLEventTimeout (using LLTimer) because we don't want every test
+// run to pause for some number of seconds until we reach a real timeout. But
+// as we've carefully put all functionality except actual LLTimer calls into
+// LLEventTimeoutBase, that should suffice. We're not not not trying to test
+// LLTimer here.
+class TestEventTimeout: public LLEventTimeoutBase
+{
+public:
+ TestEventTimeout():
+ mElapsed(true)
+ {}
+ TestEventTimeout(LLEventPump& source):
+ LLEventTimeoutBase(source),
+ mElapsed(true)
+ {}
+
+ // test hook
+ void forceTimeout(bool timeout=true) { mElapsed = timeout; }
+
+protected:
+ virtual void setCountdown(F32 seconds) { mElapsed = false; }
+ virtual bool countdownElapsed() const { return mElapsed; }
+
+private:
+ bool mElapsed;
+};
+
+/*****************************************************************************
+* TUT
+*****************************************************************************/
+namespace tut
+{
+ struct filter_data
+ {
+ // The resemblance between this test data and that in llevents_tut.cpp
+ // is not coincidental.
+ filter_data():
+ pumps(LLEventPumps::instance()),
+ mainloop(pumps.obtain("mainloop")),
+ listener0("first"),
+ listener1("second")
+ {}
+ LLEventPumps& pumps;
+ LLEventPump& mainloop;
+ Listener listener0;
+ Listener listener1;
+
+ void check_listener(const std::string& desc, const Listener& listener, const LLSD& got)
+ {
+ ensure_equals(STRINGIZE(listener << ' ' << desc),
+ listener.getLastEvent(), got);
+ }
+ };
+ typedef test_group<filter_data> filter_group;
+ typedef filter_group::object filter_object;
+ filter_group filtergrp("lleventfilter");
+
+ template<> template<>
+ void filter_object::test<1>()
+ {
+ set_test_name("LLEventMatching");
+ LLEventPump& driver(pumps.obtain("driver"));
+ listener0.reset(0);
+ // Listener isn't derived from LLEventTrackable specifically to test
+ // various connection-management mechanisms. But that means we have a
+ // couple of transient Listener objects, one of which is listening to
+ // a persistent LLEventPump. Capture those connections in local
+ // LLTempBoundListener instances so they'll disconnect
+ // on destruction.
+ LLTempBoundListener temp1(
+ listener0.listenTo(driver));
+ // Construct a pattern LLSD: desired Event must have a key "foo"
+ // containing string "bar"
+ LLEventMatching filter(driver, LLSD().insert("foo", "bar"));
+ listener1.reset(0);
+ LLTempBoundListener temp2(
+ listener1.listenTo(filter));
+ driver.post(1);
+ check_listener("direct", listener0, LLSD(1));
+ check_listener("filtered", listener1, LLSD(0));
+ // Okay, construct an LLSD map matching the pattern
+ LLSD data;
+ data["foo"] = "bar";
+ data["random"] = 17;
+ driver.post(data);
+ check_listener("direct", listener0, data);
+ check_listener("filtered", listener1, data);
+ }
+
+ template<> template<>
+ void filter_object::test<2>()
+ {
+ set_test_name("LLEventTimeout::actionAfter()");
+ LLEventPump& driver(pumps.obtain("driver"));
+ TestEventTimeout filter(driver);
+ listener0.reset(0);
+ LLTempBoundListener temp1(
+ listener0.listenTo(filter));
+ // Use listener1.call() as the Action for actionAfter(), since it
+ // already provides a way to sense the call
+ listener1.reset(0);
+ // driver --> filter --> listener0
+ filter.actionAfter(20,
+ boost::bind(&Listener::call, boost::ref(listener1), LLSD("timeout")));
+ // Okay, (fake) timer is ticking. 'filter' can only sense the timer
+ // when we pump mainloop. Do that right now to take the logic path
+ // before either the anticipated event arrives or the timer expires.
+ mainloop.post(17);
+ check_listener("no timeout 1", listener1, LLSD(0));
+ // Expected event arrives...
+ driver.post(1);
+ check_listener("event passed thru", listener0, LLSD(1));
+ // Should have canceled the timer. Verify that by asserting that the
+ // time has expired, then pumping mainloop again.
+ filter.forceTimeout();
+ mainloop.post(17);
+ check_listener("no timeout 2", listener1, LLSD(0));
+ // Verify chained actionAfter() calls, that is, that a second
+ // actionAfter() resets the timer established by the first
+ // actionAfter().
+ filter.actionAfter(20,
+ boost::bind(&Listener::call, boost::ref(listener1), LLSD("timeout")));
+ // Since our TestEventTimeout class isn't actually manipulating time
+ // (quantities of seconds), only a bool "elapsed" flag, sense that by
+ // forcing the flag between actionAfter() calls.
+ filter.forceTimeout();
+ // Pumping mainloop here would result in a timeout (as we'll verify
+ // below). This state simulates a ticking timer that has not yet timed
+ // out. But now, before a mainloop event lets 'filter' recognize
+ // timeout on the previous actionAfter() call, pretend we're pushing
+ // that timeout farther into the future.
+ filter.actionAfter(20,
+ boost::bind(&Listener::call, boost::ref(listener1), LLSD("timeout")));
+ // Look ma, no timeout!
+ mainloop.post(17);
+ check_listener("no timeout 3", listener1, LLSD(0));
+ // Now let the updated actionAfter() timer expire.
+ filter.forceTimeout();
+ // Notice the timeout.
+ mainloop.post(17);
+ check_listener("timeout", listener1, LLSD("timeout"));
+ // Timing out cancels the timer. Verify that.
+ listener1.reset(0);
+ filter.forceTimeout();
+ mainloop.post(17);
+ check_listener("no timeout 4", listener1, LLSD(0));
+ // Reset the timer and then cancel() it.
+ filter.actionAfter(20,
+ boost::bind(&Listener::call, boost::ref(listener1), LLSD("timeout")));
+ // neither expired nor satisified
+ mainloop.post(17);
+ check_listener("no timeout 5", listener1, LLSD(0));
+ // cancel
+ filter.cancel();
+ // timeout!
+ filter.forceTimeout();
+ mainloop.post(17);
+ check_listener("no timeout 6", listener1, LLSD(0));
+ }
+
+ template<> template<>
+ void filter_object::test<3>()
+ {
+ set_test_name("LLEventTimeout::eventAfter()");
+ LLEventPump& driver(pumps.obtain("driver"));
+ TestEventTimeout filter(driver);
+ listener0.reset(0);
+ LLTempBoundListener temp1(
+ listener0.listenTo(filter));
+ filter.eventAfter(20, LLSD("timeout"));
+ // Okay, (fake) timer is ticking. 'filter' can only sense the timer
+ // when we pump mainloop. Do that right now to take the logic path
+ // before either the anticipated event arrives or the timer expires.
+ mainloop.post(17);
+ check_listener("no timeout 1", listener0, LLSD(0));
+ // Expected event arrives...
+ driver.post(1);
+ check_listener("event passed thru", listener0, LLSD(1));
+ // Should have canceled the timer. Verify that by asserting that the
+ // time has expired, then pumping mainloop again.
+ filter.forceTimeout();
+ mainloop.post(17);
+ check_listener("no timeout 2", listener0, LLSD(1));
+ // Set timer again.
+ filter.eventAfter(20, LLSD("timeout"));
+ // Now let the timer expire.
+ filter.forceTimeout();
+ // Notice the timeout.
+ mainloop.post(17);
+ check_listener("timeout", listener0, LLSD("timeout"));
+ // Timing out cancels the timer. Verify that.
+ listener0.reset(0);
+ filter.forceTimeout();
+ mainloop.post(17);
+ check_listener("no timeout 3", listener0, LLSD(0));
+ }
+
+ template<> template<>
+ void filter_object::test<4>()
+ {
+ set_test_name("LLEventTimeout::errorAfter()");
+ WrapLL_ERRS capture;
+ LLEventPump& driver(pumps.obtain("driver"));
+ TestEventTimeout filter(driver);
+ listener0.reset(0);
+ LLTempBoundListener temp1(
+ listener0.listenTo(filter));
+ filter.errorAfter(20, "timeout");
+ // Okay, (fake) timer is ticking. 'filter' can only sense the timer
+ // when we pump mainloop. Do that right now to take the logic path
+ // before either the anticipated event arrives or the timer expires.
+ mainloop.post(17);
+ check_listener("no timeout 1", listener0, LLSD(0));
+ // Expected event arrives...
+ driver.post(1);
+ check_listener("event passed thru", listener0, LLSD(1));
+ // Should have canceled the timer. Verify that by asserting that the
+ // time has expired, then pumping mainloop again.
+ filter.forceTimeout();
+ mainloop.post(17);
+ check_listener("no timeout 2", listener0, LLSD(1));
+ // Set timer again.
+ filter.errorAfter(20, "timeout");
+ // Now let the timer expire.
+ filter.forceTimeout();
+ // Notice the timeout.
+ std::string threw;
+ try
+ {
+ mainloop.post(17);
+ }
+ catch (const WrapLL_ERRS::FatalException& e)
+ {
+ threw = e.what();
+ }
+ ensure_contains("errorAfter() timeout exception", threw, "timeout");
+ // Timing out cancels the timer. Verify that.
+ listener0.reset(0);
+ filter.forceTimeout();
+ mainloop.post(17);
+ check_listener("no timeout 3", listener0, LLSD(0));
+ }
+} // namespace tut
+
+/*****************************************************************************
+* Link dependencies
+*****************************************************************************/
+#include "llsdutil.cpp"
diff --git a/indra/llcommon/tests/llsdserialize_test.cpp b/indra/llcommon/tests/llsdserialize_test.cpp
index f13c69f1e3..6ab48ec34a 100644
--- a/indra/llcommon/tests/llsdserialize_test.cpp
+++ b/indra/llcommon/tests/llsdserialize_test.cpp
@@ -48,6 +48,18 @@
typedef U32 uint32_t;
#endif
+std::vector<U8> string_to_vector(std::string str)
+{
+ // bc LLSD can't...
+ size_t len = (size_t)str.length();
+ std::vector<U8> v(len);
+ for (size_t i = 0; i < len ; i++)
+ {
+ v[i] = str[i];
+ }
+ return v;
+}
+
namespace tut
{
struct sd_xml_data
@@ -107,7 +119,16 @@ namespace tut
expected = "<llsd><date>2006-04-24T16:11:33Z</date></llsd>\n";
xml_test("date", expected);
- // *FIX: test binary
+ // Generated by: echo -n 'hello' | openssl enc -e -base64
+ std::vector<U8> hello;
+ hello.push_back('h');
+ hello.push_back('e');
+ hello.push_back('l');
+ hello.push_back('l');
+ hello.push_back('o');
+ mSD = hello;
+ expected = "<llsd><binary encoding=\"base64\">aGVsbG8=</binary></llsd>\n";
+ xml_test("binary", expected);
}
template<> template<>
@@ -199,6 +220,21 @@ namespace tut
xml_test("2 element map", expected);
}
+ template<> template<>
+ void sd_xml_object::test<6>()
+ {
+ // tests with binary
+ std::string expected;
+
+ // Generated by: echo -n 'hello' | openssl enc -e -base64
+ mSD = string_to_vector("hello");
+ expected = "<llsd><binary encoding=\"base64\">aGVsbG8=</binary></llsd>\n";
+ xml_test("binary", expected);
+
+ mSD = string_to_vector("6|6|asdfhappybox|60e44ec5-305c-43c2-9a19-b4b89b1ae2a6|60e44ec5-305c-43c2-9a19-b4b89b1ae2a6|60e44ec5-305c-43c2-9a19-b4b89b1ae2a6|00000000-0000-0000-0000-000000000000|7fffffff|7fffffff|0|0|82000|450fe394-2904-c9ad-214c-a07eb7feec29|(No Description)|0|10|0");
+ expected = "<llsd><binary encoding=\"base64\">Nnw2fGFzZGZoYXBweWJveHw2MGU0NGVjNS0zMDVjLTQzYzItOWExOS1iNGI4OWIxYWUyYTZ8NjBlNDRlYzUtMzA1Yy00M2MyLTlhMTktYjRiODliMWFlMmE2fDYwZTQ0ZWM1LTMwNWMtNDNjMi05YTE5LWI0Yjg5YjFhZTJhNnwwMDAwMDAwMC0wMDAwLTAwMDAtMDAwMC0wMDAwMDAwMDAwMDB8N2ZmZmZmZmZ8N2ZmZmZmZmZ8MHwwfDgyMDAwfDQ1MGZlMzk0LTI5MDQtYzlhZC0yMTRjLWEwN2ViN2ZlZWMyOXwoTm8gRGVzY3JpcHRpb24pfDB8MTB8MA==</binary></llsd>\n";
+ xml_test("binary", expected);
+ }
class TestLLSDSerializeData
{
@@ -637,6 +673,42 @@ namespace tut
v.size() + 1);
}
+ template<> template<>
+ void TestLLSDXMLParsingObject::test<4>()
+ {
+ // test handling of binary object in XML
+ std::string xml;
+ LLSD expected;
+
+ // Generated by: echo -n 'hello' | openssl enc -e -base64
+ expected = string_to_vector("hello");
+ xml = "<llsd><binary encoding=\"base64\">aGVsbG8=</binary></llsd>\n";
+ ensureParse(
+ "the word 'hello' packed in binary encoded base64",
+ xml,
+ expected,
+ 1);
+
+ expected = string_to_vector("6|6|asdfhappybox|60e44ec5-305c-43c2-9a19-b4b89b1ae2a6|60e44ec5-305c-43c2-9a19-b4b89b1ae2a6|60e44ec5-305c-43c2-9a19-b4b89b1ae2a6|00000000-0000-0000-0000-000000000000|7fffffff|7fffffff|0|0|82000|450fe394-2904-c9ad-214c-a07eb7feec29|(No Description)|0|10|0");
+ xml = "<llsd><binary encoding=\"base64\">Nnw2fGFzZGZoYXBweWJveHw2MGU0NGVjNS0zMDVjLTQzYzItOWExOS1iNGI4OWIxYWUyYTZ8NjBlNDRlYzUtMzA1Yy00M2MyLTlhMTktYjRiODliMWFlMmE2fDYwZTQ0ZWM1LTMwNWMtNDNjMi05YTE5LWI0Yjg5YjFhZTJhNnwwMDAwMDAwMC0wMDAwLTAwMDAtMDAwMC0wMDAwMDAwMDAwMDB8N2ZmZmZmZmZ8N2ZmZmZmZmZ8MHwwfDgyMDAwfDQ1MGZlMzk0LTI5MDQtYzlhZC0yMTRjLWEwN2ViN2ZlZWMyOXwoTm8gRGVzY3JpcHRpb24pfDB8MTB8MA==</binary></llsd>\n";
+ ensureParse(
+ "a common binary blob for object -> agent offline inv transfer",
+ xml,
+ expected,
+ 1);
+
+ expected = string_to_vector("6|6|asdfhappybox|60e44ec5-305c-43c2-9a19-b4b89b1ae2a6|60e44ec5-305c-43c2-9a19-b4b89b1ae2a6|60e44ec5-305c-43c2-9a19-b4b89b1ae2a6|00000000-0000-0000-0000-000000000000|7fffffff|7fffffff|0|0|82000|450fe394-2904-c9ad-214c-a07eb7feec29|(No Description)|0|10|0");
+ xml = "<llsd><binary encoding=\"base64\">Nnw2fGFzZGZoYXBweWJveHw2MGU0NGVjNS0zMDVjLTQzYzItOWExOS1iNGI4OWIxYWUyYTZ8NjBl\n";
+ xml += "NDRlYzUtMzA1Yy00M2MyLTlhMTktYjRiODliMWFlMmE2fDYwZTQ0ZWM1LTMwNWMtNDNjMi05YTE5\n";
+ xml += "LWI0Yjg5YjFhZTJhNnwwMDAwMDAwMC0wMDAwLTAwMDAtMDAwMC0wMDAwMDAwMDAwMDB8N2ZmZmZm\n";
+ xml += "ZmZ8N2ZmZmZmZmZ8MHwwfDgyMDAwfDQ1MGZlMzk0LTI5MDQtYzlhZC0yMTRjLWEwN2ViN2ZlZWMy\n";
+ xml += "OXwoTm8gRGVzY3JpcHRpb24pfDB8MTB8MA==</binary></llsd>\n";
+ ensureParse(
+ "a common binary blob for object -> agent offline inv transfer",
+ xml,
+ expected,
+ 1);
+ }
/*
TODO:
test XML parsing
diff --git a/indra/llcommon/tests/llstring_test.cpp b/indra/llcommon/tests/llstring_test.cpp
index 6a2ebc61f5..beba55416a 100644
--- a/indra/llcommon/tests/llstring_test.cpp
+++ b/indra/llcommon/tests/llstring_test.cpp
@@ -32,6 +32,7 @@
* $/LicenseInfo$
*/
+#include "linden_common.h"
#include "../test/lltut.h"
#include "../llstring.h"
diff --git a/indra/llcommon/tests/wrapllerrs.h b/indra/llcommon/tests/wrapllerrs.h
new file mode 100644
index 0000000000..1001ebc466
--- /dev/null
+++ b/indra/llcommon/tests/wrapllerrs.h
@@ -0,0 +1,56 @@
+/**
+ * @file wrapllerrs.h
+ * @author Nat Goodspeed
+ * @date 2009-03-11
+ * @brief Define a class useful for unit tests that engage llerrs (LL_ERRS) functionality
+ *
+ * $LicenseInfo:firstyear=2009&license=viewergpl$
+ * Copyright (c) 2009, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#if ! defined(LL_WRAPLLERRS_H)
+#define LL_WRAPLLERRS_H
+
+#include "llerrorcontrol.h"
+
+struct WrapLL_ERRS
+{
+ WrapLL_ERRS():
+ // Resetting Settings discards the default Recorder that writes to
+ // stderr. Otherwise, expected llerrs (LL_ERRS) messages clutter the
+ // console output of successful tests, potentially confusing things.
+ mPriorErrorSettings(LLError::saveAndResetSettings()),
+ // Save shutdown function called by LL_ERRS
+ mPriorFatal(LLError::getFatalFunction())
+ {
+ // Make LL_ERRS call our own operator() method
+ LLError::setFatalFunction(boost::bind(&WrapLL_ERRS::operator(), this, _1));
+ }
+
+ ~WrapLL_ERRS()
+ {
+ LLError::setFatalFunction(mPriorFatal);
+ LLError::restoreSettings(mPriorErrorSettings);
+ }
+
+ struct FatalException: public std::runtime_error
+ {
+ FatalException(const std::string& what): std::runtime_error(what) {}
+ };
+
+ void operator()(const std::string& message)
+ {
+ // Save message for later in case consumer wants to sense the result directly
+ error = message;
+ // Also throw an appropriate exception since calling code is likely to
+ // assume that control won't continue beyond LL_ERRS.
+ throw FatalException(message);
+ }
+
+ std::string error;
+ LLError::Settings* mPriorErrorSettings;
+ LLError::FatalFunction mPriorFatal;
+};
+
+#endif /* ! defined(LL_WRAPLLERRS_H) */
diff --git a/indra/llcommon/timing.h b/indra/llcommon/timing.h
index 2b9f60adad..140ce1fcaa 100644
--- a/indra/llcommon/timing.h
+++ b/indra/llcommon/timing.h
@@ -43,7 +43,6 @@ const F32 SEC_TO_MICROSEC = 1000000.f;
const U64 SEC_TO_MICROSEC_U64 = 1000000;
const U32 SEC_PER_DAY = 86400;
-// This is just a stub, implementation in lltimer.cpp. This file will be deprecated in the future.
-U64 totalTime(); // Returns current system time in microseconds
+// functionality has been moved lltimer.{cpp,h}. This file will be deprecated in the future.
#endif
diff --git a/indra/llcommon/u64.h b/indra/llcommon/u64.h
index 09a6b3e18d..eb51131e94 100644
--- a/indra/llcommon/u64.h
+++ b/indra/llcommon/u64.h
@@ -39,14 +39,14 @@
* @param str The string to parse.
* @return Returns the first U64 value found in the string or 0 on failure.
*/
-U64 str_to_U64(const std::string& str);
+LL_COMMON_API U64 str_to_U64(const std::string& str);
/**
* @brief Given a U64 value, return a printable representation.
* @param value The U64 to turn into a printable character array.
* @return Returns the result string.
*/
-std::string U64_to_str(U64 value);
+LL_COMMON_API std::string U64_to_str(U64 value);
/**
* @brief Given a U64 value, return a printable representation.
@@ -65,16 +65,16 @@ std::string U64_to_str(U64 value);
* @param result_size The size of the buffer allocated. Use U64_BUF.
* @return Returns the result pointer.
*/
-char* U64_to_str(U64 value, char* result, S32 result_size);
+LL_COMMON_API char* U64_to_str(U64 value, char* result, S32 result_size);
/**
* @brief Convert a U64 to the closest F64 value.
*/
-F64 U64_to_F64(const U64 value);
+LL_COMMON_API F64 U64_to_F64(const U64 value);
/**
* @brief Helper function to wrap strtoull() which is not available on windows.
*/
-U64 llstrtou64(const char* str, char** end, S32 base);
+LL_COMMON_API U64 llstrtou64(const char* str, char** end, S32 base);
#endif