diff options
Diffstat (limited to 'indra/llcommon')
283 files changed, 48074 insertions, 48051 deletions
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 50079abb96..95e991c246 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -3,7 +3,6 @@ project(llcommon) include(00-Common) -include(ICU4C) include(LLCommon) include(bugsplat) include(Linking) @@ -282,7 +281,6 @@ target_link_libraries( ll::uriparser ll::oslibraries ll::tracy - ll::icu4c ) target_include_directories(llcommon INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/indra/llcommon/StackWalker.h b/indra/llcommon/StackWalker.h index 3667f59b38..28b10950e0 100644 --- a/indra/llcommon/StackWalker.h +++ b/indra/llcommon/StackWalker.h @@ -1,226 +1,226 @@ -/********************************************************************** - * - * StackWalker.h - * - * - * - * $LicenseInfo:firstyear=2016&license=bsd$ - * - * Linden notes: Small modifications from the original source at https://stackwalker.codeplex.com/ - * - * LICENSE (http://www.opensource.org/licenses/bsd-license.php) - * - * Copyright (c) 2005-2009, Jochen Kalmbach - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * Neither the name of Jochen Kalmbach nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * **********************************************************************/ - -#if LL_WINDOWS - -// #pragma once is supported starting with _MCS_VER 1000, -// so we need not to check the version (because we only support _MSC_VER >= 1100)! -#pragma once - -#include <windows.h> - -// special defines for VC5/6 (if no actual PSDK is installed): -#if _MSC_VER < 1300 -typedef unsigned __int64 DWORD64, *PDWORD64; -#if defined(_WIN64) -typedef unsigned __int64 SIZE_T, *PSIZE_T; -#else -typedef unsigned long SIZE_T, *PSIZE_T; -#endif -#endif // _MSC_VER < 1300 - -class StackWalkerInternal; // forward -class StackWalker -{ -public: - typedef enum StackWalkOptions - { - // No addition info will be retrived - // (only the address is available) - RetrieveNone = 0, - - // Try to get the symbol-name - RetrieveSymbol = 1, - - // Try to get the line for this symbol - RetrieveLine = 2, - - // Try to retrieve the module-infos - RetrieveModuleInfo = 4, - - // Also retrieve the version for the DLL/EXE - RetrieveFileVersion = 8, - - // Contains all the abouve - RetrieveVerbose = 0xF, - - // Generate a "good" symbol-search-path - SymBuildPath = 0x10, - - // Also use the public Microsoft-Symbol-Server - SymUseSymSrv = 0x20, - - // Contains all the abouve "Sym"-options - SymAll = 0x30, - - // Contains all options (default) - OptionsAll = 0x3F - } StackWalkOptions; - - StackWalker( - bool verbose = true, - int options = OptionsAll, // 'int' is by design, to combine the enum-flags - LPCSTR szSymPath = NULL, - DWORD dwProcessId = GetCurrentProcessId(), - HANDLE hProcess = GetCurrentProcess() - ); - StackWalker(DWORD dwProcessId, HANDLE hProcess); - virtual ~StackWalker(); - - typedef BOOL (__stdcall *PReadProcessMemoryRoutine)( - HANDLE hProcess, - DWORD64 qwBaseAddress, - PVOID lpBuffer, - DWORD nSize, - LPDWORD lpNumberOfBytesRead, - LPVOID pUserData // optional data, which was passed in "ShowCallstack" - ); - - bool LoadModules(); - - bool ShowCallstack( - bool verbose, - HANDLE hThread = GetCurrentThread(), - const CONTEXT *context = NULL, - PReadProcessMemoryRoutine readMemoryFunction = NULL, - LPVOID pUserData = NULL // optional to identify some data in the 'readMemoryFunction'-callback - ); - -#if _MSC_VER >= 1300 -// due to some reasons, the "STACKWALK_MAX_NAMELEN" must be declared as "public" -// in older compilers in order to use it... starting with VC7 we can declare it as "protected" -protected: -#endif - enum { STACKWALK_MAX_NAMELEN = 4096 }; // max name length for found symbols - -protected: - // Entry for each Callstack-Entry - typedef struct CallstackEntry - { - DWORD64 offset; // if 0, we have no valid entry - CHAR name[STACKWALK_MAX_NAMELEN]; - CHAR undName[STACKWALK_MAX_NAMELEN]; - CHAR undFullName[STACKWALK_MAX_NAMELEN]; - DWORD64 offsetFromSmybol; - DWORD offsetFromLine; - DWORD lineNumber; - CHAR lineFileName[STACKWALK_MAX_NAMELEN]; - DWORD symType; - LPCSTR symTypeString; - CHAR moduleName[STACKWALK_MAX_NAMELEN]; - DWORD64 baseOfImage; - CHAR loadedImageName[STACKWALK_MAX_NAMELEN]; - } CallstackEntry; - - enum CallstackEntryType {firstEntry, nextEntry, lastEntry}; - - virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName); - virtual void OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion); - virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry); - virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr); - virtual void OnOutput(LPCSTR szText); - - StackWalkerInternal *m_sw; - HANDLE m_hProcess; - DWORD m_dwProcessId; - bool m_modulesLoaded; - LPSTR m_szSymPath; - - bool m_verbose; - int m_options; - int m_MaxRecursionCount; - - static BOOL __stdcall myReadProcMem(HANDLE hProcess, DWORD64 qwBaseAddress, PVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead); - - friend StackWalkerInternal; -}; // class StackWalker - - -// The "ugly" assembler-implementation is needed for systems before XP -// If you have a new PSDK and you only compile for XP and later, then you can use -// the "RtlCaptureContext" -// Currently there is no define which determines the PSDK-Version... -// So we just use the compiler-version (and assumes that the PSDK is -// the one which was installed by the VS-IDE) - -// INFO: If you want, you can use the RtlCaptureContext if you only target XP and later... -// But I currently use it in x64/IA64 environments... -//#if defined(_M_IX86) && (_WIN32_WINNT <= 0x0500) && (_MSC_VER < 1400) - -#if defined(_M_IX86) -#ifdef CURRENT_THREAD_VIA_EXCEPTION -// TODO: The following is not a "good" implementation, -// because the callstack is only valid in the "__except" block... -#define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \ - do { \ - memset(&c, 0, sizeof(CONTEXT)); \ - EXCEPTION_POINTERS *pExp = NULL; \ - __try { \ - throw 0; \ - } __except( ( (pExp = GetExceptionInformation()) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_EXECUTE_HANDLER)) {} \ - if (pExp != NULL) \ - memcpy(&c, pExp->ContextRecord, sizeof(CONTEXT)); \ - c.ContextFlags = contextFlags; \ - } while(0); -#else -// The following should be enough for walking the callstack... -#define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \ - do { \ - memset(&c, 0, sizeof(CONTEXT)); \ - c.ContextFlags = contextFlags; \ - __asm call x \ - __asm x: pop eax \ - __asm mov c.Eip, eax \ - __asm mov c.Ebp, ebp \ - __asm mov c.Esp, esp \ - } while(0); -#endif - -#else - -// The following is defined for x86 (XP and higher), x64 and IA64: -#define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \ - do { \ - memset(&c, 0, sizeof(CONTEXT)); \ - c.ContextFlags = contextFlags; \ - RtlCaptureContext(&c); \ -} while(0); -#endif - -#endif // LL_WINDOWS +/**********************************************************************
+ *
+ * StackWalker.h
+ *
+ *
+ *
+ * $LicenseInfo:firstyear=2016&license=bsd$
+ *
+ * Linden notes: Small modifications from the original source at https://stackwalker.codeplex.com/
+ *
+ * LICENSE (http://www.opensource.org/licenses/bsd-license.php)
+ *
+ * Copyright (c) 2005-2009, Jochen Kalmbach
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * Neither the name of Jochen Kalmbach nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * **********************************************************************/
+
+#if LL_WINDOWS
+
+// #pragma once is supported starting with _MCS_VER 1000,
+// so we need not to check the version (because we only support _MSC_VER >= 1100)!
+#pragma once
+
+#include <windows.h>
+
+// special defines for VC5/6 (if no actual PSDK is installed):
+#if _MSC_VER < 1300
+typedef unsigned __int64 DWORD64, *PDWORD64;
+#if defined(_WIN64)
+typedef unsigned __int64 SIZE_T, *PSIZE_T;
+#else
+typedef unsigned long SIZE_T, *PSIZE_T;
+#endif
+#endif // _MSC_VER < 1300
+
+class StackWalkerInternal; // forward
+class StackWalker
+{
+public:
+ typedef enum StackWalkOptions
+ {
+ // No addition info will be retrived
+ // (only the address is available)
+ RetrieveNone = 0,
+
+ // Try to get the symbol-name
+ RetrieveSymbol = 1,
+
+ // Try to get the line for this symbol
+ RetrieveLine = 2,
+
+ // Try to retrieve the module-infos
+ RetrieveModuleInfo = 4,
+
+ // Also retrieve the version for the DLL/EXE
+ RetrieveFileVersion = 8,
+
+ // Contains all the abouve
+ RetrieveVerbose = 0xF,
+
+ // Generate a "good" symbol-search-path
+ SymBuildPath = 0x10,
+
+ // Also use the public Microsoft-Symbol-Server
+ SymUseSymSrv = 0x20,
+
+ // Contains all the abouve "Sym"-options
+ SymAll = 0x30,
+
+ // Contains all options (default)
+ OptionsAll = 0x3F
+ } StackWalkOptions;
+
+ StackWalker(
+ bool verbose = true,
+ int options = OptionsAll, // 'int' is by design, to combine the enum-flags
+ LPCSTR szSymPath = NULL,
+ DWORD dwProcessId = GetCurrentProcessId(),
+ HANDLE hProcess = GetCurrentProcess()
+ );
+ StackWalker(DWORD dwProcessId, HANDLE hProcess);
+ virtual ~StackWalker();
+
+ typedef BOOL (__stdcall *PReadProcessMemoryRoutine)(
+ HANDLE hProcess,
+ DWORD64 qwBaseAddress,
+ PVOID lpBuffer,
+ DWORD nSize,
+ LPDWORD lpNumberOfBytesRead,
+ LPVOID pUserData // optional data, which was passed in "ShowCallstack"
+ );
+
+ bool LoadModules();
+
+ bool ShowCallstack(
+ bool verbose,
+ HANDLE hThread = GetCurrentThread(),
+ const CONTEXT *context = NULL,
+ PReadProcessMemoryRoutine readMemoryFunction = NULL,
+ LPVOID pUserData = NULL // optional to identify some data in the 'readMemoryFunction'-callback
+ );
+
+#if _MSC_VER >= 1300
+// due to some reasons, the "STACKWALK_MAX_NAMELEN" must be declared as "public"
+// in older compilers in order to use it... starting with VC7 we can declare it as "protected"
+protected:
+#endif
+ enum { STACKWALK_MAX_NAMELEN = 4096 }; // max name length for found symbols
+
+protected:
+ // Entry for each Callstack-Entry
+ typedef struct CallstackEntry
+ {
+ DWORD64 offset; // if 0, we have no valid entry
+ CHAR name[STACKWALK_MAX_NAMELEN];
+ CHAR undName[STACKWALK_MAX_NAMELEN];
+ CHAR undFullName[STACKWALK_MAX_NAMELEN];
+ DWORD64 offsetFromSmybol;
+ DWORD offsetFromLine;
+ DWORD lineNumber;
+ CHAR lineFileName[STACKWALK_MAX_NAMELEN];
+ DWORD symType;
+ LPCSTR symTypeString;
+ CHAR moduleName[STACKWALK_MAX_NAMELEN];
+ DWORD64 baseOfImage;
+ CHAR loadedImageName[STACKWALK_MAX_NAMELEN];
+ } CallstackEntry;
+
+ enum CallstackEntryType {firstEntry, nextEntry, lastEntry};
+
+ virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName);
+ virtual void OnLoadModule(LPCSTR img, LPCSTR mod, DWORD64 baseAddr, DWORD size, DWORD result, LPCSTR symType, LPCSTR pdbName, ULONGLONG fileVersion);
+ virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry);
+ virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr);
+ virtual void OnOutput(LPCSTR szText);
+
+ StackWalkerInternal *m_sw;
+ HANDLE m_hProcess;
+ DWORD m_dwProcessId;
+ bool m_modulesLoaded;
+ LPSTR m_szSymPath;
+
+ bool m_verbose;
+ int m_options;
+ int m_MaxRecursionCount;
+
+ static BOOL __stdcall myReadProcMem(HANDLE hProcess, DWORD64 qwBaseAddress, PVOID lpBuffer, DWORD nSize, LPDWORD lpNumberOfBytesRead);
+
+ friend StackWalkerInternal;
+}; // class StackWalker
+
+
+// The "ugly" assembler-implementation is needed for systems before XP
+// If you have a new PSDK and you only compile for XP and later, then you can use
+// the "RtlCaptureContext"
+// Currently there is no define which determines the PSDK-Version...
+// So we just use the compiler-version (and assumes that the PSDK is
+// the one which was installed by the VS-IDE)
+
+// INFO: If you want, you can use the RtlCaptureContext if you only target XP and later...
+// But I currently use it in x64/IA64 environments...
+//#if defined(_M_IX86) && (_WIN32_WINNT <= 0x0500) && (_MSC_VER < 1400)
+
+#if defined(_M_IX86)
+#ifdef CURRENT_THREAD_VIA_EXCEPTION
+// TODO: The following is not a "good" implementation,
+// because the callstack is only valid in the "__except" block...
+#define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \
+ do { \
+ memset(&c, 0, sizeof(CONTEXT)); \
+ EXCEPTION_POINTERS *pExp = NULL; \
+ __try { \
+ throw 0; \
+ } __except( ( (pExp = GetExceptionInformation()) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_EXECUTE_HANDLER)) {} \
+ if (pExp != NULL) \
+ memcpy(&c, pExp->ContextRecord, sizeof(CONTEXT)); \
+ c.ContextFlags = contextFlags; \
+ } while(0);
+#else
+// The following should be enough for walking the callstack...
+#define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \
+ do { \
+ memset(&c, 0, sizeof(CONTEXT)); \
+ c.ContextFlags = contextFlags; \
+ __asm call x \
+ __asm x: pop eax \
+ __asm mov c.Eip, eax \
+ __asm mov c.Ebp, ebp \
+ __asm mov c.Esp, esp \
+ } while(0);
+#endif
+
+#else
+
+// The following is defined for x86 (XP and higher), x64 and IA64:
+#define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \
+ do { \
+ memset(&c, 0, sizeof(CONTEXT)); \
+ c.ContextFlags = contextFlags; \
+ RtlCaptureContext(&c); \
+} while(0);
+#endif
+
+#endif // LL_WINDOWS
diff --git a/indra/llcommon/always_return.h b/indra/llcommon/always_return.h index 6b9f1fdeaf..a206471da5 100644 --- a/indra/llcommon/always_return.h +++ b/indra/llcommon/always_return.h @@ -4,7 +4,7 @@ * @date 2023-01-20 * @brief Call specified callable with arbitrary arguments, but always return * specified type. - * + * * $LicenseInfo:firstyear=2023&license=viewerlgpl$ * Copyright (c) 2023, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/apply.cpp b/indra/llcommon/apply.cpp index 417e23d3b4..805b1234ac 100644 --- a/indra/llcommon/apply.cpp +++ b/indra/llcommon/apply.cpp @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2022-12-21 * @brief Implementation for apply. - * + * * $LicenseInfo:firstyear=2022&license=viewerlgpl$ * Copyright (c) 2022, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/apply.h b/indra/llcommon/apply.h index cf6161ed50..ec1a39f7b0 100644 --- a/indra/llcommon/apply.h +++ b/indra/llcommon/apply.h @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2022-06-18 * @brief C++14 version of std::apply() - * + * * $LicenseInfo:firstyear=2022&license=viewerlgpl$ * Copyright (c) 2022, Linden Research, Inc. * $/LicenseInfo$ @@ -69,7 +69,7 @@ using std::invoke; // Use invoke() to handle pointer-to-method: // derived from https://stackoverflow.com/a/38288251 -template<typename Fn, typename... Args, +template<typename Fn, typename... Args, typename std::enable_if<std::is_member_pointer<typename std::decay<Fn>::type>::value, int>::type = 0 > auto invoke(Fn&& f, Args&&... args) @@ -77,7 +77,7 @@ auto invoke(Fn&& f, Args&&... args) return std::mem_fn(std::forward<Fn>(f))(std::forward<Args>(args)...); } -template<typename Fn, typename... Args, +template<typename Fn, typename... Args, typename std::enable_if<!std::is_member_pointer<typename std::decay<Fn>::type>::value, int>::type = 0 > auto invoke(Fn&& f, Args&&... args) @@ -154,7 +154,7 @@ using std::bind_front; #else // no std::bind_front() -template<typename Fn, typename... Args, +template<typename Fn, typename... Args, typename std::enable_if<!std::is_member_pointer<typename std::decay<Fn>::type>::value, int>::type = 0 > auto bind_front(Fn&& f, Args&&... args) @@ -172,7 +172,7 @@ auto bind_front(Fn&& f, Args&&... args) }; } -template<typename Fn, typename... Args, +template<typename Fn, typename... Args, typename std::enable_if<std::is_member_pointer<typename std::decay<Fn>::type>::value, int>::type = 0 > auto bind_front(Fn&& f, Args&&... args) diff --git a/indra/llcommon/chrono.h b/indra/llcommon/chrono.h index 806e871892..d121b9adf6 100644 --- a/indra/llcommon/chrono.h +++ b/indra/llcommon/chrono.h @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2021-10-05 * @brief supplement <chrono> with utility functions - * + * * $LicenseInfo:firstyear=2021&license=viewerlgpl$ * Copyright (c) 2021, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/classic_callback.cpp b/indra/llcommon/classic_callback.cpp index 5674e0a44d..b2d0e7b303 100644 --- a/indra/llcommon/classic_callback.cpp +++ b/indra/llcommon/classic_callback.cpp @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2021-09-23 * @brief Implementation for classic_callback. - * + * * $LicenseInfo:firstyear=2021&license=viewerlgpl$ * Copyright (c) 2021, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/commoncontrol.cpp b/indra/llcommon/commoncontrol.cpp index 81e66baf8c..d8bdcd5aa5 100644 --- a/indra/llcommon/commoncontrol.cpp +++ b/indra/llcommon/commoncontrol.cpp @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2022-06-08 * @brief Implementation for commoncontrol. - * + * * $LicenseInfo:firstyear=2022&license=viewerlgpl$ * Copyright (c) 2022, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/commoncontrol.h b/indra/llcommon/commoncontrol.h index 07d4a45ac5..13aa983a99 100644 --- a/indra/llcommon/commoncontrol.h +++ b/indra/llcommon/commoncontrol.h @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2022-06-08 * @brief Access LLViewerControl LLEventAPI, if process has one. - * + * * $LicenseInfo:firstyear=2022&license=viewerlgpl$ * Copyright (c) 2022, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/ctype_workaround.h b/indra/llcommon/ctype_workaround.h index 552be9fb90..89a47fe3db 100644 --- a/indra/llcommon/ctype_workaround.h +++ b/indra/llcommon/ctype_workaround.h @@ -1,4 +1,4 @@ -/** +/** * @file ctype_workaround.h * @brief The workaround is to create some legacy symbols that point * to the correct symbols, which avoids link errors. @@ -6,21 +6,21 @@ * $LicenseInfo:firstyear=2006&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -42,12 +42,12 @@ __const unsigned short int *__ctype_b; __const __int32_t *__ctype_tolower; __const __int32_t *__ctype_toupper; -// call this function at the beginning of main() +// call this function at the beginning of main() void ctype_workaround() { - __ctype_b = *(__ctype_b_loc()); - __ctype_toupper = *(__ctype_toupper_loc()); - __ctype_tolower = *(__ctype_tolower_loc()); + __ctype_b = *(__ctype_b_loc()); + __ctype_toupper = *(__ctype_toupper_loc()); + __ctype_tolower = *(__ctype_tolower_loc()); } #endif diff --git a/indra/llcommon/fix_macros.h b/indra/llcommon/fix_macros.h index 43c09c54bc..ed6c26a371 100644 --- a/indra/llcommon/fix_macros.h +++ b/indra/llcommon/fix_macros.h @@ -6,7 +6,7 @@ * generic names, preventing any library from using those names. We've * had to fix these in so many places that it's worth making a header * file to handle it. - * + * * $LicenseInfo:firstyear=2012&license=viewerlgpl$ * Copyright (c) 2012, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/function_types.h b/indra/llcommon/function_types.h index 3f42f6d640..7e15895a7e 100644 --- a/indra/llcommon/function_types.h +++ b/indra/llcommon/function_types.h @@ -4,7 +4,7 @@ * @date 2023-01-20 * @brief Extend boost::function_types to examine boost::function and * std::function - * + * * $LicenseInfo:firstyear=2023&license=viewerlgpl$ * Copyright (c) 2023, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/hbxxh.cpp b/indra/llcommon/hbxxh.cpp index 388269d6c8..41d797a7e3 100644 --- a/indra/llcommon/hbxxh.cpp +++ b/indra/llcommon/hbxxh.cpp @@ -10,16 +10,16 @@ * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llcommon/hbxxh.h b/indra/llcommon/hbxxh.h index 9c0e9cf172..142e140cf3 100644 --- a/indra/llcommon/hbxxh.h +++ b/indra/llcommon/hbxxh.h @@ -10,16 +10,16 @@ * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llcommon/indra_constants.cpp b/indra/llcommon/indra_constants.cpp index 9a0c565b06..d24a221671 100644 --- a/indra/llcommon/indra_constants.cpp +++ b/indra/llcommon/indra_constants.cpp @@ -1,25 +1,25 @@ -/** +/** * @file indra_constants.cpp * @brief some useful short term constants for Indra * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -38,39 +38,39 @@ const LLUUID GOVERNOR_LINDEN_ID("3d6181b0-6a4b-97ef-18d8-722652995cf1"); // Maintenance's group id. const LLUUID MAINTENANCE_GROUP_ID("dc7b21cd-3c89-fcaa-31c8-25f9ffd224cd"); // Grass Images -const LLUUID IMG_SMOKE ("b4ba225c-373f-446d-9f7e-6cb7b5cf9b3d"); // VIEWER +const LLUUID IMG_SMOKE ("b4ba225c-373f-446d-9f7e-6cb7b5cf9b3d"); // VIEWER -const LLUUID IMG_DEFAULT ("d2114404-dd59-4a4d-8e6c-49359e91bbf0"); // VIEWER +const LLUUID IMG_DEFAULT ("d2114404-dd59-4a4d-8e6c-49359e91bbf0"); // VIEWER -const LLUUID IMG_SUN ("cce0f112-878f-4586-a2e2-a8f104bba271"); // dataserver -const LLUUID IMG_MOON ("d07f6eed-b96a-47cd-b51d-400ad4a1c428"); // dataserver -const LLUUID IMG_SHOT ("35f217a3-f618-49cf-bbca-c86d486551a9"); // dataserver -const LLUUID IMG_SPARK ("d2e75ac1-d0fb-4532-820e-a20034ac814d"); // dataserver -const LLUUID IMG_FIRE ("aca40aa8-44cf-44ca-a0fa-93e1a2986f82"); // dataserver +const LLUUID IMG_SUN ("cce0f112-878f-4586-a2e2-a8f104bba271"); // dataserver +const LLUUID IMG_MOON ("d07f6eed-b96a-47cd-b51d-400ad4a1c428"); // dataserver +const LLUUID IMG_SHOT ("35f217a3-f618-49cf-bbca-c86d486551a9"); // dataserver +const LLUUID IMG_SPARK ("d2e75ac1-d0fb-4532-820e-a20034ac814d"); // dataserver +const LLUUID IMG_FIRE ("aca40aa8-44cf-44ca-a0fa-93e1a2986f82"); // dataserver const LLUUID IMG_FACE_SELECT ("a85ac674-cb75-4af6-9499-df7c5aaf7a28"); // face selector const LLUUID IMG_DEFAULT_AVATAR ("c228d1cf-4b5d-4ba8-84f4-899a0796aa97"); // dataserver -const LLUUID IMG_INVISIBLE ("3a367d1c-bef1-6d43-7595-e88c1e3aadb3"); // dataserver +const LLUUID IMG_INVISIBLE ("3a367d1c-bef1-6d43-7595-e88c1e3aadb3"); // dataserver const LLUUID IMG_WHITE ("5748decc-f629-461c-9a36-a35a221fe21f"); // dataserver -const LLUUID IMG_EXPLOSION ("68edcf47-ccd7-45b8-9f90-1649d7f12806"); // On dataserver -const LLUUID IMG_EXPLOSION_2 ("21ce046c-83fe-430a-b629-c7660ac78d7c"); // On dataserver -const LLUUID IMG_EXPLOSION_3 ("fedea30a-1be8-47a6-bc06-337a04a39c4b"); // On dataserver -const LLUUID IMG_EXPLOSION_4 ("abf0d56b-82e5-47a2-a8ad-74741bb2c29e"); // On dataserver -const LLUUID IMG_SMOKE_POOF ("1e63e323-5fe0-452e-92f8-b98bd0f764e3"); // On dataserver +const LLUUID IMG_EXPLOSION ("68edcf47-ccd7-45b8-9f90-1649d7f12806"); // On dataserver +const LLUUID IMG_EXPLOSION_2 ("21ce046c-83fe-430a-b629-c7660ac78d7c"); // On dataserver +const LLUUID IMG_EXPLOSION_3 ("fedea30a-1be8-47a6-bc06-337a04a39c4b"); // On dataserver +const LLUUID IMG_EXPLOSION_4 ("abf0d56b-82e5-47a2-a8ad-74741bb2c29e"); // On dataserver +const LLUUID IMG_SMOKE_POOF ("1e63e323-5fe0-452e-92f8-b98bd0f764e3"); // On dataserver -const LLUUID IMG_BIG_EXPLOSION_1 ("5e47a0dc-97bf-44e0-8b40-de06718cee9d"); // On dataserver -const LLUUID IMG_BIG_EXPLOSION_2 ("9c8eca51-53d5-42a7-bb58-cef070395db8"); // On dataserver +const LLUUID IMG_BIG_EXPLOSION_1 ("5e47a0dc-97bf-44e0-8b40-de06718cee9d"); // On dataserver +const LLUUID IMG_BIG_EXPLOSION_2 ("9c8eca51-53d5-42a7-bb58-cef070395db8"); // On dataserver -const LLUUID IMG_ALPHA_GRAD ("e97cf410-8e61-7005-ec06-629eba4cd1fb"); // VIEWER -const LLUUID IMG_ALPHA_GRAD_2D ("38b86f85-2575-52a9-a531-23108d8da837"); // VIEWER -const LLUUID IMG_TRANSPARENT ("8dcd4a48-2d37-4909-9f78-f7a9eb4ef903"); // VIEWER +const LLUUID IMG_ALPHA_GRAD ("e97cf410-8e61-7005-ec06-629eba4cd1fb"); // VIEWER +const LLUUID IMG_ALPHA_GRAD_2D ("38b86f85-2575-52a9-a531-23108d8da837"); // VIEWER +const LLUUID IMG_TRANSPARENT ("8dcd4a48-2d37-4909-9f78-f7a9eb4ef903"); // VIEWER -const LLUUID TERRAIN_DIRT_DETAIL ("0bc58228-74a0-7e83-89bc-5c23464bcec5"); // VIEWER -const LLUUID TERRAIN_GRASS_DETAIL ("63338ede-0037-c4fd-855b-015d77112fc8"); // VIEWER -const LLUUID TERRAIN_MOUNTAIN_DETAIL ("303cd381-8560-7579-23f1-f0a880799740"); // VIEWER -const LLUUID TERRAIN_ROCK_DETAIL ("53a2f406-4895-1d13-d541-d2e3b86bc19c"); // VIEWER +const LLUUID TERRAIN_DIRT_DETAIL ("0bc58228-74a0-7e83-89bc-5c23464bcec5"); // VIEWER +const LLUUID TERRAIN_GRASS_DETAIL ("63338ede-0037-c4fd-855b-015d77112fc8"); // VIEWER +const LLUUID TERRAIN_MOUNTAIN_DETAIL ("303cd381-8560-7579-23f1-f0a880799740"); // VIEWER +const LLUUID TERRAIN_ROCK_DETAIL ("53a2f406-4895-1d13-d541-d2e3b86bc19c"); // VIEWER -const LLUUID DEFAULT_WATER_NORMAL ("822ded49-9a6c-f61c-cb89-6df54f42cdf4"); // VIEWER +const LLUUID DEFAULT_WATER_NORMAL ("822ded49-9a6c-f61c-cb89-6df54f42cdf4"); // VIEWER const LLUUID DEFAULT_OBJECT_TEXTURE ("89556747-24cb-43ed-920b-47caed15465f"); // On dataserver const LLUUID DEFAULT_OBJECT_SPECULAR ("87e0e8f7-8729-1ea8-cfc9-8915773009db"); // On dataserver diff --git a/indra/llcommon/indra_constants.h b/indra/llcommon/indra_constants.h index a16cfac2b9..811313e56e 100644 --- a/indra/llcommon/indra_constants.h +++ b/indra/llcommon/indra_constants.h @@ -1,25 +1,25 @@ -/** +/** * @file indra_constants.h * @brief some useful short term constants for Indra * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -37,21 +37,21 @@ static const U32 REGION_WIDTH_U32 = 256; const F32 REGION_HEIGHT_METERS = 4096.f; -const F32 DEFAULT_AGENT_DEPTH = 0.45f; -const F32 DEFAULT_AGENT_WIDTH = 0.60f; -const F32 DEFAULT_AGENT_HEIGHT = 1.9f; +const F32 DEFAULT_AGENT_DEPTH = 0.45f; +const F32 DEFAULT_AGENT_WIDTH = 0.60f; +const F32 DEFAULT_AGENT_HEIGHT = 1.9f; enum ETerrainBrushType { - // the valid brush numbers cannot be reordered, because they - // are used in the binary LSL format as arguments to llModifyLand() - E_LANDBRUSH_LEVEL = 0, - E_LANDBRUSH_RAISE = 1, - E_LANDBRUSH_LOWER = 2, - E_LANDBRUSH_SMOOTH = 3, - E_LANDBRUSH_NOISE = 4, - E_LANDBRUSH_REVERT = 5, - E_LANDBRUSH_INVALID = 6 + // the valid brush numbers cannot be reordered, because they + // are used in the binary LSL format as arguments to llModifyLand() + E_LANDBRUSH_LEVEL = 0, + E_LANDBRUSH_RAISE = 1, + E_LANDBRUSH_LOWER = 2, + E_LANDBRUSH_SMOOTH = 3, + E_LANDBRUSH_NOISE = 4, + E_LANDBRUSH_REVERT = 5, + E_LANDBRUSH_INVALID = 6 }; enum EMouseClickType{ @@ -67,101 +67,101 @@ enum EMouseClickType{ // keys // Bit masks for various keyboard modifier keys. -const MASK MASK_NONE = 0x0000; -const MASK MASK_CONTROL = 0x0001; // Mapped to cmd on Macs -const MASK MASK_ALT = 0x0002; -const MASK MASK_SHIFT = 0x0004; +const MASK MASK_NONE = 0x0000; +const MASK MASK_CONTROL = 0x0001; // Mapped to cmd on Macs +const MASK MASK_ALT = 0x0002; +const MASK MASK_SHIFT = 0x0004; const MASK MASK_NORMALKEYS = 0x0007; // A real mask - only get the bits for normal modifier keys -const MASK MASK_MAC_CONTROL = 0x0008; // Un-mapped Ctrl key on Macs, not used on Windows -const MASK MASK_MODIFIERS = MASK_CONTROL|MASK_ALT|MASK_SHIFT|MASK_MAC_CONTROL; +const MASK MASK_MAC_CONTROL = 0x0008; // Un-mapped Ctrl key on Macs, not used on Windows +const MASK MASK_MODIFIERS = MASK_CONTROL|MASK_ALT|MASK_SHIFT|MASK_MAC_CONTROL; // Special keys go into >128 -const KEY KEY_SPECIAL = 0x80; // special keys start here -const KEY KEY_RETURN = 0x81; -const KEY KEY_LEFT = 0x82; -const KEY KEY_RIGHT = 0x83; -const KEY KEY_UP = 0x84; -const KEY KEY_DOWN = 0x85; -const KEY KEY_ESCAPE = 0x86; +const KEY KEY_SPECIAL = 0x80; // special keys start here +const KEY KEY_RETURN = 0x81; +const KEY KEY_LEFT = 0x82; +const KEY KEY_RIGHT = 0x83; +const KEY KEY_UP = 0x84; +const KEY KEY_DOWN = 0x85; +const KEY KEY_ESCAPE = 0x86; const KEY KEY_BACKSPACE =0x87; -const KEY KEY_DELETE = 0x88; -const KEY KEY_SHIFT = 0x89; -const KEY KEY_CONTROL = 0x8A; -const KEY KEY_ALT = 0x8B; -const KEY KEY_HOME = 0x8C; -const KEY KEY_END = 0x8D; +const KEY KEY_DELETE = 0x88; +const KEY KEY_SHIFT = 0x89; +const KEY KEY_CONTROL = 0x8A; +const KEY KEY_ALT = 0x8B; +const KEY KEY_HOME = 0x8C; +const KEY KEY_END = 0x8D; const KEY KEY_PAGE_UP = 0x8E; const KEY KEY_PAGE_DOWN = 0x8F; const KEY KEY_HYPHEN = 0x90; const KEY KEY_EQUALS = 0x91; const KEY KEY_INSERT = 0x92; const KEY KEY_CAPSLOCK = 0x93; -const KEY KEY_TAB = 0x94; -const KEY KEY_ADD = 0x95; +const KEY KEY_TAB = 0x94; +const KEY KEY_ADD = 0x95; const KEY KEY_SUBTRACT =0x96; const KEY KEY_MULTIPLY =0x97; -const KEY KEY_DIVIDE = 0x98; -const KEY KEY_F1 = 0xA1; -const KEY KEY_F2 = 0xA2; -const KEY KEY_F3 = 0xA3; -const KEY KEY_F4 = 0xA4; -const KEY KEY_F5 = 0xA5; -const KEY KEY_F6 = 0xA6; -const KEY KEY_F7 = 0xA7; -const KEY KEY_F8 = 0xA8; -const KEY KEY_F9 = 0xA9; -const KEY KEY_F10 = 0xAA; -const KEY KEY_F11 = 0xAB; -const KEY KEY_F12 = 0xAC; - -const KEY KEY_PAD_UP = 0xC0; -const KEY KEY_PAD_DOWN = 0xC1; -const KEY KEY_PAD_LEFT = 0xC2; -const KEY KEY_PAD_RIGHT = 0xC3; -const KEY KEY_PAD_HOME = 0xC4; -const KEY KEY_PAD_END = 0xC5; -const KEY KEY_PAD_PGUP = 0xC6; -const KEY KEY_PAD_PGDN = 0xC7; -const KEY KEY_PAD_CENTER = 0xC8; // the 5 in the middle -const KEY KEY_PAD_INS = 0xC9; -const KEY KEY_PAD_DEL = 0xCA; -const KEY KEY_PAD_RETURN = 0xCB; -const KEY KEY_PAD_ADD = 0xCC; // not used -const KEY KEY_PAD_SUBTRACT = 0xCD; // not used +const KEY KEY_DIVIDE = 0x98; +const KEY KEY_F1 = 0xA1; +const KEY KEY_F2 = 0xA2; +const KEY KEY_F3 = 0xA3; +const KEY KEY_F4 = 0xA4; +const KEY KEY_F5 = 0xA5; +const KEY KEY_F6 = 0xA6; +const KEY KEY_F7 = 0xA7; +const KEY KEY_F8 = 0xA8; +const KEY KEY_F9 = 0xA9; +const KEY KEY_F10 = 0xAA; +const KEY KEY_F11 = 0xAB; +const KEY KEY_F12 = 0xAC; + +const KEY KEY_PAD_UP = 0xC0; +const KEY KEY_PAD_DOWN = 0xC1; +const KEY KEY_PAD_LEFT = 0xC2; +const KEY KEY_PAD_RIGHT = 0xC3; +const KEY KEY_PAD_HOME = 0xC4; +const KEY KEY_PAD_END = 0xC5; +const KEY KEY_PAD_PGUP = 0xC6; +const KEY KEY_PAD_PGDN = 0xC7; +const KEY KEY_PAD_CENTER = 0xC8; // the 5 in the middle +const KEY KEY_PAD_INS = 0xC9; +const KEY KEY_PAD_DEL = 0xCA; +const KEY KEY_PAD_RETURN = 0xCB; +const KEY KEY_PAD_ADD = 0xCC; // not used +const KEY KEY_PAD_SUBTRACT = 0xCD; // not used const KEY KEY_PAD_MULTIPLY = 0xCE; // not used -const KEY KEY_PAD_DIVIDE = 0xCF; // not used - -const KEY KEY_BUTTON0 = 0xD0; -const KEY KEY_BUTTON1 = 0xD1; -const KEY KEY_BUTTON2 = 0xD2; -const KEY KEY_BUTTON3 = 0xD3; -const KEY KEY_BUTTON4 = 0xD4; -const KEY KEY_BUTTON5 = 0xD5; -const KEY KEY_BUTTON6 = 0xD6; -const KEY KEY_BUTTON7 = 0xD7; -const KEY KEY_BUTTON8 = 0xD8; -const KEY KEY_BUTTON9 = 0xD9; -const KEY KEY_BUTTON10 = 0xDA; -const KEY KEY_BUTTON11 = 0xDB; -const KEY KEY_BUTTON12 = 0xDC; -const KEY KEY_BUTTON13 = 0xDD; -const KEY KEY_BUTTON14 = 0xDE; -const KEY KEY_BUTTON15 = 0xDF; - -const KEY KEY_NONE = 0xFF; // not sent from keyboard. For internal use only. +const KEY KEY_PAD_DIVIDE = 0xCF; // not used + +const KEY KEY_BUTTON0 = 0xD0; +const KEY KEY_BUTTON1 = 0xD1; +const KEY KEY_BUTTON2 = 0xD2; +const KEY KEY_BUTTON3 = 0xD3; +const KEY KEY_BUTTON4 = 0xD4; +const KEY KEY_BUTTON5 = 0xD5; +const KEY KEY_BUTTON6 = 0xD6; +const KEY KEY_BUTTON7 = 0xD7; +const KEY KEY_BUTTON8 = 0xD8; +const KEY KEY_BUTTON9 = 0xD9; +const KEY KEY_BUTTON10 = 0xDA; +const KEY KEY_BUTTON11 = 0xDB; +const KEY KEY_BUTTON12 = 0xDC; +const KEY KEY_BUTTON13 = 0xDD; +const KEY KEY_BUTTON14 = 0xDE; +const KEY KEY_BUTTON15 = 0xDF; + +const KEY KEY_NONE = 0xFF; // not sent from keyboard. For internal use only. const S32 KEY_COUNT = 256; -const F32 DEFAULT_WATER_HEIGHT = 20.0f; +const F32 DEFAULT_WATER_HEIGHT = 20.0f; // Maturity ratings for simulators -const U8 SIM_ACCESS_MIN = 0; // Treated as 'unknown', usually ends up being SIM_ACCESS_PG -const U8 SIM_ACCESS_PG = 13; -const U8 SIM_ACCESS_MATURE = 21; -const U8 SIM_ACCESS_ADULT = 42; // Seriously Adult Only -const U8 SIM_ACCESS_DOWN = 254; -const U8 SIM_ACCESS_MAX = SIM_ACCESS_ADULT; +const U8 SIM_ACCESS_MIN = 0; // Treated as 'unknown', usually ends up being SIM_ACCESS_PG +const U8 SIM_ACCESS_PG = 13; +const U8 SIM_ACCESS_MATURE = 21; +const U8 SIM_ACCESS_ADULT = 42; // Seriously Adult Only +const U8 SIM_ACCESS_DOWN = 254; +const U8 SIM_ACCESS_MAX = SIM_ACCESS_ADULT; // attachment constants const U8 ATTACHMENT_ADD = 0x80; @@ -258,84 +258,84 @@ const U32 PARCEL_MEDIA_COMMAND_LOOP_SET = 13; const S32 CHAT_CHANNEL_DEBUG = S32_MAX; // agent constants -const U32 CONTROL_AT_POS_INDEX = 0; -const U32 CONTROL_AT_NEG_INDEX = 1; -const U32 CONTROL_LEFT_POS_INDEX = 2; -const U32 CONTROL_LEFT_NEG_INDEX = 3; -const U32 CONTROL_UP_POS_INDEX = 4; -const U32 CONTROL_UP_NEG_INDEX = 5; -const U32 CONTROL_PITCH_POS_INDEX = 6; -const U32 CONTROL_PITCH_NEG_INDEX = 7; -const U32 CONTROL_YAW_POS_INDEX = 8; -const U32 CONTROL_YAW_NEG_INDEX = 9; -const U32 CONTROL_FAST_AT_INDEX = 10; -const U32 CONTROL_FAST_LEFT_INDEX = 11; -const U32 CONTROL_FAST_UP_INDEX = 12; -const U32 CONTROL_FLY_INDEX = 13; -const U32 CONTROL_STOP_INDEX = 14; -const U32 CONTROL_FINISH_ANIM_INDEX = 15; -const U32 CONTROL_STAND_UP_INDEX = 16; -const U32 CONTROL_SIT_ON_GROUND_INDEX = 17; -const U32 CONTROL_MOUSELOOK_INDEX = 18; -const U32 CONTROL_NUDGE_AT_POS_INDEX = 19; -const U32 CONTROL_NUDGE_AT_NEG_INDEX = 20; -const U32 CONTROL_NUDGE_LEFT_POS_INDEX = 21; -const U32 CONTROL_NUDGE_LEFT_NEG_INDEX = 22; -const U32 CONTROL_NUDGE_UP_POS_INDEX = 23; -const U32 CONTROL_NUDGE_UP_NEG_INDEX = 24; -const U32 CONTROL_TURN_LEFT_INDEX = 25; -const U32 CONTROL_TURN_RIGHT_INDEX = 26; -const U32 CONTROL_AWAY_INDEX = 27; -const U32 CONTROL_LBUTTON_DOWN_INDEX = 28; -const U32 CONTROL_LBUTTON_UP_INDEX = 29; -const U32 CONTROL_ML_LBUTTON_DOWN_INDEX = 30; -const U32 CONTROL_ML_LBUTTON_UP_INDEX = 31; -const U32 TOTAL_CONTROLS = 32; - -const U32 AGENT_CONTROL_AT_POS = 0x1 << CONTROL_AT_POS_INDEX; // 0x00000001 -const U32 AGENT_CONTROL_AT_NEG = 0x1 << CONTROL_AT_NEG_INDEX; // 0x00000002 -const U32 AGENT_CONTROL_LEFT_POS = 0x1 << CONTROL_LEFT_POS_INDEX; // 0x00000004 -const U32 AGENT_CONTROL_LEFT_NEG = 0x1 << CONTROL_LEFT_NEG_INDEX; // 0x00000008 -const U32 AGENT_CONTROL_UP_POS = 0x1 << CONTROL_UP_POS_INDEX; // 0x00000010 -const U32 AGENT_CONTROL_UP_NEG = 0x1 << CONTROL_UP_NEG_INDEX; // 0x00000020 -const U32 AGENT_CONTROL_PITCH_POS = 0x1 << CONTROL_PITCH_POS_INDEX; // 0x00000040 -const U32 AGENT_CONTROL_PITCH_NEG = 0x1 << CONTROL_PITCH_NEG_INDEX; // 0x00000080 -const U32 AGENT_CONTROL_YAW_POS = 0x1 << CONTROL_YAW_POS_INDEX; // 0x00000100 -const U32 AGENT_CONTROL_YAW_NEG = 0x1 << CONTROL_YAW_NEG_INDEX; // 0x00000200 - -const U32 AGENT_CONTROL_FAST_AT = 0x1 << CONTROL_FAST_AT_INDEX; // 0x00000400 -const U32 AGENT_CONTROL_FAST_LEFT = 0x1 << CONTROL_FAST_LEFT_INDEX; // 0x00000800 -const U32 AGENT_CONTROL_FAST_UP = 0x1 << CONTROL_FAST_UP_INDEX; // 0x00001000 - -const U32 AGENT_CONTROL_FLY = 0x1 << CONTROL_FLY_INDEX; // 0x00002000 -const U32 AGENT_CONTROL_STOP = 0x1 << CONTROL_STOP_INDEX; // 0x00004000 -const U32 AGENT_CONTROL_FINISH_ANIM = 0x1 << CONTROL_FINISH_ANIM_INDEX; // 0x00008000 -const U32 AGENT_CONTROL_STAND_UP = 0x1 << CONTROL_STAND_UP_INDEX; // 0x00010000 -const U32 AGENT_CONTROL_SIT_ON_GROUND = 0x1 << CONTROL_SIT_ON_GROUND_INDEX; // 0x00020000 -const U32 AGENT_CONTROL_MOUSELOOK = 0x1 << CONTROL_MOUSELOOK_INDEX; // 0x00040000 - -const U32 AGENT_CONTROL_NUDGE_AT_POS = 0x1 << CONTROL_NUDGE_AT_POS_INDEX; // 0x00080000 -const U32 AGENT_CONTROL_NUDGE_AT_NEG = 0x1 << CONTROL_NUDGE_AT_NEG_INDEX; // 0x00100000 -const U32 AGENT_CONTROL_NUDGE_LEFT_POS = 0x1 << CONTROL_NUDGE_LEFT_POS_INDEX; // 0x00200000 -const U32 AGENT_CONTROL_NUDGE_LEFT_NEG = 0x1 << CONTROL_NUDGE_LEFT_NEG_INDEX; // 0x00400000 -const U32 AGENT_CONTROL_NUDGE_UP_POS = 0x1 << CONTROL_NUDGE_UP_POS_INDEX; // 0x00800000 -const U32 AGENT_CONTROL_NUDGE_UP_NEG = 0x1 << CONTROL_NUDGE_UP_NEG_INDEX; // 0x01000000 -const U32 AGENT_CONTROL_TURN_LEFT = 0x1 << CONTROL_TURN_LEFT_INDEX; // 0x02000000 -const U32 AGENT_CONTROL_TURN_RIGHT = 0x1 << CONTROL_TURN_RIGHT_INDEX; // 0x04000000 - -const U32 AGENT_CONTROL_AWAY = 0x1 << CONTROL_AWAY_INDEX; // 0x08000000 - -const U32 AGENT_CONTROL_LBUTTON_DOWN = 0x1 << CONTROL_LBUTTON_DOWN_INDEX; // 0x10000000 -const U32 AGENT_CONTROL_LBUTTON_UP = 0x1 << CONTROL_LBUTTON_UP_INDEX; // 0x20000000 -const U32 AGENT_CONTROL_ML_LBUTTON_DOWN = 0x1 << CONTROL_ML_LBUTTON_DOWN_INDEX; // 0x40000000 -const U32 AGENT_CONTROL_ML_LBUTTON_UP = ((U32)0x1) << CONTROL_ML_LBUTTON_UP_INDEX; // 0x80000000 - -// move these up so that we can hide them in "State" for object updates +const U32 CONTROL_AT_POS_INDEX = 0; +const U32 CONTROL_AT_NEG_INDEX = 1; +const U32 CONTROL_LEFT_POS_INDEX = 2; +const U32 CONTROL_LEFT_NEG_INDEX = 3; +const U32 CONTROL_UP_POS_INDEX = 4; +const U32 CONTROL_UP_NEG_INDEX = 5; +const U32 CONTROL_PITCH_POS_INDEX = 6; +const U32 CONTROL_PITCH_NEG_INDEX = 7; +const U32 CONTROL_YAW_POS_INDEX = 8; +const U32 CONTROL_YAW_NEG_INDEX = 9; +const U32 CONTROL_FAST_AT_INDEX = 10; +const U32 CONTROL_FAST_LEFT_INDEX = 11; +const U32 CONTROL_FAST_UP_INDEX = 12; +const U32 CONTROL_FLY_INDEX = 13; +const U32 CONTROL_STOP_INDEX = 14; +const U32 CONTROL_FINISH_ANIM_INDEX = 15; +const U32 CONTROL_STAND_UP_INDEX = 16; +const U32 CONTROL_SIT_ON_GROUND_INDEX = 17; +const U32 CONTROL_MOUSELOOK_INDEX = 18; +const U32 CONTROL_NUDGE_AT_POS_INDEX = 19; +const U32 CONTROL_NUDGE_AT_NEG_INDEX = 20; +const U32 CONTROL_NUDGE_LEFT_POS_INDEX = 21; +const U32 CONTROL_NUDGE_LEFT_NEG_INDEX = 22; +const U32 CONTROL_NUDGE_UP_POS_INDEX = 23; +const U32 CONTROL_NUDGE_UP_NEG_INDEX = 24; +const U32 CONTROL_TURN_LEFT_INDEX = 25; +const U32 CONTROL_TURN_RIGHT_INDEX = 26; +const U32 CONTROL_AWAY_INDEX = 27; +const U32 CONTROL_LBUTTON_DOWN_INDEX = 28; +const U32 CONTROL_LBUTTON_UP_INDEX = 29; +const U32 CONTROL_ML_LBUTTON_DOWN_INDEX = 30; +const U32 CONTROL_ML_LBUTTON_UP_INDEX = 31; +const U32 TOTAL_CONTROLS = 32; + +const U32 AGENT_CONTROL_AT_POS = 0x1 << CONTROL_AT_POS_INDEX; // 0x00000001 +const U32 AGENT_CONTROL_AT_NEG = 0x1 << CONTROL_AT_NEG_INDEX; // 0x00000002 +const U32 AGENT_CONTROL_LEFT_POS = 0x1 << CONTROL_LEFT_POS_INDEX; // 0x00000004 +const U32 AGENT_CONTROL_LEFT_NEG = 0x1 << CONTROL_LEFT_NEG_INDEX; // 0x00000008 +const U32 AGENT_CONTROL_UP_POS = 0x1 << CONTROL_UP_POS_INDEX; // 0x00000010 +const U32 AGENT_CONTROL_UP_NEG = 0x1 << CONTROL_UP_NEG_INDEX; // 0x00000020 +const U32 AGENT_CONTROL_PITCH_POS = 0x1 << CONTROL_PITCH_POS_INDEX; // 0x00000040 +const U32 AGENT_CONTROL_PITCH_NEG = 0x1 << CONTROL_PITCH_NEG_INDEX; // 0x00000080 +const U32 AGENT_CONTROL_YAW_POS = 0x1 << CONTROL_YAW_POS_INDEX; // 0x00000100 +const U32 AGENT_CONTROL_YAW_NEG = 0x1 << CONTROL_YAW_NEG_INDEX; // 0x00000200 + +const U32 AGENT_CONTROL_FAST_AT = 0x1 << CONTROL_FAST_AT_INDEX; // 0x00000400 +const U32 AGENT_CONTROL_FAST_LEFT = 0x1 << CONTROL_FAST_LEFT_INDEX; // 0x00000800 +const U32 AGENT_CONTROL_FAST_UP = 0x1 << CONTROL_FAST_UP_INDEX; // 0x00001000 + +const U32 AGENT_CONTROL_FLY = 0x1 << CONTROL_FLY_INDEX; // 0x00002000 +const U32 AGENT_CONTROL_STOP = 0x1 << CONTROL_STOP_INDEX; // 0x00004000 +const U32 AGENT_CONTROL_FINISH_ANIM = 0x1 << CONTROL_FINISH_ANIM_INDEX; // 0x00008000 +const U32 AGENT_CONTROL_STAND_UP = 0x1 << CONTROL_STAND_UP_INDEX; // 0x00010000 +const U32 AGENT_CONTROL_SIT_ON_GROUND = 0x1 << CONTROL_SIT_ON_GROUND_INDEX; // 0x00020000 +const U32 AGENT_CONTROL_MOUSELOOK = 0x1 << CONTROL_MOUSELOOK_INDEX; // 0x00040000 + +const U32 AGENT_CONTROL_NUDGE_AT_POS = 0x1 << CONTROL_NUDGE_AT_POS_INDEX; // 0x00080000 +const U32 AGENT_CONTROL_NUDGE_AT_NEG = 0x1 << CONTROL_NUDGE_AT_NEG_INDEX; // 0x00100000 +const U32 AGENT_CONTROL_NUDGE_LEFT_POS = 0x1 << CONTROL_NUDGE_LEFT_POS_INDEX; // 0x00200000 +const U32 AGENT_CONTROL_NUDGE_LEFT_NEG = 0x1 << CONTROL_NUDGE_LEFT_NEG_INDEX; // 0x00400000 +const U32 AGENT_CONTROL_NUDGE_UP_POS = 0x1 << CONTROL_NUDGE_UP_POS_INDEX; // 0x00800000 +const U32 AGENT_CONTROL_NUDGE_UP_NEG = 0x1 << CONTROL_NUDGE_UP_NEG_INDEX; // 0x01000000 +const U32 AGENT_CONTROL_TURN_LEFT = 0x1 << CONTROL_TURN_LEFT_INDEX; // 0x02000000 +const U32 AGENT_CONTROL_TURN_RIGHT = 0x1 << CONTROL_TURN_RIGHT_INDEX; // 0x04000000 + +const U32 AGENT_CONTROL_AWAY = 0x1 << CONTROL_AWAY_INDEX; // 0x08000000 + +const U32 AGENT_CONTROL_LBUTTON_DOWN = 0x1 << CONTROL_LBUTTON_DOWN_INDEX; // 0x10000000 +const U32 AGENT_CONTROL_LBUTTON_UP = 0x1 << CONTROL_LBUTTON_UP_INDEX; // 0x20000000 +const U32 AGENT_CONTROL_ML_LBUTTON_DOWN = 0x1 << CONTROL_ML_LBUTTON_DOWN_INDEX; // 0x40000000 +const U32 AGENT_CONTROL_ML_LBUTTON_UP = ((U32)0x1) << CONTROL_ML_LBUTTON_UP_INDEX; // 0x80000000 + +// move these up so that we can hide them in "State" for object updates // (for now) -const U32 AGENT_ATTACH_OFFSET = 4; -const U32 AGENT_ATTACH_MASK = 0xf << AGENT_ATTACH_OFFSET; +const U32 AGENT_ATTACH_OFFSET = 4; +const U32 AGENT_ATTACH_MASK = 0xf << AGENT_ATTACH_OFFSET; -// RN: this method swaps the upper and lower nibbles to maintain backward +// RN: this method swaps the upper and lower nibbles to maintain backward // compatibility with old objects that only used the upper nibble #define ATTACHMENT_ID_FROM_STATE(state) ((S32)((((U8)state & AGENT_ATTACH_MASK) >> 4) | (((U8)state & ~AGENT_ATTACH_MASK) << 4))) diff --git a/indra/llcommon/lazyeventapi.cpp b/indra/llcommon/lazyeventapi.cpp index 028af9f33f..91db0ee4a6 100644 --- a/indra/llcommon/lazyeventapi.cpp +++ b/indra/llcommon/lazyeventapi.cpp @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2022-06-17 * @brief Implementation for lazyeventapi. - * + * * $LicenseInfo:firstyear=2022&license=viewerlgpl$ * Copyright (c) 2022, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/lazyeventapi.h b/indra/llcommon/lazyeventapi.h index e36831270b..0e5df4e6f4 100644 --- a/indra/llcommon/lazyeventapi.h +++ b/indra/llcommon/lazyeventapi.h @@ -4,7 +4,7 @@ * @date 2022-06-16 * @brief Declaring a static module-scope LazyEventAPI registers a specific * LLEventAPI for future on-demand instantiation. - * + * * $LicenseInfo:firstyear=2022&license=viewerlgpl$ * Copyright (c) 2022, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/linden_common.h b/indra/llcommon/linden_common.h index a228fd22be..a918caa2e8 100644 --- a/indra/llcommon/linden_common.h +++ b/indra/llcommon/linden_common.h @@ -1,25 +1,25 @@ -/** +/** * @file linden_common.h * @brief Includes common headers that are always safe to include * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llcommon/llalignedarray.h b/indra/llcommon/llalignedarray.h index da9d98c16c..0ba8b34cb6 100644 --- a/indra/llcommon/llalignedarray.h +++ b/indra/llcommon/llalignedarray.h @@ -1,25 +1,25 @@ -/** +/** * @file llalignedarray.h * @brief A static array which obeys alignment restrictions and mimics std::vector accessors. * * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -33,94 +33,94 @@ template <class T, U32 alignment> class LLAlignedArray { public: - T* mArray; - U32 mElementCount; - U32 mCapacity; - - LLAlignedArray(); - ~LLAlignedArray(); - - void push_back(const T& elem); - U32 size() const { return mElementCount; } - void resize(U32 size); - T* append(S32 N); - T& operator[](int idx); - const T& operator[](int idx) const; + T* mArray; + U32 mElementCount; + U32 mCapacity; + + LLAlignedArray(); + ~LLAlignedArray(); + + void push_back(const T& elem); + U32 size() const { return mElementCount; } + void resize(U32 size); + T* append(S32 N); + T& operator[](int idx); + const T& operator[](int idx) const; }; template <class T, U32 alignment> LLAlignedArray<T, alignment>::LLAlignedArray() { - llassert(alignment >= 16); - mArray = NULL; - mElementCount = 0; - mCapacity = 0; + llassert(alignment >= 16); + mArray = NULL; + mElementCount = 0; + mCapacity = 0; } template <class T, U32 alignment> LLAlignedArray<T, alignment>::~LLAlignedArray() { - ll_aligned_free<alignment>(mArray); - mArray = NULL; - mElementCount = 0; - mCapacity = 0; + ll_aligned_free<alignment>(mArray); + mArray = NULL; + mElementCount = 0; + mCapacity = 0; } template <class T, U32 alignment> void LLAlignedArray<T, alignment>::push_back(const T& elem) { - T* old_buf = NULL; - if (mCapacity <= mElementCount) - { - mCapacity++; - mCapacity *= 2; - T* new_buf = (T*) ll_aligned_malloc<alignment>(mCapacity*sizeof(T)); - if (mArray) - { - ll_memcpy_nonaliased_aligned_16((char*)new_buf, (char*)mArray, sizeof(T)*mElementCount); - } - old_buf = mArray; - mArray = new_buf; - } - - mArray[mElementCount++] = elem; - - //delete old array here to prevent error on a.push_back(a[0]) - ll_aligned_free<alignment>(old_buf); + T* old_buf = NULL; + if (mCapacity <= mElementCount) + { + mCapacity++; + mCapacity *= 2; + T* new_buf = (T*) ll_aligned_malloc<alignment>(mCapacity*sizeof(T)); + if (mArray) + { + ll_memcpy_nonaliased_aligned_16((char*)new_buf, (char*)mArray, sizeof(T)*mElementCount); + } + old_buf = mArray; + mArray = new_buf; + } + + mArray[mElementCount++] = elem; + + //delete old array here to prevent error on a.push_back(a[0]) + ll_aligned_free<alignment>(old_buf); } template <class T, U32 alignment> void LLAlignedArray<T, alignment>::resize(U32 size) { - if (mCapacity < size) - { - mCapacity = size+mCapacity*2; - T* new_buf = mCapacity > 0 ? (T*) ll_aligned_malloc<alignment>(mCapacity*sizeof(T)) : NULL; - if (mArray) - { - ll_memcpy_nonaliased_aligned_16((char*) new_buf, (char*) mArray, sizeof(T)*mElementCount); - ll_aligned_free<alignment>(mArray); - } - - /*for (U32 i = mElementCount; i < mCapacity; ++i) - { - new(new_buf+i) T(); - }*/ - mArray = new_buf; - } - - mElementCount = size; + if (mCapacity < size) + { + mCapacity = size+mCapacity*2; + T* new_buf = mCapacity > 0 ? (T*) ll_aligned_malloc<alignment>(mCapacity*sizeof(T)) : NULL; + if (mArray) + { + ll_memcpy_nonaliased_aligned_16((char*) new_buf, (char*) mArray, sizeof(T)*mElementCount); + ll_aligned_free<alignment>(mArray); + } + + /*for (U32 i = mElementCount; i < mCapacity; ++i) + { + new(new_buf+i) T(); + }*/ + mArray = new_buf; + } + + mElementCount = size; } template <class T, U32 alignment> T& LLAlignedArray<T, alignment>::operator[](int idx) { - if(idx >= mElementCount || idx < 0) + if(idx >= mElementCount || idx < 0) { LL_ERRS() << "Out of bounds LLAlignedArray, requested: " << (S32)idx << " size: " << mElementCount << LL_ENDL; } - return mArray[idx]; + return mArray[idx]; } template <class T, U32 alignment> @@ -130,15 +130,15 @@ const T& LLAlignedArray<T, alignment>::operator[](int idx) const { LL_ERRS() << "Out of bounds LLAlignedArray, requested: " << (S32)idx << " size: " << mElementCount << LL_ENDL; } - return mArray[idx]; + return mArray[idx]; } template <class T, U32 alignment> T* LLAlignedArray<T, alignment>::append(S32 N) { - U32 sz = size(); - resize(sz+N); - return &((*this)[sz]); + U32 sz = size(); + resize(sz+N); + return &((*this)[sz]); } #endif diff --git a/indra/llcommon/llallocator.cpp b/indra/llcommon/llallocator.cpp index ac97fb71dd..abe3779b85 100644 --- a/indra/llcommon/llallocator.cpp +++ b/indra/llcommon/llallocator.cpp @@ -1,25 +1,25 @@ -/** +/** * @file llallocator.cpp * @brief Implementation of the LLAllocator class. * * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llcommon/llallocator.h b/indra/llcommon/llallocator.h index d26ad73c5b..aa3eead546 100644 --- a/indra/llcommon/llallocator.h +++ b/indra/llcommon/llallocator.h @@ -1,25 +1,25 @@ -/** +/** * @file llallocator.h * @brief Declaration of the LLAllocator class. * * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llcommon/llallocator_heap_profile.cpp b/indra/llcommon/llallocator_heap_profile.cpp index c6d9542b42..85e56b4db4 100644 --- a/indra/llcommon/llallocator_heap_profile.cpp +++ b/indra/llcommon/llallocator_heap_profile.cpp @@ -1,4 +1,4 @@ -/** +/** * @file llallocator_heap_profile.cpp * @brief Implementation of the parser for tcmalloc heap profile data. * @author Brad Kittenbrink @@ -6,21 +6,21 @@ * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -67,7 +67,7 @@ void LLAllocatorHeapProfile::parse(std::string const & prof_text) std::string::const_iterator prof_begin = prof_text.begin() + HEAP_PROFILE_MAGIC_STR.length(); - range_t prof_range(prof_begin, prof_text.end()); + range_t prof_range(prof_begin, prof_text.end()); boost::algorithm::split(prof_lines, prof_range, boost::bind(std::equal_to<llwchar>(), '\n', _1)); @@ -107,34 +107,34 @@ void LLAllocatorHeapProfile::parse(std::string const & prof_text) ++j; while(j != line_elems.end() && j->empty()) { ++j; } // skip any separator tokens - llassert(j != line_elems.end()); + llassert(j != line_elems.end()); if (j != line_elems.end()) - { - ++j; // skip the '@' - - mLines.push_back(line(live_count, live_size, tot_count, tot_size)); - line & current_line = mLines.back(); - - for(; j != line_elems.end(); ++j) - { - if(!j->empty()) - { - U32 marker = boost::lexical_cast<U32>(*j); - current_line.mTrace.push_back(marker); - } - } - } + { + ++j; // skip the '@' + + mLines.push_back(line(live_count, live_size, tot_count, tot_size)); + line & current_line = mLines.back(); + + for(; j != line_elems.end(); ++j) + { + if(!j->empty()) + { + U32 marker = boost::lexical_cast<U32>(*j); + current_line.mTrace.push_back(marker); + } + } + } } // *TODO - parse MAPPED_LIBRARIES section here if we're ever interested in it } void LLAllocatorHeapProfile::dump(std::ostream & out) const { - for (const LLAllocatorHeapProfile::line& line : mLines) + for (const LLAllocatorHeapProfile::line& line : mLines) { out << line.mLiveCount << ": " << line.mLiveSize << '[' << line.mTotalCount << ": " << line.mTotalSize << "] @"; - for (const stack_marker marker : line.mTrace) + for (const stack_marker marker : line.mTrace) { out << ' ' << marker; } diff --git a/indra/llcommon/llallocator_heap_profile.h b/indra/llcommon/llallocator_heap_profile.h index 69300b829b..22f284b703 100644 --- a/indra/llcommon/llallocator_heap_profile.h +++ b/indra/llcommon/llallocator_heap_profile.h @@ -1,4 +1,4 @@ -/** +/** * @file llallocator_heap_profile.h * @brief Declaration of the parser for tcmalloc heap profile data. * @author Brad Kittenbrink @@ -6,21 +6,21 @@ * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -59,7 +59,7 @@ public: { } - void parse(std::string const & prof_text); + void parse(std::string const & prof_text); void dump(std::ostream & out) const; diff --git a/indra/llcommon/llapp.cpp b/indra/llcommon/llapp.cpp index 2f41c039f2..b354a60bb0 100644 --- a/indra/llcommon/llapp.cpp +++ b/indra/llcommon/llapp.cpp @@ -1,746 +1,746 @@ -/** - * @file llapp.cpp - * @brief Implementation of the LLApp class. - * - * $LicenseInfo:firstyear=2003&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" - -#include "llapp.h" - -#include <cstdlib> - -#ifdef LL_DARWIN -#include <sys/types.h> -#include <unistd.h> -#include <sys/sysctl.h> -#endif - -#include "llcommon.h" -#include "llapr.h" -#include "llerrorcontrol.h" -#include "llframetimer.h" -#include "lllivefile.h" -#include "llmemory.h" -#include "llstl.h" // for DeletePointer() -#include "llstring.h" -#include "lleventtimer.h" -#include "stringize.h" -#include "llcleanup.h" -#include "llevents.h" -#include "llsdutil.h" - -// -// Signal handling -#ifndef LL_WINDOWS -# include <signal.h> -# include <unistd.h> // for fork() -void setup_signals(); -void default_unix_signal_handler(int signum, siginfo_t *info, void *); - -#if LL_LINUX -#else -// Called by breakpad exception handler after the minidump has been generated. -bool unix_post_minidump_callback(const char *dump_dir, - const char *minidump_id, - void *context, bool succeeded); -#endif - -# if LL_DARWIN -/* OSX doesn't support SIGRT* */ -S32 LL_SMACKDOWN_SIGNAL = SIGUSR1; -S32 LL_HEARTBEAT_SIGNAL = SIGUSR2; -# else // linux or (assumed) other similar unixoid -/* We want reliable delivery of our signals - SIGRT* is it. */ -/* Old LinuxThreads versions eat SIGRTMIN+0 to SIGRTMIN+2, avoid those. */ -/* Note that SIGRTMIN/SIGRTMAX may expand to a glibc function call with a - nonconstant result so these are not consts and cannot be used in constant- - expressions. SIGRTMAX may return -1 on rare broken setups. */ -S32 LL_SMACKDOWN_SIGNAL = (SIGRTMAX >= 0) ? (SIGRTMAX-1) : SIGUSR1; -S32 LL_HEARTBEAT_SIGNAL = (SIGRTMAX >= 0) ? (SIGRTMAX-0) : SIGUSR2; -# endif // LL_DARWIN -#endif // !LL_WINDOWS - -// the static application instance -LLApp* LLApp::sApplication = NULL; - -// Allows the generation of core files for post mortem under gdb -// and disables crashlogger -bool LLApp::sDisableCrashlogger = false; - -// Local flag for whether or not to do logging in signal handlers. -//static -bool LLApp::sLogInSignal = false; - -// static -// Keeps track of application status -LLScalarCond<LLApp::EAppStatus> LLApp::sStatus{LLApp::APP_STATUS_STOPPED}; -LLAppErrorHandler LLApp::sErrorHandler = NULL; - - -LLApp::LLApp() -{ - // Set our status to running - setStatus(APP_STATUS_RUNNING); - - LLCommon::initClass(); - - // initialize the options structure. We need to make this an array - // because the structured data will not auto-allocate if we - // reference an invalid location with the [] operator. - mOptions = LLSD::emptyArray(); - LLSD sd; - for(int i = 0; i < PRIORITY_COUNT; ++i) - { - mOptions.append(sd); - } - - // Make sure we clean up APR when we exit - // Don't need to do this if we're cleaning up APR in the destructor - //atexit(ll_cleanup_apr); - - // Set the application to this instance. - sApplication = this; - - // initialize the buffer to write the minidump filename to - // (this is used to avoid allocating memory in the crash handler) - memset(mMinidumpPath, 0, MAX_MINDUMP_PATH_LENGTH); - mCrashReportPipeStr = L"\\\\.\\pipe\\LLCrashReporterPipe"; -} - - -LLApp::~LLApp() -{ - - // reclaim live file memory - std::for_each(mLiveFiles.begin(), mLiveFiles.end(), DeletePointer()); - mLiveFiles.clear(); - - setStopped(); - - SUBSYSTEM_CLEANUP_DBG(LLCommon); -} - -// static -LLApp* LLApp::instance() -{ - return sApplication; -} - - -LLSD LLApp::getOption(const std::string& name) const -{ - LLSD rv; - LLSD::array_const_iterator iter = mOptions.beginArray(); - LLSD::array_const_iterator end = mOptions.endArray(); - for(; iter != end; ++iter) - { - rv = (*iter)[name]; - if(rv.isDefined()) break; - } - return rv; -} - -bool LLApp::parseCommandOptions(int argc, char** argv) -{ - LLSD commands; - std::string name; - std::string value; - for(int ii = 1; ii < argc; ++ii) - { - if(argv[ii][0] != '-') - { - LL_INFOS() << "Did not find option identifier while parsing token: " - << argv[ii] << LL_ENDL; - return false; - } - int offset = 1; - if(argv[ii][1] == '-') ++offset; - name.assign(&argv[ii][offset]); - if(((ii+1) >= argc) || (argv[ii+1][0] == '-')) - { - // we found another option after this one or we have - // reached the end. simply record that this option was - // found and continue. - int flag = name.compare("logfile"); - if (0 == flag) - { - commands[name] = "log"; - } - else - { - commands[name] = true; - } - - continue; - } - ++ii; - value.assign(argv[ii]); - -#if LL_WINDOWS - //Windows changed command line parsing. Deal with it. - S32 slen = value.length() - 1; - S32 start = 0; - S32 end = slen; - if (argv[ii][start]=='"')start++; - if (argv[ii][end]=='"')end--; - if (start!=0 || end!=slen) - { - value = value.substr (start,end); - } -#endif - - commands[name] = value; - } - setOptionData(PRIORITY_COMMAND_LINE, commands); - return true; -} - -bool LLApp::parseCommandOptions(int argc, wchar_t** wargv) -{ - LLSD commands; - std::string name; - std::string value; - for(int ii = 1; ii < argc; ++ii) - { - if(wargv[ii][0] != '-') - { - LL_INFOS() << "Did not find option identifier while parsing token: " - << wargv[ii] << LL_ENDL; - return false; - } - int offset = 1; - if(wargv[ii][1] == '-') ++offset; - -#if LL_WINDOWS - name.assign(utf16str_to_utf8str(&wargv[ii][offset])); -#else - name.assign(wstring_to_utf8str(&wargv[ii][offset])); -#endif - if(((ii+1) >= argc) || (wargv[ii+1][0] == '-')) - { - // we found another option after this one or we have - // reached the end. simply record that this option was - // found and continue. - int flag = name.compare("logfile"); - if (0 == flag) - { - commands[name] = "log"; - } - else - { - commands[name] = true; - } - - continue; - } - ++ii; - -#if LL_WINDOWS - value.assign(utf16str_to_utf8str((wargv[ii]))); -#else - value.assign(wstring_to_utf8str((wargv[ii]))); -#endif - -#if LL_WINDOWS - //Windows changed command line parsing. Deal with it. - S32 slen = value.length() - 1; - S32 start = 0; - S32 end = slen; - if (wargv[ii][start]=='"')start++; - if (wargv[ii][end]=='"')end--; - if (start!=0 || end!=slen) - { - value = value.substr (start,end); - } -#endif - - commands[name] = value; - } - setOptionData(PRIORITY_COMMAND_LINE, commands); - return true; -} - -void LLApp::manageLiveFile(LLLiveFile* livefile) -{ - if(!livefile) return; - livefile->checkAndReload(); - livefile->addToEventTimer(); - mLiveFiles.push_back(livefile); -} - -bool LLApp::setOptionData(OptionPriority level, LLSD data) -{ - if((level < 0) - || (level >= PRIORITY_COUNT) - || (data.type() != LLSD::TypeMap)) - { - return false; - } - mOptions[level] = data; - return true; -} - -LLSD LLApp::getOptionData(OptionPriority level) -{ - if((level < 0) || (level >= PRIORITY_COUNT)) - { - return LLSD(); - } - return mOptions[level]; -} - -void LLApp::stepFrame() -{ - LLFrameTimer::updateFrameTime(); - LLFrameTimer::updateFrameCount(); - LLEventTimer::updateClass(); - mRunner.run(); -} - -void LLApp::setupErrorHandling(bool second_instance) -{ - // Error handling is done by starting up an error handling thread, which just sleeps and - // occasionally checks to see if the app is in an error state, and sees if it needs to be run. - -#if LL_WINDOWS - -#else // ! LL_WINDOWS - -#if ! defined(LL_BUGSPLAT) - // - // Start up signal handling. - // - // There are two different classes of signals. Synchronous signals are delivered to a specific - // thread, asynchronous signals can be delivered to any thread (in theory) - // - setup_signals(); -#endif // ! LL_BUGSPLAT - -#endif // ! LL_WINDOWS -} - -void LLApp::setErrorHandler(LLAppErrorHandler handler) -{ - LLApp::sErrorHandler = handler; -} - -// static -void LLApp::runErrorHandler() -{ - if (LLApp::sErrorHandler) - { - LLApp::sErrorHandler(); - } - - //LL_INFOS() << "App status now STOPPED" << LL_ENDL; - LLApp::setStopped(); -} - -namespace -{ - -static std::map<LLApp::EAppStatus, const char*> statusDesc -{ - { LLApp::APP_STATUS_RUNNING, "running" }, - { LLApp::APP_STATUS_QUITTING, "quitting" }, - { LLApp::APP_STATUS_STOPPED, "stopped" }, - { LLApp::APP_STATUS_ERROR, "error" } -}; - -} // anonymous namespace - -// static -void LLApp::setStatus(EAppStatus status) -{ - // notify everyone waiting on sStatus any time its value changes - sStatus.set_all(status); - - // This can also happen very late in the application lifecycle -- don't - // resurrect a deleted LLSingleton - if (! LLEventPumps::wasDeleted()) - { - // notify interested parties of status change - LLSD statsd; - auto found = statusDesc.find(status); - if (found != statusDesc.end()) - { - statsd = found->second; - } - else - { - // unknown status? at least report value - statsd = LLSD::Integer(status); - } - LLEventPumps::instance().obtain("LLApp").post(llsd::map("status", statsd)); - } -} - - -// static -void LLApp::setError() -{ - // set app status to ERROR - setStatus(APP_STATUS_ERROR); -} - -void LLApp::setDebugFileNames(const std::string &path) -{ - mStaticDebugFileName = path + "static_debug_info.log"; - mDynamicDebugFileName = path + "dynamic_debug_info.log"; -} - -void LLApp::writeMiniDump() -{ -} - -// static -void LLApp::setQuitting() -{ - if (!isExiting()) - { - // If we're already exiting, we don't want to reset our state back to quitting. - LL_INFOS() << "Setting app state to QUITTING" << LL_ENDL; - setStatus(APP_STATUS_QUITTING); - } -} - - -// static -void LLApp::setStopped() -{ - setStatus(APP_STATUS_STOPPED); -} - - -// static -bool LLApp::isStopped() -{ - return (APP_STATUS_STOPPED == sStatus.get()); -} - - -// static -bool LLApp::isRunning() -{ - return (APP_STATUS_RUNNING == sStatus.get()); -} - - -// static -bool LLApp::isError() -{ - return (APP_STATUS_ERROR == sStatus.get()); -} - - -// static -bool LLApp::isQuitting() -{ - return (APP_STATUS_QUITTING == sStatus.get()); -} - -// static -bool LLApp::isExiting() -{ - return isQuitting() || isError(); -} - -void LLApp::disableCrashlogger() -{ - sDisableCrashlogger = true; -} - -// static -bool LLApp::isCrashloggerDisabled() -{ - return sDisableCrashlogger; -} - -// static -int LLApp::getPid() -{ -#if LL_WINDOWS - return GetCurrentProcessId(); -#else - return getpid(); -#endif -} - -#ifndef LL_WINDOWS -void setup_signals() -{ - // - // Set up signal handlers that may result in program termination - // - struct sigaction act; - act.sa_sigaction = default_unix_signal_handler; - sigemptyset( &act.sa_mask ); - act.sa_flags = SA_SIGINFO; - - // Synchronous signals -# ifndef LL_BUGSPLAT - sigaction(SIGABRT, &act, NULL); -# endif - sigaction(SIGALRM, &act, NULL); - sigaction(SIGBUS, &act, NULL); - sigaction(SIGFPE, &act, NULL); - sigaction(SIGHUP, &act, NULL); - sigaction(SIGILL, &act, NULL); - sigaction(SIGPIPE, &act, NULL); - sigaction(SIGSEGV, &act, NULL); - sigaction(SIGSYS, &act, NULL); - - sigaction(LL_HEARTBEAT_SIGNAL, &act, NULL); - sigaction(LL_SMACKDOWN_SIGNAL, &act, NULL); - - // Asynchronous signals that are normally ignored -#ifndef LL_IGNORE_SIGCHLD - sigaction(SIGCHLD, &act, NULL); -#endif // LL_IGNORE_SIGCHLD - sigaction(SIGUSR2, &act, NULL); - - // Asynchronous signals that result in attempted graceful exit - sigaction(SIGHUP, &act, NULL); - sigaction(SIGTERM, &act, NULL); - sigaction(SIGINT, &act, NULL); - - // Asynchronous signals that result in core - sigaction(SIGQUIT, &act, NULL); - -} - -void clear_signals() -{ - struct sigaction act; - act.sa_handler = SIG_DFL; - sigemptyset( &act.sa_mask ); - act.sa_flags = SA_SIGINFO; - - // Synchronous signals -# ifndef LL_BUGSPLAT - sigaction(SIGABRT, &act, NULL); -# endif - sigaction(SIGALRM, &act, NULL); - sigaction(SIGBUS, &act, NULL); - sigaction(SIGFPE, &act, NULL); - sigaction(SIGHUP, &act, NULL); - sigaction(SIGILL, &act, NULL); - sigaction(SIGPIPE, &act, NULL); - sigaction(SIGSEGV, &act, NULL); - sigaction(SIGSYS, &act, NULL); - - sigaction(LL_HEARTBEAT_SIGNAL, &act, NULL); - sigaction(LL_SMACKDOWN_SIGNAL, &act, NULL); - - // Asynchronous signals that are normally ignored -#ifndef LL_IGNORE_SIGCHLD - sigaction(SIGCHLD, &act, NULL); -#endif // LL_IGNORE_SIGCHLD - - // Asynchronous signals that result in attempted graceful exit - sigaction(SIGHUP, &act, NULL); - sigaction(SIGTERM, &act, NULL); - sigaction(SIGINT, &act, NULL); - - // Asynchronous signals that result in core - sigaction(SIGUSR2, &act, NULL); - sigaction(SIGQUIT, &act, NULL); -} - - - -void default_unix_signal_handler(int signum, siginfo_t *info, void *) -{ - // Unix implementation of synchronous signal handler - // This runs in the thread that threw the signal. - // We do the somewhat sketchy operation of blocking in here until the error handler - // has gracefully stopped the app. - - if (LLApp::sLogInSignal) - { - LL_INFOS() << "Signal handler - Got signal " << signum << " - " << apr_signal_description_get(signum) << LL_ENDL; - } - - - switch (signum) - { - case SIGCHLD: - if (LLApp::sLogInSignal) - { - LL_INFOS() << "Signal handler - Got SIGCHLD from " << info->si_pid << LL_ENDL; - } - - return; - case SIGABRT: - // Note that this handler is not set for SIGABRT when using Bugsplat - // Abort just results in termination of the app, no funky error handling. - if (LLApp::sLogInSignal) - { - LL_WARNS() << "Signal handler - Got SIGABRT, terminating" << LL_ENDL; - } - clear_signals(); - raise(signum); - return; - case SIGINT: - case SIGHUP: - case SIGTERM: - if (LLApp::sLogInSignal) - { - LL_WARNS() << "Signal handler - Got SIGINT, HUP, or TERM, exiting gracefully" << LL_ENDL; - } - // Graceful exit - // Just set our state to quitting, not error - if (LLApp::isQuitting() || LLApp::isError()) - { - // We're already trying to die, just ignore this signal - if (LLApp::sLogInSignal) - { - LL_INFOS() << "Signal handler - Already trying to quit, ignoring signal!" << LL_ENDL; - } - return; - } - LLApp::setQuitting(); - return; - case SIGALRM: - case SIGPIPE: - case SIGUSR2: - default: - if (signum == LL_SMACKDOWN_SIGNAL || - signum == SIGBUS || - signum == SIGILL || - signum == SIGFPE || - signum == SIGSEGV || - signum == SIGQUIT) - { - if (signum == LL_SMACKDOWN_SIGNAL) - { - // Smackdown treated just like any other app termination, for now - if (LLApp::sLogInSignal) - { - LL_WARNS() << "Signal handler - Handling smackdown signal!" << LL_ENDL; - } - else - { - // Don't log anything, even errors - this is because this signal could happen anywhere. - LLError::setDefaultLevel(LLError::LEVEL_NONE); - } - - // Change the signal that we reraise to SIGABRT, so we generate a core dump. - signum = SIGABRT; - } - - if (LLApp::sLogInSignal) - { - LL_WARNS() << "Signal handler - Handling fatal signal!" << LL_ENDL; - } - if (LLApp::isError()) - { - // Received second fatal signal while handling first, just die right now - // Set the signal handlers back to default before handling the signal - this makes the next signal wipe out the app. - clear_signals(); - - if (LLApp::sLogInSignal) - { - LL_WARNS() << "Signal handler - Got another fatal signal while in the error handler, die now!" << LL_ENDL; - } - raise(signum); - return; - } - - if (LLApp::sLogInSignal) - { - LL_WARNS() << "Signal handler - Flagging error status and waiting for shutdown" << LL_ENDL; - } - - if (LLApp::isCrashloggerDisabled()) // Don't gracefully handle any signal, crash and core for a gdb post mortem - { - clear_signals(); - LL_WARNS() << "Fatal signal received, not handling the crash here, passing back to operating system" << LL_ENDL; - raise(signum); - return; - } - - // Flag status to ERROR - LLApp::setError(); - - if (LLApp::sLogInSignal) - { - LL_WARNS() << "Signal handler - App is stopped, reraising signal" << LL_ENDL; - } - clear_signals(); - raise(signum); - return; - } else { - if (LLApp::sLogInSignal) - { - LL_INFOS() << "Signal handler - Unhandled signal " << signum << ", ignoring!" << LL_ENDL; - } - } - } -} - -bool unix_post_minidump_callback(const char *dump_dir, - const char *minidump_id, - void *context, bool succeeded) -{ - // Copy minidump file path into fixed buffer in the app instance to avoid - // heap allocations in a crash handler. - - // path format: <dump_dir>/<minidump_id>.dmp - auto dirPathLength = strlen(dump_dir); - auto idLength = strlen(minidump_id); - - // The path must not be truncated. - llassert((dirPathLength + idLength + 5) <= LLApp::MAX_MINDUMP_PATH_LENGTH); - - char * path = LLApp::instance()->getMiniDumpFilename(); - auto remaining = LLApp::MAX_MINDUMP_PATH_LENGTH; - strncpy(path, dump_dir, remaining); - remaining -= dirPathLength; - path += dirPathLength; - if (remaining > 0 && dirPathLength > 0 && path[-1] != '/') - { - *path++ = '/'; - --remaining; - } - if (remaining > 0) - { - strncpy(path, minidump_id, remaining); - remaining -= idLength; - path += idLength; - strncpy(path, ".dmp", remaining); - } - - LL_INFOS("CRASHREPORT") << "generated minidump: " << LLApp::instance()->getMiniDumpFilename() << LL_ENDL; - LLApp::runErrorHandler(); - -#ifndef LL_RELEASE_FOR_DOWNLOAD - clear_signals(); - return false; -#else - return true; -#endif -} -#endif // !WINDOWS - +/**
+ * @file llapp.cpp
+ * @brief Implementation of the LLApp class.
+ *
+ * $LicenseInfo:firstyear=2003&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llapp.h"
+
+#include <cstdlib>
+
+#ifdef LL_DARWIN
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/sysctl.h>
+#endif
+
+#include "llcommon.h"
+#include "llapr.h"
+#include "llerrorcontrol.h"
+#include "llframetimer.h"
+#include "lllivefile.h"
+#include "llmemory.h"
+#include "llstl.h" // for DeletePointer()
+#include "llstring.h"
+#include "lleventtimer.h"
+#include "stringize.h"
+#include "llcleanup.h"
+#include "llevents.h"
+#include "llsdutil.h"
+
+//
+// Signal handling
+#ifndef LL_WINDOWS
+# include <signal.h>
+# include <unistd.h> // for fork()
+void setup_signals();
+void default_unix_signal_handler(int signum, siginfo_t *info, void *);
+
+#if LL_LINUX
+#else
+// Called by breakpad exception handler after the minidump has been generated.
+bool unix_post_minidump_callback(const char *dump_dir,
+ const char *minidump_id,
+ void *context, bool succeeded);
+#endif
+
+# if LL_DARWIN
+/* OSX doesn't support SIGRT* */
+S32 LL_SMACKDOWN_SIGNAL = SIGUSR1;
+S32 LL_HEARTBEAT_SIGNAL = SIGUSR2;
+# else // linux or (assumed) other similar unixoid
+/* We want reliable delivery of our signals - SIGRT* is it. */
+/* Old LinuxThreads versions eat SIGRTMIN+0 to SIGRTMIN+2, avoid those. */
+/* Note that SIGRTMIN/SIGRTMAX may expand to a glibc function call with a
+ nonconstant result so these are not consts and cannot be used in constant-
+ expressions. SIGRTMAX may return -1 on rare broken setups. */
+S32 LL_SMACKDOWN_SIGNAL = (SIGRTMAX >= 0) ? (SIGRTMAX-1) : SIGUSR1;
+S32 LL_HEARTBEAT_SIGNAL = (SIGRTMAX >= 0) ? (SIGRTMAX-0) : SIGUSR2;
+# endif // LL_DARWIN
+#endif // !LL_WINDOWS
+
+// the static application instance
+LLApp* LLApp::sApplication = NULL;
+
+// Allows the generation of core files for post mortem under gdb
+// and disables crashlogger
+bool LLApp::sDisableCrashlogger = false;
+
+// Local flag for whether or not to do logging in signal handlers.
+//static
+bool LLApp::sLogInSignal = false;
+
+// static
+// Keeps track of application status
+LLScalarCond<LLApp::EAppStatus> LLApp::sStatus{LLApp::APP_STATUS_STOPPED};
+LLAppErrorHandler LLApp::sErrorHandler = NULL;
+
+
+LLApp::LLApp()
+{
+ // Set our status to running
+ setStatus(APP_STATUS_RUNNING);
+
+ LLCommon::initClass();
+
+ // initialize the options structure. We need to make this an array
+ // because the structured data will not auto-allocate if we
+ // reference an invalid location with the [] operator.
+ mOptions = LLSD::emptyArray();
+ LLSD sd;
+ for(int i = 0; i < PRIORITY_COUNT; ++i)
+ {
+ mOptions.append(sd);
+ }
+
+ // Make sure we clean up APR when we exit
+ // Don't need to do this if we're cleaning up APR in the destructor
+ //atexit(ll_cleanup_apr);
+
+ // Set the application to this instance.
+ sApplication = this;
+
+ // initialize the buffer to write the minidump filename to
+ // (this is used to avoid allocating memory in the crash handler)
+ memset(mMinidumpPath, 0, MAX_MINDUMP_PATH_LENGTH);
+ mCrashReportPipeStr = L"\\\\.\\pipe\\LLCrashReporterPipe";
+}
+
+
+LLApp::~LLApp()
+{
+
+ // reclaim live file memory
+ std::for_each(mLiveFiles.begin(), mLiveFiles.end(), DeletePointer());
+ mLiveFiles.clear();
+
+ setStopped();
+
+ SUBSYSTEM_CLEANUP_DBG(LLCommon);
+}
+
+// static
+LLApp* LLApp::instance()
+{
+ return sApplication;
+}
+
+
+LLSD LLApp::getOption(const std::string& name) const
+{
+ LLSD rv;
+ LLSD::array_const_iterator iter = mOptions.beginArray();
+ LLSD::array_const_iterator end = mOptions.endArray();
+ for(; iter != end; ++iter)
+ {
+ rv = (*iter)[name];
+ if(rv.isDefined()) break;
+ }
+ return rv;
+}
+
+bool LLApp::parseCommandOptions(int argc, char** argv)
+{
+ LLSD commands;
+ std::string name;
+ std::string value;
+ for(int ii = 1; ii < argc; ++ii)
+ {
+ if(argv[ii][0] != '-')
+ {
+ LL_INFOS() << "Did not find option identifier while parsing token: "
+ << argv[ii] << LL_ENDL;
+ return false;
+ }
+ int offset = 1;
+ if(argv[ii][1] == '-') ++offset;
+ name.assign(&argv[ii][offset]);
+ if(((ii+1) >= argc) || (argv[ii+1][0] == '-'))
+ {
+ // we found another option after this one or we have
+ // reached the end. simply record that this option was
+ // found and continue.
+ int flag = name.compare("logfile");
+ if (0 == flag)
+ {
+ commands[name] = "log";
+ }
+ else
+ {
+ commands[name] = true;
+ }
+
+ continue;
+ }
+ ++ii;
+ value.assign(argv[ii]);
+
+#if LL_WINDOWS
+ //Windows changed command line parsing. Deal with it.
+ S32 slen = value.length() - 1;
+ S32 start = 0;
+ S32 end = slen;
+ if (argv[ii][start]=='"')start++;
+ if (argv[ii][end]=='"')end--;
+ if (start!=0 || end!=slen)
+ {
+ value = value.substr (start,end);
+ }
+#endif
+
+ commands[name] = value;
+ }
+ setOptionData(PRIORITY_COMMAND_LINE, commands);
+ return true;
+}
+
+bool LLApp::parseCommandOptions(int argc, wchar_t** wargv)
+{
+ LLSD commands;
+ std::string name;
+ std::string value;
+ for(int ii = 1; ii < argc; ++ii)
+ {
+ if(wargv[ii][0] != '-')
+ {
+ LL_INFOS() << "Did not find option identifier while parsing token: "
+ << wargv[ii] << LL_ENDL;
+ return false;
+ }
+ int offset = 1;
+ if(wargv[ii][1] == '-') ++offset;
+
+#if LL_WINDOWS
+ name.assign(utf16str_to_utf8str(&wargv[ii][offset]));
+#else
+ name.assign(wstring_to_utf8str(&wargv[ii][offset]));
+#endif
+ if(((ii+1) >= argc) || (wargv[ii+1][0] == '-'))
+ {
+ // we found another option after this one or we have
+ // reached the end. simply record that this option was
+ // found and continue.
+ int flag = name.compare("logfile");
+ if (0 == flag)
+ {
+ commands[name] = "log";
+ }
+ else
+ {
+ commands[name] = true;
+ }
+
+ continue;
+ }
+ ++ii;
+
+#if LL_WINDOWS
+ value.assign(utf16str_to_utf8str((wargv[ii])));
+#else
+ value.assign(wstring_to_utf8str((wargv[ii])));
+#endif
+
+#if LL_WINDOWS
+ //Windows changed command line parsing. Deal with it.
+ S32 slen = value.length() - 1;
+ S32 start = 0;
+ S32 end = slen;
+ if (wargv[ii][start]=='"')start++;
+ if (wargv[ii][end]=='"')end--;
+ if (start!=0 || end!=slen)
+ {
+ value = value.substr (start,end);
+ }
+#endif
+
+ commands[name] = value;
+ }
+ setOptionData(PRIORITY_COMMAND_LINE, commands);
+ return true;
+}
+
+void LLApp::manageLiveFile(LLLiveFile* livefile)
+{
+ if(!livefile) return;
+ livefile->checkAndReload();
+ livefile->addToEventTimer();
+ mLiveFiles.push_back(livefile);
+}
+
+bool LLApp::setOptionData(OptionPriority level, LLSD data)
+{
+ if((level < 0)
+ || (level >= PRIORITY_COUNT)
+ || (data.type() != LLSD::TypeMap))
+ {
+ return false;
+ }
+ mOptions[level] = data;
+ return true;
+}
+
+LLSD LLApp::getOptionData(OptionPriority level)
+{
+ if((level < 0) || (level >= PRIORITY_COUNT))
+ {
+ return LLSD();
+ }
+ return mOptions[level];
+}
+
+void LLApp::stepFrame()
+{
+ LLFrameTimer::updateFrameTime();
+ LLFrameTimer::updateFrameCount();
+ LLEventTimer::updateClass();
+ mRunner.run();
+}
+
+void LLApp::setupErrorHandling(bool second_instance)
+{
+ // Error handling is done by starting up an error handling thread, which just sleeps and
+ // occasionally checks to see if the app is in an error state, and sees if it needs to be run.
+
+#if LL_WINDOWS
+
+#else // ! LL_WINDOWS
+
+#if ! defined(LL_BUGSPLAT)
+ //
+ // Start up signal handling.
+ //
+ // There are two different classes of signals. Synchronous signals are delivered to a specific
+ // thread, asynchronous signals can be delivered to any thread (in theory)
+ //
+ setup_signals();
+#endif // ! LL_BUGSPLAT
+
+#endif // ! LL_WINDOWS
+}
+
+void LLApp::setErrorHandler(LLAppErrorHandler handler)
+{
+ LLApp::sErrorHandler = handler;
+}
+
+// static
+void LLApp::runErrorHandler()
+{
+ if (LLApp::sErrorHandler)
+ {
+ LLApp::sErrorHandler();
+ }
+
+ //LL_INFOS() << "App status now STOPPED" << LL_ENDL;
+ LLApp::setStopped();
+}
+
+namespace
+{
+
+static std::map<LLApp::EAppStatus, const char*> statusDesc
+{
+ { LLApp::APP_STATUS_RUNNING, "running" },
+ { LLApp::APP_STATUS_QUITTING, "quitting" },
+ { LLApp::APP_STATUS_STOPPED, "stopped" },
+ { LLApp::APP_STATUS_ERROR, "error" }
+};
+
+} // anonymous namespace
+
+// static
+void LLApp::setStatus(EAppStatus status)
+{
+ // notify everyone waiting on sStatus any time its value changes
+ sStatus.set_all(status);
+
+ // This can also happen very late in the application lifecycle -- don't
+ // resurrect a deleted LLSingleton
+ if (! LLEventPumps::wasDeleted())
+ {
+ // notify interested parties of status change
+ LLSD statsd;
+ auto found = statusDesc.find(status);
+ if (found != statusDesc.end())
+ {
+ statsd = found->second;
+ }
+ else
+ {
+ // unknown status? at least report value
+ statsd = LLSD::Integer(status);
+ }
+ LLEventPumps::instance().obtain("LLApp").post(llsd::map("status", statsd));
+ }
+}
+
+
+// static
+void LLApp::setError()
+{
+ // set app status to ERROR
+ setStatus(APP_STATUS_ERROR);
+}
+
+void LLApp::setDebugFileNames(const std::string &path)
+{
+ mStaticDebugFileName = path + "static_debug_info.log";
+ mDynamicDebugFileName = path + "dynamic_debug_info.log";
+}
+
+void LLApp::writeMiniDump()
+{
+}
+
+// static
+void LLApp::setQuitting()
+{
+ if (!isExiting())
+ {
+ // If we're already exiting, we don't want to reset our state back to quitting.
+ LL_INFOS() << "Setting app state to QUITTING" << LL_ENDL;
+ setStatus(APP_STATUS_QUITTING);
+ }
+}
+
+
+// static
+void LLApp::setStopped()
+{
+ setStatus(APP_STATUS_STOPPED);
+}
+
+
+// static
+bool LLApp::isStopped()
+{
+ return (APP_STATUS_STOPPED == sStatus.get());
+}
+
+
+// static
+bool LLApp::isRunning()
+{
+ return (APP_STATUS_RUNNING == sStatus.get());
+}
+
+
+// static
+bool LLApp::isError()
+{
+ return (APP_STATUS_ERROR == sStatus.get());
+}
+
+
+// static
+bool LLApp::isQuitting()
+{
+ return (APP_STATUS_QUITTING == sStatus.get());
+}
+
+// static
+bool LLApp::isExiting()
+{
+ return isQuitting() || isError();
+}
+
+void LLApp::disableCrashlogger()
+{
+ sDisableCrashlogger = true;
+}
+
+// static
+bool LLApp::isCrashloggerDisabled()
+{
+ return sDisableCrashlogger;
+}
+
+// static
+int LLApp::getPid()
+{
+#if LL_WINDOWS
+ return GetCurrentProcessId();
+#else
+ return getpid();
+#endif
+}
+
+#ifndef LL_WINDOWS
+void setup_signals()
+{
+ //
+ // Set up signal handlers that may result in program termination
+ //
+ struct sigaction act;
+ act.sa_sigaction = default_unix_signal_handler;
+ sigemptyset( &act.sa_mask );
+ act.sa_flags = SA_SIGINFO;
+
+ // Synchronous signals
+# ifndef LL_BUGSPLAT
+ sigaction(SIGABRT, &act, NULL);
+# endif
+ sigaction(SIGALRM, &act, NULL);
+ sigaction(SIGBUS, &act, NULL);
+ sigaction(SIGFPE, &act, NULL);
+ sigaction(SIGHUP, &act, NULL);
+ sigaction(SIGILL, &act, NULL);
+ sigaction(SIGPIPE, &act, NULL);
+ sigaction(SIGSEGV, &act, NULL);
+ sigaction(SIGSYS, &act, NULL);
+
+ sigaction(LL_HEARTBEAT_SIGNAL, &act, NULL);
+ sigaction(LL_SMACKDOWN_SIGNAL, &act, NULL);
+
+ // Asynchronous signals that are normally ignored
+#ifndef LL_IGNORE_SIGCHLD
+ sigaction(SIGCHLD, &act, NULL);
+#endif // LL_IGNORE_SIGCHLD
+ sigaction(SIGUSR2, &act, NULL);
+
+ // Asynchronous signals that result in attempted graceful exit
+ sigaction(SIGHUP, &act, NULL);
+ sigaction(SIGTERM, &act, NULL);
+ sigaction(SIGINT, &act, NULL);
+
+ // Asynchronous signals that result in core
+ sigaction(SIGQUIT, &act, NULL);
+
+}
+
+void clear_signals()
+{
+ struct sigaction act;
+ act.sa_handler = SIG_DFL;
+ sigemptyset( &act.sa_mask );
+ act.sa_flags = SA_SIGINFO;
+
+ // Synchronous signals
+# ifndef LL_BUGSPLAT
+ sigaction(SIGABRT, &act, NULL);
+# endif
+ sigaction(SIGALRM, &act, NULL);
+ sigaction(SIGBUS, &act, NULL);
+ sigaction(SIGFPE, &act, NULL);
+ sigaction(SIGHUP, &act, NULL);
+ sigaction(SIGILL, &act, NULL);
+ sigaction(SIGPIPE, &act, NULL);
+ sigaction(SIGSEGV, &act, NULL);
+ sigaction(SIGSYS, &act, NULL);
+
+ sigaction(LL_HEARTBEAT_SIGNAL, &act, NULL);
+ sigaction(LL_SMACKDOWN_SIGNAL, &act, NULL);
+
+ // Asynchronous signals that are normally ignored
+#ifndef LL_IGNORE_SIGCHLD
+ sigaction(SIGCHLD, &act, NULL);
+#endif // LL_IGNORE_SIGCHLD
+
+ // Asynchronous signals that result in attempted graceful exit
+ sigaction(SIGHUP, &act, NULL);
+ sigaction(SIGTERM, &act, NULL);
+ sigaction(SIGINT, &act, NULL);
+
+ // Asynchronous signals that result in core
+ sigaction(SIGUSR2, &act, NULL);
+ sigaction(SIGQUIT, &act, NULL);
+}
+
+
+
+void default_unix_signal_handler(int signum, siginfo_t *info, void *)
+{
+ // Unix implementation of synchronous signal handler
+ // This runs in the thread that threw the signal.
+ // We do the somewhat sketchy operation of blocking in here until the error handler
+ // has gracefully stopped the app.
+
+ if (LLApp::sLogInSignal)
+ {
+ LL_INFOS() << "Signal handler - Got signal " << signum << " - " << apr_signal_description_get(signum) << LL_ENDL;
+ }
+
+
+ switch (signum)
+ {
+ case SIGCHLD:
+ if (LLApp::sLogInSignal)
+ {
+ LL_INFOS() << "Signal handler - Got SIGCHLD from " << info->si_pid << LL_ENDL;
+ }
+
+ return;
+ case SIGABRT:
+ // Note that this handler is not set for SIGABRT when using Bugsplat
+ // Abort just results in termination of the app, no funky error handling.
+ if (LLApp::sLogInSignal)
+ {
+ LL_WARNS() << "Signal handler - Got SIGABRT, terminating" << LL_ENDL;
+ }
+ clear_signals();
+ raise(signum);
+ return;
+ case SIGINT:
+ case SIGHUP:
+ case SIGTERM:
+ if (LLApp::sLogInSignal)
+ {
+ LL_WARNS() << "Signal handler - Got SIGINT, HUP, or TERM, exiting gracefully" << LL_ENDL;
+ }
+ // Graceful exit
+ // Just set our state to quitting, not error
+ if (LLApp::isQuitting() || LLApp::isError())
+ {
+ // We're already trying to die, just ignore this signal
+ if (LLApp::sLogInSignal)
+ {
+ LL_INFOS() << "Signal handler - Already trying to quit, ignoring signal!" << LL_ENDL;
+ }
+ return;
+ }
+ LLApp::setQuitting();
+ return;
+ case SIGALRM:
+ case SIGPIPE:
+ case SIGUSR2:
+ default:
+ if (signum == LL_SMACKDOWN_SIGNAL ||
+ signum == SIGBUS ||
+ signum == SIGILL ||
+ signum == SIGFPE ||
+ signum == SIGSEGV ||
+ signum == SIGQUIT)
+ {
+ if (signum == LL_SMACKDOWN_SIGNAL)
+ {
+ // Smackdown treated just like any other app termination, for now
+ if (LLApp::sLogInSignal)
+ {
+ LL_WARNS() << "Signal handler - Handling smackdown signal!" << LL_ENDL;
+ }
+ else
+ {
+ // Don't log anything, even errors - this is because this signal could happen anywhere.
+ LLError::setDefaultLevel(LLError::LEVEL_NONE);
+ }
+
+ // Change the signal that we reraise to SIGABRT, so we generate a core dump.
+ signum = SIGABRT;
+ }
+
+ if (LLApp::sLogInSignal)
+ {
+ LL_WARNS() << "Signal handler - Handling fatal signal!" << LL_ENDL;
+ }
+ if (LLApp::isError())
+ {
+ // Received second fatal signal while handling first, just die right now
+ // Set the signal handlers back to default before handling the signal - this makes the next signal wipe out the app.
+ clear_signals();
+
+ if (LLApp::sLogInSignal)
+ {
+ LL_WARNS() << "Signal handler - Got another fatal signal while in the error handler, die now!" << LL_ENDL;
+ }
+ raise(signum);
+ return;
+ }
+
+ if (LLApp::sLogInSignal)
+ {
+ LL_WARNS() << "Signal handler - Flagging error status and waiting for shutdown" << LL_ENDL;
+ }
+
+ if (LLApp::isCrashloggerDisabled()) // Don't gracefully handle any signal, crash and core for a gdb post mortem
+ {
+ clear_signals();
+ LL_WARNS() << "Fatal signal received, not handling the crash here, passing back to operating system" << LL_ENDL;
+ raise(signum);
+ return;
+ }
+
+ // Flag status to ERROR
+ LLApp::setError();
+
+ if (LLApp::sLogInSignal)
+ {
+ LL_WARNS() << "Signal handler - App is stopped, reraising signal" << LL_ENDL;
+ }
+ clear_signals();
+ raise(signum);
+ return;
+ } else {
+ if (LLApp::sLogInSignal)
+ {
+ LL_INFOS() << "Signal handler - Unhandled signal " << signum << ", ignoring!" << LL_ENDL;
+ }
+ }
+ }
+}
+
+bool unix_post_minidump_callback(const char *dump_dir,
+ const char *minidump_id,
+ void *context, bool succeeded)
+{
+ // Copy minidump file path into fixed buffer in the app instance to avoid
+ // heap allocations in a crash handler.
+
+ // path format: <dump_dir>/<minidump_id>.dmp
+ auto dirPathLength = strlen(dump_dir);
+ auto idLength = strlen(minidump_id);
+
+ // The path must not be truncated.
+ llassert((dirPathLength + idLength + 5) <= LLApp::MAX_MINDUMP_PATH_LENGTH);
+
+ char * path = LLApp::instance()->getMiniDumpFilename();
+ auto remaining = LLApp::MAX_MINDUMP_PATH_LENGTH;
+ strncpy(path, dump_dir, remaining);
+ remaining -= dirPathLength;
+ path += dirPathLength;
+ if (remaining > 0 && dirPathLength > 0 && path[-1] != '/')
+ {
+ *path++ = '/';
+ --remaining;
+ }
+ if (remaining > 0)
+ {
+ strncpy(path, minidump_id, remaining);
+ remaining -= idLength;
+ path += idLength;
+ strncpy(path, ".dmp", remaining);
+ }
+
+ LL_INFOS("CRASHREPORT") << "generated minidump: " << LLApp::instance()->getMiniDumpFilename() << LL_ENDL;
+ LLApp::runErrorHandler();
+
+#ifndef LL_RELEASE_FOR_DOWNLOAD
+ clear_signals();
+ return false;
+#else
+ return true;
+#endif
+}
+#endif // !WINDOWS
+
diff --git a/indra/llcommon/llapp.h b/indra/llcommon/llapp.h index 9eaf0f8547..79b6e03581 100644 --- a/indra/llcommon/llapp.h +++ b/indra/llcommon/llapp.h @@ -1,342 +1,342 @@ -/** - * @file llapp.h - * @brief Declaration of the LLApp class. - * - * $LicenseInfo:firstyear=2003&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLAPP_H -#define LL_LLAPP_H - -#include <map> -#include "llcond.h" -#include "llrun.h" -#include "llsd.h" -#include <atomic> -#include <chrono> -// Forward declarations -class LLLiveFile; -#if LL_LINUX -#include <signal.h> -#endif - -typedef void (*LLAppErrorHandler)(); - -#if !LL_WINDOWS -extern S32 LL_SMACKDOWN_SIGNAL; -extern S32 LL_HEARTBEAT_SIGNAL; - -// Clear all of the signal handlers (which we want to do for the child process when we fork -void clear_signals(); - -#endif - -class LL_COMMON_API LLApp -{ -public: - typedef enum e_app_status - { - APP_STATUS_RUNNING, // The application is currently running - the default status - APP_STATUS_QUITTING, // The application is currently quitting - threads should listen for this and clean up - APP_STATUS_STOPPED, // The application is no longer running - tells the error thread it can exit - APP_STATUS_ERROR // The application had a fatal error occur - tells the error thread to run - } EAppStatus; - - - LLApp(); - virtual ~LLApp(); - - /** - * @brief Return the static app instance if one was created. - */ - static LLApp* instance(); - - /** @name Runtime options */ - //@{ - /** - * @brief Enumeration to specify option priorities in highest to - * lowest order. - */ - enum OptionPriority - { - PRIORITY_RUNTIME_OVERRIDE, - PRIORITY_COMMAND_LINE, - PRIORITY_SPECIFIC_CONFIGURATION, - PRIORITY_GENERAL_CONFIGURATION, - PRIORITY_DEFAULT, - PRIORITY_COUNT - }; - - /** - * @brief Get the application option at the highest priority. - * - * If the return value is undefined, the option does not exist. - * @param name The name of the option. - * @return Returns the option data. - */ - LLSD getOption(const std::string& name) const; - - /** - * @brief Parse ASCII command line options and insert them into - * application command line options. - * - * The name inserted into the option will have leading option - * identifiers (a minus or double minus) stripped. All options - * with values will be stored as a string, while all options - * without values will be stored as true. - * @param argc The argc passed into main(). - * @param argv The argv passed into main(). - * @return Returns true if the parse succeeded. - */ - bool parseCommandOptions(int argc, char** argv); - - /** - * @brief Parse Unicode command line options and insert them into - * application command line options. - * - * The name inserted into the option will have leading option - * identifiers (a minus or double minus) stripped. All options - * with values will be stored as a string, while all options - * without values will be stored as true. - * @param argc The argc passed into main(). - * @param wargv The wargv passed into main(). - * @return Returns true if the parse succeeded. - */ - bool parseCommandOptions(int argc, wchar_t** wargv); - - /** - * @brief Keep track of live files automatically. - * - * *TODO: it currently uses the <code>addToEventTimer()</code> API - * instead of the runner. I should probalby use the runner. - * - * *NOTE: DO NOT add the livefile instance to any kind of check loop. - * - * @param livefile A valid instance of an LLLiveFile. This LLApp - * instance will delete the livefile instance. - */ - void manageLiveFile(LLLiveFile* livefile); - - /** - * @brief Set the options at the specified priority. - * - * This function completely replaces the options at the priority - * level with the data specified. This function will make sure - * level and data might be valid before doing the replace. - * @param level The priority level of the data. - * @param data The data to set. - * @return Returns true if the option was set. - */ - bool setOptionData(OptionPriority level, LLSD data); - - /** - * @brief Get the option data at the specified priority. - * - * This method is probably not so useful except when merging - * information. - * @param level The priority level of the data. - * @return Returns The data (if any) at the level priority. - */ - LLSD getOptionData(OptionPriority level); - //@} - - - - // - // Main application logic - // - virtual bool init() = 0; // Override to do application initialization - - // - // cleanup() - // - // It's currently assumed that the cleanup() method will only get - // called from the main thread or the error handling thread, as it will - // likely do thread shutdown, among other things. - // - virtual bool cleanup() = 0; // Override to do application cleanup - - // - // frame() - // - // Pass control to the application for a single frame. Returns 'done' - // flag: if frame() returns false, it expects to be called again. - // - virtual bool frame() = 0; // Override for application body logic - - // - // Crash logging - // - void disableCrashlogger(); // Let the OS handle the crashes - static bool isCrashloggerDisabled(); // Get the here above set value - - // - // Application status - // - static void setQuitting(); // Set status to QUITTING, the app is now shutting down - static void setStopped(); // Set status to STOPPED, the app is done running and should exit - static void setError(); // Set status to ERROR, the error handler should run - static bool isStopped(); - static bool isRunning(); - static bool isQuitting(); - static bool isError(); - static bool isExiting(); // Either quitting or error (app is exiting, cleanly or not) - static int getPid(); - - // - // Sleep for specified time while still running - // - // For use by a coroutine or thread that performs some maintenance on a - // periodic basis. (See also LLEventTimer.) This method supports the - // pattern of an "infinite" loop that sleeps for some time, performs some - // action, then sleeps again. The trouble with literally sleeping a worker - // thread is that it could potentially sleep right through attempted - // application shutdown. This method avoids that by returning false as - // soon as the application status changes away from APP_STATUS_RUNNING - // (isRunning()). - // - // sleep() returns true if it sleeps undisturbed for the entire specified - // duration. The idea is that you can code 'while sleep(duration) ...', - // which will break the loop once shutdown begins. - // - // Since any time-based LLUnit should be implicitly convertible to - // F32Milliseconds, accept that specific type as a proxy. - static bool sleep(F32Milliseconds duration); - // Allow any duration defined in terms of <chrono>. - // One can imagine a wonderfully general bidirectional conversion system - // between any type derived from LLUnits::LLUnit<T, LLUnits::Seconds> and - // any std::chrono::duration -- but that doesn't yet exist. - template <typename Rep, typename Period> - bool sleep(const std::chrono::duration<Rep, Period>& duration) - { - // wait_for_unequal() has the opposite bool return convention - return ! sStatus.wait_for_unequal(duration, APP_STATUS_RUNNING); - } - - /** @name Error handling methods */ - //@{ - /** - * @brief Do our generic platform-specific error-handling setup -- - * signals on unix, structured exceptions on windows. - * - * DO call this method if your app will either spawn children or be - * spawned by a launcher. - * Call just after app object construction. - * (Otherwise your app will crash when getting signals, - * and will not core dump.) - * - * DO NOT call this method if your application has specialized - * error handling code. - */ - void setupErrorHandling(bool mSecondInstance=false); - - void setErrorHandler(LLAppErrorHandler handler); - static void runErrorHandler(); // run shortly after we detect an error - //@} - - // the maximum length of the minidump filename returned by getMiniDumpFilename() - static const U32 MAX_MINDUMP_PATH_LENGTH = 256; - - // change the directory where Breakpad minidump files are written to - void setDebugFileNames(const std::string &path); - - // Return the Google Breakpad minidump filename after a crash. - char *getMiniDumpFilename() { return mMinidumpPath; } - std::string* getStaticDebugFile() { return &mStaticDebugFileName; } - std::string* getDynamicDebugFile() { return &mDynamicDebugFileName; } - - // Write out a Google Breakpad minidump file. - void writeMiniDump(); - - - /** - * @brief Get a reference to the application runner - * - * Please use the runner with caution. Since the Runner usage - * pattern is not yet clear, this method just gives access to it - * to add and remove runnables. - * @return Returns the application runner. Do not save the - * pointer past the caller's stack frame. - */ - LLRunner& getRunner() { return mRunner; } - -#ifdef LL_WINDOWS - virtual void reportCrashToBugsplat(void* pExcepInfo /*EXCEPTION_POINTERS*/) { } -#endif - -public: - typedef std::map<std::string, std::string> string_map; - string_map mOptionMap; // Contains all command-line options and arguments in a map - -protected: - - static void setStatus(EAppStatus status); // Use this to change the application status. - static LLScalarCond<EAppStatus> sStatus; // Reflects current application status - static bool sDisableCrashlogger; // Let the OS handle crashes for us. - std::wstring mCrashReportPipeStr; //Name of pipe to use for crash reporting. - - std::string mDumpPath; //output path for google breakpad. Dependency workaround. - - /** - * @brief This method is called once a frame to do once a frame tasks. - */ - void stepFrame(); - -private: - // Contains the filename of the minidump file after a crash. - char mMinidumpPath[MAX_MINDUMP_PATH_LENGTH]; - - std::string mStaticDebugFileName; - std::string mDynamicDebugFileName; - - // *NOTE: On Windows, we need a routine to reset the structured - // exception handler when some evil driver has taken it over for - // their own purposes - typedef int(*signal_handler_func)(int signum); - static LLAppErrorHandler sErrorHandler; - - // This is the application level runnable scheduler. - LLRunner mRunner; - - /** @name Runtime option implementation */ - //@{ - - // The application options. - LLSD mOptions; - - // The live files for this application - std::vector<LLLiveFile*> mLiveFiles; - //@} - -private: - // the static application instance if it was created. - static LLApp* sApplication; - -#if !LL_WINDOWS - friend void default_unix_signal_handler(int signum, siginfo_t *info, void *); -#endif - -public: - static bool sLogInSignal; -}; - -#endif // LL_LLAPP_H +/**
+ * @file llapp.h
+ * @brief Declaration of the LLApp class.
+ *
+ * $LicenseInfo:firstyear=2003&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLAPP_H
+#define LL_LLAPP_H
+
+#include <map>
+#include "llcond.h"
+#include "llrun.h"
+#include "llsd.h"
+#include <atomic>
+#include <chrono>
+// Forward declarations
+class LLLiveFile;
+#if LL_LINUX
+#include <signal.h>
+#endif
+
+typedef void (*LLAppErrorHandler)();
+
+#if !LL_WINDOWS
+extern S32 LL_SMACKDOWN_SIGNAL;
+extern S32 LL_HEARTBEAT_SIGNAL;
+
+// Clear all of the signal handlers (which we want to do for the child process when we fork
+void clear_signals();
+
+#endif
+
+class LL_COMMON_API LLApp
+{
+public:
+ typedef enum e_app_status
+ {
+ APP_STATUS_RUNNING, // The application is currently running - the default status
+ APP_STATUS_QUITTING, // The application is currently quitting - threads should listen for this and clean up
+ APP_STATUS_STOPPED, // The application is no longer running - tells the error thread it can exit
+ APP_STATUS_ERROR // The application had a fatal error occur - tells the error thread to run
+ } EAppStatus;
+
+
+ LLApp();
+ virtual ~LLApp();
+
+ /**
+ * @brief Return the static app instance if one was created.
+ */
+ static LLApp* instance();
+
+ /** @name Runtime options */
+ //@{
+ /**
+ * @brief Enumeration to specify option priorities in highest to
+ * lowest order.
+ */
+ enum OptionPriority
+ {
+ PRIORITY_RUNTIME_OVERRIDE,
+ PRIORITY_COMMAND_LINE,
+ PRIORITY_SPECIFIC_CONFIGURATION,
+ PRIORITY_GENERAL_CONFIGURATION,
+ PRIORITY_DEFAULT,
+ PRIORITY_COUNT
+ };
+
+ /**
+ * @brief Get the application option at the highest priority.
+ *
+ * If the return value is undefined, the option does not exist.
+ * @param name The name of the option.
+ * @return Returns the option data.
+ */
+ LLSD getOption(const std::string& name) const;
+
+ /**
+ * @brief Parse ASCII command line options and insert them into
+ * application command line options.
+ *
+ * The name inserted into the option will have leading option
+ * identifiers (a minus or double minus) stripped. All options
+ * with values will be stored as a string, while all options
+ * without values will be stored as true.
+ * @param argc The argc passed into main().
+ * @param argv The argv passed into main().
+ * @return Returns true if the parse succeeded.
+ */
+ bool parseCommandOptions(int argc, char** argv);
+
+ /**
+ * @brief Parse Unicode command line options and insert them into
+ * application command line options.
+ *
+ * The name inserted into the option will have leading option
+ * identifiers (a minus or double minus) stripped. All options
+ * with values will be stored as a string, while all options
+ * without values will be stored as true.
+ * @param argc The argc passed into main().
+ * @param wargv The wargv passed into main().
+ * @return Returns true if the parse succeeded.
+ */
+ bool parseCommandOptions(int argc, wchar_t** wargv);
+
+ /**
+ * @brief Keep track of live files automatically.
+ *
+ * *TODO: it currently uses the <code>addToEventTimer()</code> API
+ * instead of the runner. I should probalby use the runner.
+ *
+ * *NOTE: DO NOT add the livefile instance to any kind of check loop.
+ *
+ * @param livefile A valid instance of an LLLiveFile. This LLApp
+ * instance will delete the livefile instance.
+ */
+ void manageLiveFile(LLLiveFile* livefile);
+
+ /**
+ * @brief Set the options at the specified priority.
+ *
+ * This function completely replaces the options at the priority
+ * level with the data specified. This function will make sure
+ * level and data might be valid before doing the replace.
+ * @param level The priority level of the data.
+ * @param data The data to set.
+ * @return Returns true if the option was set.
+ */
+ bool setOptionData(OptionPriority level, LLSD data);
+
+ /**
+ * @brief Get the option data at the specified priority.
+ *
+ * This method is probably not so useful except when merging
+ * information.
+ * @param level The priority level of the data.
+ * @return Returns The data (if any) at the level priority.
+ */
+ LLSD getOptionData(OptionPriority level);
+ //@}
+
+
+
+ //
+ // Main application logic
+ //
+ virtual bool init() = 0; // Override to do application initialization
+
+ //
+ // cleanup()
+ //
+ // It's currently assumed that the cleanup() method will only get
+ // called from the main thread or the error handling thread, as it will
+ // likely do thread shutdown, among other things.
+ //
+ virtual bool cleanup() = 0; // Override to do application cleanup
+
+ //
+ // frame()
+ //
+ // Pass control to the application for a single frame. Returns 'done'
+ // flag: if frame() returns false, it expects to be called again.
+ //
+ virtual bool frame() = 0; // Override for application body logic
+
+ //
+ // Crash logging
+ //
+ void disableCrashlogger(); // Let the OS handle the crashes
+ static bool isCrashloggerDisabled(); // Get the here above set value
+
+ //
+ // Application status
+ //
+ static void setQuitting(); // Set status to QUITTING, the app is now shutting down
+ static void setStopped(); // Set status to STOPPED, the app is done running and should exit
+ static void setError(); // Set status to ERROR, the error handler should run
+ static bool isStopped();
+ static bool isRunning();
+ static bool isQuitting();
+ static bool isError();
+ static bool isExiting(); // Either quitting or error (app is exiting, cleanly or not)
+ static int getPid();
+
+ //
+ // Sleep for specified time while still running
+ //
+ // For use by a coroutine or thread that performs some maintenance on a
+ // periodic basis. (See also LLEventTimer.) This method supports the
+ // pattern of an "infinite" loop that sleeps for some time, performs some
+ // action, then sleeps again. The trouble with literally sleeping a worker
+ // thread is that it could potentially sleep right through attempted
+ // application shutdown. This method avoids that by returning false as
+ // soon as the application status changes away from APP_STATUS_RUNNING
+ // (isRunning()).
+ //
+ // sleep() returns true if it sleeps undisturbed for the entire specified
+ // duration. The idea is that you can code 'while sleep(duration) ...',
+ // which will break the loop once shutdown begins.
+ //
+ // Since any time-based LLUnit should be implicitly convertible to
+ // F32Milliseconds, accept that specific type as a proxy.
+ static bool sleep(F32Milliseconds duration);
+ // Allow any duration defined in terms of <chrono>.
+ // One can imagine a wonderfully general bidirectional conversion system
+ // between any type derived from LLUnits::LLUnit<T, LLUnits::Seconds> and
+ // any std::chrono::duration -- but that doesn't yet exist.
+ template <typename Rep, typename Period>
+ bool sleep(const std::chrono::duration<Rep, Period>& duration)
+ {
+ // wait_for_unequal() has the opposite bool return convention
+ return ! sStatus.wait_for_unequal(duration, APP_STATUS_RUNNING);
+ }
+
+ /** @name Error handling methods */
+ //@{
+ /**
+ * @brief Do our generic platform-specific error-handling setup --
+ * signals on unix, structured exceptions on windows.
+ *
+ * DO call this method if your app will either spawn children or be
+ * spawned by a launcher.
+ * Call just after app object construction.
+ * (Otherwise your app will crash when getting signals,
+ * and will not core dump.)
+ *
+ * DO NOT call this method if your application has specialized
+ * error handling code.
+ */
+ void setupErrorHandling(bool mSecondInstance=false);
+
+ void setErrorHandler(LLAppErrorHandler handler);
+ static void runErrorHandler(); // run shortly after we detect an error
+ //@}
+
+ // the maximum length of the minidump filename returned by getMiniDumpFilename()
+ static const U32 MAX_MINDUMP_PATH_LENGTH = 256;
+
+ // change the directory where Breakpad minidump files are written to
+ void setDebugFileNames(const std::string &path);
+
+ // Return the Google Breakpad minidump filename after a crash.
+ char *getMiniDumpFilename() { return mMinidumpPath; }
+ std::string* getStaticDebugFile() { return &mStaticDebugFileName; }
+ std::string* getDynamicDebugFile() { return &mDynamicDebugFileName; }
+
+ // Write out a Google Breakpad minidump file.
+ void writeMiniDump();
+
+
+ /**
+ * @brief Get a reference to the application runner
+ *
+ * Please use the runner with caution. Since the Runner usage
+ * pattern is not yet clear, this method just gives access to it
+ * to add and remove runnables.
+ * @return Returns the application runner. Do not save the
+ * pointer past the caller's stack frame.
+ */
+ LLRunner& getRunner() { return mRunner; }
+
+#ifdef LL_WINDOWS
+ virtual void reportCrashToBugsplat(void* pExcepInfo /*EXCEPTION_POINTERS*/) { }
+#endif
+
+public:
+ typedef std::map<std::string, std::string> string_map;
+ string_map mOptionMap; // Contains all command-line options and arguments in a map
+
+protected:
+
+ static void setStatus(EAppStatus status); // Use this to change the application status.
+ static LLScalarCond<EAppStatus> sStatus; // Reflects current application status
+ static bool sDisableCrashlogger; // Let the OS handle crashes for us.
+ std::wstring mCrashReportPipeStr; //Name of pipe to use for crash reporting.
+
+ std::string mDumpPath; //output path for google breakpad. Dependency workaround.
+
+ /**
+ * @brief This method is called once a frame to do once a frame tasks.
+ */
+ void stepFrame();
+
+private:
+ // Contains the filename of the minidump file after a crash.
+ char mMinidumpPath[MAX_MINDUMP_PATH_LENGTH];
+
+ std::string mStaticDebugFileName;
+ std::string mDynamicDebugFileName;
+
+ // *NOTE: On Windows, we need a routine to reset the structured
+ // exception handler when some evil driver has taken it over for
+ // their own purposes
+ typedef int(*signal_handler_func)(int signum);
+ static LLAppErrorHandler sErrorHandler;
+
+ // This is the application level runnable scheduler.
+ LLRunner mRunner;
+
+ /** @name Runtime option implementation */
+ //@{
+
+ // The application options.
+ LLSD mOptions;
+
+ // The live files for this application
+ std::vector<LLLiveFile*> mLiveFiles;
+ //@}
+
+private:
+ // the static application instance if it was created.
+ static LLApp* sApplication;
+
+#if !LL_WINDOWS
+ friend void default_unix_signal_handler(int signum, siginfo_t *info, void *);
+#endif
+
+public:
+ static bool sLogInSignal;
+};
+
+#endif // LL_LLAPP_H
diff --git a/indra/llcommon/llapr.cpp b/indra/llcommon/llapr.cpp index 8b99ee93b9..0aa68f28cb 100644 --- a/indra/llcommon/llapr.cpp +++ b/indra/llcommon/llapr.cpp @@ -1,747 +1,747 @@ -/** - * @file llapr.cpp - * @author Phoenix - * @date 2004-11-28 - * @brief Helper functions for using the apache portable runtime library. - * - * $LicenseInfo:firstyear=2004&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" -#include "llapr.h" -#include "llmutex.h" -#include "apr_dso.h" - -apr_pool_t *gAPRPoolp = NULL; // Global APR memory pool -LLVolatileAPRPool *LLAPRFile::sAPRFilePoolp = NULL ; //global volatile APR memory pool. - -const S32 FULL_VOLATILE_APR_POOL = 1024 ; //number of references to LLVolatileAPRPool - -bool gAPRInitialized = false; - -int abortfunc(int retcode) -{ - LL_WARNS("APR") << "Allocation failure in apr pool with code " << (S32)retcode << LL_ENDL; - return 0; -} - -void ll_init_apr() -{ - // Initialize APR and create the global pool - apr_initialize(); - - if (!gAPRPoolp) - { - apr_pool_create_ex(&gAPRPoolp, NULL, abortfunc, NULL); - } - - if(!LLAPRFile::sAPRFilePoolp) - { - LLAPRFile::sAPRFilePoolp = new LLVolatileAPRPool(false) ; - } - - gAPRInitialized = true; -} - - -bool ll_apr_is_initialized() -{ - return gAPRInitialized; -} - -void ll_cleanup_apr() -{ - gAPRInitialized = false; - - LL_DEBUGS("APR") << "Cleaning up APR" << LL_ENDL; - - if (gAPRPoolp) - { - apr_pool_destroy(gAPRPoolp); - gAPRPoolp = NULL; - } - if (LLAPRFile::sAPRFilePoolp) - { - delete LLAPRFile::sAPRFilePoolp ; - LLAPRFile::sAPRFilePoolp = NULL ; - } - apr_terminate(); -} - -// -// -//LLAPRPool -// -LLAPRPool::LLAPRPool(apr_pool_t *parent, apr_size_t size, bool releasePoolFlag) - : mParent(parent), - mReleasePoolFlag(releasePoolFlag), - mMaxSize(size), - mPool(NULL) -{ - createAPRPool() ; -} - -LLAPRPool::~LLAPRPool() -{ - releaseAPRPool() ; -} - -void LLAPRPool::createAPRPool() -{ - if(mPool) - { - return ; - } - - mStatus = apr_pool_create(&mPool, mParent); - ll_apr_warn_status(mStatus) ; - - if(mMaxSize > 0) //size is the number of blocks (which is usually 4K), NOT bytes. - { - apr_allocator_t *allocator = apr_pool_allocator_get(mPool); - if (allocator) - { - apr_allocator_max_free_set(allocator, mMaxSize) ; - } - } -} - -void LLAPRPool::releaseAPRPool() -{ - if(!mPool) - { - return ; - } - - if(!mParent || mReleasePoolFlag) - { - apr_pool_destroy(mPool) ; - mPool = NULL ; - } -} - -//virtual -apr_pool_t* LLAPRPool::getAPRPool() -{ - return mPool ; -} - -LLVolatileAPRPool::LLVolatileAPRPool(bool is_local, apr_pool_t *parent, apr_size_t size, bool releasePoolFlag) - : LLAPRPool(parent, size, releasePoolFlag), - mNumActiveRef(0), - mNumTotalRef(0) -{ - //create mutex - if(!is_local) //not a local apr_pool, that is: shared by multiple threads. - { - mMutexp.reset(new std::mutex()); - } -} - -LLVolatileAPRPool::~LLVolatileAPRPool() -{ - //delete mutex - mMutexp.reset(); -} - -// -//define this virtual function to avoid any mistakenly calling LLAPRPool::getAPRPool(). -// -//virtual -apr_pool_t* LLVolatileAPRPool::getAPRPool() -{ - return LLVolatileAPRPool::getVolatileAPRPool() ; -} - -apr_pool_t* LLVolatileAPRPool::getVolatileAPRPool() -{ - LLScopedLock lock(mMutexp.get()) ; - - mNumTotalRef++ ; - mNumActiveRef++ ; - - if(!mPool) - { - createAPRPool() ; - } - - return mPool ; -} - -void LLVolatileAPRPool::clearVolatileAPRPool() -{ - LLScopedLock lock(mMutexp.get()); - - if(mNumActiveRef > 0) - { - mNumActiveRef--; - if(mNumActiveRef < 1) - { - if(isFull()) - { - mNumTotalRef = 0 ; - - //destroy the apr_pool. - releaseAPRPool() ; - } - else - { - //This does not actually free the memory, - //it just allows the pool to re-use this memory for the next allocation. - apr_pool_clear(mPool) ; - } - } - } - else - { - llassert_always(mNumActiveRef > 0) ; - } - - llassert(mNumTotalRef <= (FULL_VOLATILE_APR_POOL << 2)) ; -} - -bool LLVolatileAPRPool::isFull() -{ - return mNumTotalRef > FULL_VOLATILE_APR_POOL ; -} - -//--------------------------------------------------------------------- - -bool _ll_apr_warn_status(apr_status_t status, const char* file, int line) -{ - if(APR_SUCCESS == status) return false; -#if !LL_LINUX - char buf[MAX_STRING]; /* Flawfinder: ignore */ - apr_strerror(status, buf, sizeof(buf)); - LL_WARNS("APR") << "APR: " << file << ":" << line << " " << buf << LL_ENDL; -#endif - return true; -} - -void _ll_apr_assert_status(apr_status_t status, const char* file, int line) -{ - llassert(! _ll_apr_warn_status(status, file, line)); -} - -//--------------------------------------------------------------------- -// -// Scope based pool access -// -//--------------------------------------------------------------------- - -class LLAPRFilePoolScope -{ -public: - LLAPRFilePoolScope() : pPool(NULL), mInitialized(false) {} - LLAPRFilePoolScope(LLVolatileAPRPool* poolp) : mInitialized(false) - { - setFilePool(poolp); - } - ~LLAPRFilePoolScope() - { - reset(); - } - apr_pool_t* getVolatileAPRPool(LLVolatileAPRPool* poolp = NULL) - { - if (!pPool) - { - setFilePool(poolp); - } - if (mInitialized) - { - // We need one clear per one get - // At the moment no need to support multiple calls - LL_ERRS() << "LLAPRFilePoolScope is not supposed to be initialized twice" << LL_ENDL; - } - mInitialized = true; - return pPool->getVolatileAPRPool(); - } - void reset() - { - if (mInitialized) - { - pPool->clearVolatileAPRPool(); - } - } - -private: - void setFilePool(LLVolatileAPRPool* poolp = NULL) - { - if (poolp) - { - pPool = poolp; - } - else - { - pPool = LLAPRFile::sAPRFilePoolp; - } - } - - LLVolatileAPRPool *pPool; - bool mInitialized; -}; - -//--------------------------------------------------------------------- -// -// LLAPRFile functions -// -LLAPRFile::LLAPRFile() - : mFile(NULL), - mCurrentFilePoolp(NULL) -{ -} - -LLAPRFile::LLAPRFile(const std::string& filename, apr_int32_t flags, LLVolatileAPRPool* pool) - : mFile(NULL), - mCurrentFilePoolp(NULL) -{ - open(filename, flags, pool); -} - -LLAPRFile::~LLAPRFile() -{ - close() ; -} - -apr_status_t LLAPRFile::close() -{ - apr_status_t ret = APR_SUCCESS ; - if(mFile) - { - ret = apr_file_close(mFile); - mFile = NULL ; - } - - if(mCurrentFilePoolp) - { - mCurrentFilePoolp->clearVolatileAPRPool() ; - mCurrentFilePoolp = NULL ; - } - - return ret ; -} - -apr_status_t LLAPRFile::open(const std::string& filename, apr_int32_t flags, LLVolatileAPRPool* pool, S32* sizep) -{ - apr_status_t s ; - - //check if already open some file - llassert_always(!mFile) ; - llassert_always(!mCurrentFilePoolp) ; - - mCurrentFilePoolp = pool ? pool : sAPRFilePoolp; - apr_pool_t* apr_pool = mCurrentFilePoolp->getVolatileAPRPool(); //paired with clear in close() - s = apr_file_open(&mFile, filename.c_str(), flags, APR_OS_DEFAULT, apr_pool); - - if (s != APR_SUCCESS || !mFile) - { - mFile = NULL ; - - if (sizep) - { - *sizep = 0; - } - } - else if (sizep) - { - S32 file_size = 0; - apr_off_t offset = 0; - if (apr_file_seek(mFile, APR_END, &offset) == APR_SUCCESS) - { - llassert_always(offset <= 0x7fffffff); - file_size = (S32)offset; - offset = 0; - apr_file_seek(mFile, APR_SET, &offset); - } - *sizep = file_size; - } - - if (!mFile) - { - // It will clean pool - close() ; - } - - return s ; -} - -//use gAPRPoolp. -apr_status_t LLAPRFile::open(const std::string& filename, apr_int32_t flags, bool use_global_pool) -{ - apr_status_t s; - - //check if already open some file - llassert_always(!mFile) ; - llassert_always(!mCurrentFilePoolp) ; - llassert_always(use_global_pool) ; //be aware of using gAPRPoolp. - - s = apr_file_open(&mFile, filename.c_str(), flags, APR_OS_DEFAULT, gAPRPoolp); - if (s != APR_SUCCESS || !mFile) - { - mFile = NULL ; - close() ; - return s; - } - - return s; -} - -// File I/O -S32 LLAPRFile::read(void *buf, S32 nbytes) -{ - if(!mFile) - { - LL_WARNS() << "apr mFile is removed by somebody else. Can not read." << LL_ENDL ; - return 0; - } - - apr_size_t sz = nbytes; - apr_status_t s = apr_file_read(mFile, buf, &sz); - if (s != APR_SUCCESS) - { - ll_apr_warn_status(s); - return 0; - } - else - { - llassert_always(sz <= 0x7fffffff); - return (S32)sz; - } -} - -S32 LLAPRFile::write(const void *buf, S32 nbytes) -{ - if(!mFile) - { - LL_WARNS() << "apr mFile is removed by somebody else. Can not write." << LL_ENDL ; - return 0; - } - - apr_size_t sz = nbytes; - apr_status_t s = apr_file_write(mFile, buf, &sz); - if (s != APR_SUCCESS) - { - ll_apr_warn_status(s); - return 0; - } - else - { - llassert_always(sz <= 0x7fffffff); - return (S32)sz; - } -} - -S32 LLAPRFile::seek(apr_seek_where_t where, S32 offset) -{ - return LLAPRFile::seek(mFile, where, offset) ; -} - -// -//******************************************************************************************************************************* -//static components of LLAPRFile -// - -//static -apr_status_t LLAPRFile::close(apr_file_t* file_handle) -{ - apr_status_t ret = APR_SUCCESS ; - if(file_handle) - { - ret = apr_file_close(file_handle); - file_handle = NULL ; - } - - return ret ; -} - -//static -apr_file_t* LLAPRFile::open(const std::string& filename, apr_pool_t* apr_pool, apr_int32_t flags) -{ - apr_status_t s; - apr_file_t* file_handle ; - - - s = apr_file_open(&file_handle, filename.c_str(), flags, APR_OS_DEFAULT, apr_pool); - if (s != APR_SUCCESS || !file_handle) - { - ll_apr_warn_status(s); - LL_WARNS("APR") << " Attempting to open filename: " << filename << LL_ENDL; - file_handle = NULL ; - close(file_handle) ; - return NULL; - } - - return file_handle ; -} - -//static -S32 LLAPRFile::seek(apr_file_t* file_handle, apr_seek_where_t where, S32 offset) -{ - if(!file_handle) - { - return -1 ; - } - - apr_status_t s; - apr_off_t apr_offset; - if (offset >= 0) - { - apr_offset = (apr_off_t)offset; - s = apr_file_seek(file_handle, where, &apr_offset); - } - else - { - apr_offset = 0; - s = apr_file_seek(file_handle, APR_END, &apr_offset); - } - if (s != APR_SUCCESS) - { - ll_apr_warn_status(s); - return -1; - } - else - { - llassert_always(apr_offset <= 0x7fffffff); - return (S32)apr_offset; - } -} - -//static -S32 LLAPRFile::readEx(const std::string& filename, void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool) -{ - LL_PROFILE_ZONE_SCOPED; - //***************************************** - LLAPRFilePoolScope scope(pool); - apr_file_t* file_handle = open(filename, scope.getVolatileAPRPool(), APR_READ|APR_BINARY); - //***************************************** - if (!file_handle) - { - return 0; - } - - llassert(offset >= 0); - - if (offset > 0) - offset = LLAPRFile::seek(file_handle, APR_SET, offset); - - apr_size_t bytes_read; - if (offset < 0) - { - bytes_read = 0; - } - else - { - bytes_read = nbytes ; - apr_status_t s = apr_file_read(file_handle, buf, &bytes_read); - if (s != APR_SUCCESS) - { - LL_WARNS("APR") << " Attempting to read filename: " << filename << LL_ENDL; - ll_apr_warn_status(s); - bytes_read = 0; - } - else - { - llassert_always(bytes_read <= 0x7fffffff); - } - } - - //***************************************** - close(file_handle) ; - //***************************************** - return (S32)bytes_read; -} - -//static -S32 LLAPRFile::writeEx(const std::string& filename, const void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool) -{ - LL_PROFILE_ZONE_SCOPED; - apr_int32_t flags = APR_CREATE|APR_WRITE|APR_BINARY; - if (offset < 0) - { - flags |= APR_APPEND; - offset = 0; - } - - //***************************************** - LLAPRFilePoolScope scope(pool); - apr_file_t* file_handle = open(filename, scope.getVolatileAPRPool(), flags); - //***************************************** - if (!file_handle) - { - return 0; - } - - if (offset > 0) - { - offset = LLAPRFile::seek(file_handle, APR_SET, offset); - } - - apr_size_t bytes_written; - if (offset < 0) - { - bytes_written = 0; - } - else - { - bytes_written = nbytes ; - apr_status_t s = apr_file_write(file_handle, buf, &bytes_written); - if (s != APR_SUCCESS) - { - LL_WARNS("APR") << " Attempting to write filename: " << filename << LL_ENDL; - ll_apr_warn_status(s); - bytes_written = 0; - } - else - { - llassert_always(bytes_written <= 0x7fffffff); - } - } - - //***************************************** - LLAPRFile::close(file_handle); - //***************************************** - - return (S32)bytes_written; -} - -//static -bool LLAPRFile::remove(const std::string& filename, LLVolatileAPRPool* pool) -{ - apr_status_t s; - - LLAPRFilePoolScope scope(pool); - s = apr_file_remove(filename.c_str(), scope.getVolatileAPRPool()); - - if (s != APR_SUCCESS) - { - ll_apr_warn_status(s); - LL_WARNS("APR") << " Attempting to remove filename: " << filename << LL_ENDL; - return false; - } - return true; -} - -//static -bool LLAPRFile::rename(const std::string& filename, const std::string& newname, LLVolatileAPRPool* pool) -{ - apr_status_t s; - - LLAPRFilePoolScope scope(pool); - s = apr_file_rename(filename.c_str(), newname.c_str(), scope.getVolatileAPRPool()); - - if (s != APR_SUCCESS) - { - ll_apr_warn_status(s); - LL_WARNS("APR") << " Attempting to rename filename: " << filename << LL_ENDL; - return false; - } - return true; -} - -//static -bool LLAPRFile::isExist(const std::string& filename, LLVolatileAPRPool* pool, apr_int32_t flags) -{ - apr_file_t* apr_file; - apr_status_t s; - - LLAPRFilePoolScope scope(pool); - s = apr_file_open(&apr_file, filename.c_str(), flags, APR_OS_DEFAULT, scope.getVolatileAPRPool()); - - if (s != APR_SUCCESS || !apr_file) - { - return false; - } - else - { - apr_file_close(apr_file) ; - return true; - } -} - -//static -S32 LLAPRFile::size(const std::string& filename, LLVolatileAPRPool* pool) -{ - apr_file_t* apr_file; - apr_finfo_t info; - apr_status_t s; - - LLAPRFilePoolScope scope(pool); - s = apr_file_open(&apr_file, filename.c_str(), APR_READ, APR_OS_DEFAULT, scope.getVolatileAPRPool()); - - if (s != APR_SUCCESS || !apr_file) - { - return 0; - } - else - { - apr_status_t s = apr_file_info_get(&info, APR_FINFO_SIZE, apr_file); - - apr_file_close(apr_file) ; - - if (s == APR_SUCCESS) - { - return (S32)info.size; - } - else - { - return 0; - } - } -} - -//static -bool LLAPRFile::makeDir(const std::string& dirname, LLVolatileAPRPool* pool) -{ - apr_status_t s; - - LLAPRFilePoolScope scope(pool); - s = apr_dir_make(dirname.c_str(), APR_FPROT_OS_DEFAULT, scope.getVolatileAPRPool()); - - if (s != APR_SUCCESS) - { - ll_apr_warn_status(s); - LL_WARNS("APR") << " Attempting to make directory: " << dirname << LL_ENDL; - return false; - } - return true; -} - -//static -bool LLAPRFile::removeDir(const std::string& dirname, LLVolatileAPRPool* pool) -{ - apr_status_t s; - - LLAPRFilePoolScope scope(pool); - s = apr_file_remove(dirname.c_str(), scope.getVolatileAPRPool()); - - if (s != APR_SUCCESS) - { - ll_apr_warn_status(s); - LL_WARNS("APR") << " Attempting to remove directory: " << dirname << LL_ENDL; - return false; - } - return true; -} -// -//end of static components of LLAPRFile -//******************************************************************************************************************************* -// +/**
+ * @file llapr.cpp
+ * @author Phoenix
+ * @date 2004-11-28
+ * @brief Helper functions for using the apache portable runtime library.
+ *
+ * $LicenseInfo:firstyear=2004&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+#include "llapr.h"
+#include "llmutex.h"
+#include "apr_dso.h"
+
+apr_pool_t *gAPRPoolp = NULL; // Global APR memory pool
+LLVolatileAPRPool *LLAPRFile::sAPRFilePoolp = NULL ; //global volatile APR memory pool.
+
+const S32 FULL_VOLATILE_APR_POOL = 1024 ; //number of references to LLVolatileAPRPool
+
+bool gAPRInitialized = false;
+
+int abortfunc(int retcode)
+{
+ LL_WARNS("APR") << "Allocation failure in apr pool with code " << (S32)retcode << LL_ENDL;
+ return 0;
+}
+
+void ll_init_apr()
+{
+ // Initialize APR and create the global pool
+ apr_initialize();
+
+ if (!gAPRPoolp)
+ {
+ apr_pool_create_ex(&gAPRPoolp, NULL, abortfunc, NULL);
+ }
+
+ if(!LLAPRFile::sAPRFilePoolp)
+ {
+ LLAPRFile::sAPRFilePoolp = new LLVolatileAPRPool(false) ;
+ }
+
+ gAPRInitialized = true;
+}
+
+
+bool ll_apr_is_initialized()
+{
+ return gAPRInitialized;
+}
+
+void ll_cleanup_apr()
+{
+ gAPRInitialized = false;
+
+ LL_DEBUGS("APR") << "Cleaning up APR" << LL_ENDL;
+
+ if (gAPRPoolp)
+ {
+ apr_pool_destroy(gAPRPoolp);
+ gAPRPoolp = NULL;
+ }
+ if (LLAPRFile::sAPRFilePoolp)
+ {
+ delete LLAPRFile::sAPRFilePoolp ;
+ LLAPRFile::sAPRFilePoolp = NULL ;
+ }
+ apr_terminate();
+}
+
+//
+//
+//LLAPRPool
+//
+LLAPRPool::LLAPRPool(apr_pool_t *parent, apr_size_t size, bool releasePoolFlag)
+ : mParent(parent),
+ mReleasePoolFlag(releasePoolFlag),
+ mMaxSize(size),
+ mPool(NULL)
+{
+ createAPRPool() ;
+}
+
+LLAPRPool::~LLAPRPool()
+{
+ releaseAPRPool() ;
+}
+
+void LLAPRPool::createAPRPool()
+{
+ if(mPool)
+ {
+ return ;
+ }
+
+ mStatus = apr_pool_create(&mPool, mParent);
+ ll_apr_warn_status(mStatus) ;
+
+ if(mMaxSize > 0) //size is the number of blocks (which is usually 4K), NOT bytes.
+ {
+ apr_allocator_t *allocator = apr_pool_allocator_get(mPool);
+ if (allocator)
+ {
+ apr_allocator_max_free_set(allocator, mMaxSize) ;
+ }
+ }
+}
+
+void LLAPRPool::releaseAPRPool()
+{
+ if(!mPool)
+ {
+ return ;
+ }
+
+ if(!mParent || mReleasePoolFlag)
+ {
+ apr_pool_destroy(mPool) ;
+ mPool = NULL ;
+ }
+}
+
+//virtual
+apr_pool_t* LLAPRPool::getAPRPool()
+{
+ return mPool ;
+}
+
+LLVolatileAPRPool::LLVolatileAPRPool(bool is_local, apr_pool_t *parent, apr_size_t size, bool releasePoolFlag)
+ : LLAPRPool(parent, size, releasePoolFlag),
+ mNumActiveRef(0),
+ mNumTotalRef(0)
+{
+ //create mutex
+ if(!is_local) //not a local apr_pool, that is: shared by multiple threads.
+ {
+ mMutexp.reset(new std::mutex());
+ }
+}
+
+LLVolatileAPRPool::~LLVolatileAPRPool()
+{
+ //delete mutex
+ mMutexp.reset();
+}
+
+//
+//define this virtual function to avoid any mistakenly calling LLAPRPool::getAPRPool().
+//
+//virtual
+apr_pool_t* LLVolatileAPRPool::getAPRPool()
+{
+ return LLVolatileAPRPool::getVolatileAPRPool() ;
+}
+
+apr_pool_t* LLVolatileAPRPool::getVolatileAPRPool()
+{
+ LLScopedLock lock(mMutexp.get()) ;
+
+ mNumTotalRef++ ;
+ mNumActiveRef++ ;
+
+ if(!mPool)
+ {
+ createAPRPool() ;
+ }
+
+ return mPool ;
+}
+
+void LLVolatileAPRPool::clearVolatileAPRPool()
+{
+ LLScopedLock lock(mMutexp.get());
+
+ if(mNumActiveRef > 0)
+ {
+ mNumActiveRef--;
+ if(mNumActiveRef < 1)
+ {
+ if(isFull())
+ {
+ mNumTotalRef = 0 ;
+
+ //destroy the apr_pool.
+ releaseAPRPool() ;
+ }
+ else
+ {
+ //This does not actually free the memory,
+ //it just allows the pool to re-use this memory for the next allocation.
+ apr_pool_clear(mPool) ;
+ }
+ }
+ }
+ else
+ {
+ llassert_always(mNumActiveRef > 0) ;
+ }
+
+ llassert(mNumTotalRef <= (FULL_VOLATILE_APR_POOL << 2)) ;
+}
+
+bool LLVolatileAPRPool::isFull()
+{
+ return mNumTotalRef > FULL_VOLATILE_APR_POOL ;
+}
+
+//---------------------------------------------------------------------
+
+bool _ll_apr_warn_status(apr_status_t status, const char* file, int line)
+{
+ if(APR_SUCCESS == status) return false;
+#if !LL_LINUX
+ char buf[MAX_STRING]; /* Flawfinder: ignore */
+ apr_strerror(status, buf, sizeof(buf));
+ LL_WARNS("APR") << "APR: " << file << ":" << line << " " << buf << LL_ENDL;
+#endif
+ return true;
+}
+
+void _ll_apr_assert_status(apr_status_t status, const char* file, int line)
+{
+ llassert(! _ll_apr_warn_status(status, file, line));
+}
+
+//---------------------------------------------------------------------
+//
+// Scope based pool access
+//
+//---------------------------------------------------------------------
+
+class LLAPRFilePoolScope
+{
+public:
+ LLAPRFilePoolScope() : pPool(NULL), mInitialized(false) {}
+ LLAPRFilePoolScope(LLVolatileAPRPool* poolp) : mInitialized(false)
+ {
+ setFilePool(poolp);
+ }
+ ~LLAPRFilePoolScope()
+ {
+ reset();
+ }
+ apr_pool_t* getVolatileAPRPool(LLVolatileAPRPool* poolp = NULL)
+ {
+ if (!pPool)
+ {
+ setFilePool(poolp);
+ }
+ if (mInitialized)
+ {
+ // We need one clear per one get
+ // At the moment no need to support multiple calls
+ LL_ERRS() << "LLAPRFilePoolScope is not supposed to be initialized twice" << LL_ENDL;
+ }
+ mInitialized = true;
+ return pPool->getVolatileAPRPool();
+ }
+ void reset()
+ {
+ if (mInitialized)
+ {
+ pPool->clearVolatileAPRPool();
+ }
+ }
+
+private:
+ void setFilePool(LLVolatileAPRPool* poolp = NULL)
+ {
+ if (poolp)
+ {
+ pPool = poolp;
+ }
+ else
+ {
+ pPool = LLAPRFile::sAPRFilePoolp;
+ }
+ }
+
+ LLVolatileAPRPool *pPool;
+ bool mInitialized;
+};
+
+//---------------------------------------------------------------------
+//
+// LLAPRFile functions
+//
+LLAPRFile::LLAPRFile()
+ : mFile(NULL),
+ mCurrentFilePoolp(NULL)
+{
+}
+
+LLAPRFile::LLAPRFile(const std::string& filename, apr_int32_t flags, LLVolatileAPRPool* pool)
+ : mFile(NULL),
+ mCurrentFilePoolp(NULL)
+{
+ open(filename, flags, pool);
+}
+
+LLAPRFile::~LLAPRFile()
+{
+ close() ;
+}
+
+apr_status_t LLAPRFile::close()
+{
+ apr_status_t ret = APR_SUCCESS ;
+ if(mFile)
+ {
+ ret = apr_file_close(mFile);
+ mFile = NULL ;
+ }
+
+ if(mCurrentFilePoolp)
+ {
+ mCurrentFilePoolp->clearVolatileAPRPool() ;
+ mCurrentFilePoolp = NULL ;
+ }
+
+ return ret ;
+}
+
+apr_status_t LLAPRFile::open(const std::string& filename, apr_int32_t flags, LLVolatileAPRPool* pool, S32* sizep)
+{
+ apr_status_t s ;
+
+ //check if already open some file
+ llassert_always(!mFile) ;
+ llassert_always(!mCurrentFilePoolp) ;
+
+ mCurrentFilePoolp = pool ? pool : sAPRFilePoolp;
+ apr_pool_t* apr_pool = mCurrentFilePoolp->getVolatileAPRPool(); //paired with clear in close()
+ s = apr_file_open(&mFile, filename.c_str(), flags, APR_OS_DEFAULT, apr_pool);
+
+ if (s != APR_SUCCESS || !mFile)
+ {
+ mFile = NULL ;
+
+ if (sizep)
+ {
+ *sizep = 0;
+ }
+ }
+ else if (sizep)
+ {
+ S32 file_size = 0;
+ apr_off_t offset = 0;
+ if (apr_file_seek(mFile, APR_END, &offset) == APR_SUCCESS)
+ {
+ llassert_always(offset <= 0x7fffffff);
+ file_size = (S32)offset;
+ offset = 0;
+ apr_file_seek(mFile, APR_SET, &offset);
+ }
+ *sizep = file_size;
+ }
+
+ if (!mFile)
+ {
+ // It will clean pool
+ close() ;
+ }
+
+ return s ;
+}
+
+//use gAPRPoolp.
+apr_status_t LLAPRFile::open(const std::string& filename, apr_int32_t flags, bool use_global_pool)
+{
+ apr_status_t s;
+
+ //check if already open some file
+ llassert_always(!mFile) ;
+ llassert_always(!mCurrentFilePoolp) ;
+ llassert_always(use_global_pool) ; //be aware of using gAPRPoolp.
+
+ s = apr_file_open(&mFile, filename.c_str(), flags, APR_OS_DEFAULT, gAPRPoolp);
+ if (s != APR_SUCCESS || !mFile)
+ {
+ mFile = NULL ;
+ close() ;
+ return s;
+ }
+
+ return s;
+}
+
+// File I/O
+S32 LLAPRFile::read(void *buf, S32 nbytes)
+{
+ if(!mFile)
+ {
+ LL_WARNS() << "apr mFile is removed by somebody else. Can not read." << LL_ENDL ;
+ return 0;
+ }
+
+ apr_size_t sz = nbytes;
+ apr_status_t s = apr_file_read(mFile, buf, &sz);
+ if (s != APR_SUCCESS)
+ {
+ ll_apr_warn_status(s);
+ return 0;
+ }
+ else
+ {
+ llassert_always(sz <= 0x7fffffff);
+ return (S32)sz;
+ }
+}
+
+S32 LLAPRFile::write(const void *buf, S32 nbytes)
+{
+ if(!mFile)
+ {
+ LL_WARNS() << "apr mFile is removed by somebody else. Can not write." << LL_ENDL ;
+ return 0;
+ }
+
+ apr_size_t sz = nbytes;
+ apr_status_t s = apr_file_write(mFile, buf, &sz);
+ if (s != APR_SUCCESS)
+ {
+ ll_apr_warn_status(s);
+ return 0;
+ }
+ else
+ {
+ llassert_always(sz <= 0x7fffffff);
+ return (S32)sz;
+ }
+}
+
+S32 LLAPRFile::seek(apr_seek_where_t where, S32 offset)
+{
+ return LLAPRFile::seek(mFile, where, offset) ;
+}
+
+//
+//*******************************************************************************************************************************
+//static components of LLAPRFile
+//
+
+//static
+apr_status_t LLAPRFile::close(apr_file_t* file_handle)
+{
+ apr_status_t ret = APR_SUCCESS ;
+ if(file_handle)
+ {
+ ret = apr_file_close(file_handle);
+ file_handle = NULL ;
+ }
+
+ return ret ;
+}
+
+//static
+apr_file_t* LLAPRFile::open(const std::string& filename, apr_pool_t* apr_pool, apr_int32_t flags)
+{
+ apr_status_t s;
+ apr_file_t* file_handle ;
+
+
+ s = apr_file_open(&file_handle, filename.c_str(), flags, APR_OS_DEFAULT, apr_pool);
+ if (s != APR_SUCCESS || !file_handle)
+ {
+ ll_apr_warn_status(s);
+ LL_WARNS("APR") << " Attempting to open filename: " << filename << LL_ENDL;
+ file_handle = NULL ;
+ close(file_handle) ;
+ return NULL;
+ }
+
+ return file_handle ;
+}
+
+//static
+S32 LLAPRFile::seek(apr_file_t* file_handle, apr_seek_where_t where, S32 offset)
+{
+ if(!file_handle)
+ {
+ return -1 ;
+ }
+
+ apr_status_t s;
+ apr_off_t apr_offset;
+ if (offset >= 0)
+ {
+ apr_offset = (apr_off_t)offset;
+ s = apr_file_seek(file_handle, where, &apr_offset);
+ }
+ else
+ {
+ apr_offset = 0;
+ s = apr_file_seek(file_handle, APR_END, &apr_offset);
+ }
+ if (s != APR_SUCCESS)
+ {
+ ll_apr_warn_status(s);
+ return -1;
+ }
+ else
+ {
+ llassert_always(apr_offset <= 0x7fffffff);
+ return (S32)apr_offset;
+ }
+}
+
+//static
+S32 LLAPRFile::readEx(const std::string& filename, void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool)
+{
+ LL_PROFILE_ZONE_SCOPED;
+ //*****************************************
+ LLAPRFilePoolScope scope(pool);
+ apr_file_t* file_handle = open(filename, scope.getVolatileAPRPool(), APR_READ|APR_BINARY);
+ //*****************************************
+ if (!file_handle)
+ {
+ return 0;
+ }
+
+ llassert(offset >= 0);
+
+ if (offset > 0)
+ offset = LLAPRFile::seek(file_handle, APR_SET, offset);
+
+ apr_size_t bytes_read;
+ if (offset < 0)
+ {
+ bytes_read = 0;
+ }
+ else
+ {
+ bytes_read = nbytes ;
+ apr_status_t s = apr_file_read(file_handle, buf, &bytes_read);
+ if (s != APR_SUCCESS)
+ {
+ LL_WARNS("APR") << " Attempting to read filename: " << filename << LL_ENDL;
+ ll_apr_warn_status(s);
+ bytes_read = 0;
+ }
+ else
+ {
+ llassert_always(bytes_read <= 0x7fffffff);
+ }
+ }
+
+ //*****************************************
+ close(file_handle) ;
+ //*****************************************
+ return (S32)bytes_read;
+}
+
+//static
+S32 LLAPRFile::writeEx(const std::string& filename, const void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool)
+{
+ LL_PROFILE_ZONE_SCOPED;
+ apr_int32_t flags = APR_CREATE|APR_WRITE|APR_BINARY;
+ if (offset < 0)
+ {
+ flags |= APR_APPEND;
+ offset = 0;
+ }
+
+ //*****************************************
+ LLAPRFilePoolScope scope(pool);
+ apr_file_t* file_handle = open(filename, scope.getVolatileAPRPool(), flags);
+ //*****************************************
+ if (!file_handle)
+ {
+ return 0;
+ }
+
+ if (offset > 0)
+ {
+ offset = LLAPRFile::seek(file_handle, APR_SET, offset);
+ }
+
+ apr_size_t bytes_written;
+ if (offset < 0)
+ {
+ bytes_written = 0;
+ }
+ else
+ {
+ bytes_written = nbytes ;
+ apr_status_t s = apr_file_write(file_handle, buf, &bytes_written);
+ if (s != APR_SUCCESS)
+ {
+ LL_WARNS("APR") << " Attempting to write filename: " << filename << LL_ENDL;
+ ll_apr_warn_status(s);
+ bytes_written = 0;
+ }
+ else
+ {
+ llassert_always(bytes_written <= 0x7fffffff);
+ }
+ }
+
+ //*****************************************
+ LLAPRFile::close(file_handle);
+ //*****************************************
+
+ return (S32)bytes_written;
+}
+
+//static
+bool LLAPRFile::remove(const std::string& filename, LLVolatileAPRPool* pool)
+{
+ apr_status_t s;
+
+ LLAPRFilePoolScope scope(pool);
+ s = apr_file_remove(filename.c_str(), scope.getVolatileAPRPool());
+
+ if (s != APR_SUCCESS)
+ {
+ ll_apr_warn_status(s);
+ LL_WARNS("APR") << " Attempting to remove filename: " << filename << LL_ENDL;
+ return false;
+ }
+ return true;
+}
+
+//static
+bool LLAPRFile::rename(const std::string& filename, const std::string& newname, LLVolatileAPRPool* pool)
+{
+ apr_status_t s;
+
+ LLAPRFilePoolScope scope(pool);
+ s = apr_file_rename(filename.c_str(), newname.c_str(), scope.getVolatileAPRPool());
+
+ if (s != APR_SUCCESS)
+ {
+ ll_apr_warn_status(s);
+ LL_WARNS("APR") << " Attempting to rename filename: " << filename << LL_ENDL;
+ return false;
+ }
+ return true;
+}
+
+//static
+bool LLAPRFile::isExist(const std::string& filename, LLVolatileAPRPool* pool, apr_int32_t flags)
+{
+ apr_file_t* apr_file;
+ apr_status_t s;
+
+ LLAPRFilePoolScope scope(pool);
+ s = apr_file_open(&apr_file, filename.c_str(), flags, APR_OS_DEFAULT, scope.getVolatileAPRPool());
+
+ if (s != APR_SUCCESS || !apr_file)
+ {
+ return false;
+ }
+ else
+ {
+ apr_file_close(apr_file) ;
+ return true;
+ }
+}
+
+//static
+S32 LLAPRFile::size(const std::string& filename, LLVolatileAPRPool* pool)
+{
+ apr_file_t* apr_file;
+ apr_finfo_t info;
+ apr_status_t s;
+
+ LLAPRFilePoolScope scope(pool);
+ s = apr_file_open(&apr_file, filename.c_str(), APR_READ, APR_OS_DEFAULT, scope.getVolatileAPRPool());
+
+ if (s != APR_SUCCESS || !apr_file)
+ {
+ return 0;
+ }
+ else
+ {
+ apr_status_t s = apr_file_info_get(&info, APR_FINFO_SIZE, apr_file);
+
+ apr_file_close(apr_file) ;
+
+ if (s == APR_SUCCESS)
+ {
+ return (S32)info.size;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+}
+
+//static
+bool LLAPRFile::makeDir(const std::string& dirname, LLVolatileAPRPool* pool)
+{
+ apr_status_t s;
+
+ LLAPRFilePoolScope scope(pool);
+ s = apr_dir_make(dirname.c_str(), APR_FPROT_OS_DEFAULT, scope.getVolatileAPRPool());
+
+ if (s != APR_SUCCESS)
+ {
+ ll_apr_warn_status(s);
+ LL_WARNS("APR") << " Attempting to make directory: " << dirname << LL_ENDL;
+ return false;
+ }
+ return true;
+}
+
+//static
+bool LLAPRFile::removeDir(const std::string& dirname, LLVolatileAPRPool* pool)
+{
+ apr_status_t s;
+
+ LLAPRFilePoolScope scope(pool);
+ s = apr_file_remove(dirname.c_str(), scope.getVolatileAPRPool());
+
+ if (s != APR_SUCCESS)
+ {
+ ll_apr_warn_status(s);
+ LL_WARNS("APR") << " Attempting to remove directory: " << dirname << LL_ENDL;
+ return false;
+ }
+ return true;
+}
+//
+//end of static components of LLAPRFile
+//*******************************************************************************************************************************
+//
diff --git a/indra/llcommon/llapr.h b/indra/llcommon/llapr.h index acc4003d27..3a43339ca3 100644 --- a/indra/llcommon/llapr.h +++ b/indra/llcommon/llapr.h @@ -1,201 +1,201 @@ -/** - * @file llapr.h - * @author Phoenix - * @date 2004-11-28 - * @brief Helper functions for using the apache portable runtime library. - * - * $LicenseInfo:firstyear=2004&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLAPR_H -#define LL_LLAPR_H - -#if LL_LINUX -#include <sys/param.h> // Need PATH_MAX in APR headers... -#endif - -#include <boost/noncopyable.hpp> -#include "llwin32headerslean.h" -#include "apr_thread_proc.h" -#include "apr_getopt.h" -#include "apr_signal.h" - -#include "llstring.h" - -#include "mutex.h" - -struct apr_dso_handle_t; -/** - * @brief Function which appropriately logs error or remains quiet on - * APR_SUCCESS. - * @return Returns <code>true</code> if status is an error condition. - */ -#define ll_apr_warn_status(status) _ll_apr_warn_status(status, __FILE__, __LINE__) -bool LL_COMMON_API _ll_apr_warn_status(apr_status_t status, const char* file, int line); - -#define ll_apr_assert_status(status) _ll_apr_assert_status(status, __FILE__, __LINE__) -void LL_COMMON_API _ll_apr_assert_status(apr_status_t status, const char* file, int line); - -extern "C" LL_COMMON_API apr_pool_t* gAPRPoolp; // Global APR memory pool - -/** - * @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(); - -bool LL_COMMON_API ll_apr_is_initialized(); - - -// -//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) ; - virtual ~LLAPRPool() ; - - virtual 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(bool is_local = true, apr_pool_t *parent = NULL, apr_size_t size = 0, bool releasePoolFlag = true); - virtual ~LLVolatileAPRPool(); - - /*virtual*/ apr_pool_t* getAPRPool() ; //define this virtual function to avoid any mistakenly calling LLAPRPool::getAPRPool(). - apr_pool_t* getVolatileAPRPool() ; - void clearVolatileAPRPool() ; - - bool isFull() ; - -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. - - std::unique_ptr<std::mutex> mMutexp; -} ; - -// File IO convenience functions. -// Returns NULL if the file fails to open, sets *sizep to file size if 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_A (APR_CREATE|APR_WRITE|APR_APPEND) // "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_AB (APR_CREATE|APR_WRITE|APR_BINARY|APR_APPEND) -#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 temporary pool passed to an APRFile function, which is used within this function and only once. -// 2, a global pool. -// - -class LL_COMMON_API LLAPRFile : boost::noncopyable -{ - // make this non copyable since a copy closes the file -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(const std::string& filename, apr_int32_t flags, LLVolatileAPRPool* pool = NULL); - ~LLAPRFile() ; - - apr_status_t open(const std::string& filename, apr_int32_t flags, LLVolatileAPRPool* pool = NULL, S32* sizep = NULL); - apr_status_t open(const std::string& filename, apr_int32_t flags, bool use_global_pool); //use gAPRPoolp. - 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;} - -// -//******************************************************************************************************************************* -//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, apr_pool_t* apr_pool, apr_int32_t flags); - static apr_status_t close(apr_file_t* file) ; - 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, const void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool = NULL); // offset<0 means append -//******************************************************************************************************************************* -}; - - -#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=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLAPR_H
+#define LL_LLAPR_H
+
+#if LL_LINUX
+#include <sys/param.h> // Need PATH_MAX in APR headers...
+#endif
+
+#include <boost/noncopyable.hpp>
+#include "llwin32headerslean.h"
+#include "apr_thread_proc.h"
+#include "apr_getopt.h"
+#include "apr_signal.h"
+
+#include "llstring.h"
+
+#include "mutex.h"
+
+struct apr_dso_handle_t;
+/**
+ * @brief Function which appropriately logs error or remains quiet on
+ * APR_SUCCESS.
+ * @return Returns <code>true</code> if status is an error condition.
+ */
+#define ll_apr_warn_status(status) _ll_apr_warn_status(status, __FILE__, __LINE__)
+bool LL_COMMON_API _ll_apr_warn_status(apr_status_t status, const char* file, int line);
+
+#define ll_apr_assert_status(status) _ll_apr_assert_status(status, __FILE__, __LINE__)
+void LL_COMMON_API _ll_apr_assert_status(apr_status_t status, const char* file, int line);
+
+extern "C" LL_COMMON_API apr_pool_t* gAPRPoolp; // Global APR memory pool
+
+/**
+ * @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();
+
+bool LL_COMMON_API ll_apr_is_initialized();
+
+
+//
+//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) ;
+ virtual ~LLAPRPool() ;
+
+ virtual 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(bool is_local = true, apr_pool_t *parent = NULL, apr_size_t size = 0, bool releasePoolFlag = true);
+ virtual ~LLVolatileAPRPool();
+
+ /*virtual*/ apr_pool_t* getAPRPool() ; //define this virtual function to avoid any mistakenly calling LLAPRPool::getAPRPool().
+ apr_pool_t* getVolatileAPRPool() ;
+ void clearVolatileAPRPool() ;
+
+ bool isFull() ;
+
+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.
+
+ std::unique_ptr<std::mutex> mMutexp;
+} ;
+
+// File IO convenience functions.
+// Returns NULL if the file fails to open, sets *sizep to file size if 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_A (APR_CREATE|APR_WRITE|APR_APPEND) // "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_AB (APR_CREATE|APR_WRITE|APR_BINARY|APR_APPEND)
+#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 temporary pool passed to an APRFile function, which is used within this function and only once.
+// 2, a global pool.
+//
+
+class LL_COMMON_API LLAPRFile : boost::noncopyable
+{
+ // make this non copyable since a copy closes the file
+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(const std::string& filename, apr_int32_t flags, LLVolatileAPRPool* pool = NULL);
+ ~LLAPRFile() ;
+
+ apr_status_t open(const std::string& filename, apr_int32_t flags, LLVolatileAPRPool* pool = NULL, S32* sizep = NULL);
+ apr_status_t open(const std::string& filename, apr_int32_t flags, bool use_global_pool); //use gAPRPoolp.
+ 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;}
+
+//
+//*******************************************************************************************************************************
+//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, apr_pool_t* apr_pool, apr_int32_t flags);
+ static apr_status_t close(apr_file_t* file) ;
+ 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, const void *buf, S32 offset, S32 nbytes, LLVolatileAPRPool* pool = NULL); // offset<0 means append
+//*******************************************************************************************************************************
+};
+
+
+#endif // LL_LLAPR_H
diff --git a/indra/llcommon/llassettype.cpp b/indra/llcommon/llassettype.cpp index beddfc53cd..3041c1f354 100644 --- a/indra/llcommon/llassettype.cpp +++ b/indra/llcommon/llassettype.cpp @@ -1,246 +1,246 @@ -/** - * @file llassettype.cpp - * @brief Implementatino of LLAssetType functionality. - * - * $LicenseInfo:firstyear=2001&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" - -#include "llassettype.h" -#include "lldictionary.h" -#include "llmemory.h" -#include "llsingleton.h" - -///---------------------------------------------------------------------------- -/// Class LLAssetType -///---------------------------------------------------------------------------- -struct AssetEntry : public LLDictionaryEntry -{ - AssetEntry(const char *desc_name, - const char *type_name, // 8 character limit! - const char *human_name, // for decoding to human readable form; put any and as many printable characters you want in each one - bool can_link, // can you create a link to this type? - bool can_fetch, // can you fetch this asset by ID? - bool can_know) // can you see this asset's ID? - : - LLDictionaryEntry(desc_name), - mTypeName(type_name), - mHumanName(human_name), - mCanLink(can_link), - mCanFetch(can_fetch), - mCanKnow(can_know) - { - llassert(strlen(mTypeName) <= 8); - } - - const char *mTypeName; - const char *mHumanName; - bool mCanLink; - bool mCanFetch; - bool mCanKnow; -}; - -class LLAssetDictionary : public LLSingleton<LLAssetDictionary>, - public LLDictionary<LLAssetType::EType, AssetEntry> -{ - LLSINGLETON(LLAssetDictionary); -}; - -LLAssetDictionary::LLAssetDictionary() -{ - // DESCRIPTION TYPE NAME HUMAN NAME CAN LINK? CAN FETCH? CAN KNOW? - // |--------------------|-----------|-------------------|-----------|-----------|---------| - addEntry(LLAssetType::AT_TEXTURE, new AssetEntry("TEXTURE", "texture", "texture", true, false, true)); - addEntry(LLAssetType::AT_SOUND, new AssetEntry("SOUND", "sound", "sound", true, true, true)); - addEntry(LLAssetType::AT_CALLINGCARD, new AssetEntry("CALLINGCARD", "callcard", "calling card", true, false, false)); - addEntry(LLAssetType::AT_LANDMARK, new AssetEntry("LANDMARK", "landmark", "landmark", true, true, true)); - addEntry(LLAssetType::AT_SCRIPT, new AssetEntry("SCRIPT", "script", "legacy script", true, false, false)); - addEntry(LLAssetType::AT_CLOTHING, new AssetEntry("CLOTHING", "clothing", "clothing", true, true, true)); - addEntry(LLAssetType::AT_OBJECT, new AssetEntry("OBJECT", "object", "object", true, false, false)); - addEntry(LLAssetType::AT_NOTECARD, new AssetEntry("NOTECARD", "notecard", "note card", true, false, true)); - addEntry(LLAssetType::AT_CATEGORY, new AssetEntry("CATEGORY", "category", "folder", true, false, false)); - addEntry(LLAssetType::AT_LSL_TEXT, new AssetEntry("LSL_TEXT", "lsltext", "lsl2 script", true, false, false)); - addEntry(LLAssetType::AT_LSL_BYTECODE, new AssetEntry("LSL_BYTECODE", "lslbyte", "lsl bytecode", true, false, false)); - addEntry(LLAssetType::AT_TEXTURE_TGA, new AssetEntry("TEXTURE_TGA", "txtr_tga", "tga texture", true, false, false)); - addEntry(LLAssetType::AT_BODYPART, new AssetEntry("BODYPART", "bodypart", "body part", true, true, true)); - addEntry(LLAssetType::AT_SOUND_WAV, new AssetEntry("SOUND_WAV", "snd_wav", "sound", true, false, false)); - addEntry(LLAssetType::AT_IMAGE_TGA, new AssetEntry("IMAGE_TGA", "img_tga", "targa image", true, false, false)); - addEntry(LLAssetType::AT_IMAGE_JPEG, new AssetEntry("IMAGE_JPEG", "jpeg", "jpeg image", true, false, false)); - addEntry(LLAssetType::AT_ANIMATION, new AssetEntry("ANIMATION", "animatn", "animation", true, true, true)); - addEntry(LLAssetType::AT_GESTURE, new AssetEntry("GESTURE", "gesture", "gesture", true, true, true)); - addEntry(LLAssetType::AT_SIMSTATE, new AssetEntry("SIMSTATE", "simstate", "simstate", false, false, false)); - - addEntry(LLAssetType::AT_LINK, new AssetEntry("LINK", "link", "sym link", false, false, true)); - addEntry(LLAssetType::AT_LINK_FOLDER, new AssetEntry("FOLDER_LINK", "link_f", "sym folder link", false, false, true)); - addEntry(LLAssetType::AT_MESH, new AssetEntry("MESH", "mesh", "mesh", false, false, false)); - addEntry(LLAssetType::AT_WIDGET, new AssetEntry("WIDGET", "widget", "widget", false, false, false)); - addEntry(LLAssetType::AT_PERSON, new AssetEntry("PERSON", "person", "person", false, false, false)); - addEntry(LLAssetType::AT_SETTINGS, new AssetEntry("SETTINGS", "settings", "settings blob", true, true, true)); - addEntry(LLAssetType::AT_MATERIAL, new AssetEntry("MATERIAL", "material", "render material", true, true, true)); - addEntry(LLAssetType::AT_UNKNOWN, new AssetEntry("UNKNOWN", "invalid", NULL, false, false, false)); - addEntry(LLAssetType::AT_NONE, new AssetEntry("NONE", "-1", NULL, false, false, false)); - -}; - -const std::string LLAssetType::BADLOOKUP("llassettype_bad_lookup"); - -// static -LLAssetType::EType LLAssetType::getType(const std::string& desc_name) -{ - std::string s = desc_name; - LLStringUtil::toUpper(s); - return LLAssetDictionary::getInstance()->lookup(s); -} - -// static -const std::string &LLAssetType::getDesc(LLAssetType::EType asset_type) -{ - const AssetEntry *entry = LLAssetDictionary::getInstance()->lookup(asset_type); - if (entry) - { - return entry->mName; - } - else - { - return BADLOOKUP; - } -} - -// static -const char *LLAssetType::lookup(LLAssetType::EType asset_type) -{ - const LLAssetDictionary *dict = LLAssetDictionary::getInstance(); - const AssetEntry *entry = dict->lookup(asset_type); - if (entry) - { - return entry->mTypeName; - } - else - { - return BADLOOKUP.c_str(); - } -} - -// static -LLAssetType::EType LLAssetType::lookup(const char* name) -{ - return lookup(ll_safe_string(name)); -} - -// static -LLAssetType::EType LLAssetType::lookup(const std::string& type_name) -{ - const LLAssetDictionary *dict = LLAssetDictionary::getInstance(); - for (const LLAssetDictionary::value_type& pair : *dict) - { - const AssetEntry *entry = pair.second; - if (type_name == entry->mTypeName) - { - return pair.first; - } - } - return AT_UNKNOWN; -} - -// static -const char *LLAssetType::lookupHumanReadable(LLAssetType::EType asset_type) -{ - const LLAssetDictionary *dict = LLAssetDictionary::getInstance(); - const AssetEntry *entry = dict->lookup(asset_type); - if (entry) - { - return entry->mHumanName; - } - else - { - return BADLOOKUP.c_str(); - } -} - -// static -LLAssetType::EType LLAssetType::lookupHumanReadable(const char* name) -{ - return lookupHumanReadable(ll_safe_string(name)); -} - -// static -LLAssetType::EType LLAssetType::lookupHumanReadable(const std::string& readable_name) -{ - const LLAssetDictionary *dict = LLAssetDictionary::getInstance(); - for (const LLAssetDictionary::value_type& pair : *dict) - { - const AssetEntry *entry = pair.second; - if (entry->mHumanName && (readable_name == entry->mHumanName)) - { - return pair.first; - } - } - return AT_NONE; -} - -// static -bool LLAssetType::lookupCanLink(EType asset_type) -{ - const LLAssetDictionary *dict = LLAssetDictionary::getInstance(); - const AssetEntry *entry = dict->lookup(asset_type); - if (entry) - { - return entry->mCanLink; - } - return false; -} - -// static -// Not adding this to dictionary since we probably will only have these two types -bool LLAssetType::lookupIsLinkType(EType asset_type) -{ - if (asset_type == AT_LINK || asset_type == AT_LINK_FOLDER) - { - return true; - } - return false; -} - -// static -bool LLAssetType::lookupIsAssetFetchByIDAllowed(EType asset_type) -{ - const LLAssetDictionary *dict = LLAssetDictionary::getInstance(); - const AssetEntry *entry = dict->lookup(asset_type); - if (entry) - { - return entry->mCanFetch; - } - return false; -} - -// static -bool LLAssetType::lookupIsAssetIDKnowable(EType asset_type) -{ - const LLAssetDictionary *dict = LLAssetDictionary::getInstance(); - const AssetEntry *entry = dict->lookup(asset_type); - if (entry) - { - return entry->mCanKnow; - } - return false; -} +/**
+ * @file llassettype.cpp
+ * @brief Implementatino of LLAssetType functionality.
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llassettype.h"
+#include "lldictionary.h"
+#include "llmemory.h"
+#include "llsingleton.h"
+
+///----------------------------------------------------------------------------
+/// Class LLAssetType
+///----------------------------------------------------------------------------
+struct AssetEntry : public LLDictionaryEntry
+{
+ AssetEntry(const char *desc_name,
+ const char *type_name, // 8 character limit!
+ const char *human_name, // for decoding to human readable form; put any and as many printable characters you want in each one
+ bool can_link, // can you create a link to this type?
+ bool can_fetch, // can you fetch this asset by ID?
+ bool can_know) // can you see this asset's ID?
+ :
+ LLDictionaryEntry(desc_name),
+ mTypeName(type_name),
+ mHumanName(human_name),
+ mCanLink(can_link),
+ mCanFetch(can_fetch),
+ mCanKnow(can_know)
+ {
+ llassert(strlen(mTypeName) <= 8);
+ }
+
+ const char *mTypeName;
+ const char *mHumanName;
+ bool mCanLink;
+ bool mCanFetch;
+ bool mCanKnow;
+};
+
+class LLAssetDictionary : public LLSingleton<LLAssetDictionary>,
+ public LLDictionary<LLAssetType::EType, AssetEntry>
+{
+ LLSINGLETON(LLAssetDictionary);
+};
+
+LLAssetDictionary::LLAssetDictionary()
+{
+ // DESCRIPTION TYPE NAME HUMAN NAME CAN LINK? CAN FETCH? CAN KNOW?
+ // |--------------------|-----------|-------------------|-----------|-----------|---------|
+ addEntry(LLAssetType::AT_TEXTURE, new AssetEntry("TEXTURE", "texture", "texture", true, false, true));
+ addEntry(LLAssetType::AT_SOUND, new AssetEntry("SOUND", "sound", "sound", true, true, true));
+ addEntry(LLAssetType::AT_CALLINGCARD, new AssetEntry("CALLINGCARD", "callcard", "calling card", true, false, false));
+ addEntry(LLAssetType::AT_LANDMARK, new AssetEntry("LANDMARK", "landmark", "landmark", true, true, true));
+ addEntry(LLAssetType::AT_SCRIPT, new AssetEntry("SCRIPT", "script", "legacy script", true, false, false));
+ addEntry(LLAssetType::AT_CLOTHING, new AssetEntry("CLOTHING", "clothing", "clothing", true, true, true));
+ addEntry(LLAssetType::AT_OBJECT, new AssetEntry("OBJECT", "object", "object", true, false, false));
+ addEntry(LLAssetType::AT_NOTECARD, new AssetEntry("NOTECARD", "notecard", "note card", true, false, true));
+ addEntry(LLAssetType::AT_CATEGORY, new AssetEntry("CATEGORY", "category", "folder", true, false, false));
+ addEntry(LLAssetType::AT_LSL_TEXT, new AssetEntry("LSL_TEXT", "lsltext", "lsl2 script", true, false, false));
+ addEntry(LLAssetType::AT_LSL_BYTECODE, new AssetEntry("LSL_BYTECODE", "lslbyte", "lsl bytecode", true, false, false));
+ addEntry(LLAssetType::AT_TEXTURE_TGA, new AssetEntry("TEXTURE_TGA", "txtr_tga", "tga texture", true, false, false));
+ addEntry(LLAssetType::AT_BODYPART, new AssetEntry("BODYPART", "bodypart", "body part", true, true, true));
+ addEntry(LLAssetType::AT_SOUND_WAV, new AssetEntry("SOUND_WAV", "snd_wav", "sound", true, false, false));
+ addEntry(LLAssetType::AT_IMAGE_TGA, new AssetEntry("IMAGE_TGA", "img_tga", "targa image", true, false, false));
+ addEntry(LLAssetType::AT_IMAGE_JPEG, new AssetEntry("IMAGE_JPEG", "jpeg", "jpeg image", true, false, false));
+ addEntry(LLAssetType::AT_ANIMATION, new AssetEntry("ANIMATION", "animatn", "animation", true, true, true));
+ addEntry(LLAssetType::AT_GESTURE, new AssetEntry("GESTURE", "gesture", "gesture", true, true, true));
+ addEntry(LLAssetType::AT_SIMSTATE, new AssetEntry("SIMSTATE", "simstate", "simstate", false, false, false));
+
+ addEntry(LLAssetType::AT_LINK, new AssetEntry("LINK", "link", "sym link", false, false, true));
+ addEntry(LLAssetType::AT_LINK_FOLDER, new AssetEntry("FOLDER_LINK", "link_f", "sym folder link", false, false, true));
+ addEntry(LLAssetType::AT_MESH, new AssetEntry("MESH", "mesh", "mesh", false, false, false));
+ addEntry(LLAssetType::AT_WIDGET, new AssetEntry("WIDGET", "widget", "widget", false, false, false));
+ addEntry(LLAssetType::AT_PERSON, new AssetEntry("PERSON", "person", "person", false, false, false));
+ addEntry(LLAssetType::AT_SETTINGS, new AssetEntry("SETTINGS", "settings", "settings blob", true, true, true));
+ addEntry(LLAssetType::AT_MATERIAL, new AssetEntry("MATERIAL", "material", "render material", true, true, true));
+ addEntry(LLAssetType::AT_UNKNOWN, new AssetEntry("UNKNOWN", "invalid", NULL, false, false, false));
+ addEntry(LLAssetType::AT_NONE, new AssetEntry("NONE", "-1", NULL, false, false, false));
+
+};
+
+const std::string LLAssetType::BADLOOKUP("llassettype_bad_lookup");
+
+// static
+LLAssetType::EType LLAssetType::getType(const std::string& desc_name)
+{
+ std::string s = desc_name;
+ LLStringUtil::toUpper(s);
+ return LLAssetDictionary::getInstance()->lookup(s);
+}
+
+// static
+const std::string &LLAssetType::getDesc(LLAssetType::EType asset_type)
+{
+ const AssetEntry *entry = LLAssetDictionary::getInstance()->lookup(asset_type);
+ if (entry)
+ {
+ return entry->mName;
+ }
+ else
+ {
+ return BADLOOKUP;
+ }
+}
+
+// static
+const char *LLAssetType::lookup(LLAssetType::EType asset_type)
+{
+ const LLAssetDictionary *dict = LLAssetDictionary::getInstance();
+ const AssetEntry *entry = dict->lookup(asset_type);
+ if (entry)
+ {
+ return entry->mTypeName;
+ }
+ else
+ {
+ return BADLOOKUP.c_str();
+ }
+}
+
+// static
+LLAssetType::EType LLAssetType::lookup(const char* name)
+{
+ return lookup(ll_safe_string(name));
+}
+
+// static
+LLAssetType::EType LLAssetType::lookup(const std::string& type_name)
+{
+ const LLAssetDictionary *dict = LLAssetDictionary::getInstance();
+ for (const LLAssetDictionary::value_type& pair : *dict)
+ {
+ const AssetEntry *entry = pair.second;
+ if (type_name == entry->mTypeName)
+ {
+ return pair.first;
+ }
+ }
+ return AT_UNKNOWN;
+}
+
+// static
+const char *LLAssetType::lookupHumanReadable(LLAssetType::EType asset_type)
+{
+ const LLAssetDictionary *dict = LLAssetDictionary::getInstance();
+ const AssetEntry *entry = dict->lookup(asset_type);
+ if (entry)
+ {
+ return entry->mHumanName;
+ }
+ else
+ {
+ return BADLOOKUP.c_str();
+ }
+}
+
+// static
+LLAssetType::EType LLAssetType::lookupHumanReadable(const char* name)
+{
+ return lookupHumanReadable(ll_safe_string(name));
+}
+
+// static
+LLAssetType::EType LLAssetType::lookupHumanReadable(const std::string& readable_name)
+{
+ const LLAssetDictionary *dict = LLAssetDictionary::getInstance();
+ for (const LLAssetDictionary::value_type& pair : *dict)
+ {
+ const AssetEntry *entry = pair.second;
+ if (entry->mHumanName && (readable_name == entry->mHumanName))
+ {
+ return pair.first;
+ }
+ }
+ return AT_NONE;
+}
+
+// static
+bool LLAssetType::lookupCanLink(EType asset_type)
+{
+ const LLAssetDictionary *dict = LLAssetDictionary::getInstance();
+ const AssetEntry *entry = dict->lookup(asset_type);
+ if (entry)
+ {
+ return entry->mCanLink;
+ }
+ return false;
+}
+
+// static
+// Not adding this to dictionary since we probably will only have these two types
+bool LLAssetType::lookupIsLinkType(EType asset_type)
+{
+ if (asset_type == AT_LINK || asset_type == AT_LINK_FOLDER)
+ {
+ return true;
+ }
+ return false;
+}
+
+// static
+bool LLAssetType::lookupIsAssetFetchByIDAllowed(EType asset_type)
+{
+ const LLAssetDictionary *dict = LLAssetDictionary::getInstance();
+ const AssetEntry *entry = dict->lookup(asset_type);
+ if (entry)
+ {
+ return entry->mCanFetch;
+ }
+ return false;
+}
+
+// static
+bool LLAssetType::lookupIsAssetIDKnowable(EType asset_type)
+{
+ const LLAssetDictionary *dict = LLAssetDictionary::getInstance();
+ const AssetEntry *entry = dict->lookup(asset_type);
+ if (entry)
+ {
+ return entry->mCanKnow;
+ }
+ return false;
+}
diff --git a/indra/llcommon/llassettype.h b/indra/llcommon/llassettype.h index e8df8574f7..1989155550 100644 --- a/indra/llcommon/llassettype.h +++ b/indra/llcommon/llassettype.h @@ -1,25 +1,25 @@ -/** +/** * @file llassettype.h * @brief Declaration of LLAssetType. * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -32,89 +32,89 @@ class LL_COMMON_API LLAssetType { public: - enum EType - { - AT_TEXTURE = 0, - // Used for painting the faces of geometry. - // Stored in typical j2c stream format. + enum EType + { + AT_TEXTURE = 0, + // Used for painting the faces of geometry. + // Stored in typical j2c stream format. - AT_SOUND = 1, - // Used to fill the aural spectrum. + AT_SOUND = 1, + // Used to fill the aural spectrum. - AT_CALLINGCARD = 2, - // Links instant message access to the user on the card. - // : E.G. A card for yourself, for linden support, for - // : the guy you were talking to in the coliseum. + AT_CALLINGCARD = 2, + // Links instant message access to the user on the card. + // : E.G. A card for yourself, for linden support, for + // : the guy you were talking to in the coliseum. - AT_LANDMARK = 3, - // Links to places in the world with location and a screen shot or image saved. - // : E.G. Home, linden headquarters, the coliseum, destinations where - // : we want to increase traffic. + AT_LANDMARK = 3, + // Links to places in the world with location and a screen shot or image saved. + // : E.G. Home, linden headquarters, the coliseum, destinations where + // : we want to increase traffic. - AT_SCRIPT = 4, - // Valid scripts that can be attached to an object. - // : E.G. Open a door, jump into the air. + AT_SCRIPT = 4, + // Valid scripts that can be attached to an object. + // : E.G. Open a door, jump into the air. - AT_CLOTHING = 5, - // A collection of textures and parameters that can be worn by an avatar. + AT_CLOTHING = 5, + // A collection of textures and parameters that can be worn by an avatar. - AT_OBJECT = 6, - // Any combination of textures, sounds, and scripts that are - // associated with a fixed piece of geometry. - // : E.G. A hot tub, a house with working door. + AT_OBJECT = 6, + // Any combination of textures, sounds, and scripts that are + // associated with a fixed piece of geometry. + // : E.G. A hot tub, a house with working door. - AT_NOTECARD = 7, - // Just text. + AT_NOTECARD = 7, + // Just text. - AT_CATEGORY = 8, - // Holds a collection of inventory items. - // It's treated as an item in the inventory and therefore needs a type. + AT_CATEGORY = 8, + // Holds a collection of inventory items. + // It's treated as an item in the inventory and therefore needs a type. - AT_LSL_TEXT = 10, - AT_LSL_BYTECODE = 11, - // The LSL is the scripting language. - // We've split it into a text and bytecode representation. - - AT_TEXTURE_TGA = 12, - // Uncompressed TGA texture. + AT_LSL_TEXT = 10, + AT_LSL_BYTECODE = 11, + // The LSL is the scripting language. + // We've split it into a text and bytecode representation. - AT_BODYPART = 13, - // A collection of textures and parameters that can be worn by an avatar. + AT_TEXTURE_TGA = 12, + // Uncompressed TGA texture. - AT_SOUND_WAV = 17, - // Uncompressed sound. + AT_BODYPART = 13, + // A collection of textures and parameters that can be worn by an avatar. - AT_IMAGE_TGA = 18, - // Uncompressed image, non-square. - // Not appropriate for use as a texture. + AT_SOUND_WAV = 17, + // Uncompressed sound. - AT_IMAGE_JPEG = 19, - // Compressed image, non-square. - // Not appropriate for use as a texture. + AT_IMAGE_TGA = 18, + // Uncompressed image, non-square. + // Not appropriate for use as a texture. - AT_ANIMATION = 20, - // Animation. + AT_IMAGE_JPEG = 19, + // Compressed image, non-square. + // Not appropriate for use as a texture. - AT_GESTURE = 21, - // Gesture, sequence of animations, sounds, chat, wait steps. + AT_ANIMATION = 20, + // Animation. - AT_SIMSTATE = 22, - // Simstate file. + AT_GESTURE = 21, + // Gesture, sequence of animations, sounds, chat, wait steps. - AT_LINK = 24, - // Inventory symbolic link + AT_SIMSTATE = 22, + // Simstate file. + + AT_LINK = 24, + // Inventory symbolic link + + AT_LINK_FOLDER = 25, + // Inventory folder link - AT_LINK_FOLDER = 25, - // Inventory folder link - AT_MARKETPLACE_FOLDER = 26, // Marketplace folder. Same as an AT_CATEGORY but different display methods. - - AT_WIDGET = 40, - // UI Widget: this is *not* an inventory asset type, only a viewer side asset (e.g. button, other ui items...) - - AT_PERSON = 45, - // A user uuid which is not an inventory asset type, used in viewer only for adding a person to a chat via drag and drop. + + AT_WIDGET = 40, + // UI Widget: this is *not* an inventory asset type, only a viewer side asset (e.g. button, other ui items...) + + AT_PERSON = 45, + // A user uuid which is not an inventory asset type, used in viewer only for adding a person to a chat via drag and drop. AT_MESH = 49, // Mesh data in our proprietary SLM format @@ -129,45 +129,45 @@ public: AT_SETTINGS = 56, // Collection of settings AT_MATERIAL = 57, // Render Material - AT_COUNT = 58, + AT_COUNT = 58, - // +*********************************************************+ - // | TO ADD AN ELEMENT TO THIS ENUM: | - // +*********************************************************+ - // | 1. INSERT BEFORE AT_COUNT | - // | 2. INCREMENT AT_COUNT BY 1 | - // | 3. ADD TO LLAssetType.cpp | - // | 4. ADD TO LLViewerAssetType.cpp | - // | 5. ADD TO DEFAULT_ASSET_FOR_INV in LLInventoryType.cpp | - // +*********************************************************+ - AT_UNKNOWN = 255, - AT_NONE = -1 - }; + // +*********************************************************+ + // | TO ADD AN ELEMENT TO THIS ENUM: | + // +*********************************************************+ + // | 1. INSERT BEFORE AT_COUNT | + // | 2. INCREMENT AT_COUNT BY 1 | + // | 3. ADD TO LLAssetType.cpp | + // | 4. ADD TO LLViewerAssetType.cpp | + // | 5. ADD TO DEFAULT_ASSET_FOR_INV in LLInventoryType.cpp | + // +*********************************************************+ + AT_UNKNOWN = 255, + AT_NONE = -1 + }; - // machine transation between type and strings - static EType lookup(const char* name); // safe conversion to std::string, *TODO: deprecate - static EType lookup(const std::string& type_name); - static const char* lookup(EType asset_type); + // machine transation between type and strings + static EType lookup(const char* name); // safe conversion to std::string, *TODO: deprecate + static EType lookup(const std::string& type_name); + static const char* lookup(EType asset_type); - // translation from a type to a human readable form. - static EType lookupHumanReadable(const char* desc_name); // safe conversion to std::string, *TODO: deprecate - static EType lookupHumanReadable(const std::string& readable_name); - static const char* lookupHumanReadable(EType asset_type); + // translation from a type to a human readable form. + static EType lookupHumanReadable(const char* desc_name); // safe conversion to std::string, *TODO: deprecate + static EType lookupHumanReadable(const std::string& readable_name); + static const char* lookupHumanReadable(EType asset_type); - static EType getType(const std::string& desc_name); - static const std::string& getDesc(EType asset_type); + static EType getType(const std::string& desc_name); + static const std::string& getDesc(EType asset_type); - static bool lookupCanLink(EType asset_type); - static bool lookupIsLinkType(EType asset_type); + static bool lookupCanLink(EType asset_type); + static bool lookupIsLinkType(EType asset_type); - static bool lookupIsAssetFetchByIDAllowed(EType asset_type); // the asset allows direct download - static bool lookupIsAssetIDKnowable(EType asset_type); // asset data can be known by the viewer + static bool lookupIsAssetFetchByIDAllowed(EType asset_type); // the asset allows direct download + static bool lookupIsAssetIDKnowable(EType asset_type); // asset data can be known by the viewer static const std::string BADLOOKUP; protected: - LLAssetType() {} - ~LLAssetType() {} + LLAssetType() {} + ~LLAssetType() {} }; #endif // LL_LLASSETTYPE_H diff --git a/indra/llcommon/llatomic.cpp b/indra/llcommon/llatomic.cpp index 93aba1f460..d200bb0406 100644 --- a/indra/llcommon/llatomic.cpp +++ b/indra/llcommon/llatomic.cpp @@ -1,24 +1,24 @@ -/** +/** * @file llatomic.cpp * * $LicenseInfo:firstyear=2018&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2018, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llcommon/llatomic.h b/indra/llcommon/llatomic.h index 8de773846c..c5304637cc 100644 --- a/indra/llcommon/llatomic.h +++ b/indra/llcommon/llatomic.h @@ -1,25 +1,25 @@ -/** +/** * @file llatomic.h * @brief Base classes for atomic. * * $LicenseInfo:firstyear=2018&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2018, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -40,7 +40,7 @@ public: operator const Type() { return mData; } - Type CurrentValue() const { return mData; } + Type CurrentValue() const { return mData; } Type operator =(const Type& x) { mData.store(x); return mData; } void operator -=(Type x) { mData -= x; } diff --git a/indra/llcommon/llbase32.cpp b/indra/llcommon/llbase32.cpp index 349567c90b..979114fe86 100644 --- a/indra/llcommon/llbase32.cpp +++ b/indra/llcommon/llbase32.cpp @@ -1,4 +1,4 @@ -/** +/** * @file llbase32.cpp * @brief base32 encoding that returns a std::string * @author James Cook @@ -10,21 +10,21 @@ * $LicenseInfo:firstyear=2007&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -118,9 +118,9 @@ base32_encode(char *dst, size_t size, const void *data, size_t len) | 6 | 1 0 7 6 5 | 3-4 | | 7 | 4 3 2 1 0 | 4 | +-------+-----------+--------+ - + */ - + s[0] = (x[0] >> 3); s[1] = ((x[0] & 0x07) << 2) | (x[1] >> 6); s[2] = (x[1] >> 1) & 0x1f; @@ -167,12 +167,12 @@ base32_decode(char *dst, size_t size, const void *data, size_t len) if (0 == base32_map[0]) { for (i = 0; i < LL_ARRAY_SIZE(base32_map); i++) { const char *x; - + x = memchr(base32_alphabet, ascii_toupper(i), sizeof base32_alphabet); base32_map[i] = x ? (x - base32_alphabet) : (unsigned char) -1; } } - + for (i = 0; i < len && max_pad > 0; i++) { unsigned char c; char s[8]; @@ -219,20 +219,20 @@ base32_decode(char *dst, size_t size, const void *data, size_t len) // static std::string LLBase32::encode(const U8* input, size_t input_size) { - std::string output; - if (input) - { - // Each 5 byte chunk of input is represented by an - // 8 byte chunk of output. - size_t input_chunks = (input_size + 4) / 5; - size_t output_size = input_chunks * 8; - - output.resize(output_size); - - size_t encoded = base32_encode(&output[0], output_size, input, input_size); - - LL_INFOS() << "encoded " << encoded << " into buffer of size " - << output_size << LL_ENDL; - } - return output; + std::string output; + if (input) + { + // Each 5 byte chunk of input is represented by an + // 8 byte chunk of output. + size_t input_chunks = (input_size + 4) / 5; + size_t output_size = input_chunks * 8; + + output.resize(output_size); + + size_t encoded = base32_encode(&output[0], output_size, input, input_size); + + LL_INFOS() << "encoded " << encoded << " into buffer of size " + << output_size << LL_ENDL; + } + return output; } diff --git a/indra/llcommon/llbase32.h b/indra/llcommon/llbase32.h index eeb96d789d..8cfbd7ef53 100644 --- a/indra/llcommon/llbase32.h +++ b/indra/llcommon/llbase32.h @@ -1,4 +1,4 @@ -/** +/** * @file llbase32.h * @brief base32 encoding that returns a std::string * @author James Cook @@ -6,21 +6,21 @@ * $LicenseInfo:firstyear=2007&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -31,7 +31,7 @@ class LL_COMMON_API LLBase32 { public: - static std::string encode(const U8* input, size_t input_size); + static std::string encode(const U8* input, size_t input_size); }; #endif diff --git a/indra/llcommon/llbase64.cpp b/indra/llcommon/llbase64.cpp index c5960f1c7e..144b878db3 100644 --- a/indra/llcommon/llbase64.cpp +++ b/indra/llcommon/llbase64.cpp @@ -1,77 +1,77 @@ -/** - * @file llbase64.cpp - * @brief Wrapper for apr base64 encoding that returns a std::string - * @author James Cook - * - * $LicenseInfo:firstyear=2007&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" - -#include "llbase64.h" - -#include <string> - -#include "apr_base64.h" - - -// static -std::string LLBase64::encode(const U8* input, size_t input_size) -{ - std::string output; - if (input - && input_size > 0) - { - // Yes, it returns int. - int b64_buffer_length = apr_base64_encode_len(narrow<size_t>(input_size)); - char* b64_buffer = new char[b64_buffer_length]; - - // This is faster than apr_base64_encode() if you know - // you're not on an EBCDIC machine. Also, the output is - // null terminated, even though the documentation doesn't - // specify. See apr_base64.c for details. JC - b64_buffer_length = apr_base64_encode_binary( - b64_buffer, - input, - narrow<size_t>(input_size)); - output.assign(b64_buffer); - delete[] b64_buffer; - } - return output; -} - -std::string LLBase64::decodeAsString(const std::string &input) -{ - int b64_buffer_length = apr_base64_decode_len(input.c_str()); - char* b64_buffer = new char[b64_buffer_length]; - - // This is faster than apr_base64_encode() if you know - // you're not on an EBCDIC machine. Also, the output is - // null terminated, even though the documentation doesn't - // specify. See apr_base64.c for details. JC - b64_buffer_length = apr_base64_decode(b64_buffer, input.c_str()); - std::string res; - res.assign(b64_buffer); - delete[] b64_buffer; - return res; -} - +/**
+ * @file llbase64.cpp
+ * @brief Wrapper for apr base64 encoding that returns a std::string
+ * @author James Cook
+ *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llbase64.h"
+
+#include <string>
+
+#include "apr_base64.h"
+
+
+// static
+std::string LLBase64::encode(const U8* input, size_t input_size)
+{
+ std::string output;
+ if (input
+ && input_size > 0)
+ {
+ // Yes, it returns int.
+ int b64_buffer_length = apr_base64_encode_len(narrow<size_t>(input_size));
+ char* b64_buffer = new char[b64_buffer_length];
+
+ // This is faster than apr_base64_encode() if you know
+ // you're not on an EBCDIC machine. Also, the output is
+ // null terminated, even though the documentation doesn't
+ // specify. See apr_base64.c for details. JC
+ b64_buffer_length = apr_base64_encode_binary(
+ b64_buffer,
+ input,
+ narrow<size_t>(input_size));
+ output.assign(b64_buffer);
+ delete[] b64_buffer;
+ }
+ return output;
+}
+
+std::string LLBase64::decodeAsString(const std::string &input)
+{
+ int b64_buffer_length = apr_base64_decode_len(input.c_str());
+ char* b64_buffer = new char[b64_buffer_length];
+
+ // This is faster than apr_base64_encode() if you know
+ // you're not on an EBCDIC machine. Also, the output is
+ // null terminated, even though the documentation doesn't
+ // specify. See apr_base64.c for details. JC
+ b64_buffer_length = apr_base64_decode(b64_buffer, input.c_str());
+ std::string res;
+ res.assign(b64_buffer);
+ delete[] b64_buffer;
+ return res;
+}
+
diff --git a/indra/llcommon/llbase64.h b/indra/llcommon/llbase64.h index b985963fc4..a64c4f1e54 100644 --- a/indra/llcommon/llbase64.h +++ b/indra/llcommon/llbase64.h @@ -1,38 +1,38 @@ -/** - * @file llbase64.h - * @brief Wrapper for apr base64 encoding that returns a std::string - * @author James Cook - * - * $LicenseInfo:firstyear=2007&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LLBASE64_H -#define LLBASE64_H - -class LL_COMMON_API LLBase64 -{ -public: - static std::string encode(const U8* input, size_t input_size); - static std::string decodeAsString(const std::string& input); -}; - -#endif +/**
+ * @file llbase64.h
+ * @brief Wrapper for apr base64 encoding that returns a std::string
+ * @author James Cook
+ *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LLBASE64_H
+#define LLBASE64_H
+
+class LL_COMMON_API LLBase64
+{
+public:
+ static std::string encode(const U8* input, size_t input_size);
+ static std::string decodeAsString(const std::string& input);
+};
+
+#endif
diff --git a/indra/llcommon/llbitpack.cpp b/indra/llcommon/llbitpack.cpp index 622a099945..011b310f40 100644 --- a/indra/llcommon/llbitpack.cpp +++ b/indra/llcommon/llbitpack.cpp @@ -1,25 +1,25 @@ -/** +/** * @file bitpack.cpp * @brief LLBitPack class implementation * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llcommon/llbitpack.h b/indra/llcommon/llbitpack.h index f99a354cd4..f7eb57a4f0 100644 --- a/indra/llcommon/llbitpack.h +++ b/indra/llcommon/llbitpack.h @@ -1,25 +1,25 @@ -/** +/** * @file llbitpack.h * @brief Convert data to packed bit stream * * $LicenseInfo:firstyear=2000&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -35,174 +35,174 @@ const U32 MAX_DATA_BITS = 8; class LLBitPack { public: - LLBitPack(U8 *buffer, U32 max_size) : mBuffer(buffer), mBufferSize(0), mLoad(0), mLoadSize(0), mTotalBits(0), mMaxSize(max_size) - { - } - - ~LLBitPack() - { - } - - void resetBitPacking() - { - mLoad = 0; - mLoadSize = 0; - mTotalBits = 0; - mBufferSize = 0; - } - - U32 bitPack(U8 *total_data, U32 total_dsize) - { - U32 dsize; - U8 data; - - while (total_dsize > 0) - { - if (total_dsize > MAX_DATA_BITS) - { - dsize = MAX_DATA_BITS; - total_dsize -= MAX_DATA_BITS; - } - else - { - dsize = total_dsize; - total_dsize = 0; - } - - data = *total_data++; - - data <<= (MAX_DATA_BITS - dsize); - while (dsize > 0) - { - if (mLoadSize == MAX_DATA_BITS) - { - *(mBuffer + mBufferSize++) = mLoad; - if (mBufferSize > mMaxSize) - { - LL_ERRS() << "mBufferSize exceeding mMaxSize!" << LL_ENDL; - } - mLoadSize = 0; - mLoad = 0x00; - } - mLoad <<= 1; - mLoad |= (data >> (MAX_DATA_BITS - 1)); - data <<= 1; - mLoadSize++; - mTotalBits++; - dsize--; - } - } - return mBufferSize; - } - - U32 bitCopy(U8 *total_data, U32 total_dsize) - { - U32 dsize; - U8 data; - - while (total_dsize > 0) - { - if (total_dsize > MAX_DATA_BITS) - { - dsize = MAX_DATA_BITS; - total_dsize -= MAX_DATA_BITS; - } - else - { - dsize = total_dsize; - total_dsize = 0; - } - - data = *total_data++; - - while (dsize > 0) - { - if (mLoadSize == MAX_DATA_BITS) - { - *(mBuffer + mBufferSize++) = mLoad; - if (mBufferSize > mMaxSize) - { - LL_ERRS() << "mBufferSize exceeding mMaxSize!" << LL_ENDL; - } - mLoadSize = 0; - mLoad = 0x00; - } - mLoad <<= 1; - mLoad |= (data >> (MAX_DATA_BITS - 1)); - data <<= 1; - mLoadSize++; - mTotalBits++; - dsize--; - } - } - return mBufferSize; - } - - U32 bitUnpack(U8 *total_retval, U32 total_dsize) - { - U32 dsize; - U8 *retval; - - while (total_dsize > 0) - { - if (total_dsize > MAX_DATA_BITS) - { - dsize = MAX_DATA_BITS; - total_dsize -= MAX_DATA_BITS; - } - else - { - dsize = total_dsize; - total_dsize = 0; - } - - retval = total_retval++; - *retval = 0x00; - while (dsize > 0) - { - if (mLoadSize == 0) - { + LLBitPack(U8 *buffer, U32 max_size) : mBuffer(buffer), mBufferSize(0), mLoad(0), mLoadSize(0), mTotalBits(0), mMaxSize(max_size) + { + } + + ~LLBitPack() + { + } + + void resetBitPacking() + { + mLoad = 0; + mLoadSize = 0; + mTotalBits = 0; + mBufferSize = 0; + } + + U32 bitPack(U8 *total_data, U32 total_dsize) + { + U32 dsize; + U8 data; + + while (total_dsize > 0) + { + if (total_dsize > MAX_DATA_BITS) + { + dsize = MAX_DATA_BITS; + total_dsize -= MAX_DATA_BITS; + } + else + { + dsize = total_dsize; + total_dsize = 0; + } + + data = *total_data++; + + data <<= (MAX_DATA_BITS - dsize); + while (dsize > 0) + { + if (mLoadSize == MAX_DATA_BITS) + { + *(mBuffer + mBufferSize++) = mLoad; + if (mBufferSize > mMaxSize) + { + LL_ERRS() << "mBufferSize exceeding mMaxSize!" << LL_ENDL; + } + mLoadSize = 0; + mLoad = 0x00; + } + mLoad <<= 1; + mLoad |= (data >> (MAX_DATA_BITS - 1)); + data <<= 1; + mLoadSize++; + mTotalBits++; + dsize--; + } + } + return mBufferSize; + } + + U32 bitCopy(U8 *total_data, U32 total_dsize) + { + U32 dsize; + U8 data; + + while (total_dsize > 0) + { + if (total_dsize > MAX_DATA_BITS) + { + dsize = MAX_DATA_BITS; + total_dsize -= MAX_DATA_BITS; + } + else + { + dsize = total_dsize; + total_dsize = 0; + } + + data = *total_data++; + + while (dsize > 0) + { + if (mLoadSize == MAX_DATA_BITS) + { + *(mBuffer + mBufferSize++) = mLoad; + if (mBufferSize > mMaxSize) + { + LL_ERRS() << "mBufferSize exceeding mMaxSize!" << LL_ENDL; + } + mLoadSize = 0; + mLoad = 0x00; + } + mLoad <<= 1; + mLoad |= (data >> (MAX_DATA_BITS - 1)); + data <<= 1; + mLoadSize++; + mTotalBits++; + dsize--; + } + } + return mBufferSize; + } + + U32 bitUnpack(U8 *total_retval, U32 total_dsize) + { + U32 dsize; + U8 *retval; + + while (total_dsize > 0) + { + if (total_dsize > MAX_DATA_BITS) + { + dsize = MAX_DATA_BITS; + total_dsize -= MAX_DATA_BITS; + } + else + { + dsize = total_dsize; + total_dsize = 0; + } + + retval = total_retval++; + *retval = 0x00; + while (dsize > 0) + { + if (mLoadSize == 0) + { #ifdef _DEBUG - if (mBufferSize > mMaxSize) - { - LL_ERRS() << "mBufferSize exceeding mMaxSize" << LL_ENDL; - LL_ERRS() << mBufferSize << " > " << mMaxSize << LL_ENDL; - } + if (mBufferSize > mMaxSize) + { + LL_ERRS() << "mBufferSize exceeding mMaxSize" << LL_ENDL; + LL_ERRS() << mBufferSize << " > " << mMaxSize << LL_ENDL; + } #endif - mLoad = *(mBuffer + mBufferSize++); - mLoadSize = MAX_DATA_BITS; - } - *retval <<= 1; - *retval |= (mLoad >> (MAX_DATA_BITS - 1)); - mLoadSize--; - mLoad <<= 1; - dsize--; - } - } - return mBufferSize; - } - - U32 flushBitPack() - { - if (mLoadSize) - { - mLoad <<= (MAX_DATA_BITS - mLoadSize); - *(mBuffer + mBufferSize++) = mLoad; - if (mBufferSize > mMaxSize) - { - LL_ERRS() << "mBufferSize exceeding mMaxSize!" << LL_ENDL; - } - mLoadSize = 0; - } - return mBufferSize; - } - - U8 *mBuffer; - U32 mBufferSize; - U8 mLoad; - U32 mLoadSize; - U32 mTotalBits; - U32 mMaxSize; + mLoad = *(mBuffer + mBufferSize++); + mLoadSize = MAX_DATA_BITS; + } + *retval <<= 1; + *retval |= (mLoad >> (MAX_DATA_BITS - 1)); + mLoadSize--; + mLoad <<= 1; + dsize--; + } + } + return mBufferSize; + } + + U32 flushBitPack() + { + if (mLoadSize) + { + mLoad <<= (MAX_DATA_BITS - mLoadSize); + *(mBuffer + mBufferSize++) = mLoad; + if (mBufferSize > mMaxSize) + { + LL_ERRS() << "mBufferSize exceeding mMaxSize!" << LL_ENDL; + } + mLoadSize = 0; + } + return mBufferSize; + } + + U8 *mBuffer; + U32 mBufferSize; + U8 mLoad; + U32 mLoadSize; + U32 mTotalBits; + U32 mMaxSize; }; #endif diff --git a/indra/llcommon/llboost.h b/indra/llcommon/llboost.h index 57d958a51a..980fc9e1c4 100644 --- a/indra/llcommon/llboost.h +++ b/indra/llcommon/llboost.h @@ -1,25 +1,25 @@ -/** +/** * @file llboost.h * @brief helper object & functions for use with boost * * $LicenseInfo:firstyear=2005&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -31,12 +31,12 @@ // boost_tokenizer typedef /* example usage: - boost_tokenizer tokens(input_string, boost::char_separator<char>(" \t\n")); - for (boost_tokenizer::iterator token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter) - { - std::string tok = *token_iter; - process_token_string( tok ); - } + boost_tokenizer tokens(input_string, boost::char_separator<char>(" \t\n")); + for (boost_tokenizer::iterator token_iter = tokens.begin(); token_iter != tokens.end(); ++token_iter) + { + std::string tok = *token_iter; + process_token_string( tok ); + } */ typedef boost::tokenizer<boost::char_separator<char> > boost_tokenizer; @@ -44,15 +44,15 @@ typedef boost::tokenizer<boost::char_separator<char> > boost_tokenizer; // returns false if any of the callbacks return false struct boost_boolean_combiner { - typedef bool result_type; - template<typename InputIterator> - bool operator()(InputIterator first, InputIterator last) const - { - bool res = true; - while (first != last) - res &= *first++; - return res; - } + typedef bool result_type; + template<typename InputIterator> + bool operator()(InputIterator first, InputIterator last) const + { + bool res = true; + while (first != last) + res &= *first++; + return res; + } }; #endif // LL_LLBOOST_H diff --git a/indra/llcommon/llcallbacklist.cpp b/indra/llcommon/llcallbacklist.cpp index a2800d42ce..9705b69e11 100644 --- a/indra/llcommon/llcallbacklist.cpp +++ b/indra/llcommon/llcallbacklist.cpp @@ -1,230 +1,230 @@ -/** - * @file llcallbacklist.cpp - * @brief A simple list of callback functions to call. - * - * $LicenseInfo:firstyear=2001&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "llcallbacklist.h" -#include "lleventtimer.h" -#include "llerrorlegacy.h" - -// Globals -// -LLCallbackList gIdleCallbacks; - -// -// Member functions -// - -LLCallbackList::LLCallbackList() -{ - // nothing -} - -LLCallbackList::~LLCallbackList() -{ -} - - -void LLCallbackList::addFunction( callback_t func, void *data) -{ - if (!func) - { - return; - } - - // only add one callback per func/data pair - // - if (containsFunction(func, data)) - { - return; - } - - callback_pair_t t(func, data); - mCallbackList.push_back(t); -} - -bool LLCallbackList::containsFunction( callback_t func, void *data) -{ - callback_pair_t t(func, data); - callback_list_t::iterator iter = find(func,data); - if (iter != mCallbackList.end()) - { - return true; - } - else - { - return false; - } -} - - -bool LLCallbackList::deleteFunction( callback_t func, void *data) -{ - callback_list_t::iterator iter = find(func,data); - if (iter != mCallbackList.end()) - { - mCallbackList.erase(iter); - return true; - } - else - { - return false; - } -} - -inline -LLCallbackList::callback_list_t::iterator -LLCallbackList::find(callback_t func, void *data) -{ - callback_pair_t t(func, data); - return std::find(mCallbackList.begin(), mCallbackList.end(), t); -} - -void LLCallbackList::deleteAllFunctions() -{ - mCallbackList.clear(); -} - - -void LLCallbackList::callFunctions() -{ - for (callback_list_t::iterator iter = mCallbackList.begin(); iter != mCallbackList.end(); ) - { - callback_list_t::iterator curiter = iter++; - curiter->first(curiter->second); - } -} - -// Shim class to allow arbitrary boost::bind -// expressions to be run as one-time idle callbacks. -class OnIdleCallbackOneTime -{ -public: - OnIdleCallbackOneTime(nullary_func_t callable): - mCallable(callable) - { - } - static void onIdle(void *data) - { - gIdleCallbacks.deleteFunction(onIdle, data); - OnIdleCallbackOneTime* self = reinterpret_cast<OnIdleCallbackOneTime*>(data); - self->call(); - delete self; - } - void call() - { - mCallable(); - } -private: - nullary_func_t mCallable; -}; - -void doOnIdleOneTime(nullary_func_t callable) -{ - OnIdleCallbackOneTime* cb_functor = new OnIdleCallbackOneTime(callable); - gIdleCallbacks.addFunction(&OnIdleCallbackOneTime::onIdle,cb_functor); -} - -// Shim class to allow generic boost functions to be run as -// recurring idle callbacks. Callable should return true when done, -// false to continue getting called. -class OnIdleCallbackRepeating -{ -public: - OnIdleCallbackRepeating(bool_func_t callable): - mCallable(callable) - { - } - // Will keep getting called until the callable returns true. - static void onIdle(void *data) - { - OnIdleCallbackRepeating* self = reinterpret_cast<OnIdleCallbackRepeating*>(data); - bool done = self->call(); - if (done) - { - gIdleCallbacks.deleteFunction(onIdle, data); - delete self; - } - } - bool call() - { - return mCallable(); - } -private: - bool_func_t mCallable; -}; - -void doOnIdleRepeating(bool_func_t callable) -{ - OnIdleCallbackRepeating* cb_functor = new OnIdleCallbackRepeating(callable); - gIdleCallbacks.addFunction(&OnIdleCallbackRepeating::onIdle,cb_functor); -} - -class NullaryFuncEventTimer: public LLEventTimer -{ -public: - NullaryFuncEventTimer(nullary_func_t callable, F32 seconds): - LLEventTimer(seconds), - mCallable(callable) - { - } - -private: - bool tick() - { - mCallable(); - return true; - } - - nullary_func_t mCallable; -}; - -// Call a given callable once after specified interval. -void doAfterInterval(nullary_func_t callable, F32 seconds) -{ - new NullaryFuncEventTimer(callable, seconds); -} - -class BoolFuncEventTimer: public LLEventTimer -{ -public: - BoolFuncEventTimer(bool_func_t callable, F32 seconds): - LLEventTimer(seconds), - mCallable(callable) - { - } -private: - bool tick() - { - return mCallable(); - } - - bool_func_t mCallable; -}; - -// Call a given callable every specified number of seconds, until it returns true. -void doPeriodically(bool_func_t callable, F32 seconds) -{ - new BoolFuncEventTimer(callable, seconds); -} +/**
+ * @file llcallbacklist.cpp
+ * @brief A simple list of callback functions to call.
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llcallbacklist.h"
+#include "lleventtimer.h"
+#include "llerrorlegacy.h"
+
+// Globals
+//
+LLCallbackList gIdleCallbacks;
+
+//
+// Member functions
+//
+
+LLCallbackList::LLCallbackList()
+{
+ // nothing
+}
+
+LLCallbackList::~LLCallbackList()
+{
+}
+
+
+void LLCallbackList::addFunction( callback_t func, void *data)
+{
+ if (!func)
+ {
+ return;
+ }
+
+ // only add one callback per func/data pair
+ //
+ if (containsFunction(func, data))
+ {
+ return;
+ }
+
+ callback_pair_t t(func, data);
+ mCallbackList.push_back(t);
+}
+
+bool LLCallbackList::containsFunction( callback_t func, void *data)
+{
+ callback_pair_t t(func, data);
+ callback_list_t::iterator iter = find(func,data);
+ if (iter != mCallbackList.end())
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+
+bool LLCallbackList::deleteFunction( callback_t func, void *data)
+{
+ callback_list_t::iterator iter = find(func,data);
+ if (iter != mCallbackList.end())
+ {
+ mCallbackList.erase(iter);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+inline
+LLCallbackList::callback_list_t::iterator
+LLCallbackList::find(callback_t func, void *data)
+{
+ callback_pair_t t(func, data);
+ return std::find(mCallbackList.begin(), mCallbackList.end(), t);
+}
+
+void LLCallbackList::deleteAllFunctions()
+{
+ mCallbackList.clear();
+}
+
+
+void LLCallbackList::callFunctions()
+{
+ for (callback_list_t::iterator iter = mCallbackList.begin(); iter != mCallbackList.end(); )
+ {
+ callback_list_t::iterator curiter = iter++;
+ curiter->first(curiter->second);
+ }
+}
+
+// Shim class to allow arbitrary boost::bind
+// expressions to be run as one-time idle callbacks.
+class OnIdleCallbackOneTime
+{
+public:
+ OnIdleCallbackOneTime(nullary_func_t callable):
+ mCallable(callable)
+ {
+ }
+ static void onIdle(void *data)
+ {
+ gIdleCallbacks.deleteFunction(onIdle, data);
+ OnIdleCallbackOneTime* self = reinterpret_cast<OnIdleCallbackOneTime*>(data);
+ self->call();
+ delete self;
+ }
+ void call()
+ {
+ mCallable();
+ }
+private:
+ nullary_func_t mCallable;
+};
+
+void doOnIdleOneTime(nullary_func_t callable)
+{
+ OnIdleCallbackOneTime* cb_functor = new OnIdleCallbackOneTime(callable);
+ gIdleCallbacks.addFunction(&OnIdleCallbackOneTime::onIdle,cb_functor);
+}
+
+// Shim class to allow generic boost functions to be run as
+// recurring idle callbacks. Callable should return true when done,
+// false to continue getting called.
+class OnIdleCallbackRepeating
+{
+public:
+ OnIdleCallbackRepeating(bool_func_t callable):
+ mCallable(callable)
+ {
+ }
+ // Will keep getting called until the callable returns true.
+ static void onIdle(void *data)
+ {
+ OnIdleCallbackRepeating* self = reinterpret_cast<OnIdleCallbackRepeating*>(data);
+ bool done = self->call();
+ if (done)
+ {
+ gIdleCallbacks.deleteFunction(onIdle, data);
+ delete self;
+ }
+ }
+ bool call()
+ {
+ return mCallable();
+ }
+private:
+ bool_func_t mCallable;
+};
+
+void doOnIdleRepeating(bool_func_t callable)
+{
+ OnIdleCallbackRepeating* cb_functor = new OnIdleCallbackRepeating(callable);
+ gIdleCallbacks.addFunction(&OnIdleCallbackRepeating::onIdle,cb_functor);
+}
+
+class NullaryFuncEventTimer: public LLEventTimer
+{
+public:
+ NullaryFuncEventTimer(nullary_func_t callable, F32 seconds):
+ LLEventTimer(seconds),
+ mCallable(callable)
+ {
+ }
+
+private:
+ bool tick()
+ {
+ mCallable();
+ return true;
+ }
+
+ nullary_func_t mCallable;
+};
+
+// Call a given callable once after specified interval.
+void doAfterInterval(nullary_func_t callable, F32 seconds)
+{
+ new NullaryFuncEventTimer(callable, seconds);
+}
+
+class BoolFuncEventTimer: public LLEventTimer
+{
+public:
+ BoolFuncEventTimer(bool_func_t callable, F32 seconds):
+ LLEventTimer(seconds),
+ mCallable(callable)
+ {
+ }
+private:
+ bool tick()
+ {
+ return mCallable();
+ }
+
+ bool_func_t mCallable;
+};
+
+// Call a given callable every specified number of seconds, until it returns true.
+void doPeriodically(bool_func_t callable, F32 seconds)
+{
+ new BoolFuncEventTimer(callable, seconds);
+}
diff --git a/indra/llcommon/llcallbacklist.h b/indra/llcommon/llcallbacklist.h index 89716cd74c..d6c415f7c5 100644 --- a/indra/llcommon/llcallbacklist.h +++ b/indra/llcommon/llcallbacklist.h @@ -1,25 +1,25 @@ -/** +/** * @file llcallbacklist.h * @brief A simple list of callback functions to call. * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -34,29 +34,29 @@ class LLCallbackList { public: - typedef void (*callback_t)(void*); + typedef void (*callback_t)(void*); + + typedef std::pair< callback_t,void* > callback_pair_t; + // NOTE: It is confirmed that we DEPEND on the order provided by using a list :( + // + typedef std::list< callback_pair_t > callback_list_t; - typedef std::pair< callback_t,void* > callback_pair_t; - // NOTE: It is confirmed that we DEPEND on the order provided by using a list :( - // - typedef std::list< callback_pair_t > callback_list_t; - - LLCallbackList(); - ~LLCallbackList(); + LLCallbackList(); + ~LLCallbackList(); - void addFunction( callback_t func, void *data = NULL ); // register a callback, which will be called as func(data) - bool containsFunction( callback_t func, void *data = NULL ); // true if list already contains the function/data pair - bool deleteFunction( callback_t func, void *data = NULL ); // removes the first instance of this function/data pair from the list, false if not found - void callFunctions(); // calls all functions - void deleteAllFunctions(); + void addFunction( callback_t func, void *data = NULL ); // register a callback, which will be called as func(data) + bool containsFunction( callback_t func, void *data = NULL ); // true if list already contains the function/data pair + bool deleteFunction( callback_t func, void *data = NULL ); // removes the first instance of this function/data pair from the list, false if not found + void callFunctions(); // calls all functions + void deleteAllFunctions(); - static void test(); + static void test(); protected: - inline callback_list_t::iterator find(callback_t func, void *data); + inline callback_list_t::iterator find(callback_t func, void *data); - callback_list_t mCallbackList; + callback_list_t mCallbackList; }; typedef boost::function<void ()> nullary_func_t; diff --git a/indra/llcommon/llcallstack.cpp b/indra/llcommon/llcallstack.cpp index 83d5ae2a63..c0be4f598e 100644 --- a/indra/llcommon/llcallstack.cpp +++ b/indra/llcommon/llcallstack.cpp @@ -1,25 +1,25 @@ -/** +/** * @file llcallstack.cpp * @brief run-time extraction of the current callstack * * $LicenseInfo:firstyear=2016&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2016, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -91,7 +91,7 @@ LLCallStack::LLCallStack(S32 skip_count, bool verbose): bool LLCallStack::contains(const std::string& str) { - for (const std::string& src_str : m_strings) + for (const std::string& src_str : m_strings) { if (src_str.find(str) != std::string::npos) { @@ -104,7 +104,7 @@ bool LLCallStack::contains(const std::string& str) std::ostream& operator<<(std::ostream& s, const LLCallStack& call_stack) { #ifndef LL_RELEASE_FOR_DOWNLOAD - for (const std::string& str : call_stack.m_strings) + for (const std::string& str : call_stack.m_strings) { s << str; } @@ -121,27 +121,27 @@ LLContextStrings::LLContextStrings() // static LLContextStrings* LLContextStrings::getThreadLocalInstance() { - LLContextStrings *cons = LLThreadLocalSingletonPointer<LLContextStrings>::getInstance(); + LLContextStrings *cons = LLThreadLocalSingletonPointer<LLContextStrings>::getInstance(); if (!cons) { LLThreadLocalSingletonPointer<LLContextStrings>::setInstance(new LLContextStrings); } - return LLThreadLocalSingletonPointer<LLContextStrings>::getInstance(); + return LLThreadLocalSingletonPointer<LLContextStrings>::getInstance(); } // static void LLContextStrings::addContextString(const std::string& str) { - LLContextStrings *cons = getThreadLocalInstance(); + LLContextStrings *cons = getThreadLocalInstance(); //LL_INFOS() << "CTX " << (S32)cons << " ADD " << str << " CNT " << cons->m_contextStrings[str] << LL_ENDL; - cons->m_contextStrings[str]++; + cons->m_contextStrings[str]++; } // static void LLContextStrings::removeContextString(const std::string& str) { - LLContextStrings *cons = getThreadLocalInstance(); - cons->m_contextStrings[str]--; + LLContextStrings *cons = getThreadLocalInstance(); + cons->m_contextStrings[str]--; //LL_INFOS() << "CTX " << (S32)cons << " REMOVE " << str << " CNT " << cons->m_contextStrings[str] << LL_ENDL; if (cons->m_contextStrings[str] == 0) { @@ -175,7 +175,7 @@ void LLContextStrings::output(std::ostream& os) } } -// static +// static std::ostream& operator<<(std::ostream& s, const LLContextStatus& context_status) { LLThreadLocalSingletonPointer<LLContextStrings>::getInstance()->output(s); diff --git a/indra/llcommon/llcallstack.h b/indra/llcommon/llcallstack.h index d5a2b7b157..ad10a9dbf7 100644 --- a/indra/llcommon/llcallstack.h +++ b/indra/llcommon/llcallstack.h @@ -1,25 +1,25 @@ -/** +/** * @file llcallstack.h * @brief run-time extraction of the current callstack * * $LicenseInfo:firstyear=2016&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2016, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llcommon/llcleanup.cpp b/indra/llcommon/llcleanup.cpp index 1f34c2036a..38b2ccbeff 100644 --- a/indra/llcommon/llcleanup.cpp +++ b/indra/llcommon/llcleanup.cpp @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2016-08-30 * @brief Implementation for llcleanup. - * + * * $LicenseInfo:firstyear=2016&license=viewerlgpl$ * Copyright (c) 2016, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/llcleanup.h b/indra/llcommon/llcleanup.h index 0f567ed5f6..b120a5c4fe 100644 --- a/indra/llcommon/llcleanup.h +++ b/indra/llcommon/llcleanup.h @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2015-05-20 * @brief Mechanism for cleaning up subsystem resources - * + * * $LicenseInfo:firstyear=2015&license=viewerlgpl$ * Copyright (c) 2015, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/llclickaction.h b/indra/llcommon/llclickaction.h index 2f83113af9..daa4e88f80 100644 --- a/indra/llcommon/llclickaction.h +++ b/indra/llcommon/llclickaction.h @@ -1,4 +1,4 @@ -/** +/** * @file llclickaction.h * @author James Cook * @brief Constants for single-click actions on objects @@ -6,21 +6,21 @@ * $LicenseInfo:firstyear=2006&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llcommon/llcommon.cpp b/indra/llcommon/llcommon.cpp index 335c586c1d..7a10662d3d 100644 --- a/indra/llcommon/llcommon.cpp +++ b/indra/llcommon/llcommon.cpp @@ -1,153 +1,153 @@ -/** - * @file llcommon.cpp - * - * $LicenseInfo:firstyear=2006&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" - -#include "llcommon.h" - -#include "llmemory.h" -#include "llthread.h" -#include "lltrace.h" -#include "lltracethreadrecorder.h" -#include "llcleanup.h" - -thread_local bool gProfilerEnabled = false; - -#if (TRACY_ENABLE) -// Override new/delete for tracy memory profiling - -void* ll_tracy_new(size_t size) -{ - void* ptr; - if (gProfilerEnabled) - { - //LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY; - ptr = (malloc)(size); - } - else - { - ptr = (malloc)(size); - } - if (!ptr) - { - throw std::bad_alloc(); - } - TracyAlloc(ptr, size); - return ptr; -} - -void* operator new(size_t size) -{ - return ll_tracy_new(size); -} - -void* operator new[](std::size_t count) -{ - return ll_tracy_new(count); -} - -void ll_tracy_delete(void* ptr) -{ - TracyFree(ptr); - if (gProfilerEnabled) - { - //LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY; - (free)(ptr); - } - else - { - (free)(ptr); - } -} - -void operator delete(void *ptr) noexcept -{ - ll_tracy_delete(ptr); -} - -void operator delete[](void* ptr) noexcept -{ - ll_tracy_delete(ptr); -} - -// C-style malloc/free can't be so easily overridden, so we define tracy versions and use -// a pre-processor #define in linden_common.h to redirect to them. The parens around the native -// functions below prevents recursive substitution by the preprocessor. -// -// Unaligned mallocs are rare in LL code but hooking them causes problems in 3p lib code (looking at -// you, Havok), so we'll only capture the aligned version. - -void *tracy_aligned_malloc(size_t size, size_t alignment) -{ - auto ptr = ll_aligned_malloc_fallback(size, alignment); - if (ptr) TracyAlloc(ptr, size); - return ptr; -} - -void tracy_aligned_free(void *memblock) -{ - TracyFree(memblock); - ll_aligned_free_fallback(memblock); -} - -#endif - -//static -bool LLCommon::sAprInitialized = false; - -static LLTrace::ThreadRecorder* sMasterThreadRecorder = NULL; - -//static -void LLCommon::initClass() -{ - if (!sAprInitialized) - { - ll_init_apr(); - sAprInitialized = true; - } - LLTimer::initClass(); - LLThreadSafeRefCount::initThreadSafeRefCount(); - assert_main_thread(); // Make sure we record the main thread - if (!sMasterThreadRecorder) - { - sMasterThreadRecorder = new LLTrace::ThreadRecorder(); - LLTrace::set_master_thread_recorder(sMasterThreadRecorder); - } -} - -//static -void LLCommon::cleanupClass() -{ - delete sMasterThreadRecorder; - sMasterThreadRecorder = NULL; - LLTrace::set_master_thread_recorder(NULL); - LLThreadSafeRefCount::cleanupThreadSafeRefCount(); - SUBSYSTEM_CLEANUP_DBG(LLTimer); - if (sAprInitialized) - { - ll_cleanup_apr(); - sAprInitialized = false; - } -} +/**
+ * @file llcommon.cpp
+ *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llcommon.h"
+
+#include "llmemory.h"
+#include "llthread.h"
+#include "lltrace.h"
+#include "lltracethreadrecorder.h"
+#include "llcleanup.h"
+
+thread_local bool gProfilerEnabled = false;
+
+#if (TRACY_ENABLE)
+// Override new/delete for tracy memory profiling
+
+void* ll_tracy_new(size_t size)
+{
+ void* ptr;
+ if (gProfilerEnabled)
+ {
+ //LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY;
+ ptr = (malloc)(size);
+ }
+ else
+ {
+ ptr = (malloc)(size);
+ }
+ if (!ptr)
+ {
+ throw std::bad_alloc();
+ }
+ TracyAlloc(ptr, size);
+ return ptr;
+}
+
+void* operator new(size_t size)
+{
+ return ll_tracy_new(size);
+}
+
+void* operator new[](std::size_t count)
+{
+ return ll_tracy_new(count);
+}
+
+void ll_tracy_delete(void* ptr)
+{
+ TracyFree(ptr);
+ if (gProfilerEnabled)
+ {
+ //LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY;
+ (free)(ptr);
+ }
+ else
+ {
+ (free)(ptr);
+ }
+}
+
+void operator delete(void *ptr) noexcept
+{
+ ll_tracy_delete(ptr);
+}
+
+void operator delete[](void* ptr) noexcept
+{
+ ll_tracy_delete(ptr);
+}
+
+// C-style malloc/free can't be so easily overridden, so we define tracy versions and use
+// a pre-processor #define in linden_common.h to redirect to them. The parens around the native
+// functions below prevents recursive substitution by the preprocessor.
+//
+// Unaligned mallocs are rare in LL code but hooking them causes problems in 3p lib code (looking at
+// you, Havok), so we'll only capture the aligned version.
+
+void *tracy_aligned_malloc(size_t size, size_t alignment)
+{
+ auto ptr = ll_aligned_malloc_fallback(size, alignment);
+ if (ptr) TracyAlloc(ptr, size);
+ return ptr;
+}
+
+void tracy_aligned_free(void *memblock)
+{
+ TracyFree(memblock);
+ ll_aligned_free_fallback(memblock);
+}
+
+#endif
+
+//static
+bool LLCommon::sAprInitialized = false;
+
+static LLTrace::ThreadRecorder* sMasterThreadRecorder = NULL;
+
+//static
+void LLCommon::initClass()
+{
+ if (!sAprInitialized)
+ {
+ ll_init_apr();
+ sAprInitialized = true;
+ }
+ LLTimer::initClass();
+ LLThreadSafeRefCount::initThreadSafeRefCount();
+ assert_main_thread(); // Make sure we record the main thread
+ if (!sMasterThreadRecorder)
+ {
+ sMasterThreadRecorder = new LLTrace::ThreadRecorder();
+ LLTrace::set_master_thread_recorder(sMasterThreadRecorder);
+ }
+}
+
+//static
+void LLCommon::cleanupClass()
+{
+ delete sMasterThreadRecorder;
+ sMasterThreadRecorder = NULL;
+ LLTrace::set_master_thread_recorder(NULL);
+ LLThreadSafeRefCount::cleanupThreadSafeRefCount();
+ SUBSYSTEM_CLEANUP_DBG(LLTimer);
+ if (sAprInitialized)
+ {
+ ll_cleanup_apr();
+ sAprInitialized = false;
+ }
+}
diff --git a/indra/llcommon/llcommon.h b/indra/llcommon/llcommon.h index 98c5ed3bc2..3e81bb1f79 100644 --- a/indra/llcommon/llcommon.h +++ b/indra/llcommon/llcommon.h @@ -1,43 +1,43 @@ -/** - * @file llcommon.h - * - * $LicenseInfo:firstyear=2006&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_COMMON_H -#define LL_COMMON_H - -// *TODO: remove these? -#include "lltimer.h" -#include "llfile.h" - -class LL_COMMON_API LLCommon -{ -public: - static void initClass(); - static void cleanupClass(); -private: - static bool sAprInitialized; -}; - -#endif - +/**
+ * @file llcommon.h
+ *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_COMMON_H
+#define LL_COMMON_H
+
+// *TODO: remove these?
+#include "lltimer.h"
+#include "llfile.h"
+
+class LL_COMMON_API LLCommon
+{
+public:
+ static void initClass();
+ static void cleanupClass();
+private:
+ static bool sAprInitialized;
+};
+
+#endif
+
diff --git a/indra/llcommon/llcommonutils.cpp b/indra/llcommon/llcommonutils.cpp index d82554c202..a38879c89f 100644 --- a/indra/llcommon/llcommonutils.cpp +++ b/indra/llcommon/llcommonutils.cpp @@ -5,21 +5,21 @@ * $LicenseInfo:firstyear=2010&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -28,29 +28,29 @@ #include "llcommonutils.h" void LLCommonUtils::computeDifference( - const uuid_vec_t& vnew, - const uuid_vec_t& vcur, - uuid_vec_t& vadded, - uuid_vec_t& vremoved) + const uuid_vec_t& vnew, + const uuid_vec_t& vcur, + uuid_vec_t& vadded, + uuid_vec_t& vremoved) { - uuid_vec_t vnew_copy(vnew); - uuid_vec_t vcur_copy(vcur); + uuid_vec_t vnew_copy(vnew); + uuid_vec_t vcur_copy(vcur); - std::sort(vnew_copy.begin(), vnew_copy.end()); - std::sort(vcur_copy.begin(), vcur_copy.end()); + std::sort(vnew_copy.begin(), vnew_copy.end()); + std::sort(vcur_copy.begin(), vcur_copy.end()); - size_t maxsize = llmax(vnew_copy.size(), vcur_copy.size()); - vadded.resize(maxsize); - vremoved.resize(maxsize); + size_t maxsize = llmax(vnew_copy.size(), vcur_copy.size()); + vadded.resize(maxsize); + vremoved.resize(maxsize); - uuid_vec_t::iterator it; - // what was removed - it = set_difference(vcur_copy.begin(), vcur_copy.end(), vnew_copy.begin(), vnew_copy.end(), vremoved.begin()); - vremoved.erase(it, vremoved.end()); + uuid_vec_t::iterator it; + // what was removed + it = set_difference(vcur_copy.begin(), vcur_copy.end(), vnew_copy.begin(), vnew_copy.end(), vremoved.begin()); + vremoved.erase(it, vremoved.end()); - // what was added - it = set_difference(vnew_copy.begin(), vnew_copy.end(), vcur_copy.begin(), vcur_copy.end(), vadded.begin()); - vadded.erase(it, vadded.end()); + // what was added + it = set_difference(vnew_copy.begin(), vnew_copy.end(), vcur_copy.begin(), vcur_copy.end(), vadded.begin()); + vadded.erase(it, vadded.end()); } // EOF diff --git a/indra/llcommon/llcommonutils.h b/indra/llcommon/llcommonutils.h index 20ada27830..5f6fd41aa3 100644 --- a/indra/llcommon/llcommonutils.h +++ b/indra/llcommon/llcommonutils.h @@ -5,21 +5,21 @@ * $LicenseInfo:firstyear=2010&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -31,21 +31,21 @@ namespace LLCommonUtils { - /** - * Computes difference between 'vnew' and 'vcur' vectors. - * Items present in 'vnew' and missing in 'vcur' are treated as added and are copied into 'vadded' - * Items missing in 'vnew' and present in 'vcur' are treated as removed and are copied into 'vremoved' - * - * @param vnew[in] - incoming IDs - * @param vcur[in] - current IDs - * @param vadded[out] - difference between incoming and current IDS - added IDs - * @param vremoved[out] - difference between incoming and current IDS - removed IDs - */ - LL_COMMON_API void computeDifference( - const uuid_vec_t& vnew, - const uuid_vec_t& vcur, - uuid_vec_t& vadded, - uuid_vec_t& vremoved); + /** + * Computes difference between 'vnew' and 'vcur' vectors. + * Items present in 'vnew' and missing in 'vcur' are treated as added and are copied into 'vadded' + * Items missing in 'vnew' and present in 'vcur' are treated as removed and are copied into 'vremoved' + * + * @param vnew[in] - incoming IDs + * @param vcur[in] - current IDs + * @param vadded[out] - difference between incoming and current IDS - added IDs + * @param vremoved[out] - difference between incoming and current IDS - removed IDs + */ + LL_COMMON_API void computeDifference( + const uuid_vec_t& vnew, + const uuid_vec_t& vcur, + uuid_vec_t& vadded, + uuid_vec_t& vremoved); }; #endif //LL_LLCOMMONUTILS_H diff --git a/indra/llcommon/llcond.h b/indra/llcommon/llcond.h index da6e6affe1..2df1719941 100644 --- a/indra/llcommon/llcond.h +++ b/indra/llcommon/llcond.h @@ -5,7 +5,7 @@ * @brief LLCond is a wrapper around condition_variable to encapsulate the * obligatory condition_variable usage pattern. We also provide * simplified versions LLScalarCond, LLBoolCond and LLOneShotCond. - * + * * $LicenseInfo:firstyear=2019&license=viewerlgpl$ * Copyright (c) 2019, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp index c13900f74a..aa8eca7d90 100644 --- a/indra/llcommon/llcoros.cpp +++ b/indra/llcommon/llcoros.cpp @@ -3,25 +3,25 @@ * @author Nat Goodspeed * @date 2009-06-03 * @brief Implementation for llcoros. - * + * * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -123,7 +123,7 @@ LLCoros::LLCoros(): // Previously we used // boost::context::guarded_stack_allocator::default_stacksize(); // empirically this is insufficient. - mStackSize(900*1024), + mStackSize(1024*1024), // mCurrent does NOT own the current CoroData instance -- it simply // points to it. So initialize it with a no-op deleter. mCurrent{ [](CoroData*){} } diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h index fd878f20ad..71c1c1c443 100644 --- a/indra/llcommon/llcoros.h +++ b/indra/llcommon/llcoros.h @@ -3,25 +3,25 @@ * @author Nat Goodspeed * @date 2009-06-02 * @brief Manage running boost::coroutine instances - * + * * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -158,7 +158,7 @@ public: * LLCoros::launch()). */ static std::string getName(); - + /** * rethrow() is called by the thread's main fiber to propagate an * exception from any coroutine into the main fiber, where it can engage diff --git a/indra/llcommon/llcrc.cpp b/indra/llcommon/llcrc.cpp index 144efa24a8..c376269981 100644 --- a/indra/llcommon/llcrc.cpp +++ b/indra/llcommon/llcrc.cpp @@ -1,223 +1,223 @@ -/** - * @file llcrc.cpp - * @brief implementation of the crc class. - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" - -#include "llcrc.h" -#include "llerror.h" - -/* Copyright (C) 1986 Gary S. Brown. You may use this program, or - code or tables extracted from it, as desired without restriction.*/ - -/* First, the polynomial itself and its table of feedback terms. The */ -/* polynomial is */ -/* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 */ -/* Note that we take it "backwards" and put the highest-order term in */ -/* the lowest-order bit. The X^32 term is "implied"; the LSB is the */ -/* X^31 term, etc. The X^0 term (usually shown as "+1") results in */ -/* the MSB being 1. */ - -/* Note that the usual hardware shift register implementation, which */ -/* is what we're using (we're merely optimizing it by doing eight-bit */ -/* chunks at a time) shifts bits into the lowest-order term. In our */ -/* implementation, that means shifting towards the right. Why do we */ -/* do it this way? Because the calculated CRC must be transmitted in */ -/* order from highest-order term to lowest-order term. UARTs transmit */ -/* characters in order from LSB to MSB. By storing the CRC this way, */ -/* we hand it to the UART in the order low-byte to high-byte; the UART */ -/* sends each low-bit to hight-bit; and the result is transmission bit */ -/* by bit from highest- to lowest-order term without requiring any bit */ -/* shuffling on our part. Reception works similarly. */ - -/* The feedback terms table consists of 256, 32-bit entries. Notes: */ -/* */ -/* 1. The table can be generated at runtime if desired; code to do so */ -/* is shown later. It might not be obvious, but the feedback */ -/* terms simply represent the results of eight shift/xor opera- */ -/* tions for all combinations of data and CRC register values. */ -/* */ -/* 2. The CRC accumulation logic is the same for all CRC polynomials, */ -/* be they sixteen or thirty-two bits wide. You simply choose the */ -/* appropriate table. Alternatively, because the table can be */ -/* generated at runtime, you can start by generating the table for */ -/* the polynomial in question and use exactly the same "updcrc", */ -/* if your application needn't simultaneously handle two CRC */ -/* polynomials. (Note, however, that XMODEM is strange.) */ -/* */ -/* 3. For 16-bit CRCs, the table entries need be only 16 bits wide; */ -/* of course, 32-bit entries work OK if the high 16 bits are zero. */ -/* */ -/* 4. The values must be right-shifted by eight bits by the "updcrc" */ -/* logic; the shift must be unsigned (bring in zeroes). On some */ -/* hardware you could probably optimize the shift in assembler by */ -/* using byte-swap instructions. */ - -///---------------------------------------------------------------------------- -/// Local function declarations, constants, enums, and typedefs -///---------------------------------------------------------------------------- - -#define UPDC32(octet,crc) (crc_32_tab[((crc) \ - ^ ((U8)octet)) & 0xff] ^ ((crc) >> 8)) - - -static U32 crc_32_tab[] = { /* CRC polynomial 0xedb88320 */ -0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, -0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, -0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, -0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, -0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, -0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, -0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, -0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, -0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, -0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, -0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, -0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, -0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, -0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, -0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, -0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, -0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, -0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, -0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, -0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, -0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, -0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, -0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, -0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, -0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, -0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, -0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, -0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, -0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, -0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, -0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, -0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, -0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, -0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, -0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, -0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, -0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, -0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, -0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, -0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, -0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, -0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, -0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d -}; - - -///---------------------------------------------------------------------------- -/// Class llcrc -///---------------------------------------------------------------------------- - -// Default constructor -LLCRC::LLCRC() : mCurrent(0xffffffff) -{ -} - - -U32 LLCRC::getCRC() const -{ - return ~mCurrent; -} - -void LLCRC::update(U8 next_byte) -{ - mCurrent = UPDC32(next_byte, mCurrent); -} - -void LLCRC::update(const U8* buffer, size_t buffer_size) -{ - for (size_t i = 0; i < buffer_size; i++) - { - mCurrent = UPDC32(buffer[i], mCurrent); - } -} - -void LLCRC::update(const std::string& filename) -{ - if (filename.empty()) - { - LL_ERRS() << "No filename specified" << LL_ENDL; - return; - } - - FILE* fp = LLFile::fopen(filename, "rb"); /* Flawfinder: ignore */ - - if (fp) - { - fseek(fp, 0, SEEK_END); - long size = ftell(fp); - - fseek(fp, 0, SEEK_SET); - - if (size > 0) - { - U8* data = new U8[size]; - size_t nread; - - nread = fread(data, 1, size, fp); - fclose(fp); - - if (nread < (size_t) size) - { - LL_WARNS() << "Short read on " << filename << LL_ENDL; - } - - update(data, nread); - delete[] data; - } - else - { - fclose(fp); - } - } -} - - -#ifdef _DEBUG -bool LLCRC::testHarness() -{ - const S32 TEST_BUFFER_SIZE = 16; - const char TEST_BUFFER[TEST_BUFFER_SIZE] = "hello &#$)$&Nd0"; /* Flawfinder: ignore */ - LLCRC c1, c2; - c1.update((U8*)TEST_BUFFER, TEST_BUFFER_SIZE - 1); - char* rh = (char*)TEST_BUFFER; - while(*rh != '\0') - { - c2.update(*rh); - ++rh; - } - return(c1.getCRC() == c2.getCRC()); -} -#endif - - - -///---------------------------------------------------------------------------- -/// Local function definitions -///---------------------------------------------------------------------------- +/**
+ * @file llcrc.cpp
+ * @brief implementation of the crc class.
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llcrc.h"
+#include "llerror.h"
+
+/* Copyright (C) 1986 Gary S. Brown. You may use this program, or
+ code or tables extracted from it, as desired without restriction.*/
+
+/* First, the polynomial itself and its table of feedback terms. The */
+/* polynomial is */
+/* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 */
+/* Note that we take it "backwards" and put the highest-order term in */
+/* the lowest-order bit. The X^32 term is "implied"; the LSB is the */
+/* X^31 term, etc. The X^0 term (usually shown as "+1") results in */
+/* the MSB being 1. */
+
+/* Note that the usual hardware shift register implementation, which */
+/* is what we're using (we're merely optimizing it by doing eight-bit */
+/* chunks at a time) shifts bits into the lowest-order term. In our */
+/* implementation, that means shifting towards the right. Why do we */
+/* do it this way? Because the calculated CRC must be transmitted in */
+/* order from highest-order term to lowest-order term. UARTs transmit */
+/* characters in order from LSB to MSB. By storing the CRC this way, */
+/* we hand it to the UART in the order low-byte to high-byte; the UART */
+/* sends each low-bit to hight-bit; and the result is transmission bit */
+/* by bit from highest- to lowest-order term without requiring any bit */
+/* shuffling on our part. Reception works similarly. */
+
+/* The feedback terms table consists of 256, 32-bit entries. Notes: */
+/* */
+/* 1. The table can be generated at runtime if desired; code to do so */
+/* is shown later. It might not be obvious, but the feedback */
+/* terms simply represent the results of eight shift/xor opera- */
+/* tions for all combinations of data and CRC register values. */
+/* */
+/* 2. The CRC accumulation logic is the same for all CRC polynomials, */
+/* be they sixteen or thirty-two bits wide. You simply choose the */
+/* appropriate table. Alternatively, because the table can be */
+/* generated at runtime, you can start by generating the table for */
+/* the polynomial in question and use exactly the same "updcrc", */
+/* if your application needn't simultaneously handle two CRC */
+/* polynomials. (Note, however, that XMODEM is strange.) */
+/* */
+/* 3. For 16-bit CRCs, the table entries need be only 16 bits wide; */
+/* of course, 32-bit entries work OK if the high 16 bits are zero. */
+/* */
+/* 4. The values must be right-shifted by eight bits by the "updcrc" */
+/* logic; the shift must be unsigned (bring in zeroes). On some */
+/* hardware you could probably optimize the shift in assembler by */
+/* using byte-swap instructions. */
+
+///----------------------------------------------------------------------------
+/// Local function declarations, constants, enums, and typedefs
+///----------------------------------------------------------------------------
+
+#define UPDC32(octet,crc) (crc_32_tab[((crc) \
+ ^ ((U8)octet)) & 0xff] ^ ((crc) >> 8))
+
+
+static U32 crc_32_tab[] = { /* CRC polynomial 0xedb88320 */
+0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
+0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
+0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
+0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
+0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
+0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
+0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
+0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
+0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
+0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
+0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
+0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
+0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
+0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
+0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
+0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
+0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
+0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+};
+
+
+///----------------------------------------------------------------------------
+/// Class llcrc
+///----------------------------------------------------------------------------
+
+// Default constructor
+LLCRC::LLCRC() : mCurrent(0xffffffff)
+{
+}
+
+
+U32 LLCRC::getCRC() const
+{
+ return ~mCurrent;
+}
+
+void LLCRC::update(U8 next_byte)
+{
+ mCurrent = UPDC32(next_byte, mCurrent);
+}
+
+void LLCRC::update(const U8* buffer, size_t buffer_size)
+{
+ for (size_t i = 0; i < buffer_size; i++)
+ {
+ mCurrent = UPDC32(buffer[i], mCurrent);
+ }
+}
+
+void LLCRC::update(const std::string& filename)
+{
+ if (filename.empty())
+ {
+ LL_ERRS() << "No filename specified" << LL_ENDL;
+ return;
+ }
+
+ FILE* fp = LLFile::fopen(filename, "rb"); /* Flawfinder: ignore */
+
+ if (fp)
+ {
+ fseek(fp, 0, SEEK_END);
+ long size = ftell(fp);
+
+ fseek(fp, 0, SEEK_SET);
+
+ if (size > 0)
+ {
+ U8* data = new U8[size];
+ size_t nread;
+
+ nread = fread(data, 1, size, fp);
+ fclose(fp);
+
+ if (nread < (size_t) size)
+ {
+ LL_WARNS() << "Short read on " << filename << LL_ENDL;
+ }
+
+ update(data, nread);
+ delete[] data;
+ }
+ else
+ {
+ fclose(fp);
+ }
+ }
+}
+
+
+#ifdef _DEBUG
+bool LLCRC::testHarness()
+{
+ const S32 TEST_BUFFER_SIZE = 16;
+ const char TEST_BUFFER[TEST_BUFFER_SIZE] = "hello &#$)$&Nd0"; /* Flawfinder: ignore */
+ LLCRC c1, c2;
+ c1.update((U8*)TEST_BUFFER, TEST_BUFFER_SIZE - 1);
+ char* rh = (char*)TEST_BUFFER;
+ while(*rh != '\0')
+ {
+ c2.update(*rh);
+ ++rh;
+ }
+ return(c1.getCRC() == c2.getCRC());
+}
+#endif
+
+
+
+///----------------------------------------------------------------------------
+/// Local function definitions
+///----------------------------------------------------------------------------
diff --git a/indra/llcommon/llcrc.h b/indra/llcommon/llcrc.h index cc17a9936a..d6fd008740 100644 --- a/indra/llcommon/llcrc.h +++ b/indra/llcommon/llcrc.h @@ -1,68 +1,68 @@ -/** - * @file llcrc.h - * @brief LLCRC class header file. - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLCRC_H -#define LL_LLCRC_H - -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Class llcrc -// -// Simple 32 bit crc. To use, instantiate an LLCRC instance and feed -// it the bytes you want to check. It will update the internal crc as -// you go, and you can qery it at the end. As a horribly inefficient -// example (don't try this at work kids): -// -// LLCRC crc; -// FILE* fp = LLFile::fopen(filename,"rb"); -// while(!feof(fp)) { -// crc.update(fgetc(fp)); -// } -// fclose(fp); -// LL_INFOS() << "File crc: " << crc.getCRC() << LL_ENDL; -//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -class LL_COMMON_API LLCRC -{ -protected: - U32 mCurrent; - -public: - LLCRC(); - - U32 getCRC() const; - void update(U8 next_byte); - void update(const U8* buffer, size_t buffer_size); - void update(const std::string& filename); - -#ifdef _DEBUG - // This function runs tests to make sure the crc is - // working. Returns true if it is. - static bool testHarness(); -#endif -}; - - -#endif // LL_LLCRC_H +/**
+ * @file llcrc.h
+ * @brief LLCRC class header file.
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLCRC_H
+#define LL_LLCRC_H
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Class llcrc
+//
+// Simple 32 bit crc. To use, instantiate an LLCRC instance and feed
+// it the bytes you want to check. It will update the internal crc as
+// you go, and you can qery it at the end. As a horribly inefficient
+// example (don't try this at work kids):
+//
+// LLCRC crc;
+// FILE* fp = LLFile::fopen(filename,"rb");
+// while(!feof(fp)) {
+// crc.update(fgetc(fp));
+// }
+// fclose(fp);
+// LL_INFOS() << "File crc: " << crc.getCRC() << LL_ENDL;
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+class LL_COMMON_API LLCRC
+{
+protected:
+ U32 mCurrent;
+
+public:
+ LLCRC();
+
+ U32 getCRC() const;
+ void update(U8 next_byte);
+ void update(const U8* buffer, size_t buffer_size);
+ void update(const std::string& filename);
+
+#ifdef _DEBUG
+ // This function runs tests to make sure the crc is
+ // working. Returns true if it is.
+ static bool testHarness();
+#endif
+};
+
+
+#endif // LL_LLCRC_H
diff --git a/indra/llcommon/llcriticaldamp.cpp b/indra/llcommon/llcriticaldamp.cpp index 54be855f67..bb5f2b647a 100644 --- a/indra/llcommon/llcriticaldamp.cpp +++ b/indra/llcommon/llcriticaldamp.cpp @@ -1,25 +1,25 @@ -/** +/** * @file llcriticaldamp.cpp * @brief Implementation of the critical damping functionality. * * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -39,20 +39,20 @@ F32 LLSmoothInterpolation::sTimeDelta; // helper functors struct LLSmoothInterpolation::CompareTimeConstants { - bool operator()(const F32& a, const LLSmoothInterpolation::Interpolant& b) const - { - return a < b.mTimeScale; - } + bool operator()(const F32& a, const LLSmoothInterpolation::Interpolant& b) const + { + return a < b.mTimeScale; + } - bool operator()(const LLSmoothInterpolation::Interpolant& a, const F32& b) const - { - return a.mTimeScale < b; // bottom of a is higher than bottom of b - } + bool operator()(const LLSmoothInterpolation::Interpolant& a, const F32& b) const + { + return a.mTimeScale < b; // bottom of a is higher than bottom of b + } - bool operator()(const LLSmoothInterpolation::Interpolant& a, const LLSmoothInterpolation::Interpolant& b) const - { - return a.mTimeScale < b.mTimeScale; // bottom of a is higher than bottom of b - } + bool operator()(const LLSmoothInterpolation::Interpolant& a, const LLSmoothInterpolation::Interpolant& b) const + { + return a.mTimeScale < b.mTimeScale; // bottom of a is higher than bottom of b + } }; //----------------------------------------------------------------------------- @@ -60,7 +60,7 @@ struct LLSmoothInterpolation::CompareTimeConstants //----------------------------------------------------------------------------- LLSmoothInterpolation::LLSmoothInterpolation() { - sTimeDelta = 0.f; + sTimeDelta = 0.f; } // static @@ -69,46 +69,46 @@ LLSmoothInterpolation::LLSmoothInterpolation() //----------------------------------------------------------------------------- void LLSmoothInterpolation::updateInterpolants() { - sTimeDelta = sInternalTimer.getElapsedTimeAndResetF32(); + sTimeDelta = sInternalTimer.getElapsedTimeAndResetF32(); - for (S32 i = 0; i < sInterpolants.size(); i++) - { - Interpolant& interp = sInterpolants[i]; - interp.mInterpolant = calcInterpolant(interp.mTimeScale); - } -} + for (S32 i = 0; i < sInterpolants.size(); i++) + { + Interpolant& interp = sInterpolants[i]; + interp.mInterpolant = calcInterpolant(interp.mTimeScale); + } +} //----------------------------------------------------------------------------- // getInterpolant() //----------------------------------------------------------------------------- F32 LLSmoothInterpolation::getInterpolant(F32SecondsImplicit time_constant, bool use_cache) { - if (time_constant == 0.f) - { - return 1.f; - } + if (time_constant == 0.f) + { + return 1.f; + } - if (use_cache) - { - interpolant_vec_t::iterator find_it = std::lower_bound(sInterpolants.begin(), sInterpolants.end(), time_constant.value(), CompareTimeConstants()); - if (find_it != sInterpolants.end() && find_it->mTimeScale == time_constant) - { - return find_it->mInterpolant; - } - else - { - Interpolant interp; - interp.mTimeScale = time_constant.value(); - interp.mInterpolant = calcInterpolant(time_constant.value()); - sInterpolants.insert(find_it, interp); - return interp.mInterpolant; - } - } - else - { - return calcInterpolant(time_constant.value()); + if (use_cache) + { + interpolant_vec_t::iterator find_it = std::lower_bound(sInterpolants.begin(), sInterpolants.end(), time_constant.value(), CompareTimeConstants()); + if (find_it != sInterpolants.end() && find_it->mTimeScale == time_constant) + { + return find_it->mInterpolant; + } + else + { + Interpolant interp; + interp.mTimeScale = time_constant.value(); + interp.mInterpolant = calcInterpolant(time_constant.value()); + sInterpolants.insert(find_it, interp); + return interp.mInterpolant; + } + } + else + { + return calcInterpolant(time_constant.value()); - } + } } //----------------------------------------------------------------------------- @@ -116,6 +116,6 @@ F32 LLSmoothInterpolation::getInterpolant(F32SecondsImplicit time_constant, bool //----------------------------------------------------------------------------- F32 LLSmoothInterpolation::calcInterpolant(F32 time_constant) { - return llclamp(1.f - powf(2.f, -sTimeDelta / time_constant), 0.f, 1.f); + return llclamp(1.f - powf(2.f, -sTimeDelta / time_constant), 0.f, 1.f); } diff --git a/indra/llcommon/llcriticaldamp.h b/indra/llcommon/llcriticaldamp.h index 1fb6a1af29..2c37d276c4 100644 --- a/indra/llcommon/llcriticaldamp.h +++ b/indra/llcommon/llcriticaldamp.h @@ -1,26 +1,26 @@ -/** +/** * @file llcriticaldamp.h * @brief A lightweight class that calculates critical damping constants once - * per frame. + * per frame. * * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -33,39 +33,39 @@ #include "llframetimer.h" #include "llunits.h" -class LL_COMMON_API LLSmoothInterpolation +class LL_COMMON_API LLSmoothInterpolation { public: - LLSmoothInterpolation(); + LLSmoothInterpolation(); - // MANIPULATORS - static void updateInterpolants(); + // MANIPULATORS + static void updateInterpolants(); - // ACCESSORS - static F32 getInterpolant(F32SecondsImplicit time_constant, bool use_cache = true); + // ACCESSORS + static F32 getInterpolant(F32SecondsImplicit time_constant, bool use_cache = true); - template<typename T> - static T lerp(T a, T b, F32SecondsImplicit time_constant, bool use_cache = true) - { - F32 interpolant = getInterpolant(time_constant, use_cache); - return ((a * (1.f - interpolant)) - + (b * interpolant)); - } + template<typename T> + static T lerp(T a, T b, F32SecondsImplicit time_constant, bool use_cache = true) + { + F32 interpolant = getInterpolant(time_constant, use_cache); + return ((a * (1.f - interpolant)) + + (b * interpolant)); + } protected: - static F32 calcInterpolant(F32 time_constant); + static F32 calcInterpolant(F32 time_constant); - struct CompareTimeConstants; - static LLFrameTimer sInternalTimer; // frame timer for calculating deltas + struct CompareTimeConstants; + static LLFrameTimer sInternalTimer; // frame timer for calculating deltas - struct Interpolant - { - F32 mTimeScale; - F32 mInterpolant; - }; - typedef std::vector<Interpolant> interpolant_vec_t; - static interpolant_vec_t sInterpolants; - static F32 sTimeDelta; + struct Interpolant + { + F32 mTimeScale; + F32 mInterpolant; + }; + typedef std::vector<Interpolant> interpolant_vec_t; + static interpolant_vec_t sInterpolants; + static F32 sTimeDelta; }; typedef LLSmoothInterpolation LLCriticalDamp; diff --git a/indra/llcommon/lldate.cpp b/indra/llcommon/lldate.cpp index 2ddcf40895..c63c7012d1 100644 --- a/indra/llcommon/lldate.cpp +++ b/indra/llcommon/lldate.cpp @@ -1,4 +1,4 @@ -/** +/** * @file lldate.cpp * @author Phoenix * @date 2006-02-05 @@ -7,21 +7,21 @@ * $LicenseInfo:firstyear=2006&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -44,36 +44,36 @@ static const F64 DATE_EPOCH = 0.0; static const F64 LL_APR_USEC_PER_SEC = 1000000.0; - // should be APR_USEC_PER_SEC, but that relies on INT64_C which - // isn't defined in glib under our build set up for some reason + // should be APR_USEC_PER_SEC, but that relies on INT64_C which + // isn't defined in glib under our build set up for some reason LLDate::LLDate() : mSecondsSinceEpoch(DATE_EPOCH) {} LLDate::LLDate(const LLDate& date) : - mSecondsSinceEpoch(date.mSecondsSinceEpoch) + mSecondsSinceEpoch(date.mSecondsSinceEpoch) {} LLDate::LLDate(F64SecondsImplicit seconds_since_epoch) : - mSecondsSinceEpoch(seconds_since_epoch.value()) + mSecondsSinceEpoch(seconds_since_epoch.value()) {} LLDate::LLDate(const std::string& iso8601_date) { - if(!fromString(iso8601_date)) - { - LL_WARNS() << "date " << iso8601_date << " failed to parse; " - << "ZEROING IT OUT" << LL_ENDL; - mSecondsSinceEpoch = DATE_EPOCH; - } + if(!fromString(iso8601_date)) + { + LL_WARNS() << "date " << iso8601_date << " failed to parse; " + << "ZEROING IT OUT" << LL_ENDL; + mSecondsSinceEpoch = DATE_EPOCH; + } } std::string LLDate::asString() const { - std::ostringstream stream; - toStream(stream); - return stream.str(); + std::ostringstream stream; + toStream(stream); + return stream.str(); } //@ brief Converts time in seconds since EPOCH @@ -83,236 +83,236 @@ std::string LLDate::asString() const // is one of the standards used and the prefered format std::string LLDate::asRFC1123() const { - return toHTTPDateString (std::string ("%A, %d %b %Y %H:%M:%S GMT")); + return toHTTPDateString (std::string ("%A, %d %b %Y %H:%M:%S GMT")); } std::string LLDate::toHTTPDateString (std::string fmt) const { LL_PROFILE_ZONE_SCOPED; - - time_t locSeconds = (time_t) mSecondsSinceEpoch; - struct tm * gmt = gmtime (&locSeconds); - return toHTTPDateString(gmt, fmt); + + time_t locSeconds = (time_t) mSecondsSinceEpoch; + struct tm * gmt = gmtime (&locSeconds); + return toHTTPDateString(gmt, fmt); } std::string LLDate::toHTTPDateString (tm * gmt, std::string fmt) { LL_PROFILE_ZONE_SCOPED; - // avoid calling setlocale() unnecessarily - it's expensive. - static std::string prev_locale = ""; - std::string this_locale = LLStringUtil::getLocale(); - if (this_locale != prev_locale) - { - setlocale(LC_TIME, this_locale.c_str()); - prev_locale = this_locale; - } - - // use strftime() as it appears to be faster than std::time_put - char buffer[128]; - strftime(buffer, 128, fmt.c_str(), gmt); - std::string res(buffer); + // avoid calling setlocale() unnecessarily - it's expensive. + static std::string prev_locale = ""; + std::string this_locale = LLStringUtil::getLocale(); + if (this_locale != prev_locale) + { + setlocale(LC_TIME, this_locale.c_str()); + prev_locale = this_locale; + } + + // use strftime() as it appears to be faster than std::time_put + char buffer[128]; + strftime(buffer, 128, fmt.c_str(), gmt); + std::string res(buffer); #if LL_WINDOWS - // Convert from locale-dependant charset to UTF-8 (EXT-8524). - res = ll_convert_string_to_utf8_string(res); + // Convert from locale-dependant charset to UTF-8 (EXT-8524). + res = ll_convert_string_to_utf8_string(res); #endif - return res; + return res; } void LLDate::toStream(std::ostream& s) const { - apr_time_t time = (apr_time_t)(mSecondsSinceEpoch * LL_APR_USEC_PER_SEC); - - apr_time_exp_t exp_time; - if (apr_time_exp_gmt(&exp_time, time) != APR_SUCCESS) - { - s << "1970-01-01T00:00:00Z"; - return; - } - - s << std::dec << std::setfill('0'); + apr_time_t time = (apr_time_t)(mSecondsSinceEpoch * LL_APR_USEC_PER_SEC); + + apr_time_exp_t exp_time; + if (apr_time_exp_gmt(&exp_time, time) != APR_SUCCESS) + { + s << "1970-01-01T00:00:00Z"; + return; + } + + s << std::dec << std::setfill('0'); #if( LL_WINDOWS || __GNUC__ > 2) - s << std::right; + s << std::right; #else - s.setf(ios::right); + s.setf(ios::right); #endif - s << std::setw(4) << (exp_time.tm_year + 1900) - << '-' << std::setw(2) << (exp_time.tm_mon + 1) - << '-' << std::setw(2) << (exp_time.tm_mday) - << 'T' << std::setw(2) << (exp_time.tm_hour) - << ':' << std::setw(2) << (exp_time.tm_min) - << ':' << std::setw(2) << (exp_time.tm_sec); - if (exp_time.tm_usec > 0) - { - s << '.' << std::setw(2) - << (int)(exp_time.tm_usec / (LL_APR_USEC_PER_SEC / 100)); - } - s << 'Z' - << std::setfill(' '); + s << std::setw(4) << (exp_time.tm_year + 1900) + << '-' << std::setw(2) << (exp_time.tm_mon + 1) + << '-' << std::setw(2) << (exp_time.tm_mday) + << 'T' << std::setw(2) << (exp_time.tm_hour) + << ':' << std::setw(2) << (exp_time.tm_min) + << ':' << std::setw(2) << (exp_time.tm_sec); + if (exp_time.tm_usec > 0) + { + s << '.' << std::setw(2) + << (int)(exp_time.tm_usec / (LL_APR_USEC_PER_SEC / 100)); + } + s << 'Z' + << std::setfill(' '); } bool LLDate::split(S32 *year, S32 *month, S32 *day, S32 *hour, S32 *min, S32 *sec) const { - apr_time_t time = (apr_time_t)(mSecondsSinceEpoch * LL_APR_USEC_PER_SEC); - - apr_time_exp_t exp_time; - if (apr_time_exp_gmt(&exp_time, time) != APR_SUCCESS) - { - return false; - } + apr_time_t time = (apr_time_t)(mSecondsSinceEpoch * LL_APR_USEC_PER_SEC); + + apr_time_exp_t exp_time; + if (apr_time_exp_gmt(&exp_time, time) != APR_SUCCESS) + { + return false; + } - if (year) - *year = exp_time.tm_year + 1900; + if (year) + *year = exp_time.tm_year + 1900; - if (month) - *month = exp_time.tm_mon + 1; + if (month) + *month = exp_time.tm_mon + 1; - if (day) - *day = exp_time.tm_mday; + if (day) + *day = exp_time.tm_mday; - if (hour) - *hour = exp_time.tm_hour; + if (hour) + *hour = exp_time.tm_hour; - if (min) - *min = exp_time.tm_min; + if (min) + *min = exp_time.tm_min; - if (sec) - *sec = exp_time.tm_sec; + if (sec) + *sec = exp_time.tm_sec; - return true; + return true; } bool LLDate::fromString(const std::string& iso8601_date) { - std::istringstream stream(iso8601_date); - return fromStream(stream); + std::istringstream stream(iso8601_date); + return fromStream(stream); } bool LLDate::fromStream(std::istream& s) { - struct apr_time_exp_t exp_time; - apr_int32_t tm_part; - int c; - - s >> tm_part; - exp_time.tm_year = tm_part - 1900; - c = s.get(); // skip the hypen - if (c != '-') { return false; } - s >> tm_part; - exp_time.tm_mon = tm_part - 1; - c = s.get(); // skip the hypen - if (c != '-') { return false; } - s >> tm_part; - exp_time.tm_mday = tm_part; - - c = s.get(); // skip the T - if (c != 'T') { return false; } - - s >> tm_part; - exp_time.tm_hour = tm_part; - c = s.get(); // skip the : - if (c != ':') { return false; } - s >> tm_part; - exp_time.tm_min = tm_part; - c = s.get(); // skip the : - if (c != ':') { return false; } - s >> tm_part; - exp_time.tm_sec = tm_part; - - // zero out the unused fields - exp_time.tm_usec = 0; - exp_time.tm_wday = 0; - exp_time.tm_yday = 0; - exp_time.tm_isdst = 0; - exp_time.tm_gmtoff = 0; - - // generate a time_t from that - apr_time_t time; - if (apr_time_exp_gmt_get(&time, &exp_time) != APR_SUCCESS) - { - return false; - } - - F64 seconds_since_epoch = time / LL_APR_USEC_PER_SEC; - - // check for fractional - c = s.peek(); - if(c == '.') - { - F64 fractional = 0.0; - s >> fractional; - seconds_since_epoch += fractional; - } - - c = s.peek(); // check for offset - if (c == '+' || c == '-') - { - S32 offset_sign = (c == '+') ? 1 : -1; - S32 offset_hours = 0; - S32 offset_minutes = 0; - S32 offset_in_seconds = 0; - - s >> offset_hours; - - c = s.get(); // skip the colon a get the minutes if there are any - if (c == ':') - { - s >> offset_minutes; - } - - offset_in_seconds = (offset_hours * 60 + offset_sign * offset_minutes) * 60; - seconds_since_epoch -= offset_in_seconds; - } - else if (c != 'Z') { return false; } // skip the Z - - mSecondsSinceEpoch = seconds_since_epoch; - return true; + struct apr_time_exp_t exp_time; + apr_int32_t tm_part; + int c; + + s >> tm_part; + exp_time.tm_year = tm_part - 1900; + c = s.get(); // skip the hypen + if (c != '-') { return false; } + s >> tm_part; + exp_time.tm_mon = tm_part - 1; + c = s.get(); // skip the hypen + if (c != '-') { return false; } + s >> tm_part; + exp_time.tm_mday = tm_part; + + c = s.get(); // skip the T + if (c != 'T') { return false; } + + s >> tm_part; + exp_time.tm_hour = tm_part; + c = s.get(); // skip the : + if (c != ':') { return false; } + s >> tm_part; + exp_time.tm_min = tm_part; + c = s.get(); // skip the : + if (c != ':') { return false; } + s >> tm_part; + exp_time.tm_sec = tm_part; + + // zero out the unused fields + exp_time.tm_usec = 0; + exp_time.tm_wday = 0; + exp_time.tm_yday = 0; + exp_time.tm_isdst = 0; + exp_time.tm_gmtoff = 0; + + // generate a time_t from that + apr_time_t time; + if (apr_time_exp_gmt_get(&time, &exp_time) != APR_SUCCESS) + { + return false; + } + + F64 seconds_since_epoch = time / LL_APR_USEC_PER_SEC; + + // check for fractional + c = s.peek(); + if(c == '.') + { + F64 fractional = 0.0; + s >> fractional; + seconds_since_epoch += fractional; + } + + c = s.peek(); // check for offset + if (c == '+' || c == '-') + { + S32 offset_sign = (c == '+') ? 1 : -1; + S32 offset_hours = 0; + S32 offset_minutes = 0; + S32 offset_in_seconds = 0; + + s >> offset_hours; + + c = s.get(); // skip the colon a get the minutes if there are any + if (c == ':') + { + s >> offset_minutes; + } + + offset_in_seconds = (offset_hours * 60 + offset_sign * offset_minutes) * 60; + seconds_since_epoch -= offset_in_seconds; + } + else if (c != 'Z') { return false; } // skip the Z + + mSecondsSinceEpoch = seconds_since_epoch; + return true; } bool LLDate::fromYMDHMS(S32 year, S32 month, S32 day, S32 hour, S32 min, S32 sec) { - struct apr_time_exp_t exp_time; - - exp_time.tm_year = year - 1900; - exp_time.tm_mon = month - 1; - exp_time.tm_mday = day; - exp_time.tm_hour = hour; - exp_time.tm_min = min; - exp_time.tm_sec = sec; - - // zero out the unused fields - exp_time.tm_usec = 0; - exp_time.tm_wday = 0; - exp_time.tm_yday = 0; - exp_time.tm_isdst = 0; - exp_time.tm_gmtoff = 0; - - // generate a time_t from that - apr_time_t time; - if (apr_time_exp_gmt_get(&time, &exp_time) != APR_SUCCESS) - { - return false; - } - - mSecondsSinceEpoch = time / LL_APR_USEC_PER_SEC; - - return true; + struct apr_time_exp_t exp_time; + + exp_time.tm_year = year - 1900; + exp_time.tm_mon = month - 1; + exp_time.tm_mday = day; + exp_time.tm_hour = hour; + exp_time.tm_min = min; + exp_time.tm_sec = sec; + + // zero out the unused fields + exp_time.tm_usec = 0; + exp_time.tm_wday = 0; + exp_time.tm_yday = 0; + exp_time.tm_isdst = 0; + exp_time.tm_gmtoff = 0; + + // generate a time_t from that + apr_time_t time; + if (apr_time_exp_gmt_get(&time, &exp_time) != APR_SUCCESS) + { + return false; + } + + mSecondsSinceEpoch = time / LL_APR_USEC_PER_SEC; + + return true; } F64 LLDate::secondsSinceEpoch() const { - return mSecondsSinceEpoch; + return mSecondsSinceEpoch; } void LLDate::secondsSinceEpoch(F64 seconds) { - mSecondsSinceEpoch = seconds; + mSecondsSinceEpoch = seconds; } /* static */ LLDate LLDate::now() { - // time() returns seconds, we want fractions of a second, which LLTimer provides --RN - return LLDate(LLTimer::getTotalSeconds()); + // time() returns seconds, we want fractions of a second, which LLTimer provides --RN + return LLDate(LLTimer::getTotalSeconds()); } bool LLDate::operator<(const LLDate& rhs) const @@ -322,13 +322,13 @@ bool LLDate::operator<(const LLDate& rhs) const std::ostream& operator<<(std::ostream& s, const LLDate& date) { - date.toStream(s); - return s; + date.toStream(s); + return s; } std::istream& operator>>(std::istream& s, LLDate& date) { - date.fromStream(s); - return s; + date.fromStream(s); + return s; } diff --git a/indra/llcommon/lldate.h b/indra/llcommon/lldate.h index be2cd2d051..81f2dd0d1c 100644 --- a/indra/llcommon/lldate.h +++ b/indra/llcommon/lldate.h @@ -1,4 +1,4 @@ -/** +/** * @file lldate.h * @author Phoenix * @date 2006-02-05 @@ -7,21 +7,21 @@ * $LicenseInfo:firstyear=2006&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -35,7 +35,7 @@ #include "stdtypes.h" #include "llunits.h" -/** +/** * @class LLDate * @brief This class represents a particular point in time in UTC. * @@ -44,110 +44,110 @@ class LL_COMMON_API LLDate { public: - /** - * @brief Construct a date equal to epoch. - */ - LLDate(); - - /** - * @brief Construct a date equal to the source date. - */ - LLDate(const LLDate& date); - - /** - * @brief Construct a date from a seconds since epoch value. - * - * @param seconds_since_epoch The number of seconds since UTC epoch. - */ - LLDate(F64SecondsImplicit seconds_since_epoch); - - /** - * @brief Construct a date from a string representation - * - * The date is constructed in the <code>fromString()</code> - * method. See that method for details of supported formats. - * If that method fails to parse the date, the date is set to epoch. - * @param iso8601_date An iso-8601 compatible representation of the date. - */ - LLDate(const std::string& iso8601_date); - - /** - * @brief Return the date as in ISO-8601 string. - * - * @return A string representation of the date. - */ - std::string asString() const; - std::string asRFC1123() const; - void toStream(std::ostream&) const; - bool split(S32 *year, S32 *month = NULL, S32 *day = NULL, S32 *hour = NULL, S32 *min = NULL, S32 *sec = NULL) const; - std::string toHTTPDateString (std::string fmt) const; - static std::string toHTTPDateString (tm * gmt, std::string fmt); - /** - * @brief Set the date from an ISO-8601 string. - * - * The parser only supports strings conforming to - * YYYYF-MM-DDTHH:MM:SS.FFZ where Y is year, M is month, D is day, - * H is hour, M is minute, S is second, F is sub-second, and all - * other characters are literal. - * If this method fails to parse the date, the previous date is - * retained. - * @param iso8601_date An iso-8601 compatible representation of the date. - * @return Returns true if the string was successfully parsed. - */ - bool fromString(const std::string& iso8601_date); - bool fromStream(std::istream&); - bool fromYMDHMS(S32 year, S32 month = 1, S32 day = 0, S32 hour = 0, S32 min = 0, S32 sec = 0); - - /** - * @brief Return the date in seconds since epoch. - * - * @return The number of seconds since epoch UTC. - */ - F64 secondsSinceEpoch() const; - - /** - * @brief Set the date in seconds since epoch. - * - * @param seconds The number of seconds since epoch UTC. - */ - void secondsSinceEpoch(F64 seconds); - + /** + * @brief Construct a date equal to epoch. + */ + LLDate(); + + /** + * @brief Construct a date equal to the source date. + */ + LLDate(const LLDate& date); + + /** + * @brief Construct a date from a seconds since epoch value. + * + * @param seconds_since_epoch The number of seconds since UTC epoch. + */ + LLDate(F64SecondsImplicit seconds_since_epoch); + + /** + * @brief Construct a date from a string representation + * + * The date is constructed in the <code>fromString()</code> + * method. See that method for details of supported formats. + * If that method fails to parse the date, the date is set to epoch. + * @param iso8601_date An iso-8601 compatible representation of the date. + */ + LLDate(const std::string& iso8601_date); + + /** + * @brief Return the date as in ISO-8601 string. + * + * @return A string representation of the date. + */ + std::string asString() const; + std::string asRFC1123() const; + void toStream(std::ostream&) const; + bool split(S32 *year, S32 *month = NULL, S32 *day = NULL, S32 *hour = NULL, S32 *min = NULL, S32 *sec = NULL) const; + std::string toHTTPDateString (std::string fmt) const; + static std::string toHTTPDateString (tm * gmt, std::string fmt); + /** + * @brief Set the date from an ISO-8601 string. + * + * The parser only supports strings conforming to + * YYYYF-MM-DDTHH:MM:SS.FFZ where Y is year, M is month, D is day, + * H is hour, M is minute, S is second, F is sub-second, and all + * other characters are literal. + * If this method fails to parse the date, the previous date is + * retained. + * @param iso8601_date An iso-8601 compatible representation of the date. + * @return Returns true if the string was successfully parsed. + */ + bool fromString(const std::string& iso8601_date); + bool fromStream(std::istream&); + bool fromYMDHMS(S32 year, S32 month = 1, S32 day = 0, S32 hour = 0, S32 min = 0, S32 sec = 0); + + /** + * @brief Return the date in seconds since epoch. + * + * @return The number of seconds since epoch UTC. + */ + F64 secondsSinceEpoch() const; + + /** + * @brief Set the date in seconds since epoch. + * + * @param seconds The number of seconds since epoch UTC. + */ + void secondsSinceEpoch(F64 seconds); + /** * @brief Create an LLDate object set to the current time. - * - * @return The number of seconds since epoch UTC. - */ + * + * @return The number of seconds since epoch UTC. + */ static LLDate now(); - /** - * @brief Compare dates using operator< so we can order them using STL. - * - * @param rhs -- the right hand side of the comparison operator - */ - bool operator<(const LLDate& rhs) const; - - /** - * @brief Remaining comparison operators in terms of operator< + /** + * @brief Compare dates using operator< so we can order them using STL. + * + * @param rhs -- the right hand side of the comparison operator + */ + bool operator<(const LLDate& rhs) const; + + /** + * @brief Remaining comparison operators in terms of operator< * This conforms to the expectation of STL. - * - * @param rhs -- the right hand side of the comparison operator - */ + * + * @param rhs -- the right hand side of the comparison operator + */ bool operator>(const LLDate& rhs) const { return rhs < *this; } bool operator<=(const LLDate& rhs) const { return !(rhs < *this); } bool operator>=(const LLDate& rhs) const { return !(*this < rhs); } bool operator!=(const LLDate& rhs) const { return (*this < rhs) || (rhs < *this); } bool operator==(const LLDate& rhs) const { return !(*this != rhs); } - /** - * @brief Compare to epoch UTC. - */ + /** + * @brief Compare to epoch UTC. + */ + + bool isNull() const { return mSecondsSinceEpoch == 0.0; } + bool notNull() const { return mSecondsSinceEpoch != 0.0; } - bool isNull() const { return mSecondsSinceEpoch == 0.0; } - bool notNull() const { return mSecondsSinceEpoch != 0.0; } - private: - F64 mSecondsSinceEpoch; + F64 mSecondsSinceEpoch; }; // Helper function to stream out a date diff --git a/indra/llcommon/lldeadmantimer.cpp b/indra/llcommon/lldeadmantimer.cpp index 830443b956..f9c14d7c24 100644 --- a/indra/llcommon/lldeadmantimer.cpp +++ b/indra/llcommon/lldeadmantimer.cpp @@ -1,4 +1,4 @@ -/** +/** * @file lldeadmantimer.cpp * @brief Simple deadman-switch timer. * @author monty@lindenlab.com @@ -43,146 +43,146 @@ // true true Not allowed // LLDeadmanTimer::LLDeadmanTimer(F64 horizon, bool inc_cpu) - : mHorizon(time_type(llmax(horizon, F64(0.0)) * get_timer_info().mClockFrequency)), - mActive(false), // If true, a timer is running. - mDone(false), // If true, timer has completed and can be read (once) - mStarted(U64L(0)), - mExpires(U64L(0)), - mStopped(U64L(0)), - mCount(U64L(0)), - mIncCPU(inc_cpu), - mUStartCPU(LLProcInfo::time_type(U64L(0))), - mUEndCPU(LLProcInfo::time_type(U64L(0))), - mSStartCPU(LLProcInfo::time_type(U64L(0))), - mSEndCPU(LLProcInfo::time_type(U64L(0))) + : mHorizon(time_type(llmax(horizon, F64(0.0)) * get_timer_info().mClockFrequency)), + mActive(false), // If true, a timer is running. + mDone(false), // If true, timer has completed and can be read (once) + mStarted(U64L(0)), + mExpires(U64L(0)), + mStopped(U64L(0)), + mCount(U64L(0)), + mIncCPU(inc_cpu), + mUStartCPU(LLProcInfo::time_type(U64L(0))), + mUEndCPU(LLProcInfo::time_type(U64L(0))), + mSStartCPU(LLProcInfo::time_type(U64L(0))), + mSEndCPU(LLProcInfo::time_type(U64L(0))) {} // static LLDeadmanTimer::time_type LLDeadmanTimer::getNow() { - return LLTimer::getCurrentClockCount(); + return LLTimer::getCurrentClockCount(); } void LLDeadmanTimer::start(time_type now) { - // *TODO: If active, let's complete an existing timer and save - // the result to the side. I think this will be useful later. - // For now, wipe out anything in progress, start fresh. - - if (! now) - { - now = LLTimer::getCurrentClockCount(); - } - mActive = true; - mDone = false; - mStarted = now; - mExpires = now + mHorizon; - mStopped = now; - mCount = U64L(0); - if (mIncCPU) - { - LLProcInfo::getCPUUsage(mUStartCPU, mSStartCPU); - } + // *TODO: If active, let's complete an existing timer and save + // the result to the side. I think this will be useful later. + // For now, wipe out anything in progress, start fresh. + + if (! now) + { + now = LLTimer::getCurrentClockCount(); + } + mActive = true; + mDone = false; + mStarted = now; + mExpires = now + mHorizon; + mStopped = now; + mCount = U64L(0); + if (mIncCPU) + { + LLProcInfo::getCPUUsage(mUStartCPU, mSStartCPU); + } } void LLDeadmanTimer::stop(time_type now) { - if (! mActive) - { - return; - } - - if (! now) - { - now = getNow(); - } - mStopped = now; - mActive = false; - mDone = true; - if (mIncCPU) - { - LLProcInfo::getCPUUsage(mUEndCPU, mSEndCPU); - } + if (! mActive) + { + return; + } + + if (! now) + { + now = getNow(); + } + mStopped = now; + mActive = false; + mDone = true; + if (mIncCPU) + { + LLProcInfo::getCPUUsage(mUEndCPU, mSEndCPU); + } } bool LLDeadmanTimer::isExpired(time_type now, F64 & started, F64 & stopped, U64 & count, - U64 & user_cpu, U64 & sys_cpu) + U64 & user_cpu, U64 & sys_cpu) { - const bool status(isExpired(now, started, stopped, count)); - if (status) - { - user_cpu = U64(mUEndCPU - mUStartCPU); - sys_cpu = U64(mSEndCPU - mSStartCPU); - } - return status; + const bool status(isExpired(now, started, stopped, count)); + if (status) + { + user_cpu = U64(mUEndCPU - mUStartCPU); + sys_cpu = U64(mSEndCPU - mSStartCPU); + } + return status; } - + bool LLDeadmanTimer::isExpired(time_type now, F64 & started, F64 & stopped, U64 & count) { - if (mActive && ! mDone) - { - if (! now) - { - now = getNow(); - } - - if (now >= mExpires) - { - // mStopped from ringBell() is the value we want - mActive = false; - mDone = true; - } - } - - if (! mDone) - { - return false; - } - - started = mStarted * get_timer_info().mClockFrequencyInv; - stopped = mStopped * get_timer_info().mClockFrequencyInv; - count = mCount; - mDone = false; - - return true; + if (mActive && ! mDone) + { + if (! now) + { + now = getNow(); + } + + if (now >= mExpires) + { + // mStopped from ringBell() is the value we want + mActive = false; + mDone = true; + } + } + + if (! mDone) + { + return false; + } + + started = mStarted * get_timer_info().mClockFrequencyInv; + stopped = mStopped * get_timer_info().mClockFrequencyInv; + count = mCount; + mDone = false; + + return true; } - + void LLDeadmanTimer::ringBell(time_type now, unsigned int count) { - if (! mActive) - { - return; - } - - if (! now) - { - now = getNow(); - } - - if (now >= mExpires) - { - // Timer has expired, this event will be dropped - mActive = false; - mDone = true; - } - else - { - // Timer renewed, keep going - mStopped = now; - mExpires = now + mHorizon; - mCount += count; - if (mIncCPU) - { - LLProcInfo::getCPUUsage(mUEndCPU, mSEndCPU); - } - } - - return; + if (! mActive) + { + return; + } + + if (! now) + { + now = getNow(); + } + + if (now >= mExpires) + { + // Timer has expired, this event will be dropped + mActive = false; + mDone = true; + } + else + { + // Timer renewed, keep going + mStopped = now; + mExpires = now + mHorizon; + mCount += count; + if (mIncCPU) + { + LLProcInfo::getCPUUsage(mUEndCPU, mSEndCPU); + } + } + + return; } diff --git a/indra/llcommon/lldeadmantimer.h b/indra/llcommon/lldeadmantimer.h index 980976e176..3f10420d41 100644 --- a/indra/llcommon/lldeadmantimer.h +++ b/indra/llcommon/lldeadmantimer.h @@ -1,4 +1,4 @@ -/** +/** * @file lldeadmantimer.h * @brief Interface to a simple event timer with a deadman's switch * @author monty@lindenlab.com @@ -25,8 +25,8 @@ * $/LicenseInfo$ */ -#ifndef LL_DEADMANTIMER_H -#define LL_DEADMANTIMER_H +#ifndef LL_DEADMANTIMER_H +#define LL_DEADMANTIMER_H #include "linden_common.h" @@ -78,137 +78,137 @@ class LL_COMMON_API LLDeadmanTimer { public: - /// Public types - - /// Low-level time type chosen for compatibility with - /// LLTimer::getCurrentClockCount() which is the basis - /// of time operations in this class. This is likely - /// to change in a future version in a move to TSC-based - /// timing. - typedef U64 time_type; - + /// Public types + + /// Low-level time type chosen for compatibility with + /// LLTimer::getCurrentClockCount() which is the basis + /// of time operations in this class. This is likely + /// to change in a future version in a move to TSC-based + /// timing. + typedef U64 time_type; + public: - /// Construct and initialize an LLDeadmanTimer - /// - /// @param horizon Time, in seconds, after the last @see ringBell() - /// call at which point the timer will consider itself - /// expired. - /// - /// @param inc_cpu If true, gather system and user cpu stats while - /// running the timer. This does require more syscalls - /// during updates. If false, cpu usage data isn't - /// collected and will be zero if queried. - LLDeadmanTimer(F64 horizon, bool inc_cpu); - - ~LLDeadmanTimer() - {} - + /// Construct and initialize an LLDeadmanTimer + /// + /// @param horizon Time, in seconds, after the last @see ringBell() + /// call at which point the timer will consider itself + /// expired. + /// + /// @param inc_cpu If true, gather system and user cpu stats while + /// running the timer. This does require more syscalls + /// during updates. If false, cpu usage data isn't + /// collected and will be zero if queried. + LLDeadmanTimer(F64 horizon, bool inc_cpu); + + ~LLDeadmanTimer() + {} + private: - LLDeadmanTimer(const LLDeadmanTimer &); // Not defined - void operator=(const LLDeadmanTimer &); // Not defined + LLDeadmanTimer(const LLDeadmanTimer &); // Not defined + void operator=(const LLDeadmanTimer &); // Not defined public: - /// Get the current time. Zero-basis for this time - /// representation is not defined and is different on - /// different platforms. Do not attempt to compute - /// negative times relative to the first value returned, - /// there may not be enough 'front porch' on the range - /// to prevent wraparound. - /// - /// Note: Implementation is expected to change in a - /// future release as well. - /// - static time_type getNow(); - - /// Begin timing. If the timer is already active, it is reset - /// and timing begins now. - /// - /// @param now Current time as returned by @see - /// LLTimer::getCurrentClockCount(). If zero, - /// method will lookup current time. - /// - void start(time_type now); - - /// End timing. Actively declare the end of the event independent - /// of the deadman's switch operation. @see isExpired() will return - /// true and appropriate values will be returned. - /// - /// @param now Current time as returned by @see - /// LLTimer::getCurrentClockCount(). If zero, - /// method will lookup current time. - /// - void stop(time_type now); - - /// Declare that something interesting happened. This has two - /// effects on an unexpired-timer. 1) The expiration time - /// is extended for 'horizon' seconds after the 'now' value. - /// 2) An internal counter associated with the event is incremented - /// by the @ref count parameter. This count is returned via the - /// @see isExpired() method. - /// - /// @param now Current time as returned by @see - /// LLTimer::getCurrentClockCount(). If zero, - /// method will lookup current time. - /// - /// @param count Count of events to be associated with - /// this bell ringing. - /// - void ringBell(time_type now, unsigned int count); - - /// Checks the status of the timer. If the timer has expired, - /// also returns various timer-related stats. Unlike ringBell(), - /// does not extend the horizon, it only checks for expiration. - /// - /// @param now Current time as returned by @see - /// LLTimer::getCurrentClockCount(). If zero, - /// method will lookup current time. - /// - /// @param started If expired, the starting time of the event is - /// returned to the caller via this reference. - /// - /// @param stopped If expired, the ending time of the event is - /// returned to the caller via this reference. - /// Ending time will be that provided in the - /// stop() method or the last ringBell() call - /// leading to expiration, whichever (stop() call - /// or notice of expiration) happened first. - /// - /// @param count If expired, the number of ringBell() calls - /// made prior to expiration. - /// - /// @param user_cpu Amount of CPU spent in user mode by the process - /// during the event. Value in microseconds and will - /// read zero if not enabled by the constructor. - /// - /// @param sys_cpu Amount of CPU spent in system mode by the process. - /// - /// @return true if the timer has expired, false otherwise. - /// If true, it also returns the started, - /// stopped and count values otherwise these are - /// left unchanged. - /// - bool isExpired(time_type now, F64 & started, F64 & stopped, U64 & count, - U64 & user_cpu, U64 & sys_cpu); - - /// Identical to the six-arugment form except it does without the - /// CPU time return if the caller isn't interested in it. - bool isExpired(time_type now, F64 & started, F64 & stopped, U64 & count); + /// Get the current time. Zero-basis for this time + /// representation is not defined and is different on + /// different platforms. Do not attempt to compute + /// negative times relative to the first value returned, + /// there may not be enough 'front porch' on the range + /// to prevent wraparound. + /// + /// Note: Implementation is expected to change in a + /// future release as well. + /// + static time_type getNow(); + + /// Begin timing. If the timer is already active, it is reset + /// and timing begins now. + /// + /// @param now Current time as returned by @see + /// LLTimer::getCurrentClockCount(). If zero, + /// method will lookup current time. + /// + void start(time_type now); + + /// End timing. Actively declare the end of the event independent + /// of the deadman's switch operation. @see isExpired() will return + /// true and appropriate values will be returned. + /// + /// @param now Current time as returned by @see + /// LLTimer::getCurrentClockCount(). If zero, + /// method will lookup current time. + /// + void stop(time_type now); + + /// Declare that something interesting happened. This has two + /// effects on an unexpired-timer. 1) The expiration time + /// is extended for 'horizon' seconds after the 'now' value. + /// 2) An internal counter associated with the event is incremented + /// by the @ref count parameter. This count is returned via the + /// @see isExpired() method. + /// + /// @param now Current time as returned by @see + /// LLTimer::getCurrentClockCount(). If zero, + /// method will lookup current time. + /// + /// @param count Count of events to be associated with + /// this bell ringing. + /// + void ringBell(time_type now, unsigned int count); + + /// Checks the status of the timer. If the timer has expired, + /// also returns various timer-related stats. Unlike ringBell(), + /// does not extend the horizon, it only checks for expiration. + /// + /// @param now Current time as returned by @see + /// LLTimer::getCurrentClockCount(). If zero, + /// method will lookup current time. + /// + /// @param started If expired, the starting time of the event is + /// returned to the caller via this reference. + /// + /// @param stopped If expired, the ending time of the event is + /// returned to the caller via this reference. + /// Ending time will be that provided in the + /// stop() method or the last ringBell() call + /// leading to expiration, whichever (stop() call + /// or notice of expiration) happened first. + /// + /// @param count If expired, the number of ringBell() calls + /// made prior to expiration. + /// + /// @param user_cpu Amount of CPU spent in user mode by the process + /// during the event. Value in microseconds and will + /// read zero if not enabled by the constructor. + /// + /// @param sys_cpu Amount of CPU spent in system mode by the process. + /// + /// @return true if the timer has expired, false otherwise. + /// If true, it also returns the started, + /// stopped and count values otherwise these are + /// left unchanged. + /// + bool isExpired(time_type now, F64 & started, F64 & stopped, U64 & count, + U64 & user_cpu, U64 & sys_cpu); + + /// Identical to the six-arugment form except it does without the + /// CPU time return if the caller isn't interested in it. + bool isExpired(time_type now, F64 & started, F64 & stopped, U64 & count); protected: - time_type mHorizon; - bool mActive; - bool mDone; - time_type mStarted; - time_type mExpires; - time_type mStopped; - time_type mCount; - - const bool mIncCPU; // Include CPU metrics in timer - LLProcInfo::time_type mUStartCPU; - LLProcInfo::time_type mUEndCPU; - LLProcInfo::time_type mSStartCPU; - LLProcInfo::time_type mSEndCPU; + time_type mHorizon; + bool mActive; + bool mDone; + time_type mStarted; + time_type mExpires; + time_type mStopped; + time_type mCount; + + const bool mIncCPU; // Include CPU metrics in timer + LLProcInfo::time_type mUStartCPU; + LLProcInfo::time_type mUEndCPU; + LLProcInfo::time_type mSStartCPU; + LLProcInfo::time_type mSEndCPU; }; - -#endif // LL_DEADMANTIMER_H + +#endif // LL_DEADMANTIMER_H diff --git a/indra/llcommon/lldefs.h b/indra/llcommon/lldefs.h index 4e25001fff..0ba756d472 100644 --- a/indra/llcommon/lldefs.h +++ b/indra/llcommon/lldefs.h @@ -1,25 +1,25 @@ -/** +/** * @file lldefs.h * @brief Various generic constant definitions. * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -31,73 +31,73 @@ #include <type_traits> // Often used array indices -const U32 VX = 0; -const U32 VY = 1; -const U32 VZ = 2; -const U32 VW = 3; -const U32 VS = 3; - -const U32 VRED = 0; -const U32 VGREEN = 1; -const U32 VBLUE = 2; -const U32 VALPHA = 3; - -const U32 INVALID_DIRECTION = 0xFFFFFFFF; -const U32 EAST = 0; -const U32 NORTH = 1; -const U32 WEST = 2; -const U32 SOUTH = 3; - -const U32 NORTHEAST = 4; -const U32 NORTHWEST = 5; -const U32 SOUTHWEST = 6; -const U32 SOUTHEAST = 7; -const U32 MIDDLE = 8; - -const U8 EAST_MASK = 0x1<<EAST; -const U8 NORTH_MASK = 0x1<<NORTH; -const U8 WEST_MASK = 0x1<<WEST; -const U8 SOUTH_MASK = 0x1<<SOUTH; - -const U8 NORTHEAST_MASK = NORTH_MASK | EAST_MASK; -const U8 NORTHWEST_MASK = NORTH_MASK | WEST_MASK; -const U8 SOUTHWEST_MASK = SOUTH_MASK | WEST_MASK; -const U8 SOUTHEAST_MASK = SOUTH_MASK | EAST_MASK; +const U32 VX = 0; +const U32 VY = 1; +const U32 VZ = 2; +const U32 VW = 3; +const U32 VS = 3; + +const U32 VRED = 0; +const U32 VGREEN = 1; +const U32 VBLUE = 2; +const U32 VALPHA = 3; + +const U32 INVALID_DIRECTION = 0xFFFFFFFF; +const U32 EAST = 0; +const U32 NORTH = 1; +const U32 WEST = 2; +const U32 SOUTH = 3; + +const U32 NORTHEAST = 4; +const U32 NORTHWEST = 5; +const U32 SOUTHWEST = 6; +const U32 SOUTHEAST = 7; +const U32 MIDDLE = 8; + +const U8 EAST_MASK = 0x1<<EAST; +const U8 NORTH_MASK = 0x1<<NORTH; +const U8 WEST_MASK = 0x1<<WEST; +const U8 SOUTH_MASK = 0x1<<SOUTH; + +const U8 NORTHEAST_MASK = NORTH_MASK | EAST_MASK; +const U8 NORTHWEST_MASK = NORTH_MASK | WEST_MASK; +const U8 SOUTHWEST_MASK = SOUTH_MASK | WEST_MASK; +const U8 SOUTHEAST_MASK = SOUTH_MASK | EAST_MASK; const U32 gDirOpposite[8] = {2, 3, 0, 1, 6, 7, 4, 5}; const U32 gDirAdjacent[8][2] = { - {4, 7}, - {4, 5}, - {5, 6}, - {6, 7}, - {0, 1}, - {1, 2}, - {2, 3}, - {0, 3} - }; + {4, 7}, + {4, 5}, + {5, 6}, + {6, 7}, + {0, 1}, + {1, 2}, + {2, 3}, + {0, 3} + }; // Magnitude along the x and y axis const S32 gDirAxes[8][2] = { - { 1, 0}, // east - { 0, 1}, // north - {-1, 0}, // west - { 0,-1}, // south - { 1, 1}, // ne - {-1, 1}, // nw - {-1,-1}, // sw - { 1,-1}, // se - }; - -const S32 gDirMasks[8] = { - EAST_MASK, - NORTH_MASK, - WEST_MASK, - SOUTH_MASK, - NORTHEAST_MASK, - NORTHWEST_MASK, - SOUTHWEST_MASK, - SOUTHEAST_MASK - }; + { 1, 0}, // east + { 0, 1}, // north + {-1, 0}, // west + { 0,-1}, // south + { 1, 1}, // ne + {-1, 1}, // nw + {-1,-1}, // sw + { 1,-1}, // se + }; + +const S32 gDirMasks[8] = { + EAST_MASK, + NORTH_MASK, + WEST_MASK, + SOUTH_MASK, + NORTHEAST_MASK, + NORTHWEST_MASK, + SOUTHWEST_MASK, + SOUTHEAST_MASK + }; // Sides of a box... // . Z __.Y @@ -109,21 +109,21 @@ const S32 gDirMasks[8] = { // / | /| -3- / | 5 = TOP_SIDE = +z // +------------------+ | 6 = BOTTOM_SIDE = -z // | | | / | | -// | |/| | / | |/| -// | 2 | | *-------|-1--------> X -// |/| | -4- |/| | +// | |/| | / | |/| +// | 2 | | *-------|-1--------> X +// |/| | -4- |/| | // | +----|---------|---+ // | / / | / // | / -6- | / -// |/ / |/ +// |/ / |/ // +------------------+ -const U32 NO_SIDE = 0; -const U32 FRONT_SIDE = 1; -const U32 BACK_SIDE = 2; -const U32 LEFT_SIDE = 3; -const U32 RIGHT_SIDE = 4; -const U32 TOP_SIDE = 5; -const U32 BOTTOM_SIDE = 6; +const U32 NO_SIDE = 0; +const U32 FRONT_SIDE = 1; +const U32 BACK_SIDE = 2; +const U32 LEFT_SIDE = 3; +const U32 RIGHT_SIDE = 4; +const U32 TOP_SIDE = 5; +const U32 BOTTOM_SIDE = 6; const U8 LL_SOUND_FLAG_NONE = 0x0; const U8 LL_SOUND_FLAG_LOOP = 1<<0; @@ -141,17 +141,17 @@ const U8 LL_SOUND_FLAG_SYNC_MASK = LL_SOUND_FLAG_SYNC_MASTER | LL_SOUND_FLAG_SYN // DO NOT CHANGE. // -------------- // -const U32 LL_MAX_PATH = 1024; // buffer size of maximum path + filename string length +const U32 LL_MAX_PATH = 1024; // buffer size of maximum path + filename string length // For strings we send in messages -const U32 STD_STRING_BUF_SIZE = 255; // Buffer size -const U32 STD_STRING_STR_LEN = 254; // Length of the string (not including \0) +const U32 STD_STRING_BUF_SIZE = 255; // Buffer size +const U32 STD_STRING_STR_LEN = 254; // Length of the string (not including \0) // *NOTE: This value is used as hard-coded numbers in scanf() variants. // DO NOT CHANGE. -const U32 MAX_STRING = STD_STRING_BUF_SIZE; // Buffer size +const U32 MAX_STRING = STD_STRING_BUF_SIZE; // Buffer size -const U32 MAXADDRSTR = 17; // 123.567.901.345 = 15 chars + \0 + 1 for good luck +const U32 MAXADDRSTR = 17; // 123.567.901.345 = 15 chars + \0 + 1 for good luck // C++ is our friend. . . use template functions to make life easier! @@ -176,7 +176,7 @@ inline auto llmax(T data) return data; } -template <typename T0, typename T1, typename... Ts> +template <typename T0, typename T1, typename... Ts> inline auto llmax(T0 d0, T1 d1, Ts... rest) { auto maxrest = llmax(d1, rest...); @@ -190,44 +190,44 @@ inline auto llmin(T data) return data; } -template <typename T0, typename T1, typename... Ts> +template <typename T0, typename T1, typename... Ts> inline auto llmin(T0 d0, T1 d1, Ts... rest) { auto minrest = llmin(d1, rest...); return (d0 < minrest) ? d0 : minrest; } -template <typename A, typename MIN, typename MAX> +template <typename A, typename MIN, typename MAX> inline A llclamp(A a, MIN minval, MAX maxval) { - A aminval{ static_cast<A>(minval) }, amaxval{ static_cast<A>(maxval) }; - if ( a < aminval ) - { - return aminval; - } - else if ( a > amaxval ) - { - return amaxval; - } - return a; + A aminval{ static_cast<A>(minval) }, amaxval{ static_cast<A>(maxval) }; + if ( a < aminval ) + { + return aminval; + } + else if ( a > amaxval ) + { + return amaxval; + } + return a; } -template <class LLDATATYPE> +template <class LLDATATYPE> inline LLDATATYPE llclampf(LLDATATYPE a) { - return llmin(llmax(a, LLDATATYPE(0)), LLDATATYPE(1)); + return llmin(llmax(a, LLDATATYPE(0)), LLDATATYPE(1)); } -template <class LLDATATYPE> +template <class LLDATATYPE> inline LLDATATYPE llclampb(LLDATATYPE a) { - return llmin(llmax(a, LLDATATYPE(0)), LLDATATYPE(255)); + return llmin(llmax(a, LLDATATYPE(0)), LLDATATYPE(255)); } -template <class LLDATATYPE> +template <class LLDATATYPE> inline void llswap(LLDATATYPE& lhs, LLDATATYPE& rhs) { - std::swap(lhs, rhs); + std::swap(lhs, rhs); } #endif // LL_LLDEFS_H diff --git a/indra/llcommon/lldependencies.cpp b/indra/llcommon/lldependencies.cpp index db546c5c3b..dec0748374 100644 --- a/indra/llcommon/lldependencies.cpp +++ b/indra/llcommon/lldependencies.cpp @@ -3,25 +3,25 @@ * @author Nat Goodspeed * @date 2008-09-17 * @brief Implementation for lldependencies. - * + * * $LicenseInfo:firstyear=2008&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llcommon/lldependencies.h b/indra/llcommon/lldependencies.h index 950af4a4ad..47b6fedc7d 100644 --- a/indra/llcommon/lldependencies.h +++ b/indra/llcommon/lldependencies.h @@ -8,21 +8,21 @@ * $LicenseInfo:firstyear=2008&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -177,7 +177,7 @@ struct LLDependenciesEmpty * values such as NULL or 0 rather than having to write * LLDependenciesEmpty(). */ - LLDependenciesEmpty(void*) {} + LLDependenciesEmpty(void*) {} }; /** @@ -209,7 +209,7 @@ class LLDependencies: public LLDependenciesBase before(before_) {} NODE node; - dep_set after, before; + dep_set after, before; }; typedef std::map<KEY, DepNode> DepNodeMap; typedef typename DepNodeMap::value_type DepNodeMapEntry; @@ -239,7 +239,7 @@ public: * NODE& reference. * * @note - * Actual dependency analysis is deferred to the sort() method, so + * Actual dependency analysis is deferred to the sort() method, so * you can add an arbitrary number of nodes without incurring analysis * overhead for each. The flip side of this is that add()ing nodes that * define a cycle leaves this object in a state in which sort() will @@ -598,7 +598,7 @@ public: return sorted_range(begin, end); } - using LLDependenciesBase::describe; // unhide virtual std::string describe(bool full=true) const; + using LLDependenciesBase::describe; // unhide virtual std::string describe(bool full=true) const; /// Override base-class describe() with actual implementation virtual std::ostream& describe(std::ostream& out, bool full=true) const diff --git a/indra/llcommon/lldepthstack.h b/indra/llcommon/lldepthstack.h index b65840d342..66a7a4ce15 100644 --- a/indra/llcommon/lldepthstack.h +++ b/indra/llcommon/lldepthstack.h @@ -1,25 +1,25 @@ -/** +/** * @file lldepthstack.h * @brief Declaration of the LLDepthStack class * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -32,65 +32,65 @@ template <class DATA_TYPE> class LLDepthStack { private: - std::deque<DATA_TYPE*> mStack; - U32 mCurrentDepth; - U32 mMaxDepth; + std::deque<DATA_TYPE*> mStack; + U32 mCurrentDepth; + U32 mMaxDepth; public: - LLDepthStack() - : mCurrentDepth(0), mMaxDepth(0) - {} + LLDepthStack() + : mCurrentDepth(0), mMaxDepth(0) + {} + + void setDepth(U32 depth) + { + mMaxDepth = depth; + } + + U32 getDepth(void) const + { + return mCurrentDepth; + } - void setDepth(U32 depth) - { - mMaxDepth = depth; - } + void push(DATA_TYPE *data) + { + if (mCurrentDepth < mMaxDepth) + { + mStack.push_back(data); + mCurrentDepth++; + } + else + { + // the last item falls off stack and is deleted + if (!mStack.empty()) + { + mStack.pop_front(); + } + mStack.push_back(data); + } + } - U32 getDepth(void) const - { - return mCurrentDepth; - } + DATA_TYPE *pop() + { + DATA_TYPE *tempp = NULL; + if (!mStack.empty()) + { + tempp = mStack.back(); + mStack.pop_back(); + mCurrentDepth--; + } + return tempp; + } - void push(DATA_TYPE *data) - { - if (mCurrentDepth < mMaxDepth) - { - mStack.push_back(data); - mCurrentDepth++; - } - else - { - // the last item falls off stack and is deleted - if (!mStack.empty()) - { - mStack.pop_front(); - } - mStack.push_back(data); - } - } - - DATA_TYPE *pop() - { - DATA_TYPE *tempp = NULL; - if (!mStack.empty()) - { - tempp = mStack.back(); - mStack.pop_back(); - mCurrentDepth--; - } - return tempp; - } - - DATA_TYPE *check() - { - return mStack.empty() ? NULL : mStack.back(); - } + DATA_TYPE *check() + { + return mStack.empty() ? NULL : mStack.back(); + } - void removeAllNodes() - { - mCurrentDepth = 0; - mStack.clear(); - } + void removeAllNodes() + { + mCurrentDepth = 0; + mStack.clear(); + } }; #endif diff --git a/indra/llcommon/lldictionary.cpp b/indra/llcommon/lldictionary.cpp index e16c35ed6a..1970aa58f2 100644 --- a/indra/llcommon/lldictionary.cpp +++ b/indra/llcommon/lldictionary.cpp @@ -1,25 +1,25 @@ -/** +/** * @file lldictionary.cpp * @brief Lldictionary class header file * * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -32,16 +32,16 @@ // Define in .cpp file to prevent header include of llstring.h LLDictionaryEntry::LLDictionaryEntry(const std::string &name) -: mName(name) +: mName(name) { - mNameCapitalized = mName; - LLStringUtil::replaceChar(mNameCapitalized, '-', ' '); - LLStringUtil::replaceChar(mNameCapitalized, '_', ' '); - for (U32 i=0; i < mNameCapitalized.size(); i++) - { - if (i == 0 || mNameCapitalized[i-1] == ' ') // don't change ordering of this statement or crash - { - mNameCapitalized[i] = toupper(mNameCapitalized[i]); - } - } + mNameCapitalized = mName; + LLStringUtil::replaceChar(mNameCapitalized, '-', ' '); + LLStringUtil::replaceChar(mNameCapitalized, '_', ' '); + for (U32 i=0; i < mNameCapitalized.size(); i++) + { + if (i == 0 || mNameCapitalized[i-1] == ' ') // don't change ordering of this statement or crash + { + mNameCapitalized[i] = toupper(mNameCapitalized[i]); + } + } } diff --git a/indra/llcommon/lldictionary.h b/indra/llcommon/lldictionary.h index 18664e340e..9c399057ae 100644 --- a/indra/llcommon/lldictionary.h +++ b/indra/llcommon/lldictionary.h @@ -1,25 +1,25 @@ -/** +/** * @file lldictionary.h * @brief Lldictionary class header file * * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -34,64 +34,64 @@ struct LL_COMMON_API LLDictionaryEntry { - LLDictionaryEntry(const std::string &name); - virtual ~LLDictionaryEntry() {} - const std::string mName; - std::string mNameCapitalized; + LLDictionaryEntry(const std::string &name); + virtual ~LLDictionaryEntry() {} + const std::string mName; + std::string mNameCapitalized; }; template <class Index, class Entry> class LLDictionary : public std::map<Index, Entry *> { public: - typedef std::map<Index, Entry *> map_t; - typedef typename map_t::iterator iterator_t; - typedef typename map_t::const_iterator const_iterator_t; - - LLDictionary() {} - virtual ~LLDictionary() - { - for (iterator_t iter = map_t::begin(); iter != map_t::end(); ++iter) - delete (iter->second); - } + typedef std::map<Index, Entry *> map_t; + typedef typename map_t::iterator iterator_t; + typedef typename map_t::const_iterator const_iterator_t; + + LLDictionary() {} + virtual ~LLDictionary() + { + for (iterator_t iter = map_t::begin(); iter != map_t::end(); ++iter) + delete (iter->second); + } - const Entry *lookup(Index index) const - { - const_iterator_t dictionary_iter = map_t::find(index); - if (dictionary_iter == map_t::end()) return NULL; - return dictionary_iter->second; - } - const Index lookup(const std::string &name) const - { - for (const_iterator_t dictionary_iter = map_t::begin(); - dictionary_iter != map_t::end(); - dictionary_iter++) - { - const Entry *entry = dictionary_iter->second; - if (entry->mName == name) - { - return dictionary_iter->first; - } - } - return notFound(); - } + const Entry *lookup(Index index) const + { + const_iterator_t dictionary_iter = map_t::find(index); + if (dictionary_iter == map_t::end()) return NULL; + return dictionary_iter->second; + } + const Index lookup(const std::string &name) const + { + for (const_iterator_t dictionary_iter = map_t::begin(); + dictionary_iter != map_t::end(); + dictionary_iter++) + { + const Entry *entry = dictionary_iter->second; + if (entry->mName == name) + { + return dictionary_iter->first; + } + } + return notFound(); + } protected: - virtual Index notFound() const - { - // default is to assert - // don't assert -- makes it impossible to work on mesh-development and viewer-development simultaneously - // -- davep 2010.10.29 - //llassert(false); - return Index(-1); - } - void addEntry(Index index, Entry *entry) - { - if (!this->emplace(index, entry).second) - { - LL_ERRS() << "Dictionary entry already added (attempted to add duplicate entry)" << LL_ENDL; - } - } + virtual Index notFound() const + { + // default is to assert + // don't assert -- makes it impossible to work on mesh-development and viewer-development simultaneously + // -- davep 2010.10.29 + //llassert(false); + return Index(-1); + } + void addEntry(Index index, Entry *entry) + { + if (!this->emplace(index, entry).second) + { + LL_ERRS() << "Dictionary entry already added (attempted to add duplicate entry)" << LL_ENDL; + } + } }; #endif // LL_LLDICTIONARY_H diff --git a/indra/llcommon/lldoubledispatch.h b/indra/llcommon/lldoubledispatch.h index ce6731e864..c8c566205a 100644 --- a/indra/llcommon/lldoubledispatch.h +++ b/indra/llcommon/lldoubledispatch.h @@ -3,25 +3,25 @@ * @author Nat Goodspeed * @date 2008-11-11 * @brief function calls virtual on more than one parameter - * + * * $LicenseInfo:firstyear=2008&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -41,7 +41,7 @@ * subset of that problem: function calls which accept two parameters, and * select which particular function to call depending on the dynamic type of * both. - * + * * Scott Meyers, in More Effective C++ (Item 31), talks about some of the perils * and pitfalls lurking down this pathway. He discusses and dismisses the * straightforward approaches of using single-dispatch virtual functions twice, @@ -50,17 +50,17 @@ * look up the actual types of both parameters (he uses the classes' string names, * via typeid(param).name()) to obtain a pointer to a free (non-member) function * that will accept this pair of parameters. - * + * * He does point out that his approach doesn't handle inheritance. If you have a * registry entry for SpaceShip, and you have in hand a MilitaryShip (subclass of * SpaceShip) and an Asteroid, you'd like to call the function appropriate for * SpaceShips and Asteroids -- but alas, his lookup fails because the class name * for your MilitaryShip subclass isn't in the registry. - * + * * This class extends his idea to build a registry whose entries can examine the * dynamic type of the parameter in a more flexible way -- using dynamic_cast<> * -- thereby supporting inheritance relationships. - * + * * Of course we must allow for the ambiguity this permits. We choose to use a * sequence container rather than a map, and require that the client code * specify the order in which dispatch-table entries should be searched. The @@ -69,7 +69,7 @@ * you catch ErrorBaseClass before you catch ErrorSubclass, then any * ErrorSubclass exceptions thrown by the protected code will always match * ErrorBaseClass, and you will never reach your catch(ErrorSubclass) clause. - * + * * So, in a similar way, if you have a specific routine to process * MilitaryShip and Asteroid, you'd better place that in the table @em before * your more general routine that processes SpaceShip and Asteroid, or else @@ -279,7 +279,7 @@ private: /// Look up the first matching entry. EntryPtr lookup(const ParamBaseType& param1, const ParamBaseType& param2) const { - typename DispatchTable::const_iterator found = find(param1, param2); + typename DispatchTable::const_iterator found = find(param1, param2); if (found != mDispatch.end()) { // Dereferencing the list iterator gets us an EntryPtr diff --git a/indra/llcommon/llendianswizzle.h b/indra/llcommon/llendianswizzle.h index 4c08074a9c..d56eb7be2e 100644 --- a/indra/llcommon/llendianswizzle.h +++ b/indra/llcommon/llendianswizzle.h @@ -1,25 +1,25 @@ -/** +/** * @file llendianswizzle.h * @brief Functions for in-place bit swizzling * * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -28,63 +28,63 @@ #define LL_LLENDIANSWIZZLE_H /* This function is intended to be used for in-place swizzling, particularly after fread() of - binary values from a file. Such as: - - numRead = fread(scale.mV, sizeof(float), 3, fp); - llendianswizzle(scale.mV, sizeof(float), 3); - - It assumes that the values in the file are LITTLE endian, so it's a no-op on a little endian machine. - - It keys off of typesize to do the correct swizzle, so make sure that typesize is the size of the native type. - - 64-bit types are not yet handled. + binary values from a file. Such as: + + numRead = fread(scale.mV, sizeof(float), 3, fp); + llendianswizzle(scale.mV, sizeof(float), 3); + + It assumes that the values in the file are LITTLE endian, so it's a no-op on a little endian machine. + + It keys off of typesize to do the correct swizzle, so make sure that typesize is the size of the native type. + + 64-bit types are not yet handled. */ #ifdef LL_LITTLE_ENDIAN - // little endian is native for most things. - inline void llendianswizzle(void *,int,int) - { - // Nothing to do - } + // little endian is native for most things. + inline void llendianswizzle(void *,int,int) + { + // Nothing to do + } #endif #ifdef LL_BIG_ENDIAN - // big endian requires a bit of work. - inline void llendianswizzle(void *p,int typesize, int count) - { - int i; - switch(typesize) - { - case 2: - { - U16 temp; - for(i=count ;i!=0 ;i--) - { - temp = ((U16*)p)[0]; - ((U16*)p)[0] = ((temp >> 8) & 0x000000FF) | ((temp << 8) & 0x0000FF00); - p = (void*)(((U16*)p) + 1); - } - } - break; - - case 4: - { - U32 temp; - for(i=count; i!=0; i--) - { - temp = ((U32*)p)[0]; - ((U32*)p)[0] = - ((temp >> 24) & 0x000000FF) | - ((temp >> 8) & 0x0000FF00) | - ((temp << 8) & 0x00FF0000) | - ((temp << 24) & 0xFF000000); - p = (void*)(((U32*)p) + 1); - } - } - break; - } - - } + // big endian requires a bit of work. + inline void llendianswizzle(void *p,int typesize, int count) + { + int i; + switch(typesize) + { + case 2: + { + U16 temp; + for(i=count ;i!=0 ;i--) + { + temp = ((U16*)p)[0]; + ((U16*)p)[0] = ((temp >> 8) & 0x000000FF) | ((temp << 8) & 0x0000FF00); + p = (void*)(((U16*)p) + 1); + } + } + break; + + case 4: + { + U32 temp; + for(i=count; i!=0; i--) + { + temp = ((U32*)p)[0]; + ((U32*)p)[0] = + ((temp >> 24) & 0x000000FF) | + ((temp >> 8) & 0x0000FF00) | + ((temp << 8) & 0x00FF0000) | + ((temp << 24) & 0xFF000000); + p = (void*)(((U32*)p) + 1); + } + } + break; + } + + } #endif // Use this when working with a single integral value you want swizzled diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp index 6211362615..80fcaefad2 100644 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -1,1662 +1,1662 @@ -/** - * @file llerror.cpp - * @date December 2006 - * @brief error message system - * - * $LicenseInfo:firstyear=2006&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" - -#include "llerror.h" -#include "llerrorcontrol.h" -#include "llsdutil.h" - -#include <cctype> -#ifdef __GNUC__ -# include <cxxabi.h> -#endif // __GNUC__ -#include <sstream> -#if !LL_WINDOWS -# include <syslog.h> -# include <unistd.h> -# include <sys/stat.h> -#else -# include <io.h> -#endif // !LL_WINDOWS -#include <vector> -#include "string.h" - -#include "llapp.h" -#include "llapr.h" -#include "llfile.h" -#include "lllivefile.h" -#include "llsd.h" -#include "llsdserialize.h" -#include "llsingleton.h" -#include "llstl.h" -#include "lltimer.h" - -// On Mac, got: -// #error "Boost.Stacktrace requires `_Unwind_Backtrace` function. Define -// `_GNU_SOURCE` macro or `BOOST_STACKTRACE_GNU_SOURCE_NOT_REQUIRED` if -// _Unwind_Backtrace is available without `_GNU_SOURCE`." -#define BOOST_STACKTRACE_GNU_SOURCE_NOT_REQUIRED -#include <boost/stacktrace.hpp> - -namespace { -#if LL_WINDOWS - void debugger_print(const std::string& s) - { - // Be careful when calling OutputDebugString as it throws DBG_PRINTEXCEPTION_C - // which works just fine under the windows debugger, but can cause users who - // have enabled SEHOP exception chain validation to crash due to interactions - // between the Win 32-bit exception handling and boost coroutine fiber stacks. BUG-2707 - // - if (IsDebuggerPresent()) - { - // Need UTF16 for Unicode OutputDebugString - // - if (s.size()) - { - OutputDebugString(utf8str_to_utf16str(s).c_str()); - OutputDebugString(TEXT("\n")); - } - } - } -#else - class RecordToSyslog : public LLError::Recorder - { - public: - RecordToSyslog(const std::string& identity) - : mIdentity(identity) - { - openlog(mIdentity.c_str(), LOG_CONS|LOG_PID, LOG_LOCAL0); - // we need to set the string from a local copy of the string - // since apparanetly openlog expects the const char* to remain - // valid even after it returns (presumably until closelog) - } - - ~RecordToSyslog() - { - closelog(); - } - - virtual bool enabled() override - { - return LLError::getEnabledLogTypesMask() & 0x01; - } - - virtual void recordMessage(LLError::ELevel level, - const std::string& message) override - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_LOGGING - int syslogPriority = LOG_CRIT; - switch (level) { - case LLError::LEVEL_DEBUG: syslogPriority = LOG_DEBUG; break; - case LLError::LEVEL_INFO: syslogPriority = LOG_INFO; break; - case LLError::LEVEL_WARN: syslogPriority = LOG_WARNING; break; - case LLError::LEVEL_ERROR: syslogPriority = LOG_CRIT; break; - default: syslogPriority = LOG_CRIT; - } - - syslog(syslogPriority, "%s", message.c_str()); - } - private: - std::string mIdentity; - }; -#endif - - class RecordToFile : public LLError::Recorder - { - public: - RecordToFile(const std::string& filename): - mName(filename) - { - mFile.open(filename.c_str(), std::ios_base::out | std::ios_base::app); - if (!mFile) - { - LL_INFOS() << "Error setting log file to " << filename << LL_ENDL; - } - else - { - if (!LLError::getAlwaysFlush()) - { - mFile.sync_with_stdio(false); - } - } - } - - ~RecordToFile() - { - mFile.close(); - } - - virtual bool enabled() override - { -#ifdef LL_RELEASE_FOR_DOWNLOAD - return 1; -#else - return LLError::getEnabledLogTypesMask() & 0x02; -#endif - } - - bool okay() const { return mFile.good(); } - - std::string getFilename() const { return mName; } - - virtual void recordMessage(LLError::ELevel level, - const std::string& message) override - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_LOGGING - if (LLError::getAlwaysFlush()) - { - mFile << message << std::endl; - } - else - { - mFile << message << "\n"; - } - } - - private: - const std::string mName; - llofstream mFile; - }; - - - class RecordToStderr : public LLError::Recorder - { - public: - RecordToStderr(bool timestamp) : mUseANSI(checkANSI()) - { - this->showMultiline(true); - } - - virtual bool enabled() override - { - return LLError::getEnabledLogTypesMask() & 0x04; - } - - LL_FORCE_INLINE std::string createBoldANSI() - { - std::string ansi_code; - ansi_code += '\033'; - ansi_code += "["; - ansi_code += "1"; - ansi_code += "m"; - - return ansi_code; - } - - LL_FORCE_INLINE std::string createResetANSI() - { - std::string ansi_code; - ansi_code += '\033'; - ansi_code += "["; - ansi_code += "0"; - ansi_code += "m"; - - return ansi_code; - } - - LL_FORCE_INLINE std::string createANSI(const std::string& color) - { - std::string ansi_code; - ansi_code += '\033'; - ansi_code += "["; - ansi_code += "38;5;"; - ansi_code += color; - ansi_code += "m"; - - return ansi_code; - } - - virtual void recordMessage(LLError::ELevel level, - const std::string& message) override - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_LOGGING - // The default colors for error, warn and debug are now a bit more pastel - // and easier to read on the default (black) terminal background but you - // now have the option to set the color of each via an environment variables: - // LL_ANSI_ERROR_COLOR_CODE (default is red) - // LL_ANSI_WARN_COLOR_CODE (default is blue) - // LL_ANSI_DEBUG_COLOR_CODE (default is magenta) - // The list of color codes can be found in many places but I used this page: - // https://www.lihaoyi.com/post/BuildyourownCommandLinewithANSIescapecodes.html#256-colors - // (Note: you may need to restart Visual Studio to pick environment changes) - char* val = nullptr; - std::string s_ansi_error_code = "160"; - if ((val = getenv("LL_ANSI_ERROR_COLOR_CODE")) != nullptr) s_ansi_error_code = std::string(val); - std::string s_ansi_warn_code = "33"; - if ((val = getenv("LL_ANSI_WARN_COLOR_CODE")) != nullptr) s_ansi_warn_code = std::string(val); - std::string s_ansi_debug_code = "177"; - if ((val = getenv("LL_ANSI_DEBUG_COLOR_CODE")) != nullptr) s_ansi_debug_code = std::string(val); - - static std::string s_ansi_error = createANSI(s_ansi_error_code); // default is red - static std::string s_ansi_warn = createANSI(s_ansi_warn_code); // default is blue - static std::string s_ansi_debug = createANSI(s_ansi_debug_code); // default is magenta - - if (mUseANSI) - { - writeANSI((level == LLError::LEVEL_ERROR) ? s_ansi_error : - (level == LLError::LEVEL_WARN) ? s_ansi_warn : - s_ansi_debug, message); - } - else - { - LL_PROFILE_ZONE_NAMED("fprintf"); - fprintf(stderr, "%s\n", message.c_str()); - } - } - - private: - bool mUseANSI; - - LL_FORCE_INLINE void writeANSI(const std::string& ansi_code, const std::string& message) - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_LOGGING - static std::string s_ansi_bold = createBoldANSI(); // bold text - static std::string s_ansi_reset = createResetANSI(); // reset - // ANSI color code escape sequence, message, and reset in one fprintf call - // Default all message levels to bold so we can distinguish our own messages from those dumped by subprocesses and libraries. - fprintf(stderr, "%s%s\n%s", ansi_code.c_str(), message.c_str(), s_ansi_reset.c_str() ); - } - - static bool checkANSI(void) - { - // Check whether it's okay to use ANSI; if stderr is - // a tty then we assume yes. Can be turned off with - // the LL_NO_ANSI_COLOR env var. - return (0 != isatty(2)) && - (NULL == getenv("LL_NO_ANSI_COLOR")); - } - }; - - class RecordToFixedBuffer : public LLError::Recorder - { - public: - RecordToFixedBuffer(LLLineBuffer* buffer) - : mBuffer(buffer) - { - this->showMultiline(true); - this->showTags(false); - this->showLocation(false); - } - - virtual bool enabled() override - { - return LLError::getEnabledLogTypesMask() & 0x08; - } - - virtual void recordMessage(LLError::ELevel level, - const std::string& message) override - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_LOGGING - mBuffer->addLine(message); - } - - private: - LLLineBuffer* mBuffer; - }; - -#if LL_WINDOWS - class RecordToWinDebug: public LLError::Recorder - { - public: - RecordToWinDebug() - { - this->showMultiline(true); - this->showTags(false); - this->showLocation(false); - } - - virtual bool enabled() override - { - return LLError::getEnabledLogTypesMask() & 0x10; - } - - virtual void recordMessage(LLError::ELevel level, - const std::string& message) override - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_LOGGING - debugger_print(message); - } - }; -#endif -} - - -namespace -{ - std::string className(const std::type_info& type) - { - return LLError::Log::demangle(type.name()); - } -} // anonymous - -namespace LLError -{ - std::string Log::demangle(const char* mangled) - { -#ifdef __GNUC__ - // GCC: type_info::name() returns a mangled class name,st demangle - // passing nullptr, 0 forces allocation of a unique buffer we can free - // fixing MAINT-8724 on OSX 10.14 - int status = -1; - char* name = abi::__cxa_demangle(mangled, nullptr, 0, &status); - std::string result(name ? name : mangled); - free(name); - return result; - -#elif LL_WINDOWS - // Visual Studio: type_info::name() includes the text "class " at the start - std::string name = mangled; - for (const auto& prefix : std::vector<std::string>{ "class ", "struct " }) - { - if (0 == name.compare(0, prefix.length(), prefix)) - { - return name.substr(prefix.length()); - } - } - // huh, that's odd, we should see one or the other prefix -- but don't - // try to log unless logging is already initialized - // in Python, " or ".join(vector) -- but in C++, a PITB - LL_DEBUGS() << "Did not see 'class' or 'struct' prefix on '" - << name << "'" << LL_ENDL; - return name; - -#else // neither GCC nor Visual Studio - return mangled; -#endif - } -} // LLError - -namespace -{ - std::string functionName(const std::string& preprocessor_name) - { -#if LL_WINDOWS - // DevStudio: the __FUNCTION__ macro string includes - // the type and/or namespace prefixes - - std::string::size_type p = preprocessor_name.rfind(':'); - if (p == std::string::npos) - { - return preprocessor_name; - } - return preprocessor_name.substr(p + 1); - -#else - return preprocessor_name; -#endif - } - - - class LogControlFile : public LLLiveFile - { - LOG_CLASS(LogControlFile); - - public: - static LogControlFile& fromDirectory(const std::string& user_dir, const std::string& app_dir); - - virtual bool loadFile(); - - private: - LogControlFile(const std::string &filename) - : LLLiveFile(filename) - { } - }; - - LogControlFile& LogControlFile::fromDirectory(const std::string& user_dir, const std::string& app_dir) - { - // NB: We have no abstraction in llcommon for the "proper" - // delimiter but it turns out that "/" works on all three platforms - - std::string file = user_dir + "/logcontrol-dev.xml"; - - llstat stat_info; - if (LLFile::stat(file, &stat_info)) { - // NB: stat returns non-zero if it can't read the file, for example - // if it doesn't exist. LLFile has no better abstraction for - // testing for file existence. - - file = app_dir + "/logcontrol.xml"; - } - return * new LogControlFile(file); - // NB: This instance is never freed - } - - bool LogControlFile::loadFile() - { - LLSD configuration; - - { - llifstream file(filename().c_str()); - if (!file.is_open()) - { - LL_WARNS() << filename() << " failed to open file; not changing configuration" << LL_ENDL; - return false; - } - - if (LLSDSerialize::fromXML(configuration, file) == LLSDParser::PARSE_FAILURE) - { - LL_WARNS() << filename() << " parcing error; not changing configuration" << LL_ENDL; - return false; - } - - if (! configuration || !configuration.isMap()) - { - LL_WARNS() << filename() << " missing, ill-formed, or simply undefined" - " content; not changing configuration" - << LL_ENDL; - return false; - } - } - - LLError::configure(configuration); - LL_INFOS("LogControlFile") << "logging reconfigured from " << filename() << LL_ENDL; - return true; - } - - - typedef std::map<std::string, LLError::ELevel> LevelMap; - typedef std::vector<LLError::RecorderPtr> Recorders; - typedef std::vector<LLError::CallSite*> CallSiteVector; - - class SettingsConfig : public LLRefCount - { - friend class Globals; - - public: - virtual ~SettingsConfig(); - - LLError::ELevel mDefaultLevel; - - bool mLogAlwaysFlush; - - U32 mEnabledLogTypesMask; - - LevelMap mFunctionLevelMap; - LevelMap mClassLevelMap; - LevelMap mFileLevelMap; - LevelMap mTagLevelMap; - std::map<std::string, unsigned int> mUniqueLogMessages; - - LLError::FatalFunction mCrashFunction; - LLError::TimeFunction mTimeFunction; - - Recorders mRecorders; - LLMutex mRecorderMutex; - - int mShouldLogCallCounter; - - private: - SettingsConfig(); - }; - - typedef LLPointer<SettingsConfig> SettingsConfigPtr; - - SettingsConfig::SettingsConfig() - : LLRefCount(), - mDefaultLevel(LLError::LEVEL_DEBUG), - mLogAlwaysFlush(true), - mEnabledLogTypesMask(255), - mFunctionLevelMap(), - mClassLevelMap(), - mFileLevelMap(), - mTagLevelMap(), - mUniqueLogMessages(), - mCrashFunction(NULL), - mTimeFunction(NULL), - mRecorders(), - mRecorderMutex(), - mShouldLogCallCounter(0) - { - } - - SettingsConfig::~SettingsConfig() - { - mRecorders.clear(); - } - - class Globals - { - public: - static Globals* getInstance(); - protected: - Globals(); - public: - std::string mFatalMessage; - - void addCallSite(LLError::CallSite&); - void invalidateCallSites(); - - SettingsConfigPtr getSettingsConfig(); - - void resetSettingsConfig(); - LLError::SettingsStoragePtr saveAndResetSettingsConfig(); - void restore(LLError::SettingsStoragePtr pSettingsStorage); - private: - CallSiteVector callSites; - SettingsConfigPtr mSettingsConfig; - }; - - Globals::Globals() - : - callSites(), - mSettingsConfig(new SettingsConfig()) - { - } - - - Globals* Globals::getInstance() - { - // According to C++11 Function-Local Initialization - // of static variables is supposed to be thread safe - // without risk of deadlocks. - static Globals inst; - - return &inst; - } - - void Globals::addCallSite(LLError::CallSite& site) - { - callSites.push_back(&site); - } - - void Globals::invalidateCallSites() - { - for (LLError::CallSite* site : callSites) - { - site->invalidate(); - } - - callSites.clear(); - } - - SettingsConfigPtr Globals::getSettingsConfig() - { - return mSettingsConfig; - } - - void Globals::resetSettingsConfig() - { - invalidateCallSites(); - mSettingsConfig = new SettingsConfig(); - } - - LLError::SettingsStoragePtr Globals::saveAndResetSettingsConfig() - { - LLError::SettingsStoragePtr oldSettingsConfig(mSettingsConfig.get()); - resetSettingsConfig(); - return oldSettingsConfig; - } - - void Globals::restore(LLError::SettingsStoragePtr pSettingsStorage) - { - invalidateCallSites(); - SettingsConfigPtr newSettingsConfig(dynamic_cast<SettingsConfig *>(pSettingsStorage.get())); - mSettingsConfig = newSettingsConfig; - } -} - -namespace LLError -{ - CallSite::CallSite(ELevel level, - const char* file, - int line, - const std::type_info& class_info, - const char* function, - bool printOnce, - const char** tags, - size_t tag_count) - : mLevel(level), - mFile(file), - mLine(line), - mClassInfo(class_info), - mFunction(function), - mCached(false), - mShouldLog(false), - mPrintOnce(printOnce), - mTags(new const char* [tag_count]), - mTagCount(tag_count) - { - switch (mLevel) - { - case LEVEL_DEBUG: mLevelString = "DEBUG"; break; - case LEVEL_INFO: mLevelString = "INFO"; break; - case LEVEL_WARN: mLevelString = "WARNING"; break; - case LEVEL_ERROR: mLevelString = "ERROR"; break; - default: mLevelString = "XXX"; break; - }; - - mLocationString = llformat("%s(%d)", abbreviateFile(mFile).c_str(), mLine); -#if LL_WINDOWS - // DevStudio: __FUNCTION__ already includes the full class name -#else -#if LL_LINUX - // gross, but typeid comparison seems to always fail here with gcc4.1 - if (0 != strcmp(mClassInfo.name(), typeid(NoClassInfo).name())) -#else - if (mClassInfo != typeid(NoClassInfo)) -#endif // LL_LINUX - { - mFunctionString = className(mClassInfo) + "::"; - } -#endif - mFunctionString += std::string(mFunction); - - for (int i = 0; i < tag_count; i++) - { - if (strchr(tags[i], ' ')) - { - LL_ERRS() << "Space is not allowed in a log tag at " << mLocationString << LL_ENDL; - } - mTags[i] = tags[i]; - } - - mTagString.append("#"); - // always construct a tag sequence; will be just a single # if no tag - for (size_t i = 0; i < mTagCount; i++) - { - mTagString.append(mTags[i]); - mTagString.append("#"); - } - } - - CallSite::~CallSite() - { - delete []mTags; - } - - void CallSite::invalidate() - { - mCached = false; - } -} - -namespace -{ - bool shouldLogToStderr() - { -#if LL_DARWIN - // On macOS, stderr from apps launched from the Finder goes to the - // console log. It's generally considered bad form to spam too much - // there. That scenario can be detected by noticing that stderr is a - // character device (S_IFCHR). - - // If stderr is a tty or a pipe, assume the user launched from the - // command line or debugger and therefore wants to see stderr. - if (isatty(STDERR_FILENO)) - return true; - // not a tty, but might still be a pipe -- check - struct stat st; - if (fstat(STDERR_FILENO, &st) < 0) - { - // capture errno right away, before engaging any other operations - auto errno_save = errno; - // this gets called during log-system setup -- can't log yet! - std::cerr << "shouldLogToStderr: fstat(" << STDERR_FILENO << ") failed, errno " - << errno_save << std::endl; - // if we can't tell, err on the safe side and don't write stderr - return false; - } - - // fstat() worked: return true only if stderr is a pipe - return ((st.st_mode & S_IFMT) == S_IFIFO); -#else - return true; -#endif - } - - bool stderrLogWantsTime() - { -#if LL_WINDOWS - return false; -#else - return true; -#endif - } - - - void commonInit(const std::string& user_dir, const std::string& app_dir, bool log_to_stderr = true) - { - Globals::getInstance()->resetSettingsConfig(); - - LLError::setDefaultLevel(LLError::LEVEL_INFO); - LLError::setAlwaysFlush(true); - LLError::setEnabledLogTypesMask(0xFFFFFFFF); - LLError::setTimeFunction(LLError::utcTime); - - // log_to_stderr is only false in the unit and integration tests to keep builds quieter - if (log_to_stderr && shouldLogToStderr()) - { - LLError::logToStderr(); - } - -#if LL_WINDOWS - LLError::RecorderPtr recordToWinDebug(new RecordToWinDebug()); - LLError::addRecorder(recordToWinDebug); -#endif - - LogControlFile& e = LogControlFile::fromDirectory(user_dir, app_dir); - - // NOTE: We want to explicitly load the file before we add it to the event timer - // that checks for changes to the file. Else, we're not actually loading the file yet, - // and most of the initialization happens without any attention being paid to the - // log control file. Not to mention that when it finally gets checked later, - // all log statements that have been evaluated already become dirty and need to be - // evaluated for printing again. So, make sure to call checkAndReload() - // before addToEventTimer(). - e.checkAndReload(); - e.addToEventTimer(); - } -} - -namespace LLError -{ - void initForApplication(const std::string& user_dir, const std::string& app_dir, bool log_to_stderr) - { - commonInit(user_dir, app_dir, log_to_stderr); - } - - void setFatalFunction(const FatalFunction& f) - { - SettingsConfigPtr s = Globals::getInstance()->getSettingsConfig(); - s->mCrashFunction = f; - } - - FatalFunction getFatalFunction() - { - SettingsConfigPtr s = Globals::getInstance()->getSettingsConfig(); - return s->mCrashFunction; - } - - std::string getFatalMessage() - { - return Globals::getInstance()->mFatalMessage; - } - - void setTimeFunction(TimeFunction f) - { - SettingsConfigPtr s = Globals::getInstance()->getSettingsConfig(); - s->mTimeFunction = f; - } - - void setDefaultLevel(ELevel level) - { - Globals *g = Globals::getInstance(); - g->invalidateCallSites(); - SettingsConfigPtr s = g->getSettingsConfig(); - s->mDefaultLevel = level; - } - - ELevel getDefaultLevel() - { - SettingsConfigPtr s = Globals::getInstance()->getSettingsConfig(); - return s->mDefaultLevel; - } - - void setAlwaysFlush(bool flush) - { - SettingsConfigPtr s = Globals::getInstance()->getSettingsConfig(); - s->mLogAlwaysFlush = flush; - } - - bool getAlwaysFlush() - { - SettingsConfigPtr s = Globals::getInstance()->getSettingsConfig(); - return s->mLogAlwaysFlush; - } - - void setEnabledLogTypesMask(U32 mask) - { - SettingsConfigPtr s = Globals::getInstance()->getSettingsConfig(); - s->mEnabledLogTypesMask = mask; - } - - U32 getEnabledLogTypesMask() - { - SettingsConfigPtr s = Globals::getInstance()->getSettingsConfig(); - return s->mEnabledLogTypesMask; - } - - void setFunctionLevel(const std::string& function_name, ELevel level) - { - Globals *g = Globals::getInstance(); - g->invalidateCallSites(); - SettingsConfigPtr s = g->getSettingsConfig(); - s->mFunctionLevelMap[function_name] = level; - } - - void setClassLevel(const std::string& class_name, ELevel level) - { - Globals *g = Globals::getInstance(); - g->invalidateCallSites(); - SettingsConfigPtr s = g->getSettingsConfig(); - s->mClassLevelMap[class_name] = level; - } - - void setFileLevel(const std::string& file_name, ELevel level) - { - Globals *g = Globals::getInstance(); - g->invalidateCallSites(); - SettingsConfigPtr s = g->getSettingsConfig(); - s->mFileLevelMap[file_name] = level; - } - - void setTagLevel(const std::string& tag_name, ELevel level) - { - Globals *g = Globals::getInstance(); - g->invalidateCallSites(); - SettingsConfigPtr s = g->getSettingsConfig(); - s->mTagLevelMap[tag_name] = level; - } - - LLError::ELevel decodeLevel(std::string name) - { - static LevelMap level_names; - if (level_names.empty()) - { - level_names["ALL"] = LLError::LEVEL_ALL; - level_names["DEBUG"] = LLError::LEVEL_DEBUG; - level_names["INFO"] = LLError::LEVEL_INFO; - level_names["WARN"] = LLError::LEVEL_WARN; - level_names["ERROR"] = LLError::LEVEL_ERROR; - level_names["NONE"] = LLError::LEVEL_NONE; - } - - std::transform(name.begin(), name.end(), name.begin(), toupper); - - LevelMap::const_iterator i = level_names.find(name); - if (i == level_names.end()) - { - LL_WARNS() << "unrecognized logging level: '" << name << "'" << LL_ENDL; - return LLError::LEVEL_INFO; - } - - return i->second; - } -} - -namespace { - void setLevels(LevelMap& map, const LLSD& list, LLError::ELevel level) - { - LLSD::array_const_iterator i, end; - for (i = list.beginArray(), end = list.endArray(); i != end; ++i) - { - map[*i] = level; - } - } -} - -namespace LLError -{ - void configure(const LLSD& config) - { - Globals *g = Globals::getInstance(); - g->invalidateCallSites(); - SettingsConfigPtr s = g->getSettingsConfig(); - - s->mFunctionLevelMap.clear(); - s->mClassLevelMap.clear(); - s->mFileLevelMap.clear(); - s->mTagLevelMap.clear(); - s->mUniqueLogMessages.clear(); - - setDefaultLevel(decodeLevel(config["default-level"])); - if (config.has("log-always-flush")) - { - setAlwaysFlush(config["log-always-flush"]); - } - if (config.has("enabled-log-types-mask")) - { - setEnabledLogTypesMask(config["enabled-log-types-mask"].asInteger()); - } - - if (config.has("settings") && config["settings"].isArray()) - { - LLSD sets = config["settings"]; - LLSD::array_const_iterator a, end; - for (a = sets.beginArray(), end = sets.endArray(); a != end; ++a) - { - const LLSD& entry = *a; - if (entry.isMap() && entry.size() != 0) - { - ELevel level = decodeLevel(entry["level"]); - - setLevels(s->mFunctionLevelMap, entry["functions"], level); - setLevels(s->mClassLevelMap, entry["classes"], level); - setLevels(s->mFileLevelMap, entry["files"], level); - setLevels(s->mTagLevelMap, entry["tags"], level); - } - } - } - } -} - - -namespace LLError -{ - Recorder::Recorder() - : mWantsTime(true) - , mWantsTags(true) - , mWantsLevel(true) - , mWantsLocation(true) - , mWantsFunctionName(true) - , mWantsMultiline(false) - { - } - - Recorder::~Recorder() - { - } - - bool Recorder::wantsTime() - { - return mWantsTime; - } - - // virtual - bool Recorder::wantsTags() - { - return mWantsTags; - } - - // virtual - bool Recorder::wantsLevel() - { - return mWantsLevel; - } - - // virtual - bool Recorder::wantsLocation() - { - return mWantsLocation; - } - - // virtual - bool Recorder::wantsFunctionName() - { - return mWantsFunctionName; - } - - // virtual - bool Recorder::wantsMultiline() - { - return mWantsMultiline; - } - - void Recorder::showTime(bool show) - { - mWantsTime = show; - } - - void Recorder::showTags(bool show) - { - mWantsTags = show; - } - - void Recorder::showLevel(bool show) - { - mWantsLevel = show; - } - - void Recorder::showLocation(bool show) - { - mWantsLocation = show; - } - - void Recorder::showFunctionName(bool show) - { - mWantsFunctionName = show; - } - - void Recorder::showMultiline(bool show) - { - mWantsMultiline = show; - } - - void addRecorder(RecorderPtr recorder) - { - if (!recorder) - { - return; - } - SettingsConfigPtr s = Globals::getInstance()->getSettingsConfig(); - LLMutexLock lock(&s->mRecorderMutex); - s->mRecorders.push_back(recorder); - } - - void removeRecorder(RecorderPtr recorder) - { - if (!recorder) - { - return; - } - SettingsConfigPtr s = Globals::getInstance()->getSettingsConfig(); - LLMutexLock lock(&s->mRecorderMutex); - s->mRecorders.erase(std::remove(s->mRecorders.begin(), s->mRecorders.end(), recorder), - s->mRecorders.end()); - } - - // Find an entry in SettingsConfig::mRecorders whose RecorderPtr points to - // a Recorder subclass of type RECORDER. Return, not a RecorderPtr (which - // points to the Recorder base class), but a shared_ptr<RECORDER> which - // specifically points to the concrete RECORDER subclass instance, along - // with a Recorders::iterator indicating the position of that entry in - // mRecorders. The shared_ptr might be empty (operator!() returns true) if - // there was no such RECORDER subclass instance in mRecorders. - // - // NOTE!!! Requires external mutex lock!!! - template <typename RECORDER> - std::pair<std::shared_ptr<RECORDER>, Recorders::iterator> - findRecorderPos(SettingsConfigPtr &s) - { - // Since we promise to return an iterator, use a classic iterator - // loop. - auto end{s->mRecorders.end()}; - for (Recorders::iterator it{s->mRecorders.begin()}; it != end; ++it) - { - // *it is a RecorderPtr, a shared_ptr<Recorder>. Use a - // dynamic_pointer_cast to try to downcast to test if it's also a - // shared_ptr<RECORDER>. - auto ptr = std::dynamic_pointer_cast<RECORDER>(*it); - if (ptr) - { - // found the entry we want - return { ptr, it }; - } - } - // dropped out of the loop without finding any such entry -- instead - // of default-constructing Recorders::iterator (which might or might - // not be valid), return a value that is valid but not dereferenceable. - return { {}, end }; - } - - // Find an entry in SettingsConfig::mRecorders whose RecorderPtr points to - // a Recorder subclass of type RECORDER. Return, not a RecorderPtr (which - // points to the Recorder base class), but a shared_ptr<RECORDER> which - // specifically points to the concrete RECORDER subclass instance. The - // shared_ptr might be empty (operator!() returns true) if there was no - // such RECORDER subclass instance in mRecorders. - template <typename RECORDER> - std::shared_ptr<RECORDER> findRecorder() - { - SettingsConfigPtr s = Globals::getInstance()->getSettingsConfig(); - LLMutexLock lock(&s->mRecorderMutex); - return findRecorderPos<RECORDER>(s).first; - } - - // Remove an entry from SettingsConfig::mRecorders whose RecorderPtr - // points to a Recorder subclass of type RECORDER. Return true if there - // was one and we removed it, false if there wasn't one to start with. - template <typename RECORDER> - bool removeRecorder() - { - SettingsConfigPtr s = Globals::getInstance()->getSettingsConfig(); - LLMutexLock lock(&s->mRecorderMutex); - auto found = findRecorderPos<RECORDER>(s); - if (found.first) - { - s->mRecorders.erase(found.second); - } - return bool(found.first); - } -} - -namespace LLError -{ - void logToFile(const std::string& file_name) - { - // remove any previous Recorder filling this role - removeRecorder<RecordToFile>(); - - if (!file_name.empty()) - { - std::shared_ptr<RecordToFile> recordToFile(new RecordToFile(file_name)); - if (recordToFile->okay()) - { - addRecorder(recordToFile); - } - } - } - - std::string logFileName() - { - auto found = findRecorder<RecordToFile>(); - return found? found->getFilename() : std::string(); - } - - void logToStderr() - { - if (! findRecorder<RecordToStderr>()) - { - RecorderPtr recordToStdErr(new RecordToStderr(stderrLogWantsTime())); - addRecorder(recordToStdErr); - } - } - - void logToFixedBuffer(LLLineBuffer* fixedBuffer) - { - // remove any previous Recorder filling this role - removeRecorder<RecordToFixedBuffer>(); - - if (fixedBuffer) - { - RecorderPtr recordToFixedBuffer(new RecordToFixedBuffer(fixedBuffer)); - addRecorder(recordToFixedBuffer); - } - } -} - -namespace -{ - std::string escapedMessageLines(const std::string& message) - { - std::ostringstream out; - size_t written_out = 0; - size_t all_content = message.length(); - size_t escape_char_index; // always relative to start of message - // Use find_first_of to find the next character in message that needs escaping - for ( escape_char_index = message.find_first_of("\\\n\r"); - escape_char_index != std::string::npos && written_out < all_content; - // record what we've written this iteration, scan for next char that needs escaping - written_out = escape_char_index + 1, escape_char_index = message.find_first_of("\\\n\r", written_out) - ) - { - // found a character that needs escaping, so write up to that with the escape prefix - // note that escape_char_index is relative to the start, not to the written_out offset - out << message.substr(written_out, escape_char_index - written_out) << '\\'; - - // write out the appropriate second character in the escape sequence - char found = message[escape_char_index]; - switch ( found ) - { - case '\\': - out << '\\'; - break; - case '\n': - out << 'n'; - break; - case '\r': - out << 'r'; - break; - } - } - - if ( written_out < all_content ) // if the loop above didn't write everything - { - // write whatever was left - out << message.substr(written_out, std::string::npos); - } - return out.str(); - } - - void writeToRecorders(const LLError::CallSite& site, const std::string& message) - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_LOGGING - LLError::ELevel level = site.mLevel; - SettingsConfigPtr s = Globals::getInstance()->getSettingsConfig(); - - std::string escaped_message; - - LLMutexLock lock(&s->mRecorderMutex); - for (LLError::RecorderPtr& r : s->mRecorders) - { - if (!r->enabled()) - { - continue; - } - - std::ostringstream message_stream; - - if (r->wantsTime() && s->mTimeFunction != NULL) - { - message_stream << s->mTimeFunction(); - } - message_stream << " "; - - if (r->wantsLevel()) - { - message_stream << site.mLevelString; - } - message_stream << " "; - - if (r->wantsTags()) - { - message_stream << site.mTagString; - } - message_stream << " "; - - if (r->wantsLocation() || level == LLError::LEVEL_ERROR) - { - message_stream << site.mLocationString; - } - message_stream << " "; - - if (r->wantsFunctionName()) - { - message_stream << site.mFunctionString; - } - message_stream << " : "; - - if (r->wantsMultiline()) - { - message_stream << message; - } - else - { - if (escaped_message.empty()) - { - escaped_message = escapedMessageLines(message); - } - message_stream << escaped_message; - } - - r->recordMessage(level, message_stream.str()); - } - } -} - -namespace { - // We need a couple different mutexes, but we want to use the same mechanism - // for both. Make getMutex() a template function with different instances - // for different MutexDiscriminator values. - enum MutexDiscriminator - { - LOG_MUTEX, - STACKS_MUTEX - }; - // Some logging calls happen very early in processing -- so early that our - // module-static variables aren't yet initialized. getMutex() wraps a - // function-static LLMutex so that early calls can still have a valid - // LLMutex instance. - template <MutexDiscriminator MTX> - LLMutex* getMutex() - { - // guaranteed to be initialized the first time control reaches here - static LLMutex sMutex; - return &sMutex; - } - - bool checkLevelMap(const LevelMap& map, const std::string& key, - LLError::ELevel& level) - { - bool stop_checking; - LevelMap::const_iterator i = map.find(key); - if (i == map.end()) - { - return stop_checking = false; - } - - level = i->second; - return stop_checking = true; - } - - bool checkLevelMap( const LevelMap& map, - const char *const * keys, - size_t count, - LLError::ELevel& level) - { - bool found_level = false; - - LLError::ELevel tag_level = LLError::LEVEL_NONE; - - for (size_t i = 0; i < count; i++) - { - LevelMap::const_iterator it = map.find(keys[i]); - if (it != map.end()) - { - found_level = true; - tag_level = llmin(tag_level, it->second); - } - } - - if (found_level) - { - level = tag_level; - } - return found_level; - } -} - -namespace LLError -{ - - bool Log::shouldLog(CallSite& site) - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_LOGGING - LLMutexTrylock lock(getMutex<LOG_MUTEX>(), 5); - if (!lock.isLocked()) - { - return false; - } - - Globals *g = Globals::getInstance(); - SettingsConfigPtr s = g->getSettingsConfig(); - - s->mShouldLogCallCounter++; - - const std::string& class_name = className(site.mClassInfo); - std::string function_name = functionName(site.mFunction); -#if LL_LINUX - // gross, but typeid comparison seems to always fail here with gcc4.1 - if (0 != strcmp(site.mClassInfo.name(), typeid(NoClassInfo).name())) -#else - if (site.mClassInfo != typeid(NoClassInfo)) -#endif // LL_LINUX - { - function_name = class_name + "::" + function_name; - } - - ELevel compareLevel = s->mDefaultLevel; - - // The most specific match found will be used as the log level, - // since the computation short circuits. - // So, in increasing order of importance: - // Default < Tags < File < Class < Function - checkLevelMap(s->mFunctionLevelMap, function_name, compareLevel) - || checkLevelMap(s->mClassLevelMap, class_name, compareLevel) - || checkLevelMap(s->mFileLevelMap, abbreviateFile(site.mFile), compareLevel) - || (site.mTagCount > 0 - ? checkLevelMap(s->mTagLevelMap, site.mTags, site.mTagCount, compareLevel) - : false); - - site.mCached = true; - g->addCallSite(site); - return site.mShouldLog = site.mLevel >= compareLevel; - } - - - void Log::flush(const std::ostringstream& out, const CallSite& site) - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_LOGGING - LLMutexTrylock lock(getMutex<LOG_MUTEX>(),5); - if (!lock.isLocked()) - { - return; - } - - Globals* g = Globals::getInstance(); - SettingsConfigPtr s = g->getSettingsConfig(); - - std::string message = out.str(); - - if (site.mPrintOnce) - { - std::ostringstream message_stream; - - std::map<std::string, unsigned int>::iterator messageIter = s->mUniqueLogMessages.find(message); - if (messageIter != s->mUniqueLogMessages.end()) - { - messageIter->second++; - unsigned int num_messages = messageIter->second; - if (num_messages == 10 || num_messages == 50 || (num_messages % 100) == 0) - { - message_stream << "ONCE (" << num_messages << "th time seen): "; - } - else - { - return; - } - } - else - { - message_stream << "ONCE: "; - s->mUniqueLogMessages[message] = 1; - } - message_stream << message; - message = message_stream.str(); - } - - writeToRecorders(site, message); - - if (site.mLevel == LEVEL_ERROR) - { - g->mFatalMessage = message; - if (s->mCrashFunction) - { - s->mCrashFunction(message); - } - } - } -} - -namespace LLError -{ - SettingsStoragePtr saveAndResetSettings() - { - return Globals::getInstance()->saveAndResetSettingsConfig(); - } - - void restoreSettings(SettingsStoragePtr pSettingsStorage) - { - return Globals::getInstance()->restore(pSettingsStorage); - } - - std::string removePrefix(std::string& s, const std::string& p) - { - std::string::size_type where = s.find(p); - if (where == std::string::npos) - { - return s; - } - - return std::string(s, where + p.size()); - } - - void replaceChar(std::string& s, char old, char replacement) - { - std::string::size_type i = 0; - std::string::size_type len = s.length(); - for ( ; i < len; i++ ) - { - if (s[i] == old) - { - s[i] = replacement; - } - } - } - - std::string abbreviateFile(const std::string& filePath) - { - std::string f = filePath; -#if LL_WINDOWS - replaceChar(f, '\\', '/'); -#endif - static std::string indra_prefix = "indra/"; - f = removePrefix(f, indra_prefix); - -#if LL_DARWIN - static std::string newview_prefix = "newview/../"; - f = removePrefix(f, newview_prefix); -#endif - - return f; - } - - int shouldLogCallCount() - { - SettingsConfigPtr s = Globals::getInstance()->getSettingsConfig(); - return s->mShouldLogCallCounter; - } - - std::string utcTime() - { - time_t now = time(NULL); - const size_t BUF_SIZE = 64; - char time_str[BUF_SIZE]; /* Flawfinder: ignore */ - - auto chars = strftime(time_str, BUF_SIZE, - "%Y-%m-%dT%H:%M:%SZ", - gmtime(&now)); - - return chars ? time_str : "time error"; - } -} - -namespace LLError -{ - LLCallStacks::StringVector LLCallStacks::sBuffer ; - - //static - void LLCallStacks::push(const char* function, const int line) - { - LLMutexTrylock lock(getMutex<STACKS_MUTEX>(), 5); - if (!lock.isLocked()) - { - return; - } - - if(sBuffer.size() > 511) - { - clear() ; - } - - std::ostringstream out; - insert(out, function, line); - sBuffer.push_back(out.str()); - } - - //static - void LLCallStacks::insert(std::ostream& out, const char* function, const int line) - { - out << function << " line " << line << " " ; - } - - //static - void LLCallStacks::end(const std::ostringstream& out) - { - LLMutexTrylock lock(getMutex<STACKS_MUTEX>(), 5); - if (!lock.isLocked()) - { - return; - } - - if(sBuffer.size() > 511) - { - clear() ; - } - - sBuffer.push_back(out.str()); - } - - //static - void LLCallStacks::print() - { - LLMutexTrylock lock(getMutex<STACKS_MUTEX>(), 5); - if (!lock.isLocked()) - { - return; - } - - if(! sBuffer.empty()) - { - LL_INFOS() << " ************* PRINT OUT LL CALL STACKS ************* " << LL_ENDL; - for (StringVector::const_reverse_iterator ri(sBuffer.rbegin()), re(sBuffer.rend()); - ri != re; ++ri) - { - LL_INFOS() << (*ri) << LL_ENDL; - } - LL_INFOS() << " *************** END OF LL CALL STACKS *************** " << LL_ENDL; - } - - cleanup(); - } - - //static - void LLCallStacks::clear() - { - sBuffer.clear(); - } - - //static - void LLCallStacks::cleanup() - { - clear(); - } - - std::ostream& operator<<(std::ostream& out, const LLStacktrace&) - { - return out << boost::stacktrace::stacktrace(); - } - - // LLOutOfMemoryWarning - std::string LLUserWarningMsg::sLocalizedOutOfMemoryTitle; - std::string LLUserWarningMsg::sLocalizedOutOfMemoryWarning; - LLUserWarningMsg::Handler LLUserWarningMsg::sHandler; - - void LLUserWarningMsg::show(const std::string& message) - { - if (sHandler) - { - sHandler(std::string(), message); - } - } - - void LLUserWarningMsg::showOutOfMemory() - { - if (sHandler && !sLocalizedOutOfMemoryTitle.empty()) - { - sHandler(sLocalizedOutOfMemoryTitle, sLocalizedOutOfMemoryWarning); - } - } - - void LLUserWarningMsg::showMissingFiles() - { - // Files Are missing, likely can't localize. - const std::string error_string = - "Second Life viewer couldn't access some of the files it needs and will be closed." - "\n\nPlease reinstall viewer from https://secondlife.com/support/downloads/ and " - "contact https://support.secondlife.com if issue persists after reinstall."; - sHandler("Missing Files", error_string); - } - - void LLUserWarningMsg::setHandler(const LLUserWarningMsg::Handler &handler) - { - sHandler = handler; - } - - void LLUserWarningMsg::setOutOfMemoryStrings(const std::string& title, const std::string& message) - { - sLocalizedOutOfMemoryTitle = title; - sLocalizedOutOfMemoryWarning = message; - } -} - -void crashdriver(void (*callback)(int*)) -{ - // The LLERROR_CRASH macro used to have inline code of the form: - //int* make_me_crash = NULL; - //*make_me_crash = 0; - - // But compilers are getting smart enough to recognize that, so we must - // assign to an address supplied by a separate source file. We could do - // the assignment here in crashdriver() -- but then BugSplat would group - // all LL_ERRS() crashes as the fault of this one function, instead of - // identifying the specific LL_ERRS() source line. So instead, do the - // assignment in a lambda in the caller's source. We just provide the - // nullptr target. - callback(nullptr); -} +/**
+ * @file llerror.cpp
+ * @date December 2006
+ * @brief error message system
+ *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llerror.h"
+#include "llerrorcontrol.h"
+#include "llsdutil.h"
+
+#include <cctype>
+#ifdef __GNUC__
+# include <cxxabi.h>
+#endif // __GNUC__
+#include <sstream>
+#if !LL_WINDOWS
+# include <syslog.h>
+# include <unistd.h>
+# include <sys/stat.h>
+#else
+# include <io.h>
+#endif // !LL_WINDOWS
+#include <vector>
+#include "string.h"
+
+#include "llapp.h"
+#include "llapr.h"
+#include "llfile.h"
+#include "lllivefile.h"
+#include "llsd.h"
+#include "llsdserialize.h"
+#include "llsingleton.h"
+#include "llstl.h"
+#include "lltimer.h"
+
+// On Mac, got:
+// #error "Boost.Stacktrace requires `_Unwind_Backtrace` function. Define
+// `_GNU_SOURCE` macro or `BOOST_STACKTRACE_GNU_SOURCE_NOT_REQUIRED` if
+// _Unwind_Backtrace is available without `_GNU_SOURCE`."
+#define BOOST_STACKTRACE_GNU_SOURCE_NOT_REQUIRED
+#include <boost/stacktrace.hpp>
+
+namespace {
+#if LL_WINDOWS
+ void debugger_print(const std::string& s)
+ {
+ // Be careful when calling OutputDebugString as it throws DBG_PRINTEXCEPTION_C
+ // which works just fine under the windows debugger, but can cause users who
+ // have enabled SEHOP exception chain validation to crash due to interactions
+ // between the Win 32-bit exception handling and boost coroutine fiber stacks. BUG-2707
+ //
+ if (IsDebuggerPresent())
+ {
+ // Need UTF16 for Unicode OutputDebugString
+ //
+ if (s.size())
+ {
+ OutputDebugString(utf8str_to_utf16str(s).c_str());
+ OutputDebugString(TEXT("\n"));
+ }
+ }
+ }
+#else
+ class RecordToSyslog : public LLError::Recorder
+ {
+ public:
+ RecordToSyslog(const std::string& identity)
+ : mIdentity(identity)
+ {
+ openlog(mIdentity.c_str(), LOG_CONS|LOG_PID, LOG_LOCAL0);
+ // we need to set the string from a local copy of the string
+ // since apparanetly openlog expects the const char* to remain
+ // valid even after it returns (presumably until closelog)
+ }
+
+ ~RecordToSyslog()
+ {
+ closelog();
+ }
+
+ virtual bool enabled() override
+ {
+ return LLError::getEnabledLogTypesMask() & 0x01;
+ }
+
+ virtual void recordMessage(LLError::ELevel level,
+ const std::string& message) override
+ {
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_LOGGING
+ int syslogPriority = LOG_CRIT;
+ switch (level) {
+ case LLError::LEVEL_DEBUG: syslogPriority = LOG_DEBUG; break;
+ case LLError::LEVEL_INFO: syslogPriority = LOG_INFO; break;
+ case LLError::LEVEL_WARN: syslogPriority = LOG_WARNING; break;
+ case LLError::LEVEL_ERROR: syslogPriority = LOG_CRIT; break;
+ default: syslogPriority = LOG_CRIT;
+ }
+
+ syslog(syslogPriority, "%s", message.c_str());
+ }
+ private:
+ std::string mIdentity;
+ };
+#endif
+
+ class RecordToFile : public LLError::Recorder
+ {
+ public:
+ RecordToFile(const std::string& filename):
+ mName(filename)
+ {
+ mFile.open(filename.c_str(), std::ios_base::out | std::ios_base::app);
+ if (!mFile)
+ {
+ LL_INFOS() << "Error setting log file to " << filename << LL_ENDL;
+ }
+ else
+ {
+ if (!LLError::getAlwaysFlush())
+ {
+ mFile.sync_with_stdio(false);
+ }
+ }
+ }
+
+ ~RecordToFile()
+ {
+ mFile.close();
+ }
+
+ virtual bool enabled() override
+ {
+#ifdef LL_RELEASE_FOR_DOWNLOAD
+ return 1;
+#else
+ return LLError::getEnabledLogTypesMask() & 0x02;
+#endif
+ }
+
+ bool okay() const { return mFile.good(); }
+
+ std::string getFilename() const { return mName; }
+
+ virtual void recordMessage(LLError::ELevel level,
+ const std::string& message) override
+ {
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_LOGGING
+ if (LLError::getAlwaysFlush())
+ {
+ mFile << message << std::endl;
+ }
+ else
+ {
+ mFile << message << "\n";
+ }
+ }
+
+ private:
+ const std::string mName;
+ llofstream mFile;
+ };
+
+
+ class RecordToStderr : public LLError::Recorder
+ {
+ public:
+ RecordToStderr(bool timestamp) : mUseANSI(checkANSI())
+ {
+ this->showMultiline(true);
+ }
+
+ virtual bool enabled() override
+ {
+ return LLError::getEnabledLogTypesMask() & 0x04;
+ }
+
+ LL_FORCE_INLINE std::string createBoldANSI()
+ {
+ std::string ansi_code;
+ ansi_code += '\033';
+ ansi_code += "[";
+ ansi_code += "1";
+ ansi_code += "m";
+
+ return ansi_code;
+ }
+
+ LL_FORCE_INLINE std::string createResetANSI()
+ {
+ std::string ansi_code;
+ ansi_code += '\033';
+ ansi_code += "[";
+ ansi_code += "0";
+ ansi_code += "m";
+
+ return ansi_code;
+ }
+
+ LL_FORCE_INLINE std::string createANSI(const std::string& color)
+ {
+ std::string ansi_code;
+ ansi_code += '\033';
+ ansi_code += "[";
+ ansi_code += "38;5;";
+ ansi_code += color;
+ ansi_code += "m";
+
+ return ansi_code;
+ }
+
+ virtual void recordMessage(LLError::ELevel level,
+ const std::string& message) override
+ {
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_LOGGING
+ // The default colors for error, warn and debug are now a bit more pastel
+ // and easier to read on the default (black) terminal background but you
+ // now have the option to set the color of each via an environment variables:
+ // LL_ANSI_ERROR_COLOR_CODE (default is red)
+ // LL_ANSI_WARN_COLOR_CODE (default is blue)
+ // LL_ANSI_DEBUG_COLOR_CODE (default is magenta)
+ // The list of color codes can be found in many places but I used this page:
+ // https://www.lihaoyi.com/post/BuildyourownCommandLinewithANSIescapecodes.html#256-colors
+ // (Note: you may need to restart Visual Studio to pick environment changes)
+ char* val = nullptr;
+ std::string s_ansi_error_code = "160";
+ if ((val = getenv("LL_ANSI_ERROR_COLOR_CODE")) != nullptr) s_ansi_error_code = std::string(val);
+ std::string s_ansi_warn_code = "33";
+ if ((val = getenv("LL_ANSI_WARN_COLOR_CODE")) != nullptr) s_ansi_warn_code = std::string(val);
+ std::string s_ansi_debug_code = "177";
+ if ((val = getenv("LL_ANSI_DEBUG_COLOR_CODE")) != nullptr) s_ansi_debug_code = std::string(val);
+
+ static std::string s_ansi_error = createANSI(s_ansi_error_code); // default is red
+ static std::string s_ansi_warn = createANSI(s_ansi_warn_code); // default is blue
+ static std::string s_ansi_debug = createANSI(s_ansi_debug_code); // default is magenta
+
+ if (mUseANSI)
+ {
+ writeANSI((level == LLError::LEVEL_ERROR) ? s_ansi_error :
+ (level == LLError::LEVEL_WARN) ? s_ansi_warn :
+ s_ansi_debug, message);
+ }
+ else
+ {
+ LL_PROFILE_ZONE_NAMED("fprintf");
+ fprintf(stderr, "%s\n", message.c_str());
+ }
+ }
+
+ private:
+ bool mUseANSI;
+
+ LL_FORCE_INLINE void writeANSI(const std::string& ansi_code, const std::string& message)
+ {
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_LOGGING
+ static std::string s_ansi_bold = createBoldANSI(); // bold text
+ static std::string s_ansi_reset = createResetANSI(); // reset
+ // ANSI color code escape sequence, message, and reset in one fprintf call
+ // Default all message levels to bold so we can distinguish our own messages from those dumped by subprocesses and libraries.
+ fprintf(stderr, "%s%s\n%s", ansi_code.c_str(), message.c_str(), s_ansi_reset.c_str() );
+ }
+
+ static bool checkANSI(void)
+ {
+ // Check whether it's okay to use ANSI; if stderr is
+ // a tty then we assume yes. Can be turned off with
+ // the LL_NO_ANSI_COLOR env var.
+ return (0 != isatty(2)) &&
+ (NULL == getenv("LL_NO_ANSI_COLOR"));
+ }
+ };
+
+ class RecordToFixedBuffer : public LLError::Recorder
+ {
+ public:
+ RecordToFixedBuffer(LLLineBuffer* buffer)
+ : mBuffer(buffer)
+ {
+ this->showMultiline(true);
+ this->showTags(false);
+ this->showLocation(false);
+ }
+
+ virtual bool enabled() override
+ {
+ return LLError::getEnabledLogTypesMask() & 0x08;
+ }
+
+ virtual void recordMessage(LLError::ELevel level,
+ const std::string& message) override
+ {
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_LOGGING
+ mBuffer->addLine(message);
+ }
+
+ private:
+ LLLineBuffer* mBuffer;
+ };
+
+#if LL_WINDOWS
+ class RecordToWinDebug: public LLError::Recorder
+ {
+ public:
+ RecordToWinDebug()
+ {
+ this->showMultiline(true);
+ this->showTags(false);
+ this->showLocation(false);
+ }
+
+ virtual bool enabled() override
+ {
+ return LLError::getEnabledLogTypesMask() & 0x10;
+ }
+
+ virtual void recordMessage(LLError::ELevel level,
+ const std::string& message) override
+ {
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_LOGGING
+ debugger_print(message);
+ }
+ };
+#endif
+}
+
+
+namespace
+{
+ std::string className(const std::type_info& type)
+ {
+ return LLError::Log::demangle(type.name());
+ }
+} // anonymous
+
+namespace LLError
+{
+ std::string Log::demangle(const char* mangled)
+ {
+#ifdef __GNUC__
+ // GCC: type_info::name() returns a mangled class name,st demangle
+ // passing nullptr, 0 forces allocation of a unique buffer we can free
+ // fixing MAINT-8724 on OSX 10.14
+ int status = -1;
+ char* name = abi::__cxa_demangle(mangled, nullptr, 0, &status);
+ std::string result(name ? name : mangled);
+ free(name);
+ return result;
+
+#elif LL_WINDOWS
+ // Visual Studio: type_info::name() includes the text "class " at the start
+ std::string name = mangled;
+ for (const auto& prefix : std::vector<std::string>{ "class ", "struct " })
+ {
+ if (0 == name.compare(0, prefix.length(), prefix))
+ {
+ return name.substr(prefix.length());
+ }
+ }
+ // huh, that's odd, we should see one or the other prefix -- but don't
+ // try to log unless logging is already initialized
+ // in Python, " or ".join(vector) -- but in C++, a PITB
+ LL_DEBUGS() << "Did not see 'class' or 'struct' prefix on '"
+ << name << "'" << LL_ENDL;
+ return name;
+
+#else // neither GCC nor Visual Studio
+ return mangled;
+#endif
+ }
+} // LLError
+
+namespace
+{
+ std::string functionName(const std::string& preprocessor_name)
+ {
+#if LL_WINDOWS
+ // DevStudio: the __FUNCTION__ macro string includes
+ // the type and/or namespace prefixes
+
+ std::string::size_type p = preprocessor_name.rfind(':');
+ if (p == std::string::npos)
+ {
+ return preprocessor_name;
+ }
+ return preprocessor_name.substr(p + 1);
+
+#else
+ return preprocessor_name;
+#endif
+ }
+
+
+ class LogControlFile : public LLLiveFile
+ {
+ LOG_CLASS(LogControlFile);
+
+ public:
+ static LogControlFile& fromDirectory(const std::string& user_dir, const std::string& app_dir);
+
+ virtual bool loadFile();
+
+ private:
+ LogControlFile(const std::string &filename)
+ : LLLiveFile(filename)
+ { }
+ };
+
+ LogControlFile& LogControlFile::fromDirectory(const std::string& user_dir, const std::string& app_dir)
+ {
+ // NB: We have no abstraction in llcommon for the "proper"
+ // delimiter but it turns out that "/" works on all three platforms
+
+ std::string file = user_dir + "/logcontrol-dev.xml";
+
+ llstat stat_info;
+ if (LLFile::stat(file, &stat_info)) {
+ // NB: stat returns non-zero if it can't read the file, for example
+ // if it doesn't exist. LLFile has no better abstraction for
+ // testing for file existence.
+
+ file = app_dir + "/logcontrol.xml";
+ }
+ return * new LogControlFile(file);
+ // NB: This instance is never freed
+ }
+
+ bool LogControlFile::loadFile()
+ {
+ LLSD configuration;
+
+ {
+ llifstream file(filename().c_str());
+ if (!file.is_open())
+ {
+ LL_WARNS() << filename() << " failed to open file; not changing configuration" << LL_ENDL;
+ return false;
+ }
+
+ if (LLSDSerialize::fromXML(configuration, file) == LLSDParser::PARSE_FAILURE)
+ {
+ LL_WARNS() << filename() << " parcing error; not changing configuration" << LL_ENDL;
+ return false;
+ }
+
+ if (! configuration || !configuration.isMap())
+ {
+ LL_WARNS() << filename() << " missing, ill-formed, or simply undefined"
+ " content; not changing configuration"
+ << LL_ENDL;
+ return false;
+ }
+ }
+
+ LLError::configure(configuration);
+ LL_INFOS("LogControlFile") << "logging reconfigured from " << filename() << LL_ENDL;
+ return true;
+ }
+
+
+ typedef std::map<std::string, LLError::ELevel> LevelMap;
+ typedef std::vector<LLError::RecorderPtr> Recorders;
+ typedef std::vector<LLError::CallSite*> CallSiteVector;
+
+ class SettingsConfig : public LLRefCount
+ {
+ friend class Globals;
+
+ public:
+ virtual ~SettingsConfig();
+
+ LLError::ELevel mDefaultLevel;
+
+ bool mLogAlwaysFlush;
+
+ U32 mEnabledLogTypesMask;
+
+ LevelMap mFunctionLevelMap;
+ LevelMap mClassLevelMap;
+ LevelMap mFileLevelMap;
+ LevelMap mTagLevelMap;
+ std::map<std::string, unsigned int> mUniqueLogMessages;
+
+ LLError::FatalFunction mCrashFunction;
+ LLError::TimeFunction mTimeFunction;
+
+ Recorders mRecorders;
+ LLMutex mRecorderMutex;
+
+ int mShouldLogCallCounter;
+
+ private:
+ SettingsConfig();
+ };
+
+ typedef LLPointer<SettingsConfig> SettingsConfigPtr;
+
+ SettingsConfig::SettingsConfig()
+ : LLRefCount(),
+ mDefaultLevel(LLError::LEVEL_DEBUG),
+ mLogAlwaysFlush(true),
+ mEnabledLogTypesMask(255),
+ mFunctionLevelMap(),
+ mClassLevelMap(),
+ mFileLevelMap(),
+ mTagLevelMap(),
+ mUniqueLogMessages(),
+ mCrashFunction(NULL),
+ mTimeFunction(NULL),
+ mRecorders(),
+ mRecorderMutex(),
+ mShouldLogCallCounter(0)
+ {
+ }
+
+ SettingsConfig::~SettingsConfig()
+ {
+ mRecorders.clear();
+ }
+
+ class Globals
+ {
+ public:
+ static Globals* getInstance();
+ protected:
+ Globals();
+ public:
+ std::string mFatalMessage;
+
+ void addCallSite(LLError::CallSite&);
+ void invalidateCallSites();
+
+ SettingsConfigPtr getSettingsConfig();
+
+ void resetSettingsConfig();
+ LLError::SettingsStoragePtr saveAndResetSettingsConfig();
+ void restore(LLError::SettingsStoragePtr pSettingsStorage);
+ private:
+ CallSiteVector callSites;
+ SettingsConfigPtr mSettingsConfig;
+ };
+
+ Globals::Globals()
+ :
+ callSites(),
+ mSettingsConfig(new SettingsConfig())
+ {
+ }
+
+
+ Globals* Globals::getInstance()
+ {
+ // According to C++11 Function-Local Initialization
+ // of static variables is supposed to be thread safe
+ // without risk of deadlocks.
+ static Globals inst;
+
+ return &inst;
+ }
+
+ void Globals::addCallSite(LLError::CallSite& site)
+ {
+ callSites.push_back(&site);
+ }
+
+ void Globals::invalidateCallSites()
+ {
+ for (LLError::CallSite* site : callSites)
+ {
+ site->invalidate();
+ }
+
+ callSites.clear();
+ }
+
+ SettingsConfigPtr Globals::getSettingsConfig()
+ {
+ return mSettingsConfig;
+ }
+
+ void Globals::resetSettingsConfig()
+ {
+ invalidateCallSites();
+ mSettingsConfig = new SettingsConfig();
+ }
+
+ LLError::SettingsStoragePtr Globals::saveAndResetSettingsConfig()
+ {
+ LLError::SettingsStoragePtr oldSettingsConfig(mSettingsConfig.get());
+ resetSettingsConfig();
+ return oldSettingsConfig;
+ }
+
+ void Globals::restore(LLError::SettingsStoragePtr pSettingsStorage)
+ {
+ invalidateCallSites();
+ SettingsConfigPtr newSettingsConfig(dynamic_cast<SettingsConfig *>(pSettingsStorage.get()));
+ mSettingsConfig = newSettingsConfig;
+ }
+}
+
+namespace LLError
+{
+ CallSite::CallSite(ELevel level,
+ const char* file,
+ int line,
+ const std::type_info& class_info,
+ const char* function,
+ bool printOnce,
+ const char** tags,
+ size_t tag_count)
+ : mLevel(level),
+ mFile(file),
+ mLine(line),
+ mClassInfo(class_info),
+ mFunction(function),
+ mCached(false),
+ mShouldLog(false),
+ mPrintOnce(printOnce),
+ mTags(new const char* [tag_count]),
+ mTagCount(tag_count)
+ {
+ switch (mLevel)
+ {
+ case LEVEL_DEBUG: mLevelString = "DEBUG"; break;
+ case LEVEL_INFO: mLevelString = "INFO"; break;
+ case LEVEL_WARN: mLevelString = "WARNING"; break;
+ case LEVEL_ERROR: mLevelString = "ERROR"; break;
+ default: mLevelString = "XXX"; break;
+ };
+
+ mLocationString = llformat("%s(%d)", abbreviateFile(mFile).c_str(), mLine);
+#if LL_WINDOWS
+ // DevStudio: __FUNCTION__ already includes the full class name
+#else
+#if LL_LINUX
+ // gross, but typeid comparison seems to always fail here with gcc4.1
+ if (0 != strcmp(mClassInfo.name(), typeid(NoClassInfo).name()))
+#else
+ if (mClassInfo != typeid(NoClassInfo))
+#endif // LL_LINUX
+ {
+ mFunctionString = className(mClassInfo) + "::";
+ }
+#endif
+ mFunctionString += std::string(mFunction);
+
+ for (int i = 0; i < tag_count; i++)
+ {
+ if (strchr(tags[i], ' '))
+ {
+ LL_ERRS() << "Space is not allowed in a log tag at " << mLocationString << LL_ENDL;
+ }
+ mTags[i] = tags[i];
+ }
+
+ mTagString.append("#");
+ // always construct a tag sequence; will be just a single # if no tag
+ for (size_t i = 0; i < mTagCount; i++)
+ {
+ mTagString.append(mTags[i]);
+ mTagString.append("#");
+ }
+ }
+
+ CallSite::~CallSite()
+ {
+ delete []mTags;
+ }
+
+ void CallSite::invalidate()
+ {
+ mCached = false;
+ }
+}
+
+namespace
+{
+ bool shouldLogToStderr()
+ {
+#if LL_DARWIN
+ // On macOS, stderr from apps launched from the Finder goes to the
+ // console log. It's generally considered bad form to spam too much
+ // there. That scenario can be detected by noticing that stderr is a
+ // character device (S_IFCHR).
+
+ // If stderr is a tty or a pipe, assume the user launched from the
+ // command line or debugger and therefore wants to see stderr.
+ if (isatty(STDERR_FILENO))
+ return true;
+ // not a tty, but might still be a pipe -- check
+ struct stat st;
+ if (fstat(STDERR_FILENO, &st) < 0)
+ {
+ // capture errno right away, before engaging any other operations
+ auto errno_save = errno;
+ // this gets called during log-system setup -- can't log yet!
+ std::cerr << "shouldLogToStderr: fstat(" << STDERR_FILENO << ") failed, errno "
+ << errno_save << std::endl;
+ // if we can't tell, err on the safe side and don't write stderr
+ return false;
+ }
+
+ // fstat() worked: return true only if stderr is a pipe
+ return ((st.st_mode & S_IFMT) == S_IFIFO);
+#else
+ return true;
+#endif
+ }
+
+ bool stderrLogWantsTime()
+ {
+#if LL_WINDOWS
+ return false;
+#else
+ return true;
+#endif
+ }
+
+
+ void commonInit(const std::string& user_dir, const std::string& app_dir, bool log_to_stderr = true)
+ {
+ Globals::getInstance()->resetSettingsConfig();
+
+ LLError::setDefaultLevel(LLError::LEVEL_INFO);
+ LLError::setAlwaysFlush(true);
+ LLError::setEnabledLogTypesMask(0xFFFFFFFF);
+ LLError::setTimeFunction(LLError::utcTime);
+
+ // log_to_stderr is only false in the unit and integration tests to keep builds quieter
+ if (log_to_stderr && shouldLogToStderr())
+ {
+ LLError::logToStderr();
+ }
+
+#if LL_WINDOWS
+ LLError::RecorderPtr recordToWinDebug(new RecordToWinDebug());
+ LLError::addRecorder(recordToWinDebug);
+#endif
+
+ LogControlFile& e = LogControlFile::fromDirectory(user_dir, app_dir);
+
+ // NOTE: We want to explicitly load the file before we add it to the event timer
+ // that checks for changes to the file. Else, we're not actually loading the file yet,
+ // and most of the initialization happens without any attention being paid to the
+ // log control file. Not to mention that when it finally gets checked later,
+ // all log statements that have been evaluated already become dirty and need to be
+ // evaluated for printing again. So, make sure to call checkAndReload()
+ // before addToEventTimer().
+ e.checkAndReload();
+ e.addToEventTimer();
+ }
+}
+
+namespace LLError
+{
+ void initForApplication(const std::string& user_dir, const std::string& app_dir, bool log_to_stderr)
+ {
+ commonInit(user_dir, app_dir, log_to_stderr);
+ }
+
+ void setFatalFunction(const FatalFunction& f)
+ {
+ SettingsConfigPtr s = Globals::getInstance()->getSettingsConfig();
+ s->mCrashFunction = f;
+ }
+
+ FatalFunction getFatalFunction()
+ {
+ SettingsConfigPtr s = Globals::getInstance()->getSettingsConfig();
+ return s->mCrashFunction;
+ }
+
+ std::string getFatalMessage()
+ {
+ return Globals::getInstance()->mFatalMessage;
+ }
+
+ void setTimeFunction(TimeFunction f)
+ {
+ SettingsConfigPtr s = Globals::getInstance()->getSettingsConfig();
+ s->mTimeFunction = f;
+ }
+
+ void setDefaultLevel(ELevel level)
+ {
+ Globals *g = Globals::getInstance();
+ g->invalidateCallSites();
+ SettingsConfigPtr s = g->getSettingsConfig();
+ s->mDefaultLevel = level;
+ }
+
+ ELevel getDefaultLevel()
+ {
+ SettingsConfigPtr s = Globals::getInstance()->getSettingsConfig();
+ return s->mDefaultLevel;
+ }
+
+ void setAlwaysFlush(bool flush)
+ {
+ SettingsConfigPtr s = Globals::getInstance()->getSettingsConfig();
+ s->mLogAlwaysFlush = flush;
+ }
+
+ bool getAlwaysFlush()
+ {
+ SettingsConfigPtr s = Globals::getInstance()->getSettingsConfig();
+ return s->mLogAlwaysFlush;
+ }
+
+ void setEnabledLogTypesMask(U32 mask)
+ {
+ SettingsConfigPtr s = Globals::getInstance()->getSettingsConfig();
+ s->mEnabledLogTypesMask = mask;
+ }
+
+ U32 getEnabledLogTypesMask()
+ {
+ SettingsConfigPtr s = Globals::getInstance()->getSettingsConfig();
+ return s->mEnabledLogTypesMask;
+ }
+
+ void setFunctionLevel(const std::string& function_name, ELevel level)
+ {
+ Globals *g = Globals::getInstance();
+ g->invalidateCallSites();
+ SettingsConfigPtr s = g->getSettingsConfig();
+ s->mFunctionLevelMap[function_name] = level;
+ }
+
+ void setClassLevel(const std::string& class_name, ELevel level)
+ {
+ Globals *g = Globals::getInstance();
+ g->invalidateCallSites();
+ SettingsConfigPtr s = g->getSettingsConfig();
+ s->mClassLevelMap[class_name] = level;
+ }
+
+ void setFileLevel(const std::string& file_name, ELevel level)
+ {
+ Globals *g = Globals::getInstance();
+ g->invalidateCallSites();
+ SettingsConfigPtr s = g->getSettingsConfig();
+ s->mFileLevelMap[file_name] = level;
+ }
+
+ void setTagLevel(const std::string& tag_name, ELevel level)
+ {
+ Globals *g = Globals::getInstance();
+ g->invalidateCallSites();
+ SettingsConfigPtr s = g->getSettingsConfig();
+ s->mTagLevelMap[tag_name] = level;
+ }
+
+ LLError::ELevel decodeLevel(std::string name)
+ {
+ static LevelMap level_names;
+ if (level_names.empty())
+ {
+ level_names["ALL"] = LLError::LEVEL_ALL;
+ level_names["DEBUG"] = LLError::LEVEL_DEBUG;
+ level_names["INFO"] = LLError::LEVEL_INFO;
+ level_names["WARN"] = LLError::LEVEL_WARN;
+ level_names["ERROR"] = LLError::LEVEL_ERROR;
+ level_names["NONE"] = LLError::LEVEL_NONE;
+ }
+
+ std::transform(name.begin(), name.end(), name.begin(), toupper);
+
+ LevelMap::const_iterator i = level_names.find(name);
+ if (i == level_names.end())
+ {
+ LL_WARNS() << "unrecognized logging level: '" << name << "'" << LL_ENDL;
+ return LLError::LEVEL_INFO;
+ }
+
+ return i->second;
+ }
+}
+
+namespace {
+ void setLevels(LevelMap& map, const LLSD& list, LLError::ELevel level)
+ {
+ LLSD::array_const_iterator i, end;
+ for (i = list.beginArray(), end = list.endArray(); i != end; ++i)
+ {
+ map[*i] = level;
+ }
+ }
+}
+
+namespace LLError
+{
+ void configure(const LLSD& config)
+ {
+ Globals *g = Globals::getInstance();
+ g->invalidateCallSites();
+ SettingsConfigPtr s = g->getSettingsConfig();
+
+ s->mFunctionLevelMap.clear();
+ s->mClassLevelMap.clear();
+ s->mFileLevelMap.clear();
+ s->mTagLevelMap.clear();
+ s->mUniqueLogMessages.clear();
+
+ setDefaultLevel(decodeLevel(config["default-level"]));
+ if (config.has("log-always-flush"))
+ {
+ setAlwaysFlush(config["log-always-flush"]);
+ }
+ if (config.has("enabled-log-types-mask"))
+ {
+ setEnabledLogTypesMask(config["enabled-log-types-mask"].asInteger());
+ }
+
+ if (config.has("settings") && config["settings"].isArray())
+ {
+ LLSD sets = config["settings"];
+ LLSD::array_const_iterator a, end;
+ for (a = sets.beginArray(), end = sets.endArray(); a != end; ++a)
+ {
+ const LLSD& entry = *a;
+ if (entry.isMap() && entry.size() != 0)
+ {
+ ELevel level = decodeLevel(entry["level"]);
+
+ setLevels(s->mFunctionLevelMap, entry["functions"], level);
+ setLevels(s->mClassLevelMap, entry["classes"], level);
+ setLevels(s->mFileLevelMap, entry["files"], level);
+ setLevels(s->mTagLevelMap, entry["tags"], level);
+ }
+ }
+ }
+ }
+}
+
+
+namespace LLError
+{
+ Recorder::Recorder()
+ : mWantsTime(true)
+ , mWantsTags(true)
+ , mWantsLevel(true)
+ , mWantsLocation(true)
+ , mWantsFunctionName(true)
+ , mWantsMultiline(false)
+ {
+ }
+
+ Recorder::~Recorder()
+ {
+ }
+
+ bool Recorder::wantsTime()
+ {
+ return mWantsTime;
+ }
+
+ // virtual
+ bool Recorder::wantsTags()
+ {
+ return mWantsTags;
+ }
+
+ // virtual
+ bool Recorder::wantsLevel()
+ {
+ return mWantsLevel;
+ }
+
+ // virtual
+ bool Recorder::wantsLocation()
+ {
+ return mWantsLocation;
+ }
+
+ // virtual
+ bool Recorder::wantsFunctionName()
+ {
+ return mWantsFunctionName;
+ }
+
+ // virtual
+ bool Recorder::wantsMultiline()
+ {
+ return mWantsMultiline;
+ }
+
+ void Recorder::showTime(bool show)
+ {
+ mWantsTime = show;
+ }
+
+ void Recorder::showTags(bool show)
+ {
+ mWantsTags = show;
+ }
+
+ void Recorder::showLevel(bool show)
+ {
+ mWantsLevel = show;
+ }
+
+ void Recorder::showLocation(bool show)
+ {
+ mWantsLocation = show;
+ }
+
+ void Recorder::showFunctionName(bool show)
+ {
+ mWantsFunctionName = show;
+ }
+
+ void Recorder::showMultiline(bool show)
+ {
+ mWantsMultiline = show;
+ }
+
+ void addRecorder(RecorderPtr recorder)
+ {
+ if (!recorder)
+ {
+ return;
+ }
+ SettingsConfigPtr s = Globals::getInstance()->getSettingsConfig();
+ LLMutexLock lock(&s->mRecorderMutex);
+ s->mRecorders.push_back(recorder);
+ }
+
+ void removeRecorder(RecorderPtr recorder)
+ {
+ if (!recorder)
+ {
+ return;
+ }
+ SettingsConfigPtr s = Globals::getInstance()->getSettingsConfig();
+ LLMutexLock lock(&s->mRecorderMutex);
+ s->mRecorders.erase(std::remove(s->mRecorders.begin(), s->mRecorders.end(), recorder),
+ s->mRecorders.end());
+ }
+
+ // Find an entry in SettingsConfig::mRecorders whose RecorderPtr points to
+ // a Recorder subclass of type RECORDER. Return, not a RecorderPtr (which
+ // points to the Recorder base class), but a shared_ptr<RECORDER> which
+ // specifically points to the concrete RECORDER subclass instance, along
+ // with a Recorders::iterator indicating the position of that entry in
+ // mRecorders. The shared_ptr might be empty (operator!() returns true) if
+ // there was no such RECORDER subclass instance in mRecorders.
+ //
+ // NOTE!!! Requires external mutex lock!!!
+ template <typename RECORDER>
+ std::pair<std::shared_ptr<RECORDER>, Recorders::iterator>
+ findRecorderPos(SettingsConfigPtr &s)
+ {
+ // Since we promise to return an iterator, use a classic iterator
+ // loop.
+ auto end{s->mRecorders.end()};
+ for (Recorders::iterator it{s->mRecorders.begin()}; it != end; ++it)
+ {
+ // *it is a RecorderPtr, a shared_ptr<Recorder>. Use a
+ // dynamic_pointer_cast to try to downcast to test if it's also a
+ // shared_ptr<RECORDER>.
+ auto ptr = std::dynamic_pointer_cast<RECORDER>(*it);
+ if (ptr)
+ {
+ // found the entry we want
+ return { ptr, it };
+ }
+ }
+ // dropped out of the loop without finding any such entry -- instead
+ // of default-constructing Recorders::iterator (which might or might
+ // not be valid), return a value that is valid but not dereferenceable.
+ return { {}, end };
+ }
+
+ // Find an entry in SettingsConfig::mRecorders whose RecorderPtr points to
+ // a Recorder subclass of type RECORDER. Return, not a RecorderPtr (which
+ // points to the Recorder base class), but a shared_ptr<RECORDER> which
+ // specifically points to the concrete RECORDER subclass instance. The
+ // shared_ptr might be empty (operator!() returns true) if there was no
+ // such RECORDER subclass instance in mRecorders.
+ template <typename RECORDER>
+ std::shared_ptr<RECORDER> findRecorder()
+ {
+ SettingsConfigPtr s = Globals::getInstance()->getSettingsConfig();
+ LLMutexLock lock(&s->mRecorderMutex);
+ return findRecorderPos<RECORDER>(s).first;
+ }
+
+ // Remove an entry from SettingsConfig::mRecorders whose RecorderPtr
+ // points to a Recorder subclass of type RECORDER. Return true if there
+ // was one and we removed it, false if there wasn't one to start with.
+ template <typename RECORDER>
+ bool removeRecorder()
+ {
+ SettingsConfigPtr s = Globals::getInstance()->getSettingsConfig();
+ LLMutexLock lock(&s->mRecorderMutex);
+ auto found = findRecorderPos<RECORDER>(s);
+ if (found.first)
+ {
+ s->mRecorders.erase(found.second);
+ }
+ return bool(found.first);
+ }
+}
+
+namespace LLError
+{
+ void logToFile(const std::string& file_name)
+ {
+ // remove any previous Recorder filling this role
+ removeRecorder<RecordToFile>();
+
+ if (!file_name.empty())
+ {
+ std::shared_ptr<RecordToFile> recordToFile(new RecordToFile(file_name));
+ if (recordToFile->okay())
+ {
+ addRecorder(recordToFile);
+ }
+ }
+ }
+
+ std::string logFileName()
+ {
+ auto found = findRecorder<RecordToFile>();
+ return found? found->getFilename() : std::string();
+ }
+
+ void logToStderr()
+ {
+ if (! findRecorder<RecordToStderr>())
+ {
+ RecorderPtr recordToStdErr(new RecordToStderr(stderrLogWantsTime()));
+ addRecorder(recordToStdErr);
+ }
+ }
+
+ void logToFixedBuffer(LLLineBuffer* fixedBuffer)
+ {
+ // remove any previous Recorder filling this role
+ removeRecorder<RecordToFixedBuffer>();
+
+ if (fixedBuffer)
+ {
+ RecorderPtr recordToFixedBuffer(new RecordToFixedBuffer(fixedBuffer));
+ addRecorder(recordToFixedBuffer);
+ }
+ }
+}
+
+namespace
+{
+ std::string escapedMessageLines(const std::string& message)
+ {
+ std::ostringstream out;
+ size_t written_out = 0;
+ size_t all_content = message.length();
+ size_t escape_char_index; // always relative to start of message
+ // Use find_first_of to find the next character in message that needs escaping
+ for ( escape_char_index = message.find_first_of("\\\n\r");
+ escape_char_index != std::string::npos && written_out < all_content;
+ // record what we've written this iteration, scan for next char that needs escaping
+ written_out = escape_char_index + 1, escape_char_index = message.find_first_of("\\\n\r", written_out)
+ )
+ {
+ // found a character that needs escaping, so write up to that with the escape prefix
+ // note that escape_char_index is relative to the start, not to the written_out offset
+ out << message.substr(written_out, escape_char_index - written_out) << '\\';
+
+ // write out the appropriate second character in the escape sequence
+ char found = message[escape_char_index];
+ switch ( found )
+ {
+ case '\\':
+ out << '\\';
+ break;
+ case '\n':
+ out << 'n';
+ break;
+ case '\r':
+ out << 'r';
+ break;
+ }
+ }
+
+ if ( written_out < all_content ) // if the loop above didn't write everything
+ {
+ // write whatever was left
+ out << message.substr(written_out, std::string::npos);
+ }
+ return out.str();
+ }
+
+ void writeToRecorders(const LLError::CallSite& site, const std::string& message)
+ {
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_LOGGING
+ LLError::ELevel level = site.mLevel;
+ SettingsConfigPtr s = Globals::getInstance()->getSettingsConfig();
+
+ std::string escaped_message;
+
+ LLMutexLock lock(&s->mRecorderMutex);
+ for (LLError::RecorderPtr& r : s->mRecorders)
+ {
+ if (!r->enabled())
+ {
+ continue;
+ }
+
+ std::ostringstream message_stream;
+
+ if (r->wantsTime() && s->mTimeFunction != NULL)
+ {
+ message_stream << s->mTimeFunction();
+ }
+ message_stream << " ";
+
+ if (r->wantsLevel())
+ {
+ message_stream << site.mLevelString;
+ }
+ message_stream << " ";
+
+ if (r->wantsTags())
+ {
+ message_stream << site.mTagString;
+ }
+ message_stream << " ";
+
+ if (r->wantsLocation() || level == LLError::LEVEL_ERROR)
+ {
+ message_stream << site.mLocationString;
+ }
+ message_stream << " ";
+
+ if (r->wantsFunctionName())
+ {
+ message_stream << site.mFunctionString;
+ }
+ message_stream << " : ";
+
+ if (r->wantsMultiline())
+ {
+ message_stream << message;
+ }
+ else
+ {
+ if (escaped_message.empty())
+ {
+ escaped_message = escapedMessageLines(message);
+ }
+ message_stream << escaped_message;
+ }
+
+ r->recordMessage(level, message_stream.str());
+ }
+ }
+}
+
+namespace {
+ // We need a couple different mutexes, but we want to use the same mechanism
+ // for both. Make getMutex() a template function with different instances
+ // for different MutexDiscriminator values.
+ enum MutexDiscriminator
+ {
+ LOG_MUTEX,
+ STACKS_MUTEX
+ };
+ // Some logging calls happen very early in processing -- so early that our
+ // module-static variables aren't yet initialized. getMutex() wraps a
+ // function-static LLMutex so that early calls can still have a valid
+ // LLMutex instance.
+ template <MutexDiscriminator MTX>
+ LLMutex* getMutex()
+ {
+ // guaranteed to be initialized the first time control reaches here
+ static LLMutex sMutex;
+ return &sMutex;
+ }
+
+ bool checkLevelMap(const LevelMap& map, const std::string& key,
+ LLError::ELevel& level)
+ {
+ bool stop_checking;
+ LevelMap::const_iterator i = map.find(key);
+ if (i == map.end())
+ {
+ return stop_checking = false;
+ }
+
+ level = i->second;
+ return stop_checking = true;
+ }
+
+ bool checkLevelMap( const LevelMap& map,
+ const char *const * keys,
+ size_t count,
+ LLError::ELevel& level)
+ {
+ bool found_level = false;
+
+ LLError::ELevel tag_level = LLError::LEVEL_NONE;
+
+ for (size_t i = 0; i < count; i++)
+ {
+ LevelMap::const_iterator it = map.find(keys[i]);
+ if (it != map.end())
+ {
+ found_level = true;
+ tag_level = llmin(tag_level, it->second);
+ }
+ }
+
+ if (found_level)
+ {
+ level = tag_level;
+ }
+ return found_level;
+ }
+}
+
+namespace LLError
+{
+
+ bool Log::shouldLog(CallSite& site)
+ {
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_LOGGING
+ LLMutexTrylock lock(getMutex<LOG_MUTEX>(), 5);
+ if (!lock.isLocked())
+ {
+ return false;
+ }
+
+ Globals *g = Globals::getInstance();
+ SettingsConfigPtr s = g->getSettingsConfig();
+
+ s->mShouldLogCallCounter++;
+
+ const std::string& class_name = className(site.mClassInfo);
+ std::string function_name = functionName(site.mFunction);
+#if LL_LINUX
+ // gross, but typeid comparison seems to always fail here with gcc4.1
+ if (0 != strcmp(site.mClassInfo.name(), typeid(NoClassInfo).name()))
+#else
+ if (site.mClassInfo != typeid(NoClassInfo))
+#endif // LL_LINUX
+ {
+ function_name = class_name + "::" + function_name;
+ }
+
+ ELevel compareLevel = s->mDefaultLevel;
+
+ // The most specific match found will be used as the log level,
+ // since the computation short circuits.
+ // So, in increasing order of importance:
+ // Default < Tags < File < Class < Function
+ checkLevelMap(s->mFunctionLevelMap, function_name, compareLevel)
+ || checkLevelMap(s->mClassLevelMap, class_name, compareLevel)
+ || checkLevelMap(s->mFileLevelMap, abbreviateFile(site.mFile), compareLevel)
+ || (site.mTagCount > 0
+ ? checkLevelMap(s->mTagLevelMap, site.mTags, site.mTagCount, compareLevel)
+ : false);
+
+ site.mCached = true;
+ g->addCallSite(site);
+ return site.mShouldLog = site.mLevel >= compareLevel;
+ }
+
+
+ void Log::flush(const std::ostringstream& out, const CallSite& site)
+ {
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_LOGGING
+ LLMutexTrylock lock(getMutex<LOG_MUTEX>(),5);
+ if (!lock.isLocked())
+ {
+ return;
+ }
+
+ Globals* g = Globals::getInstance();
+ SettingsConfigPtr s = g->getSettingsConfig();
+
+ std::string message = out.str();
+
+ if (site.mPrintOnce)
+ {
+ std::ostringstream message_stream;
+
+ std::map<std::string, unsigned int>::iterator messageIter = s->mUniqueLogMessages.find(message);
+ if (messageIter != s->mUniqueLogMessages.end())
+ {
+ messageIter->second++;
+ unsigned int num_messages = messageIter->second;
+ if (num_messages == 10 || num_messages == 50 || (num_messages % 100) == 0)
+ {
+ message_stream << "ONCE (" << num_messages << "th time seen): ";
+ }
+ else
+ {
+ return;
+ }
+ }
+ else
+ {
+ message_stream << "ONCE: ";
+ s->mUniqueLogMessages[message] = 1;
+ }
+ message_stream << message;
+ message = message_stream.str();
+ }
+
+ writeToRecorders(site, message);
+
+ if (site.mLevel == LEVEL_ERROR)
+ {
+ g->mFatalMessage = message;
+ if (s->mCrashFunction)
+ {
+ s->mCrashFunction(message);
+ }
+ }
+ }
+}
+
+namespace LLError
+{
+ SettingsStoragePtr saveAndResetSettings()
+ {
+ return Globals::getInstance()->saveAndResetSettingsConfig();
+ }
+
+ void restoreSettings(SettingsStoragePtr pSettingsStorage)
+ {
+ return Globals::getInstance()->restore(pSettingsStorage);
+ }
+
+ std::string removePrefix(std::string& s, const std::string& p)
+ {
+ std::string::size_type where = s.find(p);
+ if (where == std::string::npos)
+ {
+ return s;
+ }
+
+ return std::string(s, where + p.size());
+ }
+
+ void replaceChar(std::string& s, char old, char replacement)
+ {
+ std::string::size_type i = 0;
+ std::string::size_type len = s.length();
+ for ( ; i < len; i++ )
+ {
+ if (s[i] == old)
+ {
+ s[i] = replacement;
+ }
+ }
+ }
+
+ std::string abbreviateFile(const std::string& filePath)
+ {
+ std::string f = filePath;
+#if LL_WINDOWS
+ replaceChar(f, '\\', '/');
+#endif
+ static std::string indra_prefix = "indra/";
+ f = removePrefix(f, indra_prefix);
+
+#if LL_DARWIN
+ static std::string newview_prefix = "newview/../";
+ f = removePrefix(f, newview_prefix);
+#endif
+
+ return f;
+ }
+
+ int shouldLogCallCount()
+ {
+ SettingsConfigPtr s = Globals::getInstance()->getSettingsConfig();
+ return s->mShouldLogCallCounter;
+ }
+
+ std::string utcTime()
+ {
+ time_t now = time(NULL);
+ const size_t BUF_SIZE = 64;
+ char time_str[BUF_SIZE]; /* Flawfinder: ignore */
+
+ auto chars = strftime(time_str, BUF_SIZE,
+ "%Y-%m-%dT%H:%M:%SZ",
+ gmtime(&now));
+
+ return chars ? time_str : "time error";
+ }
+}
+
+namespace LLError
+{
+ LLCallStacks::StringVector LLCallStacks::sBuffer ;
+
+ //static
+ void LLCallStacks::push(const char* function, const int line)
+ {
+ LLMutexTrylock lock(getMutex<STACKS_MUTEX>(), 5);
+ if (!lock.isLocked())
+ {
+ return;
+ }
+
+ if(sBuffer.size() > 511)
+ {
+ clear() ;
+ }
+
+ std::ostringstream out;
+ insert(out, function, line);
+ sBuffer.push_back(out.str());
+ }
+
+ //static
+ void LLCallStacks::insert(std::ostream& out, const char* function, const int line)
+ {
+ out << function << " line " << line << " " ;
+ }
+
+ //static
+ void LLCallStacks::end(const std::ostringstream& out)
+ {
+ LLMutexTrylock lock(getMutex<STACKS_MUTEX>(), 5);
+ if (!lock.isLocked())
+ {
+ return;
+ }
+
+ if(sBuffer.size() > 511)
+ {
+ clear() ;
+ }
+
+ sBuffer.push_back(out.str());
+ }
+
+ //static
+ void LLCallStacks::print()
+ {
+ LLMutexTrylock lock(getMutex<STACKS_MUTEX>(), 5);
+ if (!lock.isLocked())
+ {
+ return;
+ }
+
+ if(! sBuffer.empty())
+ {
+ LL_INFOS() << " ************* PRINT OUT LL CALL STACKS ************* " << LL_ENDL;
+ for (StringVector::const_reverse_iterator ri(sBuffer.rbegin()), re(sBuffer.rend());
+ ri != re; ++ri)
+ {
+ LL_INFOS() << (*ri) << LL_ENDL;
+ }
+ LL_INFOS() << " *************** END OF LL CALL STACKS *************** " << LL_ENDL;
+ }
+
+ cleanup();
+ }
+
+ //static
+ void LLCallStacks::clear()
+ {
+ sBuffer.clear();
+ }
+
+ //static
+ void LLCallStacks::cleanup()
+ {
+ clear();
+ }
+
+ std::ostream& operator<<(std::ostream& out, const LLStacktrace&)
+ {
+ return out << boost::stacktrace::stacktrace();
+ }
+
+ // LLOutOfMemoryWarning
+ std::string LLUserWarningMsg::sLocalizedOutOfMemoryTitle;
+ std::string LLUserWarningMsg::sLocalizedOutOfMemoryWarning;
+ LLUserWarningMsg::Handler LLUserWarningMsg::sHandler;
+
+ void LLUserWarningMsg::show(const std::string& message)
+ {
+ if (sHandler)
+ {
+ sHandler(std::string(), message);
+ }
+ }
+
+ void LLUserWarningMsg::showOutOfMemory()
+ {
+ if (sHandler && !sLocalizedOutOfMemoryTitle.empty())
+ {
+ sHandler(sLocalizedOutOfMemoryTitle, sLocalizedOutOfMemoryWarning);
+ }
+ }
+
+ void LLUserWarningMsg::showMissingFiles()
+ {
+ // Files Are missing, likely can't localize.
+ const std::string error_string =
+ "Second Life viewer couldn't access some of the files it needs and will be closed."
+ "\n\nPlease reinstall viewer from https://secondlife.com/support/downloads/ and "
+ "contact https://support.secondlife.com if issue persists after reinstall.";
+ sHandler("Missing Files", error_string);
+ }
+
+ void LLUserWarningMsg::setHandler(const LLUserWarningMsg::Handler &handler)
+ {
+ sHandler = handler;
+ }
+
+ void LLUserWarningMsg::setOutOfMemoryStrings(const std::string& title, const std::string& message)
+ {
+ sLocalizedOutOfMemoryTitle = title;
+ sLocalizedOutOfMemoryWarning = message;
+ }
+}
+
+void crashdriver(void (*callback)(int*))
+{
+ // The LLERROR_CRASH macro used to have inline code of the form:
+ //int* make_me_crash = NULL;
+ //*make_me_crash = 0;
+
+ // But compilers are getting smart enough to recognize that, so we must
+ // assign to an address supplied by a separate source file. We could do
+ // the assignment here in crashdriver() -- but then BugSplat would group
+ // all LL_ERRS() crashes as the fault of this one function, instead of
+ // identifying the specific LL_ERRS() source line. So instead, do the
+ // assignment in a lambda in the caller's source. We just provide the
+ // nullptr target.
+ callback(nullptr);
+}
diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h index 6f6b349cf5..6176ce0d1d 100644 --- a/indra/llcommon/llerror.h +++ b/indra/llcommon/llerror.h @@ -1,4 +1,4 @@ -/** +/** * @file llerror.h * @date December 2006 * @brief error message system @@ -6,21 +6,21 @@ * $LicenseInfo:firstyear=2006&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -79,16 +79,16 @@ const int LL_ERR_NOERR = 0; #define llassert_always_msg(func, msg) if (LL_UNLIKELY(!(func))) LL_ERRS() << "ASSERT (" << msg << ")" << LL_ENDL -#define llassert_always(func) llassert_always_msg(func, #func) +#define llassert_always(func) llassert_always_msg(func, #func) #ifdef SHOW_ASSERT -#define llassert(func) llassert_always_msg(func, #func) -#define llassert_msg(func, msg) llassert_always_msg(func, msg) -#define llverify(func) llassert_always_msg(func, #func) +#define llassert(func) llassert_always_msg(func, #func) +#define llassert_msg(func, msg) llassert_always_msg(func, msg) +#define llverify(func) llassert_always_msg(func, #func) #else #define llassert(func) #define llassert_msg(func, msg) -#define llverify(func) do {if (func) {}} while(0) +#define llverify(func) do {if (func) {}} while(0) #endif #ifdef LL_WINDOWS @@ -102,59 +102,59 @@ const int LL_ERR_NOERR = 0; /** Error Logging Facility - Information for most users: - - Code can log messages with constructions like this: - - LL_INFOS("StringTag") << "request to fizzbip agent " << agent_id - << " denied due to timeout" << LL_ENDL; - - Messages can be logged to one of four increasing levels of concern, - using one of four "streams": - - LL_DEBUGS("StringTag") - debug messages that are normally suppressed - LL_INFOS("StringTag") - informational messages that are normal shown - LL_WARNS("StringTag") - warning messages that signal a problem - LL_ERRS("StringTag") - error messages that are major, unrecoverable failures - - The later (LL_ERRS("StringTag")) automatically crashes the process after the message - is logged. - - Note that these "streams" are actually #define magic. Rules for use: - * they cannot be used as normal streams, only to start a message - * messages written to them MUST be terminated with LL_ENDL - * between the opening and closing, the << operator is indeed - writing onto a std::ostream, so all conversions and stream - formating are available - - These messages are automatically logged with function name, and (if enabled) - file and line of the message. (Note: Existing messages that already include - the function name don't get name printed twice.) - - If you have a class, adding LOG_CLASS line to the declaration will cause - all messages emitted from member functions (normal and static) to be tagged - with the proper class name as well as the function name: - - class LLFoo - { - LOG_CLASS(LLFoo); - public: - ... - }; - - void LLFoo::doSomething(int i) - { - if (i > 100) - { - LL_WARNS("FooBarTag") << "called with a big value for i: " << i << LL_ENDL; - } - ... - } - - will result in messages like: - - WARN #FooBarTag# llcommon/llfoo(100) LLFoo::doSomething : called with a big value for i: 283 - + Information for most users: + + Code can log messages with constructions like this: + + LL_INFOS("StringTag") << "request to fizzbip agent " << agent_id + << " denied due to timeout" << LL_ENDL; + + Messages can be logged to one of four increasing levels of concern, + using one of four "streams": + + LL_DEBUGS("StringTag") - debug messages that are normally suppressed + LL_INFOS("StringTag") - informational messages that are normal shown + LL_WARNS("StringTag") - warning messages that signal a problem + LL_ERRS("StringTag") - error messages that are major, unrecoverable failures + + The later (LL_ERRS("StringTag")) automatically crashes the process after the message + is logged. + + Note that these "streams" are actually #define magic. Rules for use: + * they cannot be used as normal streams, only to start a message + * messages written to them MUST be terminated with LL_ENDL + * between the opening and closing, the << operator is indeed + writing onto a std::ostream, so all conversions and stream + formating are available + + These messages are automatically logged with function name, and (if enabled) + file and line of the message. (Note: Existing messages that already include + the function name don't get name printed twice.) + + If you have a class, adding LOG_CLASS line to the declaration will cause + all messages emitted from member functions (normal and static) to be tagged + with the proper class name as well as the function name: + + class LLFoo + { + LOG_CLASS(LLFoo); + public: + ... + }; + + void LLFoo::doSomething(int i) + { + if (i > 100) + { + LL_WARNS("FooBarTag") << "called with a big value for i: " << i << LL_ENDL; + } + ... + } + + will result in messages like: + + WARN #FooBarTag# llcommon/llfoo(100) LLFoo::doSomething : called with a big value for i: 283 + the syntax is: <timestamp> SPACE <level> SPACE <tags> SPACE <location> SPACE <function> SPACE COLON SPACE <message> @@ -164,121 +164,121 @@ const int LL_ERR_NOERR = 0; The tags must be a single word (may not contain a space); if more than one tag is specified, they are all surrounded by '#' ( #FooTag#BarTag# ). - Which messages are logged and which are suppressed can be controlled at run - time from the configuration file. The default configuration is in newview/app_settings/logcontrol.xml + Which messages are logged and which are suppressed can be controlled at run + time from the configuration file. The default configuration is in newview/app_settings/logcontrol.xml A copy of that file named logcontrol-dev.xml can be made in the users personal settings directory; that will override the installed default file. See the logcontrol.xml file or http://wiki.secondlife.com/wiki/Logging_System_Overview for configuration details. - - Lastly, logging is now very efficient in both compiled code and execution - when skipped. There is no need to wrap messages, even debugging ones, in - #ifdef _DEBUG constructs. LL_DEBUGS("StringTag") messages are compiled into all builds, - even release. Which means you can use them to help debug even when deployed - to a real grid. + + Lastly, logging is now very efficient in both compiled code and execution + when skipped. There is no need to wrap messages, even debugging ones, in + #ifdef _DEBUG constructs. LL_DEBUGS("StringTag") messages are compiled into all builds, + even release. Which means you can use them to help debug even when deployed + to a real grid. */ namespace LLError { - enum ELevel - { - LEVEL_ALL = 0, - // used to indicate that all messages should be logged - - LEVEL_DEBUG = 0, - LEVEL_INFO = 1, - LEVEL_WARN = 2, - LEVEL_ERROR = 3, // used to be called FATAL - - LEVEL_NONE = 4 - // not really a level - // used to indicate that no messages should be logged - }; - // If you change ELevel, please update llvlog() macro below. - - /* Macro support - The classes CallSite and Log are used by the logging macros below. - They are not intended for general use. - */ - - struct CallSite; - - class LL_COMMON_API Log - { - public: - static bool shouldLog(CallSite&); - static void flush(const std::ostringstream&, const CallSite&); - static std::string demangle(const char* mangled); - /// classname<TYPE>() - template <typename T> - static std::string classname() { return demangle(typeid(T).name()); } - /// classname(some_pointer) - template <typename T> - static std::string classname(T* const ptr) { return ptr? demangle(typeid(*ptr).name()) : "nullptr"; } - /// classname(some_reference) - template <typename T> - static std::string classname(const T& obj) { return demangle(typeid(obj).name()); } - }; - - struct 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 - // intended for public use. - CallSite(ELevel level, - const char* file, - int line, - const std::type_info& class_info, - const char* function, - bool print_once, - const char** tags, - size_t tag_count); - - ~CallSite(); + enum ELevel + { + LEVEL_ALL = 0, + // used to indicate that all messages should be logged + + LEVEL_DEBUG = 0, + LEVEL_INFO = 1, + LEVEL_WARN = 2, + LEVEL_ERROR = 3, // used to be called FATAL + + LEVEL_NONE = 4 + // not really a level + // used to indicate that no messages should be logged + }; + // If you change ELevel, please update llvlog() macro below. + + /* Macro support + The classes CallSite and Log are used by the logging macros below. + They are not intended for general use. + */ + + struct CallSite; + + class LL_COMMON_API Log + { + public: + static bool shouldLog(CallSite&); + static void flush(const std::ostringstream&, const CallSite&); + static std::string demangle(const char* mangled); + /// classname<TYPE>() + template <typename T> + static std::string classname() { return demangle(typeid(T).name()); } + /// classname(some_pointer) + template <typename T> + static std::string classname(T* const ptr) { return ptr? demangle(typeid(*ptr).name()) : "nullptr"; } + /// classname(some_reference) + template <typename T> + static std::string classname(const T& obj) { return demangle(typeid(obj).name()); } + }; + + struct 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 + // intended for public use. + CallSite(ELevel level, + const char* file, + int line, + const std::type_info& class_info, + const char* function, + bool print_once, + const char** tags, + size_t tag_count); + + ~CallSite(); #ifdef LL_LIBRARY_INCLUDE - bool shouldLog(); + bool shouldLog(); #else // LL_LIBRARY_INCLUDE - bool shouldLog() - { - return mCached - ? mShouldLog - : Log::shouldLog(*this); - } - // this member function needs to be in-line for efficiency + bool shouldLog() + { + return mCached + ? mShouldLog + : Log::shouldLog(*this); + } + // this member function needs to be in-line for efficiency #endif // LL_LIBRARY_INCLUDE - - void invalidate(); - - // these describe the call site and never change - const ELevel mLevel; - const char* const mFile; - const int mLine; - const std::type_info& mClassInfo; - const char* const mFunction; - const char** mTags; - size_t mTagCount; - const bool mPrintOnce; - const char* mLevelString; - std::string mLocationString, - mFunctionString, - mTagString; - bool mCached, - mShouldLog; - - friend class Log; - }; - - - class End { }; - inline std::ostream& operator<<(std::ostream& s, const End&) - { return s; } - // used to indicate the end of a message - - class LL_COMMON_API NoClassInfo { }; - // used to indicate no class info known for logging + + void invalidate(); + + // these describe the call site and never change + const ELevel mLevel; + const char* const mFile; + const int mLine; + const std::type_info& mClassInfo; + const char* const mFunction; + const char** mTags; + size_t mTagCount; + const bool mPrintOnce; + const char* mLevelString; + std::string mLocationString, + mFunctionString, + mTagString; + bool mCached, + mShouldLog; + + friend class Log; + }; + + + class End { }; + inline std::ostream& operator<<(std::ostream& s, const End&) + { return s; } + // used to indicate the end of a message + + class LL_COMMON_API NoClassInfo { }; + // used to indicate no class info known for logging //LLCallStacks keeps track of call stacks and output the call stacks to log file // - //Note: to be simple, efficient and necessary to keep track of correct call stacks, + //Note: to be simple, efficient and necessary to keep track of correct call stacks, //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. @@ -287,8 +287,8 @@ namespace LLError private: typedef std::vector<std::string> StringVector; static StringVector sBuffer ; - - public: + + public: static void push(const char* function, const int line) ; static void insert(std::ostream& out, const char* function, const int line) ; static void print() ; @@ -326,34 +326,34 @@ namespace LLError }; } -//this is cheaper than llcallstacks if no need to output other variables to call stacks. +//this is cheaper than llcallstacks if no need to output other variables to call stacks. #define LL_PUSH_CALLSTACKS() LLError::LLCallStacks::push(__FUNCTION__, __LINE__) #define llcallstacks \ - { \ - std::ostringstream _out; \ - LLError::LLCallStacks::insert(_out, __FUNCTION__, __LINE__) ; \ - _out + { \ + std::ostringstream _out; \ + LLError::LLCallStacks::insert(_out, __FUNCTION__, __LINE__) ; \ + _out #define llcallstacksendl \ - LLError::End(); \ - LLError::LLCallStacks::end(_out) ; \ - } + LLError::End(); \ + LLError::LLCallStacks::end(_out) ; \ + } #define LL_CLEAR_CALLSTACKS() LLError::LLCallStacks::clear() -#define LL_PRINT_CALLSTACKS() LLError::LLCallStacks::print() +#define LL_PRINT_CALLSTACKS() LLError::LLCallStacks::print() /* - Class type information for logging + Class type information for logging */ -#define LOG_CLASS(s) typedef s _LL_CLASS_TO_LOG - // Declares class to tag logged messages with. - // See top of file for example of how to use this - +#define LOG_CLASS(s) typedef s _LL_CLASS_TO_LOG + // Declares class to tag logged messages with. + // See top of file for example of how to use this + typedef LLError::NoClassInfo _LL_CLASS_TO_LOG; - // Outside a class declaration, or in class without LOG_CLASS(), this - // typedef causes the messages to not be associated with any class. + // Outside a class declaration, or in class without LOG_CLASS(), this + // typedef causes the messages to not be associated with any class. ///////////////////////////////// // Error Logging Macros @@ -376,34 +376,34 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG; #define lllog(level, once, ...) \ do { \ LL_PROFILE_ZONE_NAMED("lllog"); \ - const char* tags[] = {"", ##__VA_ARGS__}; \ - static LLError::CallSite _site(lllog_site_args_(level, once, tags)); \ - lllog_test_() + const char* tags[] = {"", ##__VA_ARGS__}; \ + static LLError::CallSite _site(lllog_site_args_(level, once, tags)); \ + lllog_test_() #define lllog_test_() \ - if (LL_UNLIKELY(_site.shouldLog())) \ - { \ - std::ostringstream _out; \ - _out + if (LL_UNLIKELY(_site.shouldLog())) \ + { \ + std::ostringstream _out; \ + _out #define lllog_site_args_(level, once, tags) \ - level, __FILE__, __LINE__, typeid(_LL_CLASS_TO_LOG), \ - __FUNCTION__, once, &tags[1], LL_ARRAY_SIZE(tags)-1 + level, __FILE__, __LINE__, typeid(_LL_CLASS_TO_LOG), \ + __FUNCTION__, once, &tags[1], LL_ARRAY_SIZE(tags)-1 //Use this construct if you need to do computation in the middle of a //message: -// -// LL_INFOS("AgentGesture") << "the agent " << agend_id; -// switch (f) -// { -// case FOP_SHRUGS: LL_CONT << "shrugs"; break; -// case FOP_TAPS: LL_CONT << "points at " << who; break; -// case FOP_SAYS: LL_CONT << "says " << message; break; -// } -// LL_CONT << " for " << t << " seconds" << LL_ENDL; -// +// +// LL_INFOS("AgentGesture") << "the agent " << agend_id; +// switch (f) +// { +// case FOP_SHRUGS: LL_CONT << "shrugs"; break; +// case FOP_TAPS: LL_CONT << "points at " << who; break; +// case FOP_SAYS: LL_CONT << "says " << message; break; +// } +// LL_CONT << " for " << t << " seconds" << LL_ENDL; +// //Such computation is done iff the message will be logged. -#define LL_CONT _out +#define LL_CONT _out #define LL_NEWLINE '\n' @@ -426,24 +426,24 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG; // NEW Macros for debugging, allow the passing of a string tag // Pass comma separated list of tags (currently only supports up to 0, 1, or 2) -#define LL_DEBUGS(...) lllog(LLError::LEVEL_DEBUG, false, ##__VA_ARGS__) -#define LL_INFOS(...) lllog(LLError::LEVEL_INFO, false, ##__VA_ARGS__) -#define LL_WARNS(...) lllog(LLError::LEVEL_WARN, false, ##__VA_ARGS__) -#define LL_ERRS(...) lllog(LLError::LEVEL_ERROR, false, ##__VA_ARGS__) +#define LL_DEBUGS(...) lllog(LLError::LEVEL_DEBUG, false, ##__VA_ARGS__) +#define LL_INFOS(...) lllog(LLError::LEVEL_INFO, false, ##__VA_ARGS__) +#define LL_WARNS(...) lllog(LLError::LEVEL_WARN, false, ##__VA_ARGS__) +#define LL_ERRS(...) lllog(LLError::LEVEL_ERROR, false, ##__VA_ARGS__) // alternative to llassert_always that prints explanatory message // note ## token paste operator hack used above will only work in gcc following // a comma and is completely unnecessary in VS since the comma is automatically // suppressed // https://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html // https://docs.microsoft.com/en-us/cpp/preprocessor/variadic-macros?view=vs-2015 -#define LL_WARNS_IF(exp, ...) if (exp) LL_WARNS(__VA_ARGS__) << "(" #exp ")" -#define LL_ERRS_IF(exp, ...) if (exp) LL_ERRS(__VA_ARGS__) << "(" #exp ")" +#define LL_WARNS_IF(exp, ...) if (exp) LL_WARNS(__VA_ARGS__) << "(" #exp ")" +#define LL_ERRS_IF(exp, ...) if (exp) LL_ERRS(__VA_ARGS__) << "(" #exp ")" // Only print the log message once (good for warnings or infos that would otherwise // spam the log file over and over, such as tighter loops). -#define LL_DEBUGS_ONCE(...) lllog(LLError::LEVEL_DEBUG, true, ##__VA_ARGS__) -#define LL_INFOS_ONCE(...) lllog(LLError::LEVEL_INFO, true, ##__VA_ARGS__) -#define LL_WARNS_ONCE(...) lllog(LLError::LEVEL_WARN, true, ##__VA_ARGS__) +#define LL_DEBUGS_ONCE(...) lllog(LLError::LEVEL_DEBUG, true, ##__VA_ARGS__) +#define LL_INFOS_ONCE(...) lllog(LLError::LEVEL_INFO, true, ##__VA_ARGS__) +#define LL_WARNS_ONCE(...) lllog(LLError::LEVEL_WARN, true, ##__VA_ARGS__) // Use this if you need to pass LLError::ELevel as a variable. #define LL_VLOGS(level, ...) llvlog(level, false, ##__VA_ARGS__) @@ -459,33 +459,33 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG; // instance for the passed level value. // Compare implementation to lllog() above. #define llvlog(level, once, ...) \ - do { \ - const char* tags[] = {"", ##__VA_ARGS__}; \ - /* Need a static CallSite instance per expected ELevel value. */ \ - /* Since we intend to index this array with the ELevel, */ \ - /* _sites[0] should be ELevel(0), and so on -- avoid using */ \ - /* ELevel symbolic names when initializing -- except for */ \ - /* the last entry, which handles anything beyond the end. */ \ - /* (Commented ELevel value names are from 2016-09-01.) */ \ - /* Passing an ELevel past the end of this array is itself */ \ - /* a fatal error, so ensure the last is LEVEL_ERROR. */ \ - static LLError::CallSite _sites[] = \ - { \ - /* LEVEL_DEBUG */ \ - LLError::CallSite(lllog_site_args_(LLError::ELevel(0), once, tags)), \ - /* LEVEL_INFO */ \ - LLError::CallSite(lllog_site_args_(LLError::ELevel(1), once, tags)), \ - /* LEVEL_WARN */ \ - LLError::CallSite(lllog_site_args_(LLError::ELevel(2), once, tags)), \ - /* LEVEL_ERROR */ \ - LLError::CallSite(lllog_site_args_(LLError::LEVEL_ERROR, once, tags)) \ - }; \ - /* Clamp the passed 'level' to at most last entry */ \ - std::size_t which((std::size_t(level) >= LL_ARRAY_SIZE(_sites)) ? \ - (LL_ARRAY_SIZE(_sites) - 1) : std::size_t(level)); \ - /* selected CallSite *must* be named _site for LL_ENDL */ \ - LLError::CallSite& _site(_sites[which]); \ - lllog_test_() + do { \ + const char* tags[] = {"", ##__VA_ARGS__}; \ + /* Need a static CallSite instance per expected ELevel value. */ \ + /* Since we intend to index this array with the ELevel, */ \ + /* _sites[0] should be ELevel(0), and so on -- avoid using */ \ + /* ELevel symbolic names when initializing -- except for */ \ + /* the last entry, which handles anything beyond the end. */ \ + /* (Commented ELevel value names are from 2016-09-01.) */ \ + /* Passing an ELevel past the end of this array is itself */ \ + /* a fatal error, so ensure the last is LEVEL_ERROR. */ \ + static LLError::CallSite _sites[] = \ + { \ + /* LEVEL_DEBUG */ \ + LLError::CallSite(lllog_site_args_(LLError::ELevel(0), once, tags)), \ + /* LEVEL_INFO */ \ + LLError::CallSite(lllog_site_args_(LLError::ELevel(1), once, tags)), \ + /* LEVEL_WARN */ \ + LLError::CallSite(lllog_site_args_(LLError::ELevel(2), once, tags)), \ + /* LEVEL_ERROR */ \ + LLError::CallSite(lllog_site_args_(LLError::LEVEL_ERROR, once, tags)) \ + }; \ + /* Clamp the passed 'level' to at most last entry */ \ + std::size_t which((std::size_t(level) >= LL_ARRAY_SIZE(_sites)) ? \ + (LL_ARRAY_SIZE(_sites) - 1) : std::size_t(level)); \ + /* selected CallSite *must* be named _site for LL_ENDL */ \ + LLError::CallSite& _site(_sites[which]); \ + lllog_test_() /* // Check at run-time whether logging is enabled, without generating output. @@ -510,7 +510,7 @@ LL_ENDL; LL_DEBUGS("SomeTag") performs the locking and map-searching ONCE, then caches the result in a static variable. -*/ +*/ // used by LLERROR_CRASH void crashdriver(void (*)(int*)); diff --git a/indra/llcommon/llerrorcontrol.h b/indra/llcommon/llerrorcontrol.h index 77b187a80f..bf5a6df556 100644 --- a/indra/llcommon/llerrorcontrol.h +++ b/indra/llcommon/llerrorcontrol.h @@ -38,143 +38,143 @@ class LLSD; /* - This is the part of the LLError namespace that manages the messages - produced by the logging. The logging support is defined in llerror.h. - Most files do not need to include this. + This is the part of the LLError namespace that manages the messages + produced by the logging. The logging support is defined in llerror.h. + Most files do not need to include this. - These implementations are in llerror.cpp. + These implementations are in llerror.cpp. */ // Line buffer interface class LLLineBuffer { public: - LLLineBuffer() {}; - virtual ~LLLineBuffer() {}; + LLLineBuffer() {}; + virtual ~LLLineBuffer() {}; - virtual void clear() = 0; // Clear the buffer, and reset it. + virtual void clear() = 0; // Clear the buffer, and reset it. - virtual void addLine(const std::string& utf8line) = 0; + virtual void addLine(const std::string& utf8line) = 0; }; namespace LLError { - LL_COMMON_API void initForApplication(const std::string& user_dir, const std::string& app_dir, bool log_to_stderr = true); - // 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 + LL_COMMON_API void initForApplication(const std::string& user_dir, const std::string& app_dir, bool log_to_stderr = true); + // 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 - /* - Settings that control what is logged. - Setting a level means log messages at that level or above. - */ + /* + Settings that control what is logged. + Setting a level means log messages at that level or above. + */ - LL_COMMON_API void setPrintLocation(bool); - LL_COMMON_API void setDefaultLevel(LLError::ELevel); - LL_COMMON_API ELevel getDefaultLevel(); - LL_COMMON_API void setAlwaysFlush(bool flush); + LL_COMMON_API void setPrintLocation(bool); + LL_COMMON_API void setDefaultLevel(LLError::ELevel); + LL_COMMON_API ELevel getDefaultLevel(); + LL_COMMON_API void setAlwaysFlush(bool flush); LL_COMMON_API bool getAlwaysFlush(); - LL_COMMON_API void setEnabledLogTypesMask(U32 mask); - LL_COMMON_API U32 getEnabledLogTypesMask(); - 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); - - LL_COMMON_API LLError::ELevel decodeLevel(std::string name); - LL_COMMON_API void configure(const LLSD&); - // the LLSD can configure all of the settings - // usually read automatically from the live errorlog.xml file - - - /* - Control functions. - */ - - typedef boost::function<void(const std::string&)> FatalFunction; - - LL_COMMON_API void setFatalFunction(const FatalFunction&); - // The fatal function will be called after 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 - // that message from causing the fatal function to be invoked. - // The passed FatalFunction will be the LAST log function called - // before LL_ERRS crashes its caller. A FatalFunction can throw an - // exception, or call exit(), to bypass the crash. It MUST disrupt the - // flow of control because no caller expects LL_ERRS to return. - - LL_COMMON_API FatalFunction getFatalFunction(); - // Retrieve the previously-set FatalFunction - - LL_COMMON_API std::string getFatalMessage(); - // Retrieve the message last passed to FatalFunction, if any - - /// temporarily override the FatalFunction for the duration of a - /// particular scope, e.g. for unit tests - class LL_COMMON_API OverrideFatalFunction - { - public: - OverrideFatalFunction(const FatalFunction& func): - mPrev(getFatalFunction()) - { - setFatalFunction(func); - } - ~OverrideFatalFunction() - { - setFatalFunction(mPrev); - } - - private: - FatalFunction mPrev; - }; - - typedef std::string (*TimeFunction)(); - LL_COMMON_API std::string utcTime(); - - 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 LL_COMMON_API Recorder - { - // An object that handles the actual output or error messages. - public: - Recorder(); - virtual ~Recorder(); - - virtual void recordMessage(LLError::ELevel, const std::string& message) = 0; - // use the level for better display, not for filtering - - virtual bool enabled() { return true; } - - bool wantsTime(); - bool wantsTags(); - bool wantsLevel(); - bool wantsLocation(); - bool wantsFunctionName(); - bool wantsMultiline(); - - void showTime(bool show); - void showTags(bool show); - void showLevel(bool show); - void showLocation(bool show); - void showFunctionName(bool show); - void showMultiline(bool show); - - protected: - bool mWantsTime; - bool mWantsTags; - bool mWantsLevel; - bool mWantsLocation; - bool mWantsFunctionName; - bool mWantsMultiline; - }; - - typedef std::shared_ptr<Recorder> RecorderPtr; + LL_COMMON_API void setEnabledLogTypesMask(U32 mask); + LL_COMMON_API U32 getEnabledLogTypesMask(); + 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); + + LL_COMMON_API LLError::ELevel decodeLevel(std::string name); + LL_COMMON_API void configure(const LLSD&); + // the LLSD can configure all of the settings + // usually read automatically from the live errorlog.xml file + + + /* + Control functions. + */ + + typedef boost::function<void(const std::string&)> FatalFunction; + + LL_COMMON_API void setFatalFunction(const FatalFunction&); + // The fatal function will be called after 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 + // that message from causing the fatal function to be invoked. + // The passed FatalFunction will be the LAST log function called + // before LL_ERRS crashes its caller. A FatalFunction can throw an + // exception, or call exit(), to bypass the crash. It MUST disrupt the + // flow of control because no caller expects LL_ERRS to return. + + LL_COMMON_API FatalFunction getFatalFunction(); + // Retrieve the previously-set FatalFunction + + LL_COMMON_API std::string getFatalMessage(); + // Retrieve the message last passed to FatalFunction, if any + + /// temporarily override the FatalFunction for the duration of a + /// particular scope, e.g. for unit tests + class LL_COMMON_API OverrideFatalFunction + { + public: + OverrideFatalFunction(const FatalFunction& func): + mPrev(getFatalFunction()) + { + setFatalFunction(func); + } + ~OverrideFatalFunction() + { + setFatalFunction(mPrev); + } + + private: + FatalFunction mPrev; + }; + + typedef std::string (*TimeFunction)(); + LL_COMMON_API std::string utcTime(); + + 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 LL_COMMON_API Recorder + { + // An object that handles the actual output or error messages. + public: + Recorder(); + virtual ~Recorder(); + + virtual void recordMessage(LLError::ELevel, const std::string& message) = 0; + // use the level for better display, not for filtering + + virtual bool enabled() { return true; } + + bool wantsTime(); + bool wantsTags(); + bool wantsLevel(); + bool wantsLocation(); + bool wantsFunctionName(); + bool wantsMultiline(); + + void showTime(bool show); + void showTags(bool show); + void showLevel(bool show); + void showLocation(bool show); + void showFunctionName(bool show); + void showMultiline(bool show); + + protected: + bool mWantsTime; + bool mWantsTags; + bool mWantsLevel; + bool mWantsLocation; + bool mWantsFunctionName; + bool mWantsMultiline; + }; + + typedef std::shared_ptr<Recorder> RecorderPtr; /** * Instantiate GenericRecorder with a callable(level, message) to get @@ -197,48 +197,48 @@ namespace LLError CALLABLE mCallable; }; - /** - * @NOTE: addRecorder() and removeRecorder() uses the boost::shared_ptr to allow for shared ownership - * while still ensuring that the allocated memory is eventually freed - */ - LL_COMMON_API void addRecorder(RecorderPtr); - LL_COMMON_API void removeRecorder(RecorderPtr); - // each error message is passed to each recorder via recordMessage() - /** - * Call addGenericRecorder() with a callable(level, message) to get - * control on every log message without having to code an explicit - * Recorder subclass. Save the returned RecorderPtr if you later want to - * call removeRecorder(). - */ - template <typename CALLABLE> - RecorderPtr addGenericRecorder(const CALLABLE& callable) - { - RecorderPtr ptr{ new GenericRecorder<CALLABLE>(callable) }; - addRecorder(ptr); - return ptr; - } - - LL_COMMON_API void logToFile(const std::string& filename); - LL_COMMON_API void logToStderr(); - 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. - LL_COMMON_API std::string logFileName(); - // returns name of current logging file, empty string if none - - - /* - Utilities for use by the unit tests of LLError itself. - */ - - typedef LLPointer<LLRefCount> SettingsStoragePtr; - LL_COMMON_API SettingsStoragePtr saveAndResetSettings(); - LL_COMMON_API void restoreSettings(SettingsStoragePtr pSettingsStorage); - - LL_COMMON_API std::string abbreviateFile(const std::string& filePath); - LL_COMMON_API int shouldLogCallCount(); + /** + * @NOTE: addRecorder() and removeRecorder() uses the boost::shared_ptr to allow for shared ownership + * while still ensuring that the allocated memory is eventually freed + */ + LL_COMMON_API void addRecorder(RecorderPtr); + LL_COMMON_API void removeRecorder(RecorderPtr); + // each error message is passed to each recorder via recordMessage() + /** + * Call addGenericRecorder() with a callable(level, message) to get + * control on every log message without having to code an explicit + * Recorder subclass. Save the returned RecorderPtr if you later want to + * call removeRecorder(). + */ + template <typename CALLABLE> + RecorderPtr addGenericRecorder(const CALLABLE& callable) + { + RecorderPtr ptr{ new GenericRecorder<CALLABLE>(callable) }; + addRecorder(ptr); + return ptr; + } + + LL_COMMON_API void logToFile(const std::string& filename); + LL_COMMON_API void logToStderr(); + 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. + LL_COMMON_API std::string logFileName(); + // returns name of current logging file, empty string if none + + + /* + Utilities for use by the unit tests of LLError itself. + */ + + typedef LLPointer<LLRefCount> SettingsStoragePtr; + LL_COMMON_API SettingsStoragePtr saveAndResetSettings(); + LL_COMMON_API void restoreSettings(SettingsStoragePtr pSettingsStorage); + + LL_COMMON_API std::string abbreviateFile(const std::string& filePath); + LL_COMMON_API int shouldLogCallCount(); }; #endif // LL_LLERRORCONTROL_H diff --git a/indra/llcommon/llerrorlegacy.h b/indra/llcommon/llerrorlegacy.h index 31dd207008..693e1501d5 100644 --- a/indra/llcommon/llerrorlegacy.h +++ b/indra/llcommon/llerrorlegacy.h @@ -1,4 +1,4 @@ -/** +/** * @file llerrorlegacy.h * @date January 2007 * @brief old things from the older error system @@ -6,21 +6,21 @@ * $LicenseInfo:firstyear=2007&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llcommon/llevent.cpp b/indra/llcommon/llevent.cpp index 501d06e3cd..305ab8144a 100644 --- a/indra/llcommon/llevent.cpp +++ b/indra/llcommon/llevent.cpp @@ -1,25 +1,25 @@ -/** +/** * @file llevent.cpp * @brief LLEvent and LLEventListener base classes. * * $LicenseInfo:firstyear=2006&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -42,13 +42,13 @@ LLEvent::~LLEvent() // virtual bool LLEvent::accept(LLEventListener* listener) { - return true; + return true; } // virtual const std::string& LLEvent::desc() { - return mDesc; + return mDesc; } /************************************************ @@ -56,50 +56,50 @@ const std::string& LLEvent::desc() ************************************************/ LLObservable::LLObservable() - : mDispatcher(new LLEventDispatcher()) + : mDispatcher(new LLEventDispatcher()) { } // virtual LLObservable::~LLObservable() { - if (mDispatcher.notNull()) - { - mDispatcher->disengage(this); - mDispatcher = NULL; - } + if (mDispatcher.notNull()) + { + mDispatcher->disengage(this); + mDispatcher = NULL; + } } // virtual bool LLObservable::setDispatcher(LLPointer<LLEventDispatcher> dispatcher) { - if (mDispatcher.notNull()) - { - mDispatcher->disengage(this); - mDispatcher = NULL; - } - if (dispatcher.notNull() || dispatcher->engage(this)) - { - mDispatcher = dispatcher; - return true; - } - return false; + if (mDispatcher.notNull()) + { + mDispatcher->disengage(this); + mDispatcher = NULL; + } + if (dispatcher.notNull() || dispatcher->engage(this)) + { + mDispatcher = dispatcher; + return true; + } + return false; } // Returns the current dispatcher pointer. // virtual LLEventDispatcher* LLObservable::getDispatcher() { - return mDispatcher; + return mDispatcher; } // Notifies the dispatcher of an event being fired. void LLObservable::fireEvent(LLPointer<LLEvent> event, LLSD filter) { - if (mDispatcher.notNull()) - { - mDispatcher->fireEvent(event, filter); - } + if (mDispatcher.notNull()) + { + mDispatcher->fireEvent(event, filter); + } } /************************************************ @@ -109,134 +109,134 @@ void LLObservable::fireEvent(LLPointer<LLEvent> event, LLSD filter) class LLEventDispatcher::Impl { public: - virtual ~Impl() { } - virtual bool engage(LLObservable* observable) { return true; } - virtual void disengage(LLObservable* observable) { } - - virtual void addListener(LLEventListener *listener, LLSD filter, const LLSD& userdata) = 0; - virtual void removeListener(LLEventListener *listener) = 0; - virtual std::vector<LLListenerEntry> getListeners() const = 0; - virtual bool fireEvent(LLPointer<LLEvent> event, LLSD filter) = 0; + virtual ~Impl() { } + virtual bool engage(LLObservable* observable) { return true; } + virtual void disengage(LLObservable* observable) { } + + virtual void addListener(LLEventListener *listener, LLSD filter, const LLSD& userdata) = 0; + virtual void removeListener(LLEventListener *listener) = 0; + virtual std::vector<LLListenerEntry> getListeners() const = 0; + virtual bool fireEvent(LLPointer<LLEvent> event, LLSD filter) = 0; }; bool LLEventDispatcher::engage(LLObservable* observable) { - return impl->engage(observable); + return impl->engage(observable); } void LLEventDispatcher::disengage(LLObservable* observable) { - impl->disengage(observable); + impl->disengage(observable); } void LLEventDispatcher::addListener(LLEventListener *listener, LLSD filter, const LLSD& userdata) { - impl->addListener(listener, filter, userdata); + impl->addListener(listener, filter, userdata); } void LLEventDispatcher::removeListener(LLEventListener *listener) { - impl->removeListener(listener); + impl->removeListener(listener); } std::vector<LLListenerEntry> LLEventDispatcher::getListeners() const { - return impl->getListeners(); + return impl->getListeners(); } bool LLEventDispatcher::fireEvent(LLPointer<LLEvent> event, LLSD filter) { - return impl->fireEvent(event, filter); + return impl->fireEvent(event, filter); } class LLSimpleDispatcher : public LLEventDispatcher::Impl { public: - LLSimpleDispatcher(LLEventDispatcher *parent) : mParent(parent) { } - virtual ~LLSimpleDispatcher(); - virtual void addListener(LLEventListener* listener, LLSD filter, const LLSD& userdata); - virtual void removeListener(LLEventListener* listener); - virtual std::vector<LLListenerEntry> getListeners() const; - virtual bool fireEvent(LLPointer<LLEvent> event, LLSD filter); + LLSimpleDispatcher(LLEventDispatcher *parent) : mParent(parent) { } + virtual ~LLSimpleDispatcher(); + virtual void addListener(LLEventListener* listener, LLSD filter, const LLSD& userdata); + virtual void removeListener(LLEventListener* listener); + virtual std::vector<LLListenerEntry> getListeners() const; + virtual bool fireEvent(LLPointer<LLEvent> event, LLSD filter); protected: - std::vector<LLListenerEntry> mListeners; - LLEventDispatcher *mParent; + std::vector<LLListenerEntry> mListeners; + LLEventDispatcher *mParent; }; LLSimpleDispatcher::~LLSimpleDispatcher() { - while (mListeners.size() > 0) - { - removeListener(mListeners.begin()->listener); - } + while (mListeners.size() > 0) + { + removeListener(mListeners.begin()->listener); + } } void LLSimpleDispatcher::addListener(LLEventListener* listener, LLSD filter, const LLSD& userdata) { - if (listener == NULL) return; - removeListener(listener); - LLListenerEntry new_entry; - new_entry.listener = listener; - new_entry.filter = filter; - new_entry.userdata = userdata; - mListeners.push_back(new_entry); - listener->handleAttach(mParent); + if (listener == NULL) return; + removeListener(listener); + LLListenerEntry new_entry; + new_entry.listener = listener; + new_entry.filter = filter; + new_entry.userdata = userdata; + mListeners.push_back(new_entry); + listener->handleAttach(mParent); } void LLSimpleDispatcher::removeListener(LLEventListener* listener) { - std::vector<LLListenerEntry>::iterator itor = mListeners.begin(); - std::vector<LLListenerEntry>::iterator end = mListeners.end(); - for (; itor != end; ++itor) - { - if ((*itor).listener == listener) - { - mListeners.erase(itor); - break; - } - } - listener->handleDetach(mParent); + std::vector<LLListenerEntry>::iterator itor = mListeners.begin(); + std::vector<LLListenerEntry>::iterator end = mListeners.end(); + for (; itor != end; ++itor) + { + if ((*itor).listener == listener) + { + mListeners.erase(itor); + break; + } + } + listener->handleDetach(mParent); } std::vector<LLListenerEntry> LLSimpleDispatcher::getListeners() const { - std::vector<LLListenerEntry> ret; - for (const LLListenerEntry& entry : mListeners) - { - ret.push_back(entry); - } - - return ret; + std::vector<LLListenerEntry> ret; + for (const LLListenerEntry& entry : mListeners) + { + ret.push_back(entry); + } + + return ret; } // virtual bool LLSimpleDispatcher::fireEvent(LLPointer<LLEvent> event, LLSD filter) { - std::string filter_string = filter.asString(); - for (LLListenerEntry& entry : mListeners) - { - if (filter_string == "" || entry.filter.asString() == filter_string) - { - (entry.listener)->handleEvent(event, entry.userdata); - } - } - return true; + std::string filter_string = filter.asString(); + for (LLListenerEntry& entry : mListeners) + { + if (filter_string == "" || entry.filter.asString() == filter_string) + { + (entry.listener)->handleEvent(event, entry.userdata); + } + } + return true; } LLEventDispatcher::LLEventDispatcher() { - impl = new LLSimpleDispatcher(this); + impl = new LLSimpleDispatcher(this); } LLEventDispatcher::~LLEventDispatcher() { - if (impl) - { - delete impl; - impl = NULL; - } + if (impl) + { + delete impl; + impl = NULL; + } } /************************************************ @@ -249,52 +249,52 @@ LLEventListener::~LLEventListener() LLSimpleListener::~LLSimpleListener() { - clearDispatchers(); + clearDispatchers(); } void LLSimpleListener::clearDispatchers() { - // Remove myself from all listening dispatchers - std::vector<LLEventDispatcher *>::iterator itor; - while (mDispatchers.size() > 0) - { - itor = mDispatchers.begin(); - LLEventDispatcher *dispatcher = *itor; - dispatcher->removeListener(this); - itor = mDispatchers.begin(); - if (itor != mDispatchers.end() && (*itor) == dispatcher) - { - // Somehow, the dispatcher was not removed. Remove it forcibly - mDispatchers.erase(itor); - } - } + // Remove myself from all listening dispatchers + std::vector<LLEventDispatcher *>::iterator itor; + while (mDispatchers.size() > 0) + { + itor = mDispatchers.begin(); + LLEventDispatcher *dispatcher = *itor; + dispatcher->removeListener(this); + itor = mDispatchers.begin(); + if (itor != mDispatchers.end() && (*itor) == dispatcher) + { + // Somehow, the dispatcher was not removed. Remove it forcibly + mDispatchers.erase(itor); + } + } } bool LLSimpleListener::handleAttach(LLEventDispatcher *dispatcher) { - // Add dispatcher if it doesn't already exist - for (LLEventDispatcher* disp : mDispatchers) - { - if (disp == dispatcher) return true; - } - mDispatchers.push_back(dispatcher); - return true; + // Add dispatcher if it doesn't already exist + for (LLEventDispatcher* disp : mDispatchers) + { + if (disp == dispatcher) return true; + } + mDispatchers.push_back(dispatcher); + return true; } bool LLSimpleListener::handleDetach(LLEventDispatcher *dispatcher) { - // Remove dispatcher from list - std::vector<LLEventDispatcher *>::iterator itor; - for (itor = mDispatchers.begin(); itor != mDispatchers.end(); ) - { - if ((*itor) == dispatcher) - { - itor = mDispatchers.erase(itor); - } - else - { - ++itor; - } - } - return true; + // Remove dispatcher from list + std::vector<LLEventDispatcher *>::iterator itor; + for (itor = mDispatchers.begin(); itor != mDispatchers.end(); ) + { + if ((*itor) == dispatcher) + { + itor = mDispatchers.erase(itor); + } + else + { + ++itor; + } + } + return true; } diff --git a/indra/llcommon/llevent.h b/indra/llcommon/llevent.h index 28ce7de102..4933f8d607 100644 --- a/indra/llcommon/llevent.h +++ b/indra/llcommon/llevent.h @@ -1,4 +1,4 @@ -/** +/** * @file llevent.h * @author Tom Yedwab * @brief LLEvent and LLEventListener base classes. @@ -6,21 +6,21 @@ * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -44,58 +44,58 @@ class LLObservable; class LL_COMMON_API LLEvent : public LLThreadSafeRefCount { protected: - virtual ~LLEvent(); - + virtual ~LLEvent(); + public: - LLEvent(LLObservable* source, const std::string& desc = "") : mSource(source), mDesc(desc) { } - - LLObservable* getSource() { return mSource; } - virtual LLSD getValue() { return LLSD(); } - // Determines whether this particular listener - // should be notified of this event. - // If this function returns true, handleEvent is - // called on the listener with this event as the - // argument. - // Defaults to handling all events. Override this - // if associated with an Observable with many different listeners - virtual bool accept(LLEventListener* listener); - - // return a string describing the event - virtual const std::string& desc(); + LLEvent(LLObservable* source, const std::string& desc = "") : mSource(source), mDesc(desc) { } + + LLObservable* getSource() { return mSource; } + virtual LLSD getValue() { return LLSD(); } + // Determines whether this particular listener + // should be notified of this event. + // If this function returns true, handleEvent is + // called on the listener with this event as the + // argument. + // Defaults to handling all events. Override this + // if associated with an Observable with many different listeners + virtual bool accept(LLEventListener* listener); + + // return a string describing the event + virtual const std::string& desc(); private: - LLObservable* mSource; - std::string mDesc; + LLObservable* mSource; + std::string mDesc; }; // Abstract listener. All listeners derive from LLEventListener class LL_COMMON_API LLEventListener : public LLThreadSafeRefCount { protected: - virtual ~LLEventListener(); - + virtual ~LLEventListener(); + public: - // Processes the event. - // TODO: Make the return value less ambiguous? - virtual bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata) = 0; + // Processes the event. + // TODO: Make the return value less ambiguous? + virtual bool handleEvent(LLPointer<LLEvent> event, const LLSD& userdata) = 0; - // Called when an dispatcher starts/stops listening - virtual bool handleAttach(LLEventDispatcher *dispatcher) = 0; - virtual bool handleDetach(LLEventDispatcher *dispatcher) = 0; + // Called when an dispatcher starts/stops listening + virtual bool handleAttach(LLEventDispatcher *dispatcher) = 0; + virtual bool handleDetach(LLEventDispatcher *dispatcher) = 0; }; // A listener which tracks references to it and cleans up when it's deallocated class LL_COMMON_API LLSimpleListener : public LLEventListener { public: - void clearDispatchers(); - virtual bool handleAttach(LLEventDispatcher *dispatcher); - virtual bool handleDetach(LLEventDispatcher *dispatcher); + void clearDispatchers(); + virtual bool handleAttach(LLEventDispatcher *dispatcher); + virtual bool handleDetach(LLEventDispatcher *dispatcher); protected: - ~LLSimpleListener(); - std::vector<LLEventDispatcher *> mDispatchers; + ~LLSimpleListener(); + std::vector<LLEventDispatcher *> mDispatchers; }; class LLObservable; // defined below @@ -103,9 +103,9 @@ class LLObservable; // defined below // A structure which stores a Listener and its metadata struct LLListenerEntry { - LLEventListener* listener; - LLSD filter; - LLSD userdata; + LLEventListener* listener; + LLSD filter; + LLSD userdata; }; // Base class for a dispatcher - an object which listens @@ -114,40 +114,40 @@ struct LLListenerEntry class LL_COMMON_API LLEventDispatcher : public LLThreadSafeRefCount { protected: - virtual ~LLEventDispatcher(); - + virtual ~LLEventDispatcher(); + public: - // The default constructor creates a default simple dispatcher implementation. - // The simple implementation has an array of listeners and fires every event to - // all of them. - LLEventDispatcher(); - - // This dispatcher is being attached to an observable object. - // If we return false, the attach fails. - bool engage(LLObservable* observable); + // The default constructor creates a default simple dispatcher implementation. + // The simple implementation has an array of listeners and fires every event to + // all of them. + LLEventDispatcher(); + + // This dispatcher is being attached to an observable object. + // If we return false, the attach fails. + bool engage(LLObservable* observable); - // This dispatcher is being detached from an observable object. - void disengage(LLObservable* observable); + // This dispatcher is being detached from an observable object. + void disengage(LLObservable* observable); - // Adds a listener to this dispatcher, with a given user data - // that will be passed to the listener when an event is fired. - // Duplicate pointers are removed on addtion. - void addListener(LLEventListener *listener, LLSD filter, const LLSD& userdata); + // Adds a listener to this dispatcher, with a given user data + // that will be passed to the listener when an event is fired. + // Duplicate pointers are removed on addtion. + void addListener(LLEventListener *listener, LLSD filter, const LLSD& userdata); - // Removes a listener from this dispatcher - void removeListener(LLEventListener *listener); + // Removes a listener from this dispatcher + void removeListener(LLEventListener *listener); - // Gets a list of interested listeners - std::vector<LLListenerEntry> getListeners() const; + // Gets a list of interested listeners + std::vector<LLListenerEntry> getListeners() const; - // Handle an event that has just been fired by communicating it - // to listeners, passing it across a network, etc. - bool fireEvent(LLPointer<LLEvent> event, LLSD filter); + // Handle an event that has just been fired by communicating it + // to listeners, passing it across a network, etc. + bool fireEvent(LLPointer<LLEvent> event, LLSD filter); public: - class Impl; + class Impl; private: - Impl* impl; + Impl* impl; }; // Interface for observable data (data that fires events) @@ -157,38 +157,38 @@ private: class LL_COMMON_API LLObservable { public: - // Initialize with the default Dispatcher - LLObservable(); - virtual ~LLObservable(); - - // Replaces the existing dispatcher pointer to the new one, - // informing the dispatcher of the change. - virtual bool setDispatcher(LLPointer<LLEventDispatcher> dispatcher); - - // Returns the current dispatcher pointer. - virtual LLEventDispatcher* getDispatcher(); - - void addListener(LLEventListener *listener, LLSD filter = "", const LLSD& userdata = "") - { - if (mDispatcher.notNull()) mDispatcher->addListener(listener, filter, userdata); - } - void removeListener(LLEventListener *listener) - { - if (mDispatcher.notNull()) mDispatcher->removeListener(listener); - } - // Notifies the dispatcher of an event being fired. - void fireEvent(LLPointer<LLEvent> event, LLSD filter = LLSD()); + // Initialize with the default Dispatcher + LLObservable(); + virtual ~LLObservable(); + + // Replaces the existing dispatcher pointer to the new one, + // informing the dispatcher of the change. + virtual bool setDispatcher(LLPointer<LLEventDispatcher> dispatcher); + + // Returns the current dispatcher pointer. + virtual LLEventDispatcher* getDispatcher(); + + void addListener(LLEventListener *listener, LLSD filter = "", const LLSD& userdata = "") + { + if (mDispatcher.notNull()) mDispatcher->addListener(listener, filter, userdata); + } + void removeListener(LLEventListener *listener) + { + if (mDispatcher.notNull()) mDispatcher->removeListener(listener); + } + // Notifies the dispatcher of an event being fired. + void fireEvent(LLPointer<LLEvent> event, LLSD filter = LLSD()); protected: - LLPointer<LLEventDispatcher> mDispatcher; + LLPointer<LLEventDispatcher> mDispatcher; }; class LLValueChangedEvent : public LLEvent { public: - LLValueChangedEvent(LLObservable* source, LLSD value) : LLEvent(source, "value_changed"), mValue(value) { } - LLSD getValue() { return mValue; } - LLSD mValue; + LLValueChangedEvent(LLObservable* source, LLSD value) : LLEvent(source, "value_changed"), mValue(value) { } + LLSD getValue() { return mValue; } + LLSD mValue; }; } // LLOldEvents diff --git a/indra/llcommon/lleventapi.cpp b/indra/llcommon/lleventapi.cpp index 3d46ef1034..8b724256b8 100644 --- a/indra/llcommon/lleventapi.cpp +++ b/indra/llcommon/lleventapi.cpp @@ -3,25 +3,25 @@ * @author Nat Goodspeed * @date 2009-11-10 * @brief Implementation for lleventapi. - * + * * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llcommon/lleventapi.h b/indra/llcommon/lleventapi.h index 25f6becd8b..3ae820db51 100644 --- a/indra/llcommon/lleventapi.h +++ b/indra/llcommon/lleventapi.h @@ -5,25 +5,25 @@ * @brief LLEventAPI is the base class for every class that wraps a C++ API * in an event API * (see https://wiki.lindenlab.com/wiki/Incremental_Viewer_Automation/Event_API). - * + * * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llcommon/lleventcoro.cpp b/indra/llcommon/lleventcoro.cpp index 067b5e6fbc..e1fc4764f6 100644 --- a/indra/llcommon/lleventcoro.cpp +++ b/indra/llcommon/lleventcoro.cpp @@ -3,25 +3,25 @@ * @author Nat Goodspeed * @date 2009-04-29 * @brief Implementation for lleventcoro. - * + * * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llcommon/lleventcoro.h b/indra/llcommon/lleventcoro.h index c0fe8b094f..492563bb98 100644 --- a/indra/llcommon/lleventcoro.h +++ b/indra/llcommon/lleventcoro.h @@ -3,25 +3,25 @@ * @author Nat Goodspeed * @date 2009-04-29 * @brief Utilities to interface between coroutines and events. - * + * * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -152,7 +152,7 @@ LLSD postAndSuspendWithTimeout(const LLSD& event, F32 timeout, const LLSD& timeoutResult); /// Suspend the coroutine until an event is fired on the identified pump -/// or the timeout duration has elapsed. If the timeout duration +/// or the timeout duration has elapsed. If the timeout duration /// elapses the specified LLSD is returned. inline LLSD suspendUntilEventOnWithTimeout(const LLEventPumpOrPumpName& suspendPumpOrName, diff --git a/indra/llcommon/lleventdispatcher.cpp b/indra/llcommon/lleventdispatcher.cpp index da96de18f7..9dd6864cff 100644 --- a/indra/llcommon/lleventdispatcher.cpp +++ b/indra/llcommon/lleventdispatcher.cpp @@ -3,25 +3,25 @@ * @author Nat Goodspeed * @date 2009-06-18 * @brief Implementation for lleventdispatcher. - * + * * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -755,7 +755,7 @@ std::string LLEventDispatcher::getState() const bool LLEventDispatcher::setState(SetState&, const std::string& state) const { - // If SetState is instantiated at multiple levels of function call, ignore + // If SetState is instantiated at multiple levels of function call, ignore // the lower-level call because the outer call presumably provides more // context. if (mState.get()) diff --git a/indra/llcommon/lleventdispatcher.h b/indra/llcommon/lleventdispatcher.h index a82bc7a69b..4c3c0f3414 100644 --- a/indra/llcommon/lleventdispatcher.h +++ b/indra/llcommon/lleventdispatcher.h @@ -6,25 +6,25 @@ * 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=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -201,7 +201,7 @@ public: template <typename R, class CLASS, typename ARG, typename = typename std::enable_if< ! std::is_same<typename std::decay<ARG>::type, LLSD>::value - >::type> + >::type> void add(const std::string& name, const std::string& desc, R (CLASS::*method)(ARG)) @@ -213,7 +213,7 @@ public: template <typename R, class CLASS, typename ARG, typename = typename std::enable_if< ! std::is_same<typename std::decay<ARG>::type, LLSD>::value - >::type> + >::type> void add(const std::string& name, const std::string& desc, R (CLASS::*method)(ARG) const) @@ -226,7 +226,7 @@ public: template <class CLASS, typename ARG, typename = typename std::enable_if< ! std::is_same<typename std::decay<ARG>::type, LLSD>::value - >::type> + >::type> void add(const std::string& name, const std::string& desc, void (CLASS::*method)(ARG)) @@ -238,7 +238,7 @@ public: template <class CLASS, typename ARG, typename = typename std::enable_if< ! std::is_same<typename std::decay<ARG>::type, LLSD>::value - >::type> + >::type> void add(const std::string& name, const std::string& desc, void (CLASS::*method)(ARG) const) @@ -247,7 +247,7 @@ public: } // non-const binary (or more) method - template <typename R, class CLASS, typename ARG0, typename ARG1, typename... ARGS> + template <typename R, class CLASS, typename ARG0, typename ARG1, typename... ARGS> void add(const std::string& name, const std::string& desc, R (CLASS::*method)(ARG0, ARG1, ARGS...)) @@ -256,7 +256,7 @@ public: } // const binary (or more) method - template <typename R, class CLASS, typename ARG0, typename ARG1, typename... ARGS> + template <typename R, class CLASS, typename ARG0, typename ARG1, typename... ARGS> void add(const std::string& name, const std::string& desc, R (CLASS::*method)(ARG0, ARG1, ARGS...) const) @@ -265,7 +265,7 @@ public: } // non-const binary (or more) method returning void - template <class CLASS, typename ARG0, typename ARG1, typename... ARGS> + template <class CLASS, typename ARG0, typename ARG1, typename... ARGS> void add(const std::string& name, const std::string& desc, void (CLASS::*method)(ARG0, ARG1, ARGS...)) @@ -274,7 +274,7 @@ public: } // const binary (or more) method returning void - template <class CLASS, typename ARG0, typename ARG1, typename... ARGS> + template <class CLASS, typename ARG0, typename ARG1, typename... ARGS> void add(const std::string& name, const std::string& desc, void (CLASS::*method)(ARG0, ARG1, ARGS...) const) @@ -371,7 +371,7 @@ public: const InstanceGetter& getter, const LLSD& params, const LLSD& defaults=LLSD()); - //@} + //@} /// Unregister a callable bool remove(const std::string& name); diff --git a/indra/llcommon/lleventemitter.h b/indra/llcommon/lleventemitter.h index 3663032b8c..0079104715 100644 --- a/indra/llcommon/lleventemitter.h +++ b/indra/llcommon/lleventemitter.h @@ -1,102 +1,102 @@ -/** - * @file lleventemitter.h - * @brief General event emitter class - * - * $LicenseInfo:firstyear=2005&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -// header guard -#ifndef LL_EVENTEMITTER_H -#define LL_EVENTEMITTER_H - -// standard headers -#include <algorithm> -#include <typeinfo> -#include <iostream> -#include <string> -#include <list> - -#include "stdtypes.h" - -/////////////////////////////////////////////////////////////////////////////// -// templatized emitter class -template < class T > -class eventEmitter -{ - public: - typedef typename T::EventType EventType; - typedef std::list< T* > ObserverContainer; - typedef void ( T::*observerMethod )( const EventType& ); - - protected: - ObserverContainer observers; - - public: - eventEmitter () { }; - - ~eventEmitter () { }; - - /////////////////////////////////////////////////////////////////////////////// - // - bool addObserver ( T* observerIn ) - { - if ( ! observerIn ) - return false; - - // check if observer already exists - if ( std::find ( observers.begin (), observers.end (), observerIn ) != observers.end () ) - return false; - - // save it - observers.push_back ( observerIn ); - - return true; - }; - - /////////////////////////////////////////////////////////////////////////////// - // - bool remObserver ( T* observerIn ) - { - if ( ! observerIn ) - return false; - - observers.remove ( observerIn ); - - return true; - }; - - /////////////////////////////////////////////////////////////////////////////// - // - void update ( observerMethod method, const EventType& msgIn ) - { - typename std::list< T* >::iterator iter = observers.begin (); - - while ( iter != observers.end () ) - { - ( ( *iter )->*method ) ( msgIn ); - - ++iter; - }; - }; -}; - -#endif // lleventemitter_h +/**
+ * @file lleventemitter.h
+ * @brief General event emitter class
+ *
+ * $LicenseInfo:firstyear=2005&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+// header guard
+#ifndef LL_EVENTEMITTER_H
+#define LL_EVENTEMITTER_H
+
+// standard headers
+#include <algorithm>
+#include <typeinfo>
+#include <iostream>
+#include <string>
+#include <list>
+
+#include "stdtypes.h"
+
+///////////////////////////////////////////////////////////////////////////////
+// templatized emitter class
+template < class T >
+class eventEmitter
+{
+ public:
+ typedef typename T::EventType EventType;
+ typedef std::list< T* > ObserverContainer;
+ typedef void ( T::*observerMethod )( const EventType& );
+
+ protected:
+ ObserverContainer observers;
+
+ public:
+ eventEmitter () { };
+
+ ~eventEmitter () { };
+
+ ///////////////////////////////////////////////////////////////////////////////
+ //
+ bool addObserver ( T* observerIn )
+ {
+ if ( ! observerIn )
+ return false;
+
+ // check if observer already exists
+ if ( std::find ( observers.begin (), observers.end (), observerIn ) != observers.end () )
+ return false;
+
+ // save it
+ observers.push_back ( observerIn );
+
+ return true;
+ };
+
+ ///////////////////////////////////////////////////////////////////////////////
+ //
+ bool remObserver ( T* observerIn )
+ {
+ if ( ! observerIn )
+ return false;
+
+ observers.remove ( observerIn );
+
+ return true;
+ };
+
+ ///////////////////////////////////////////////////////////////////////////////
+ //
+ void update ( observerMethod method, const EventType& msgIn )
+ {
+ typename std::list< T* >::iterator iter = observers.begin ();
+
+ while ( iter != observers.end () )
+ {
+ ( ( *iter )->*method ) ( msgIn );
+
+ ++iter;
+ };
+ };
+};
+
+#endif // lleventemitter_h
diff --git a/indra/llcommon/lleventfilter.cpp b/indra/llcommon/lleventfilter.cpp index 4cded7f88e..604ee8a42d 100644 --- a/indra/llcommon/lleventfilter.cpp +++ b/indra/llcommon/lleventfilter.cpp @@ -3,25 +3,25 @@ * @author Nat Goodspeed * @date 2009-03-05 * @brief Implementation for lleventfilter. - * + * * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llcommon/lleventfilter.h b/indra/llcommon/lleventfilter.h index 1fb41e0297..5c45144fad 100644 --- a/indra/llcommon/lleventfilter.h +++ b/indra/llcommon/lleventfilter.h @@ -3,25 +3,25 @@ * @author Nat Goodspeed * @date 2009-03-05 * @brief Define LLEventFilter: LLEventStream subclass with conditions - * + * * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -149,11 +149,11 @@ public: /** * 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 @@ -161,7 +161,7 @@ public: * \ \ 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 @@ -197,8 +197,8 @@ private: /** * Production implementation of LLEventTimoutBase. - * - * @NOTE: Caution should be taken when using the LLEventTimeout(LLEventPump &) + * + * @NOTE: Caution should be taken when using the LLEventTimeout(LLEventPump &) * constructor to ensure that the upstream event pump is not an LLEventMaildrop * or any other kind of store and forward pump which may have events outstanding. * Using this constructor will cause the upstream event pump to fire any pending @@ -207,7 +207,7 @@ private: * from the event pump and attached using the listen method. * See llcoro::suspendUntilEventOnWithTimeout() for an example. */ - + class LL_COMMON_API LLEventTimeout: public LLEventTimeoutBase { public: diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp index 70931f3a65..5b4e69659d 100644 --- a/indra/llcommon/llevents.cpp +++ b/indra/llcommon/llevents.cpp @@ -3,25 +3,25 @@ * @author Nat Goodspeed * @date 2008-09-12 * @brief Implementation for llevents. - * + * * $LicenseInfo:firstyear=2008&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -423,8 +423,8 @@ LLBoundListener LLEventPump::listen_impl(const std::string& name, const LLEventL float nodePosition = 1.0; - // if the supplied name is empty we are not interested in the ordering mechanism - // and can bypass attempting to find the optimal location to insert the new + // if the supplied name is empty we are not interested in the ordering mechanism + // and can bypass attempting to find the optimal location to insert the new // listener. We'll just tack it on to the end. if (!name.empty()) // should be the same as testing against ANONYMOUS { @@ -569,12 +569,12 @@ 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(nodePosition, listener); - + if (!name.empty()) { // note that we are not tracking anonymous listeners here either. - // This means that it is the caller's responsibility to either assign - // to a TempBoundListerer (scoped_connection) or manually disconnect - // when done. + // This means that it is the caller's responsibility to either assign + // to a TempBoundListerer (scoped_connection) or manually disconnect + // when done. mConnections[name] = bound; } return bound; @@ -641,9 +641,9 @@ bool LLEventMailDrop::post(const LLSD& event) { // forward the call to our base class bool posted = LLEventStream::post(event); - + if (!posted) - { // if the event was not handled we will save it for later so that it can + { // if the event was not handled we will save it for later so that it can // be posted to any future listeners when they attach. mEventHistory.push_back(event); } diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h index bebcfacdcb..9a0a6863f0 100644 --- a/indra/llcommon/llevents.h +++ b/indra/llcommon/llevents.h @@ -6,25 +6,25 @@ * 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=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -39,13 +39,13 @@ #include <deque> #include <functional> #if LL_WINDOWS - #pragma warning (push) - #pragma warning (disable : 4263) // boost::signals2::expired_slot::what() has const mismatch - #pragma warning (disable : 4264) + #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) + #pragma warning (pop) #endif #include <boost/bind.hpp> @@ -310,9 +310,9 @@ public: /** * Find the named LLEventPump instance. If it exists post the message to it. * If the pump does not exist, do nothing. - * + * * returns the result of the LLEventPump::post. If no pump exists returns false. - * + * * This is syntactically similar to LLEventPumps::instance().post(name, message), * however if the pump does not already exist it will not be created. */ @@ -541,10 +541,10 @@ public: * instantiate your listener, then passing the same name on each listen() * call, allows us to optimize away the second and subsequent dependency * sorts. - * - * If name is set to LLEventPump::ANONYMOUS listen will bypass the entire - * dependency and ordering calculation. In this case, it is critical that - * the result be assigned to a LLTempBoundListener or the listener is + * + * If name is set to LLEventPump::ANONYMOUS listen will bypass the entire + * dependency and ordering calculation. In this case, it is critical that + * the result be assigned to a LLTempBoundListener or the listener is * manually disconnected when no longer needed since there will be no * way to later find and disconnect this listener manually. */ @@ -607,7 +607,7 @@ protected: virtual LLBoundListener listen_impl(const std::string& name, const LLEventListener&, const NameList& after, const NameList& before); - + /// implement the dispatching std::shared_ptr<LLStandardSignal> mSignal; @@ -660,21 +660,21 @@ public: * by all listeners, until some listener consumes it. The caveat is that each * event *must* eventually reach a listener that will consume it, else the * queue will grow to arbitrary length. - * + * * @NOTE: When using an LLEventMailDrop with an LLEventTimeout or * LLEventFilter attaching the filter downstream, using Timeout's constructor will - * cause the MailDrop to discharge any of its stored events. The timeout should - * instead be connected upstream using its listen() method. + * cause the MailDrop to discharge any of its stored events. The timeout should + * instead be connected upstream using its listen() method. */ class LL_COMMON_API LLEventMailDrop : public LLEventStream { public: LLEventMailDrop(const std::string& name, bool tweak = false) : LLEventStream(name, tweak) {} virtual ~LLEventMailDrop() {} - + /// Post an event to all listeners virtual bool post(const LLSD& event) override; - + /// Remove any history stored in the mail drop. void discard(); diff --git a/indra/llcommon/lleventtimer.cpp b/indra/llcommon/lleventtimer.cpp index f575a7b6bf..33fafffefd 100644 --- a/indra/llcommon/lleventtimer.cpp +++ b/indra/llcommon/lleventtimer.cpp @@ -1,25 +1,25 @@ -/** +/** * @file lleventtimer.cpp - * @brief Cross-platform objects for doing timing + * @brief Cross-platform objects for doing timing * * $LicenseInfo:firstyear=2000&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -33,20 +33,20 @@ ////////////////////////////////////////////////////////////////////////////// // -// LLEventTimer Implementation +// LLEventTimer Implementation // ////////////////////////////////////////////////////////////////////////////// LLEventTimer::LLEventTimer(F32 period) : mEventTimer() { - mPeriod = period; + mPeriod = period; } LLEventTimer::LLEventTimer(const LLDate& time) : mEventTimer() { - mPeriod = (F32)(time.secondsSinceEpoch() - LLDate::now().secondsSinceEpoch()); + mPeriod = (F32)(time.secondsSinceEpoch() - LLDate::now().secondsSinceEpoch()); } @@ -55,19 +55,19 @@ LLEventTimer::~LLEventTimer() } //static -void LLEventTimer::updateClass() +void LLEventTimer::updateClass() { - for (auto& timer : instance_snapshot()) - { - F32 et = timer.mEventTimer.getElapsedTimeF32(); - if (timer.mEventTimer.getStarted() && et > timer.mPeriod) { - timer.mEventTimer.reset(); - if ( timer.tick() ) - { - delete &timer; - } - } - } + for (auto& timer : instance_snapshot()) + { + F32 et = timer.mEventTimer.getElapsedTimeF32(); + if (timer.mEventTimer.getStarted() && et > timer.mPeriod) { + timer.mEventTimer.reset(); + if ( timer.tick() ) + { + delete &timer; + } + } + } } diff --git a/indra/llcommon/lleventtimer.h b/indra/llcommon/lleventtimer.h index 9bd34915a5..20c5fe7a82 100644 --- a/indra/llcommon/lleventtimer.h +++ b/indra/llcommon/lleventtimer.h @@ -1,122 +1,122 @@ -/** - * @file lleventtimer.h - * @brief Cross-platform objects for doing timing - * - * $LicenseInfo:firstyear=2000&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_EVENTTIMER_H -#define LL_EVENTTIMER_H - -#include "stdtypes.h" -#include "lldate.h" -#include "llinstancetracker.h" -#include "lltimer.h" - -// class for scheduling a function to be called at a given frequency (approximate, inprecise) -class LL_COMMON_API LLEventTimer : public LLInstanceTracker<LLEventTimer> -{ -public: - - LLEventTimer(F32 period); // period is the amount of time between each call to tick() in seconds - LLEventTimer(const LLDate& time); - virtual ~LLEventTimer(); - - //function to be called at the supplied frequency - // Normally return FALSE; TRUE will delete the timer after the function returns. - virtual bool tick() = 0; - - static void updateClass(); - - /// Schedule recurring calls to generic callable every period seconds. - /// Returns a pointer; if you delete it, cancels the recurring calls. - template <typename CALLABLE> - static LLEventTimer* run_every(F32 period, const CALLABLE& callable); - - /// Schedule a future call to generic callable. Returns a pointer. - /// CAUTION: The object referenced by that pointer WILL BE DELETED once - /// the callback has been called! LLEventTimer::getInstance(pointer) (NOT - /// pointer->getInstance(pointer)!) can be used to test whether the - /// pointer is still valid. If it is, deleting it will cancel the - /// callback. - template <typename CALLABLE> - static LLEventTimer* run_at(const LLDate& time, const CALLABLE& callable); - - /// Like run_at(), but after a time delta rather than at a timestamp. - /// Same CAUTION. - template <typename CALLABLE> - static LLEventTimer* run_after(F32 interval, const CALLABLE& callable); - -protected: - LLTimer mEventTimer; - F32 mPeriod; - -private: - template <typename CALLABLE> - class Generic; -}; - -template <typename CALLABLE> -class LLEventTimer::Generic: public LLEventTimer -{ -public: - // making TIME generic allows engaging either LLEventTimer constructor - template <typename TIME> - Generic(const TIME& time, bool once, const CALLABLE& callable): - LLEventTimer(time), - mOnce(once), - mCallable(callable) - {} - bool tick() override - { - mCallable(); - // true tells updateClass() to delete this instance - return mOnce; - } - -private: - bool mOnce; - CALLABLE mCallable; -}; - -template <typename CALLABLE> -LLEventTimer* LLEventTimer::run_every(F32 period, const CALLABLE& callable) -{ - // return false to schedule recurring calls - return new Generic<CALLABLE>(period, false, callable); -} - -template <typename CALLABLE> -LLEventTimer* LLEventTimer::run_at(const LLDate& time, const CALLABLE& callable) -{ - // return true for one-shot callback - return new Generic<CALLABLE>(time, true, callable); -} - -template <typename CALLABLE> -LLEventTimer* LLEventTimer::run_after(F32 interval, const CALLABLE& callable) -{ - // one-shot callback after specified interval - return new Generic<CALLABLE>(interval, true, callable); -} - -#endif //LL_EVENTTIMER_H +/**
+ * @file lleventtimer.h
+ * @brief Cross-platform objects for doing timing
+ *
+ * $LicenseInfo:firstyear=2000&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_EVENTTIMER_H
+#define LL_EVENTTIMER_H
+
+#include "stdtypes.h"
+#include "lldate.h"
+#include "llinstancetracker.h"
+#include "lltimer.h"
+
+// class for scheduling a function to be called at a given frequency (approximate, inprecise)
+class LL_COMMON_API LLEventTimer : public LLInstanceTracker<LLEventTimer>
+{
+public:
+
+ LLEventTimer(F32 period); // period is the amount of time between each call to tick() in seconds
+ LLEventTimer(const LLDate& time);
+ virtual ~LLEventTimer();
+
+ //function to be called at the supplied frequency
+ // Normally return FALSE; TRUE will delete the timer after the function returns.
+ virtual bool tick() = 0;
+
+ static void updateClass();
+
+ /// Schedule recurring calls to generic callable every period seconds.
+ /// Returns a pointer; if you delete it, cancels the recurring calls.
+ template <typename CALLABLE>
+ static LLEventTimer* run_every(F32 period, const CALLABLE& callable);
+
+ /// Schedule a future call to generic callable. Returns a pointer.
+ /// CAUTION: The object referenced by that pointer WILL BE DELETED once
+ /// the callback has been called! LLEventTimer::getInstance(pointer) (NOT
+ /// pointer->getInstance(pointer)!) can be used to test whether the
+ /// pointer is still valid. If it is, deleting it will cancel the
+ /// callback.
+ template <typename CALLABLE>
+ static LLEventTimer* run_at(const LLDate& time, const CALLABLE& callable);
+
+ /// Like run_at(), but after a time delta rather than at a timestamp.
+ /// Same CAUTION.
+ template <typename CALLABLE>
+ static LLEventTimer* run_after(F32 interval, const CALLABLE& callable);
+
+protected:
+ LLTimer mEventTimer;
+ F32 mPeriod;
+
+private:
+ template <typename CALLABLE>
+ class Generic;
+};
+
+template <typename CALLABLE>
+class LLEventTimer::Generic: public LLEventTimer
+{
+public:
+ // making TIME generic allows engaging either LLEventTimer constructor
+ template <typename TIME>
+ Generic(const TIME& time, bool once, const CALLABLE& callable):
+ LLEventTimer(time),
+ mOnce(once),
+ mCallable(callable)
+ {}
+ bool tick() override
+ {
+ mCallable();
+ // true tells updateClass() to delete this instance
+ return mOnce;
+ }
+
+private:
+ bool mOnce;
+ CALLABLE mCallable;
+};
+
+template <typename CALLABLE>
+LLEventTimer* LLEventTimer::run_every(F32 period, const CALLABLE& callable)
+{
+ // return false to schedule recurring calls
+ return new Generic<CALLABLE>(period, false, callable);
+}
+
+template <typename CALLABLE>
+LLEventTimer* LLEventTimer::run_at(const LLDate& time, const CALLABLE& callable)
+{
+ // return true for one-shot callback
+ return new Generic<CALLABLE>(time, true, callable);
+}
+
+template <typename CALLABLE>
+LLEventTimer* LLEventTimer::run_after(F32 interval, const CALLABLE& callable)
+{
+ // one-shot callback after specified interval
+ return new Generic<CALLABLE>(interval, true, callable);
+}
+
+#endif //LL_EVENTTIMER_H
diff --git a/indra/llcommon/llexception.cpp b/indra/llcommon/llexception.cpp index 0787bde57f..c0154a569f 100644 --- a/indra/llcommon/llexception.cpp +++ b/indra/llcommon/llexception.cpp @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2016-08-12 * @brief Implementation for llexception. - * + * * $LicenseInfo:firstyear=2016&license=viewerlgpl$ * Copyright (c) 2016, Linden Research, Inc. * $/LicenseInfo$ @@ -99,8 +99,8 @@ static const U32 STATUS_MSC_EXCEPTION = 0xE06D7363; // compiler specific U32 msc_exception_filter(U32 code, struct _EXCEPTION_POINTERS *exception_infop) { const auto stack = to_string(boost::stacktrace::stacktrace()); - LL_WARNS() << "SEH Exception handled (that probably shouldn't be): Code " << code - << "\n Stack trace: \n" + LL_WARNS() << "SEH Exception handled (that probably shouldn't be): Code " << code + << "\n Stack trace: \n" << stack << LL_ENDL; if (code == STATUS_MSC_EXCEPTION) diff --git a/indra/llcommon/llexception.h b/indra/llcommon/llexception.h index 375bea4a57..68e609444e 100644 --- a/indra/llcommon/llexception.h +++ b/indra/llcommon/llexception.h @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2016-06-29 * @brief Types needed for generic exception handling - * + * * $LicenseInfo:firstyear=2016&license=viewerlgpl$ * Copyright (c) 2016, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/llfasttimer.cpp b/indra/llcommon/llfasttimer.cpp index 2612d0f07c..722743f453 100644 --- a/indra/llcommon/llfasttimer.cpp +++ b/indra/llcommon/llfasttimer.cpp @@ -1,25 +1,25 @@ -/** +/** * @file llfasttimer.cpp * @brief Implementation of the fast timer. * * $LicenseInfo:firstyear=2004&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -49,8 +49,8 @@ #include "lltimer.h" #elif LL_DARWIN #include <sys/time.h> -#include "lltimer.h" // get_clock_count() -#else +#include "lltimer.h" // get_clock_count() +#else #error "architecture not supported" #endif @@ -60,7 +60,7 @@ namespace LLTrace ////////////////////////////////////////////////////////////////////////////// // statics -bool BlockTimer::sLog = false; +bool BlockTimer::sLog = false; std::string BlockTimer::sLogName = ""; bool BlockTimer::sMetricLog = false; @@ -70,83 +70,83 @@ U64 BlockTimer::sClockResolution = 1000000000; // Nanosecond resolution U64 BlockTimer::sClockResolution = 1000000; // Microsecond resolution #endif -static LLMutex* sLogLock = NULL; +static LLMutex* sLogLock = NULL; static std::queue<LLSD> sLogQueue; -block_timer_tree_df_iterator_t begin_block_timer_tree_df(BlockTimerStatHandle& id) -{ - return block_timer_tree_df_iterator_t(&id, - boost::bind(boost::mem_fn(&BlockTimerStatHandle::beginChildren), _1), - boost::bind(boost::mem_fn(&BlockTimerStatHandle::endChildren), _1)); +block_timer_tree_df_iterator_t begin_block_timer_tree_df(BlockTimerStatHandle& id) +{ + return block_timer_tree_df_iterator_t(&id, + boost::bind(boost::mem_fn(&BlockTimerStatHandle::beginChildren), _1), + boost::bind(boost::mem_fn(&BlockTimerStatHandle::endChildren), _1)); } -block_timer_tree_df_iterator_t end_block_timer_tree_df() -{ - return block_timer_tree_df_iterator_t(); +block_timer_tree_df_iterator_t end_block_timer_tree_df() +{ + return block_timer_tree_df_iterator_t(); } -block_timer_tree_df_post_iterator_t begin_block_timer_tree_df_post(BlockTimerStatHandle& id) -{ - return block_timer_tree_df_post_iterator_t(&id, - boost::bind(boost::mem_fn(&BlockTimerStatHandle::beginChildren), _1), - boost::bind(boost::mem_fn(&BlockTimerStatHandle::endChildren), _1)); +block_timer_tree_df_post_iterator_t begin_block_timer_tree_df_post(BlockTimerStatHandle& id) +{ + return block_timer_tree_df_post_iterator_t(&id, + boost::bind(boost::mem_fn(&BlockTimerStatHandle::beginChildren), _1), + boost::bind(boost::mem_fn(&BlockTimerStatHandle::endChildren), _1)); } -block_timer_tree_df_post_iterator_t end_block_timer_tree_df_post() -{ - return block_timer_tree_df_post_iterator_t(); +block_timer_tree_df_post_iterator_t end_block_timer_tree_df_post() +{ + return block_timer_tree_df_post_iterator_t(); } block_timer_tree_bf_iterator_t begin_block_timer_tree_bf(BlockTimerStatHandle& id) -{ - return block_timer_tree_bf_iterator_t(&id, - boost::bind(boost::mem_fn(&BlockTimerStatHandle::beginChildren), _1), - boost::bind(boost::mem_fn(&BlockTimerStatHandle::endChildren), _1)); +{ + return block_timer_tree_bf_iterator_t(&id, + boost::bind(boost::mem_fn(&BlockTimerStatHandle::beginChildren), _1), + boost::bind(boost::mem_fn(&BlockTimerStatHandle::endChildren), _1)); } block_timer_tree_bf_iterator_t end_block_timer_tree_bf() - { - return block_timer_tree_bf_iterator_t(); - } - -block_timer_tree_df_iterator_t begin_timer_tree(BlockTimerStatHandle& id) - { - return block_timer_tree_df_iterator_t(&id, - boost::bind(boost::mem_fn(&BlockTimerStatHandle::beginChildren), _1), - boost::bind(boost::mem_fn(&BlockTimerStatHandle::endChildren), _1)); - } - -block_timer_tree_df_iterator_t end_timer_tree() - { - return block_timer_tree_df_iterator_t(); + { + return block_timer_tree_bf_iterator_t(); + } + +block_timer_tree_df_iterator_t begin_timer_tree(BlockTimerStatHandle& id) + { + return block_timer_tree_df_iterator_t(&id, + boost::bind(boost::mem_fn(&BlockTimerStatHandle::beginChildren), _1), + boost::bind(boost::mem_fn(&BlockTimerStatHandle::endChildren), _1)); + } + +block_timer_tree_df_iterator_t end_timer_tree() + { + return block_timer_tree_df_iterator_t(); } // sort child timers by name struct SortTimerByName - { - bool operator()(const BlockTimerStatHandle* i1, const BlockTimerStatHandle* i2) - { - return i1->getName() < i2->getName(); - } + { + bool operator()(const BlockTimerStatHandle* i1, const BlockTimerStatHandle* i2) + { + return i1->getName() < i2->getName(); + } }; static BlockTimerStatHandle sRootTimer("root", NULL); BlockTimerStatHandle& BlockTimer::getRootTimeBlock() { - return sRootTimer; - } + return sRootTimer; + } void BlockTimer::pushLog(LLSD log) { - LLMutexLock lock(sLogLock); + LLMutexLock lock(sLogLock); - sLogQueue.push(log); + sLogQueue.push(log); } void BlockTimer::setLogLock(LLMutex* lock) { - sLogLock = lock; + sLogLock = lock; } @@ -154,40 +154,40 @@ void BlockTimer::setLogLock(LLMutex* lock) #if (LL_DARWIN || LL_LINUX) && !(defined(__i386__) || defined(__amd64__)) U64 BlockTimer::countsPerSecond() { - return sClockResolution; + return sClockResolution; } #else // windows or x86-mac or x86-linux U64 BlockTimer::countsPerSecond() { #if LL_FASTTIMER_USE_RDTSC || !LL_WINDOWS - //getCPUFrequency returns MHz and sCPUClockFrequency wants to be in Hz - static LLUnit<U64, LLUnits::Hertz> sCPUClockFrequency = LLProcessorInfo().getCPUFrequency(); - return sCPUClockFrequency.value(); + //getCPUFrequency returns MHz and sCPUClockFrequency wants to be in Hz + static LLUnit<U64, LLUnits::Hertz> sCPUClockFrequency = LLProcessorInfo().getCPUFrequency(); + return sCPUClockFrequency.value(); #else - // If we're not using RDTSC, each fasttimer tick is just a performance counter tick. - // Not redefining the clock frequency itself (in llprocessor.cpp/calculate_cpu_frequency()) - // since that would change displayed MHz stats for CPUs - static bool firstcall = true; - static U64 sCPUClockFrequency; - if (firstcall) - { - QueryPerformanceFrequency((LARGE_INTEGER*)&sCPUClockFrequency); - firstcall = false; - } - return sCPUClockFrequency.value(); + // If we're not using RDTSC, each fasttimer tick is just a performance counter tick. + // Not redefining the clock frequency itself (in llprocessor.cpp/calculate_cpu_frequency()) + // since that would change displayed MHz stats for CPUs + static bool firstcall = true; + static U64 sCPUClockFrequency; + if (firstcall) + { + QueryPerformanceFrequency((LARGE_INTEGER*)&sCPUClockFrequency); + firstcall = false; + } + return sCPUClockFrequency.value(); #endif } #endif BlockTimerStatHandle::BlockTimerStatHandle(const char* name, const char* description) -: StatType<TimeBlockAccumulator>(name, description) +: StatType<TimeBlockAccumulator>(name, description) {} TimeBlockTreeNode& BlockTimerStatHandle::getTreeNode() const { - TimeBlockTreeNode* nodep = LLTrace::get_thread_recorder()->getTimeBlockTreeNode(getIndex()); - llassert(nodep); - return *nodep; + TimeBlockTreeNode* nodep = LLTrace::get_thread_recorder()->getTimeBlockTreeNode(getIndex()); + llassert(nodep); + return *nodep; } @@ -223,71 +223,71 @@ void BlockTimer::bootstrapTimerTree() void BlockTimer::incrementalUpdateTimerTree() { LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - for(block_timer_tree_df_post_iterator_t it = begin_block_timer_tree_df_post(BlockTimer::getRootTimeBlock()); - it != end_block_timer_tree_df_post(); - ++it) - { - BlockTimerStatHandle* timerp = *it; - - // sort timers by time last called, so call graph makes sense - TimeBlockTreeNode& tree_node = timerp->getTreeNode(); - if (tree_node.mNeedsSorting) + for(block_timer_tree_df_post_iterator_t it = begin_block_timer_tree_df_post(BlockTimer::getRootTimeBlock()); + it != end_block_timer_tree_df_post(); + ++it) + { + BlockTimerStatHandle* timerp = *it; + + // sort timers by time last called, so call graph makes sense + TimeBlockTreeNode& tree_node = timerp->getTreeNode(); + if (tree_node.mNeedsSorting) { - std::sort(tree_node.mChildren.begin(), tree_node.mChildren.end(), SortTimerByName()); + std::sort(tree_node.mChildren.begin(), tree_node.mChildren.end(), SortTimerByName()); } - // skip root timer - if (timerp != &BlockTimer::getRootTimeBlock()) + // skip root timer + if (timerp != &BlockTimer::getRootTimeBlock()) { - TimeBlockAccumulator& accumulator = timerp->getCurrentAccumulator(); + TimeBlockAccumulator& accumulator = timerp->getCurrentAccumulator(); - if (accumulator.mMoveUpTree) + if (accumulator.mMoveUpTree) { - // since ancestors have already been visited, re-parenting won't affect tree traversal - //step up tree, bringing our descendants with us - LL_DEBUGS("FastTimers") << "Moving " << timerp->getName() << " from child of " << timerp->getParent()->getName() << - " to child of " << timerp->getParent()->getParent()->getName() << LL_ENDL; - timerp->setParent(timerp->getParent()->getParent()); - accumulator.mParent = timerp->getParent(); - accumulator.mMoveUpTree = false; - - // don't bubble up any ancestors until descendants are done bubbling up - // as ancestors may call this timer only on certain paths, so we want to resolve - // child-most block locations before their parents - it.skipAncestors(); - } - } - } + // since ancestors have already been visited, re-parenting won't affect tree traversal + //step up tree, bringing our descendants with us + LL_DEBUGS("FastTimers") << "Moving " << timerp->getName() << " from child of " << timerp->getParent()->getName() << + " to child of " << timerp->getParent()->getParent()->getName() << LL_ENDL; + timerp->setParent(timerp->getParent()->getParent()); + accumulator.mParent = timerp->getParent(); + accumulator.mMoveUpTree = false; + + // don't bubble up any ancestors until descendants are done bubbling up + // as ancestors may call this timer only on certain paths, so we want to resolve + // child-most block locations before their parents + it.skipAncestors(); + } + } + } } void BlockTimer::updateTimes() { LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - // walk up stack of active timers and accumulate current time while leaving timing structures active - BlockTimerStackRecord* stack_record = LLThreadLocalSingletonPointer<BlockTimerStackRecord>::getInstance(); - if (!stack_record) return; - - U64 cur_time = getCPUClockCount64(); - BlockTimer* cur_timer = stack_record->mActiveTimer; - TimeBlockAccumulator* accumulator = &stack_record->mTimeBlock->getCurrentAccumulator(); - - while(cur_timer - && cur_timer->mParentTimerData.mActiveTimer != cur_timer) // root defined by parent pointing to self - { - U64 cumulative_time_delta = cur_time - cur_timer->mStartTime; - cur_timer->mStartTime = cur_time; - - accumulator->mTotalTimeCounter += cumulative_time_delta; - accumulator->mSelfTimeCounter += cumulative_time_delta - stack_record->mChildTime; - stack_record->mChildTime = 0; - - stack_record = &cur_timer->mParentTimerData; - accumulator = &stack_record->mTimeBlock->getCurrentAccumulator(); - cur_timer = stack_record->mActiveTimer; - - stack_record->mChildTime += cumulative_time_delta; - } + // walk up stack of active timers and accumulate current time while leaving timing structures active + BlockTimerStackRecord* stack_record = LLThreadLocalSingletonPointer<BlockTimerStackRecord>::getInstance(); + if (!stack_record) return; + + U64 cur_time = getCPUClockCount64(); + BlockTimer* cur_timer = stack_record->mActiveTimer; + TimeBlockAccumulator* accumulator = &stack_record->mTimeBlock->getCurrentAccumulator(); + + while(cur_timer + && cur_timer->mParentTimerData.mActiveTimer != cur_timer) // root defined by parent pointing to self + { + U64 cumulative_time_delta = cur_time - cur_timer->mStartTime; + cur_timer->mStartTime = cur_time; + + accumulator->mTotalTimeCounter += cumulative_time_delta; + accumulator->mSelfTimeCounter += cumulative_time_delta - stack_record->mChildTime; + stack_record->mChildTime = 0; + + stack_record = &cur_timer->mParentTimerData; + accumulator = &stack_record->mTimeBlock->getCurrentAccumulator(); + cur_timer = stack_record->mActiveTimer; + + stack_record->mChildTime += cumulative_time_delta; + } } static LLTrace::BlockTimerStatHandle FTM_PROCESS_TIMES("Process FastTimer Times"); @@ -297,192 +297,192 @@ static LLTrace::BlockTimerStatHandle FTM_PROCESS_TIMES("Process FastTimer Times" void BlockTimer::processTimes() { #if LL_TRACE_ENABLED - LL_RECORD_BLOCK_TIME(FTM_PROCESS_TIMES); - get_clock_count(); // good place to calculate clock frequency + LL_RECORD_BLOCK_TIME(FTM_PROCESS_TIMES); + get_clock_count(); // good place to calculate clock frequency - // set up initial tree - bootstrapTimerTree(); + // set up initial tree + bootstrapTimerTree(); - incrementalUpdateTimerTree(); + incrementalUpdateTimerTree(); - updateTimes(); + updateTimes(); - // reset for next frame - for (auto& base : BlockTimerStatHandle::instance_snapshot()) - { - // because of indirect derivation from LLInstanceTracker, have to downcast - BlockTimerStatHandle& timer = static_cast<BlockTimerStatHandle&>(base); - TimeBlockAccumulator& accumulator = timer.getCurrentAccumulator(); + // reset for next frame + for (auto& base : BlockTimerStatHandle::instance_snapshot()) + { + // because of indirect derivation from LLInstanceTracker, have to downcast + BlockTimerStatHandle& timer = static_cast<BlockTimerStatHandle&>(base); + TimeBlockAccumulator& accumulator = timer.getCurrentAccumulator(); - accumulator.mLastCaller = NULL; - accumulator.mMoveUpTree = false; - } + accumulator.mLastCaller = NULL; + accumulator.mMoveUpTree = false; + } #endif } std::vector<BlockTimerStatHandle*>::iterator BlockTimerStatHandle::beginChildren() - { - return getTreeNode().mChildren.begin(); - } + { + return getTreeNode().mChildren.begin(); + } std::vector<BlockTimerStatHandle*>::iterator BlockTimerStatHandle::endChildren() - { - return getTreeNode().mChildren.end(); + { + return getTreeNode().mChildren.end(); } std::vector<BlockTimerStatHandle*>& BlockTimerStatHandle::getChildren() { - return getTreeNode().mChildren; - } + return getTreeNode().mChildren; + } bool BlockTimerStatHandle::hasChildren() { - return ! getTreeNode().mChildren.empty(); + return ! getTreeNode().mChildren.empty(); } // static void BlockTimer::logStats() { - // get ready for next frame - if (sLog) - { //output current frame counts to performance log - - static S32 call_count = 0; - if (call_count % 100 == 0) - { - LL_DEBUGS("FastTimers") << "countsPerSecond: " << countsPerSecond() << LL_ENDL; - LL_DEBUGS("FastTimers") << "LLProcessorInfo().getCPUFrequency() " << LLProcessorInfo().getCPUFrequency() << LL_ENDL; - LL_DEBUGS("FastTimers") << "getCPUClockCount32() " << getCPUClockCount32() << LL_ENDL; - LL_DEBUGS("FastTimers") << "getCPUClockCount64() " << getCPUClockCount64() << LL_ENDL; - LL_DEBUGS("FastTimers") << "elapsed sec " << ((F64)getCPUClockCount64() / (F64HertzImplicit)LLProcessorInfo().getCPUFrequency()) << LL_ENDL; - } - call_count++; - - F64Seconds total_time(0); - LLSD sd; - - { - for (auto& base : BlockTimerStatHandle::instance_snapshot()) - { - // because of indirect derivation from LLInstanceTracker, have to downcast - BlockTimerStatHandle& timer = static_cast<BlockTimerStatHandle&>(base); - LLTrace::PeriodicRecording& frame_recording = LLTrace::get_frame_recording(); - sd[timer.getName()]["Time"] = (LLSD::Real) (frame_recording.getLastRecording().getSum(timer).value()); - sd[timer.getName()]["Calls"] = (LLSD::Integer) (frame_recording.getLastRecording().getSum(timer.callCount())); - - // computing total time here because getting the root timer's getCountHistory - // doesn't work correctly on the first frame - total_time += frame_recording.getLastRecording().getSum(timer); - } - } - - sd["Total"]["Time"] = (LLSD::Real) total_time.value(); - sd["Total"]["Calls"] = (LLSD::Integer) 1; - - { - LLMutexLock lock(sLogLock); - sLogQueue.push(sd); - } - } + // get ready for next frame + if (sLog) + { //output current frame counts to performance log + + static S32 call_count = 0; + if (call_count % 100 == 0) + { + LL_DEBUGS("FastTimers") << "countsPerSecond: " << countsPerSecond() << LL_ENDL; + LL_DEBUGS("FastTimers") << "LLProcessorInfo().getCPUFrequency() " << LLProcessorInfo().getCPUFrequency() << LL_ENDL; + LL_DEBUGS("FastTimers") << "getCPUClockCount32() " << getCPUClockCount32() << LL_ENDL; + LL_DEBUGS("FastTimers") << "getCPUClockCount64() " << getCPUClockCount64() << LL_ENDL; + LL_DEBUGS("FastTimers") << "elapsed sec " << ((F64)getCPUClockCount64() / (F64HertzImplicit)LLProcessorInfo().getCPUFrequency()) << LL_ENDL; + } + call_count++; + + F64Seconds total_time(0); + LLSD sd; + + { + for (auto& base : BlockTimerStatHandle::instance_snapshot()) + { + // because of indirect derivation from LLInstanceTracker, have to downcast + BlockTimerStatHandle& timer = static_cast<BlockTimerStatHandle&>(base); + LLTrace::PeriodicRecording& frame_recording = LLTrace::get_frame_recording(); + sd[timer.getName()]["Time"] = (LLSD::Real) (frame_recording.getLastRecording().getSum(timer).value()); + sd[timer.getName()]["Calls"] = (LLSD::Integer) (frame_recording.getLastRecording().getSum(timer.callCount())); + + // computing total time here because getting the root timer's getCountHistory + // doesn't work correctly on the first frame + total_time += frame_recording.getLastRecording().getSum(timer); + } + } + + sd["Total"]["Time"] = (LLSD::Real) total_time.value(); + sd["Total"]["Calls"] = (LLSD::Integer) 1; + + { + LLMutexLock lock(sLogLock); + sLogQueue.push(sd); + } + } } //static void BlockTimer::dumpCurTimes() { - LLTrace::PeriodicRecording& frame_recording = LLTrace::get_frame_recording(); - LLTrace::Recording& last_frame_recording = frame_recording.getLastRecording(); - - // walk over timers in depth order and output timings - for(block_timer_tree_df_iterator_t it = begin_timer_tree(BlockTimer::getRootTimeBlock()); - it != end_timer_tree(); - ++it) - { - BlockTimerStatHandle* timerp = (*it); - F64Seconds total_time = last_frame_recording.getSum(*timerp); - U32 num_calls = last_frame_recording.getSum(timerp->callCount()); - - // Don't bother with really brief times, keep output concise - if (total_time < F32Milliseconds(0.1f)) continue; - - std::ostringstream out_str; - BlockTimerStatHandle* parent_timerp = timerp; - while(parent_timerp && parent_timerp != parent_timerp->getParent()) - { - out_str << "\t"; - parent_timerp = parent_timerp->getParent(); - } - - out_str << timerp->getName() << " " - << std::setprecision(3) << total_time.valueInUnits<LLUnits::Milliseconds>() << " ms, " - << num_calls << " calls"; - - LL_INFOS() << out_str.str() << LL_ENDL; + LLTrace::PeriodicRecording& frame_recording = LLTrace::get_frame_recording(); + LLTrace::Recording& last_frame_recording = frame_recording.getLastRecording(); + + // walk over timers in depth order and output timings + for(block_timer_tree_df_iterator_t it = begin_timer_tree(BlockTimer::getRootTimeBlock()); + it != end_timer_tree(); + ++it) + { + BlockTimerStatHandle* timerp = (*it); + F64Seconds total_time = last_frame_recording.getSum(*timerp); + U32 num_calls = last_frame_recording.getSum(timerp->callCount()); + + // Don't bother with really brief times, keep output concise + if (total_time < F32Milliseconds(0.1f)) continue; + + std::ostringstream out_str; + BlockTimerStatHandle* parent_timerp = timerp; + while(parent_timerp && parent_timerp != parent_timerp->getParent()) + { + out_str << "\t"; + parent_timerp = parent_timerp->getParent(); + } + + out_str << timerp->getName() << " " + << std::setprecision(3) << total_time.valueInUnits<LLUnits::Milliseconds>() << " ms, " + << num_calls << " calls"; + + LL_INFOS() << out_str.str() << LL_ENDL; } } //static void BlockTimer::writeLog(std::ostream& os) { - while (!sLogQueue.empty()) - { - LLSD& sd = sLogQueue.front(); - LLSDSerialize::toXML(sd, os); - LLMutexLock lock(sLogLock); - sLogQueue.pop(); - } + while (!sLogQueue.empty()) + { + LLSD& sd = sLogQueue.front(); + LLSDSerialize::toXML(sd, os); + LLMutexLock lock(sLogLock); + sLogQueue.pop(); + } } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // TimeBlockAccumulator ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -TimeBlockAccumulator::TimeBlockAccumulator() -: mTotalTimeCounter(0), - mSelfTimeCounter(0), - mCalls(0), - mLastCaller(NULL), - mActiveCount(0), - mMoveUpTree(false), - mParent(NULL) +TimeBlockAccumulator::TimeBlockAccumulator() +: mTotalTimeCounter(0), + mSelfTimeCounter(0), + mCalls(0), + mLastCaller(NULL), + mActiveCount(0), + mMoveUpTree(false), + mParent(NULL) {} void TimeBlockAccumulator::addSamples( const TimeBlockAccumulator& other, EBufferAppendType append_type ) { #if LL_TRACE_ENABLED - // we can't merge two unrelated time block samples, as that will screw with the nested timings - // due to the call hierarchy of each thread - llassert(append_type == SEQUENTIAL); - mTotalTimeCounter += other.mTotalTimeCounter; - mSelfTimeCounter += other.mSelfTimeCounter; - mCalls += other.mCalls; - mLastCaller = other.mLastCaller; - mActiveCount = other.mActiveCount; - mMoveUpTree = other.mMoveUpTree; - mParent = other.mParent; + // we can't merge two unrelated time block samples, as that will screw with the nested timings + // due to the call hierarchy of each thread + llassert(append_type == SEQUENTIAL); + mTotalTimeCounter += other.mTotalTimeCounter; + mSelfTimeCounter += other.mSelfTimeCounter; + mCalls += other.mCalls; + mLastCaller = other.mLastCaller; + mActiveCount = other.mActiveCount; + mMoveUpTree = other.mMoveUpTree; + mParent = other.mParent; #endif } void TimeBlockAccumulator::reset( const TimeBlockAccumulator* other ) { - mCalls = 0; - mSelfTimeCounter = 0; - mTotalTimeCounter = 0; + mCalls = 0; + mSelfTimeCounter = 0; + mTotalTimeCounter = 0; - if (other) + if (other) { - mLastCaller = other->mLastCaller; - mActiveCount = other->mActiveCount; - mMoveUpTree = other->mMoveUpTree; - mParent = other->mParent; - } + mLastCaller = other->mLastCaller; + mActiveCount = other->mActiveCount; + mMoveUpTree = other->mMoveUpTree; + mParent = other->mParent; + } } F64Seconds BlockTimer::getElapsedTime() { - U64 total_time = getCPUClockCount64() - mStartTime; + U64 total_time = getCPUClockCount64() - mStartTime; - return F64Seconds((F64)total_time / (F64)BlockTimer::countsPerSecond()); + return F64Seconds((F64)total_time / (F64)BlockTimer::countsPerSecond()); } diff --git a/indra/llcommon/llfasttimer.h b/indra/llcommon/llfasttimer.h index 9bd93d7240..17ad37b031 100644 --- a/indra/llcommon/llfasttimer.h +++ b/indra/llcommon/llfasttimer.h @@ -5,21 +5,21 @@ * $LicenseInfo:firstyear=2004&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -51,77 +51,77 @@ class BlockTimer timeThisBlock(class BlockTimerStatHandle& timer); class BlockTimer { public: - typedef BlockTimer self_t; - typedef class BlockTimerStatHandle DeclareTimer; + typedef BlockTimer self_t; + typedef class BlockTimerStatHandle DeclareTimer; - ~BlockTimer(); + ~BlockTimer(); - F64Seconds getElapsedTime(); + F64Seconds getElapsedTime(); - ////////////////////////////////////////////////////////////////////////////// - // - // Important note: These implementations must be FAST! - // + ////////////////////////////////////////////////////////////////////////////// + // + // Important note: These implementations must be FAST! + // #if LL_WINDOWS - // - // Windows implementation of CPU clock - // - - // - // NOTE: put back in when we aren't using platform sdk anymore - // - // because MS has different signatures for these functions in winnt.h - // need to rename them to avoid conflicts - //#define _interlockedbittestandset _renamed_interlockedbittestandset - //#define _interlockedbittestandreset _renamed_interlockedbittestandreset - //#include <intrin.h> - //#undef _interlockedbittestandset - //#undef _interlockedbittestandreset - - //inline U32 getCPUClockCount32() - //{ - // U64 time_stamp = __rdtsc(); - // return (U32)(time_stamp >> 8); - //} - // - //// return full timer value, *not* shifted by 8 bits - //inline U64 getCPUClockCount64() - //{ - // return __rdtsc(); - //} - - - - // 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 + // + // Windows implementation of CPU clock + // + + // + // NOTE: put back in when we aren't using platform sdk anymore + // + // because MS has different signatures for these functions in winnt.h + // need to rename them to avoid conflicts + //#define _interlockedbittestandset _renamed_interlockedbittestandset + //#define _interlockedbittestandreset _renamed_interlockedbittestandreset + //#include <intrin.h> + //#undef _interlockedbittestandset + //#undef _interlockedbittestandreset + + //inline U32 getCPUClockCount32() + //{ + // U64 time_stamp = __rdtsc(); + // return (U32)(time_stamp >> 8); + //} + // + //// return full timer value, *not* shifted by 8 bits + //inline U64 getCPUClockCount64() + //{ + // return __rdtsc(); + //} + + + + // 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 #if LL_FASTTIMER_USE_RDTSC - static U32 getCPUClockCount32() - { - unsigned __int64 val = __rdtsc(); - val = val >> 8; - return static_cast<U32>(val); - } - - // return full timer value, *not* shifted by 8 bits - static U64 getCPUClockCount64() - { - return static_cast<U64>( __rdtsc() ); - } + static U32 getCPUClockCount32() + { + unsigned __int64 val = __rdtsc(); + val = val >> 8; + return static_cast<U32>(val); + } + + // return full timer value, *not* shifted by 8 bits + static U64 getCPUClockCount64() + { + return static_cast<U64>( __rdtsc() ); + } #else - //U64 get_clock_count(); // in lltimer.cpp - // These use QueryPerformanceCounter, which is arguably fine and also works on AMD architectures. - static U32 getCPUClockCount32() - { - return (U32)(get_clock_count()>>8); - } - - static U64 getCPUClockCount64() - { - return get_clock_count(); - } + //U64 get_clock_count(); // in lltimer.cpp + // These use QueryPerformanceCounter, which is arguably fine and also works on AMD architectures. + static U32 getCPUClockCount32() + { + return (U32)(get_clock_count()>>8); + } + + static U64 getCPUClockCount64() + { + return get_clock_count(); + } #endif @@ -129,142 +129,142 @@ public: #if (LL_LINUX) && !(defined(__i386__) || defined(__amd64__)) - // - // Linux implementation of CPU clock - non-x86. - // This is accurate but SLOW! Only use out of desperation. - // - // Try to use the MONOTONIC clock if available, this is a constant time counter - // with nanosecond resolution (but not necessarily accuracy) and attempts are - // made to synchronize this value between cores at kernel start. It should not - // be affected by CPU frequency. If not available use the REALTIME clock, but - // this may be affected by NTP adjustments or other user activity affecting - // the system time. - static U64 getCPUClockCount64() - { - struct timespec tp; + // + // Linux implementation of CPU clock - non-x86. + // This is accurate but SLOW! Only use out of desperation. + // + // Try to use the MONOTONIC clock if available, this is a constant time counter + // with nanosecond resolution (but not necessarily accuracy) and attempts are + // made to synchronize this value between cores at kernel start. It should not + // be affected by CPU frequency. If not available use the REALTIME clock, but + // this may be affected by NTP adjustments or other user activity affecting + // the system time. + static U64 getCPUClockCount64() + { + struct timespec tp; #ifdef CLOCK_MONOTONIC // MONOTONIC supported at build-time? - if (-1 == clock_gettime(CLOCK_MONOTONIC,&tp)) // if MONOTONIC isn't supported at runtime then ouch, try REALTIME + if (-1 == clock_gettime(CLOCK_MONOTONIC,&tp)) // if MONOTONIC isn't supported at runtime then ouch, try REALTIME #endif - clock_gettime(CLOCK_REALTIME,&tp); + clock_gettime(CLOCK_REALTIME,&tp); - return (tp.tv_sec*sClockResolution)+tp.tv_nsec; - } + return (tp.tv_sec*sClockResolution)+tp.tv_nsec; + } - static U32 getCPUClockCount32() - { - return (U32)(getCPUClockCount64() >> 8); - } + static U32 getCPUClockCount32() + { + return (U32)(getCPUClockCount64() >> 8); + } #endif // (LL_LINUX) && !(defined(__i386__) || defined(__amd64__)) #if (LL_LINUX || LL_DARWIN) && (defined(__i386__) || defined(__amd64__)) - // - // Mac+Linux FAST x86 implementation of CPU clock - static U32 getCPUClockCount32() - { - U32 low(0),high(0); - __asm__ volatile (".byte 0x0f, 0x31": "=a"(low), "=d"(high) ); - return (low>>8) | (high<<24); - } - - static U64 getCPUClockCount64() - { - U32 low(0),high(0); - __asm__ volatile (".byte 0x0f, 0x31": "=a"(low), "=d"(high) ); - return (U64)low | ( ((U64)high) << 32); - } + // + // Mac+Linux FAST x86 implementation of CPU clock + static U32 getCPUClockCount32() + { + U32 low(0),high(0); + __asm__ volatile (".byte 0x0f, 0x31": "=a"(low), "=d"(high) ); + return (low>>8) | (high<<24); + } + + static U64 getCPUClockCount64() + { + U32 low(0),high(0); + __asm__ volatile (".byte 0x0f, 0x31": "=a"(low), "=d"(high) ); + return (U64)low | ( ((U64)high) << 32); + } #endif - static BlockTimerStatHandle& getRootTimeBlock(); - static void pushLog(LLSD sd); - static void setLogLock(class LLMutex* mutex); - static void writeLog(std::ostream& os); - static void updateTimes(); - - static U64 countsPerSecond(); + static BlockTimerStatHandle& getRootTimeBlock(); + static void pushLog(LLSD sd); + static void setLogLock(class LLMutex* mutex); + static void writeLog(std::ostream& os); + static void updateTimes(); + + static U64 countsPerSecond(); - // updates cumulative times and hierarchy, - // can be called multiple times in a frame, at any point - static void processTimes(); + // updates cumulative times and hierarchy, + // can be called multiple times in a frame, at any point + static void processTimes(); - static void bootstrapTimerTree(); - static void incrementalUpdateTimerTree(); + static void bootstrapTimerTree(); + static void incrementalUpdateTimerTree(); - // call this once a frame to periodically log timers - static void logStats(); + // call this once a frame to periodically log timers + static void logStats(); - // dumps current cumulative frame stats to log - // call nextFrame() to reset timers - static void dumpCurTimes(); + // dumps current cumulative frame stats to log + // call nextFrame() to reset timers + static void dumpCurTimes(); private: - friend class BlockTimerStatHandle; - // FIXME: this friendship exists so that each thread can instantiate a root timer, - // which could be a derived class with a public constructor instead, possibly - friend class ThreadRecorder; - friend BlockTimer timeThisBlock(BlockTimerStatHandle&); + friend class BlockTimerStatHandle; + // FIXME: this friendship exists so that each thread can instantiate a root timer, + // which could be a derived class with a public constructor instead, possibly + friend class ThreadRecorder; + friend BlockTimer timeThisBlock(BlockTimerStatHandle&); - BlockTimer(BlockTimerStatHandle& timer); + BlockTimer(BlockTimerStatHandle& timer); - // no-copy - BlockTimer(const BlockTimer& other); - BlockTimer& operator=(const BlockTimer& other); + // no-copy + BlockTimer(const BlockTimer& other); + BlockTimer& operator=(const BlockTimer& other); private: - U64 mStartTime; - BlockTimerStackRecord mParentTimerData; + U64 mStartTime; + BlockTimerStackRecord mParentTimerData; public: - // statics - static std::string sLogName; - static bool sMetricLog, - sLog; - static U64 sClockResolution; + // statics + static std::string sLogName; + static bool sMetricLog, + sLog; + static U64 sClockResolution; }; // this dummy function assists in allocating a block timer with stack-based lifetime. -// this is done by capturing the return value in a stack-allocated const reference variable. +// this is done by capturing the return value in a stack-allocated const reference variable. // (This is most easily done using the macro LL_RECORD_BLOCK_TIME) -// Otherwise, it would be possible to store a BlockTimer on the heap, resulting in non-nested lifetimes, +// Otherwise, it would be possible to store a BlockTimer on the heap, resulting in non-nested lifetimes, // which would break the invariants of the timing hierarchy logic LL_FORCE_INLINE class BlockTimer timeThisBlock(class BlockTimerStatHandle& timer) { - return BlockTimer(timer); + return BlockTimer(timer); } // stores a "named" timer instance to be reused via multiple BlockTimer stack instances -class BlockTimerStatHandle -: public StatType<TimeBlockAccumulator> +class BlockTimerStatHandle +: public StatType<TimeBlockAccumulator> { public: - BlockTimerStatHandle(const char* name, const char* description = ""); - - TimeBlockTreeNode& getTreeNode() const; - BlockTimerStatHandle* getParent() const { return getTreeNode().getParent(); } - void setParent(BlockTimerStatHandle* parent) { getTreeNode().setParent(parent); } - - typedef std::vector<BlockTimerStatHandle*>::iterator child_iter; - typedef std::vector<BlockTimerStatHandle*>::const_iterator child_const_iter; - child_iter beginChildren(); - child_iter endChildren(); - bool hasChildren(); - std::vector<BlockTimerStatHandle*>& getChildren(); - - StatType<TimeBlockAccumulator::CallCountFacet>& callCount() - { - return static_cast<StatType<TimeBlockAccumulator::CallCountFacet>&>(*(StatType<TimeBlockAccumulator>*)this); - } - - StatType<TimeBlockAccumulator::SelfTimeFacet>& selfTime() - { - return static_cast<StatType<TimeBlockAccumulator::SelfTimeFacet>&>(*(StatType<TimeBlockAccumulator>*)this); - } - - bool mCollapsed; // don't show children + BlockTimerStatHandle(const char* name, const char* description = ""); + + TimeBlockTreeNode& getTreeNode() const; + BlockTimerStatHandle* getParent() const { return getTreeNode().getParent(); } + void setParent(BlockTimerStatHandle* parent) { getTreeNode().setParent(parent); } + + typedef std::vector<BlockTimerStatHandle*>::iterator child_iter; + typedef std::vector<BlockTimerStatHandle*>::const_iterator child_const_iter; + child_iter beginChildren(); + child_iter endChildren(); + bool hasChildren(); + std::vector<BlockTimerStatHandle*>& getChildren(); + + StatType<TimeBlockAccumulator::CallCountFacet>& callCount() + { + return static_cast<StatType<TimeBlockAccumulator::CallCountFacet>&>(*(StatType<TimeBlockAccumulator>*)this); + } + + StatType<TimeBlockAccumulator::SelfTimeFacet>& selfTime() + { + return static_cast<StatType<TimeBlockAccumulator::SelfTimeFacet>&>(*(StatType<TimeBlockAccumulator>*)this); + } + + bool mCollapsed; // don't show children }; // iterators and helper functions for walking the call hierarchy of block timers in different ways @@ -282,61 +282,61 @@ block_timer_tree_bf_iterator_t end_block_timer_tree_bf(); LL_FORCE_INLINE BlockTimer::BlockTimer(BlockTimerStatHandle& timer) { #if LL_FAST_TIMER_ON - BlockTimerStackRecord* cur_timer_data = LLThreadLocalSingletonPointer<BlockTimerStackRecord>::getInstance(); - if (!cur_timer_data) - { - // How likely is it that - // LLThreadLocalSingletonPointer<T>::getInstance() will return NULL? - // Even without researching, what we can say is that if we exit - // without setting mStartTime at all, gcc 4.7 produces (fatal) - // warnings about a possibly-uninitialized data member. - mStartTime = 0; - return; - } - TimeBlockAccumulator& accumulator = timer.getCurrentAccumulator(); - accumulator.mActiveCount++; - // keep current parent as long as it is active when we are - accumulator.mMoveUpTree |= (accumulator.mParent->getCurrentAccumulator().mActiveCount == 0); - - // store top of stack - mParentTimerData = *cur_timer_data; - // push new information - cur_timer_data->mActiveTimer = this; - cur_timer_data->mTimeBlock = &timer; - cur_timer_data->mChildTime = 0; - - mStartTime = getCPUClockCount64(); + BlockTimerStackRecord* cur_timer_data = LLThreadLocalSingletonPointer<BlockTimerStackRecord>::getInstance(); + if (!cur_timer_data) + { + // How likely is it that + // LLThreadLocalSingletonPointer<T>::getInstance() will return NULL? + // Even without researching, what we can say is that if we exit + // without setting mStartTime at all, gcc 4.7 produces (fatal) + // warnings about a possibly-uninitialized data member. + mStartTime = 0; + return; + } + TimeBlockAccumulator& accumulator = timer.getCurrentAccumulator(); + accumulator.mActiveCount++; + // keep current parent as long as it is active when we are + accumulator.mMoveUpTree |= (accumulator.mParent->getCurrentAccumulator().mActiveCount == 0); + + // store top of stack + mParentTimerData = *cur_timer_data; + // push new information + cur_timer_data->mActiveTimer = this; + cur_timer_data->mTimeBlock = &timer; + cur_timer_data->mChildTime = 0; + + mStartTime = getCPUClockCount64(); #endif } LL_FORCE_INLINE BlockTimer::~BlockTimer() { #if LL_FAST_TIMER_ON - U64 total_time = getCPUClockCount64() - mStartTime; - BlockTimerStackRecord* cur_timer_data = LLThreadLocalSingletonPointer<BlockTimerStackRecord>::getInstance(); - if (!cur_timer_data) return; + U64 total_time = getCPUClockCount64() - mStartTime; + BlockTimerStackRecord* cur_timer_data = LLThreadLocalSingletonPointer<BlockTimerStackRecord>::getInstance(); + if (!cur_timer_data) return; - TimeBlockAccumulator& accumulator = cur_timer_data->mTimeBlock->getCurrentAccumulator(); + TimeBlockAccumulator& accumulator = cur_timer_data->mTimeBlock->getCurrentAccumulator(); - accumulator.mCalls++; - accumulator.mTotalTimeCounter += total_time; - accumulator.mSelfTimeCounter += total_time - cur_timer_data->mChildTime; - accumulator.mActiveCount--; + accumulator.mCalls++; + accumulator.mTotalTimeCounter += total_time; + accumulator.mSelfTimeCounter += total_time - cur_timer_data->mChildTime; + accumulator.mActiveCount--; - // store last caller to bootstrap tree creation - // do this in the destructor in case of recursion to get topmost caller - accumulator.mLastCaller = mParentTimerData.mTimeBlock; + // store last caller to bootstrap tree creation + // do this in the destructor in case of recursion to get topmost caller + accumulator.mLastCaller = mParentTimerData.mTimeBlock; - // we are only tracking self time, so subtract our total time delta from parents - mParentTimerData.mChildTime += total_time; + // we are only tracking self time, so subtract our total time delta from parents + mParentTimerData.mChildTime += total_time; - //pop stack - *cur_timer_data = mParentTimerData; + //pop stack + *cur_timer_data = mParentTimerData; #endif } } -typedef LLTrace::BlockTimer LLFastTimer; +typedef LLTrace::BlockTimer LLFastTimer; #endif // LL_LLFASTTIMER_H diff --git a/indra/llcommon/llfile.cpp b/indra/llcommon/llfile.cpp index 8355b1e797..1877dd54ed 100644 --- a/indra/llcommon/llfile.cpp +++ b/indra/llcommon/llfile.cpp @@ -52,9 +52,9 @@ static std::string empty; // On Windows, use strerror_s(). std::string strerr(int errn) { - char buffer[256]; - strerror_s(buffer, errn); // infers sizeof(buffer) -- love it! - return buffer; + char buffer[256]; + strerror_s(buffer, errn); // infers sizeof(buffer) -- love it! + return buffer; } typedef std::basic_ios<char,std::char_traits < char > > _Myios; @@ -69,295 +69,295 @@ typedef std::basic_ios<char,std::char_traits < char > > _Myios; // strerror_r() returns char* std::string message_from(int /*orig_errno*/, const char* /*buffer*/, size_t /*bufflen*/, - const char* strerror_ret) + const char* strerror_ret) { - return strerror_ret; + return strerror_ret; } // strerror_r() returns int std::string message_from(int orig_errno, const char* buffer, size_t bufflen, - int strerror_ret) + int strerror_ret) { - if (strerror_ret == 0) - { - return buffer; - } - // Here strerror_r() has set errno. Since strerror_r() has already failed, - // seems like a poor bet to call it again to diagnose its own error... - int stre_errno = errno; - if (stre_errno == ERANGE) - { - return STRINGIZE("strerror_r() can't explain errno " << orig_errno - << " (" << bufflen << "-byte buffer too small)"); - } - if (stre_errno == EINVAL) - { - return STRINGIZE("unknown errno " << orig_errno); - } - // Here we don't even understand the errno from strerror_r()! - return STRINGIZE("strerror_r() can't explain errno " << orig_errno - << " (error " << stre_errno << ')'); + if (strerror_ret == 0) + { + return buffer; + } + // Here strerror_r() has set errno. Since strerror_r() has already failed, + // seems like a poor bet to call it again to diagnose its own error... + int stre_errno = errno; + if (stre_errno == ERANGE) + { + return STRINGIZE("strerror_r() can't explain errno " << orig_errno + << " (" << bufflen << "-byte buffer too small)"); + } + if (stre_errno == EINVAL) + { + return STRINGIZE("unknown errno " << orig_errno); + } + // Here we don't even understand the errno from strerror_r()! + return STRINGIZE("strerror_r() can't explain errno " << orig_errno + << " (error " << stre_errno << ')'); } std::string strerr(int errn) { - char buffer[256]; - // Select message_from() function matching the strerror_r() we have on hand. - return message_from(errn, buffer, sizeof(buffer), - strerror_r(errn, buffer, sizeof(buffer))); + char buffer[256]; + // Select message_from() function matching the strerror_r() we have on hand. + return message_from(errn, buffer, sizeof(buffer), + strerror_r(errn, buffer, sizeof(buffer))); } -#endif // ! LL_WINDOWS +#endif // ! LL_WINDOWS // On either system, shorthand call just infers global 'errno'. std::string strerr() { - return strerr(errno); + return strerr(errno); } int warnif(const std::string& desc, const std::string& filename, int rc, int accept=0) { - if (rc < 0) - { - // Capture errno before we start emitting output - int errn = errno; - // For certain operations, a particular errno value might be - // acceptable -- e.g. stat() could permit ENOENT, mkdir() could permit - // EEXIST. Don't warn if caller explicitly says this errno is okay. - if (errn != accept) - { - LL_WARNS("LLFile") << "Couldn't " << desc << " '" << filename - << "' (errno " << errn << "): " << strerr(errn) << LL_ENDL; - } + if (rc < 0) + { + // Capture errno before we start emitting output + int errn = errno; + // For certain operations, a particular errno value might be + // acceptable -- e.g. stat() could permit ENOENT, mkdir() could permit + // EEXIST. Don't warn if caller explicitly says this errno is okay. + if (errn != accept) + { + LL_WARNS("LLFile") << "Couldn't " << desc << " '" << filename + << "' (errno " << errn << "): " << strerr(errn) << LL_ENDL; + } #if 0 && LL_WINDOWS // turn on to debug file-locking problems - // If the problem is "Permission denied," maybe it's because another - // process has the file open. Try to find out. - if (errn == EACCES) // *not* EPERM - { - // Only do any of this stuff (before LL_ENDL) if it will be logged. - LL_DEBUGS("LLFile") << empty; - // would be nice to use LLDir for this, but dependency goes the - // wrong way - const char* TEMP = LLFile::tmpdir(); - if (! (TEMP && *TEMP)) - { - LL_CONT << "No $TEMP, not running 'handle'"; - } - else - { - std::string tf(TEMP); - tf += "\\handle.tmp"; - // http://technet.microsoft.com/en-us/sysinternals/bb896655 - std::string cmd(STRINGIZE("handle \"" << filename - // "openfiles /query /v | fgrep -i \"" << filename - << "\" > \"" << tf << '"')); - LL_CONT << cmd; - if (system(cmd.c_str()) != 0) - { - LL_CONT << "\nDownload 'handle.exe' from http://technet.microsoft.com/en-us/sysinternals/bb896655"; - } - else - { - std::ifstream inf(tf); - std::string line; - while (std::getline(inf, line)) - { - LL_CONT << '\n' << line; - } - } - LLFile::remove(tf); - } - LL_CONT << LL_ENDL; - } + // If the problem is "Permission denied," maybe it's because another + // process has the file open. Try to find out. + if (errn == EACCES) // *not* EPERM + { + // Only do any of this stuff (before LL_ENDL) if it will be logged. + LL_DEBUGS("LLFile") << empty; + // would be nice to use LLDir for this, but dependency goes the + // wrong way + const char* TEMP = LLFile::tmpdir(); + if (! (TEMP && *TEMP)) + { + LL_CONT << "No $TEMP, not running 'handle'"; + } + else + { + std::string tf(TEMP); + tf += "\\handle.tmp"; + // http://technet.microsoft.com/en-us/sysinternals/bb896655 + std::string cmd(STRINGIZE("handle \"" << filename + // "openfiles /query /v | fgrep -i \"" << filename + << "\" > \"" << tf << '"')); + LL_CONT << cmd; + if (system(cmd.c_str()) != 0) + { + LL_CONT << "\nDownload 'handle.exe' from http://technet.microsoft.com/en-us/sysinternals/bb896655"; + } + else + { + std::ifstream inf(tf); + std::string line; + while (std::getline(inf, line)) + { + LL_CONT << '\n' << line; + } + } + LLFile::remove(tf); + } + LL_CONT << LL_ENDL; + } #endif // LL_WINDOWS hack to identify processes holding file open - } - return rc; + } + return rc; } // static -int LLFile::mkdir(const std::string& dirname, int perms) +int LLFile::mkdir(const std::string& dirname, int perms) { #if LL_WINDOWS - // permissions are ignored on Windows - std::string utf8dirname = dirname; - llutf16string utf16dirname = utf8str_to_utf16str(utf8dirname); - int rc = _wmkdir(utf16dirname.c_str()); + // permissions are ignored on Windows + std::string utf8dirname = dirname; + llutf16string utf16dirname = utf8str_to_utf16str(utf8dirname); + int rc = _wmkdir(utf16dirname.c_str()); #else - int rc = ::mkdir(dirname.c_str(), (mode_t)perms); + int rc = ::mkdir(dirname.c_str(), (mode_t)perms); #endif - // We often use mkdir() to ensure the existence of a directory that might - // already exist. There is no known case in which we want to call out as - // an error the requested directory already existing. - if (rc < 0 && errno == EEXIST) - { - // this is not the error you want, move along - return 0; - } - // anything else might be a problem - return warnif("mkdir", dirname, rc, EEXIST); + // We often use mkdir() to ensure the existence of a directory that might + // already exist. There is no known case in which we want to call out as + // an error the requested directory already existing. + if (rc < 0 && errno == EEXIST) + { + // this is not the error you want, move along + return 0; + } + // anything else might be a problem + return warnif("mkdir", dirname, rc, EEXIST); } // static -int LLFile::rmdir(const std::string& dirname) +int LLFile::rmdir(const std::string& dirname) { #if LL_WINDOWS - // permissions are ignored on Windows - std::string utf8dirname = dirname; - llutf16string utf16dirname = utf8str_to_utf16str(utf8dirname); - int rc = _wrmdir(utf16dirname.c_str()); + // permissions are ignored on Windows + std::string utf8dirname = dirname; + llutf16string utf16dirname = utf8str_to_utf16str(utf8dirname); + int rc = _wrmdir(utf16dirname.c_str()); #else - int rc = ::rmdir(dirname.c_str()); + int rc = ::rmdir(dirname.c_str()); #endif - return warnif("rmdir", dirname, rc); + return warnif("rmdir", dirname, rc); } // static -LLFILE* LLFile::fopen(const std::string& filename, const char* mode) /* Flawfinder: ignore */ +LLFILE* LLFile::fopen(const std::string& filename, const char* mode) /* Flawfinder: ignore */ { -#if LL_WINDOWS - std::string utf8filename = filename; - std::string utf8mode = std::string(mode); - llutf16string utf16filename = utf8str_to_utf16str(utf8filename); - llutf16string utf16mode = utf8str_to_utf16str(utf8mode); - return _wfopen(utf16filename.c_str(),utf16mode.c_str()); +#if LL_WINDOWS + std::string utf8filename = filename; + std::string utf8mode = std::string(mode); + llutf16string utf16filename = utf8str_to_utf16str(utf8filename); + llutf16string utf16mode = utf8str_to_utf16str(utf8mode); + return _wfopen(utf16filename.c_str(),utf16mode.c_str()); #else - return ::fopen(filename.c_str(),mode); /* Flawfinder: ignore */ + return ::fopen(filename.c_str(),mode); /* Flawfinder: ignore */ #endif } -LLFILE* LLFile::_fsopen(const std::string& filename, const char* mode, int sharingFlag) +LLFILE* LLFile::_fsopen(const std::string& filename, const char* mode, int sharingFlag) { -#if LL_WINDOWS - std::string utf8filename = filename; - std::string utf8mode = std::string(mode); - llutf16string utf16filename = utf8str_to_utf16str(utf8filename); - llutf16string utf16mode = utf8str_to_utf16str(utf8mode); - return _wfsopen(utf16filename.c_str(),utf16mode.c_str(),sharingFlag); +#if LL_WINDOWS + std::string utf8filename = filename; + std::string utf8mode = std::string(mode); + llutf16string utf16filename = utf8str_to_utf16str(utf8filename); + llutf16string utf16mode = utf8str_to_utf16str(utf8mode); + return _wfsopen(utf16filename.c_str(),utf16mode.c_str(),sharingFlag); #else - llassert(0);//No corresponding function on non-windows - return NULL; + llassert(0);//No corresponding function on non-windows + return NULL; #endif } -int LLFile::close(LLFILE * file) +int LLFile::close(LLFILE * file) { - int ret_value = 0; - if (file) - { - ret_value = fclose(file); - } - return ret_value; + int ret_value = 0; + if (file) + { + ret_value = fclose(file); + } + return ret_value; } -int LLFile::remove(const std::string& filename, int supress_error) +int LLFile::remove(const std::string& filename, int supress_error) { -#if LL_WINDOWS - std::string utf8filename = filename; - llutf16string utf16filename = utf8str_to_utf16str(utf8filename); - int rc = _wremove(utf16filename.c_str()); +#if LL_WINDOWS + std::string utf8filename = filename; + llutf16string utf16filename = utf8str_to_utf16str(utf8filename); + int rc = _wremove(utf16filename.c_str()); #else - int rc = ::remove(filename.c_str()); + int rc = ::remove(filename.c_str()); #endif - return warnif("remove", filename, rc, supress_error); + return warnif("remove", filename, rc, supress_error); } -int LLFile::rename(const std::string& filename, const std::string& newname, int supress_error) +int LLFile::rename(const std::string& filename, const std::string& newname, int supress_error) { -#if LL_WINDOWS - std::string utf8filename = filename; - std::string utf8newname = newname; - llutf16string utf16filename = utf8str_to_utf16str(utf8filename); - llutf16string utf16newname = utf8str_to_utf16str(utf8newname); - int rc = _wrename(utf16filename.c_str(),utf16newname.c_str()); +#if LL_WINDOWS + std::string utf8filename = filename; + std::string utf8newname = newname; + llutf16string utf16filename = utf8str_to_utf16str(utf8filename); + llutf16string utf16newname = utf8str_to_utf16str(utf8newname); + int rc = _wrename(utf16filename.c_str(),utf16newname.c_str()); #else - int rc = ::rename(filename.c_str(),newname.c_str()); + int rc = ::rename(filename.c_str(),newname.c_str()); #endif - return warnif(STRINGIZE("rename to '" << newname << "' from"), filename, rc, supress_error); + return warnif(STRINGIZE("rename to '" << newname << "' from"), filename, rc, supress_error); } bool LLFile::copy(const std::string from, const std::string to) { - bool copied = false; - LLFILE* in = LLFile::fopen(from, "rb"); /* Flawfinder: ignore */ - if (in) - { - LLFILE* out = LLFile::fopen(to, "wb"); /* Flawfinder: ignore */ - if (out) - { - char buf[16384]; /* Flawfinder: ignore */ - size_t readbytes; - bool write_ok = true; - while(write_ok && (readbytes = fread(buf, 1, 16384, in))) /* Flawfinder: ignore */ - { - if (fwrite(buf, 1, readbytes, out) != readbytes) - { - LL_WARNS("LLFile") << "Short write" << LL_ENDL; - write_ok = false; - } - } - if ( write_ok ) - { - copied = true; - } - fclose(out); - } - fclose(in); - } - return copied; + bool copied = false; + LLFILE* in = LLFile::fopen(from, "rb"); /* Flawfinder: ignore */ + if (in) + { + LLFILE* out = LLFile::fopen(to, "wb"); /* Flawfinder: ignore */ + if (out) + { + char buf[16384]; /* Flawfinder: ignore */ + size_t readbytes; + bool write_ok = true; + while(write_ok && (readbytes = fread(buf, 1, 16384, in))) /* Flawfinder: ignore */ + { + if (fwrite(buf, 1, readbytes, out) != readbytes) + { + LL_WARNS("LLFile") << "Short write" << LL_ENDL; + write_ok = false; + } + } + if ( write_ok ) + { + copied = true; + } + fclose(out); + } + fclose(in); + } + return copied; } -int LLFile::stat(const std::string& filename, llstat* filestatus) +int LLFile::stat(const std::string& filename, llstat* filestatus) { #if LL_WINDOWS - std::string utf8filename = filename; - llutf16string utf16filename = utf8str_to_utf16str(utf8filename); - int rc = _wstat(utf16filename.c_str(),filestatus); + std::string utf8filename = filename; + llutf16string utf16filename = utf8str_to_utf16str(utf8filename); + int rc = _wstat(utf16filename.c_str(),filestatus); #else - int rc = ::stat(filename.c_str(),filestatus); + int rc = ::stat(filename.c_str(),filestatus); #endif - // We use stat() to determine existence (see isfile(), isdir()). - // Don't spam the log if the subject pathname doesn't exist. - return warnif("stat", filename, rc, ENOENT); + // We use stat() to determine existence (see isfile(), isdir()). + // Don't spam the log if the subject pathname doesn't exist. + return warnif("stat", filename, rc, ENOENT); } bool LLFile::isdir(const std::string& filename) { - llstat st; + llstat st; - return stat(filename, &st) == 0 && S_ISDIR(st.st_mode); + return stat(filename, &st) == 0 && S_ISDIR(st.st_mode); } bool LLFile::isfile(const std::string& filename) { - llstat st; + llstat st; - return stat(filename, &st) == 0 && S_ISREG(st.st_mode); + return stat(filename, &st) == 0 && S_ISREG(st.st_mode); } const char *LLFile::tmpdir() { - static std::string utf8path; + static std::string utf8path; - if (utf8path.empty()) - { - char sep; + if (utf8path.empty()) + { + char sep; #if LL_WINDOWS - sep = '\\'; + sep = '\\'; - std::vector<wchar_t> utf16path(MAX_PATH + 1); - GetTempPathW(utf16path.size(), &utf16path[0]); - utf8path = ll_convert_wide_to_string(&utf16path[0]); + std::vector<wchar_t> utf16path(MAX_PATH + 1); + GetTempPathW(utf16path.size(), &utf16path[0]); + utf8path = ll_convert_wide_to_string(&utf16path[0]); #else - sep = '/'; + sep = '/'; - utf8path = LLStringUtil::getenv("TMPDIR", "/tmp/"); + utf8path = LLStringUtil::getenv("TMPDIR", "/tmp/"); #endif - if (utf8path[utf8path.size() - 1] != sep) - { - utf8path += sep; - } - } - return utf8path.c_str(); + if (utf8path[utf8path.size() - 1] != sep) + { + utf8path += sep; + } + } + return utf8path.c_str(); } @@ -365,64 +365,64 @@ const char *LLFile::tmpdir() #if LL_WINDOWS -LLFILE * LLFile::_Fiopen(const std::string& filename, - std::ios::openmode mode) -{ // open a file - static const char *mods[] = - { // fopen mode strings corresponding to valid[i] - "r", "w", "w", "a", "rb", "wb", "wb", "ab", - "r+", "w+", "a+", "r+b", "w+b", "a+b", - 0}; - static const int valid[] = - { // valid combinations of open flags - ios_base::in, - ios_base::out, - ios_base::out | ios_base::trunc, - ios_base::out | ios_base::app, - ios_base::in | ios_base::binary, - ios_base::out | ios_base::binary, - ios_base::out | ios_base::trunc | ios_base::binary, - ios_base::out | ios_base::app | ios_base::binary, - ios_base::in | ios_base::out, - ios_base::in | ios_base::out | ios_base::trunc, - ios_base::in | ios_base::out | ios_base::app, - ios_base::in | ios_base::out | ios_base::binary, - ios_base::in | ios_base::out | ios_base::trunc - | ios_base::binary, - ios_base::in | ios_base::out | ios_base::app - | ios_base::binary, - 0}; - - LLFILE *fp = 0; - int n; - ios_base::openmode atendflag = mode & ios_base::ate; - ios_base::openmode norepflag = mode & ios_base::_Noreplace; - - if (mode & ios_base::_Nocreate) - mode |= ios_base::in; // file must exist - mode &= ~(ios_base::ate | ios_base::_Nocreate | ios_base::_Noreplace); - for (n = 0; valid[n] != 0 && valid[n] != mode; ++n) - ; // look for a valid mode - - if (valid[n] == 0) - return (0); // no valid mode - else if (norepflag && mode & (ios_base::out || ios_base::app) - && (fp = LLFile::fopen(filename, "r")) != 0) /* Flawfinder: ignore */ - { // file must not exist, close and fail - fclose(fp); - return (0); - } - else if (fp != 0 && fclose(fp) != 0) - return (0); // can't close after test open +LLFILE * LLFile::_Fiopen(const std::string& filename, + std::ios::openmode mode) +{ // open a file + static const char *mods[] = + { // fopen mode strings corresponding to valid[i] + "r", "w", "w", "a", "rb", "wb", "wb", "ab", + "r+", "w+", "a+", "r+b", "w+b", "a+b", + 0}; + static const int valid[] = + { // valid combinations of open flags + ios_base::in, + ios_base::out, + ios_base::out | ios_base::trunc, + ios_base::out | ios_base::app, + ios_base::in | ios_base::binary, + ios_base::out | ios_base::binary, + ios_base::out | ios_base::trunc | ios_base::binary, + ios_base::out | ios_base::app | ios_base::binary, + ios_base::in | ios_base::out, + ios_base::in | ios_base::out | ios_base::trunc, + ios_base::in | ios_base::out | ios_base::app, + ios_base::in | ios_base::out | ios_base::binary, + ios_base::in | ios_base::out | ios_base::trunc + | ios_base::binary, + ios_base::in | ios_base::out | ios_base::app + | ios_base::binary, + 0}; + + LLFILE *fp = 0; + int n; + ios_base::openmode atendflag = mode & ios_base::ate; + ios_base::openmode norepflag = mode & ios_base::_Noreplace; + + if (mode & ios_base::_Nocreate) + mode |= ios_base::in; // file must exist + mode &= ~(ios_base::ate | ios_base::_Nocreate | ios_base::_Noreplace); + for (n = 0; valid[n] != 0 && valid[n] != mode; ++n) + ; // look for a valid mode + + if (valid[n] == 0) + return (0); // no valid mode + else if (norepflag && mode & (ios_base::out || ios_base::app) + && (fp = LLFile::fopen(filename, "r")) != 0) /* Flawfinder: ignore */ + { // file must not exist, close and fail + fclose(fp); + return (0); + } + else if (fp != 0 && fclose(fp) != 0) + return (0); // can't close after test open // should open with protection here, if other than default - else if ((fp = LLFile::fopen(filename, mods[n])) == 0) /* Flawfinder: ignore */ - return (0); // open failed + else if ((fp = LLFile::fopen(filename, mods[n])) == 0) /* Flawfinder: ignore */ + return (0); // open failed - if (!atendflag || fseek(fp, 0, SEEK_END) == 0) - return (fp); // no need to seek to end, or seek succeeded + if (!atendflag || fseek(fp, 0, SEEK_END) == 0) + return (fp); // no need to seek to end, or seek succeeded - fclose(fp); // can't position at end - return (0); + fclose(fp); // can't position at end + return (0); } #endif /* LL_WINDOWS */ @@ -469,26 +469,26 @@ void llofstream::open(const std::string& _Filename, ios_base::openmode _Mode) std::streamsize llifstream_size(llifstream& ifstr) { - if(!ifstr.is_open()) return 0; - std::streampos pos_old = ifstr.tellg(); - ifstr.seekg(0, ios_base::beg); - std::streampos pos_beg = ifstr.tellg(); - ifstr.seekg(0, ios_base::end); - std::streampos pos_end = ifstr.tellg(); - ifstr.seekg(pos_old, ios_base::beg); - return pos_end - pos_beg; + if(!ifstr.is_open()) return 0; + std::streampos pos_old = ifstr.tellg(); + ifstr.seekg(0, ios_base::beg); + std::streampos pos_beg = ifstr.tellg(); + ifstr.seekg(0, ios_base::end); + std::streampos pos_end = ifstr.tellg(); + ifstr.seekg(pos_old, ios_base::beg); + return pos_end - pos_beg; } std::streamsize llofstream_size(llofstream& ofstr) { - if(!ofstr.is_open()) return 0; - std::streampos pos_old = ofstr.tellp(); - ofstr.seekp(0, ios_base::beg); - std::streampos pos_beg = ofstr.tellp(); - ofstr.seekp(0, ios_base::end); - std::streampos pos_end = ofstr.tellp(); - ofstr.seekp(pos_old, ios_base::beg); - return pos_end - pos_beg; + if(!ofstr.is_open()) return 0; + std::streampos pos_old = ofstr.tellp(); + ofstr.seekp(0, ios_base::beg); + std::streampos pos_beg = ofstr.tellp(); + ofstr.seekp(0, ios_base::end); + std::streampos pos_end = ofstr.tellp(); + ofstr.seekp(pos_old, ios_base::beg); + return pos_end - pos_beg; } #endif // LL_WINDOWS diff --git a/indra/llcommon/llfile.h b/indra/llcommon/llfile.h index 9de095b45d..08a008c19a 100644 --- a/indra/llcommon/llfile.h +++ b/indra/llcommon/llfile.h @@ -1,4 +1,4 @@ -/** +/** * @file llfile.h * @author Michael Schlachter * @date 2006-03-23 @@ -8,21 +8,21 @@ * $LicenseInfo:firstyear=2006&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -35,16 +35,16 @@ * Attempts to mostly mirror the POSIX style IO functions. */ -typedef FILE LLFILE; +typedef FILE LLFILE; #include <fstream> #include <sys/stat.h> #if LL_WINDOWS // windows version of stat function and stat data structure are called _stat -typedef struct _stat llstat; +typedef struct _stat llstat; #else -typedef struct stat llstat; +typedef struct stat llstat; #include <sys/types.h> #endif @@ -61,29 +61,29 @@ typedef struct stat llstat; class LL_COMMON_API LLFile { public: - // All these functions take UTF8 path/filenames. - static LLFILE* fopen(const std::string& filename,const char* accessmode); /* Flawfinder: ignore */ - static LLFILE* _fsopen(const std::string& filename,const char* accessmode,int sharingFlag); + // All these functions take UTF8 path/filenames. + static LLFILE* fopen(const std::string& filename,const char* accessmode); /* Flawfinder: ignore */ + static LLFILE* _fsopen(const std::string& filename,const char* accessmode,int sharingFlag); - static int close(LLFILE * file); + static int close(LLFILE * file); - // perms is a permissions mask like 0777 or 0700. In most cases it will - // be overridden by the user's umask. It is ignored on Windows. - // mkdir() considers "directory already exists" to be SUCCESS. - static int mkdir(const std::string& filename, int perms = 0700); + // perms is a permissions mask like 0777 or 0700. In most cases it will + // be overridden by the user's umask. It is ignored on Windows. + // mkdir() considers "directory already exists" to be SUCCESS. + static int mkdir(const std::string& filename, int perms = 0700); - static int rmdir(const std::string& filename); - static int remove(const std::string& filename, int supress_error = 0); - static int rename(const std::string& filename,const std::string& newname, int supress_error = 0); - static bool copy(const std::string from, const std::string to); + static int rmdir(const std::string& filename); + static int remove(const std::string& filename, int supress_error = 0); + static int rename(const std::string& filename,const std::string& newname, int supress_error = 0); + static bool copy(const std::string from, const std::string to); - static int stat(const std::string& filename,llstat* file_status); - static bool isdir(const std::string& filename); - static bool isfile(const std::string& filename); - static LLFILE * _Fiopen(const std::string& filename, - std::ios::openmode mode); + static int stat(const std::string& filename,llstat* file_status); + static bool isdir(const std::string& filename); + static bool isfile(const std::string& filename); + static LLFILE * _Fiopen(const std::string& filename, + std::ios::openmode mode); - static const char * tmpdir(); + static const char * tmpdir(); }; /// RAII class @@ -158,39 +158,39 @@ private: * Does The Right Thing when passed a non-ASCII pathname. Sadly, that isn't * true of Microsoft's std::ifstream. */ -class LL_COMMON_API llifstream : public std::ifstream +class LL_COMMON_API llifstream : public std::ifstream { - // input stream associated with a C stream + // input stream associated with a C stream public: - // Constructors: - /** - * @brief Default constructor. - * - * Initializes @c sb using its default constructor, and passes - * @c &sb to the base class initializer. Does not open any files - * (you haven't given it a filename to open). + // Constructors: + /** + * @brief Default constructor. + * + * Initializes @c sb using its default constructor, and passes + * @c &sb to the base class initializer. Does not open any files + * (you haven't given it a filename to open). */ - llifstream(); + llifstream(); - /** - * @brief Create an input file stream. - * @param Filename String specifying the filename. - * @param Mode Open file in specified mode (see std::ios_base). - * + /** + * @brief Create an input file stream. + * @param Filename String specifying the filename. + * @param Mode Open file in specified mode (see std::ios_base). + * * @c ios_base::in is automatically included in @a mode. */ - explicit llifstream(const std::string& _Filename, + explicit llifstream(const std::string& _Filename, ios_base::openmode _Mode = ios_base::in); - /** - * @brief Opens an external file. - * @param Filename The name of the file. - * @param Node The open mode flags. - * - * Calls @c llstdio_filebuf::open(s,mode|in). If that function - * fails, @c failbit is set in the stream's error state. + /** + * @brief Opens an external file. + * @param Filename The name of the file. + * @param Node The open mode flags. + * + * Calls @c llstdio_filebuf::open(s,mode|in). If that function + * fails, @c failbit is set in the stream's error state. */ - void open(const std::string& _Filename, + void open(const std::string& _Filename, ios_base::openmode _Mode = ios_base::in); }; @@ -203,37 +203,37 @@ class LL_COMMON_API llifstream : public std::ifstream * Right Thing when passed a non-ASCII pathname. Sadly, that isn't true of * Microsoft's std::ofstream. */ -class LL_COMMON_API llofstream : public std::ofstream +class LL_COMMON_API llofstream : public std::ofstream { public: - // Constructors: - /** - * @brief Default constructor. - * - * Initializes @c sb using its default constructor, and passes - * @c &sb to the base class initializer. Does not open any files - * (you haven't given it a filename to open). + // Constructors: + /** + * @brief Default constructor. + * + * Initializes @c sb using its default constructor, and passes + * @c &sb to the base class initializer. Does not open any files + * (you haven't given it a filename to open). */ - llofstream(); - - /** - * @brief Create an output file stream. - * @param Filename String specifying the filename. - * @param Mode Open file in specified mode (see std::ios_base). - * - * @c ios_base::out is automatically included in @a mode. + llofstream(); + + /** + * @brief Create an output file stream. + * @param Filename String specifying the filename. + * @param Mode Open file in specified mode (see std::ios_base). + * + * @c ios_base::out is automatically included in @a mode. */ - explicit llofstream(const std::string& _Filename, + explicit llofstream(const std::string& _Filename, ios_base::openmode _Mode = ios_base::out|ios_base::trunc); - /** - * @brief Opens an external file. - * @param Filename The name of the file. - * @param Node The open mode flags. - * - * @c ios_base::out is automatically included in @a mode. + /** + * @brief Opens an external file. + * @param Filename The name of the file. + * @param Node The open mode flags. + * + * @c ios_base::out is automatically included in @a mode. */ - void open(const std::string& _Filename, + void open(const std::string& _Filename, ios_base::openmode _Mode = ios_base::out|ios_base::trunc); }; diff --git a/indra/llcommon/llfindlocale.cpp b/indra/llcommon/llfindlocale.cpp index f019bd0c64..e39812bfc4 100644 --- a/indra/llcommon/llfindlocale.cpp +++ b/indra/llcommon/llfindlocale.cpp @@ -1,25 +1,25 @@ -/** +/** * @file llfindlocale.cpp * @brief Detect system language setting * * $LicenseInfo:firstyear=2008&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llcommon/llfindlocale.h b/indra/llcommon/llfindlocale.h index 6770db5774..07b59853c3 100644 --- a/indra/llcommon/llfindlocale.h +++ b/indra/llcommon/llfindlocale.h @@ -1,25 +1,25 @@ -/** +/** * @file llfindlocale.h * @brief Detect system language setting * * $LicenseInfo:firstyear=2008&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llcommon/llfixedbuffer.cpp b/indra/llcommon/llfixedbuffer.cpp index bd4db8be84..4f28d2240e 100644 --- a/indra/llcommon/llfixedbuffer.cpp +++ b/indra/llcommon/llfixedbuffer.cpp @@ -1,24 +1,24 @@ -/** +/** * @file llfixedbuffer.cpp * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -29,69 +29,69 @@ //////////////////////////////////////////////////////////////////////////// LLFixedBuffer::LLFixedBuffer(const U32 max_lines) - : LLLineBuffer(), - mMaxLines(max_lines), - mMutex() + : LLLineBuffer(), + mMaxLines(max_lines), + mMutex() { - mTimer.reset(); + mTimer.reset(); } LLFixedBuffer::~LLFixedBuffer() { - clear(); + clear(); } void LLFixedBuffer::clear() { - mMutex.lock() ; - mLines.clear(); - mAddTimes.clear(); - mLineLengths.clear(); - mMutex.unlock() ; + mMutex.lock() ; + mLines.clear(); + mAddTimes.clear(); + mLineLengths.clear(); + mMutex.unlock() ; - mTimer.reset(); + mTimer.reset(); } void LLFixedBuffer::addLine(const std::string& utf8line) { - LLWString wstring = utf8str_to_wstring(utf8line); - addWLine(wstring); + LLWString wstring = utf8str_to_wstring(utf8line); + addWLine(wstring); } void LLFixedBuffer::addWLine(const LLWString& line) { - if (line.empty()) - { - return; - } - - removeExtraLines(); - - mMutex.lock() ; - mLines.push_back(line); - mLineLengths.push_back((S32)line.length()); - mAddTimes.push_back(mTimer.getElapsedTimeF32()); - mMutex.unlock() ; + if (line.empty()) + { + return; + } + + removeExtraLines(); + + mMutex.lock() ; + mLines.push_back(line); + mLineLengths.push_back((S32)line.length()); + mAddTimes.push_back(mTimer.getElapsedTimeF32()); + mMutex.unlock() ; } void LLFixedBuffer::setMaxLines(S32 max_lines) { - mMaxLines = max_lines; + mMaxLines = max_lines; - removeExtraLines(); + removeExtraLines(); } void LLFixedBuffer::removeExtraLines() { - mMutex.lock() ; - while ((S32)mLines.size() > llmax((S32)0, (S32)(mMaxLines - 1))) - { - mLines.pop_front(); - mAddTimes.pop_front(); - mLineLengths.pop_front(); - } - mMutex.unlock() ; + mMutex.lock() ; + while ((S32)mLines.size() > llmax((S32)0, (S32)(mMaxLines - 1))) + { + mLines.pop_front(); + mAddTimes.pop_front(); + mLineLengths.pop_front(); + } + mMutex.unlock() ; } diff --git a/indra/llcommon/llfixedbuffer.h b/indra/llcommon/llfixedbuffer.h index 554cf48a4c..eca0792d35 100644 --- a/indra/llcommon/llfixedbuffer.h +++ b/indra/llcommon/llfixedbuffer.h @@ -1,25 +1,25 @@ -/** +/** * @file llfixedbuffer.h * @brief A fixed size buffer of lines. * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -38,27 +38,27 @@ class LL_COMMON_API LLFixedBuffer : public LLLineBuffer { public: - LLFixedBuffer(const U32 max_lines = 20); - ~LLFixedBuffer(); + LLFixedBuffer(const U32 max_lines = 20); + ~LLFixedBuffer(); + + LLTimer mTimer; + U32 mMaxLines; + std::deque<LLWString> mLines; + std::deque<F32> mAddTimes; + std::deque<S32> mLineLengths; - LLTimer mTimer; - U32 mMaxLines; - std::deque<LLWString> mLines; - std::deque<F32> mAddTimes; - std::deque<S32> mLineLengths; + /*virtual*/ void clear(); // Clear the buffer, and reset it. - /*virtual*/ void clear(); // Clear the buffer, and reset it. + /*virtual*/ void addLine(const std::string& utf8line); - /*virtual*/ void addLine(const std::string& utf8line); + void setMaxLines(S32 max_lines); - void setMaxLines(S32 max_lines); - protected: - void removeExtraLines(); - void addWLine(const LLWString& line); + void removeExtraLines(); + void addWLine(const LLWString& line); protected: - LLMutex mMutex ; + LLMutex mMutex ; }; #endif //LL_FIXED_BUFFER_H diff --git a/indra/llcommon/llformat.cpp b/indra/llcommon/llformat.cpp index 3b2b3038ea..dc5726e29f 100644 --- a/indra/llcommon/llformat.cpp +++ b/indra/llcommon/llformat.cpp @@ -1,4 +1,4 @@ -/** +/** * @file llformat.cpp * @date January 2007 * @brief string formatting utility @@ -6,21 +6,21 @@ * $LicenseInfo:firstyear=2007&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -35,36 +35,36 @@ // wrapper for vsnprintf to be called from llformatXXX functions. static void va_format(std::string& out, const char *fmt, va_list va) { - char tstr[1024]; /* Flawfinder: ignore */ + char tstr[1024]; /* Flawfinder: ignore */ #if LL_WINDOWS - _vsnprintf(tstr, 1024, fmt, va); + _vsnprintf(tstr, 1024, fmt, va); #else - vsnprintf(tstr, 1024, fmt, va); /* Flawfinder: ignore */ + vsnprintf(tstr, 1024, fmt, va); /* Flawfinder: ignore */ #endif - out.assign(tstr); + out.assign(tstr); } std::string llformat(const char *fmt, ...) { - std::string res; - va_list va; - va_start(va, fmt); - va_format(res, fmt, va); - va_end(va); - return res; + std::string res; + va_list va; + va_start(va, fmt); + va_format(res, fmt, va); + va_end(va); + return res; } std::string llformat_to_utf8(const char *fmt, ...) { - std::string res; - va_list va; - va_start(va, fmt); - va_format(res, fmt, va); - va_end(va); + std::string res; + va_list va; + va_start(va, fmt); + va_format(res, fmt, va); + va_end(va); #if LL_WINDOWS - // made converting to utf8. See EXT-8318. - res = ll_convert_string_to_utf8_string(res); + // made converting to utf8. See EXT-8318. + res = ll_convert_string_to_utf8_string(res); #endif - return res; + return res; } diff --git a/indra/llcommon/llformat.h b/indra/llcommon/llformat.h index fb8e7cd045..4456a72696 100644 --- a/indra/llcommon/llformat.h +++ b/indra/llcommon/llformat.h @@ -1,4 +1,4 @@ -/** +/** * @file llformat.h * @date January 2007 * @brief string formatting utility @@ -6,21 +6,21 @@ * $LicenseInfo:firstyear=2007&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llcommon/llframetimer.cpp b/indra/llcommon/llframetimer.cpp index 024ef27228..8694129c77 100644 --- a/indra/llcommon/llframetimer.cpp +++ b/indra/llcommon/llframetimer.cpp @@ -1,150 +1,150 @@ -/** - * @file llframetimer.cpp - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" - -#include "u64.h" - -#include "llframetimer.h" - -// Static members -//LLTimer LLFrameTimer::sInternalTimer; -U64 LLFrameTimer::sStartTotalTime = totalTime(); -F64 LLFrameTimer::sFrameTime = 0.0; -U64 LLFrameTimer::sTotalTime = 0; -F64 LLFrameTimer::sTotalSeconds = 0.0; -S32 LLFrameTimer::sFrameCount = 0; -U64 LLFrameTimer::sFrameDeltaTime = 0; -const F64 USEC_TO_SEC_F64 = 0.000001; - -// static -void LLFrameTimer::updateFrameTime() -{ - U64 total_time = totalTime(); - sFrameDeltaTime = total_time - sTotalTime; - sTotalTime = total_time; - sTotalSeconds = U64_to_F64(sTotalTime) * USEC_TO_SEC_F64; - sFrameTime = U64_to_F64(sTotalTime - sStartTotalTime) * USEC_TO_SEC_F64; -} - -void LLFrameTimer::start() -{ - reset(); - mStarted = true; -} - -void LLFrameTimer::stop() -{ - mStarted = false; -} - -void LLFrameTimer::reset() -{ - mStartTime = sFrameTime; - mExpiry = sFrameTime; -} - -void LLFrameTimer::resetWithExpiry(F32 expiration) -{ - reset(); - setTimerExpirySec(expiration); -} - -// Don't combine pause/unpause with start/stop -// Useage: -// LLFrameTime foo; // starts automatically -// foo.unpause(); // noop but safe -// foo.pause(); // pauses timer -// foo.unpause() // unpauses -// F32 elapsed = foo.getElapsedTimeF32() // does not include time between pause() and unpause() -// Note: elapsed would also be valid with no unpause() call (= time run until pause() called) -void LLFrameTimer::pause() -{ - if (mStarted) - mStartTime = sFrameTime - mStartTime; // save dtime - mStarted = false; -} - -void LLFrameTimer::unpause() -{ - if (!mStarted) - mStartTime = sFrameTime - mStartTime; // restore dtime - mStarted = true; -} - -void LLFrameTimer::setTimerExpirySec(F32 expiration) -{ - mExpiry = expiration + mStartTime; -} - -void LLFrameTimer::setExpiryAt(F64 seconds_since_epoch) -{ - mStartTime = sFrameTime; - mExpiry = seconds_since_epoch - (USEC_TO_SEC_F64 * sStartTotalTime); -} - -F64 LLFrameTimer::expiresAt() const -{ - F64 expires_at = U64_to_F64(sStartTotalTime) * USEC_TO_SEC_F64; - expires_at += mExpiry; - return expires_at; -} - -bool LLFrameTimer::checkExpirationAndReset(F32 expiration) -{ - //LL_INFOS() << "LLFrameTimer::checkExpirationAndReset()" << LL_ENDL; - //LL_INFOS() << " mStartTime:" << mStartTime << LL_ENDL; - //LL_INFOS() << " sFrameTime:" << sFrameTime << LL_ENDL; - //LL_INFOS() << " mExpiry: " << mExpiry << LL_ENDL; - - if(hasExpired()) - { - reset(); - setTimerExpirySec(expiration); - return true; - } - return false; -} - -// static -F32 LLFrameTimer::getFrameDeltaTimeF32() -{ - return (F32)(U64_to_F64(sFrameDeltaTime) * USEC_TO_SEC_F64); -} - - -// static -// Return seconds since the current frame started -F32 LLFrameTimer::getCurrentFrameTime() -{ - U64 frame_time = totalTime() - sTotalTime; - return (F32)(U64_to_F64(frame_time) * USEC_TO_SEC_F64); -} - -// Glue code to avoid full class .h file #includes -F32 getCurrentFrameTime() -{ - return (F32)(LLFrameTimer::getCurrentFrameTime()); -} +/**
+ * @file llframetimer.cpp
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "u64.h"
+
+#include "llframetimer.h"
+
+// Static members
+//LLTimer LLFrameTimer::sInternalTimer;
+U64 LLFrameTimer::sStartTotalTime = totalTime();
+F64 LLFrameTimer::sFrameTime = 0.0;
+U64 LLFrameTimer::sTotalTime = 0;
+F64 LLFrameTimer::sTotalSeconds = 0.0;
+S32 LLFrameTimer::sFrameCount = 0;
+U64 LLFrameTimer::sFrameDeltaTime = 0;
+const F64 USEC_TO_SEC_F64 = 0.000001;
+
+// static
+void LLFrameTimer::updateFrameTime()
+{
+ U64 total_time = totalTime();
+ sFrameDeltaTime = total_time - sTotalTime;
+ sTotalTime = total_time;
+ sTotalSeconds = U64_to_F64(sTotalTime) * USEC_TO_SEC_F64;
+ sFrameTime = U64_to_F64(sTotalTime - sStartTotalTime) * USEC_TO_SEC_F64;
+}
+
+void LLFrameTimer::start()
+{
+ reset();
+ mStarted = true;
+}
+
+void LLFrameTimer::stop()
+{
+ mStarted = false;
+}
+
+void LLFrameTimer::reset()
+{
+ mStartTime = sFrameTime;
+ mExpiry = sFrameTime;
+}
+
+void LLFrameTimer::resetWithExpiry(F32 expiration)
+{
+ reset();
+ setTimerExpirySec(expiration);
+}
+
+// Don't combine pause/unpause with start/stop
+// Useage:
+// LLFrameTime foo; // starts automatically
+// foo.unpause(); // noop but safe
+// foo.pause(); // pauses timer
+// foo.unpause() // unpauses
+// F32 elapsed = foo.getElapsedTimeF32() // does not include time between pause() and unpause()
+// Note: elapsed would also be valid with no unpause() call (= time run until pause() called)
+void LLFrameTimer::pause()
+{
+ if (mStarted)
+ mStartTime = sFrameTime - mStartTime; // save dtime
+ mStarted = false;
+}
+
+void LLFrameTimer::unpause()
+{
+ if (!mStarted)
+ mStartTime = sFrameTime - mStartTime; // restore dtime
+ mStarted = true;
+}
+
+void LLFrameTimer::setTimerExpirySec(F32 expiration)
+{
+ mExpiry = expiration + mStartTime;
+}
+
+void LLFrameTimer::setExpiryAt(F64 seconds_since_epoch)
+{
+ mStartTime = sFrameTime;
+ mExpiry = seconds_since_epoch - (USEC_TO_SEC_F64 * sStartTotalTime);
+}
+
+F64 LLFrameTimer::expiresAt() const
+{
+ F64 expires_at = U64_to_F64(sStartTotalTime) * USEC_TO_SEC_F64;
+ expires_at += mExpiry;
+ return expires_at;
+}
+
+bool LLFrameTimer::checkExpirationAndReset(F32 expiration)
+{
+ //LL_INFOS() << "LLFrameTimer::checkExpirationAndReset()" << LL_ENDL;
+ //LL_INFOS() << " mStartTime:" << mStartTime << LL_ENDL;
+ //LL_INFOS() << " sFrameTime:" << sFrameTime << LL_ENDL;
+ //LL_INFOS() << " mExpiry: " << mExpiry << LL_ENDL;
+
+ if(hasExpired())
+ {
+ reset();
+ setTimerExpirySec(expiration);
+ return true;
+ }
+ return false;
+}
+
+// static
+F32 LLFrameTimer::getFrameDeltaTimeF32()
+{
+ return (F32)(U64_to_F64(sFrameDeltaTime) * USEC_TO_SEC_F64);
+}
+
+
+// static
+// Return seconds since the current frame started
+F32 LLFrameTimer::getCurrentFrameTime()
+{
+ U64 frame_time = totalTime() - sTotalTime;
+ return (F32)(U64_to_F64(frame_time) * USEC_TO_SEC_F64);
+}
+
+// Glue code to avoid full class .h file #includes
+F32 getCurrentFrameTime()
+{
+ return (F32)(LLFrameTimer::getCurrentFrameTime());
+}
diff --git a/indra/llcommon/llframetimer.h b/indra/llcommon/llframetimer.h index 0d90eab2f4..b519a637e3 100644 --- a/indra/llcommon/llframetimer.h +++ b/indra/llcommon/llframetimer.h @@ -1,151 +1,151 @@ -/** - * @file llframetimer.h - * @brief A lightweight timer that measures seconds and is only - * updated once per frame. - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLFRAMETIMER_H -#define LL_LLFRAMETIMER_H - -/** - * *NOTE: Because of limitations on linux which we do not really have - * time to explore, the total time is derived from the frame time - * and is recsynchronized on every frame. - */ - -#include "lltimer.h" - -class LL_COMMON_API LLFrameTimer -{ -public: - LLFrameTimer() : mStartTime( sFrameTime ), mExpiry(0), mStarted(true) {} - - // Return the number of seconds since the start of this - // application instance. - static F64SecondsImplicit getElapsedSeconds() - { - // Loses msec precision after ~4.5 hours... - return sFrameTime; - } - - // Return a low precision usec since epoch - static U64 getTotalTime() - { - return sTotalTime ? U64MicrosecondsImplicit(sTotalTime) : totalTime(); - } - - // Return a low precision seconds since epoch - static F64 getTotalSeconds() - { - return sTotalSeconds; - } - - // Call this method once per frame to update the current frame time. This is actually called - // at some other times as well - static void updateFrameTime(); - - // Call this method once, and only once, per frame to update the current frame count. - static void updateFrameCount() { sFrameCount++; } - - static U32 getFrameCount() { return sFrameCount; } - - static F32 getFrameDeltaTimeF32(); - - // Return seconds since the current frame started - static F32 getCurrentFrameTime(); - - // MANIPULATORS - void start(); - void stop(); - void reset(); - void resetWithExpiry(F32 expiration); - void pause(); - void unpause(); - void setTimerExpirySec(F32 expiration); - void setExpiryAt(F64 seconds_since_epoch); - bool checkExpirationAndReset(F32 expiration); - F32 getElapsedTimeAndResetF32() { F32 t = F32(sFrameTime - mStartTime); reset(); return t; } - - void setAge(const F64 age) { mStartTime = sFrameTime - age; } - - // ACCESSORS - bool hasExpired() const { return (sFrameTime >= mExpiry); } - F32 getTimeToExpireF32() const { return (F32)(mExpiry - sFrameTime); } - F32 getElapsedTimeF32() const { return mStarted ? (F32)(sFrameTime - mStartTime) : (F32)mStartTime; } - bool getStarted() const { return mStarted; } - - // return the seconds since epoch when this timer will expire. - F64 expiresAt() const; - -protected: - // A single, high resolution timer that drives all LLFrameTimers - // *NOTE: no longer used. - //static LLTimer sInternalTimer; - - // - // Aplication constants - // - - // Start time of opp in usec since epoch - static U64 sStartTotalTime; - - // - // Data updated per frame - // - - // Seconds since application start - static F64 sFrameTime; - - // Time that has elapsed since last call to updateFrameTime() - static U64 sFrameDeltaTime; - - // Total microseconds since epoch. - static U64 sTotalTime; - - // Seconds since epoch. - static F64 sTotalSeconds; - - // Total number of frames elapsed in application - static S32 sFrameCount; - - // - // Member data - // - - // Number of seconds after application start when this timer was - // started. Set equal to sFrameTime when reset. - F64 mStartTime; - - // Timer expires this many seconds after application start time. - F64 mExpiry; - - // Useful bit of state usually associated with timers, but does - // not affect actual functionality - bool mStarted; -}; - -// Glue code for Havok (or anything else that doesn't want the full .h files) -extern F32 getCurrentFrameTime(); - -#endif // LL_LLFRAMETIMER_H +/**
+ * @file llframetimer.h
+ * @brief A lightweight timer that measures seconds and is only
+ * updated once per frame.
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLFRAMETIMER_H
+#define LL_LLFRAMETIMER_H
+
+/**
+ * *NOTE: Because of limitations on linux which we do not really have
+ * time to explore, the total time is derived from the frame time
+ * and is recsynchronized on every frame.
+ */
+
+#include "lltimer.h"
+
+class LL_COMMON_API LLFrameTimer
+{
+public:
+ LLFrameTimer() : mStartTime( sFrameTime ), mExpiry(0), mStarted(true) {}
+
+ // Return the number of seconds since the start of this
+ // application instance.
+ static F64SecondsImplicit getElapsedSeconds()
+ {
+ // Loses msec precision after ~4.5 hours...
+ return sFrameTime;
+ }
+
+ // Return a low precision usec since epoch
+ static U64 getTotalTime()
+ {
+ return sTotalTime ? U64MicrosecondsImplicit(sTotalTime) : totalTime();
+ }
+
+ // Return a low precision seconds since epoch
+ static F64 getTotalSeconds()
+ {
+ return sTotalSeconds;
+ }
+
+ // Call this method once per frame to update the current frame time. This is actually called
+ // at some other times as well
+ static void updateFrameTime();
+
+ // Call this method once, and only once, per frame to update the current frame count.
+ static void updateFrameCount() { sFrameCount++; }
+
+ static U32 getFrameCount() { return sFrameCount; }
+
+ static F32 getFrameDeltaTimeF32();
+
+ // Return seconds since the current frame started
+ static F32 getCurrentFrameTime();
+
+ // MANIPULATORS
+ void start();
+ void stop();
+ void reset();
+ void resetWithExpiry(F32 expiration);
+ void pause();
+ void unpause();
+ void setTimerExpirySec(F32 expiration);
+ void setExpiryAt(F64 seconds_since_epoch);
+ bool checkExpirationAndReset(F32 expiration);
+ F32 getElapsedTimeAndResetF32() { F32 t = F32(sFrameTime - mStartTime); reset(); return t; }
+
+ void setAge(const F64 age) { mStartTime = sFrameTime - age; }
+
+ // ACCESSORS
+ bool hasExpired() const { return (sFrameTime >= mExpiry); }
+ F32 getTimeToExpireF32() const { return (F32)(mExpiry - sFrameTime); }
+ F32 getElapsedTimeF32() const { return mStarted ? (F32)(sFrameTime - mStartTime) : (F32)mStartTime; }
+ bool getStarted() const { return mStarted; }
+
+ // return the seconds since epoch when this timer will expire.
+ F64 expiresAt() const;
+
+protected:
+ // A single, high resolution timer that drives all LLFrameTimers
+ // *NOTE: no longer used.
+ //static LLTimer sInternalTimer;
+
+ //
+ // Aplication constants
+ //
+
+ // Start time of opp in usec since epoch
+ static U64 sStartTotalTime;
+
+ //
+ // Data updated per frame
+ //
+
+ // Seconds since application start
+ static F64 sFrameTime;
+
+ // Time that has elapsed since last call to updateFrameTime()
+ static U64 sFrameDeltaTime;
+
+ // Total microseconds since epoch.
+ static U64 sTotalTime;
+
+ // Seconds since epoch.
+ static F64 sTotalSeconds;
+
+ // Total number of frames elapsed in application
+ static S32 sFrameCount;
+
+ //
+ // Member data
+ //
+
+ // Number of seconds after application start when this timer was
+ // started. Set equal to sFrameTime when reset.
+ F64 mStartTime;
+
+ // Timer expires this many seconds after application start time.
+ F64 mExpiry;
+
+ // Useful bit of state usually associated with timers, but does
+ // not affect actual functionality
+ bool mStarted;
+};
+
+// Glue code for Havok (or anything else that doesn't want the full .h files)
+extern F32 getCurrentFrameTime();
+
+#endif // LL_LLFRAMETIMER_H
diff --git a/indra/llcommon/llhandle.h b/indra/llcommon/llhandle.h index 570cd330b8..ceea1d9c48 100644 --- a/indra/llcommon/llhandle.h +++ b/indra/llcommon/llhandle.h @@ -1,4 +1,4 @@ -/** +/** * @file llhandle.h * @brief "Handle" to an object (usually a floater) whose lifetime you don't * control. @@ -6,21 +6,21 @@ * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. -* +* * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. -* +* * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. -* +* * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -* +* * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -42,22 +42,22 @@ class LLTombStone : public LLRefCount { public: - LLTombStone(void* target = NULL) : mTarget(target) {} + LLTombStone(void* target = NULL) : mTarget(target) {} - void setTarget(void* target) { mTarget = target; } - void* getTarget() const { return mTarget; } + void setTarget(void* target) { mTarget = target; } + void* getTarget() const { return mTarget; } private: - mutable void* mTarget; + mutable void* mTarget; }; /** - * LLHandles are used to refer to objects whose lifetime you do not control or influence. - * Calling get() on a handle will return a pointer to the referenced object or NULL, - * if the object no longer exists. Note that during the lifetime of the returned pointer, - * you are assuming that the object will not be deleted by any action you perform, - * or any other thread, as normal when using pointers, so avoid using that pointer outside of - * the local code block. - * + * LLHandles are used to refer to objects whose lifetime you do not control or influence. + * Calling get() on a handle will return a pointer to the referenced object or NULL, + * if the object no longer exists. Note that during the lifetime of the returned pointer, + * you are assuming that the object will not be deleted by any action you perform, + * or any other thread, as normal when using pointers, so avoid using that pointer outside of + * the local code block. + * * https://wiki.lindenlab.com/mediawiki/index.php?title=LLHandle&oldid=79669 * * The implementation is like some "weak pointer" implementations. When we @@ -84,58 +84,58 @@ private: template <typename T> class LLHandle { - template <typename U> friend class LLHandle; - template <typename U> friend class LLHandleProvider; + template <typename U> friend class LLHandle; + template <typename U> friend class LLHandleProvider; public: - LLHandle() : mTombStone(getDefaultTombStone()) {} - - template<typename U> - LLHandle(const LLHandle<U>& other, typename boost::enable_if< typename boost::is_convertible<U*, T*> >::type* dummy = 0) - : mTombStone(other.mTombStone) - {} - - bool isDead() const - { - return mTombStone->getTarget() == NULL; - } - - void markDead() - { - mTombStone = getDefaultTombStone(); - } - - T* get() const - { - return reinterpret_cast<T*>(mTombStone->getTarget()); - } - - friend bool operator== (const LLHandle<T>& lhs, const LLHandle<T>& rhs) - { - return lhs.mTombStone == rhs.mTombStone; - } - friend bool operator!= (const LLHandle<T>& lhs, const LLHandle<T>& rhs) - { - return !(lhs == rhs); - } - friend bool operator< (const LLHandle<T>& lhs, const LLHandle<T>& rhs) - { - return lhs.mTombStone < rhs.mTombStone; - } - friend bool operator> (const LLHandle<T>& lhs, const LLHandle<T>& rhs) - { - return lhs.mTombStone > rhs.mTombStone; - } + LLHandle() : mTombStone(getDefaultTombStone()) {} + + template<typename U> + LLHandle(const LLHandle<U>& other, typename boost::enable_if< typename boost::is_convertible<U*, T*> >::type* dummy = 0) + : mTombStone(other.mTombStone) + {} + + bool isDead() const + { + return mTombStone->getTarget() == NULL; + } + + void markDead() + { + mTombStone = getDefaultTombStone(); + } + + T* get() const + { + return reinterpret_cast<T*>(mTombStone->getTarget()); + } + + friend bool operator== (const LLHandle<T>& lhs, const LLHandle<T>& rhs) + { + return lhs.mTombStone == rhs.mTombStone; + } + friend bool operator!= (const LLHandle<T>& lhs, const LLHandle<T>& rhs) + { + return !(lhs == rhs); + } + friend bool operator< (const LLHandle<T>& lhs, const LLHandle<T>& rhs) + { + return lhs.mTombStone < rhs.mTombStone; + } + friend bool operator> (const LLHandle<T>& lhs, const LLHandle<T>& rhs) + { + return lhs.mTombStone > rhs.mTombStone; + } protected: - LLPointer<LLTombStone> mTombStone; + LLPointer<LLTombStone> mTombStone; private: - typedef T* pointer_t; - static LLPointer<LLTombStone>& getDefaultTombStone() - { - static LLPointer<LLTombStone> sDefaultTombStone = new LLTombStone; - return sDefaultTombStone; - } + typedef T* pointer_t; + static LLPointer<LLTombStone>& getDefaultTombStone() + { + static LLPointer<LLTombStone> sDefaultTombStone = new LLTombStone; + return sDefaultTombStone; + } }; /** @@ -150,36 +150,36 @@ template <typename T> class LLRootHandle : public LLHandle<T> { public: - typedef LLRootHandle<T> self_t; - typedef LLHandle<T> base_t; - - LLRootHandle(T* object) { bind(object); } - LLRootHandle() {}; - ~LLRootHandle() { unbind(); } - - // this is redundant, since an LLRootHandle *is* an LLHandle - //LLHandle<T> getHandle() { return LLHandle<T>(*this); } - - void bind(T* object) - { - // unbind existing tombstone - if (LLHandle<T>::mTombStone.notNull()) - { - if (LLHandle<T>::mTombStone->getTarget() == (void*)object) return; - LLHandle<T>::mTombStone->setTarget(NULL); - } - // tombstone reference counted, so no paired delete - LLHandle<T>::mTombStone = new LLTombStone((void*)object); - } - - void unbind() - { - LLHandle<T>::mTombStone->setTarget(NULL); - } - - //don't allow copying of root handles, since there should only be one + typedef LLRootHandle<T> self_t; + typedef LLHandle<T> base_t; + + LLRootHandle(T* object) { bind(object); } + LLRootHandle() {}; + ~LLRootHandle() { unbind(); } + + // this is redundant, since an LLRootHandle *is* an LLHandle + //LLHandle<T> getHandle() { return LLHandle<T>(*this); } + + void bind(T* object) + { + // unbind existing tombstone + if (LLHandle<T>::mTombStone.notNull()) + { + if (LLHandle<T>::mTombStone->getTarget() == (void*)object) return; + LLHandle<T>::mTombStone->setTarget(NULL); + } + // tombstone reference counted, so no paired delete + LLHandle<T>::mTombStone = new LLTombStone((void*)object); + } + + void unbind() + { + LLHandle<T>::mTombStone->setTarget(NULL); + } + + //don't allow copying of root handles, since there should only be one private: - LLRootHandle(const LLRootHandle& other) {}; + LLRootHandle(const LLRootHandle& other) {}; }; /** @@ -190,31 +190,31 @@ template <typename T> class LLHandleProvider { public: - LLHandle<T> getHandle() const - { - // perform lazy binding to avoid small tombstone allocations for handle - // providers whose handles are never referenced - mHandle.bind(static_cast<T*>(const_cast<LLHandleProvider<T>* >(this))); - return mHandle; - } - - template <typename U> - LLHandle<U> getDerivedHandle(typename boost::enable_if< typename boost::is_convertible<U*, T*> >::type* dummy = 0) const - { - LLHandle<U> downcast_handle; - downcast_handle.mTombStone = getHandle().mTombStone; - return downcast_handle; - } + LLHandle<T> getHandle() const + { + // perform lazy binding to avoid small tombstone allocations for handle + // providers whose handles are never referenced + mHandle.bind(static_cast<T*>(const_cast<LLHandleProvider<T>* >(this))); + return mHandle; + } + + template <typename U> + LLHandle<U> getDerivedHandle(typename boost::enable_if< typename boost::is_convertible<U*, T*> >::type* dummy = 0) const + { + LLHandle<U> downcast_handle; + downcast_handle.mTombStone = getHandle().mTombStone; + return downcast_handle; + } protected: - typedef LLHandle<T> handle_type_t; - LLHandleProvider() - { - // provided here to enforce T deriving from LLHandleProvider<T> - } + typedef LLHandle<T> handle_type_t; + LLHandleProvider() + { + // provided here to enforce T deriving from LLHandleProvider<T> + } private: - mutable LLRootHandle<T> mHandle; + mutable LLRootHandle<T> mHandle; }; @@ -236,12 +236,12 @@ protected: }; /** - * This is a simple wrapper for Handles, allowing direct calls to the underlying - * pointer. The checked handle will throw a Stale if an attempt - * is made to access the object referenced by the handle and that object has + * This is a simple wrapper for Handles, allowing direct calls to the underlying + * pointer. The checked handle will throw a Stale if an attempt + * is made to access the object referenced by the handle and that object has * been destroyed. **/ -template <typename T> +template <typename T> class LLCheckedHandle: public LLCheckedHandleBase { public: @@ -269,7 +269,7 @@ public: } /** - * Converts the LLCheckedHandle to a bool. Allows for if (chkdHandle) {} + * Converts the LLCheckedHandle to a bool. Allows for if (chkdHandle) {} * Does not throw. */ /*explicit*/ operator bool() const // explicit conversion operator not available with Linux compiler @@ -278,8 +278,8 @@ public: } /** - * Attempt to call a method or access a member in the structure referenced - * by the handle. If the handle no longer points to a valid structure + * Attempt to call a method or access a member in the structure referenced + * by the handle. If the handle no longer points to a valid structure * throw a Stale. */ T* operator ->() const diff --git a/indra/llcommon/llhash.h b/indra/llcommon/llhash.h index 4b58e81565..aa1fa2d336 100644 --- a/indra/llcommon/llhash.h +++ b/indra/llcommon/llhash.h @@ -1,25 +1,25 @@ -/** +/** * @file llhash.h * @brief Wrapper for a hash function. * * $LicenseInfo:firstyear=2004&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -36,17 +36,17 @@ inline size_t llhash( const char * value ) { - // boost::hash is defined for std::string and for char, but there's no - // special overload for const char*. The lazy approach would be to - // instantiate a std::string and take its hash, but that might be more - // overhead than our callers want. Or we could use boost::hash_range() -- - // but that would require a preliminary pass over the value to determine - // the end iterator. Instead, use boost::hash_combine() to hash individual - // characters. - std::size_t seed = 0; - for ( ; *value; ++value) - boost::hash_combine(seed, *value); - return seed; + // boost::hash is defined for std::string and for char, but there's no + // special overload for const char*. The lazy approach would be to + // instantiate a std::string and take its hash, but that might be more + // overhead than our callers want. Or we could use boost::hash_range() -- + // but that would require a preliminary pass over the value to determine + // the end iterator. Instead, use boost::hash_combine() to hash individual + // characters. + std::size_t seed = 0; + for ( ; *value; ++value) + boost::hash_combine(seed, *value); + return seed; } #endif diff --git a/indra/llcommon/llheartbeat.cpp b/indra/llcommon/llheartbeat.cpp index 19b7452748..96480050a5 100644 --- a/indra/llcommon/llheartbeat.cpp +++ b/indra/llcommon/llheartbeat.cpp @@ -5,21 +5,21 @@ * $LicenseInfo:firstyear=2008&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -33,28 +33,28 @@ #include "llheartbeat.h" LLHeartbeat::LLHeartbeat(F32 secs_between_heartbeat, - F32 aggressive_heartbeat_panic_secs, - F32 aggressive_heartbeat_max_blocking_secs) - : mSecsBetweenHeartbeat(secs_between_heartbeat), - mAggressiveHeartbeatPanicSecs(aggressive_heartbeat_panic_secs), - mAggressiveHeartbeatMaxBlockingSecs(aggressive_heartbeat_max_blocking_secs), - mSuppressed(false) + F32 aggressive_heartbeat_panic_secs, + F32 aggressive_heartbeat_max_blocking_secs) + : mSecsBetweenHeartbeat(secs_between_heartbeat), + mAggressiveHeartbeatPanicSecs(aggressive_heartbeat_panic_secs), + mAggressiveHeartbeatMaxBlockingSecs(aggressive_heartbeat_max_blocking_secs), + mSuppressed(false) { - mBeatTimer.reset(); - mBeatTimer.setTimerExpirySec(mSecsBetweenHeartbeat); - mPanicTimer.reset(); - mPanicTimer.setTimerExpirySec(mAggressiveHeartbeatPanicSecs); + mBeatTimer.reset(); + mBeatTimer.setTimerExpirySec(mSecsBetweenHeartbeat); + mPanicTimer.reset(); + mPanicTimer.setTimerExpirySec(mAggressiveHeartbeatPanicSecs); } LLHeartbeat::~LLHeartbeat() { - // do nothing. + // do nothing. } void LLHeartbeat::setSuppressed(bool is_suppressed) { - mSuppressed = is_suppressed; + mSuppressed = is_suppressed; } // returns 0 on success, -1 on permanent failure, 1 on temporary failure @@ -62,104 +62,104 @@ int LLHeartbeat::rawSend() { #if LL_WINDOWS - return 0; // Pretend we succeeded. + return 0; // Pretend we succeeded. #else - if (mSuppressed) - return 0; // Pretend we succeeded. + if (mSuppressed) + return 0; // Pretend we succeeded. - int result; + int result; #ifndef LL_DARWIN - union sigval dummy; - result = sigqueue(getppid(), LL_HEARTBEAT_SIGNAL, dummy); + union sigval dummy; + result = sigqueue(getppid(), LL_HEARTBEAT_SIGNAL, dummy); #else - result = kill(getppid(), LL_HEARTBEAT_SIGNAL); + result = kill(getppid(), LL_HEARTBEAT_SIGNAL); #endif - if (result == 0) - return 0; // success + if (result == 0) + return 0; // success - int err = errno; - if (err == EAGAIN) - return 1; // failed to queue, try again + int err = errno; + if (err == EAGAIN) + return 1; // failed to queue, try again - return -1; // other failure. + return -1; // other failure. #endif } int LLHeartbeat::rawSendWithTimeout(F32 timeout_sec) { - int result = 0; - - // Spin tightly until our heartbeat is digested by the watchdog - // or we time-out. We don't really want to sleep because our - // wake-up time might be undesirably synchronised to a hidden - // clock by the system's scheduler. - mTimeoutTimer.reset(); - mTimeoutTimer.setTimerExpirySec(timeout_sec); - do { - result = rawSend(); - //LL_INFOS() << " HEARTSENDc=" << result << LL_ENDL; - } while (result==1 && !mTimeoutTimer.hasExpired()); - - return result; + int result = 0; + + // Spin tightly until our heartbeat is digested by the watchdog + // or we time-out. We don't really want to sleep because our + // wake-up time might be undesirably synchronised to a hidden + // clock by the system's scheduler. + mTimeoutTimer.reset(); + mTimeoutTimer.setTimerExpirySec(timeout_sec); + do { + result = rawSend(); + //LL_INFOS() << " HEARTSENDc=" << result << LL_ENDL; + } while (result==1 && !mTimeoutTimer.hasExpired()); + + return result; } bool LLHeartbeat::send(F32 timeout_sec) { - bool total_success = false; - int result = 1; - - if (timeout_sec > 0.f) { - // force a spin until success or timeout - result = rawSendWithTimeout(timeout_sec); - } else { - if (mBeatTimer.hasExpired()) { - // zero-timeout; we don't care too much whether our - // heartbeat was digested. - result = rawSend(); - //LL_INFOS() << " HEARTSENDb=" << result << LL_ENDL; - } - } - - if (result == -1) { - // big failure. - } else if (result == 0) { - total_success = true; - } else { - // need to retry at some point - } - - if (total_success) { - mBeatTimer.reset(); - mBeatTimer.setTimerExpirySec(mSecsBetweenHeartbeat); - // reset the time until we start panicking about lost - // heartbeats again. - mPanicTimer.reset(); - mPanicTimer.setTimerExpirySec(mAggressiveHeartbeatPanicSecs); - } else { - // leave mBeatTimer as expired so we'll lazily poke the - // watchdog again next time through. - } - - if (mPanicTimer.hasExpired()) { - // It's been ages since we successfully had a heartbeat - // digested by the watchdog. Sit here and spin a while - // in the hope that we can force it through. - LL_WARNS() << "Unable to deliver heartbeat to launcher for " << mPanicTimer.getElapsedTimeF32() << " seconds. Going to try very hard for up to " << mAggressiveHeartbeatMaxBlockingSecs << " seconds." << LL_ENDL; - result = rawSendWithTimeout(mAggressiveHeartbeatMaxBlockingSecs); - if (result == 0) { - total_success = true; - } else { - // we couldn't even force it through. That's bad, - // but we'll try again in a while. - LL_WARNS() << "Could not deliver heartbeat to launcher even after trying very hard for " << mAggressiveHeartbeatMaxBlockingSecs << " seconds." << LL_ENDL; - } - - // in any case, reset the panic timer. - mPanicTimer.reset(); - mPanicTimer.setTimerExpirySec(mAggressiveHeartbeatPanicSecs); - } - - return total_success; + bool total_success = false; + int result = 1; + + if (timeout_sec > 0.f) { + // force a spin until success or timeout + result = rawSendWithTimeout(timeout_sec); + } else { + if (mBeatTimer.hasExpired()) { + // zero-timeout; we don't care too much whether our + // heartbeat was digested. + result = rawSend(); + //LL_INFOS() << " HEARTSENDb=" << result << LL_ENDL; + } + } + + if (result == -1) { + // big failure. + } else if (result == 0) { + total_success = true; + } else { + // need to retry at some point + } + + if (total_success) { + mBeatTimer.reset(); + mBeatTimer.setTimerExpirySec(mSecsBetweenHeartbeat); + // reset the time until we start panicking about lost + // heartbeats again. + mPanicTimer.reset(); + mPanicTimer.setTimerExpirySec(mAggressiveHeartbeatPanicSecs); + } else { + // leave mBeatTimer as expired so we'll lazily poke the + // watchdog again next time through. + } + + if (mPanicTimer.hasExpired()) { + // It's been ages since we successfully had a heartbeat + // digested by the watchdog. Sit here and spin a while + // in the hope that we can force it through. + LL_WARNS() << "Unable to deliver heartbeat to launcher for " << mPanicTimer.getElapsedTimeF32() << " seconds. Going to try very hard for up to " << mAggressiveHeartbeatMaxBlockingSecs << " seconds." << LL_ENDL; + result = rawSendWithTimeout(mAggressiveHeartbeatMaxBlockingSecs); + if (result == 0) { + total_success = true; + } else { + // we couldn't even force it through. That's bad, + // but we'll try again in a while. + LL_WARNS() << "Could not deliver heartbeat to launcher even after trying very hard for " << mAggressiveHeartbeatMaxBlockingSecs << " seconds." << LL_ENDL; + } + + // in any case, reset the panic timer. + mPanicTimer.reset(); + mPanicTimer.setTimerExpirySec(mAggressiveHeartbeatPanicSecs); + } + + return total_success; } diff --git a/indra/llcommon/llheartbeat.h b/indra/llcommon/llheartbeat.h index 4a75fcc103..54feeb4c92 100644 --- a/indra/llcommon/llheartbeat.h +++ b/indra/llcommon/llheartbeat.h @@ -1,25 +1,25 @@ -/** +/** * @file llheartbeat.h * @brief Class encapsulating logic for telling a watchdog that we live. * * $LicenseInfo:firstyear=2008&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -37,32 +37,32 @@ class LL_COMMON_API LLHeartbeat { public: - // secs_between_heartbeat: after a heartbeat is successfully delivered, - // we suppress sending more for this length of time. - // aggressive_heartbeat_panic_secs: if we've been failing to - // successfully deliver heartbeats for this length of time then - // we block for a while until we're really sure we got one delivered. - // aggressive_heartbeat_max_blocking_secs: this is the length of - // time we block for when we're aggressively ensuring that a 'panic' - // heartbeat was delivered. - LLHeartbeat(F32 secs_between_heartbeat = 5.0f, - F32 aggressive_heartbeat_panic_secs = 10.0f, - F32 aggressive_heartbeat_max_blocking_secs = 4.0f); - ~LLHeartbeat(); + // secs_between_heartbeat: after a heartbeat is successfully delivered, + // we suppress sending more for this length of time. + // aggressive_heartbeat_panic_secs: if we've been failing to + // successfully deliver heartbeats for this length of time then + // we block for a while until we're really sure we got one delivered. + // aggressive_heartbeat_max_blocking_secs: this is the length of + // time we block for when we're aggressively ensuring that a 'panic' + // heartbeat was delivered. + LLHeartbeat(F32 secs_between_heartbeat = 5.0f, + F32 aggressive_heartbeat_panic_secs = 10.0f, + F32 aggressive_heartbeat_max_blocking_secs = 4.0f); + ~LLHeartbeat(); - bool send(F32 timeout_sec = 0.0f); - void setSuppressed(bool is_suppressed); + bool send(F32 timeout_sec = 0.0f); + void setSuppressed(bool is_suppressed); private: - int rawSend(); - int rawSendWithTimeout(F32 timeout_sec); - F32 mSecsBetweenHeartbeat; - F32 mAggressiveHeartbeatPanicSecs; - F32 mAggressiveHeartbeatMaxBlockingSecs; - bool mSuppressed; - LLTimer mBeatTimer; - LLTimer mPanicTimer; - LLTimer mTimeoutTimer; + int rawSend(); + int rawSendWithTimeout(F32 timeout_sec); + F32 mSecsBetweenHeartbeat; + F32 mAggressiveHeartbeatPanicSecs; + F32 mAggressiveHeartbeatMaxBlockingSecs; + bool mSuppressed; + LLTimer mBeatTimer; + LLTimer mPanicTimer; + LLTimer mTimeoutTimer; }; #endif // LL_HEARTBEAT_H diff --git a/indra/llcommon/llheteromap.cpp b/indra/llcommon/llheteromap.cpp index c84e49d085..823bea7a3c 100644 --- a/indra/llcommon/llheteromap.cpp +++ b/indra/llcommon/llheteromap.cpp @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2016-10-12 * @brief Implementation for llheteromap. - * + * * $LicenseInfo:firstyear=2016&license=viewerlgpl$ * Copyright (c) 2016, Linden Research, Inc. * $/LicenseInfo$ @@ -22,7 +22,7 @@ LLHeteroMap::~LLHeteroMap() { // For each entry in our map, we must call its deleter, which is the only // record we have of its original type. - for (TypeMap::value_type& pair : mMap) + for (TypeMap::value_type& pair : mMap) { // pair.second is the std::pair; pair.second.first is the void*; // pair.second.second points to the deleter function diff --git a/indra/llcommon/llheteromap.h b/indra/llcommon/llheteromap.h index 7e96172333..d8e6fefb17 100644 --- a/indra/llcommon/llheteromap.h +++ b/indra/llcommon/llheteromap.h @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2016-10-12 * @brief Map capable of storing objects of diverse types, looked up by type. - * + * * $LicenseInfo:firstyear=2016&license=viewerlgpl$ * Copyright (c) 2016, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/llindexedvector.h b/indra/llcommon/llindexedvector.h index 68c3821802..de3ae0dcc4 100644 --- a/indra/llcommon/llindexedvector.h +++ b/indra/llcommon/llindexedvector.h @@ -1,25 +1,25 @@ -/** +/** * @file lldarray.h * @brief Wrapped std::vector for backward compatibility. * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -36,65 +36,65 @@ // LLIndexedVector //-------------------------------------------------------- -template <typename Type, typename Key, int BlockSize = 32> +template <typename Type, typename Key, int BlockSize = 32> class LLIndexedVector { public: - typedef typename std::vector<Type>::iterator iterator; - typedef typename std::vector<Type>::const_iterator const_iterator; - typedef typename std::vector<Type>::reverse_iterator reverse_iterator; - typedef typename std::vector<Type>::const_reverse_iterator const_reverse_iterator; - typedef typename std::vector<Type>::size_type size_type; + typedef typename std::vector<Type>::iterator iterator; + typedef typename std::vector<Type>::const_iterator const_iterator; + typedef typename std::vector<Type>::reverse_iterator reverse_iterator; + typedef typename std::vector<Type>::const_reverse_iterator const_reverse_iterator; + typedef typename std::vector<Type>::size_type size_type; protected: - std::vector<Type> mVector; - std::map<Key, U32> mIndexMap; - + std::vector<Type> mVector; + std::map<Key, U32> mIndexMap; + public: - LLIndexedVector() { mVector.reserve(BlockSize); } - - iterator begin() { return mVector.begin(); } - const_iterator begin() const { return mVector.begin(); } - iterator end() { return mVector.end(); } - const_iterator end() const { return mVector.end(); } + LLIndexedVector() { mVector.reserve(BlockSize); } + + iterator begin() { return mVector.begin(); } + const_iterator begin() const { return mVector.begin(); } + iterator end() { return mVector.end(); } + const_iterator end() const { return mVector.end(); } + + reverse_iterator rbegin() { return mVector.rbegin(); } + const_reverse_iterator rbegin() const { return mVector.rbegin(); } + reverse_iterator rend() { return mVector.rend(); } + const_reverse_iterator rend() const { return mVector.rend(); } - reverse_iterator rbegin() { return mVector.rbegin(); } - const_reverse_iterator rbegin() const { return mVector.rbegin(); } - reverse_iterator rend() { return mVector.rend(); } - const_reverse_iterator rend() const { return mVector.rend(); } + void reset() { mVector.resize(0); mIndexMap.resize(0); } + bool empty() const { return mVector.empty(); } + size_type size() const { return mVector.size(); } - void reset() { mVector.resize(0); mIndexMap.resize(0); } - bool empty() const { return mVector.empty(); } - size_type size() const { return mVector.size(); } - - Type& operator[](const Key& k) - { - typename std::map<Key, U32>::const_iterator iter = mIndexMap.find(k); - if (iter == mIndexMap.end()) - { - U32 n = mVector.size(); - mIndexMap[k] = n; - mVector.push_back(Type()); - llassert(mVector.size() == mIndexMap.size()); - return mVector[n]; - } - else - { - return mVector[iter->second]; - } - } + Type& operator[](const Key& k) + { + typename std::map<Key, U32>::const_iterator iter = mIndexMap.find(k); + if (iter == mIndexMap.end()) + { + U32 n = mVector.size(); + mIndexMap[k] = n; + mVector.push_back(Type()); + llassert(mVector.size() == mIndexMap.size()); + return mVector[n]; + } + else + { + return mVector[iter->second]; + } + } - const_iterator find(const Key& k) const - { - typename std::map<Key, U32>::const_iterator iter = mIndexMap.find(k); - if(iter == mIndexMap.end()) - { - return mVector.end(); - } - else - { - return mVector.begin() + iter->second; - } - } + const_iterator find(const Key& k) const + { + typename std::map<Key, U32>::const_iterator iter = mIndexMap.find(k); + if(iter == mIndexMap.end()) + { + return mVector.end(); + } + else + { + return mVector.begin() + iter->second; + } + } }; #endif diff --git a/indra/llcommon/llinitdestroyclass.cpp b/indra/llcommon/llinitdestroyclass.cpp index e3b9e6d099..50e6e24b8d 100644 --- a/indra/llcommon/llinitdestroyclass.cpp +++ b/indra/llcommon/llinitdestroyclass.cpp @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2016-08-30 * @brief Implementation for llinitdestroyclass. - * + * * $LicenseInfo:firstyear=2016&license=viewerlgpl$ * Copyright (c) 2016, Linden Research, Inc. * $/LicenseInfo$ @@ -21,9 +21,9 @@ void LLCallbackRegistry::fireCallbacks() const { - for (FuncList::value_type pair : mCallbacks) - { - LL_INFOS("LLInitDestroyClass") << "calling " << pair.first << "()" << LL_ENDL; - pair.second(); - } + for (FuncList::value_type pair : mCallbacks) + { + LL_INFOS("LLInitDestroyClass") << "calling " << pair.first << "()" << LL_ENDL; + pair.second(); + } } diff --git a/indra/llcommon/llinitdestroyclass.h b/indra/llcommon/llinitdestroyclass.h index 5f979614fe..2354c9f2ed 100644 --- a/indra/llcommon/llinitdestroyclass.h +++ b/indra/llcommon/llinitdestroyclass.h @@ -50,21 +50,21 @@ class LLCallbackRegistry { public: - typedef boost::function<void()> func_t; + typedef boost::function<void()> func_t; - void registerCallback(const std::string& name, const func_t& func) - { - mCallbacks.push_back(FuncList::value_type(name, func)); - } + void registerCallback(const std::string& name, const func_t& func) + { + mCallbacks.push_back(FuncList::value_type(name, func)); + } - void fireCallbacks() const; + void fireCallbacks() const; private: - // Arguably this should be a boost::signals2::signal, which is, after all, - // a sequence of callables. We manage it by hand so we can log a name for - // each registered function we call. - typedef std::vector< std::pair<std::string, func_t> > FuncList; - FuncList mCallbacks; + // Arguably this should be a boost::signals2::signal, which is, after all, + // a sequence of callables. We manage it by hand so we can log a name for + // each registered function we call. + typedef std::vector< std::pair<std::string, func_t> > FuncList; + FuncList mCallbacks; }; /** @@ -74,11 +74,11 @@ private: * (before main()), requiring LLInitClassList to be fully constructed on * demand regardless of module initialization order. */ -class LLInitClassList : - public LLCallbackRegistry, - public LLSingleton<LLInitClassList> +class LLInitClassList : + public LLCallbackRegistry, + public LLSingleton<LLInitClassList> { - LLSINGLETON_EMPTY_CTOR(LLInitClassList); + LLSINGLETON_EMPTY_CTOR(LLInitClassList); }; /** @@ -88,11 +88,11 @@ class LLInitClassList : * time (before main()), requiring LLDestroyClassList to be fully constructed * on demand regardless of module initialization order. */ -class LLDestroyClassList : - public LLCallbackRegistry, - public LLSingleton<LLDestroyClassList> +class LLDestroyClassList : + public LLCallbackRegistry, + public LLSingleton<LLDestroyClassList> { - LLSINGLETON_EMPTY_CTOR(LLDestroyClassList); + LLSINGLETON_EMPTY_CTOR(LLDestroyClassList); }; /** @@ -105,19 +105,19 @@ template<typename T> class LLRegisterWith { public: - LLRegisterWith(const std::string& name, const LLCallbackRegistry::func_t& func) - { - T::instance().registerCallback(name, func); - } - - // this avoids a MSVC bug where non-referenced static members are "optimized" away - // even if their constructors have side effects - S32 reference() - { - S32 dummy; - dummy = 0; - return dummy; - } + LLRegisterWith(const std::string& name, const LLCallbackRegistry::func_t& func) + { + T::instance().registerCallback(name, func); + } + + // this avoids a MSVC bug where non-referenced static members are "optimized" away + // even if their constructors have side effects + S32 reference() + { + S32 dummy; + dummy = 0; + return dummy; + } }; /** @@ -133,11 +133,11 @@ template<typename T> class LLInitClass { public: - LLInitClass() { sRegister.reference(); } + LLInitClass() { sRegister.reference(); } - // When this static member is initialized, the subclass initClass() method - // is registered on LLInitClassList. See sRegister definition below. - static LLRegisterWith<LLInitClassList> sRegister; + // When this static member is initialized, the subclass initClass() method + // is registered on LLInitClassList. See sRegister definition below. + static LLRegisterWith<LLInitClassList> sRegister; }; /** @@ -153,23 +153,23 @@ template<typename T> class LLDestroyClass { public: - LLDestroyClass() { sRegister.reference(); } + LLDestroyClass() { sRegister.reference(); } - // When this static member is initialized, the subclass destroyClass() - // method is registered on LLInitClassList. See sRegister definition - // below. - static LLRegisterWith<LLDestroyClassList> sRegister; + // When this static member is initialized, the subclass destroyClass() + // method is registered on LLInitClassList. See sRegister definition + // below. + static LLRegisterWith<LLDestroyClassList> sRegister; }; // Here's where LLInitClass<T> specifies the subclass initClass() method. template <typename T> LLRegisterWith<LLInitClassList> LLInitClass<T>::sRegister(std::string(typeid(T).name()) + "::initClass", - &T::initClass); + &T::initClass); // Here's where LLDestroyClass<T> specifies the subclass destroyClass() method. template <typename T> LLRegisterWith<LLDestroyClassList> LLDestroyClass<T>::sRegister(std::string(typeid(T).name()) + "::destroyClass", - &T::destroyClass); + &T::destroyClass); #endif /* ! defined(LL_LLINITDESTROYCLASS_H) */ diff --git a/indra/llcommon/llinitparam.cpp b/indra/llcommon/llinitparam.cpp index d15bd2f619..7bba1a540d 100644 --- a/indra/llcommon/llinitparam.cpp +++ b/indra/llcommon/llinitparam.cpp @@ -1,26 +1,26 @@ -/** +/** * @file llinitparam.cpp - * @brief parameter block abstraction for creating complex objects and + * @brief parameter block abstraction for creating complex objects and * parsing construction parameters from xml and LLSD * * $LicenseInfo:firstyear=2008&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -34,434 +34,434 @@ namespace LLInitParam { - predicate_rule_t default_parse_rules() - { - return ll_make_predicate(PROVIDED) && !ll_make_predicate(EMPTY); - } - - // - // Param - // - Param::Param(BaseBlock* enclosing_block) - : mIsProvided(false) - { - const U8* my_addr = reinterpret_cast<const U8*>(this); - const U8* block_addr = reinterpret_cast<const U8*>(enclosing_block); - U32 enclosing_block_offset = 0x7FFFffff & (U32)(my_addr - block_addr); - mEnclosingBlockOffsetLow = enclosing_block_offset & 0x0000ffff; - mEnclosingBlockOffsetHigh = (enclosing_block_offset & 0x007f0000) >> 16; - } - - // - // ParamDescriptor - // - ParamDescriptor::ParamDescriptor(param_handle_t p, - merge_func_t merge_func, - deserialize_func_t deserialize_func, - serialize_func_t serialize_func, - validation_func_t validation_func, - inspect_func_t inspect_func, - S32 min_count, - S32 max_count) - : mParamHandle(p), - mMergeFunc(merge_func), - mDeserializeFunc(deserialize_func), - mSerializeFunc(serialize_func), - mValidationFunc(validation_func), - mInspectFunc(inspect_func), - mMinCount(min_count), - mMaxCount(max_count), - mUserData(NULL) - {} - - ParamDescriptor::ParamDescriptor() - : mParamHandle(0), - mMergeFunc(NULL), - mDeserializeFunc(NULL), - mSerializeFunc(NULL), - mValidationFunc(NULL), - mInspectFunc(NULL), - mMinCount(0), - mMaxCount(0), - mUserData(NULL) - {} - - ParamDescriptor::~ParamDescriptor() - { - delete mUserData; - } - - // - // Parser - // - Parser::~Parser() - {} - - void Parser::parserWarning(const std::string& message) - { - if (mParseSilently) return; - LL_WARNS() << message << LL_ENDL; - } - - void Parser::parserError(const std::string& message) - { - if (mParseSilently) return; - LL_ERRS() << message << LL_ENDL; - } - - - // - // BlockDescriptor - // - void BlockDescriptor::aggregateBlockData(BlockDescriptor& src_block_data) - { - mNamedParams.insert(src_block_data.mNamedParams.begin(), src_block_data.mNamedParams.end()); - std::copy(src_block_data.mUnnamedParams.begin(), src_block_data.mUnnamedParams.end(), std::back_inserter(mUnnamedParams)); - std::copy(src_block_data.mValidationList.begin(), src_block_data.mValidationList.end(), std::back_inserter(mValidationList)); - std::copy(src_block_data.mAllParams.begin(), src_block_data.mAllParams.end(), std::back_inserter(mAllParams)); - } - - void BlockDescriptor::addParam(const ParamDescriptorPtr in_param, const char* char_name) - { - // create a copy of the param descriptor in mAllParams - // so other data structures can store a pointer to it - mAllParams.push_back(in_param); - ParamDescriptorPtr param(mAllParams.back()); - - std::string name(char_name); - if ((size_t)param->mParamHandle > mMaxParamOffset) - { - LL_ERRS() << "Attempted to register param with block defined for parent class, make sure to derive from LLInitParam::Block<YOUR_CLASS, PARAM_BLOCK_BASE_CLASS>" << LL_ENDL; - } - - if (name.empty()) - { - mUnnamedParams.push_back(param); - } - else - { - // don't use insert, since we want to overwrite existing entries - mNamedParams[name] = param; - } - - if (param->mValidationFunc) - { - mValidationList.push_back(std::make_pair(param->mParamHandle, param->mValidationFunc)); - } - } - - BlockDescriptor::BlockDescriptor() - : mMaxParamOffset(0), - mInitializationState(UNINITIALIZED), - mCurrentBlockPtr(NULL) - {} - - // called by each derived class in least to most derived order - void BaseBlock::init(BlockDescriptor& descriptor, BlockDescriptor& base_descriptor, size_t block_size) - { - descriptor.mCurrentBlockPtr = this; - descriptor.mMaxParamOffset = block_size; - - switch(descriptor.mInitializationState) - { - case BlockDescriptor::UNINITIALIZED: - // copy params from base class here - descriptor.aggregateBlockData(base_descriptor); - - descriptor.mInitializationState = BlockDescriptor::INITIALIZING; - break; - case BlockDescriptor::INITIALIZING: - descriptor.mInitializationState = BlockDescriptor::INITIALIZED; - break; - case BlockDescriptor::INITIALIZED: - // nothing to do - break; - } - } - - param_handle_t BaseBlock::getHandleFromParam(const Param* param) const - { - const U8* param_address = reinterpret_cast<const U8*>(param); - const U8* baseblock_address = reinterpret_cast<const U8*>(this); - return (param_address - baseblock_address); - } - - bool BaseBlock::submitValue(Parser::name_stack_t& name_stack, Parser& p, bool silent) - { - Parser::name_stack_range_t range = std::make_pair(name_stack.begin(), name_stack.end()); - if (!deserializeBlock(p, range, true)) - { - if (!silent) - { - p.parserWarning(llformat("Failed to parse parameter \"%s\"", p.getCurrentElementName().c_str())); - } - return false; - } - return true; - } - - - bool BaseBlock::validateBlock(bool emit_errors) const - { - // only validate block when it hasn't already passed validation with current data - if (!mValidated) - { - const BlockDescriptor& block_data = mostDerivedBlockDescriptor(); - for (const BlockDescriptor::param_validation_list_t::value_type& pair : block_data.mValidationList) - { - const Param* param = getParamFromHandle(pair.first); - if (!pair.second(param)) - { - if (emit_errors) - { - LL_WARNS() << "Invalid param \"" << getParamName(block_data, param) << "\"" << LL_ENDL; - } - return false; - } - } - mValidated = true; - } - return mValidated; - } - - bool BaseBlock::serializeBlock(Parser& parser, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const LLInitParam::BaseBlock* diff_block) const - { - bool serialized = false; - if (!predicate_rule.check(ll_make_predicate(PROVIDED, isProvided()))) - { - return false; - } - // named param is one like LLView::Params::follows - // unnamed param is like LLView::Params::rect - implicit - const BlockDescriptor& block_data = mostDerivedBlockDescriptor(); - - for (const ParamDescriptorPtr& ptr : block_data.mUnnamedParams) - { - param_handle_t param_handle = ptr->mParamHandle; - const Param* param = getParamFromHandle(param_handle); - ParamDescriptor::serialize_func_t serialize_func = ptr->mSerializeFunc; - if (serialize_func && predicate_rule.check(ll_make_predicate(PROVIDED, param->anyProvided()))) - { - const Param* diff_param = diff_block ? diff_block->getParamFromHandle(param_handle) : NULL; - serialized |= serialize_func(*param, parser, name_stack, predicate_rule, diff_param); - } - } - - for (const BlockDescriptor::param_map_t::value_type& pair : block_data.mNamedParams) - { - param_handle_t param_handle = pair.second->mParamHandle; - const Param* param = getParamFromHandle(param_handle); - ParamDescriptor::serialize_func_t serialize_func = pair.second->mSerializeFunc; - if (serialize_func && predicate_rule.check(ll_make_predicate(PROVIDED, param->anyProvided()))) - { - // Ensure this param has not already been serialized - // Prevents <rect> from being serialized as its own tag. - bool duplicate = false; - for (const ParamDescriptorPtr& ptr : block_data.mUnnamedParams) - { - if (param_handle == ptr->mParamHandle) - { - duplicate = true; - break; - } - } - - //FIXME: for now, don't attempt to serialize values under synonyms, as current parsers - // don't know how to detect them - if (duplicate) - { - continue; - } - - name_stack.push_back(std::make_pair(pair.first, !duplicate)); - const Param* diff_param = diff_block ? diff_block->getParamFromHandle(param_handle) : NULL; - serialized |= serialize_func(*param, parser, name_stack, predicate_rule, diff_param); - name_stack.pop_back(); - } - } - - if (!serialized && predicate_rule.check(ll_make_predicate(EMPTY))) - { - serialized |= parser.writeValue(Flag(), name_stack); - } - // was anything serialized in this block? - return serialized; - } - - bool BaseBlock::inspectBlock(Parser& parser, Parser::name_stack_t name_stack, S32 min_count, S32 max_count) const - { - // named param is one like LLView::Params::follows - // unnamed param is like LLView::Params::rect - implicit - const BlockDescriptor& block_data = mostDerivedBlockDescriptor(); - - for (const ParamDescriptorPtr& ptr : block_data.mUnnamedParams) - { - param_handle_t param_handle = ptr->mParamHandle; - const Param* param = getParamFromHandle(param_handle); - ParamDescriptor::inspect_func_t inspect_func = ptr->mInspectFunc; - if (inspect_func) - { - name_stack.push_back(std::make_pair("", true)); - inspect_func(*param, parser, name_stack, ptr->mMinCount, ptr->mMaxCount); - name_stack.pop_back(); - } - } - - for(const BlockDescriptor::param_map_t::value_type& pair : block_data.mNamedParams) - { - param_handle_t param_handle = pair.second->mParamHandle; - const Param* param = getParamFromHandle(param_handle); - ParamDescriptor::inspect_func_t inspect_func = pair.second->mInspectFunc; - if (inspect_func) - { - // Ensure this param has not already been inspected - bool duplicate = false; + predicate_rule_t default_parse_rules() + { + return ll_make_predicate(PROVIDED) && !ll_make_predicate(EMPTY); + } + + // + // Param + // + Param::Param(BaseBlock* enclosing_block) + : mIsProvided(false) + { + const U8* my_addr = reinterpret_cast<const U8*>(this); + const U8* block_addr = reinterpret_cast<const U8*>(enclosing_block); + U32 enclosing_block_offset = 0x7FFFffff & (U32)(my_addr - block_addr); + mEnclosingBlockOffsetLow = enclosing_block_offset & 0x0000ffff; + mEnclosingBlockOffsetHigh = (enclosing_block_offset & 0x007f0000) >> 16; + } + + // + // ParamDescriptor + // + ParamDescriptor::ParamDescriptor(param_handle_t p, + merge_func_t merge_func, + deserialize_func_t deserialize_func, + serialize_func_t serialize_func, + validation_func_t validation_func, + inspect_func_t inspect_func, + S32 min_count, + S32 max_count) + : mParamHandle(p), + mMergeFunc(merge_func), + mDeserializeFunc(deserialize_func), + mSerializeFunc(serialize_func), + mValidationFunc(validation_func), + mInspectFunc(inspect_func), + mMinCount(min_count), + mMaxCount(max_count), + mUserData(NULL) + {} + + ParamDescriptor::ParamDescriptor() + : mParamHandle(0), + mMergeFunc(NULL), + mDeserializeFunc(NULL), + mSerializeFunc(NULL), + mValidationFunc(NULL), + mInspectFunc(NULL), + mMinCount(0), + mMaxCount(0), + mUserData(NULL) + {} + + ParamDescriptor::~ParamDescriptor() + { + delete mUserData; + } + + // + // Parser + // + Parser::~Parser() + {} + + void Parser::parserWarning(const std::string& message) + { + if (mParseSilently) return; + LL_WARNS() << message << LL_ENDL; + } + + void Parser::parserError(const std::string& message) + { + if (mParseSilently) return; + LL_ERRS() << message << LL_ENDL; + } + + + // + // BlockDescriptor + // + void BlockDescriptor::aggregateBlockData(BlockDescriptor& src_block_data) + { + mNamedParams.insert(src_block_data.mNamedParams.begin(), src_block_data.mNamedParams.end()); + std::copy(src_block_data.mUnnamedParams.begin(), src_block_data.mUnnamedParams.end(), std::back_inserter(mUnnamedParams)); + std::copy(src_block_data.mValidationList.begin(), src_block_data.mValidationList.end(), std::back_inserter(mValidationList)); + std::copy(src_block_data.mAllParams.begin(), src_block_data.mAllParams.end(), std::back_inserter(mAllParams)); + } + + void BlockDescriptor::addParam(const ParamDescriptorPtr in_param, const char* char_name) + { + // create a copy of the param descriptor in mAllParams + // so other data structures can store a pointer to it + mAllParams.push_back(in_param); + ParamDescriptorPtr param(mAllParams.back()); + + std::string name(char_name); + if ((size_t)param->mParamHandle > mMaxParamOffset) + { + LL_ERRS() << "Attempted to register param with block defined for parent class, make sure to derive from LLInitParam::Block<YOUR_CLASS, PARAM_BLOCK_BASE_CLASS>" << LL_ENDL; + } + + if (name.empty()) + { + mUnnamedParams.push_back(param); + } + else + { + // don't use insert, since we want to overwrite existing entries + mNamedParams[name] = param; + } + + if (param->mValidationFunc) + { + mValidationList.push_back(std::make_pair(param->mParamHandle, param->mValidationFunc)); + } + } + + BlockDescriptor::BlockDescriptor() + : mMaxParamOffset(0), + mInitializationState(UNINITIALIZED), + mCurrentBlockPtr(NULL) + {} + + // called by each derived class in least to most derived order + void BaseBlock::init(BlockDescriptor& descriptor, BlockDescriptor& base_descriptor, size_t block_size) + { + descriptor.mCurrentBlockPtr = this; + descriptor.mMaxParamOffset = block_size; + + switch(descriptor.mInitializationState) + { + case BlockDescriptor::UNINITIALIZED: + // copy params from base class here + descriptor.aggregateBlockData(base_descriptor); + + descriptor.mInitializationState = BlockDescriptor::INITIALIZING; + break; + case BlockDescriptor::INITIALIZING: + descriptor.mInitializationState = BlockDescriptor::INITIALIZED; + break; + case BlockDescriptor::INITIALIZED: + // nothing to do + break; + } + } + + param_handle_t BaseBlock::getHandleFromParam(const Param* param) const + { + const U8* param_address = reinterpret_cast<const U8*>(param); + const U8* baseblock_address = reinterpret_cast<const U8*>(this); + return (param_address - baseblock_address); + } + + bool BaseBlock::submitValue(Parser::name_stack_t& name_stack, Parser& p, bool silent) + { + Parser::name_stack_range_t range = std::make_pair(name_stack.begin(), name_stack.end()); + if (!deserializeBlock(p, range, true)) + { + if (!silent) + { + p.parserWarning(llformat("Failed to parse parameter \"%s\"", p.getCurrentElementName().c_str())); + } + return false; + } + return true; + } + + + bool BaseBlock::validateBlock(bool emit_errors) const + { + // only validate block when it hasn't already passed validation with current data + if (!mValidated) + { + const BlockDescriptor& block_data = mostDerivedBlockDescriptor(); + for (const BlockDescriptor::param_validation_list_t::value_type& pair : block_data.mValidationList) + { + const Param* param = getParamFromHandle(pair.first); + if (!pair.second(param)) + { + if (emit_errors) + { + LL_WARNS() << "Invalid param \"" << getParamName(block_data, param) << "\"" << LL_ENDL; + } + return false; + } + } + mValidated = true; + } + return mValidated; + } + + bool BaseBlock::serializeBlock(Parser& parser, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const LLInitParam::BaseBlock* diff_block) const + { + bool serialized = false; + if (!predicate_rule.check(ll_make_predicate(PROVIDED, isProvided()))) + { + return false; + } + // named param is one like LLView::Params::follows + // unnamed param is like LLView::Params::rect - implicit + const BlockDescriptor& block_data = mostDerivedBlockDescriptor(); + + for (const ParamDescriptorPtr& ptr : block_data.mUnnamedParams) + { + param_handle_t param_handle = ptr->mParamHandle; + const Param* param = getParamFromHandle(param_handle); + ParamDescriptor::serialize_func_t serialize_func = ptr->mSerializeFunc; + if (serialize_func && predicate_rule.check(ll_make_predicate(PROVIDED, param->anyProvided()))) + { + const Param* diff_param = diff_block ? diff_block->getParamFromHandle(param_handle) : NULL; + serialized |= serialize_func(*param, parser, name_stack, predicate_rule, diff_param); + } + } + + for (const BlockDescriptor::param_map_t::value_type& pair : block_data.mNamedParams) + { + param_handle_t param_handle = pair.second->mParamHandle; + const Param* param = getParamFromHandle(param_handle); + ParamDescriptor::serialize_func_t serialize_func = pair.second->mSerializeFunc; + if (serialize_func && predicate_rule.check(ll_make_predicate(PROVIDED, param->anyProvided()))) + { + // Ensure this param has not already been serialized + // Prevents <rect> from being serialized as its own tag. + bool duplicate = false; + for (const ParamDescriptorPtr& ptr : block_data.mUnnamedParams) + { + if (param_handle == ptr->mParamHandle) + { + duplicate = true; + break; + } + } + + //FIXME: for now, don't attempt to serialize values under synonyms, as current parsers + // don't know how to detect them + if (duplicate) + { + continue; + } + + name_stack.push_back(std::make_pair(pair.first, !duplicate)); + const Param* diff_param = diff_block ? diff_block->getParamFromHandle(param_handle) : NULL; + serialized |= serialize_func(*param, parser, name_stack, predicate_rule, diff_param); + name_stack.pop_back(); + } + } + + if (!serialized && predicate_rule.check(ll_make_predicate(EMPTY))) + { + serialized |= parser.writeValue(Flag(), name_stack); + } + // was anything serialized in this block? + return serialized; + } + + bool BaseBlock::inspectBlock(Parser& parser, Parser::name_stack_t name_stack, S32 min_count, S32 max_count) const + { + // named param is one like LLView::Params::follows + // unnamed param is like LLView::Params::rect - implicit + const BlockDescriptor& block_data = mostDerivedBlockDescriptor(); + + for (const ParamDescriptorPtr& ptr : block_data.mUnnamedParams) + { + param_handle_t param_handle = ptr->mParamHandle; + const Param* param = getParamFromHandle(param_handle); + ParamDescriptor::inspect_func_t inspect_func = ptr->mInspectFunc; + if (inspect_func) + { + name_stack.push_back(std::make_pair("", true)); + inspect_func(*param, parser, name_stack, ptr->mMinCount, ptr->mMaxCount); + name_stack.pop_back(); + } + } + + for(const BlockDescriptor::param_map_t::value_type& pair : block_data.mNamedParams) + { + param_handle_t param_handle = pair.second->mParamHandle; + const Param* param = getParamFromHandle(param_handle); + ParamDescriptor::inspect_func_t inspect_func = pair.second->mInspectFunc; + if (inspect_func) + { + // Ensure this param has not already been inspected + bool duplicate = false; for (const ParamDescriptorPtr &ptr : block_data.mUnnamedParams) - { - if (param_handle == ptr->mParamHandle) - { - duplicate = true; - break; - } - } - - name_stack.push_back(std::make_pair(pair.first, !duplicate)); - inspect_func(*param, parser, name_stack, pair.second->mMinCount, pair.second->mMaxCount); - name_stack.pop_back(); - } - } - - return true; - } - - bool BaseBlock::deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack_range, bool ignored) - { - BlockDescriptor& block_data = mostDerivedBlockDescriptor(); - bool names_left = name_stack_range.first != name_stack_range.second; - - bool new_name = names_left - ? name_stack_range.first->second - : true; - - if (names_left) - { - const std::string& top_name = name_stack_range.first->first; - - BlockDescriptor::param_map_t::iterator found_it = block_data.mNamedParams.find(top_name); - if (found_it != block_data.mNamedParams.end()) - { - // find pointer to member parameter from offset table - Param* paramp = getParamFromHandle(found_it->second->mParamHandle); - ParamDescriptor::deserialize_func_t deserialize_func = found_it->second->mDeserializeFunc; - - Parser::name_stack_range_t new_name_stack(name_stack_range.first, name_stack_range.second); - ++new_name_stack.first; - if (deserialize_func(*paramp, p, new_name_stack, new_name)) - { - // value is no longer new, we know about it now - name_stack_range.first->second = false; - return true; - } - else - { - return false; - } - } - } - - // try to parse unnamed parameters, in declaration order - for (ParamDescriptorPtr& ptr : block_data.mUnnamedParams) - { - Param* paramp = getParamFromHandle(ptr->mParamHandle); - ParamDescriptor::deserialize_func_t deserialize_func = ptr->mDeserializeFunc; - - if (deserialize_func && deserialize_func(*paramp, p, name_stack_range, new_name)) - { - return true; - } - } - - // if no match, and no names left on stack, this is just an existence assertion of this block - // verify by calling readValue with NoParamValue type, an inherently unparseable type - if (!names_left) - { - Flag no_value; - return p.readValue(no_value); - } - - return false; - } - - void BaseBlock::addSynonym(Param& param, const std::string& synonym) - { - BlockDescriptor& block_data = mostDerivedBlockDescriptor(); - if (block_data.mInitializationState == BlockDescriptor::INITIALIZING) - { - param_handle_t handle = getHandleFromParam(¶m); - - // check for invalid derivation from a paramblock (i.e. without using - // Block<T, Base_Class> - if ((size_t)handle > block_data.mMaxParamOffset) - { - LL_ERRS() << "Attempted to register param with block defined for parent class, make sure to derive from LLInitParam::Block<YOUR_CLASS, PARAM_BLOCK_BASE_CLASS>" << LL_ENDL; - } - - ParamDescriptorPtr param_descriptor = findParamDescriptor(param); - if (param_descriptor) - { - if (synonym.empty()) - { - block_data.mUnnamedParams.push_back(param_descriptor); - } - else - { - block_data.mNamedParams[synonym] = param_descriptor; - } - } - } - } - - const std::string& BaseBlock::getParamName(const BlockDescriptor& block_data, const Param* paramp) const - { - param_handle_t handle = getHandleFromParam(paramp); - for (BlockDescriptor::param_map_t::const_iterator it = block_data.mNamedParams.begin(); it != block_data.mNamedParams.end(); ++it) - { - if (it->second->mParamHandle == handle) - { - return it->first; - } - } - - return LLStringUtil::null; - } - - ParamDescriptorPtr BaseBlock::findParamDescriptor(const Param& param) - { - param_handle_t handle = getHandleFromParam(¶m); - BlockDescriptor& descriptor = mostDerivedBlockDescriptor(); - for (ParamDescriptorPtr& ptr : descriptor.mAllParams) - { - if (ptr->mParamHandle == handle) return ptr; - } - return ParamDescriptorPtr(); - } - - // take all provided params from other and apply to self - // NOTE: this requires that "other" is of the same derived type as this - bool BaseBlock::mergeBlock(BlockDescriptor& block_data, const BaseBlock& other, bool overwrite) - { - bool some_param_changed = false; - for (const ParamDescriptorPtr& ptr : block_data.mAllParams) - { - const Param* other_paramp = other.getParamFromHandle(ptr->mParamHandle); - ParamDescriptor::merge_func_t merge_func = ptr->mMergeFunc; - if (merge_func) - { - Param* paramp = getParamFromHandle(ptr->mParamHandle); - llassert(paramp->getEnclosingBlockOffset() == ptr->mParamHandle); - some_param_changed |= merge_func(*paramp, *other_paramp, overwrite); - } - } - return some_param_changed; - } + { + if (param_handle == ptr->mParamHandle) + { + duplicate = true; + break; + } + } + + name_stack.push_back(std::make_pair(pair.first, !duplicate)); + inspect_func(*param, parser, name_stack, pair.second->mMinCount, pair.second->mMaxCount); + name_stack.pop_back(); + } + } + + return true; + } + + bool BaseBlock::deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack_range, bool ignored) + { + BlockDescriptor& block_data = mostDerivedBlockDescriptor(); + bool names_left = name_stack_range.first != name_stack_range.second; + + bool new_name = names_left + ? name_stack_range.first->second + : true; + + if (names_left) + { + const std::string& top_name = name_stack_range.first->first; + + BlockDescriptor::param_map_t::iterator found_it = block_data.mNamedParams.find(top_name); + if (found_it != block_data.mNamedParams.end()) + { + // find pointer to member parameter from offset table + Param* paramp = getParamFromHandle(found_it->second->mParamHandle); + ParamDescriptor::deserialize_func_t deserialize_func = found_it->second->mDeserializeFunc; + + Parser::name_stack_range_t new_name_stack(name_stack_range.first, name_stack_range.second); + ++new_name_stack.first; + if (deserialize_func(*paramp, p, new_name_stack, new_name)) + { + // value is no longer new, we know about it now + name_stack_range.first->second = false; + return true; + } + else + { + return false; + } + } + } + + // try to parse unnamed parameters, in declaration order + for (ParamDescriptorPtr& ptr : block_data.mUnnamedParams) + { + Param* paramp = getParamFromHandle(ptr->mParamHandle); + ParamDescriptor::deserialize_func_t deserialize_func = ptr->mDeserializeFunc; + + if (deserialize_func && deserialize_func(*paramp, p, name_stack_range, new_name)) + { + return true; + } + } + + // if no match, and no names left on stack, this is just an existence assertion of this block + // verify by calling readValue with NoParamValue type, an inherently unparseable type + if (!names_left) + { + Flag no_value; + return p.readValue(no_value); + } + + return false; + } + + void BaseBlock::addSynonym(Param& param, const std::string& synonym) + { + BlockDescriptor& block_data = mostDerivedBlockDescriptor(); + if (block_data.mInitializationState == BlockDescriptor::INITIALIZING) + { + param_handle_t handle = getHandleFromParam(¶m); + + // check for invalid derivation from a paramblock (i.e. without using + // Block<T, Base_Class> + if ((size_t)handle > block_data.mMaxParamOffset) + { + LL_ERRS() << "Attempted to register param with block defined for parent class, make sure to derive from LLInitParam::Block<YOUR_CLASS, PARAM_BLOCK_BASE_CLASS>" << LL_ENDL; + } + + ParamDescriptorPtr param_descriptor = findParamDescriptor(param); + if (param_descriptor) + { + if (synonym.empty()) + { + block_data.mUnnamedParams.push_back(param_descriptor); + } + else + { + block_data.mNamedParams[synonym] = param_descriptor; + } + } + } + } + + const std::string& BaseBlock::getParamName(const BlockDescriptor& block_data, const Param* paramp) const + { + param_handle_t handle = getHandleFromParam(paramp); + for (BlockDescriptor::param_map_t::const_iterator it = block_data.mNamedParams.begin(); it != block_data.mNamedParams.end(); ++it) + { + if (it->second->mParamHandle == handle) + { + return it->first; + } + } + + return LLStringUtil::null; + } + + ParamDescriptorPtr BaseBlock::findParamDescriptor(const Param& param) + { + param_handle_t handle = getHandleFromParam(¶m); + BlockDescriptor& descriptor = mostDerivedBlockDescriptor(); + for (ParamDescriptorPtr& ptr : descriptor.mAllParams) + { + if (ptr->mParamHandle == handle) return ptr; + } + return ParamDescriptorPtr(); + } + + // take all provided params from other and apply to self + // NOTE: this requires that "other" is of the same derived type as this + bool BaseBlock::mergeBlock(BlockDescriptor& block_data, const BaseBlock& other, bool overwrite) + { + bool some_param_changed = false; + for (const ParamDescriptorPtr& ptr : block_data.mAllParams) + { + const Param* other_paramp = other.getParamFromHandle(ptr->mParamHandle); + ParamDescriptor::merge_func_t merge_func = ptr->mMergeFunc; + if (merge_func) + { + Param* paramp = getParamFromHandle(ptr->mParamHandle); + llassert(paramp->getEnclosingBlockOffset() == ptr->mParamHandle); + some_param_changed |= merge_func(*paramp, *other_paramp, overwrite); + } + } + return some_param_changed; + } } diff --git a/indra/llcommon/llinitparam.h b/indra/llcommon/llinitparam.h index e0d0ab9ac7..206aa51ba3 100644 --- a/indra/llcommon/llinitparam.h +++ b/indra/llcommon/llinitparam.h @@ -1,26 +1,26 @@ -/** +/** * @file llinitparam.h - * @brief parameter block abstraction for creating complex objects and + * @brief parameter block abstraction for creating complex objects and * parsing construction parameters from xml and LLSD * * $LicenseInfo:firstyear=2008&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -43,2796 +43,2796 @@ namespace LLTypeTags { - template <typename INNER_TYPE, int _SORT_ORDER> - struct TypeTagBase - { - typedef void is_tag_t; - typedef INNER_TYPE inner_t; - static const int SORT_ORDER=_SORT_ORDER; - }; - - template <int VAL1, int VAL2> - struct GreaterThan - { - static const bool value = VAL1 > VAL2; - }; - - template<typename ITEM, typename REST, bool NEEDS_SWAP = GreaterThan<ITEM::SORT_ORDER, REST::SORT_ORDER>::value > - struct Swap - { - typedef typename ITEM::template Cons<REST>::value_t value_t; - }; - - template<typename ITEM, typename REST> - struct Swap<ITEM, REST, true> - { - typedef typename REST::template Cons<Swap<ITEM, typename REST::inner_t>::value_t>::value_t value_t; - }; - - template<typename T, typename SORTABLE = void> - struct IsSortable - { - static const bool value = false; - }; - - template<typename T> - struct IsSortable<T, typename T::is_tag_t> - { - static const bool value = true; - }; - - template<typename ITEM, typename REST, bool IS_REST_SORTABLE = IsSortable<REST>::value> - struct InsertInto - { - typedef typename ITEM::template Cons<REST>::value_t value_t; - }; - - template<typename ITEM, typename REST> - struct InsertInto <ITEM, REST, true> - { - typedef typename Swap<ITEM, REST>::value_t value_t; - }; - - template<typename T, bool SORTABLE = IsSortable<T>::value> - struct Sorted - { - typedef T value_t; - }; - - template<typename T> - struct Sorted <T, true> - { - typedef typename InsertInto<T, typename Sorted<typename T::inner_t>::value_t>::value_t value_t; - }; + template <typename INNER_TYPE, int _SORT_ORDER> + struct TypeTagBase + { + typedef void is_tag_t; + typedef INNER_TYPE inner_t; + static const int SORT_ORDER=_SORT_ORDER; + }; + + template <int VAL1, int VAL2> + struct GreaterThan + { + static const bool value = VAL1 > VAL2; + }; + + template<typename ITEM, typename REST, bool NEEDS_SWAP = GreaterThan<ITEM::SORT_ORDER, REST::SORT_ORDER>::value > + struct Swap + { + typedef typename ITEM::template Cons<REST>::value_t value_t; + }; + + template<typename ITEM, typename REST> + struct Swap<ITEM, REST, true> + { + typedef typename REST::template Cons<Swap<ITEM, typename REST::inner_t>::value_t>::value_t value_t; + }; + + template<typename T, typename SORTABLE = void> + struct IsSortable + { + static const bool value = false; + }; + + template<typename T> + struct IsSortable<T, typename T::is_tag_t> + { + static const bool value = true; + }; + + template<typename ITEM, typename REST, bool IS_REST_SORTABLE = IsSortable<REST>::value> + struct InsertInto + { + typedef typename ITEM::template Cons<REST>::value_t value_t; + }; + + template<typename ITEM, typename REST> + struct InsertInto <ITEM, REST, true> + { + typedef typename Swap<ITEM, REST>::value_t value_t; + }; + + template<typename T, bool SORTABLE = IsSortable<T>::value> + struct Sorted + { + typedef T value_t; + }; + + template<typename T> + struct Sorted <T, true> + { + typedef typename InsertInto<T, typename Sorted<typename T::inner_t>::value_t>::value_t value_t; + }; } namespace LLInitParam { - // used to indicate no matching value to a given name when parsing - struct Flag{}; - - template<typename T> const T& defaultValue() { static T value; return value; } - - // wraps comparison operator between any 2 values of the same type - // specialize to handle cases where equality isn't defined well, or at all - template <typename T, bool IS_BOOST_FUNCTION = boost::is_convertible<T, boost::function_base>::value > - struct ParamCompare - { - static bool equals(const T &a, const T &b) - { - return a == b; - } + // used to indicate no matching value to a given name when parsing + struct Flag{}; + + template<typename T> const T& defaultValue() { static T value; return value; } + + // wraps comparison operator between any 2 values of the same type + // specialize to handle cases where equality isn't defined well, or at all + template <typename T, bool IS_BOOST_FUNCTION = boost::is_convertible<T, boost::function_base>::value > + struct ParamCompare + { + static bool equals(const T &a, const T &b) + { + return a == b; + } + }; + + // boost function types are not comparable + template<typename T> + struct ParamCompare<T, true> + { + static bool equals(const T&a, const T &b) + { + return false; + } + }; + + template<> + struct ParamCompare<LLSD, false> + { + static bool equals(const LLSD &a, const LLSD &b) { return false; } + }; + + template<> + struct ParamCompare<Flag, false> + { + static bool equals(const Flag& a, const Flag& b) { return false; } + }; + + + // helper functions and classes + typedef ptrdiff_t param_handle_t; + struct IS_A_BLOCK {}; + struct NOT_BLOCK {}; + + // these templates allow us to distinguish between template parameters + // that derive from BaseBlock and those that don't + template<typename T, typename BLOCK_IDENTIFIER = void> + struct IsBlock + { + typedef NOT_BLOCK value_t; + }; + + template<typename T> + struct IsBlock<T, typename T::baseblock_base_class_t> + { + typedef IS_A_BLOCK value_t; + }; + + // ParamValue class directly manages the wrapped value + // by holding on to a copy (scalar params) + // or deriving from it (blocks) + // has specializations for custom value behavior + // and "tag" values like Lazy and Atomic + template<typename T, typename VALUE_IS_BLOCK = typename IsBlock<T>::value_t> + class ParamValue + { + typedef ParamValue<T, VALUE_IS_BLOCK> self_t; + + public: + typedef T default_value_t; + typedef T value_t; + + ParamValue(): mValue() {} + ParamValue(const default_value_t& other) : mValue(other) {} + + void setValue(const value_t& val) + { + mValue = val; + } + + const value_t& getValue() const + { + return mValue; + } + + T& getValue() + { + return mValue; + } + + bool isValid() const { return true; } + + protected: + T mValue; + }; + + template<typename T> + class ParamValue<T, IS_A_BLOCK> + : public T + { + typedef ParamValue<T, IS_A_BLOCK> self_t; + public: + typedef T default_value_t; + typedef T value_t; + + ParamValue() + : T() + {} + + ParamValue(const default_value_t& other) + : T(other) + {} + + void setValue(const value_t& val) + { + *this = val; + } + + const value_t& getValue() const + { + return *this; + } + + T& getValue() + { + return *this; + } }; - // boost function types are not comparable - template<typename T> - struct ParamCompare<T, true> - { - static bool equals(const T&a, const T &b) - { - return false; - } - }; - - template<> - struct ParamCompare<LLSD, false> - { - static bool equals(const LLSD &a, const LLSD &b) { return false; } - }; - - template<> - struct ParamCompare<Flag, false> - { - static bool equals(const Flag& a, const Flag& b) { return false; } - }; - - - // helper functions and classes - typedef ptrdiff_t param_handle_t; - struct IS_A_BLOCK {}; - struct NOT_BLOCK {}; - - // these templates allow us to distinguish between template parameters - // that derive from BaseBlock and those that don't - template<typename T, typename BLOCK_IDENTIFIER = void> - struct IsBlock - { - typedef NOT_BLOCK value_t; - }; - - template<typename T> - struct IsBlock<T, typename T::baseblock_base_class_t> - { - typedef IS_A_BLOCK value_t; - }; - - // ParamValue class directly manages the wrapped value - // by holding on to a copy (scalar params) - // or deriving from it (blocks) - // has specializations for custom value behavior - // and "tag" values like Lazy and Atomic - template<typename T, typename VALUE_IS_BLOCK = typename IsBlock<T>::value_t> - class ParamValue - { - typedef ParamValue<T, VALUE_IS_BLOCK> self_t; - - public: - typedef T default_value_t; - typedef T value_t; - - ParamValue(): mValue() {} - ParamValue(const default_value_t& other) : mValue(other) {} - - void setValue(const value_t& val) - { - mValue = val; - } - - const value_t& getValue() const - { - return mValue; - } - - T& getValue() - { - return mValue; - } - - bool isValid() const { return true; } - - protected: - T mValue; - }; - - template<typename T> - class ParamValue<T, IS_A_BLOCK> - : public T - { - typedef ParamValue<T, IS_A_BLOCK> self_t; - public: - typedef T default_value_t; - typedef T value_t; - - ParamValue() - : T() - {} - - ParamValue(const default_value_t& other) - : T(other) - {} - - void setValue(const value_t& val) - { - *this = val; - } - - const value_t& getValue() const - { - return *this; - } - - T& getValue() - { - return *this; - } - }; - - - // empty default implementation of key cache - // leverages empty base class optimization - template <typename T> - class TypeValues - : public ParamValue<typename LLTypeTags::Sorted<T>::value_t> - { - private: - struct Inaccessable{}; - public: - typedef std::map<std::string, T> value_name_map_t; - typedef Inaccessable name_t; - typedef TypeValues<T> type_value_t; - typedef ParamValue<typename LLTypeTags::Sorted<T>::value_t> param_value_t; - typedef typename param_value_t::value_t value_t; - - TypeValues(const typename param_value_t::value_t& val) - : param_value_t(val) - {} - - void setValueName(const std::string& key) {} - std::string getValueName() const { return ""; } - std::string calcValueName(const value_t& value) const { return ""; } - void clearValueName() const {} - - static bool getValueFromName(const std::string& name, value_t& value) - { - return false; - } - - static bool valueNamesExist() - { - return false; - } - - static std::vector<std::string>* getPossibleValues() - { - return NULL; - } - - void assignNamedValue(const Inaccessable& name) - {} - - operator const value_t&() const - { - return param_value_t::getValue(); - } - - const value_t& operator()() const - { - return param_value_t::getValue(); - } - - static value_name_map_t* getValueNames() {return NULL;} - }; - - // helper class to implement name value lookups - // and caching of last used name - template <typename T, typename DERIVED_TYPE = TypeValues<T>, bool IS_SPECIALIZED = true > - class TypeValuesHelper - : public ParamValue<typename LLTypeTags::Sorted<T>::value_t> - { - typedef TypeValuesHelper<T, DERIVED_TYPE, IS_SPECIALIZED> self_t; - public: - typedef typename std::map<std::string, T> value_name_map_t; - typedef std::string name_t; - typedef self_t type_value_t; - typedef ParamValue<typename LLTypeTags::Sorted<T>::value_t> param_value_t; - typedef typename param_value_t::value_t value_t; - - TypeValuesHelper(const typename param_value_t::value_t& val) - : param_value_t(val) - {} - - //TODO: cache key by index to save on param block size - void setValueName(const std::string& value_name) - { - mValueName = value_name; - } - - std::string getValueName() const - { - return mValueName; - } - - std::string calcValueName(const value_t& value) const - { - value_name_map_t* map = getValueNames(); - for (typename value_name_map_t::value_type& map_pair : *map) - { - if (ParamCompare<T>::equals(map_pair.second, value)) - { - return map_pair.first; - } - } - - return ""; - } - - void clearValueName() const - { - mValueName.clear(); - } - - static bool getValueFromName(const std::string& name, value_t& value) - { - value_name_map_t* map = getValueNames(); - typename value_name_map_t::iterator found_it = map->find(name); - if (found_it == map->end()) return false; - - value = found_it->second; - return true; - } - - static bool valueNamesExist() - { - return !getValueNames()->empty(); - } - - static value_name_map_t* getValueNames() - { - static value_name_map_t sMap; - static bool sInitialized = false; - - if (!sInitialized) - { - sInitialized = true; - DERIVED_TYPE::declareValues(); - } - return &sMap; - } - - static std::vector<std::string>* getPossibleValues() - { - static std::vector<std::string> sValues; - - value_name_map_t* map = getValueNames(); - for (typename value_name_map_t::value_type& map_pair : *map) - { - sValues.push_back(map_pair.first); - } - return &sValues; - } - - static void declare(const std::string& name, const value_t& value) - { - (*getValueNames())[name] = value; - } - - void operator ()(const std::string& name) - { - *this = name; - } - - void assignNamedValue(const std::string& name) - { - if (getValueFromName(name, param_value_t::getValue())) - { - setValueName(name); - } - } - - operator const value_t&() const - { - return param_value_t::getValue(); - } - - const value_t& operator()() const - { - return param_value_t::getValue(); - } - - protected: - static void getName(const std::string& name, const value_t& value) - {} - - mutable std::string mValueName; - }; - - // string types can support custom named values, but need - // to disambiguate in code between a string that is a named value - // and a string that is a name - template <typename DERIVED_TYPE> - class TypeValuesHelper<std::string, DERIVED_TYPE, true> - : public TypeValuesHelper<std::string, DERIVED_TYPE, false> - { - public: - typedef TypeValuesHelper<std::string, DERIVED_TYPE, true> self_t; - typedef TypeValuesHelper<std::string, DERIVED_TYPE, false> base_t; - typedef std::string value_t; - typedef std::string name_t; - typedef self_t type_value_t; - - TypeValuesHelper(const std::string& val) - : base_t(val) - {} - - void operator ()(const std::string& name) - { - *this = name; - } - - self_t& operator =(const std::string& name) - { - if (base_t::getValueFromName(name, ParamValue<std::string>::getValue())) - { - base_t::setValueName(name); - } - else - { - ParamValue<std::string>::setValue(name); - } - return *this; - } - - operator const value_t&() const - { - return ParamValue<std::string>::getValue(); - } - - const value_t& operator()() const - { - return ParamValue<std::string>::getValue(); - } - - }; - - // parser base class with mechanisms for registering readers/writers/inspectors of different types - class LL_COMMON_API Parser - { - LOG_CLASS(Parser); - public: - typedef std::vector<std::pair<std::string, bool> > name_stack_t; - typedef std::pair<name_stack_t::iterator, name_stack_t::iterator> name_stack_range_t; - typedef std::vector<std::string> possible_values_t; - - typedef bool (*parser_read_func_t)(Parser& parser, void* output); - typedef bool (*parser_write_func_t)(Parser& parser, const void*, name_stack_t&); - typedef boost::function<void (name_stack_t&, S32, S32, const possible_values_t*)> parser_inspect_func_t; - - typedef std::map<const std::type_info*, parser_read_func_t> parser_read_func_map_t; - typedef std::map<const std::type_info*, parser_write_func_t> parser_write_func_map_t; - typedef std::map<const std::type_info*, parser_inspect_func_t> parser_inspect_func_map_t; - - public: - - Parser(parser_read_func_map_t& read_map, parser_write_func_map_t& write_map, parser_inspect_func_map_t& inspect_map) - : mParseSilently(false), - mParserReadFuncs(&read_map), - mParserWriteFuncs(&write_map), - mParserInspectFuncs(&inspect_map) - {} - - virtual ~Parser(); - - template <typename T> bool readValue(T& param, typename boost::disable_if<boost::is_enum<T> >::type* dummy = 0) - { - parser_read_func_map_t::iterator found_it = mParserReadFuncs->find(&typeid(T)); - if (found_it != mParserReadFuncs->end()) - { - return found_it->second(*this, (void*)¶m); - } - - return false; - } - - template <typename T> bool readValue(T& param, typename boost::enable_if<boost::is_enum<T> >::type* dummy = 0) - { - parser_read_func_map_t::iterator found_it = mParserReadFuncs->find(&typeid(T)); - if (found_it != mParserReadFuncs->end()) - { - return found_it->second(*this, (void*)¶m); - } - else - { - found_it = mParserReadFuncs->find(&typeid(S32)); - if (found_it != mParserReadFuncs->end()) - { - S32 int_value; - bool parsed = found_it->second(*this, (void*)&int_value); - param = (T)int_value; - return parsed; - } - } - return false; - } - - template <typename T> bool writeValue(const T& param, name_stack_t& name_stack) - { - parser_write_func_map_t::iterator found_it = mParserWriteFuncs->find(&typeid(T)); - if (found_it != mParserWriteFuncs->end()) - { - return found_it->second(*this, (const void*)¶m, name_stack); - } - return false; - } - - // dispatch inspection to registered inspection functions, for each parameter in a param block - template <typename T> bool inspectValue(name_stack_t& name_stack, S32 min_count, S32 max_count, const possible_values_t* possible_values) - { - parser_inspect_func_map_t::iterator found_it = mParserInspectFuncs->find(&typeid(T)); - if (found_it != mParserInspectFuncs->end()) - { - found_it->second(name_stack, min_count, max_count, possible_values); - return true; - } - return false; - } - - virtual std::string getCurrentElementName() = 0; - virtual std::string getCurrentFileName() = 0; - virtual void parserWarning(const std::string& message); - virtual void parserError(const std::string& message); - void setParseSilently(bool silent) { mParseSilently = silent; } - - protected: - template <typename T> - void registerParserFuncs(parser_read_func_t read_func, parser_write_func_t write_func = NULL) - { - mParserReadFuncs->insert(std::make_pair(&typeid(T), read_func)); - mParserWriteFuncs->insert(std::make_pair(&typeid(T), write_func)); - } - - template <typename T> - void registerInspectFunc(parser_inspect_func_t inspect_func) - { - mParserInspectFuncs->insert(std::make_pair(&typeid(T), inspect_func)); - } - - bool mParseSilently; - - private: - parser_read_func_map_t* mParserReadFuncs; - parser_write_func_map_t* mParserWriteFuncs; - parser_inspect_func_map_t* mParserInspectFuncs; - }; - - class Param; - - enum ESerializePredicates - { - PROVIDED, - REQUIRED, - VALID, - HAS_DEFAULT_VALUE, - EMPTY - }; - - typedef LLPredicate::Rule<ESerializePredicates> predicate_rule_t; - - predicate_rule_t default_parse_rules(); - - // various callbacks and constraints associated with an individual param - struct LL_COMMON_API ParamDescriptor - { - struct UserData - { - virtual ~UserData() {} - }; - - typedef bool(*merge_func_t)(Param&, const Param&, bool); - typedef bool(*deserialize_func_t)(Param&, Parser&, Parser::name_stack_range_t&, bool); - typedef bool(*serialize_func_t)(const Param&, Parser&, Parser::name_stack_t&, const predicate_rule_t rules, const Param* diff_param); - typedef void(*inspect_func_t)(const Param&, Parser&, Parser::name_stack_t&, S32 min_count, S32 max_count); - typedef bool(*validation_func_t)(const Param*); - - ParamDescriptor(param_handle_t p, - merge_func_t merge_func, - deserialize_func_t deserialize_func, - serialize_func_t serialize_func, - validation_func_t validation_func, - inspect_func_t inspect_func, - S32 min_count, - S32 max_count); - - ParamDescriptor(); - ~ParamDescriptor(); - - param_handle_t mParamHandle; - merge_func_t mMergeFunc; - deserialize_func_t mDeserializeFunc; - serialize_func_t mSerializeFunc; - inspect_func_t mInspectFunc; - validation_func_t mValidationFunc; - S32 mMinCount; - S32 mMaxCount; - S32 mNumRefs; - UserData* mUserData; - }; - - typedef std::shared_ptr<ParamDescriptor> ParamDescriptorPtr; - - // each derived Block class keeps a static data structure maintaining offsets to various params - class LL_COMMON_API BlockDescriptor - { - public: - BlockDescriptor(); - - typedef enum e_initialization_state - { - UNINITIALIZED, - INITIALIZING, - INITIALIZED - } EInitializationState; - - void aggregateBlockData(BlockDescriptor& src_block_data); - void addParam(ParamDescriptorPtr param, const char* name); - - typedef boost::unordered_map<const std::string, ParamDescriptorPtr> param_map_t; - typedef std::vector<ParamDescriptorPtr> param_list_t; - typedef std::list<ParamDescriptorPtr> all_params_list_t; - typedef std::vector<std::pair<param_handle_t, ParamDescriptor::validation_func_t> > param_validation_list_t; - - param_map_t mNamedParams; // parameters with associated names - param_list_t mUnnamedParams; // parameters with_out_ associated names - param_validation_list_t mValidationList; // parameters that must be validated - all_params_list_t mAllParams; // all parameters, owns descriptors - size_t mMaxParamOffset; - EInitializationState mInitializationState; // whether or not static block data has been initialized - class BaseBlock* mCurrentBlockPtr; // pointer to block currently being constructed - }; - - //TODO: implement in terms of owned_ptr - template<typename T> - class LazyValue - { - public: - LazyValue() - : mPtr(NULL) - {} - - ~LazyValue() - { - delete mPtr; - } - - LazyValue(const T& value) - { - mPtr = new T(value); - } - - LazyValue(const LazyValue& other) - : mPtr(NULL) - { - *this = other; - } - - LazyValue& operator = (const LazyValue& other) - { - if (!other.mPtr) - { - delete mPtr; - mPtr = NULL; - } - else - { - if (!mPtr) - { - mPtr = new T(*other.mPtr); - } - else - { - *mPtr = *(other.mPtr); - } - } - return *this; - } - - bool operator==(const LazyValue& other) const - { - if (empty() || other.empty()) return false; - return *mPtr == *other.mPtr; - } - - bool empty() const - { - return mPtr == NULL; - } - - void set(const T& other) - { - if (!mPtr) - { - mPtr = new T(other); - } - else - { - *mPtr = other; - } - } - - const T& get() const - { - return *ensureInstance(); - } - - T& get() - { - return *ensureInstance(); - } - - operator const T&() const - { - return get(); - } - - private: - // lazily allocate an instance of T - T* ensureInstance() const - { - if (mPtr == NULL) - { - mPtr = new T(); - } - return mPtr; - } - - private: - - mutable T* mPtr; - }; - - // root class of all parameter blocks - - class LL_COMMON_API BaseBlock - { - public: - // lift block tags into baseblock namespace so derived classes do not need to qualify them - typedef LLInitParam::IS_A_BLOCK IS_A_BLOCK; - typedef LLInitParam::NOT_BLOCK NOT_A_BLOCK; - - template<typename T> - struct Sequential : public LLTypeTags::TypeTagBase<T, 2> - { - template <typename S> struct Cons { typedef Sequential<ParamValue<S> > value_t; }; - template <typename S> struct Cons<Sequential<S> > { typedef Sequential<S> value_t; }; - }; - - template<typename T> - struct Atomic : public LLTypeTags::TypeTagBase<T, 1> - { - template <typename S> struct Cons { typedef Atomic<ParamValue<S> > value_t; }; - template <typename S> struct Cons<Atomic<S> > { typedef Atomic<S> value_t; }; - }; - - template<typename T, typename BLOCK_T = typename IsBlock<T>::value_t > - struct Lazy : public LLTypeTags::TypeTagBase<T, 0> - { - template <typename S> struct Cons - { - typedef Lazy<ParamValue<S, BLOCK_T>, BLOCK_T> value_t; - }; - template <typename S> struct Cons<Lazy<S, IS_A_BLOCK> > - { - typedef Lazy<S, IS_A_BLOCK> value_t; - }; - template <typename S> struct Cons<Lazy<S, NOT_A_BLOCK> > - { - typedef Lazy<S, BLOCK_T> value_t; - }; - }; - - // "Multiple" constraint types, put here in root class to avoid ambiguity during use - struct AnyAmount - { - enum { minCount = 0 }; - enum { maxCount = U32_MAX }; - }; - - template<U32 MIN_AMOUNT> - struct AtLeast - { - enum { minCount = MIN_AMOUNT }; - enum { maxCount = U32_MAX }; - }; - - template<U32 MAX_AMOUNT> - struct AtMost - { - enum { minCount = 0 }; - enum { maxCount = MAX_AMOUNT }; - }; - - template<U32 MIN_AMOUNT, U32 MAX_AMOUNT> - struct Between - { - enum { minCount = MIN_AMOUNT }; - enum { maxCount = MAX_AMOUNT }; - }; - - template<U32 EXACT_COUNT> - struct Exactly - { - enum { minCount = EXACT_COUNT }; - enum { maxCount = EXACT_COUNT }; - }; - - // this typedef identifies derived classes as being blocks - typedef void baseblock_base_class_t; - LOG_CLASS(BaseBlock); - friend class Param; - - BaseBlock() - : mValidated(false), - mParamProvided(false) - {} - - virtual ~BaseBlock() {} - bool submitValue(Parser::name_stack_t& name_stack, Parser& p, bool silent=false); - - param_handle_t getHandleFromParam(const Param* param) const; - bool validateBlock(bool emit_errors = true) const; - - bool isProvided() const - { - return mParamProvided; - } - - bool isValid() const - { - return validateBlock(false); - } - - - Param* getParamFromHandle(const param_handle_t param_handle) - { - if (param_handle == 0) return NULL; - - U8* baseblock_address = reinterpret_cast<U8*>(this); - return reinterpret_cast<Param*>(baseblock_address + param_handle); - } - - const Param* getParamFromHandle(const param_handle_t param_handle) const - { - const U8* baseblock_address = reinterpret_cast<const U8*>(this); - return reinterpret_cast<const Param*>(baseblock_address + param_handle); - } - - void addSynonym(Param& param, const std::string& synonym); - - // Blocks can override this to do custom tracking of changes - virtual void paramChanged(const Param& changed_param, bool user_provided) - { - if (user_provided) - { - // a child param has been explicitly changed - // so *some* aspect of this block is now provided - mValidated = false; - mParamProvided = true; - } - } - - bool deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack_range, bool new_name); - bool serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const predicate_rule_t rule, const BaseBlock* diff_block = NULL) const; - bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const; - - virtual const BlockDescriptor& mostDerivedBlockDescriptor() const { return getBlockDescriptor(); } - virtual BlockDescriptor& mostDerivedBlockDescriptor() { return getBlockDescriptor(); } - - // take all provided params from other and apply to self - bool overwriteFrom(const BaseBlock& other) - { - return false; - } - - // take all provided params that are not already provided, and apply to self - bool fillFrom(const BaseBlock& other) - { - return false; - } - - ParamDescriptorPtr findParamDescriptor(const Param& param); - - // take all provided params from other and apply to self - bool mergeBlock(BlockDescriptor& block_data, const BaseBlock& other, bool overwrite); - - static BlockDescriptor& getBlockDescriptor() - { - static BlockDescriptor sBlockDescriptor; - return sBlockDescriptor; - } - - protected: - void init(BlockDescriptor& descriptor, BlockDescriptor& base_descriptor, size_t block_size); - - - bool mergeBlockParam(bool source_provided, bool dst_provided, BlockDescriptor& block_data, const BaseBlock& source, bool overwrite) - { - return mergeBlock(block_data, source, overwrite); - } - - mutable bool mValidated; // lazy validation flag - bool mParamProvided; - - private: - const std::string& getParamName(const BlockDescriptor& block_data, const Param* paramp) const; - }; - - class LL_COMMON_API Param - { - public: - void setProvided(bool is_provided = true) - { - mIsProvided = is_provided; - enclosingBlock().paramChanged(*this, is_provided); - } - - Param& operator =(const Param& other) - { - mIsProvided = other.mIsProvided; - // don't change mEnclosingblockoffset - return *this; - } - protected: - - bool anyProvided() const { return mIsProvided; } - - Param(BaseBlock* enclosing_block); - - // store pointer to enclosing block as offset to reduce space and allow for quick copying - BaseBlock& enclosingBlock() const - { - const U8* my_addr = reinterpret_cast<const U8*>(this); - // get address of enclosing BLOCK class using stored offset to enclosing BaseBlock class - return *const_cast<BaseBlock*> - (reinterpret_cast<const BaseBlock*> - (my_addr - (ptrdiff_t)getEnclosingBlockOffset())); - } - - U32 getEnclosingBlockOffset() const - { - return ((U32)mEnclosingBlockOffsetHigh << 16) | (U32)mEnclosingBlockOffsetLow; - } - - private: - friend class BaseBlock; - - //24 bits for member offset field and 1 bit for provided flag - U16 mEnclosingBlockOffsetLow; - U8 mEnclosingBlockOffsetHigh:7; - U8 mIsProvided:1; - - }; - - template<typename T, typename NAME_VALUE_LOOKUP = TypeValues<T> > - struct ParamIterator - { - typedef typename std::vector<typename NAME_VALUE_LOOKUP::type_value_t >::const_iterator const_iterator; - typedef typename std::vector<typename NAME_VALUE_LOOKUP::type_value_t >::iterator iterator; - }; - - // wrapper for parameter with a known type - // specialized to handle 4 cases: - // simple "scalar" value - // parameter that is itself a block - // multiple scalar values, stored in a vector - // multiple blocks, stored in a vector - template<typename T, - typename NAME_VALUE_LOOKUP = TypeValues<T>, - bool HAS_MULTIPLE_VALUES = false, - typename VALUE_IS_BLOCK = typename IsBlock<ParamValue<typename LLTypeTags::Sorted<T>::value_t> >::value_t> - class TypedParam - : public Param, - public NAME_VALUE_LOOKUP::type_value_t - { - protected: - typedef TypedParam<T, NAME_VALUE_LOOKUP, HAS_MULTIPLE_VALUES, VALUE_IS_BLOCK> self_t; - typedef ParamValue<typename LLTypeTags::Sorted<T>::value_t> param_value_t; - typedef typename param_value_t::default_value_t default_value_t; - typedef typename NAME_VALUE_LOOKUP::type_value_t named_value_t; - public: - typedef typename param_value_t::value_t value_t; - - using named_value_t::operator(); - - TypedParam(BlockDescriptor& block_descriptor, const char* name, const default_value_t& value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count) - : Param(block_descriptor.mCurrentBlockPtr), - named_value_t(value) - { - if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)) - { - init(block_descriptor, validate_func, min_count, max_count, name); - } - } - - bool isProvided() const { return Param::anyProvided(); } - - bool isValid() const { return true; } - - static bool deserializeParam(Param& param, Parser& parser, Parser::name_stack_range_t& name_stack_range, bool new_name) - { - self_t& typed_param = static_cast<self_t&>(param); - // no further names in stack, attempt to parse value now - if (name_stack_range.first == name_stack_range.second) - { - std::string name; - - // try to parse a known named value - if(named_value_t::valueNamesExist() - && parser.readValue(name) - && named_value_t::getValueFromName(name, typed_param.getValue())) - { - typed_param.setValueName(name); - typed_param.setProvided(); - return true; - } - // try to read value directly - else if (parser.readValue(typed_param.getValue())) - { - typed_param.clearValueName(); - typed_param.setProvided(); - return true; - } - } - return false; - } - - static bool serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const Param* diff_param) - { - bool serialized = false; - const self_t& typed_param = static_cast<const self_t&>(param); - const self_t* diff_typed_param = static_cast<const self_t*>(diff_param); - - LLPredicate::Value<ESerializePredicates> predicate; - if (diff_typed_param && ParamCompare<T>::equals(typed_param.getValue(), diff_typed_param->getValue())) - { - predicate.set(HAS_DEFAULT_VALUE); - } - - predicate.set(VALID, typed_param.isValid()); - predicate.set(PROVIDED, typed_param.anyProvided()); - predicate.set(EMPTY, false); - - if (!predicate_rule.check(predicate)) return false; - - if (!name_stack.empty()) - { - name_stack.back().second = true; - } - - std::string key = typed_param.getValueName(); - - // first try to write out name of name/value pair - - if (!key.empty()) - { - if (!diff_typed_param || !ParamCompare<std::string>::equals(diff_typed_param->getValueName(), key)) - { - serialized = parser.writeValue(key, name_stack); - } - } - // then try to serialize value directly - else if (!diff_typed_param || ParamCompare<T>::equals(typed_param.getValue(), diff_typed_param->getValue())) - { - serialized = parser.writeValue(typed_param.getValue(), name_stack); - if (!serialized) - { - std::string calculated_key = typed_param.calcValueName(typed_param.getValue()); - if (calculated_key.size() - && (!diff_typed_param - || !ParamCompare<std::string>::equals(static_cast<const self_t*>(diff_param)->getValueName(), calculated_key))) - { - serialized = parser.writeValue(calculated_key, name_stack); - } - } - } - return serialized; - } - - static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count) - { - // tell parser about our actual type - parser.inspectValue<T>(name_stack, min_count, max_count, NULL); - // then tell it about string-based alternatives ("red", "blue", etc. for LLColor4) - if (named_value_t::getPossibleValues()) - { - parser.inspectValue<std::string>(name_stack, min_count, max_count, named_value_t::getPossibleValues()); - } - } - - void set(const value_t& val, bool flag_as_provided = true) - { - named_value_t::clearValueName(); - named_value_t::setValue(val); - setProvided(flag_as_provided); - } - - self_t& operator =(const typename named_value_t::name_t& name) - { - named_value_t::assignNamedValue(name); - return *this; - } - - protected: - - self_t& operator =(const self_t& other) - { - param_value_t::operator =(other); - Param::operator =(other); - return *this; - } - - static bool mergeWith(Param& dst, const Param& src, bool overwrite) - { - const self_t& src_typed_param = static_cast<const self_t&>(src); - self_t& dst_typed_param = static_cast<self_t&>(dst); - - if (src_typed_param.isProvided() - && (overwrite || !dst_typed_param.isProvided())) - { - dst_typed_param.set(src_typed_param.getValue()); - return true; - } - return false; - } - private: - void init( BlockDescriptor &block_descriptor, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count, const char* name ) - { - ParamDescriptorPtr param_descriptor = ParamDescriptorPtr(new ParamDescriptor( - block_descriptor.mCurrentBlockPtr->getHandleFromParam(this), - &mergeWith, - &deserializeParam, - &serializeParam, - validate_func, - &inspectParam, - min_count, max_count)); - block_descriptor.addParam(param_descriptor, name); - } - }; - - // parameter that is a block - template <typename BLOCK_T, typename NAME_VALUE_LOOKUP> - class TypedParam<BLOCK_T, NAME_VALUE_LOOKUP, false, IS_A_BLOCK> - : public Param, - public NAME_VALUE_LOOKUP::type_value_t - { - protected: - typedef ParamValue<typename LLTypeTags::Sorted<BLOCK_T>::value_t> param_value_t; - typedef typename param_value_t::default_value_t default_value_t; - typedef TypedParam<BLOCK_T, NAME_VALUE_LOOKUP, false, IS_A_BLOCK> self_t; - typedef typename NAME_VALUE_LOOKUP::type_value_t named_value_t; - public: - using named_value_t::operator(); - typedef typename param_value_t::value_t value_t; - - TypedParam(BlockDescriptor& block_descriptor, const char* name, const default_value_t& value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count) - : Param(block_descriptor.mCurrentBlockPtr), - named_value_t(value) - { - if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)) - { - init(block_descriptor, validate_func, min_count, max_count, name); - } - } - - static bool deserializeParam(Param& param, Parser& parser, Parser::name_stack_range_t& name_stack_range, bool new_name) - { - self_t& typed_param = static_cast<self_t&>(param); - - if (name_stack_range.first == name_stack_range.second) - { // try to parse a known named value - std::string name; - - if(named_value_t::valueNamesExist() - && parser.readValue(name) - && named_value_t::getValueFromName(name, typed_param.getValue())) - { - typed_param.setValueName(name); - typed_param.setProvided(); - return true; - } - } - - if(typed_param.deserializeBlock(parser, name_stack_range, new_name)) - { // attempt to parse block... - typed_param.clearValueName(); - typed_param.setProvided(); - return true; - } - - - return false; - } - - static bool serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const Param* diff_param) - { - const self_t& typed_param = static_cast<const self_t&>(param); - - LLPredicate::Value<ESerializePredicates> predicate; - - predicate.set(VALID, typed_param.isValid()); - predicate.set(PROVIDED, typed_param.anyProvided()); - - if (!predicate_rule.check(predicate)) return false; - - if (!name_stack.empty()) - { - name_stack.back().second = true; - } - - std::string key = typed_param.getValueName(); - if (!key.empty()) - { - if (!diff_param || !ParamCompare<std::string>::equals(static_cast<const self_t*>(diff_param)->getValueName(), key)) - { - parser.writeValue(key, name_stack); - return true; - } - } - else - { - return typed_param.serializeBlock(parser, name_stack, predicate_rule, static_cast<const self_t*>(diff_param)); - } - - return false; - } - - static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count) - { - const self_t& typed_param = static_cast<const self_t&>(param); - - // tell parser about our actual type - parser.inspectValue<value_t>(name_stack, min_count, max_count, NULL); - // then tell it about string-based alternatives ("red", "blue", etc. for LLColor4) - if (named_value_t::getPossibleValues()) - { - parser.inspectValue<std::string>(name_stack, min_count, max_count, named_value_t::getPossibleValues()); - } - - typed_param.inspectBlock(parser, name_stack, min_count, max_count); - } - - // a param-that-is-a-block is provided when the user has set one of its child params - // *and* the block as a whole validates - bool isProvided() const - { - return Param::anyProvided() && isValid(); - } - - bool isValid() const - { - return param_value_t::isValid(); - } - - // assign block contents to this param-that-is-a-block - void set(const value_t& val, bool flag_as_provided = true) - { - named_value_t::setValue(val); - named_value_t::clearValueName(); - setProvided(flag_as_provided); - } - - self_t& operator =(const typename named_value_t::name_t& name) - { - named_value_t::assignNamedValue(name); - return *this; - } - - // propagate changed status up to enclosing block - /*virtual*/ void paramChanged(const Param& changed_param, bool user_provided) - { - param_value_t::paramChanged(changed_param, user_provided); - - if (user_provided) - { - setProvided(); - named_value_t::clearValueName(); - } - else - { - Param::enclosingBlock().paramChanged(*this, user_provided); - } - } - - protected: - - self_t& operator =(const self_t& other) - { - param_value_t::operator =(other); - Param::operator =(other); - return *this; - } - - static bool mergeWith(Param& dst, const Param& src, bool overwrite) - { - const self_t& src_typed_param = static_cast<const self_t&>(src); - self_t& dst_typed_param = static_cast<self_t&>(dst); - - if (src_typed_param.anyProvided()) - { - if (dst_typed_param.mergeBlockParam(src_typed_param.isProvided(), dst_typed_param.isProvided(), param_value_t::getBlockDescriptor(), src_typed_param, overwrite)) - { - dst_typed_param.clearValueName(); - dst_typed_param.setProvided(true); - return true; - } - } - return false; - } - - private: - void init( BlockDescriptor &block_descriptor, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count, const char* name ) - { - ParamDescriptorPtr param_descriptor = ParamDescriptorPtr(new ParamDescriptor( - block_descriptor.mCurrentBlockPtr->getHandleFromParam(this), - &mergeWith, - &deserializeParam, - &serializeParam, - validate_func, - &inspectParam, - min_count, max_count)); - block_descriptor.addParam(param_descriptor, name); - } - }; - - // list of non-block parameters - template <typename MULTI_VALUE_T, typename NAME_VALUE_LOOKUP> - class TypedParam<MULTI_VALUE_T, NAME_VALUE_LOOKUP, true, NOT_BLOCK> - : public Param - { - protected: - typedef TypedParam<MULTI_VALUE_T, NAME_VALUE_LOOKUP, true, NOT_BLOCK> self_t; - typedef ParamValue<typename LLTypeTags::Sorted<MULTI_VALUE_T>::value_t> param_value_t; - typedef typename std::vector<typename NAME_VALUE_LOOKUP::type_value_t> container_t; - typedef container_t default_value_t; - typedef typename NAME_VALUE_LOOKUP::type_value_t named_value_t; - - public: - typedef typename param_value_t::value_t value_t; - - TypedParam(BlockDescriptor& block_descriptor, const char* name, const default_value_t& value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count) - : Param(block_descriptor.mCurrentBlockPtr), - mMinCount(min_count), - mMaxCount(max_count) - { - std::copy(value.begin(), value.end(), std::back_inserter(mValues)); - - if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)) - { - init(block_descriptor, validate_func, min_count, max_count, name); - - } - } - - bool isProvided() const { return Param::anyProvided() && isValid(); } - - bool isValid() const - { - size_t num_elements = numValidElements(); - return mMinCount < num_elements && num_elements < mMaxCount; - } - - static bool deserializeParam(Param& param, Parser& parser, Parser::name_stack_range_t& name_stack_range, bool new_name) - { - Parser::name_stack_range_t new_name_stack_range(name_stack_range); - self_t& typed_param = static_cast<self_t&>(param); - value_t value; - - // pop first element if empty string - if (new_name_stack_range.first != new_name_stack_range.second && new_name_stack_range.first->first.empty()) - { - ++new_name_stack_range.first; - } - - // no further names in stack, attempt to parse value now - if (new_name_stack_range.first == new_name_stack_range.second) - { - std::string name; - - // try to parse a known named value - if(named_value_t::valueNamesExist() - && parser.readValue(name) - && named_value_t::getValueFromName(name, value)) - { - typed_param.add(value); - typed_param.mValues.back().setValueName(name); - return true; - } - else if (parser.readValue(value)) // attempt to read value directly - { - typed_param.add(value); - return true; - } - } - return false; - } - - static bool serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const Param* diff_param) - { - bool serialized = false; - const self_t& typed_param = static_cast<const self_t&>(param); - - LLPredicate::Value<ESerializePredicates> predicate; - - predicate.set(REQUIRED, typed_param.mMinCount > 0); - predicate.set(VALID, typed_param.isValid()); - predicate.set(PROVIDED, typed_param.anyProvided()); - predicate.set(EMPTY, typed_param.mValues.empty()); - - if (!predicate_rule.check(predicate)) return false; - - for (const_iterator it = typed_param.mValues.begin(), end_it = typed_param.mValues.end(); - it != end_it; - ++it) - { - std::string key = it->getValueName(); - name_stack.push_back(std::make_pair(std::string(), true)); - - if(key.empty()) - // not parsed via name values, write out value directly - { - bool value_written = parser.writeValue(*it, name_stack); - if (!value_written) - { - std::string calculated_key = it->calcValueName(it->getValue()); - if (parser.writeValue(calculated_key, name_stack)) - { - serialized = true; - } - else - { - break; - } - } - } - else - { - if(parser.writeValue(key, name_stack)) - { - serialized = true; - } - else - { - break; - } - } - } - - return serialized; - } - - static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count) - { - parser.inspectValue<MULTI_VALUE_T>(name_stack, min_count, max_count, NULL); - if (named_value_t::getPossibleValues()) - { - parser.inspectValue<std::string>(name_stack, min_count, max_count, named_value_t::getPossibleValues()); - } - } - - void set(const container_t& val, bool flag_as_provided = true) - { - mValues = val; - setProvided(flag_as_provided); - } - - param_value_t& add() - { - mValues.push_back(value_t()); - Param::setProvided(); - return mValues.back(); - } - - self_t& add(const value_t& item) - { - mValues.push_back(item); - setProvided(); - return *this; - } - - self_t& add(const typename named_value_t::name_t& name) - { - value_t value; - - // try to parse a per type named value - if (named_value_t::getValueFromName(name, value)) - { - add(value); - mValues.back().setValueName(name); - } - - return *this; - } - - // implicit conversion - operator const container_t&() const { return mValues; } - // explicit conversion - const container_t& operator()() const { return mValues; } - - typedef typename container_t::iterator iterator; - typedef typename container_t::const_iterator const_iterator; - iterator begin() { return mValues.begin(); } - iterator end() { return mValues.end(); } - const_iterator begin() const { return mValues.begin(); } - const_iterator end() const { return mValues.end(); } - bool empty() const { return mValues.empty(); } - size_t size() const { return mValues.size(); } - - size_t numValidElements() const - { - return mValues.size(); - } - - protected: - static bool mergeWith(Param& dst, const Param& src, bool overwrite) - { - const self_t& src_typed_param = static_cast<const self_t&>(src); - self_t& dst_typed_param = static_cast<self_t&>(dst); - - if (overwrite) - { - std::copy(src_typed_param.begin(), src_typed_param.end(), std::back_inserter(dst_typed_param.mValues)); - } - else - { - container_t new_values(src_typed_param.mValues); - std::copy(dst_typed_param.begin(), dst_typed_param.end(), std::back_inserter(new_values)); - std::swap(dst_typed_param.mValues, new_values); - } - - if (src_typed_param.begin() != src_typed_param.end()) - { - dst_typed_param.setProvided(); - } - return true; - } - - container_t mValues; - size_t mMinCount, - mMaxCount; - - private: - void init( BlockDescriptor &block_descriptor, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count, const char* name ) - { - ParamDescriptorPtr param_descriptor = ParamDescriptorPtr(new ParamDescriptor( - block_descriptor.mCurrentBlockPtr->getHandleFromParam(this), - &mergeWith, - &deserializeParam, - &serializeParam, - validate_func, - &inspectParam, - min_count, max_count)); - block_descriptor.addParam(param_descriptor, name); - } - }; - - // list of block parameters - template <typename MULTI_BLOCK_T, typename NAME_VALUE_LOOKUP> - class TypedParam<MULTI_BLOCK_T, NAME_VALUE_LOOKUP, true, IS_A_BLOCK> - : public Param - { - protected: - typedef TypedParam<MULTI_BLOCK_T, NAME_VALUE_LOOKUP, true, IS_A_BLOCK> self_t; - typedef ParamValue<typename LLTypeTags::Sorted<MULTI_BLOCK_T>::value_t> param_value_t; - typedef typename std::vector<typename NAME_VALUE_LOOKUP::type_value_t> container_t; - typedef typename NAME_VALUE_LOOKUP::type_value_t named_value_t; - typedef container_t default_value_t; - typedef typename container_t::iterator iterator; - typedef typename container_t::const_iterator const_iterator; - public: - typedef typename param_value_t::value_t value_t; - - TypedParam(BlockDescriptor& block_descriptor, const char* name, const default_value_t& value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count) - : Param(block_descriptor.mCurrentBlockPtr), - mMinCount(min_count), - mMaxCount(max_count) - { - std::copy(value.begin(), value.end(), back_inserter(mValues)); - - if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)) - { - init(block_descriptor, validate_func, min_count, max_count, name); - } - } - - bool isProvided() const { return Param::anyProvided() && isValid(); } - - bool isValid() const - { - size_t num_elements = numValidElements(); - return mMinCount < num_elements && num_elements < mMaxCount; - } - - - static bool deserializeParam(Param& param, Parser& parser, Parser::name_stack_range_t& name_stack_range, bool new_name) - { - Parser::name_stack_range_t new_name_stack_range(name_stack_range); - self_t& typed_param = static_cast<self_t&>(param); - bool new_value = false; - bool new_array_value = false; - - // pop first element if empty string - if (new_name_stack_range.first != new_name_stack_range.second && new_name_stack_range.first->first.empty()) - { - new_array_value = new_name_stack_range.first->second; - ++new_name_stack_range.first; - } - - if (new_name || new_array_value || typed_param.mValues.empty()) - { - new_value = true; - typed_param.mValues.push_back(value_t()); - } - param_value_t& value = typed_param.mValues.back(); - - if (new_name_stack_range.first == new_name_stack_range.second) - { // try to parse a known named value - std::string name; - - if(named_value_t::valueNamesExist() - && parser.readValue(name) - && named_value_t::getValueFromName(name, value.getValue())) - { - typed_param.mValues.back().setValueName(name); - typed_param.setProvided(); - if (new_array_value) - { - name_stack_range.first->second = false; - } - return true; - } - } - - // attempt to parse block... - if(value.deserializeBlock(parser, new_name_stack_range, new_name)) - { - typed_param.setProvided(); - if (new_array_value) - { - name_stack_range.first->second = false; - } - return true; - } - - - if (new_value) - { // failed to parse new value, pop it off - typed_param.mValues.pop_back(); - } - - return false; - } - - static bool serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const Param* diff_param) - { - bool serialized = false; - const self_t& typed_param = static_cast<const self_t&>(param); - LLPredicate::Value<ESerializePredicates> predicate; - - predicate.set(REQUIRED, typed_param.mMinCount > 0); - predicate.set(VALID, typed_param.isValid()); - predicate.set(PROVIDED, typed_param.anyProvided()); - predicate.set(EMPTY, typed_param.mValues.empty()); - - if (!predicate_rule.check(predicate)) return false; - - for (const_iterator it = typed_param.mValues.begin(), end_it = typed_param.mValues.end(); - it != end_it; - ++it) - { - name_stack.push_back(std::make_pair(std::string(), true)); - - std::string key = it->getValueName(); - if (!key.empty()) - { - serialized |= parser.writeValue(key, name_stack); - } - // Not parsed via named values, write out value directly - // NOTE: currently we don't do diffing of Multiples - else - { - serialized = it->serializeBlock(parser, name_stack, predicate_rule, NULL); - } - - name_stack.pop_back(); - } - - if (!serialized && predicate_rule.check(ll_make_predicate(EMPTY))) - { - serialized |= parser.writeValue(Flag(), name_stack); - } - - return serialized; - } - - static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count) - { - const param_value_t& value_param = param_value_t(value_t()); - - // tell parser about our actual type - parser.inspectValue<value_t>(name_stack, min_count, max_count, NULL); - // then tell it about string-based alternatives ("red", "blue", etc. for LLColor4) - if (named_value_t::getPossibleValues()) - { - parser.inspectValue<std::string>(name_stack, min_count, max_count, named_value_t::getPossibleValues()); - } - - value_param.inspectBlock(parser, name_stack, min_count, max_count); - } - - void set(const container_t& val, bool flag_as_provided = true) - { - mValues = val; - setProvided(flag_as_provided); - } - - param_value_t& add() - { - mValues.push_back(value_t()); - setProvided(); - return mValues.back(); - } - - self_t& add(const value_t& item) - { - mValues.push_back(item); - setProvided(); - return *this; - } - - self_t& add(const typename named_value_t::name_t& name) - { - value_t value; - - // try to parse a per type named value - if (named_value_t::getValueFromName(name, value)) - { - add(value); - mValues.back().setValueName(name); - } - return *this; - } - - // implicit conversion - operator const container_t&() const { return mValues; } - // explicit conversion - const container_t& operator()() const { return mValues; } - - iterator begin() { return mValues.begin(); } - iterator end() { return mValues.end(); } - const_iterator begin() const { return mValues.begin(); } - const_iterator end() const { return mValues.end(); } - bool empty() const { return mValues.empty(); } - size_t size() const { return mValues.size(); } - - size_t numValidElements() const - { - size_t count = 0; - for (const_iterator it = mValues.begin(), end_it = mValues.end(); - it != end_it; - ++it) - { - if(it->isValid()) count++; - } - return count; - } - - protected: - - static bool mergeWith(Param& dst, const Param& src, bool overwrite) - { - const self_t& src_typed_param = static_cast<const self_t&>(src); - self_t& dst_typed_param = static_cast<self_t&>(dst); - - if (overwrite) - { - std::copy(src_typed_param.begin(), src_typed_param.end(), std::back_inserter(dst_typed_param.mValues)); - } - else - { - container_t new_values(src_typed_param.mValues); - std::copy(dst_typed_param.begin(), dst_typed_param.end(), std::back_inserter(new_values)); - std::swap(dst_typed_param.mValues, new_values); - } - - if (src_typed_param.begin() != src_typed_param.end()) - { - dst_typed_param.setProvided(); - } - - return true; - } - - container_t mValues; - size_t mMinCount, - mMaxCount; - - private: - void init( BlockDescriptor &block_descriptor, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count, const char* name ) - { - ParamDescriptorPtr param_descriptor = ParamDescriptorPtr(new ParamDescriptor( - block_descriptor.mCurrentBlockPtr->getHandleFromParam(this), - &mergeWith, - &deserializeParam, - &serializeParam, - validate_func, - &inspectParam, - min_count, max_count)); - block_descriptor.addParam(param_descriptor, name); - } - }; - - template <typename DERIVED_BLOCK, typename BASE_BLOCK = BaseBlock> - class ChoiceBlock : public BASE_BLOCK - { - typedef ChoiceBlock<DERIVED_BLOCK, BASE_BLOCK> self_t; - typedef ChoiceBlock<DERIVED_BLOCK, BASE_BLOCK> enclosing_block_t; - typedef BASE_BLOCK base_block_t; - - LOG_CLASS(self_t); - public: - // take all provided params from other and apply to self - bool overwriteFrom(const self_t& other) - { - return static_cast<DERIVED_BLOCK*>(this)->mergeBlock(getBlockDescriptor(), other, true); - } - - // take all provided params that are not already provided, and apply to self - bool fillFrom(const self_t& other) - { - return static_cast<DERIVED_BLOCK*>(this)->mergeBlock(getBlockDescriptor(), other, false); - } - - bool mergeBlockParam(bool source_provided, bool dest_provided, BlockDescriptor& block_data, const self_t& source, bool overwrite) - { - bool source_override = source_provided && (overwrite || !dest_provided); - - if (source_override || source.mCurChoice == mCurChoice) - { - return mergeBlock(block_data, source, overwrite); - } - return false; - } - - // merge with other block - bool mergeBlock(BlockDescriptor& block_data, const self_t& other, bool overwrite) - { - mCurChoice = other.mCurChoice; - return base_block_t::mergeBlock(getBlockDescriptor(), other, overwrite); - } - - // clear out old choice when param has changed - /*virtual*/ void paramChanged(const Param& changed_param, bool user_provided) - { - param_handle_t changed_param_handle = base_block_t::getHandleFromParam(&changed_param); - // if we have a new choice... - if (changed_param_handle != mCurChoice) - { - // clear provided flag on previous choice - Param* previous_choice = base_block_t::getParamFromHandle(mCurChoice); - if (previous_choice) - { - previous_choice->setProvided(false); - } - mCurChoice = changed_param_handle; - } - base_block_t::paramChanged(changed_param, user_provided); - } - - virtual const BlockDescriptor& mostDerivedBlockDescriptor() const { return getBlockDescriptor(); } - virtual BlockDescriptor& mostDerivedBlockDescriptor() { return getBlockDescriptor(); } - - protected: - ChoiceBlock() - : mCurChoice(0) - { - BaseBlock::init(getBlockDescriptor(), base_block_t::getBlockDescriptor(), sizeof(DERIVED_BLOCK)); - } - - // Alternatives are mutually exclusive wrt other Alternatives in the same block. - // One alternative in a block will always have isChosen() == true. - // At most one alternative in a block will have isProvided() == true. - template <typename T, typename NAME_VALUE_LOOKUP = typename TypeValues<T>::type_value_t > - class Alternative : public TypedParam<T, NAME_VALUE_LOOKUP, false> - { - typedef TypedParam<T, NAME_VALUE_LOOKUP, false> super_t; - typedef typename super_t::value_t value_t; - typedef typename super_t::default_value_t default_value_t; - - public: - friend class ChoiceBlock<DERIVED_BLOCK>; - - using super_t::operator =; - - explicit Alternative(const char* name = "", const default_value_t& val = defaultValue<default_value_t>()) - : super_t(DERIVED_BLOCK::getBlockDescriptor(), name, val, NULL, 0, 1), - mOriginalValue(val) - { - // assign initial choice to first declared option - DERIVED_BLOCK* blockp = ((DERIVED_BLOCK*)DERIVED_BLOCK::getBlockDescriptor().mCurrentBlockPtr); - if (LL_UNLIKELY(DERIVED_BLOCK::getBlockDescriptor().mInitializationState == BlockDescriptor::INITIALIZING)) - { - if(blockp->mCurChoice == 0) - { - blockp->mCurChoice = Param::enclosingBlock().getHandleFromParam(this); - } - } - } - - void choose() - { - static_cast<enclosing_block_t&>(Param::enclosingBlock()).paramChanged(*this, true); - } - - void chooseAs(const value_t& val) - { - super_t::set(val); - } - - void operator =(const value_t& val) - { - super_t::set(val); - } - - void operator()(const value_t& val) - { - super_t::set(val); - } - - operator const value_t&() const - { - return (*this)(); - } - - const value_t& operator()() const - { - if (static_cast<enclosing_block_t&>(Param::enclosingBlock()).getCurrentChoice() == this) - { - return super_t::getValue(); - } - return mOriginalValue; - } - - bool isChosen() const - { - return static_cast<enclosing_block_t&>(Param::enclosingBlock()).getCurrentChoice() == this; - } - - private: - default_value_t mOriginalValue; - }; - - public: - static BlockDescriptor& getBlockDescriptor() - { - static BlockDescriptor sBlockDescriptor; - return sBlockDescriptor; - } - - private: - param_handle_t mCurChoice; - - const Param* getCurrentChoice() const - { - return base_block_t::getParamFromHandle(mCurChoice); - } - }; - - template <typename DERIVED_BLOCK, typename BASE_BLOCK = BaseBlock> - class Block - : public BASE_BLOCK - { - typedef Block<DERIVED_BLOCK, BASE_BLOCK> self_t; - - protected: - typedef Block<DERIVED_BLOCK, BASE_BLOCK> block_t; - - public: - typedef BASE_BLOCK base_block_t; - - // take all provided params from other and apply to self - bool overwriteFrom(const self_t& other) - { - return static_cast<DERIVED_BLOCK*>(this)->mergeBlock(getBlockDescriptor(), other, true); - } - - // take all provided params that are not already provided, and apply to self - bool fillFrom(const self_t& other) - { - return static_cast<DERIVED_BLOCK*>(this)->mergeBlock(getBlockDescriptor(), other, false); - } - - virtual const BlockDescriptor& mostDerivedBlockDescriptor() const { return getBlockDescriptor(); } - virtual BlockDescriptor& mostDerivedBlockDescriptor() { return getBlockDescriptor(); } - - protected: - Block() - { - //#pragma message("Parsing LLInitParam::Block") - BaseBlock::init(getBlockDescriptor(), BASE_BLOCK::getBlockDescriptor(), sizeof(DERIVED_BLOCK)); - } - - // - // Nested classes for declaring parameters - // - template <typename T, typename NAME_VALUE_LOOKUP = typename TypeValues<T>::type_value_t > - class Optional : public TypedParam<T, NAME_VALUE_LOOKUP, false> - { - typedef TypedParam<T, NAME_VALUE_LOOKUP, false> super_t; - typedef typename super_t::value_t value_t; - typedef typename super_t::default_value_t default_value_t; - - public: - using super_t::operator(); - using super_t::operator =; - - explicit Optional(const char* name = "", const default_value_t& val = defaultValue<default_value_t>()) - : super_t(DERIVED_BLOCK::getBlockDescriptor(), name, val, NULL, 0, 1) - { - //#pragma message("Parsing LLInitParam::Block::Optional") - } - - Optional& operator =(const value_t& val) - { - super_t::set(val); - return *this; - } - - DERIVED_BLOCK& operator()(const value_t& val) - { - super_t::set(val); - return static_cast<DERIVED_BLOCK&>(Param::enclosingBlock()); - } - }; - - template <typename T, typename NAME_VALUE_LOOKUP = typename TypeValues<T>::type_value_t > - class Mandatory : public TypedParam<T, NAME_VALUE_LOOKUP, false> - { - typedef TypedParam<T, NAME_VALUE_LOOKUP, false> super_t; - typedef Mandatory<T, NAME_VALUE_LOOKUP> self_t; - typedef typename super_t::value_t value_t; - typedef typename super_t::default_value_t default_value_t; - - public: - using super_t::operator(); - using super_t::operator =; - - // mandatory parameters require a name to be parseable - explicit Mandatory(const char* name = "", const default_value_t& val = defaultValue<default_value_t>()) - : super_t(DERIVED_BLOCK::getBlockDescriptor(), name, val, &validate, 1, 1) - {} - - Mandatory& operator =(const value_t& val) - { - super_t::set(val); - return *this; - } - - DERIVED_BLOCK& operator()(const value_t& val) - { - super_t::set(val); - return static_cast<DERIVED_BLOCK&>(Param::enclosingBlock()); - } - - static bool validate(const Param* p) - { - // valid only if provided - return static_cast<const self_t*>(p)->isProvided(); - } - - }; - - template <typename T, typename RANGE = BaseBlock::AnyAmount, typename NAME_VALUE_LOOKUP = typename TypeValues<T>::type_value_t > - class Multiple : public TypedParam<T, NAME_VALUE_LOOKUP, true> - { - typedef TypedParam<T, NAME_VALUE_LOOKUP, true> super_t; - typedef Multiple<T, RANGE, NAME_VALUE_LOOKUP> self_t; - typedef typename super_t::container_t container_t; - typedef typename super_t::value_t value_t; - - public: - typedef typename super_t::iterator iterator; - typedef typename super_t::const_iterator const_iterator; - - using super_t::operator(); - using super_t::operator const container_t&; - - explicit Multiple(const char* name = "") - : super_t(DERIVED_BLOCK::getBlockDescriptor(), name, container_t(), &validate, RANGE::minCount, RANGE::maxCount) - {} - - Multiple& operator =(const container_t& val) - { - super_t::set(val); - return *this; - } - - DERIVED_BLOCK& operator()(const container_t& val) - { - super_t::set(val); - return static_cast<DERIVED_BLOCK&>(Param::enclosingBlock()); - } - - static bool validate(const Param* paramp) - { - size_t num_valid = ((super_t*)paramp)->numValidElements(); - return RANGE::minCount <= num_valid && num_valid <= RANGE::maxCount; - } - }; - - // can appear in data files, but will ignored during parsing - // cannot read or write in code - class Ignored : public Param - { - public: - explicit Ignored(const char* name) - : Param(DERIVED_BLOCK::getBlockDescriptor().mCurrentBlockPtr) - { - BlockDescriptor& block_descriptor = DERIVED_BLOCK::getBlockDescriptor(); - if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)) - { - ParamDescriptorPtr param_descriptor = ParamDescriptorPtr(new ParamDescriptor( - block_descriptor.mCurrentBlockPtr->getHandleFromParam(this), - NULL, - &deserializeParam, - NULL, - NULL, - NULL, - 0, S32_MAX)); - block_descriptor.addParam(param_descriptor, name); - } - } - - static bool deserializeParam(Param& param, Parser& parser, Parser::name_stack_range_t& name_stack_range, bool new_name) - { - if (name_stack_range.first == name_stack_range.second) - { - //std::string message = llformat("Deprecated value %s ignored", getName().c_str()); - //parser.parserWarning(message); - return true; - } - - return false; - } - }; - - // can appear in data files, or be written to in code, but data will be ignored - // cannot be read in code - class Deprecated : public Ignored - { - public: - explicit Deprecated(const char* name) : Ignored(name) {} - - // dummy writer interfaces - template<typename T> - Deprecated& operator =(const T& val) - { - // do nothing - return *this; - } - - template<typename T> - DERIVED_BLOCK& operator()(const T& val) - { - // do nothing - return static_cast<DERIVED_BLOCK&>(Param::enclosingBlock()); - } - - template<typename T> - void set(const T& val, bool flag_as_provided = true) - { - // do nothing - } - }; - - public: - static BlockDescriptor& getBlockDescriptor() - { - static BlockDescriptor sBlockDescriptor; - return sBlockDescriptor; - } - - protected: - template <typename T, typename NAME_VALUE_LOOKUP, bool multiple, typename is_block> - void changeDefault(TypedParam<T, NAME_VALUE_LOOKUP, multiple, is_block>& param, - const typename TypedParam<T, NAME_VALUE_LOOKUP, multiple, is_block>::value_t& value) - { - if (!param.isProvided()) - { - param.set(value, false); - } - } - - }; - - template<typename T, typename BLOCK_T> - struct IsBlock<ParamValue<BaseBlock::Lazy<T, BaseBlock::IS_A_BLOCK>, BLOCK_T >, void> - { - typedef IS_A_BLOCK value_t; - }; - - template<typename T, typename BLOCK_T> - struct IsBlock<ParamValue<BaseBlock::Lazy<T, BaseBlock::NOT_A_BLOCK>, BLOCK_T >, void> - { - typedef NOT_BLOCK value_t; - }; - - template<typename T, typename BLOCK_IDENTIFIER> - struct IsBlock<ParamValue<BaseBlock::Atomic<T>, typename IsBlock<BaseBlock::Atomic<T> >::value_t >, BLOCK_IDENTIFIER> - { - typedef typename IsBlock<T>::value_t value_t; - }; - - template<typename T, typename BLOCK_IDENTIFIER> - struct IsBlock<ParamValue<BaseBlock::Sequential<T>, typename IsBlock<BaseBlock::Sequential<T> >::value_t >, BLOCK_IDENTIFIER> - { - typedef typename IsBlock<T>::value_t value_t; - }; - - - template<typename T> - struct InnerMostType - { - typedef T value_t; - }; - - template<typename T> - struct InnerMostType<ParamValue<T, NOT_BLOCK> > - { - typedef typename InnerMostType<T>::value_t value_t; - }; - - template<typename T> - struct InnerMostType<ParamValue<T, IS_A_BLOCK> > - { - typedef typename InnerMostType<T>::value_t value_t; - }; - - template<typename T, typename BLOCK_T> - class ParamValue <BaseBlock::Atomic<T>, BLOCK_T> - { - typedef ParamValue <BaseBlock::Atomic<T>, BLOCK_T> self_t; - - public: - typedef typename InnerMostType<T>::value_t value_t; - typedef T default_value_t; - - ParamValue() - : mValue() - {} - - ParamValue(const default_value_t& value) - : mValue(value) - {} - - void setValue(const value_t& val) - { - mValue.setValue(val); - } - - const value_t& getValue() const - { - return mValue.getValue(); - } - - value_t& getValue() - { - return mValue.getValue(); - } - - bool deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack_range, bool new_name) - { - if (new_name) - { - resetToDefault(); - } - return mValue.deserializeBlock(p, name_stack_range, new_name); - } - - bool serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const self_t* diff_block = NULL) const - { - const BaseBlock* base_block = diff_block - ? &(diff_block->mValue) - : NULL; - return mValue.serializeBlock(p, name_stack, predicate_rule, base_block); - } - - bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const - { - return mValue.inspectBlock(p, name_stack, min_count, max_count); - } - - bool mergeBlockParam(bool source_provided, bool dst_provided, BlockDescriptor& block_data, const self_t& source, bool overwrite) - { - if ((overwrite && source_provided) // new values coming in on top or... - || (!overwrite && !dst_provided)) // values being pushed under with nothing already there - { - // clear away what is there and take the new stuff as a whole - resetToDefault(); - return mValue.mergeBlock(block_data, source.getValue(), overwrite); - } - return mValue.mergeBlock(block_data, source.getValue(), overwrite); - } - - bool validateBlock(bool emit_errors = true) const - { - return mValue.validateBlock(emit_errors); - } - - bool isValid() const - { - return validateBlock(false); - } - - static BlockDescriptor& getBlockDescriptor() - { - return value_t::getBlockDescriptor(); - } - - - private: - void resetToDefault() - { - static T default_value; - mValue = default_value; - } - - T mValue; - }; - - template<typename T> - class ParamValue <BaseBlock::Sequential<T>, IS_A_BLOCK> - { - typedef ParamValue <BaseBlock::Sequential<T>, IS_A_BLOCK> self_t; - - public: - typedef typename InnerMostType<T>::value_t value_t; - typedef T default_value_t; - - ParamValue() - : mValue() - { - mCurParam = getBlockDescriptor().mAllParams.begin(); - } - - ParamValue(const default_value_t& value) - : mValue(value) - { - mCurParam = getBlockDescriptor().mAllParams.begin(); - } - - void setValue(const value_t& val) - { - mValue.setValue(val); - } - - const value_t& getValue() const - { - return mValue.getValue(); - } - - value_t& getValue() - { - return mValue.getValue(); - } - - bool deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack_range, bool new_name) - { - if (new_name) - { - mCurParam = getBlockDescriptor().mAllParams.begin(); - } - if (name_stack_range.first == name_stack_range.second - && mCurParam != getBlockDescriptor().mAllParams.end()) - { - // deserialize to mCurParam - ParamDescriptor& pd = *(*mCurParam); - ParamDescriptor::deserialize_func_t deserialize_func = pd.mDeserializeFunc; - Param* paramp = mValue.getParamFromHandle(pd.mParamHandle); - - if (deserialize_func - && paramp - && deserialize_func(*paramp, p, name_stack_range, new_name)) - { - ++mCurParam; - return true; - } - else - { - return false; - } - } - else - { - return mValue.deserializeBlock(p, name_stack_range, new_name); - } - } - - bool serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const self_t* diff_block = NULL) const - { - const BaseBlock* base_block = diff_block - ? &(diff_block->mValue) - : NULL; - return mValue.serializeBlock(p, name_stack, predicate_rule, base_block); - } - - bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const - { - return mValue.inspectBlock(p, name_stack, min_count, max_count); - } - - bool mergeBlockParam(bool source_provided, bool dst_provided, BlockDescriptor& block_data, const self_t& source, bool overwrite) - { - return mValue.mergeBlock(block_data, source.getValue(), overwrite); - } - - bool validateBlock(bool emit_errors = true) const - { - return mValue.validateBlock(emit_errors); - } - - bool isValid() const - { - return validateBlock(false); - } - - static BlockDescriptor& getBlockDescriptor() - { - return value_t::getBlockDescriptor(); - } - - private: - - BlockDescriptor::all_params_list_t::iterator mCurParam; - T mValue; - }; - - template<typename T> - class ParamValue <BaseBlock::Sequential<T>, NOT_BLOCK> - : public T - { - typedef ParamValue <BaseBlock::Sequential<T>, NOT_BLOCK> self_t; - - public: - typedef typename InnerMostType<T>::value_t value_t; - typedef T default_value_t; - - ParamValue() - : T() - {} - - ParamValue(const default_value_t& value) - : T(value.getValue()) - {} - - bool isValid() const { return true; } - }; - - template<typename T, typename BLOCK_T> - class ParamValue <BaseBlock::Lazy<T, IS_A_BLOCK>, BLOCK_T> - { - typedef ParamValue <BaseBlock::Lazy<T, IS_A_BLOCK>, BLOCK_T> self_t; - - public: - typedef typename InnerMostType<T>::value_t value_t; - typedef LazyValue<T> default_value_t; - - ParamValue() - : mValue() - {} - - ParamValue(const default_value_t& other) - : mValue(other) - {} - - ParamValue(const T& value) - : mValue(value) - {} - - void setValue(const value_t& val) - { - mValue.set(val); - } - - const value_t& getValue() const - { - return mValue.get().getValue(); - } - - value_t& getValue() - { - return mValue.get().getValue(); - } - - bool deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack_range, bool new_name) - { - return mValue.get().deserializeBlock(p, name_stack_range, new_name); - } - - bool serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const self_t* diff_block = NULL) const - { - if (mValue.empty()) return false; - - const BaseBlock* base_block = (diff_block && !diff_block->mValue.empty()) - ? &(diff_block->mValue.get().getValue()) - : NULL; - return mValue.get().serializeBlock(p, name_stack, predicate_rule, base_block); - } - - bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const - { - return mValue.get().inspectBlock(p, name_stack, min_count, max_count); - } - - bool mergeBlockParam(bool source_provided, bool dst_provided, BlockDescriptor& block_data, const self_t& source, bool overwrite) - { - return source.mValue.empty() || mValue.get().mergeBlock(block_data, source.getValue(), overwrite); - } - - bool validateBlock(bool emit_errors = true) const - { - return mValue.empty() || mValue.get().validateBlock(emit_errors); - } - - bool isValid() const - { - return validateBlock(false); - } - - static BlockDescriptor& getBlockDescriptor() - { - return value_t::getBlockDescriptor(); - } - - private: - LazyValue<T> mValue; - }; - - template<typename T, typename BLOCK_T> - class ParamValue <BaseBlock::Lazy<T, NOT_BLOCK>, BLOCK_T> - { - typedef ParamValue <BaseBlock::Lazy<T, NOT_BLOCK>, BLOCK_T> self_t; - - public: - typedef typename InnerMostType<T>::value_t value_t; - typedef LazyValue<T> default_value_t; - - ParamValue() - : mValue() - {} - - ParamValue(const default_value_t& other) - : mValue(other) - {} - - ParamValue(const T& value) - : mValue(value) - {} - - void setValue(const value_t& val) - { - mValue.set(val); - } - - const value_t& getValue() const - { - return mValue.get().getValue(); - } - - value_t& getValue() - { - return mValue.get().getValue(); - } - - bool isValid() const - { - return true; - } - - private: - LazyValue<T> mValue; - }; - - template <> - class ParamValue <LLSD, NOT_BLOCK> - : public BaseBlock - { - public: - typedef LLSD value_t; - typedef LLSD default_value_t; - - ParamValue() - {} - - ParamValue(const default_value_t& other) - : mValue(other) - {} - - void setValue(const value_t& val) { mValue = val; } - - const value_t& getValue() const { return mValue; } - LLSD& getValue() { return mValue; } - - // block param interface - LL_COMMON_API bool deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack_range, bool new_name); - LL_COMMON_API bool serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const BaseBlock* diff_block = NULL) const; - bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const - { - //TODO: implement LLSD params as schema type Any - return true; - } - - private: - static void serializeElement(Parser& p, const LLSD& sd, Parser::name_stack_t& name_stack); - - LLSD mValue; - }; - - template<typename T> - class CustomParamValue - : public Block<ParamValue<T> > - { - public: - typedef enum e_value_age - { - VALUE_NEEDS_UPDATE, // mValue needs to be refreshed from the block parameters - VALUE_AUTHORITATIVE, // mValue holds the authoritative value (which has been replicated to the block parameters via updateBlockFromValue) - BLOCK_AUTHORITATIVE // mValue is derived from the block parameters, which are authoritative - } EValueAge; - - typedef TypeValues<T> derived_t; - typedef CustomParamValue<T> self_t; - typedef Block<ParamValue<T> > block_t; - typedef T default_value_t; - typedef T value_t; - typedef void baseblock_base_class_t; - - - CustomParamValue(const default_value_t& value = T()) - : mValue(value), - mValueAge(VALUE_AUTHORITATIVE) - {} - - bool deserializeBlock(Parser& parser, Parser::name_stack_range_t& name_stack_range, bool new_name) - { - derived_t& typed_param = static_cast<derived_t&>(*this); - // try to parse direct value T - if (name_stack_range.first == name_stack_range.second) - { - if(parser.readValue(typed_param.mValue)) - { - typed_param.mValueAge = VALUE_AUTHORITATIVE; - typed_param.updateBlockFromValue(false); - - return true; - } - } - - // fall back on parsing block components for T - return typed_param.BaseBlock::deserializeBlock(parser, name_stack_range, new_name); - } - - bool serializeBlock(Parser& parser, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const BaseBlock* diff_block = NULL) const - { - const derived_t& typed_param = static_cast<const derived_t&>(*this); - const derived_t* diff_param = static_cast<const derived_t*>(diff_block); - - //std::string key = typed_param.getValueName(); - - //// first try to write out name of name/value pair - //if (!key.empty()) - //{ - // if (!diff_param || !ParamCompare<std::string>::equals(diff_param->getValueName(), key)) - // { - // return parser.writeValue(key, name_stack); - // } - //} - // then try to serialize value directly - if (!diff_param || !ParamCompare<T>::equals(typed_param.getValue(), diff_param->getValue())) - { - - if (parser.writeValue(typed_param.getValue(), name_stack)) - { - return true; - } - else - { - //RN: *always* serialize provided components of BlockValue (don't pass diff_param on), - // since these tend to be viewed as the constructor arguments for the value T. It seems - // cleaner to treat the uniqueness of a BlockValue according to the generated value, and - // not the individual components. This way <color red="0" green="1" blue="0"/> will not - // be exported as <color green="1"/>, since it was probably the intent of the user to - // be specific about the RGB color values. This also fixes an issue where we distinguish - // between rect.left not being provided and rect.left being explicitly set to 0 (same as default) - - if (typed_param.mValueAge == VALUE_AUTHORITATIVE) - { - // if the value is authoritative but the parser doesn't accept the value type - // go ahead and make a copy, and splat the value out to its component params - // and serialize those params - derived_t copy(typed_param); - copy.updateBlockFromValue(true); - return copy.block_t::serializeBlock(parser, name_stack, predicate_rule, NULL); - } - else - { - return block_t::serializeBlock(parser, name_stack, predicate_rule, NULL); - } - } - } - return false; - } - - bool validateBlock(bool emit_errors = true) const - { - if (mValueAge == VALUE_NEEDS_UPDATE) - { - if (block_t::validateBlock(emit_errors)) - { - // clear stale keyword associated with old value - mValueAge = BLOCK_AUTHORITATIVE; - static_cast<derived_t*>(const_cast<self_t*>(this))->updateValueFromBlock(); - return true; - } - else - { - //block value incomplete, so not considered provided - // will attempt to revalidate on next call to isProvided() - return false; - } - } - else - { - // we have a valid value in hand - return true; - } - } - - // propagate change status up to enclosing block - /*virtual*/ void paramChanged(const Param& changed_param, bool user_provided) - { - BaseBlock::paramChanged(changed_param, user_provided); - if (user_provided) - { - // a parameter changed, so our value is out of date - mValueAge = VALUE_NEEDS_UPDATE; - } - } - - void setValue(const value_t& val) - { - // set param version number to be up to date, so we ignore block contents - mValueAge = VALUE_AUTHORITATIVE; - mValue = val; - static_cast<derived_t*>(this)->updateBlockFromValue(false); - } - - const value_t& getValue() const - { - validateBlock(true); - return mValue; - } - - T& getValue() - { - validateBlock(true); - return mValue; - } - - protected: - - // use this from within updateValueFromBlock() to set the value without making it authoritative - void updateValue(const value_t& value) - { - mValue = value; - } - - bool mergeBlockParam(bool source_provided, bool dst_provided, BlockDescriptor& block_data, const BaseBlock& source, bool overwrite) - { - bool source_override = source_provided && (overwrite || !dst_provided); - - const derived_t& src_typed_param = static_cast<const derived_t&>(source); - - if (source_override && src_typed_param.mValueAge == VALUE_AUTHORITATIVE) - { - // copy value over - setValue(src_typed_param.getValue()); - return true; - } - // merge individual parameters into destination - if (mValueAge == VALUE_AUTHORITATIVE) - { - static_cast<derived_t*>(this)->updateBlockFromValue(dst_provided); - } - return mergeBlock(block_data, source, overwrite); - } - - bool mergeBlock(BlockDescriptor& block_data, const BaseBlock& source, bool overwrite) - { - return block_t::mergeBlock(block_data, source, overwrite); - } - - private: - mutable T mValue; - mutable EValueAge mValueAge; - }; + + // empty default implementation of key cache + // leverages empty base class optimization + template <typename T> + class TypeValues + : public ParamValue<typename LLTypeTags::Sorted<T>::value_t> + { + private: + struct Inaccessable{}; + public: + typedef std::map<std::string, T> value_name_map_t; + typedef Inaccessable name_t; + typedef TypeValues<T> type_value_t; + typedef ParamValue<typename LLTypeTags::Sorted<T>::value_t> param_value_t; + typedef typename param_value_t::value_t value_t; + + TypeValues(const typename param_value_t::value_t& val) + : param_value_t(val) + {} + + void setValueName(const std::string& key) {} + std::string getValueName() const { return ""; } + std::string calcValueName(const value_t& value) const { return ""; } + void clearValueName() const {} + + static bool getValueFromName(const std::string& name, value_t& value) + { + return false; + } + + static bool valueNamesExist() + { + return false; + } + + static std::vector<std::string>* getPossibleValues() + { + return NULL; + } + + void assignNamedValue(const Inaccessable& name) + {} + + operator const value_t&() const + { + return param_value_t::getValue(); + } + + const value_t& operator()() const + { + return param_value_t::getValue(); + } + + static value_name_map_t* getValueNames() {return NULL;} + }; + + // helper class to implement name value lookups + // and caching of last used name + template <typename T, typename DERIVED_TYPE = TypeValues<T>, bool IS_SPECIALIZED = true > + class TypeValuesHelper + : public ParamValue<typename LLTypeTags::Sorted<T>::value_t> + { + typedef TypeValuesHelper<T, DERIVED_TYPE, IS_SPECIALIZED> self_t; + public: + typedef typename std::map<std::string, T> value_name_map_t; + typedef std::string name_t; + typedef self_t type_value_t; + typedef ParamValue<typename LLTypeTags::Sorted<T>::value_t> param_value_t; + typedef typename param_value_t::value_t value_t; + + TypeValuesHelper(const typename param_value_t::value_t& val) + : param_value_t(val) + {} + + //TODO: cache key by index to save on param block size + void setValueName(const std::string& value_name) + { + mValueName = value_name; + } + + std::string getValueName() const + { + return mValueName; + } + + std::string calcValueName(const value_t& value) const + { + value_name_map_t* map = getValueNames(); + for (typename value_name_map_t::value_type& map_pair : *map) + { + if (ParamCompare<T>::equals(map_pair.second, value)) + { + return map_pair.first; + } + } + + return ""; + } + + void clearValueName() const + { + mValueName.clear(); + } + + static bool getValueFromName(const std::string& name, value_t& value) + { + value_name_map_t* map = getValueNames(); + typename value_name_map_t::iterator found_it = map->find(name); + if (found_it == map->end()) return false; + + value = found_it->second; + return true; + } + + static bool valueNamesExist() + { + return !getValueNames()->empty(); + } + + static value_name_map_t* getValueNames() + { + static value_name_map_t sMap; + static bool sInitialized = false; + + if (!sInitialized) + { + sInitialized = true; + DERIVED_TYPE::declareValues(); + } + return &sMap; + } + + static std::vector<std::string>* getPossibleValues() + { + static std::vector<std::string> sValues; + + value_name_map_t* map = getValueNames(); + for (typename value_name_map_t::value_type& map_pair : *map) + { + sValues.push_back(map_pair.first); + } + return &sValues; + } + + static void declare(const std::string& name, const value_t& value) + { + (*getValueNames())[name] = value; + } + + void operator ()(const std::string& name) + { + *this = name; + } + + void assignNamedValue(const std::string& name) + { + if (getValueFromName(name, param_value_t::getValue())) + { + setValueName(name); + } + } + + operator const value_t&() const + { + return param_value_t::getValue(); + } + + const value_t& operator()() const + { + return param_value_t::getValue(); + } + + protected: + static void getName(const std::string& name, const value_t& value) + {} + + mutable std::string mValueName; + }; + + // string types can support custom named values, but need + // to disambiguate in code between a string that is a named value + // and a string that is a name + template <typename DERIVED_TYPE> + class TypeValuesHelper<std::string, DERIVED_TYPE, true> + : public TypeValuesHelper<std::string, DERIVED_TYPE, false> + { + public: + typedef TypeValuesHelper<std::string, DERIVED_TYPE, true> self_t; + typedef TypeValuesHelper<std::string, DERIVED_TYPE, false> base_t; + typedef std::string value_t; + typedef std::string name_t; + typedef self_t type_value_t; + + TypeValuesHelper(const std::string& val) + : base_t(val) + {} + + void operator ()(const std::string& name) + { + *this = name; + } + + self_t& operator =(const std::string& name) + { + if (base_t::getValueFromName(name, ParamValue<std::string>::getValue())) + { + base_t::setValueName(name); + } + else + { + ParamValue<std::string>::setValue(name); + } + return *this; + } + + operator const value_t&() const + { + return ParamValue<std::string>::getValue(); + } + + const value_t& operator()() const + { + return ParamValue<std::string>::getValue(); + } + + }; + + // parser base class with mechanisms for registering readers/writers/inspectors of different types + class LL_COMMON_API Parser + { + LOG_CLASS(Parser); + public: + typedef std::vector<std::pair<std::string, bool> > name_stack_t; + typedef std::pair<name_stack_t::iterator, name_stack_t::iterator> name_stack_range_t; + typedef std::vector<std::string> possible_values_t; + + typedef bool (*parser_read_func_t)(Parser& parser, void* output); + typedef bool (*parser_write_func_t)(Parser& parser, const void*, name_stack_t&); + typedef boost::function<void (name_stack_t&, S32, S32, const possible_values_t*)> parser_inspect_func_t; + + typedef std::map<const std::type_info*, parser_read_func_t> parser_read_func_map_t; + typedef std::map<const std::type_info*, parser_write_func_t> parser_write_func_map_t; + typedef std::map<const std::type_info*, parser_inspect_func_t> parser_inspect_func_map_t; + + public: + + Parser(parser_read_func_map_t& read_map, parser_write_func_map_t& write_map, parser_inspect_func_map_t& inspect_map) + : mParseSilently(false), + mParserReadFuncs(&read_map), + mParserWriteFuncs(&write_map), + mParserInspectFuncs(&inspect_map) + {} + + virtual ~Parser(); + + template <typename T> bool readValue(T& param, typename boost::disable_if<boost::is_enum<T> >::type* dummy = 0) + { + parser_read_func_map_t::iterator found_it = mParserReadFuncs->find(&typeid(T)); + if (found_it != mParserReadFuncs->end()) + { + return found_it->second(*this, (void*)¶m); + } + + return false; + } + + template <typename T> bool readValue(T& param, typename boost::enable_if<boost::is_enum<T> >::type* dummy = 0) + { + parser_read_func_map_t::iterator found_it = mParserReadFuncs->find(&typeid(T)); + if (found_it != mParserReadFuncs->end()) + { + return found_it->second(*this, (void*)¶m); + } + else + { + found_it = mParserReadFuncs->find(&typeid(S32)); + if (found_it != mParserReadFuncs->end()) + { + S32 int_value; + bool parsed = found_it->second(*this, (void*)&int_value); + param = (T)int_value; + return parsed; + } + } + return false; + } + + template <typename T> bool writeValue(const T& param, name_stack_t& name_stack) + { + parser_write_func_map_t::iterator found_it = mParserWriteFuncs->find(&typeid(T)); + if (found_it != mParserWriteFuncs->end()) + { + return found_it->second(*this, (const void*)¶m, name_stack); + } + return false; + } + + // dispatch inspection to registered inspection functions, for each parameter in a param block + template <typename T> bool inspectValue(name_stack_t& name_stack, S32 min_count, S32 max_count, const possible_values_t* possible_values) + { + parser_inspect_func_map_t::iterator found_it = mParserInspectFuncs->find(&typeid(T)); + if (found_it != mParserInspectFuncs->end()) + { + found_it->second(name_stack, min_count, max_count, possible_values); + return true; + } + return false; + } + + virtual std::string getCurrentElementName() = 0; + virtual std::string getCurrentFileName() = 0; + virtual void parserWarning(const std::string& message); + virtual void parserError(const std::string& message); + void setParseSilently(bool silent) { mParseSilently = silent; } + + protected: + template <typename T> + void registerParserFuncs(parser_read_func_t read_func, parser_write_func_t write_func = NULL) + { + mParserReadFuncs->insert(std::make_pair(&typeid(T), read_func)); + mParserWriteFuncs->insert(std::make_pair(&typeid(T), write_func)); + } + + template <typename T> + void registerInspectFunc(parser_inspect_func_t inspect_func) + { + mParserInspectFuncs->insert(std::make_pair(&typeid(T), inspect_func)); + } + + bool mParseSilently; + + private: + parser_read_func_map_t* mParserReadFuncs; + parser_write_func_map_t* mParserWriteFuncs; + parser_inspect_func_map_t* mParserInspectFuncs; + }; + + class Param; + + enum ESerializePredicates + { + PROVIDED, + REQUIRED, + VALID, + HAS_DEFAULT_VALUE, + EMPTY + }; + + typedef LLPredicate::Rule<ESerializePredicates> predicate_rule_t; + + predicate_rule_t default_parse_rules(); + + // various callbacks and constraints associated with an individual param + struct LL_COMMON_API ParamDescriptor + { + struct UserData + { + virtual ~UserData() {} + }; + + typedef bool(*merge_func_t)(Param&, const Param&, bool); + typedef bool(*deserialize_func_t)(Param&, Parser&, Parser::name_stack_range_t&, bool); + typedef bool(*serialize_func_t)(const Param&, Parser&, Parser::name_stack_t&, const predicate_rule_t rules, const Param* diff_param); + typedef void(*inspect_func_t)(const Param&, Parser&, Parser::name_stack_t&, S32 min_count, S32 max_count); + typedef bool(*validation_func_t)(const Param*); + + ParamDescriptor(param_handle_t p, + merge_func_t merge_func, + deserialize_func_t deserialize_func, + serialize_func_t serialize_func, + validation_func_t validation_func, + inspect_func_t inspect_func, + S32 min_count, + S32 max_count); + + ParamDescriptor(); + ~ParamDescriptor(); + + param_handle_t mParamHandle; + merge_func_t mMergeFunc; + deserialize_func_t mDeserializeFunc; + serialize_func_t mSerializeFunc; + inspect_func_t mInspectFunc; + validation_func_t mValidationFunc; + S32 mMinCount; + S32 mMaxCount; + S32 mNumRefs; + UserData* mUserData; + }; + + typedef std::shared_ptr<ParamDescriptor> ParamDescriptorPtr; + + // each derived Block class keeps a static data structure maintaining offsets to various params + class LL_COMMON_API BlockDescriptor + { + public: + BlockDescriptor(); + + typedef enum e_initialization_state + { + UNINITIALIZED, + INITIALIZING, + INITIALIZED + } EInitializationState; + + void aggregateBlockData(BlockDescriptor& src_block_data); + void addParam(ParamDescriptorPtr param, const char* name); + + typedef boost::unordered_map<const std::string, ParamDescriptorPtr> param_map_t; + typedef std::vector<ParamDescriptorPtr> param_list_t; + typedef std::list<ParamDescriptorPtr> all_params_list_t; + typedef std::vector<std::pair<param_handle_t, ParamDescriptor::validation_func_t> > param_validation_list_t; + + param_map_t mNamedParams; // parameters with associated names + param_list_t mUnnamedParams; // parameters with_out_ associated names + param_validation_list_t mValidationList; // parameters that must be validated + all_params_list_t mAllParams; // all parameters, owns descriptors + size_t mMaxParamOffset; + EInitializationState mInitializationState; // whether or not static block data has been initialized + class BaseBlock* mCurrentBlockPtr; // pointer to block currently being constructed + }; + + //TODO: implement in terms of owned_ptr + template<typename T> + class LazyValue + { + public: + LazyValue() + : mPtr(NULL) + {} + + ~LazyValue() + { + delete mPtr; + } + + LazyValue(const T& value) + { + mPtr = new T(value); + } + + LazyValue(const LazyValue& other) + : mPtr(NULL) + { + *this = other; + } + + LazyValue& operator = (const LazyValue& other) + { + if (!other.mPtr) + { + delete mPtr; + mPtr = NULL; + } + else + { + if (!mPtr) + { + mPtr = new T(*other.mPtr); + } + else + { + *mPtr = *(other.mPtr); + } + } + return *this; + } + + bool operator==(const LazyValue& other) const + { + if (empty() || other.empty()) return false; + return *mPtr == *other.mPtr; + } + + bool empty() const + { + return mPtr == NULL; + } + + void set(const T& other) + { + if (!mPtr) + { + mPtr = new T(other); + } + else + { + *mPtr = other; + } + } + + const T& get() const + { + return *ensureInstance(); + } + + T& get() + { + return *ensureInstance(); + } + + operator const T&() const + { + return get(); + } + + private: + // lazily allocate an instance of T + T* ensureInstance() const + { + if (mPtr == NULL) + { + mPtr = new T(); + } + return mPtr; + } + + private: + + mutable T* mPtr; + }; + + // root class of all parameter blocks + + class LL_COMMON_API BaseBlock + { + public: + // lift block tags into baseblock namespace so derived classes do not need to qualify them + typedef LLInitParam::IS_A_BLOCK IS_A_BLOCK; + typedef LLInitParam::NOT_BLOCK NOT_A_BLOCK; + + template<typename T> + struct Sequential : public LLTypeTags::TypeTagBase<T, 2> + { + template <typename S> struct Cons { typedef Sequential<ParamValue<S> > value_t; }; + template <typename S> struct Cons<Sequential<S> > { typedef Sequential<S> value_t; }; + }; + + template<typename T> + struct Atomic : public LLTypeTags::TypeTagBase<T, 1> + { + template <typename S> struct Cons { typedef Atomic<ParamValue<S> > value_t; }; + template <typename S> struct Cons<Atomic<S> > { typedef Atomic<S> value_t; }; + }; + + template<typename T, typename BLOCK_T = typename IsBlock<T>::value_t > + struct Lazy : public LLTypeTags::TypeTagBase<T, 0> + { + template <typename S> struct Cons + { + typedef Lazy<ParamValue<S, BLOCK_T>, BLOCK_T> value_t; + }; + template <typename S> struct Cons<Lazy<S, IS_A_BLOCK> > + { + typedef Lazy<S, IS_A_BLOCK> value_t; + }; + template <typename S> struct Cons<Lazy<S, NOT_A_BLOCK> > + { + typedef Lazy<S, BLOCK_T> value_t; + }; + }; + + // "Multiple" constraint types, put here in root class to avoid ambiguity during use + struct AnyAmount + { + enum { minCount = 0 }; + enum { maxCount = U32_MAX }; + }; + + template<U32 MIN_AMOUNT> + struct AtLeast + { + enum { minCount = MIN_AMOUNT }; + enum { maxCount = U32_MAX }; + }; + + template<U32 MAX_AMOUNT> + struct AtMost + { + enum { minCount = 0 }; + enum { maxCount = MAX_AMOUNT }; + }; + + template<U32 MIN_AMOUNT, U32 MAX_AMOUNT> + struct Between + { + enum { minCount = MIN_AMOUNT }; + enum { maxCount = MAX_AMOUNT }; + }; + + template<U32 EXACT_COUNT> + struct Exactly + { + enum { minCount = EXACT_COUNT }; + enum { maxCount = EXACT_COUNT }; + }; + + // this typedef identifies derived classes as being blocks + typedef void baseblock_base_class_t; + LOG_CLASS(BaseBlock); + friend class Param; + + BaseBlock() + : mValidated(false), + mParamProvided(false) + {} + + virtual ~BaseBlock() {} + bool submitValue(Parser::name_stack_t& name_stack, Parser& p, bool silent=false); + + param_handle_t getHandleFromParam(const Param* param) const; + bool validateBlock(bool emit_errors = true) const; + + bool isProvided() const + { + return mParamProvided; + } + + bool isValid() const + { + return validateBlock(false); + } + + + Param* getParamFromHandle(const param_handle_t param_handle) + { + if (param_handle == 0) return NULL; + + U8* baseblock_address = reinterpret_cast<U8*>(this); + return reinterpret_cast<Param*>(baseblock_address + param_handle); + } + + const Param* getParamFromHandle(const param_handle_t param_handle) const + { + const U8* baseblock_address = reinterpret_cast<const U8*>(this); + return reinterpret_cast<const Param*>(baseblock_address + param_handle); + } + + void addSynonym(Param& param, const std::string& synonym); + + // Blocks can override this to do custom tracking of changes + virtual void paramChanged(const Param& changed_param, bool user_provided) + { + if (user_provided) + { + // a child param has been explicitly changed + // so *some* aspect of this block is now provided + mValidated = false; + mParamProvided = true; + } + } + + bool deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack_range, bool new_name); + bool serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const predicate_rule_t rule, const BaseBlock* diff_block = NULL) const; + bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const; + + virtual const BlockDescriptor& mostDerivedBlockDescriptor() const { return getBlockDescriptor(); } + virtual BlockDescriptor& mostDerivedBlockDescriptor() { return getBlockDescriptor(); } + + // take all provided params from other and apply to self + bool overwriteFrom(const BaseBlock& other) + { + return false; + } + + // take all provided params that are not already provided, and apply to self + bool fillFrom(const BaseBlock& other) + { + return false; + } + + ParamDescriptorPtr findParamDescriptor(const Param& param); + + // take all provided params from other and apply to self + bool mergeBlock(BlockDescriptor& block_data, const BaseBlock& other, bool overwrite); + + static BlockDescriptor& getBlockDescriptor() + { + static BlockDescriptor sBlockDescriptor; + return sBlockDescriptor; + } + + protected: + void init(BlockDescriptor& descriptor, BlockDescriptor& base_descriptor, size_t block_size); + + + bool mergeBlockParam(bool source_provided, bool dst_provided, BlockDescriptor& block_data, const BaseBlock& source, bool overwrite) + { + return mergeBlock(block_data, source, overwrite); + } + + mutable bool mValidated; // lazy validation flag + bool mParamProvided; + + private: + const std::string& getParamName(const BlockDescriptor& block_data, const Param* paramp) const; + }; + + class LL_COMMON_API Param + { + public: + void setProvided(bool is_provided = true) + { + mIsProvided = is_provided; + enclosingBlock().paramChanged(*this, is_provided); + } + + Param& operator =(const Param& other) + { + mIsProvided = other.mIsProvided; + // don't change mEnclosingblockoffset + return *this; + } + protected: + + bool anyProvided() const { return mIsProvided; } + + Param(BaseBlock* enclosing_block); + + // store pointer to enclosing block as offset to reduce space and allow for quick copying + BaseBlock& enclosingBlock() const + { + const U8* my_addr = reinterpret_cast<const U8*>(this); + // get address of enclosing BLOCK class using stored offset to enclosing BaseBlock class + return *const_cast<BaseBlock*> + (reinterpret_cast<const BaseBlock*> + (my_addr - (ptrdiff_t)getEnclosingBlockOffset())); + } + + U32 getEnclosingBlockOffset() const + { + return ((U32)mEnclosingBlockOffsetHigh << 16) | (U32)mEnclosingBlockOffsetLow; + } + + private: + friend class BaseBlock; + + //24 bits for member offset field and 1 bit for provided flag + U16 mEnclosingBlockOffsetLow; + U8 mEnclosingBlockOffsetHigh:7; + U8 mIsProvided:1; + + }; + + template<typename T, typename NAME_VALUE_LOOKUP = TypeValues<T> > + struct ParamIterator + { + typedef typename std::vector<typename NAME_VALUE_LOOKUP::type_value_t >::const_iterator const_iterator; + typedef typename std::vector<typename NAME_VALUE_LOOKUP::type_value_t >::iterator iterator; + }; + + // wrapper for parameter with a known type + // specialized to handle 4 cases: + // simple "scalar" value + // parameter that is itself a block + // multiple scalar values, stored in a vector + // multiple blocks, stored in a vector + template<typename T, + typename NAME_VALUE_LOOKUP = TypeValues<T>, + bool HAS_MULTIPLE_VALUES = false, + typename VALUE_IS_BLOCK = typename IsBlock<ParamValue<typename LLTypeTags::Sorted<T>::value_t> >::value_t> + class TypedParam + : public Param, + public NAME_VALUE_LOOKUP::type_value_t + { + protected: + typedef TypedParam<T, NAME_VALUE_LOOKUP, HAS_MULTIPLE_VALUES, VALUE_IS_BLOCK> self_t; + typedef ParamValue<typename LLTypeTags::Sorted<T>::value_t> param_value_t; + typedef typename param_value_t::default_value_t default_value_t; + typedef typename NAME_VALUE_LOOKUP::type_value_t named_value_t; + public: + typedef typename param_value_t::value_t value_t; + + using named_value_t::operator(); + + TypedParam(BlockDescriptor& block_descriptor, const char* name, const default_value_t& value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count) + : Param(block_descriptor.mCurrentBlockPtr), + named_value_t(value) + { + if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)) + { + init(block_descriptor, validate_func, min_count, max_count, name); + } + } + + bool isProvided() const { return Param::anyProvided(); } + + bool isValid() const { return true; } + + static bool deserializeParam(Param& param, Parser& parser, Parser::name_stack_range_t& name_stack_range, bool new_name) + { + self_t& typed_param = static_cast<self_t&>(param); + // no further names in stack, attempt to parse value now + if (name_stack_range.first == name_stack_range.second) + { + std::string name; + + // try to parse a known named value + if(named_value_t::valueNamesExist() + && parser.readValue(name) + && named_value_t::getValueFromName(name, typed_param.getValue())) + { + typed_param.setValueName(name); + typed_param.setProvided(); + return true; + } + // try to read value directly + else if (parser.readValue(typed_param.getValue())) + { + typed_param.clearValueName(); + typed_param.setProvided(); + return true; + } + } + return false; + } + + static bool serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const Param* diff_param) + { + bool serialized = false; + const self_t& typed_param = static_cast<const self_t&>(param); + const self_t* diff_typed_param = static_cast<const self_t*>(diff_param); + + LLPredicate::Value<ESerializePredicates> predicate; + if (diff_typed_param && ParamCompare<T>::equals(typed_param.getValue(), diff_typed_param->getValue())) + { + predicate.set(HAS_DEFAULT_VALUE); + } + + predicate.set(VALID, typed_param.isValid()); + predicate.set(PROVIDED, typed_param.anyProvided()); + predicate.set(EMPTY, false); + + if (!predicate_rule.check(predicate)) return false; + + if (!name_stack.empty()) + { + name_stack.back().second = true; + } + + std::string key = typed_param.getValueName(); + + // first try to write out name of name/value pair + + if (!key.empty()) + { + if (!diff_typed_param || !ParamCompare<std::string>::equals(diff_typed_param->getValueName(), key)) + { + serialized = parser.writeValue(key, name_stack); + } + } + // then try to serialize value directly + else if (!diff_typed_param || ParamCompare<T>::equals(typed_param.getValue(), diff_typed_param->getValue())) + { + serialized = parser.writeValue(typed_param.getValue(), name_stack); + if (!serialized) + { + std::string calculated_key = typed_param.calcValueName(typed_param.getValue()); + if (calculated_key.size() + && (!diff_typed_param + || !ParamCompare<std::string>::equals(static_cast<const self_t*>(diff_param)->getValueName(), calculated_key))) + { + serialized = parser.writeValue(calculated_key, name_stack); + } + } + } + return serialized; + } + + static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count) + { + // tell parser about our actual type + parser.inspectValue<T>(name_stack, min_count, max_count, NULL); + // then tell it about string-based alternatives ("red", "blue", etc. for LLColor4) + if (named_value_t::getPossibleValues()) + { + parser.inspectValue<std::string>(name_stack, min_count, max_count, named_value_t::getPossibleValues()); + } + } + + void set(const value_t& val, bool flag_as_provided = true) + { + named_value_t::clearValueName(); + named_value_t::setValue(val); + setProvided(flag_as_provided); + } + + self_t& operator =(const typename named_value_t::name_t& name) + { + named_value_t::assignNamedValue(name); + return *this; + } + + protected: + + self_t& operator =(const self_t& other) + { + param_value_t::operator =(other); + Param::operator =(other); + return *this; + } + + static bool mergeWith(Param& dst, const Param& src, bool overwrite) + { + const self_t& src_typed_param = static_cast<const self_t&>(src); + self_t& dst_typed_param = static_cast<self_t&>(dst); + + if (src_typed_param.isProvided() + && (overwrite || !dst_typed_param.isProvided())) + { + dst_typed_param.set(src_typed_param.getValue()); + return true; + } + return false; + } + private: + void init( BlockDescriptor &block_descriptor, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count, const char* name ) + { + ParamDescriptorPtr param_descriptor = ParamDescriptorPtr(new ParamDescriptor( + block_descriptor.mCurrentBlockPtr->getHandleFromParam(this), + &mergeWith, + &deserializeParam, + &serializeParam, + validate_func, + &inspectParam, + min_count, max_count)); + block_descriptor.addParam(param_descriptor, name); + } + }; + + // parameter that is a block + template <typename BLOCK_T, typename NAME_VALUE_LOOKUP> + class TypedParam<BLOCK_T, NAME_VALUE_LOOKUP, false, IS_A_BLOCK> + : public Param, + public NAME_VALUE_LOOKUP::type_value_t + { + protected: + typedef ParamValue<typename LLTypeTags::Sorted<BLOCK_T>::value_t> param_value_t; + typedef typename param_value_t::default_value_t default_value_t; + typedef TypedParam<BLOCK_T, NAME_VALUE_LOOKUP, false, IS_A_BLOCK> self_t; + typedef typename NAME_VALUE_LOOKUP::type_value_t named_value_t; + public: + using named_value_t::operator(); + typedef typename param_value_t::value_t value_t; + + TypedParam(BlockDescriptor& block_descriptor, const char* name, const default_value_t& value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count) + : Param(block_descriptor.mCurrentBlockPtr), + named_value_t(value) + { + if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)) + { + init(block_descriptor, validate_func, min_count, max_count, name); + } + } + + static bool deserializeParam(Param& param, Parser& parser, Parser::name_stack_range_t& name_stack_range, bool new_name) + { + self_t& typed_param = static_cast<self_t&>(param); + + if (name_stack_range.first == name_stack_range.second) + { // try to parse a known named value + std::string name; + + if(named_value_t::valueNamesExist() + && parser.readValue(name) + && named_value_t::getValueFromName(name, typed_param.getValue())) + { + typed_param.setValueName(name); + typed_param.setProvided(); + return true; + } + } + + if(typed_param.deserializeBlock(parser, name_stack_range, new_name)) + { // attempt to parse block... + typed_param.clearValueName(); + typed_param.setProvided(); + return true; + } + + + return false; + } + + static bool serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const Param* diff_param) + { + const self_t& typed_param = static_cast<const self_t&>(param); + + LLPredicate::Value<ESerializePredicates> predicate; + + predicate.set(VALID, typed_param.isValid()); + predicate.set(PROVIDED, typed_param.anyProvided()); + + if (!predicate_rule.check(predicate)) return false; + + if (!name_stack.empty()) + { + name_stack.back().second = true; + } + + std::string key = typed_param.getValueName(); + if (!key.empty()) + { + if (!diff_param || !ParamCompare<std::string>::equals(static_cast<const self_t*>(diff_param)->getValueName(), key)) + { + parser.writeValue(key, name_stack); + return true; + } + } + else + { + return typed_param.serializeBlock(parser, name_stack, predicate_rule, static_cast<const self_t*>(diff_param)); + } + + return false; + } + + static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count) + { + const self_t& typed_param = static_cast<const self_t&>(param); + + // tell parser about our actual type + parser.inspectValue<value_t>(name_stack, min_count, max_count, NULL); + // then tell it about string-based alternatives ("red", "blue", etc. for LLColor4) + if (named_value_t::getPossibleValues()) + { + parser.inspectValue<std::string>(name_stack, min_count, max_count, named_value_t::getPossibleValues()); + } + + typed_param.inspectBlock(parser, name_stack, min_count, max_count); + } + + // a param-that-is-a-block is provided when the user has set one of its child params + // *and* the block as a whole validates + bool isProvided() const + { + return Param::anyProvided() && isValid(); + } + + bool isValid() const + { + return param_value_t::isValid(); + } + + // assign block contents to this param-that-is-a-block + void set(const value_t& val, bool flag_as_provided = true) + { + named_value_t::setValue(val); + named_value_t::clearValueName(); + setProvided(flag_as_provided); + } + + self_t& operator =(const typename named_value_t::name_t& name) + { + named_value_t::assignNamedValue(name); + return *this; + } + + // propagate changed status up to enclosing block + /*virtual*/ void paramChanged(const Param& changed_param, bool user_provided) + { + param_value_t::paramChanged(changed_param, user_provided); + + if (user_provided) + { + setProvided(); + named_value_t::clearValueName(); + } + else + { + Param::enclosingBlock().paramChanged(*this, user_provided); + } + } + + protected: + + self_t& operator =(const self_t& other) + { + param_value_t::operator =(other); + Param::operator =(other); + return *this; + } + + static bool mergeWith(Param& dst, const Param& src, bool overwrite) + { + const self_t& src_typed_param = static_cast<const self_t&>(src); + self_t& dst_typed_param = static_cast<self_t&>(dst); + + if (src_typed_param.anyProvided()) + { + if (dst_typed_param.mergeBlockParam(src_typed_param.isProvided(), dst_typed_param.isProvided(), param_value_t::getBlockDescriptor(), src_typed_param, overwrite)) + { + dst_typed_param.clearValueName(); + dst_typed_param.setProvided(true); + return true; + } + } + return false; + } + + private: + void init( BlockDescriptor &block_descriptor, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count, const char* name ) + { + ParamDescriptorPtr param_descriptor = ParamDescriptorPtr(new ParamDescriptor( + block_descriptor.mCurrentBlockPtr->getHandleFromParam(this), + &mergeWith, + &deserializeParam, + &serializeParam, + validate_func, + &inspectParam, + min_count, max_count)); + block_descriptor.addParam(param_descriptor, name); + } + }; + + // list of non-block parameters + template <typename MULTI_VALUE_T, typename NAME_VALUE_LOOKUP> + class TypedParam<MULTI_VALUE_T, NAME_VALUE_LOOKUP, true, NOT_BLOCK> + : public Param + { + protected: + typedef TypedParam<MULTI_VALUE_T, NAME_VALUE_LOOKUP, true, NOT_BLOCK> self_t; + typedef ParamValue<typename LLTypeTags::Sorted<MULTI_VALUE_T>::value_t> param_value_t; + typedef typename std::vector<typename NAME_VALUE_LOOKUP::type_value_t> container_t; + typedef container_t default_value_t; + typedef typename NAME_VALUE_LOOKUP::type_value_t named_value_t; + + public: + typedef typename param_value_t::value_t value_t; + + TypedParam(BlockDescriptor& block_descriptor, const char* name, const default_value_t& value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count) + : Param(block_descriptor.mCurrentBlockPtr), + mMinCount(min_count), + mMaxCount(max_count) + { + std::copy(value.begin(), value.end(), std::back_inserter(mValues)); + + if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)) + { + init(block_descriptor, validate_func, min_count, max_count, name); + + } + } + + bool isProvided() const { return Param::anyProvided() && isValid(); } + + bool isValid() const + { + size_t num_elements = numValidElements(); + return mMinCount < num_elements && num_elements < mMaxCount; + } + + static bool deserializeParam(Param& param, Parser& parser, Parser::name_stack_range_t& name_stack_range, bool new_name) + { + Parser::name_stack_range_t new_name_stack_range(name_stack_range); + self_t& typed_param = static_cast<self_t&>(param); + value_t value; + + // pop first element if empty string + if (new_name_stack_range.first != new_name_stack_range.second && new_name_stack_range.first->first.empty()) + { + ++new_name_stack_range.first; + } + + // no further names in stack, attempt to parse value now + if (new_name_stack_range.first == new_name_stack_range.second) + { + std::string name; + + // try to parse a known named value + if(named_value_t::valueNamesExist() + && parser.readValue(name) + && named_value_t::getValueFromName(name, value)) + { + typed_param.add(value); + typed_param.mValues.back().setValueName(name); + return true; + } + else if (parser.readValue(value)) // attempt to read value directly + { + typed_param.add(value); + return true; + } + } + return false; + } + + static bool serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const Param* diff_param) + { + bool serialized = false; + const self_t& typed_param = static_cast<const self_t&>(param); + + LLPredicate::Value<ESerializePredicates> predicate; + + predicate.set(REQUIRED, typed_param.mMinCount > 0); + predicate.set(VALID, typed_param.isValid()); + predicate.set(PROVIDED, typed_param.anyProvided()); + predicate.set(EMPTY, typed_param.mValues.empty()); + + if (!predicate_rule.check(predicate)) return false; + + for (const_iterator it = typed_param.mValues.begin(), end_it = typed_param.mValues.end(); + it != end_it; + ++it) + { + std::string key = it->getValueName(); + name_stack.push_back(std::make_pair(std::string(), true)); + + if(key.empty()) + // not parsed via name values, write out value directly + { + bool value_written = parser.writeValue(*it, name_stack); + if (!value_written) + { + std::string calculated_key = it->calcValueName(it->getValue()); + if (parser.writeValue(calculated_key, name_stack)) + { + serialized = true; + } + else + { + break; + } + } + } + else + { + if(parser.writeValue(key, name_stack)) + { + serialized = true; + } + else + { + break; + } + } + } + + return serialized; + } + + static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count) + { + parser.inspectValue<MULTI_VALUE_T>(name_stack, min_count, max_count, NULL); + if (named_value_t::getPossibleValues()) + { + parser.inspectValue<std::string>(name_stack, min_count, max_count, named_value_t::getPossibleValues()); + } + } + + void set(const container_t& val, bool flag_as_provided = true) + { + mValues = val; + setProvided(flag_as_provided); + } + + param_value_t& add() + { + mValues.push_back(value_t()); + Param::setProvided(); + return mValues.back(); + } + + self_t& add(const value_t& item) + { + mValues.push_back(item); + setProvided(); + return *this; + } + + self_t& add(const typename named_value_t::name_t& name) + { + value_t value; + + // try to parse a per type named value + if (named_value_t::getValueFromName(name, value)) + { + add(value); + mValues.back().setValueName(name); + } + + return *this; + } + + // implicit conversion + operator const container_t&() const { return mValues; } + // explicit conversion + const container_t& operator()() const { return mValues; } + + typedef typename container_t::iterator iterator; + typedef typename container_t::const_iterator const_iterator; + iterator begin() { return mValues.begin(); } + iterator end() { return mValues.end(); } + const_iterator begin() const { return mValues.begin(); } + const_iterator end() const { return mValues.end(); } + bool empty() const { return mValues.empty(); } + size_t size() const { return mValues.size(); } + + size_t numValidElements() const + { + return mValues.size(); + } + + protected: + static bool mergeWith(Param& dst, const Param& src, bool overwrite) + { + const self_t& src_typed_param = static_cast<const self_t&>(src); + self_t& dst_typed_param = static_cast<self_t&>(dst); + + if (overwrite) + { + std::copy(src_typed_param.begin(), src_typed_param.end(), std::back_inserter(dst_typed_param.mValues)); + } + else + { + container_t new_values(src_typed_param.mValues); + std::copy(dst_typed_param.begin(), dst_typed_param.end(), std::back_inserter(new_values)); + std::swap(dst_typed_param.mValues, new_values); + } + + if (src_typed_param.begin() != src_typed_param.end()) + { + dst_typed_param.setProvided(); + } + return true; + } + + container_t mValues; + size_t mMinCount, + mMaxCount; + + private: + void init( BlockDescriptor &block_descriptor, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count, const char* name ) + { + ParamDescriptorPtr param_descriptor = ParamDescriptorPtr(new ParamDescriptor( + block_descriptor.mCurrentBlockPtr->getHandleFromParam(this), + &mergeWith, + &deserializeParam, + &serializeParam, + validate_func, + &inspectParam, + min_count, max_count)); + block_descriptor.addParam(param_descriptor, name); + } + }; + + // list of block parameters + template <typename MULTI_BLOCK_T, typename NAME_VALUE_LOOKUP> + class TypedParam<MULTI_BLOCK_T, NAME_VALUE_LOOKUP, true, IS_A_BLOCK> + : public Param + { + protected: + typedef TypedParam<MULTI_BLOCK_T, NAME_VALUE_LOOKUP, true, IS_A_BLOCK> self_t; + typedef ParamValue<typename LLTypeTags::Sorted<MULTI_BLOCK_T>::value_t> param_value_t; + typedef typename std::vector<typename NAME_VALUE_LOOKUP::type_value_t> container_t; + typedef typename NAME_VALUE_LOOKUP::type_value_t named_value_t; + typedef container_t default_value_t; + typedef typename container_t::iterator iterator; + typedef typename container_t::const_iterator const_iterator; + public: + typedef typename param_value_t::value_t value_t; + + TypedParam(BlockDescriptor& block_descriptor, const char* name, const default_value_t& value, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count) + : Param(block_descriptor.mCurrentBlockPtr), + mMinCount(min_count), + mMaxCount(max_count) + { + std::copy(value.begin(), value.end(), back_inserter(mValues)); + + if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)) + { + init(block_descriptor, validate_func, min_count, max_count, name); + } + } + + bool isProvided() const { return Param::anyProvided() && isValid(); } + + bool isValid() const + { + size_t num_elements = numValidElements(); + return mMinCount < num_elements && num_elements < mMaxCount; + } + + + static bool deserializeParam(Param& param, Parser& parser, Parser::name_stack_range_t& name_stack_range, bool new_name) + { + Parser::name_stack_range_t new_name_stack_range(name_stack_range); + self_t& typed_param = static_cast<self_t&>(param); + bool new_value = false; + bool new_array_value = false; + + // pop first element if empty string + if (new_name_stack_range.first != new_name_stack_range.second && new_name_stack_range.first->first.empty()) + { + new_array_value = new_name_stack_range.first->second; + ++new_name_stack_range.first; + } + + if (new_name || new_array_value || typed_param.mValues.empty()) + { + new_value = true; + typed_param.mValues.push_back(value_t()); + } + param_value_t& value = typed_param.mValues.back(); + + if (new_name_stack_range.first == new_name_stack_range.second) + { // try to parse a known named value + std::string name; + + if(named_value_t::valueNamesExist() + && parser.readValue(name) + && named_value_t::getValueFromName(name, value.getValue())) + { + typed_param.mValues.back().setValueName(name); + typed_param.setProvided(); + if (new_array_value) + { + name_stack_range.first->second = false; + } + return true; + } + } + + // attempt to parse block... + if(value.deserializeBlock(parser, new_name_stack_range, new_name)) + { + typed_param.setProvided(); + if (new_array_value) + { + name_stack_range.first->second = false; + } + return true; + } + + + if (new_value) + { // failed to parse new value, pop it off + typed_param.mValues.pop_back(); + } + + return false; + } + + static bool serializeParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const Param* diff_param) + { + bool serialized = false; + const self_t& typed_param = static_cast<const self_t&>(param); + LLPredicate::Value<ESerializePredicates> predicate; + + predicate.set(REQUIRED, typed_param.mMinCount > 0); + predicate.set(VALID, typed_param.isValid()); + predicate.set(PROVIDED, typed_param.anyProvided()); + predicate.set(EMPTY, typed_param.mValues.empty()); + + if (!predicate_rule.check(predicate)) return false; + + for (const_iterator it = typed_param.mValues.begin(), end_it = typed_param.mValues.end(); + it != end_it; + ++it) + { + name_stack.push_back(std::make_pair(std::string(), true)); + + std::string key = it->getValueName(); + if (!key.empty()) + { + serialized |= parser.writeValue(key, name_stack); + } + // Not parsed via named values, write out value directly + // NOTE: currently we don't do diffing of Multiples + else + { + serialized = it->serializeBlock(parser, name_stack, predicate_rule, NULL); + } + + name_stack.pop_back(); + } + + if (!serialized && predicate_rule.check(ll_make_predicate(EMPTY))) + { + serialized |= parser.writeValue(Flag(), name_stack); + } + + return serialized; + } + + static void inspectParam(const Param& param, Parser& parser, Parser::name_stack_t& name_stack, S32 min_count, S32 max_count) + { + const param_value_t& value_param = param_value_t(value_t()); + + // tell parser about our actual type + parser.inspectValue<value_t>(name_stack, min_count, max_count, NULL); + // then tell it about string-based alternatives ("red", "blue", etc. for LLColor4) + if (named_value_t::getPossibleValues()) + { + parser.inspectValue<std::string>(name_stack, min_count, max_count, named_value_t::getPossibleValues()); + } + + value_param.inspectBlock(parser, name_stack, min_count, max_count); + } + + void set(const container_t& val, bool flag_as_provided = true) + { + mValues = val; + setProvided(flag_as_provided); + } + + param_value_t& add() + { + mValues.push_back(value_t()); + setProvided(); + return mValues.back(); + } + + self_t& add(const value_t& item) + { + mValues.push_back(item); + setProvided(); + return *this; + } + + self_t& add(const typename named_value_t::name_t& name) + { + value_t value; + + // try to parse a per type named value + if (named_value_t::getValueFromName(name, value)) + { + add(value); + mValues.back().setValueName(name); + } + return *this; + } + + // implicit conversion + operator const container_t&() const { return mValues; } + // explicit conversion + const container_t& operator()() const { return mValues; } + + iterator begin() { return mValues.begin(); } + iterator end() { return mValues.end(); } + const_iterator begin() const { return mValues.begin(); } + const_iterator end() const { return mValues.end(); } + bool empty() const { return mValues.empty(); } + size_t size() const { return mValues.size(); } + + size_t numValidElements() const + { + size_t count = 0; + for (const_iterator it = mValues.begin(), end_it = mValues.end(); + it != end_it; + ++it) + { + if(it->isValid()) count++; + } + return count; + } + + protected: + + static bool mergeWith(Param& dst, const Param& src, bool overwrite) + { + const self_t& src_typed_param = static_cast<const self_t&>(src); + self_t& dst_typed_param = static_cast<self_t&>(dst); + + if (overwrite) + { + std::copy(src_typed_param.begin(), src_typed_param.end(), std::back_inserter(dst_typed_param.mValues)); + } + else + { + container_t new_values(src_typed_param.mValues); + std::copy(dst_typed_param.begin(), dst_typed_param.end(), std::back_inserter(new_values)); + std::swap(dst_typed_param.mValues, new_values); + } + + if (src_typed_param.begin() != src_typed_param.end()) + { + dst_typed_param.setProvided(); + } + + return true; + } + + container_t mValues; + size_t mMinCount, + mMaxCount; + + private: + void init( BlockDescriptor &block_descriptor, ParamDescriptor::validation_func_t validate_func, S32 min_count, S32 max_count, const char* name ) + { + ParamDescriptorPtr param_descriptor = ParamDescriptorPtr(new ParamDescriptor( + block_descriptor.mCurrentBlockPtr->getHandleFromParam(this), + &mergeWith, + &deserializeParam, + &serializeParam, + validate_func, + &inspectParam, + min_count, max_count)); + block_descriptor.addParam(param_descriptor, name); + } + }; + + template <typename DERIVED_BLOCK, typename BASE_BLOCK = BaseBlock> + class ChoiceBlock : public BASE_BLOCK + { + typedef ChoiceBlock<DERIVED_BLOCK, BASE_BLOCK> self_t; + typedef ChoiceBlock<DERIVED_BLOCK, BASE_BLOCK> enclosing_block_t; + typedef BASE_BLOCK base_block_t; + + LOG_CLASS(self_t); + public: + // take all provided params from other and apply to self + bool overwriteFrom(const self_t& other) + { + return static_cast<DERIVED_BLOCK*>(this)->mergeBlock(getBlockDescriptor(), other, true); + } + + // take all provided params that are not already provided, and apply to self + bool fillFrom(const self_t& other) + { + return static_cast<DERIVED_BLOCK*>(this)->mergeBlock(getBlockDescriptor(), other, false); + } + + bool mergeBlockParam(bool source_provided, bool dest_provided, BlockDescriptor& block_data, const self_t& source, bool overwrite) + { + bool source_override = source_provided && (overwrite || !dest_provided); + + if (source_override || source.mCurChoice == mCurChoice) + { + return mergeBlock(block_data, source, overwrite); + } + return false; + } + + // merge with other block + bool mergeBlock(BlockDescriptor& block_data, const self_t& other, bool overwrite) + { + mCurChoice = other.mCurChoice; + return base_block_t::mergeBlock(getBlockDescriptor(), other, overwrite); + } + + // clear out old choice when param has changed + /*virtual*/ void paramChanged(const Param& changed_param, bool user_provided) + { + param_handle_t changed_param_handle = base_block_t::getHandleFromParam(&changed_param); + // if we have a new choice... + if (changed_param_handle != mCurChoice) + { + // clear provided flag on previous choice + Param* previous_choice = base_block_t::getParamFromHandle(mCurChoice); + if (previous_choice) + { + previous_choice->setProvided(false); + } + mCurChoice = changed_param_handle; + } + base_block_t::paramChanged(changed_param, user_provided); + } + + virtual const BlockDescriptor& mostDerivedBlockDescriptor() const { return getBlockDescriptor(); } + virtual BlockDescriptor& mostDerivedBlockDescriptor() { return getBlockDescriptor(); } + + protected: + ChoiceBlock() + : mCurChoice(0) + { + BaseBlock::init(getBlockDescriptor(), base_block_t::getBlockDescriptor(), sizeof(DERIVED_BLOCK)); + } + + // Alternatives are mutually exclusive wrt other Alternatives in the same block. + // One alternative in a block will always have isChosen() == true. + // At most one alternative in a block will have isProvided() == true. + template <typename T, typename NAME_VALUE_LOOKUP = typename TypeValues<T>::type_value_t > + class Alternative : public TypedParam<T, NAME_VALUE_LOOKUP, false> + { + typedef TypedParam<T, NAME_VALUE_LOOKUP, false> super_t; + typedef typename super_t::value_t value_t; + typedef typename super_t::default_value_t default_value_t; + + public: + friend class ChoiceBlock<DERIVED_BLOCK>; + + using super_t::operator =; + + explicit Alternative(const char* name = "", const default_value_t& val = defaultValue<default_value_t>()) + : super_t(DERIVED_BLOCK::getBlockDescriptor(), name, val, NULL, 0, 1), + mOriginalValue(val) + { + // assign initial choice to first declared option + DERIVED_BLOCK* blockp = ((DERIVED_BLOCK*)DERIVED_BLOCK::getBlockDescriptor().mCurrentBlockPtr); + if (LL_UNLIKELY(DERIVED_BLOCK::getBlockDescriptor().mInitializationState == BlockDescriptor::INITIALIZING)) + { + if(blockp->mCurChoice == 0) + { + blockp->mCurChoice = Param::enclosingBlock().getHandleFromParam(this); + } + } + } + + void choose() + { + static_cast<enclosing_block_t&>(Param::enclosingBlock()).paramChanged(*this, true); + } + + void chooseAs(const value_t& val) + { + super_t::set(val); + } + + void operator =(const value_t& val) + { + super_t::set(val); + } + + void operator()(const value_t& val) + { + super_t::set(val); + } + + operator const value_t&() const + { + return (*this)(); + } + + const value_t& operator()() const + { + if (static_cast<enclosing_block_t&>(Param::enclosingBlock()).getCurrentChoice() == this) + { + return super_t::getValue(); + } + return mOriginalValue; + } + + bool isChosen() const + { + return static_cast<enclosing_block_t&>(Param::enclosingBlock()).getCurrentChoice() == this; + } + + private: + default_value_t mOriginalValue; + }; + + public: + static BlockDescriptor& getBlockDescriptor() + { + static BlockDescriptor sBlockDescriptor; + return sBlockDescriptor; + } + + private: + param_handle_t mCurChoice; + + const Param* getCurrentChoice() const + { + return base_block_t::getParamFromHandle(mCurChoice); + } + }; + + template <typename DERIVED_BLOCK, typename BASE_BLOCK = BaseBlock> + class Block + : public BASE_BLOCK + { + typedef Block<DERIVED_BLOCK, BASE_BLOCK> self_t; + + protected: + typedef Block<DERIVED_BLOCK, BASE_BLOCK> block_t; + + public: + typedef BASE_BLOCK base_block_t; + + // take all provided params from other and apply to self + bool overwriteFrom(const self_t& other) + { + return static_cast<DERIVED_BLOCK*>(this)->mergeBlock(getBlockDescriptor(), other, true); + } + + // take all provided params that are not already provided, and apply to self + bool fillFrom(const self_t& other) + { + return static_cast<DERIVED_BLOCK*>(this)->mergeBlock(getBlockDescriptor(), other, false); + } + + virtual const BlockDescriptor& mostDerivedBlockDescriptor() const { return getBlockDescriptor(); } + virtual BlockDescriptor& mostDerivedBlockDescriptor() { return getBlockDescriptor(); } + + protected: + Block() + { + //#pragma message("Parsing LLInitParam::Block") + BaseBlock::init(getBlockDescriptor(), BASE_BLOCK::getBlockDescriptor(), sizeof(DERIVED_BLOCK)); + } + + // + // Nested classes for declaring parameters + // + template <typename T, typename NAME_VALUE_LOOKUP = typename TypeValues<T>::type_value_t > + class Optional : public TypedParam<T, NAME_VALUE_LOOKUP, false> + { + typedef TypedParam<T, NAME_VALUE_LOOKUP, false> super_t; + typedef typename super_t::value_t value_t; + typedef typename super_t::default_value_t default_value_t; + + public: + using super_t::operator(); + using super_t::operator =; + + explicit Optional(const char* name = "", const default_value_t& val = defaultValue<default_value_t>()) + : super_t(DERIVED_BLOCK::getBlockDescriptor(), name, val, NULL, 0, 1) + { + //#pragma message("Parsing LLInitParam::Block::Optional") + } + + Optional& operator =(const value_t& val) + { + super_t::set(val); + return *this; + } + + DERIVED_BLOCK& operator()(const value_t& val) + { + super_t::set(val); + return static_cast<DERIVED_BLOCK&>(Param::enclosingBlock()); + } + }; + + template <typename T, typename NAME_VALUE_LOOKUP = typename TypeValues<T>::type_value_t > + class Mandatory : public TypedParam<T, NAME_VALUE_LOOKUP, false> + { + typedef TypedParam<T, NAME_VALUE_LOOKUP, false> super_t; + typedef Mandatory<T, NAME_VALUE_LOOKUP> self_t; + typedef typename super_t::value_t value_t; + typedef typename super_t::default_value_t default_value_t; + + public: + using super_t::operator(); + using super_t::operator =; + + // mandatory parameters require a name to be parseable + explicit Mandatory(const char* name = "", const default_value_t& val = defaultValue<default_value_t>()) + : super_t(DERIVED_BLOCK::getBlockDescriptor(), name, val, &validate, 1, 1) + {} + + Mandatory& operator =(const value_t& val) + { + super_t::set(val); + return *this; + } + + DERIVED_BLOCK& operator()(const value_t& val) + { + super_t::set(val); + return static_cast<DERIVED_BLOCK&>(Param::enclosingBlock()); + } + + static bool validate(const Param* p) + { + // valid only if provided + return static_cast<const self_t*>(p)->isProvided(); + } + + }; + + template <typename T, typename RANGE = BaseBlock::AnyAmount, typename NAME_VALUE_LOOKUP = typename TypeValues<T>::type_value_t > + class Multiple : public TypedParam<T, NAME_VALUE_LOOKUP, true> + { + typedef TypedParam<T, NAME_VALUE_LOOKUP, true> super_t; + typedef Multiple<T, RANGE, NAME_VALUE_LOOKUP> self_t; + typedef typename super_t::container_t container_t; + typedef typename super_t::value_t value_t; + + public: + typedef typename super_t::iterator iterator; + typedef typename super_t::const_iterator const_iterator; + + using super_t::operator(); + using super_t::operator const container_t&; + + explicit Multiple(const char* name = "") + : super_t(DERIVED_BLOCK::getBlockDescriptor(), name, container_t(), &validate, RANGE::minCount, RANGE::maxCount) + {} + + Multiple& operator =(const container_t& val) + { + super_t::set(val); + return *this; + } + + DERIVED_BLOCK& operator()(const container_t& val) + { + super_t::set(val); + return static_cast<DERIVED_BLOCK&>(Param::enclosingBlock()); + } + + static bool validate(const Param* paramp) + { + size_t num_valid = ((super_t*)paramp)->numValidElements(); + return RANGE::minCount <= num_valid && num_valid <= RANGE::maxCount; + } + }; + + // can appear in data files, but will ignored during parsing + // cannot read or write in code + class Ignored : public Param + { + public: + explicit Ignored(const char* name) + : Param(DERIVED_BLOCK::getBlockDescriptor().mCurrentBlockPtr) + { + BlockDescriptor& block_descriptor = DERIVED_BLOCK::getBlockDescriptor(); + if (LL_UNLIKELY(block_descriptor.mInitializationState == BlockDescriptor::INITIALIZING)) + { + ParamDescriptorPtr param_descriptor = ParamDescriptorPtr(new ParamDescriptor( + block_descriptor.mCurrentBlockPtr->getHandleFromParam(this), + NULL, + &deserializeParam, + NULL, + NULL, + NULL, + 0, S32_MAX)); + block_descriptor.addParam(param_descriptor, name); + } + } + + static bool deserializeParam(Param& param, Parser& parser, Parser::name_stack_range_t& name_stack_range, bool new_name) + { + if (name_stack_range.first == name_stack_range.second) + { + //std::string message = llformat("Deprecated value %s ignored", getName().c_str()); + //parser.parserWarning(message); + return true; + } + + return false; + } + }; + + // can appear in data files, or be written to in code, but data will be ignored + // cannot be read in code + class Deprecated : public Ignored + { + public: + explicit Deprecated(const char* name) : Ignored(name) {} + + // dummy writer interfaces + template<typename T> + Deprecated& operator =(const T& val) + { + // do nothing + return *this; + } + + template<typename T> + DERIVED_BLOCK& operator()(const T& val) + { + // do nothing + return static_cast<DERIVED_BLOCK&>(Param::enclosingBlock()); + } + + template<typename T> + void set(const T& val, bool flag_as_provided = true) + { + // do nothing + } + }; + + public: + static BlockDescriptor& getBlockDescriptor() + { + static BlockDescriptor sBlockDescriptor; + return sBlockDescriptor; + } + + protected: + template <typename T, typename NAME_VALUE_LOOKUP, bool multiple, typename is_block> + void changeDefault(TypedParam<T, NAME_VALUE_LOOKUP, multiple, is_block>& param, + const typename TypedParam<T, NAME_VALUE_LOOKUP, multiple, is_block>::value_t& value) + { + if (!param.isProvided()) + { + param.set(value, false); + } + } + + }; + + template<typename T, typename BLOCK_T> + struct IsBlock<ParamValue<BaseBlock::Lazy<T, BaseBlock::IS_A_BLOCK>, BLOCK_T >, void> + { + typedef IS_A_BLOCK value_t; + }; + + template<typename T, typename BLOCK_T> + struct IsBlock<ParamValue<BaseBlock::Lazy<T, BaseBlock::NOT_A_BLOCK>, BLOCK_T >, void> + { + typedef NOT_BLOCK value_t; + }; + + template<typename T, typename BLOCK_IDENTIFIER> + struct IsBlock<ParamValue<BaseBlock::Atomic<T>, typename IsBlock<BaseBlock::Atomic<T> >::value_t >, BLOCK_IDENTIFIER> + { + typedef typename IsBlock<T>::value_t value_t; + }; + + template<typename T, typename BLOCK_IDENTIFIER> + struct IsBlock<ParamValue<BaseBlock::Sequential<T>, typename IsBlock<BaseBlock::Sequential<T> >::value_t >, BLOCK_IDENTIFIER> + { + typedef typename IsBlock<T>::value_t value_t; + }; + + + template<typename T> + struct InnerMostType + { + typedef T value_t; + }; + + template<typename T> + struct InnerMostType<ParamValue<T, NOT_BLOCK> > + { + typedef typename InnerMostType<T>::value_t value_t; + }; + + template<typename T> + struct InnerMostType<ParamValue<T, IS_A_BLOCK> > + { + typedef typename InnerMostType<T>::value_t value_t; + }; + + template<typename T, typename BLOCK_T> + class ParamValue <BaseBlock::Atomic<T>, BLOCK_T> + { + typedef ParamValue <BaseBlock::Atomic<T>, BLOCK_T> self_t; + + public: + typedef typename InnerMostType<T>::value_t value_t; + typedef T default_value_t; + + ParamValue() + : mValue() + {} + + ParamValue(const default_value_t& value) + : mValue(value) + {} + + void setValue(const value_t& val) + { + mValue.setValue(val); + } + + const value_t& getValue() const + { + return mValue.getValue(); + } + + value_t& getValue() + { + return mValue.getValue(); + } + + bool deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack_range, bool new_name) + { + if (new_name) + { + resetToDefault(); + } + return mValue.deserializeBlock(p, name_stack_range, new_name); + } + + bool serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const self_t* diff_block = NULL) const + { + const BaseBlock* base_block = diff_block + ? &(diff_block->mValue) + : NULL; + return mValue.serializeBlock(p, name_stack, predicate_rule, base_block); + } + + bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const + { + return mValue.inspectBlock(p, name_stack, min_count, max_count); + } + + bool mergeBlockParam(bool source_provided, bool dst_provided, BlockDescriptor& block_data, const self_t& source, bool overwrite) + { + if ((overwrite && source_provided) // new values coming in on top or... + || (!overwrite && !dst_provided)) // values being pushed under with nothing already there + { + // clear away what is there and take the new stuff as a whole + resetToDefault(); + return mValue.mergeBlock(block_data, source.getValue(), overwrite); + } + return mValue.mergeBlock(block_data, source.getValue(), overwrite); + } + + bool validateBlock(bool emit_errors = true) const + { + return mValue.validateBlock(emit_errors); + } + + bool isValid() const + { + return validateBlock(false); + } + + static BlockDescriptor& getBlockDescriptor() + { + return value_t::getBlockDescriptor(); + } + + + private: + void resetToDefault() + { + static T default_value; + mValue = default_value; + } + + T mValue; + }; + + template<typename T> + class ParamValue <BaseBlock::Sequential<T>, IS_A_BLOCK> + { + typedef ParamValue <BaseBlock::Sequential<T>, IS_A_BLOCK> self_t; + + public: + typedef typename InnerMostType<T>::value_t value_t; + typedef T default_value_t; + + ParamValue() + : mValue() + { + mCurParam = getBlockDescriptor().mAllParams.begin(); + } + + ParamValue(const default_value_t& value) + : mValue(value) + { + mCurParam = getBlockDescriptor().mAllParams.begin(); + } + + void setValue(const value_t& val) + { + mValue.setValue(val); + } + + const value_t& getValue() const + { + return mValue.getValue(); + } + + value_t& getValue() + { + return mValue.getValue(); + } + + bool deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack_range, bool new_name) + { + if (new_name) + { + mCurParam = getBlockDescriptor().mAllParams.begin(); + } + if (name_stack_range.first == name_stack_range.second + && mCurParam != getBlockDescriptor().mAllParams.end()) + { + // deserialize to mCurParam + ParamDescriptor& pd = *(*mCurParam); + ParamDescriptor::deserialize_func_t deserialize_func = pd.mDeserializeFunc; + Param* paramp = mValue.getParamFromHandle(pd.mParamHandle); + + if (deserialize_func + && paramp + && deserialize_func(*paramp, p, name_stack_range, new_name)) + { + ++mCurParam; + return true; + } + else + { + return false; + } + } + else + { + return mValue.deserializeBlock(p, name_stack_range, new_name); + } + } + + bool serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const self_t* diff_block = NULL) const + { + const BaseBlock* base_block = diff_block + ? &(diff_block->mValue) + : NULL; + return mValue.serializeBlock(p, name_stack, predicate_rule, base_block); + } + + bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const + { + return mValue.inspectBlock(p, name_stack, min_count, max_count); + } + + bool mergeBlockParam(bool source_provided, bool dst_provided, BlockDescriptor& block_data, const self_t& source, bool overwrite) + { + return mValue.mergeBlock(block_data, source.getValue(), overwrite); + } + + bool validateBlock(bool emit_errors = true) const + { + return mValue.validateBlock(emit_errors); + } + + bool isValid() const + { + return validateBlock(false); + } + + static BlockDescriptor& getBlockDescriptor() + { + return value_t::getBlockDescriptor(); + } + + private: + + BlockDescriptor::all_params_list_t::iterator mCurParam; + T mValue; + }; + + template<typename T> + class ParamValue <BaseBlock::Sequential<T>, NOT_BLOCK> + : public T + { + typedef ParamValue <BaseBlock::Sequential<T>, NOT_BLOCK> self_t; + + public: + typedef typename InnerMostType<T>::value_t value_t; + typedef T default_value_t; + + ParamValue() + : T() + {} + + ParamValue(const default_value_t& value) + : T(value.getValue()) + {} + + bool isValid() const { return true; } + }; + + template<typename T, typename BLOCK_T> + class ParamValue <BaseBlock::Lazy<T, IS_A_BLOCK>, BLOCK_T> + { + typedef ParamValue <BaseBlock::Lazy<T, IS_A_BLOCK>, BLOCK_T> self_t; + + public: + typedef typename InnerMostType<T>::value_t value_t; + typedef LazyValue<T> default_value_t; + + ParamValue() + : mValue() + {} + + ParamValue(const default_value_t& other) + : mValue(other) + {} + + ParamValue(const T& value) + : mValue(value) + {} + + void setValue(const value_t& val) + { + mValue.set(val); + } + + const value_t& getValue() const + { + return mValue.get().getValue(); + } + + value_t& getValue() + { + return mValue.get().getValue(); + } + + bool deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack_range, bool new_name) + { + return mValue.get().deserializeBlock(p, name_stack_range, new_name); + } + + bool serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const self_t* diff_block = NULL) const + { + if (mValue.empty()) return false; + + const BaseBlock* base_block = (diff_block && !diff_block->mValue.empty()) + ? &(diff_block->mValue.get().getValue()) + : NULL; + return mValue.get().serializeBlock(p, name_stack, predicate_rule, base_block); + } + + bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const + { + return mValue.get().inspectBlock(p, name_stack, min_count, max_count); + } + + bool mergeBlockParam(bool source_provided, bool dst_provided, BlockDescriptor& block_data, const self_t& source, bool overwrite) + { + return source.mValue.empty() || mValue.get().mergeBlock(block_data, source.getValue(), overwrite); + } + + bool validateBlock(bool emit_errors = true) const + { + return mValue.empty() || mValue.get().validateBlock(emit_errors); + } + + bool isValid() const + { + return validateBlock(false); + } + + static BlockDescriptor& getBlockDescriptor() + { + return value_t::getBlockDescriptor(); + } + + private: + LazyValue<T> mValue; + }; + + template<typename T, typename BLOCK_T> + class ParamValue <BaseBlock::Lazy<T, NOT_BLOCK>, BLOCK_T> + { + typedef ParamValue <BaseBlock::Lazy<T, NOT_BLOCK>, BLOCK_T> self_t; + + public: + typedef typename InnerMostType<T>::value_t value_t; + typedef LazyValue<T> default_value_t; + + ParamValue() + : mValue() + {} + + ParamValue(const default_value_t& other) + : mValue(other) + {} + + ParamValue(const T& value) + : mValue(value) + {} + + void setValue(const value_t& val) + { + mValue.set(val); + } + + const value_t& getValue() const + { + return mValue.get().getValue(); + } + + value_t& getValue() + { + return mValue.get().getValue(); + } + + bool isValid() const + { + return true; + } + + private: + LazyValue<T> mValue; + }; + + template <> + class ParamValue <LLSD, NOT_BLOCK> + : public BaseBlock + { + public: + typedef LLSD value_t; + typedef LLSD default_value_t; + + ParamValue() + {} + + ParamValue(const default_value_t& other) + : mValue(other) + {} + + void setValue(const value_t& val) { mValue = val; } + + const value_t& getValue() const { return mValue; } + LLSD& getValue() { return mValue; } + + // block param interface + LL_COMMON_API bool deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack_range, bool new_name); + LL_COMMON_API bool serializeBlock(Parser& p, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const BaseBlock* diff_block = NULL) const; + bool inspectBlock(Parser& p, Parser::name_stack_t name_stack = Parser::name_stack_t(), S32 min_count = 0, S32 max_count = S32_MAX) const + { + //TODO: implement LLSD params as schema type Any + return true; + } + + private: + static void serializeElement(Parser& p, const LLSD& sd, Parser::name_stack_t& name_stack); + + LLSD mValue; + }; + + template<typename T> + class CustomParamValue + : public Block<ParamValue<T> > + { + public: + typedef enum e_value_age + { + VALUE_NEEDS_UPDATE, // mValue needs to be refreshed from the block parameters + VALUE_AUTHORITATIVE, // mValue holds the authoritative value (which has been replicated to the block parameters via updateBlockFromValue) + BLOCK_AUTHORITATIVE // mValue is derived from the block parameters, which are authoritative + } EValueAge; + + typedef TypeValues<T> derived_t; + typedef CustomParamValue<T> self_t; + typedef Block<ParamValue<T> > block_t; + typedef T default_value_t; + typedef T value_t; + typedef void baseblock_base_class_t; + + + CustomParamValue(const default_value_t& value = T()) + : mValue(value), + mValueAge(VALUE_AUTHORITATIVE) + {} + + bool deserializeBlock(Parser& parser, Parser::name_stack_range_t& name_stack_range, bool new_name) + { + derived_t& typed_param = static_cast<derived_t&>(*this); + // try to parse direct value T + if (name_stack_range.first == name_stack_range.second) + { + if(parser.readValue(typed_param.mValue)) + { + typed_param.mValueAge = VALUE_AUTHORITATIVE; + typed_param.updateBlockFromValue(false); + + return true; + } + } + + // fall back on parsing block components for T + return typed_param.BaseBlock::deserializeBlock(parser, name_stack_range, new_name); + } + + bool serializeBlock(Parser& parser, Parser::name_stack_t& name_stack, const predicate_rule_t predicate_rule, const BaseBlock* diff_block = NULL) const + { + const derived_t& typed_param = static_cast<const derived_t&>(*this); + const derived_t* diff_param = static_cast<const derived_t*>(diff_block); + + //std::string key = typed_param.getValueName(); + + //// first try to write out name of name/value pair + //if (!key.empty()) + //{ + // if (!diff_param || !ParamCompare<std::string>::equals(diff_param->getValueName(), key)) + // { + // return parser.writeValue(key, name_stack); + // } + //} + // then try to serialize value directly + if (!diff_param || !ParamCompare<T>::equals(typed_param.getValue(), diff_param->getValue())) + { + + if (parser.writeValue(typed_param.getValue(), name_stack)) + { + return true; + } + else + { + //RN: *always* serialize provided components of BlockValue (don't pass diff_param on), + // since these tend to be viewed as the constructor arguments for the value T. It seems + // cleaner to treat the uniqueness of a BlockValue according to the generated value, and + // not the individual components. This way <color red="0" green="1" blue="0"/> will not + // be exported as <color green="1"/>, since it was probably the intent of the user to + // be specific about the RGB color values. This also fixes an issue where we distinguish + // between rect.left not being provided and rect.left being explicitly set to 0 (same as default) + + if (typed_param.mValueAge == VALUE_AUTHORITATIVE) + { + // if the value is authoritative but the parser doesn't accept the value type + // go ahead and make a copy, and splat the value out to its component params + // and serialize those params + derived_t copy(typed_param); + copy.updateBlockFromValue(true); + return copy.block_t::serializeBlock(parser, name_stack, predicate_rule, NULL); + } + else + { + return block_t::serializeBlock(parser, name_stack, predicate_rule, NULL); + } + } + } + return false; + } + + bool validateBlock(bool emit_errors = true) const + { + if (mValueAge == VALUE_NEEDS_UPDATE) + { + if (block_t::validateBlock(emit_errors)) + { + // clear stale keyword associated with old value + mValueAge = BLOCK_AUTHORITATIVE; + static_cast<derived_t*>(const_cast<self_t*>(this))->updateValueFromBlock(); + return true; + } + else + { + //block value incomplete, so not considered provided + // will attempt to revalidate on next call to isProvided() + return false; + } + } + else + { + // we have a valid value in hand + return true; + } + } + + // propagate change status up to enclosing block + /*virtual*/ void paramChanged(const Param& changed_param, bool user_provided) + { + BaseBlock::paramChanged(changed_param, user_provided); + if (user_provided) + { + // a parameter changed, so our value is out of date + mValueAge = VALUE_NEEDS_UPDATE; + } + } + + void setValue(const value_t& val) + { + // set param version number to be up to date, so we ignore block contents + mValueAge = VALUE_AUTHORITATIVE; + mValue = val; + static_cast<derived_t*>(this)->updateBlockFromValue(false); + } + + const value_t& getValue() const + { + validateBlock(true); + return mValue; + } + + T& getValue() + { + validateBlock(true); + return mValue; + } + + protected: + + // use this from within updateValueFromBlock() to set the value without making it authoritative + void updateValue(const value_t& value) + { + mValue = value; + } + + bool mergeBlockParam(bool source_provided, bool dst_provided, BlockDescriptor& block_data, const BaseBlock& source, bool overwrite) + { + bool source_override = source_provided && (overwrite || !dst_provided); + + const derived_t& src_typed_param = static_cast<const derived_t&>(source); + + if (source_override && src_typed_param.mValueAge == VALUE_AUTHORITATIVE) + { + // copy value over + setValue(src_typed_param.getValue()); + return true; + } + // merge individual parameters into destination + if (mValueAge == VALUE_AUTHORITATIVE) + { + static_cast<derived_t*>(this)->updateBlockFromValue(dst_provided); + } + return mergeBlock(block_data, source, overwrite); + } + + bool mergeBlock(BlockDescriptor& block_data, const BaseBlock& source, bool overwrite) + { + return block_t::mergeBlock(block_data, source, overwrite); + } + + private: + mutable T mValue; + mutable EValueAge mValueAge; + }; } diff --git a/indra/llcommon/llinstancetracker.cpp b/indra/llcommon/llinstancetracker.cpp index e7193b70b5..d1b02066bd 100644 --- a/indra/llcommon/llinstancetracker.cpp +++ b/indra/llcommon/llinstancetracker.cpp @@ -1,24 +1,24 @@ /** * @file lllinstancetracker.cpp - * + * * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llcommon/llinstancetracker.h b/indra/llcommon/llinstancetracker.h index 27422e1266..3232a0e219 100644 --- a/indra/llcommon/llinstancetracker.h +++ b/indra/llcommon/llinstancetracker.h @@ -1,4 +1,4 @@ -/** +/** * @file llinstancetracker.h * @brief LLInstanceTracker is a mixin class that automatically tracks object * instances with or without an associated key @@ -6,21 +6,21 @@ * $LicenseInfo:firstyear=2000&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -99,11 +99,11 @@ public: return mSelf; } - static size_t instanceCount() - { - return LockStatic()->mMap.size(); + static size_t instanceCount() + { + return LockStatic()->mMap.size(); } - + // snapshot of std::pair<const KEY, std::shared_ptr<SUBCLASS>> pairs, for // some SUBCLASS derived from T template <typename SUBCLASS> @@ -243,7 +243,7 @@ public: } protected: - LLInstanceTracker(const KEY& key) + LLInstanceTracker(const KEY& key) { // We do not intend to manage the lifespan of this object with // shared_ptr, so give it a no-op deleter. We store shared_ptrs in our @@ -286,9 +286,9 @@ private: static std::string report(const char* key) { return report(std::string(key)); } // caller must instantiate LockStatic - void add_(LockStatic& lock, const KEY& key, const ptr_t& ptr) - { - mInstanceKey = key; + void add_(LockStatic& lock, const KEY& key, const ptr_t& ptr) + { + mInstanceKey = key; InstanceMap& map = lock->mMap; switch(KEY_COLLISION_BEHAVIOR) { @@ -373,7 +373,7 @@ public: { return mSelf; } - + static size_t instanceCount() { return LockStatic()->mSet.size(); diff --git a/indra/llcommon/llkeybind.cpp b/indra/llcommon/llkeybind.cpp index b89160cc55..e36c1d0a4c 100644 --- a/indra/llcommon/llkeybind.cpp +++ b/indra/llcommon/llkeybind.cpp @@ -1,25 +1,25 @@ -/** +/** * @file llkeybind.cpp * @brief Information about key combinations. * * $LicenseInfo:firstyear=2019&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2019, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -207,7 +207,7 @@ bool LLKeyBind::operator!=(const LLKeyBind& rhs) bool LLKeyBind::isEmpty() const { - for (const LLKeyData& key_data : mData) + for (const LLKeyData& key_data : mData) { if (!key_data.isEmpty()) return false; } @@ -225,7 +225,7 @@ LLKeyBind::data_vector_t::const_iterator LLKeyBind::endNonEmpty() const LLSD LLKeyBind::asLLSD() const { LLSD data; - for (const LLKeyData& key_data : mData) + for (const LLKeyData& key_data : mData) { // append intermediate entries even if empty to not affect visual // representation @@ -242,7 +242,7 @@ bool LLKeyBind::canHandle(EMouseClickType mouse, KEY key, MASK mask) const return false; } - for (const LLKeyData& key_data : mData) + for (const LLKeyData& key_data : mData) { if (key_data.canHandle(mouse, key, mask)) { @@ -266,7 +266,7 @@ bool LLKeyBind::hasKeyData(EMouseClickType mouse, KEY key, MASK mask, bool ignor { if (mouse != CLICK_NONE || key != KEY_NONE) { - for (const LLKeyData& key_data : mData) + for (const LLKeyData& key_data : mData) { if (key_data.mKey == key && key_data.mMask == mask @@ -353,7 +353,7 @@ void LLKeyBind::replaceKeyData(const LLKeyData& data, U32 index) { // if both click and key are none (isEmpty()), we are inserting a placeholder, we don't want to reset anything // otherwise reset identical key - for (LLKeyData& key_data : mData) + for (LLKeyData& key_data : mData) { if (key_data.mKey == data.mKey && key_data.mMouse == data.mMouse diff --git a/indra/llcommon/llkeybind.h b/indra/llcommon/llkeybind.h index 488f509411..1bbb2fadb5 100644 --- a/indra/llcommon/llkeybind.h +++ b/indra/llcommon/llkeybind.h @@ -1,25 +1,25 @@ -/** +/** * @file llkeybind.h * @brief Information about key combinations. * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -54,7 +54,7 @@ public: KEY mKey; MASK mMask; // Either to expect exact match or ignore not expected masks as long as expected mask-bit is present - bool mIgnoreMasks; + bool mIgnoreMasks; }; // One function can bind to multiple Key options diff --git a/indra/llcommon/llkeythrottle.h b/indra/llcommon/llkeythrottle.h index 18befe65ae..0909acb747 100644 --- a/indra/llcommon/llkeythrottle.h +++ b/indra/llcommon/llkeythrottle.h @@ -1,331 +1,331 @@ -/** - * @file llkeythrottle.h - * - * $LicenseInfo:firstyear=2005&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLKEY_THROTTLE_H -#define LL_LLKEY_THROTTLE_H - -// LLKeyThrottle keeps track of the number of action occurences with a key value -// for a type over a given time period. If the rate set in the constructor is -// exceeed, the key is considered blocked. The transition from unblocked to -// blocked is noted so the responsible agent can be informed. This transition -// takes twice the look back window to clear. - -#include "linden_common.h" - -#include "llframetimer.h" -#include <map> - - -// forward declaration so LLKeyThrottleImpl can befriend it -template <class T> class LLKeyThrottle; - - -// Implementation utility class - use LLKeyThrottle, not this -template <class T> -class LLKeyThrottleImpl -{ - friend class LLKeyThrottle<T>; -protected: - struct Entry { - U32 count; - bool blocked; - - Entry() : count(0), blocked(false) { } - }; - - typedef std::map<T, Entry> EntryMap; - - EntryMap* prevMap; - EntryMap* currMap; - - U32 countLimit; - // maximum number of keys allowed per interval - - U64 intervalLength; // each map covers this time period (usec or frame number) - U64 startTime; // start of the time period (usec or frame number) - // currMap started counting at this time - // prevMap covers the previous interval - - LLKeyThrottleImpl() : - prevMap(NULL), - currMap(NULL), - countLimit(0), - intervalLength(1), - startTime(0) - {} - - static U64 getTime() - { - return LLFrameTimer::getTotalTime(); - } - static U64 getFrame() // Return the current frame number - { - return (U64) LLFrameTimer::getFrameCount(); - } -}; - - -template< class T > -class LLKeyThrottle -{ -public: - // @param realtime = false for frame-based throttle, true for usec - // real-time throttle - LLKeyThrottle(U32 limit, F32 interval, bool realtime = true) - : m(* new LLKeyThrottleImpl<T>) - { - setParameters( limit, interval, realtime ); - } - - ~LLKeyThrottle() - { - delete m.prevMap; - delete m.currMap; - delete &m; - } - - enum State { - THROTTLE_OK, // rate not exceeded, let pass - THROTTLE_NEWLY_BLOCKED, // rate exceed for the first time - 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) - { - 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; - bool prevBlocked = false; - - typename LLKeyThrottleImpl<T>::EntryMap::const_iterator prev = m.prevMap->find(id); - if (prev != m.prevMap->end()) - { - prevCount = prev->second.count; - prevBlocked = prev->second.blocked; - } - - typename LLKeyThrottleImpl<T>::Entry& curr = (*m.currMap)[id]; - - bool wereBlocked = curr.blocked || prevBlocked; - - curr.count += weight; - - // 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); - - curr.blocked |= averageCount > m.countLimit; - - bool nowBlocked = curr.blocked || prevBlocked; - - if (!nowBlocked) - { - return THROTTLE_OK; - } - else if (!wereBlocked) - { - return THROTTLE_NEWLY_BLOCKED; - } - else - { - return THROTTLE_BLOCKED; - } - } - - // call to force throttle conditions for id - void throttleAction(const T& id) - { - noteAction(id); - typename LLKeyThrottleImpl<T>::Entry& curr = (*m.currMap)[id]; - curr.count = llmax(m.countLimit, curr.count); - curr.blocked = true; - } - - // returns true if key is blocked - bool isThrottled(const T& id) const - { - if (m.currMap->empty() - && m.prevMap->empty()) - { - // most of the time we'll fall in here - return false; - } - - // NOTE, we ignore the case where id is in the map but the map is stale. - // You might think that we'd stop throttling things in such a case, - // however it may be that a god has disabled scripts in the region or - // estate --> we probably want to report the state of the id when the - // scripting engine was paused. - typename LLKeyThrottleImpl<T>::EntryMap::const_iterator entry = m.currMap->find(id); - if (entry != m.currMap->end()) - { - return entry->second.blocked; - } - entry = m.prevMap->find(id); - if (entry != m.prevMap->end()) - { - return entry->second.blocked; - } - return false; - } - - // Get the throttling parameters - void getParameters( U32 & out_limit, F32 & out_interval, bool & out_realtime ) - { - out_limit = m.countLimit; - out_interval = m.intervalLength; - out_realtime = mIsRealtime; - } - - // Set the throttling behavior - void setParameters( U32 limit, F32 interval, bool realtime = true ) - { - // limit is the maximum number of keys - // allowed per interval (in seconds or frames) - mIsRealtime = realtime; - m.countLimit = limit; - if ( mIsRealtime ) - { - m.intervalLength = (U64)(interval * USEC_PER_SEC); - m.startTime = LLKeyThrottleImpl<T>::getTime(); - } - else - { - m.intervalLength = (U64)interval; - m.startTime = LLKeyThrottleImpl<T>::getFrame(); - } - - if ( m.intervalLength == 0 ) - { // Don't allow zero intervals - m.intervalLength = 1; - } - - delete m.prevMap; - m.prevMap = new typename LLKeyThrottleImpl<T>::EntryMap; - delete m.currMap; - m.currMap = new typename LLKeyThrottleImpl<T>::EntryMap; - } - -protected: - LLKeyThrottleImpl<T>& m; - bool mIsRealtime; // true to be time based (default), false for frame based -}; - -#endif +/**
+ * @file llkeythrottle.h
+ *
+ * $LicenseInfo:firstyear=2005&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLKEY_THROTTLE_H
+#define LL_LLKEY_THROTTLE_H
+
+// LLKeyThrottle keeps track of the number of action occurences with a key value
+// for a type over a given time period. If the rate set in the constructor is
+// exceeed, the key is considered blocked. The transition from unblocked to
+// blocked is noted so the responsible agent can be informed. This transition
+// takes twice the look back window to clear.
+
+#include "linden_common.h"
+
+#include "llframetimer.h"
+#include <map>
+
+
+// forward declaration so LLKeyThrottleImpl can befriend it
+template <class T> class LLKeyThrottle;
+
+
+// Implementation utility class - use LLKeyThrottle, not this
+template <class T>
+class LLKeyThrottleImpl
+{
+ friend class LLKeyThrottle<T>;
+protected:
+ struct Entry {
+ U32 count;
+ bool blocked;
+
+ Entry() : count(0), blocked(false) { }
+ };
+
+ typedef std::map<T, Entry> EntryMap;
+
+ EntryMap* prevMap;
+ EntryMap* currMap;
+
+ U32 countLimit;
+ // maximum number of keys allowed per interval
+
+ U64 intervalLength; // each map covers this time period (usec or frame number)
+ U64 startTime; // start of the time period (usec or frame number)
+ // currMap started counting at this time
+ // prevMap covers the previous interval
+
+ LLKeyThrottleImpl() :
+ prevMap(NULL),
+ currMap(NULL),
+ countLimit(0),
+ intervalLength(1),
+ startTime(0)
+ {}
+
+ static U64 getTime()
+ {
+ return LLFrameTimer::getTotalTime();
+ }
+ static U64 getFrame() // Return the current frame number
+ {
+ return (U64) LLFrameTimer::getFrameCount();
+ }
+};
+
+
+template< class T >
+class LLKeyThrottle
+{
+public:
+ // @param realtime = false for frame-based throttle, true for usec
+ // real-time throttle
+ LLKeyThrottle(U32 limit, F32 interval, bool realtime = true)
+ : m(* new LLKeyThrottleImpl<T>)
+ {
+ setParameters( limit, interval, realtime );
+ }
+
+ ~LLKeyThrottle()
+ {
+ delete m.prevMap;
+ delete m.currMap;
+ delete &m;
+ }
+
+ enum State {
+ THROTTLE_OK, // rate not exceeded, let pass
+ THROTTLE_NEWLY_BLOCKED, // rate exceed for the first time
+ 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)
+ {
+ 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;
+ bool prevBlocked = false;
+
+ typename LLKeyThrottleImpl<T>::EntryMap::const_iterator prev = m.prevMap->find(id);
+ if (prev != m.prevMap->end())
+ {
+ prevCount = prev->second.count;
+ prevBlocked = prev->second.blocked;
+ }
+
+ typename LLKeyThrottleImpl<T>::Entry& curr = (*m.currMap)[id];
+
+ bool wereBlocked = curr.blocked || prevBlocked;
+
+ curr.count += weight;
+
+ // 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);
+
+ curr.blocked |= averageCount > m.countLimit;
+
+ bool nowBlocked = curr.blocked || prevBlocked;
+
+ if (!nowBlocked)
+ {
+ return THROTTLE_OK;
+ }
+ else if (!wereBlocked)
+ {
+ return THROTTLE_NEWLY_BLOCKED;
+ }
+ else
+ {
+ return THROTTLE_BLOCKED;
+ }
+ }
+
+ // call to force throttle conditions for id
+ void throttleAction(const T& id)
+ {
+ noteAction(id);
+ typename LLKeyThrottleImpl<T>::Entry& curr = (*m.currMap)[id];
+ curr.count = llmax(m.countLimit, curr.count);
+ curr.blocked = true;
+ }
+
+ // returns true if key is blocked
+ bool isThrottled(const T& id) const
+ {
+ if (m.currMap->empty()
+ && m.prevMap->empty())
+ {
+ // most of the time we'll fall in here
+ return false;
+ }
+
+ // NOTE, we ignore the case where id is in the map but the map is stale.
+ // You might think that we'd stop throttling things in such a case,
+ // however it may be that a god has disabled scripts in the region or
+ // estate --> we probably want to report the state of the id when the
+ // scripting engine was paused.
+ typename LLKeyThrottleImpl<T>::EntryMap::const_iterator entry = m.currMap->find(id);
+ if (entry != m.currMap->end())
+ {
+ return entry->second.blocked;
+ }
+ entry = m.prevMap->find(id);
+ if (entry != m.prevMap->end())
+ {
+ return entry->second.blocked;
+ }
+ return false;
+ }
+
+ // Get the throttling parameters
+ void getParameters( U32 & out_limit, F32 & out_interval, bool & out_realtime )
+ {
+ out_limit = m.countLimit;
+ out_interval = m.intervalLength;
+ out_realtime = mIsRealtime;
+ }
+
+ // Set the throttling behavior
+ void setParameters( U32 limit, F32 interval, bool realtime = true )
+ {
+ // limit is the maximum number of keys
+ // allowed per interval (in seconds or frames)
+ mIsRealtime = realtime;
+ m.countLimit = limit;
+ if ( mIsRealtime )
+ {
+ m.intervalLength = (U64)(interval * USEC_PER_SEC);
+ m.startTime = LLKeyThrottleImpl<T>::getTime();
+ }
+ else
+ {
+ m.intervalLength = (U64)interval;
+ m.startTime = LLKeyThrottleImpl<T>::getFrame();
+ }
+
+ if ( m.intervalLength == 0 )
+ { // Don't allow zero intervals
+ m.intervalLength = 1;
+ }
+
+ delete m.prevMap;
+ m.prevMap = new typename LLKeyThrottleImpl<T>::EntryMap;
+ delete m.currMap;
+ m.currMap = new typename LLKeyThrottleImpl<T>::EntryMap;
+ }
+
+protected:
+ LLKeyThrottleImpl<T>& m;
+ bool mIsRealtime; // true to be time based (default), false for frame based
+};
+
+#endif
diff --git a/indra/llcommon/llkeyusetracker.h b/indra/llcommon/llkeyusetracker.h index 1fb222dd40..790c7b713b 100644 --- a/indra/llcommon/llkeyusetracker.h +++ b/indra/llcommon/llkeyusetracker.h @@ -1,25 +1,25 @@ -/** +/** * @file llkeyusetracker.h * @brief Declaration of the LLKeyUseTracker class. * * $LicenseInfo:firstyear=2003&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -40,176 +40,176 @@ template <class TKey, class TData> class KeyUseTrackerNodeImpl { public: - U64 mLastUse; - U32 mUseCount; - TKey mKey; - TData mData; - - KeyUseTrackerNodeImpl( TKey k, TData d ) : - mLastUse(0), - mUseCount(0), - mKey( k ), - mData( d ) - { - } + U64 mLastUse; + U32 mUseCount; + TKey mKey; + TData mData; + + KeyUseTrackerNodeImpl( TKey k, TData d ) : + mLastUse(0), + mUseCount(0), + mKey( k ), + mData( d ) + { + } }; template <class TKey, class TData> class LLKeyUseTracker { - typedef KeyUseTrackerNodeImpl<TKey,TData> TKeyUseTrackerNode; - typedef std::list<TKeyUseTrackerNode *> TKeyList; - TKeyList mKeyList; - U64 mMemUsecs; - U64 mLastExpire; - U32 mMaxCount; - U32 mCount; - - static U64 getTime() - { - // This function operates on a frame basis, not instantaneous. - // We can rely on its output for determining first use in a - // frame. - return LLFrameTimer::getTotalTime(); - } - - void ageKeys() - { - U64 now = getTime(); - if( now == mLastExpire ) - { - return; - } - mLastExpire = now; - - while( !mKeyList.empty() && (now - mKeyList.front()->mLastUse > mMemUsecs ) ) - { - delete mKeyList.front(); - mKeyList.pop_front(); - mCount--; - } - } - - TKeyUseTrackerNode *findNode( TKey key ) - { - ageKeys(); - - typename TKeyList::iterator i; - for( i = mKeyList.begin(); i != mKeyList.end(); i++ ) - { - if( (*i)->mKey == key ) - return *i; - } - - return NULL; - } - - TKeyUseTrackerNode *removeNode( TKey key ) - { - TKeyUseTrackerNode *i; - i = findNode( key ); - if( i ) - { - mKeyList.remove( i ); - mCount--; - return i; - } - - return NULL; - } + typedef KeyUseTrackerNodeImpl<TKey,TData> TKeyUseTrackerNode; + typedef std::list<TKeyUseTrackerNode *> TKeyList; + TKeyList mKeyList; + U64 mMemUsecs; + U64 mLastExpire; + U32 mMaxCount; + U32 mCount; + + static U64 getTime() + { + // This function operates on a frame basis, not instantaneous. + // We can rely on its output for determining first use in a + // frame. + return LLFrameTimer::getTotalTime(); + } + + void ageKeys() + { + U64 now = getTime(); + if( now == mLastExpire ) + { + return; + } + mLastExpire = now; + + while( !mKeyList.empty() && (now - mKeyList.front()->mLastUse > mMemUsecs ) ) + { + delete mKeyList.front(); + mKeyList.pop_front(); + mCount--; + } + } + + TKeyUseTrackerNode *findNode( TKey key ) + { + ageKeys(); + + typename TKeyList::iterator i; + for( i = mKeyList.begin(); i != mKeyList.end(); i++ ) + { + if( (*i)->mKey == key ) + return *i; + } + + return NULL; + } + + TKeyUseTrackerNode *removeNode( TKey key ) + { + TKeyUseTrackerNode *i; + i = findNode( key ); + if( i ) + { + mKeyList.remove( i ); + mCount--; + return i; + } + + return NULL; + } public: - LLKeyUseTracker( U32 memory_seconds, U32 max_count ) : - mLastExpire(0), - mMaxCount( max_count ), - mCount(0) - { - mMemUsecs = ((U64)memory_seconds) * 1000000; - } - - ~LLKeyUseTracker() - { - // Flush list - while( !mKeyList.empty() ) - { - delete mKeyList.front(); - mKeyList.pop_front(); - mCount--; - } - } - - void markUse( TKey key, TData data ) - { - TKeyUseTrackerNode *node = removeNode( key ); - if( !node ) - { - node = new TKeyUseTrackerNode(key, data); - } - else - { - // Update data - node->mData = data; - } - node->mLastUse = getTime(); - node->mUseCount++; - mKeyList.push_back( node ); - mCount++; - - // Too many items? Drop head - if( mCount > mMaxCount ) - { - delete mKeyList.front(); - mKeyList.pop_front(); - mCount--; - } - } - - void forgetKey( TKey key ) - { - TKeyUseTrackerNode *node = removeNode( key ); - if( node ) - { - delete node; - } - } - - U32 getUseCount( TKey key ) - { - TKeyUseTrackerNode *node = findNode( key ); - if( node ) - { - return node->mUseCount; - } - return 0; - } - - U64 getTimeSinceUse( TKey key ) - { - TKeyUseTrackerNode *node = findNode( key ); - if( node ) - { - U64 now = getTime(); - U64 delta = now - node->mLastUse; - return (U32)( delta / 1000000 ); - } - return 0; - } - - TData *getLastUseData( TKey key ) - { - TKeyUseTrackerNode *node = findNode( key ); - if( node ) - { - return &node->mData; - } - return NULL; - } - - U32 getKeyCount() - { - return mCount; - } + LLKeyUseTracker( U32 memory_seconds, U32 max_count ) : + mLastExpire(0), + mMaxCount( max_count ), + mCount(0) + { + mMemUsecs = ((U64)memory_seconds) * 1000000; + } + + ~LLKeyUseTracker() + { + // Flush list + while( !mKeyList.empty() ) + { + delete mKeyList.front(); + mKeyList.pop_front(); + mCount--; + } + } + + void markUse( TKey key, TData data ) + { + TKeyUseTrackerNode *node = removeNode( key ); + if( !node ) + { + node = new TKeyUseTrackerNode(key, data); + } + else + { + // Update data + node->mData = data; + } + node->mLastUse = getTime(); + node->mUseCount++; + mKeyList.push_back( node ); + mCount++; + + // Too many items? Drop head + if( mCount > mMaxCount ) + { + delete mKeyList.front(); + mKeyList.pop_front(); + mCount--; + } + } + + void forgetKey( TKey key ) + { + TKeyUseTrackerNode *node = removeNode( key ); + if( node ) + { + delete node; + } + } + + U32 getUseCount( TKey key ) + { + TKeyUseTrackerNode *node = findNode( key ); + if( node ) + { + return node->mUseCount; + } + return 0; + } + + U64 getTimeSinceUse( TKey key ) + { + TKeyUseTrackerNode *node = findNode( key ); + if( node ) + { + U64 now = getTime(); + U64 delta = now - node->mLastUse; + return (U32)( delta / 1000000 ); + } + return 0; + } + + TData *getLastUseData( TKey key ) + { + TKeyUseTrackerNode *node = findNode( key ); + if( node ) + { + return &node->mData; + } + return NULL; + } + + U32 getKeyCount() + { + return mCount; + } }; #endif diff --git a/indra/llcommon/llleap.cpp b/indra/llcommon/llleap.cpp index b2b1162f63..d0fb586459 100644 --- a/indra/llcommon/llleap.cpp +++ b/indra/llcommon/llleap.cpp @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2012-02-20 * @brief Implementation for llleap. - * + * * $LicenseInfo:firstyear=2012&license=viewerlgpl$ * Copyright (c) 2012, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/llleap.h b/indra/llcommon/llleap.h index 7cecdf2f8f..acf9edf418 100644 --- a/indra/llcommon/llleap.h +++ b/indra/llcommon/llleap.h @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2012-02-20 * @brief Class that implements "LLSD Event API Plugin" - * + * * $LicenseInfo:firstyear=2012&license=viewerlgpl$ * Copyright (c) 2012, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/llleaplistener.cpp b/indra/llcommon/llleaplistener.cpp index 471f52e91c..050d71c327 100644 --- a/indra/llcommon/llleaplistener.cpp +++ b/indra/llcommon/llleaplistener.cpp @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2012-03-16 * @brief Implementation for llleaplistener. - * + * * $LicenseInfo:firstyear=2012&license=viewerlgpl$ * Copyright (c) 2012, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/llleaplistener.h b/indra/llcommon/llleaplistener.h index 0ca5893657..cad4543d02 100644 --- a/indra/llcommon/llleaplistener.h +++ b/indra/llcommon/llleaplistener.h @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2012-03-16 * @brief LLEventAPI supporting LEAP plugins - * + * * $LicenseInfo:firstyear=2012&license=viewerlgpl$ * Copyright (c) 2012, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/llliveappconfig.cpp b/indra/llcommon/llliveappconfig.cpp index a9b1cdf4f6..559bfc443c 100644 --- a/indra/llcommon/llliveappconfig.cpp +++ b/indra/llcommon/llliveappconfig.cpp @@ -1,25 +1,25 @@ -/** +/** * @file llliveappconfig.cpp * @brief Configuration information for an LLApp that overrides indra.xml * * $LicenseInfo:firstyear=2003&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -33,48 +33,48 @@ #include "llsdserialize.h" LLLiveAppConfig::LLLiveAppConfig( - const std::string& filename, - F32 refresh_period, - LLApp::OptionPriority priority) : - LLLiveFile(filename, refresh_period), - mPriority(priority) + const std::string& filename, + F32 refresh_period, + LLApp::OptionPriority priority) : + LLLiveFile(filename, refresh_period), + mPriority(priority) { } LLLiveAppConfig::~LLLiveAppConfig() { } -// virtual +// virtual bool LLLiveAppConfig::loadFile() { - LL_INFOS() << "LLLiveAppConfig::loadFile(): reading from " - << filename() << LL_ENDL; + LL_INFOS() << "LLLiveAppConfig::loadFile(): reading from " + << filename() << LL_ENDL; llifstream file(filename().c_str()); - LLSD config; + LLSD config; if (file.is_open()) { LLSDSerialize::fromXML(config, file); - if(!config.isMap()) - { - LL_WARNS() << "Live app config not an map in " << filename() - << " Ignoring the data." << LL_ENDL; - return false; - } - file.close(); + if(!config.isMap()) + { + LL_WARNS() << "Live app config not an map in " << filename() + << " Ignoring the data." << LL_ENDL; + return false; + } + file.close(); + } + else + { + LL_INFOS() << "Live file " << filename() << " does not exit." << LL_ENDL; } - else - { - LL_INFOS() << "Live file " << filename() << " does not exit." << LL_ENDL; - } - // *NOTE: we do not handle the else case here because we would not - // have attempted to load the file unless LLLiveFile had - // determined there was a reason to load it. This only happens - // when either the file has been updated or it is either suddenly - // in existence or has passed out of existence. Therefore, we want - // to set the config to an empty config, and return that it - // changed. + // *NOTE: we do not handle the else case here because we would not + // have attempted to load the file unless LLLiveFile had + // determined there was a reason to load it. This only happens + // when either the file has been updated or it is either suddenly + // in existence or has passed out of existence. Therefore, we want + // to set the config to an empty config, and return that it + // changed. - LLApp* app = LLApp::instance(); - if(app) app->setOptionData(mPriority, config); - return true; + LLApp* app = LLApp::instance(); + if(app) app->setOptionData(mPriority, config); + return true; } diff --git a/indra/llcommon/llliveappconfig.h b/indra/llcommon/llliveappconfig.h index 4fd7c26a07..0dea643d87 100644 --- a/indra/llcommon/llliveappconfig.h +++ b/indra/llcommon/llliveappconfig.h @@ -1,25 +1,25 @@ -/** +/** * @file llliveappconfig.h * @brief Configuration information for an LLApp that overrides indra.xml * * $LicenseInfo:firstyear=2003&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -43,27 +43,27 @@ class LL_COMMON_API LLLiveAppConfig : public LLLiveFile { public: - /** - * @brief Constructor - * - * @param filename. The name of the file for periodically checking - * configuration. - * @param refresh_period How often the internal timer should - * bother checking the filesystem. - * @param The application priority level of that configuration file. - */ - LLLiveAppConfig( - const std::string& filename, - F32 refresh_period, - LLApp::OptionPriority priority); + /** + * @brief Constructor + * + * @param filename. The name of the file for periodically checking + * configuration. + * @param refresh_period How often the internal timer should + * bother checking the filesystem. + * @param The application priority level of that configuration file. + */ + LLLiveAppConfig( + const std::string& filename, + F32 refresh_period, + LLApp::OptionPriority priority); - ~LLLiveAppConfig(); ///< Destructor + ~LLLiveAppConfig(); ///< Destructor protected: - /*virtual*/ bool loadFile(); + /*virtual*/ bool loadFile(); private: - LLApp::OptionPriority mPriority; + LLApp::OptionPriority mPriority; }; #endif diff --git a/indra/llcommon/lllivefile.cpp b/indra/llcommon/lllivefile.cpp index 852ddb45e9..3fe5f4bdb6 100644 --- a/indra/llcommon/lllivefile.cpp +++ b/indra/llcommon/lllivefile.cpp @@ -1,198 +1,198 @@ -/** - * @file lllivefile.cpp - * - * $LicenseInfo:firstyear=2006&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" - -#include "lllivefile.h" -#include "llframetimer.h" -#include "lleventtimer.h" - -const F32 DEFAULT_CONFIG_FILE_REFRESH = 5.0f; - - -class LLLiveFile::Impl -{ -public: - Impl(const std::string& filename, const F32 refresh_period); - ~Impl(); - - bool check(); - void changed(); - - bool mForceCheck; - F32 mRefreshPeriod; - LLFrameTimer mRefreshTimer; - - std::string mFilename; - time_t mLastModTime; - time_t mLastStatTime; - bool mLastExists; - - LLEventTimer* mEventTimer; -private: - LOG_CLASS(LLLiveFile); -}; - -LLLiveFile::Impl::Impl(const std::string& filename, const F32 refresh_period) - : - mForceCheck(true), - mRefreshPeriod(refresh_period), - mFilename(filename), - mLastModTime(0), - mLastStatTime(0), - mLastExists(false), - mEventTimer(NULL) -{ -} - -LLLiveFile::Impl::~Impl() -{ - delete mEventTimer; -} - -LLLiveFile::LLLiveFile(const std::string& filename, const F32 refresh_period) - : impl(* new Impl(filename, refresh_period)) -{ -} - -LLLiveFile::~LLLiveFile() -{ - delete &impl; -} - - -bool LLLiveFile::Impl::check() -{ - bool detected_change = false; - // Skip the check if not enough time has elapsed and we're not - // forcing a check of the file - if (mForceCheck || mRefreshTimer.getElapsedTimeF32() >= mRefreshPeriod) - { - mForceCheck = false; // force only forces one check - mRefreshTimer.reset(); // don't check again until mRefreshPeriod has passed - - // Stat the file to see if it exists and when it was last modified. - llstat stat_data; - if (LLFile::stat(mFilename, &stat_data)) - { - // Couldn't stat the file, that means it doesn't exist or is - // broken somehow. - if (mLastExists) - { - mLastExists = false; - detected_change = true; // no longer existing is a change! - LL_DEBUGS() << "detected deleted file '" << mFilename << "'" << LL_ENDL; - } - } - else - { - // The file exists - if ( ! mLastExists ) - { - // last check, it did not exist - that counts as a change - LL_DEBUGS() << "detected created file '" << mFilename << "'" << LL_ENDL; - detected_change = true; - } - else if ( stat_data.st_mtime > mLastModTime ) - { - // file modification time is newer than last check - LL_DEBUGS() << "detected updated file '" << mFilename << "'" << LL_ENDL; - detected_change = true; - } - mLastExists = true; - mLastStatTime = stat_data.st_mtime; - } - } - if (detected_change) - { - LL_INFOS() << "detected file change '" << mFilename << "'" << LL_ENDL; - } - return detected_change; -} - -void LLLiveFile::Impl::changed() -{ - // we wanted to read this file, and we were successful. - mLastModTime = mLastStatTime; -} - -bool LLLiveFile::checkAndReload() -{ - bool changed = impl.check(); - if (changed) - { - if(loadFile()) - { - impl.changed(); - this->changed(); - } - else - { - changed = false; - } - } - return changed; -} - -std::string LLLiveFile::filename() const -{ - return impl.mFilename; -} - -namespace -{ - class LiveFileEventTimer : public LLEventTimer - { - public: - LiveFileEventTimer(LLLiveFile& f, F32 refresh) - : LLEventTimer(refresh), mLiveFile(f) - { } - - bool tick() - { - mLiveFile.checkAndReload(); - return false; - } - - private: - LLLiveFile& mLiveFile; - }; - -} - -void LLLiveFile::addToEventTimer() -{ - impl.mEventTimer = new LiveFileEventTimer(*this, impl.mRefreshPeriod); -} - -void LLLiveFile::setRefreshPeriod(F32 seconds) -{ - if (seconds < 0.f) - { - seconds = -seconds; - } - impl.mRefreshPeriod = seconds; -} - +/**
+ * @file lllivefile.cpp
+ *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "lllivefile.h"
+#include "llframetimer.h"
+#include "lleventtimer.h"
+
+const F32 DEFAULT_CONFIG_FILE_REFRESH = 5.0f;
+
+
+class LLLiveFile::Impl
+{
+public:
+ Impl(const std::string& filename, const F32 refresh_period);
+ ~Impl();
+
+ bool check();
+ void changed();
+
+ bool mForceCheck;
+ F32 mRefreshPeriod;
+ LLFrameTimer mRefreshTimer;
+
+ std::string mFilename;
+ time_t mLastModTime;
+ time_t mLastStatTime;
+ bool mLastExists;
+
+ LLEventTimer* mEventTimer;
+private:
+ LOG_CLASS(LLLiveFile);
+};
+
+LLLiveFile::Impl::Impl(const std::string& filename, const F32 refresh_period)
+ :
+ mForceCheck(true),
+ mRefreshPeriod(refresh_period),
+ mFilename(filename),
+ mLastModTime(0),
+ mLastStatTime(0),
+ mLastExists(false),
+ mEventTimer(NULL)
+{
+}
+
+LLLiveFile::Impl::~Impl()
+{
+ delete mEventTimer;
+}
+
+LLLiveFile::LLLiveFile(const std::string& filename, const F32 refresh_period)
+ : impl(* new Impl(filename, refresh_period))
+{
+}
+
+LLLiveFile::~LLLiveFile()
+{
+ delete &impl;
+}
+
+
+bool LLLiveFile::Impl::check()
+{
+ bool detected_change = false;
+ // Skip the check if not enough time has elapsed and we're not
+ // forcing a check of the file
+ if (mForceCheck || mRefreshTimer.getElapsedTimeF32() >= mRefreshPeriod)
+ {
+ mForceCheck = false; // force only forces one check
+ mRefreshTimer.reset(); // don't check again until mRefreshPeriod has passed
+
+ // Stat the file to see if it exists and when it was last modified.
+ llstat stat_data;
+ if (LLFile::stat(mFilename, &stat_data))
+ {
+ // Couldn't stat the file, that means it doesn't exist or is
+ // broken somehow.
+ if (mLastExists)
+ {
+ mLastExists = false;
+ detected_change = true; // no longer existing is a change!
+ LL_DEBUGS() << "detected deleted file '" << mFilename << "'" << LL_ENDL;
+ }
+ }
+ else
+ {
+ // The file exists
+ if ( ! mLastExists )
+ {
+ // last check, it did not exist - that counts as a change
+ LL_DEBUGS() << "detected created file '" << mFilename << "'" << LL_ENDL;
+ detected_change = true;
+ }
+ else if ( stat_data.st_mtime > mLastModTime )
+ {
+ // file modification time is newer than last check
+ LL_DEBUGS() << "detected updated file '" << mFilename << "'" << LL_ENDL;
+ detected_change = true;
+ }
+ mLastExists = true;
+ mLastStatTime = stat_data.st_mtime;
+ }
+ }
+ if (detected_change)
+ {
+ LL_INFOS() << "detected file change '" << mFilename << "'" << LL_ENDL;
+ }
+ return detected_change;
+}
+
+void LLLiveFile::Impl::changed()
+{
+ // we wanted to read this file, and we were successful.
+ mLastModTime = mLastStatTime;
+}
+
+bool LLLiveFile::checkAndReload()
+{
+ bool changed = impl.check();
+ if (changed)
+ {
+ if(loadFile())
+ {
+ impl.changed();
+ this->changed();
+ }
+ else
+ {
+ changed = false;
+ }
+ }
+ return changed;
+}
+
+std::string LLLiveFile::filename() const
+{
+ return impl.mFilename;
+}
+
+namespace
+{
+ class LiveFileEventTimer : public LLEventTimer
+ {
+ public:
+ LiveFileEventTimer(LLLiveFile& f, F32 refresh)
+ : LLEventTimer(refresh), mLiveFile(f)
+ { }
+
+ bool tick()
+ {
+ mLiveFile.checkAndReload();
+ return false;
+ }
+
+ private:
+ LLLiveFile& mLiveFile;
+ };
+
+}
+
+void LLLiveFile::addToEventTimer()
+{
+ impl.mEventTimer = new LiveFileEventTimer(*this, impl.mRefreshPeriod);
+}
+
+void LLLiveFile::setRefreshPeriod(F32 seconds)
+{
+ if (seconds < 0.f)
+ {
+ seconds = -seconds;
+ }
+ impl.mRefreshPeriod = seconds;
+}
+
diff --git a/indra/llcommon/lllivefile.h b/indra/llcommon/lllivefile.h index 320aa4bc3e..305b205a68 100644 --- a/indra/llcommon/lllivefile.h +++ b/indra/llcommon/lllivefile.h @@ -1,25 +1,25 @@ -/** +/** * @file lllivefile.h * @brief Automatically reloads a file whenever it changes or is removed. * * $LicenseInfo:firstyear=2006&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -33,63 +33,63 @@ extern const F32 DEFAULT_CONFIG_FILE_REFRESH; class LL_COMMON_API LLLiveFile { public: - LLLiveFile(const std::string& filename, const F32 refresh_period = 5.f); - virtual ~LLLiveFile(); + LLLiveFile(const std::string& filename, const F32 refresh_period = 5.f); + virtual ~LLLiveFile(); + + /** + * @brief Check to see if this live file should reload. + * + * Call this before using anything that was read & cached + * from the file. + * + * This method calls the <code>loadFile()</code> method if + * any of: + * file has a new modify time since the last check + * file used to exist and now does not + * file used to not exist but now does + * @return Returns true if the file was reloaded. + */ + bool checkAndReload(); - /** - * @brief Check to see if this live file should reload. - * - * Call this before using anything that was read & cached - * from the file. - * - * This method calls the <code>loadFile()</code> method if - * any of: - * file has a new modify time since the last check - * file used to exist and now does not - * file used to not exist but now does - * @return Returns true if the file was reloaded. - */ - bool checkAndReload(); - - std::string filename() const; + std::string filename() const; - /** - * @brief Add this live file to an automated recheck. - * - * Normally, just calling checkAndReload() is enough. In some - * cases though, you may need to let the live file periodically - * check itself. - */ - void addToEventTimer(); + /** + * @brief Add this live file to an automated recheck. + * + * Normally, just calling checkAndReload() is enough. In some + * cases though, you may need to let the live file periodically + * check itself. + */ + void addToEventTimer(); - void setRefreshPeriod(F32 seconds); + void setRefreshPeriod(F32 seconds); protected: - /** - * @breif Implement this to load your file if it changed. - * - * This method is called automatically by <code>checkAndReload()</code>, - * so though you must implement this in derived classes, you do - * not need to call it manually. - * @return Returns true if the file was successfully loaded. - */ - virtual bool loadFile() = 0; + /** + * @breif Implement this to load your file if it changed. + * + * This method is called automatically by <code>checkAndReload()</code>, + * so though you must implement this in derived classes, you do + * not need to call it manually. + * @return Returns true if the file was successfully loaded. + */ + virtual bool loadFile() = 0; - /** - * @brief Implement this method if you want to get a change callback. - * - * This virtual function will be called automatically at the end - * of <code>checkAndReload()</code> if a new configuration was - * loaded. This does not track differences between the current and - * newly loaded file, so any successful load event will trigger a - * <code>changed()</code> callback. Default is to do nothing. - */ - virtual void changed() {} + /** + * @brief Implement this method if you want to get a change callback. + * + * This virtual function will be called automatically at the end + * of <code>checkAndReload()</code> if a new configuration was + * loaded. This does not track differences between the current and + * newly loaded file, so any successful load event will trigger a + * <code>changed()</code> callback. Default is to do nothing. + */ + virtual void changed() {} private: - class Impl; - Impl& impl; + class Impl; + Impl& impl; }; #endif //LL_LLLIVEFILE_H diff --git a/indra/llcommon/llmainthreadtask.cpp b/indra/llcommon/llmainthreadtask.cpp index e0d70cacd8..eb26ded63f 100644 --- a/indra/llcommon/llmainthreadtask.cpp +++ b/indra/llcommon/llmainthreadtask.cpp @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2019-12-05 * @brief Implementation for llmainthreadtask. - * + * * $LicenseInfo:firstyear=2019&license=viewerlgpl$ * Copyright (c) 2019, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/llmake.h b/indra/llcommon/llmake.h index 02463d97ea..e575128be7 100644 --- a/indra/llcommon/llmake.h +++ b/indra/llcommon/llmake.h @@ -9,7 +9,7 @@ * make an instance with arguments of arbitrary type. llmake() * eliminates the need to declare a new helper function for every such * class template. - * + * * also relevant: * * Template argument deduction for class templates (C++17) diff --git a/indra/llcommon/llmd5.cpp b/indra/llcommon/llmd5.cpp index 0abe817f1d..f64f54c262 100644 --- a/indra/llcommon/llmd5.cpp +++ b/indra/llcommon/llmd5.cpp @@ -1,42 +1,42 @@ -/** +/** * @file llmd5.cpp * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ -// llMD5.CC - source code for the C++/object oriented translation and +// llMD5.CC - source code for the C++/object oriented translation and // modification of MD5. // // Adapted to Linden Lab by Frank Filipanits, 6/25/2002 // Fixed potential memory leak, James Cook, 6/27/2002 -// Translation and modification (c) 1995 by Mordechai T. Abzug +// Translation and modification (c) 1995 by Mordechai T. Abzug -// This translation/ modification is provided "as is," without express or +// This translation/ modification is provided "as is," without express or // implied warranty of any kind. -// The translator/ modifier does not claim (1) that MD5 will do what you think -// it does; (2) that this translation/ modification is accurate; or (3) that -// this software is "merchantible." (Language for this disclaimer partially +// The translator/ modifier does not claim (1) that MD5 will do what you think +// it does; (2) that this translation/ modification is accurate; or (3) that +// this software is "merchantible." (Language for this disclaimer partially // copied from the disclaimer below). /* based on: @@ -76,7 +76,7 @@ documentation and/or software. #include "llmd5.h" -#include <iostream> // cerr +#include <iostream> // cerr // how many bytes to grab at a time when checking files const int LLMD5::BLOCK_LEN = 4096; @@ -102,7 +102,7 @@ void LLMD5::update (const uint8_t *input, const size_t input_length) { size_t buffer_space; // how much space is left in buffer if (finalized){ // so we can't update! - std::cerr << "LLMD5::update: Can't update a finalized digest!" << std::endl; + std::cerr << "LLMD5::update: Can't update a finalized digest!" << std::endl; return; } @@ -116,21 +116,21 @@ void LLMD5::update (const uint8_t *input, const size_t input_length) { // now, transform each 64-byte piece of the input, bypassing the buffer if (input == NULL || input_length == 0){ - std::cerr << "LLMD5::update: Invalid input!" << std::endl; - return; + std::cerr << "LLMD5::update: Invalid input!" << std::endl; + return; } // Transform as many times as possible. if (input_length >= buffer_space) { // ie. we have enough to fill the buffer // fill the rest of the buffer and transform - memcpy( /* Flawfinder: ignore */ - buffer + buffer_index, - input, - buffer_space); + memcpy( /* Flawfinder: ignore */ + buffer + buffer_index, + input, + buffer_space); transform (buffer); - for (input_index = buffer_space; input_index + 63 < input_length; - input_index += 64) + for (input_index = buffer_space; input_index + 63 < input_length; + input_index += 64) transform (input+input_index); buffer_index = 0; // so we can buffer remaining @@ -140,7 +140,7 @@ void LLMD5::update (const uint8_t *input, const size_t input_length) { // and here we do the buffering: - memcpy(buffer+buffer_index, input+input_index, input_length-input_index); /* Flawfinder: ignore */ + memcpy(buffer+buffer_index, input+input_index, input_length-input_index); /* Flawfinder: ignore */ } @@ -150,7 +150,7 @@ void LLMD5::update (const uint8_t *input, const size_t input_length) { void LLMD5::update(FILE* file){ - unsigned char buffer[BLOCK_LEN]; /* Flawfinder: ignore */ + unsigned char buffer[BLOCK_LEN]; /* Flawfinder: ignore */ int len; while ( (len=(int)fread(buffer, 1, BLOCK_LEN, file)) ) @@ -165,11 +165,11 @@ void LLMD5::update(FILE* file){ void LLMD5::update(std::istream& stream){ - unsigned char buffer[BLOCK_LEN]; /* Flawfinder: ignore */ + unsigned char buffer[BLOCK_LEN]; /* Flawfinder: ignore */ int len; while (stream.good()){ - stream.read( (char*)buffer, BLOCK_LEN); /* Flawfinder: ignore */ // note that return value of read is unusable. + stream.read( (char*)buffer, BLOCK_LEN); /* Flawfinder: ignore */ // note that return value of read is unusable. len=(int)stream.gcount(); update(buffer, len); } @@ -178,7 +178,7 @@ void LLMD5::update(std::istream& stream){ void LLMD5::update(const std::string& s) { - update((unsigned char *)s.c_str(),s.length()); + update((unsigned char *)s.c_str(),s.length()); } // MD5 finalization. Ends an MD5 message-digest operation, writing the @@ -187,7 +187,7 @@ void LLMD5::update(const std::string& s) void LLMD5::finalize (){ - unsigned char bits[8]; /* Flawfinder: ignore */ + unsigned char bits[8]; /* Flawfinder: ignore */ size_t index, padLen; static uint8_t PADDING[64]={ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -245,60 +245,60 @@ LLMD5::LLMD5(std::istream& stream){ // Digest a string of the format ("%s:%i" % (s, number)) LLMD5::LLMD5(const unsigned char *string, const unsigned int number) { - const char *colon = ":"; - char tbuf[16]; /* Flawfinder: ignore */ - init(); - update(string, (U32)strlen((const char *) string)); /* Flawfinder: ignore */ - update((const unsigned char *) colon, (U32)strlen(colon)); /* Flawfinder: ignore */ - snprintf(tbuf, sizeof(tbuf), "%i", number); /* Flawfinder: ignore */ - update((const unsigned char *) tbuf, (U32)strlen(tbuf)); /* Flawfinder: ignore */ - finalize(); + const char *colon = ":"; + char tbuf[16]; /* Flawfinder: ignore */ + init(); + update(string, (U32)strlen((const char *) string)); /* Flawfinder: ignore */ + update((const unsigned char *) colon, (U32)strlen(colon)); /* Flawfinder: ignore */ + snprintf(tbuf, sizeof(tbuf), "%i", number); /* Flawfinder: ignore */ + update((const unsigned char *) tbuf, (U32)strlen(tbuf)); /* Flawfinder: ignore */ + finalize(); } // Digest a string LLMD5::LLMD5(const unsigned char *s) { - init(); - update(s, (U32)strlen((const char *) s)); /* Flawfinder: ignore */ - finalize(); + init(); + update(s, (U32)strlen((const char *) s)); /* Flawfinder: ignore */ + finalize(); } void LLMD5::raw_digest(unsigned char *s) const { - if (!finalized) - { - std::cerr << "LLMD5::raw_digest: Can't get digest if you haven't "<< - "finalized the digest!" << std::endl; - s[0] = '\0'; - return; - } - - memcpy(s, digest, 16); /* Flawfinder: ignore */ - return; + if (!finalized) + { + std::cerr << "LLMD5::raw_digest: Can't get digest if you haven't "<< + "finalized the digest!" << std::endl; + s[0] = '\0'; + return; + } + + memcpy(s, digest, 16); /* Flawfinder: ignore */ + return; } void LLMD5::hex_digest(char *s) const { - int i; + int i; - if (!finalized) - { - std::cerr << "LLMD5::hex_digest: Can't get digest if you haven't "<< - "finalized the digest!" <<std::endl; - s[0] = '\0'; - return; - } + if (!finalized) + { + std::cerr << "LLMD5::hex_digest: Can't get digest if you haven't "<< + "finalized the digest!" <<std::endl; + s[0] = '\0'; + return; + } - for (i=0; i<16; i++) - { - sprintf(s+i*2, "%02x", digest[i]); /* Flawfinder: ignore */ - } + for (i=0; i<16; i++) + { + sprintf(s+i*2, "%02x", digest[i]); /* Flawfinder: ignore */ + } - s[32]='\0'; + s[32]='\0'; - return; + return; } @@ -308,27 +308,27 @@ void LLMD5::hex_digest(char *s) const std::ostream& operator<<(std::ostream &stream, LLMD5 context) { - char s[33]; /* Flawfinder: ignore */ - context.hex_digest(s); - stream << s; - return stream; + char s[33]; /* Flawfinder: ignore */ + context.hex_digest(s); + stream << s; + return stream; } bool operator==(const LLMD5& a, const LLMD5& b) { - unsigned char a_guts[16]; - unsigned char b_guts[16]; - a.raw_digest(a_guts); - b.raw_digest(b_guts); - if (memcmp(a_guts,b_guts,16)==0) - return true; - else - return false; + unsigned char a_guts[16]; + unsigned char b_guts[16]; + a.raw_digest(a_guts); + b.raw_digest(b_guts); + if (memcmp(a_guts,b_guts,16)==0) + return true; + else + return false; } bool operator!=(const LLMD5& a, const LLMD5& b) { - return !(a==b); + return !(a==b); } // PRIVATE METHODS: diff --git a/indra/llcommon/llmd5.h b/indra/llcommon/llmd5.h index 7d6373c20c..46c79cf5a2 100644 --- a/indra/llcommon/llmd5.h +++ b/indra/llcommon/llmd5.h @@ -1,24 +1,24 @@ -/** +/** * @file llmd5.h * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -26,17 +26,17 @@ #ifndef LL_LLMD5_H #define LL_LLMD5_H -// LLMD5.CC - source code for the C++/object oriented translation and +// LLMD5.CC - source code for the C++/object oriented translation and // modification of MD5. -// Translation and modification (c) 1995 by Mordechai T. Abzug +// Translation and modification (c) 1995 by Mordechai T. Abzug -// This translation/ modification is provided "as is," without express or +// This translation/ modification is provided "as is," without express or // implied warranty of any kind. -// The translator/ modifier does not claim (1) that MD5 will do what you think -// it does; (2) that this translation/ modification is accurate; or (3) that -// this software is "merchantible." (Language for this disclaimer partially +// The translator/ modifier does not claim (1) that MD5 will do what you think +// it does; (2) that this translation/ modification is accurate; or (3) that +// this software is "merchantible." (Language for this disclaimer partially // copied from the disclaimer below). /* based on: @@ -95,10 +95,10 @@ public: LLMD5 (std::istream& stream); // digest stream, finalize LLMD5 (FILE *file); // digest file, close, finalize LLMD5 (const unsigned char *string, const unsigned int number); - + // methods to acquire finalized result - void raw_digest(unsigned char *array) const; // provide 16-byte array for binary data - void hex_digest(char *string) const; // provide 33-byte array for ascii-hex string + void raw_digest(unsigned char *array) const; // provide 16-byte array for binary data + void hex_digest(char *string) const; // provide 33-byte array for ascii-hex string friend LL_COMMON_API std::ostream& operator<< (std::ostream&, LLMD5 context); @@ -114,7 +114,7 @@ private: // last, the private methods, mostly static: void init (); // called by all constructors - void transform (const uint8_t *buffer); // does the real update work. Note + void transform (const uint8_t *buffer); // does the real update work. Note // that length is implied to be 64. static void encode (uint8_t *dest, const uint32_t *src, const size_t length); diff --git a/indra/llcommon/llmemory.cpp b/indra/llcommon/llmemory.cpp index 6e0715d697..0e5b91c9b8 100644 --- a/indra/llcommon/llmemory.cpp +++ b/indra/llcommon/llmemory.cpp @@ -1,354 +1,354 @@ -/** - * @file llmemory.cpp - * @brief Very special memory allocation/deallocation stuff here - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" - - -#include "llthread.h" - -#if defined(LL_WINDOWS) -# include <psapi.h> -#elif defined(LL_DARWIN) -# include <sys/types.h> -# include <mach/task.h> -# include <mach/mach_init.h> -#include <mach/mach_host.h> -#elif LL_LINUX -# include <unistd.h> -# include <sys/resource.h> -#endif - -#include "llmemory.h" - -#include "llsys.h" -#include "llframetimer.h" -#include "lltrace.h" -#include "llerror.h" -//---------------------------------------------------------------------------- - -//static -U32Kilobytes LLMemory::sAvailPhysicalMemInKB(U32_MAX); -U32Kilobytes LLMemory::sMaxPhysicalMemInKB(0); -static LLTrace::SampleStatHandle<F64Megabytes> sAllocatedMem("allocated_mem", "active memory in use by application"); -static LLTrace::SampleStatHandle<F64Megabytes> sVirtualMem("virtual_mem", "virtual memory assigned to application"); -U32Kilobytes LLMemory::sAllocatedMemInKB(0); -U32Kilobytes LLMemory::sAllocatedPageSizeInKB(0); -U32Kilobytes LLMemory::sMaxHeapSizeInKB(U32_MAX); - -void ll_assert_aligned_func(uintptr_t ptr,U32 alignment) -{ -#if defined(LL_WINDOWS) && defined(LL_DEBUG_BUFFER_OVERRUN) - //do not check - return; -#else - #ifdef SHOW_ASSERT - // Redundant, place to set breakpoints. - if (ptr%alignment!=0) - { - LL_WARNS() << "alignment check failed" << LL_ENDL; - } - llassert(ptr%alignment==0); - #endif -#endif -} - -//static -void LLMemory::initMaxHeapSizeGB(F32Gigabytes max_heap_size) -{ - sMaxHeapSizeInKB = U32Kilobytes::convert(max_heap_size); -} - -//static -void LLMemory::updateMemoryInfo() -{ - LL_PROFILE_ZONE_SCOPED -#if LL_WINDOWS - PROCESS_MEMORY_COUNTERS counters; - - if (!GetProcessMemoryInfo(GetCurrentProcess(), &counters, sizeof(counters))) - { - LL_WARNS() << "GetProcessMemoryInfo failed" << LL_ENDL; - return ; - } - - sAllocatedMemInKB = U32Kilobytes::convert(U64Bytes(counters.WorkingSetSize)); - sample(sAllocatedMem, sAllocatedMemInKB); - sAllocatedPageSizeInKB = U32Kilobytes::convert(U64Bytes(counters.PagefileUsage)); - sample(sVirtualMem, sAllocatedPageSizeInKB); - - U32Kilobytes avail_phys, avail_virtual; - LLMemoryInfo::getAvailableMemoryKB(avail_phys, avail_virtual) ; - sMaxPhysicalMemInKB = llmin(avail_phys + sAllocatedMemInKB, sMaxHeapSizeInKB); - - if(sMaxPhysicalMemInKB > sAllocatedMemInKB) - { - sAvailPhysicalMemInKB = sMaxPhysicalMemInKB - sAllocatedMemInKB ; - } - else - { - sAvailPhysicalMemInKB = U32Kilobytes(0); - } - -#elif defined(LL_DARWIN) - task_vm_info info; - mach_msg_type_number_t infoCount = TASK_VM_INFO_COUNT; - // MACH_TASK_BASIC_INFO reports the same resident_size, but does not tell us the reusable bytes or phys_footprint. - if (task_info(mach_task_self(), TASK_VM_INFO, reinterpret_cast<task_info_t>(&info), &infoCount) == KERN_SUCCESS) - { - // Our Windows definition of PagefileUsage is documented by Microsoft as "the total amount of - // memory that the memory manager has committed for a running process", which is rss. - sAllocatedPageSizeInKB = U32Bytes(info.resident_size); - - // Activity Monitor => Inspect Process => Real Memory Size appears to report resident_size - // Activity monitor => main window memory column appears to report phys_footprint, which spot checks as at least 30% less. - // I think that is because of compression, which isn't going to give us a consistent measurement. We want uncompressed totals. - // - // In between is resident_size - reusable. This is what Chrome source code uses, with source comments saying it is 'the "Real Memory" value - // reported for the app by the Memory Monitor in Instruments.' It is still about 8% bigger than phys_footprint. - // - // (On Windows, we use WorkingSetSize.) - sAllocatedMemInKB = U32Bytes(info.resident_size - info.reusable); - } - else - { - LL_WARNS() << "task_info failed" << LL_ENDL; - } - - // Total installed and available physical memory are properties of the host, not just our process. - vm_statistics64_data_t vmstat; - mach_msg_type_number_t count = HOST_VM_INFO64_COUNT; - mach_port_t host = mach_host_self(); - vm_size_t page_size; - host_page_size(host, &page_size); - kern_return_t result = host_statistics64(host, HOST_VM_INFO64, reinterpret_cast<host_info_t>(&vmstat), &count); - if (result == KERN_SUCCESS) { - // This is what Chrome reports as 'the "Physical Memory Free" value reported by the Memory Monitor in Instruments.' - // Note though that inactive pages are not included here and not yet free, but could become so under memory pressure. - sAvailPhysicalMemInKB = U32Bytes(vmstat.free_count * page_size); - sMaxPhysicalMemInKB = LLMemoryInfo::getHardwareMemSize(); - } - else - { - LL_WARNS() << "task_info failed" << LL_ENDL; - } - -#else - //not valid for other systems for now. - sAllocatedMemInKB = U64Bytes(LLMemory::getCurrentRSS()); - sMaxPhysicalMemInKB = U64Bytes(U32_MAX); - sAvailPhysicalMemInKB = U64Bytes(U32_MAX); -#endif - - return ; -} - -// -//this function is to test if there is enough space with the size in the virtual address space. -//it does not do any real allocation -//if success, it returns the address where the memory chunk can fit in; -//otherwise it returns NULL. -// -//static -void* LLMemory::tryToAlloc(void* address, U32 size) -{ -#if LL_WINDOWS - address = VirtualAlloc(address, size, MEM_RESERVE | MEM_TOP_DOWN, PAGE_NOACCESS) ; - if(address) - { - if(!VirtualFree(address, 0, MEM_RELEASE)) - { - LL_ERRS() << "error happens when free some memory reservation." << LL_ENDL ; - } - } - return address ; -#else - return (void*)0x01 ; //skip checking -#endif -} - -//static -void LLMemory::logMemoryInfo(bool update) -{ - LL_PROFILE_ZONE_SCOPED - if(update) - { - updateMemoryInfo() ; - } - - LL_INFOS() << "Current allocated physical memory(KB): " << sAllocatedMemInKB << LL_ENDL ; - LL_INFOS() << "Current allocated page size (KB): " << sAllocatedPageSizeInKB << LL_ENDL ; - LL_INFOS() << "Current available physical memory(KB): " << sAvailPhysicalMemInKB << LL_ENDL ; - LL_INFOS() << "Current max usable memory(KB): " << sMaxPhysicalMemInKB << LL_ENDL ; -} - -//static -U32Kilobytes LLMemory::getAvailableMemKB() -{ - return sAvailPhysicalMemInKB ; -} - -//static -U32Kilobytes LLMemory::getMaxMemKB() -{ - return sMaxPhysicalMemInKB ; -} - -//static -U32Kilobytes LLMemory::getAllocatedMemKB() -{ - return sAllocatedMemInKB ; -} - -//---------------------------------------------------------------------------- - -#if defined(LL_WINDOWS) - -//static -U64 LLMemory::getCurrentRSS() -{ - PROCESS_MEMORY_COUNTERS counters; - - if (!GetProcessMemoryInfo(GetCurrentProcess(), &counters, sizeof(counters))) - { - LL_WARNS() << "GetProcessMemoryInfo failed" << LL_ENDL; - return 0; - } - - return counters.WorkingSetSize; -} - -#elif defined(LL_DARWIN) - -// if (sysctl(ctl, 2, &page_size, &size, NULL, 0) == -1) -// { -// LL_WARNS() << "Couldn't get page size" << LL_ENDL; -// return 0; -// } else { -// return page_size; -// } -// } - -U64 LLMemory::getCurrentRSS() -{ - U64 residentSize = 0; - mach_task_basic_info_data_t basicInfo; - mach_msg_type_number_t basicInfoCount = MACH_TASK_BASIC_INFO_COUNT; - if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&basicInfo, &basicInfoCount) == KERN_SUCCESS) - { - residentSize = basicInfo.resident_size; - // 64-bit macos apps allocate 32 GB or more at startup, and this is reflected in virtual_size. - // basicInfo.virtual_size is not what we want. - } - else - { - LL_WARNS() << "task_info failed" << LL_ENDL; - } - - return residentSize; -} - -#elif defined(LL_LINUX) - -U64 LLMemory::getCurrentRSS() -{ - struct rusage usage; - - if (getrusage(RUSAGE_SELF, &usage) != 0) { - // Error handling code could be here - return 0; - } - - // ru_maxrss (since Linux 2.6.32) - // This is the maximum resident set size used (in kilobytes). - return usage.ru_maxrss * 1024; -} - -#else - -U64 LLMemory::getCurrentRSS() -{ - return 0; -} - -#endif - -//-------------------------------------------------------------------- - -#if defined(LL_WINDOWS) && defined(LL_DEBUG_BUFFER_OVERRUN) - -#include <map> - -struct mem_info { - std::map<void*, void*> memory_info; - LLMutex mutex; - - static mem_info& get() { - static mem_info instance; - return instance; - } - -private: - mem_info(){} -}; - -void* ll_aligned_malloc_fallback( size_t size, int align ) -{ - SYSTEM_INFO sysinfo; - GetSystemInfo(&sysinfo); - - unsigned int for_alloc = (size/sysinfo.dwPageSize + !!(size%sysinfo.dwPageSize)) * sysinfo.dwPageSize; - - void *p = VirtualAlloc(NULL, for_alloc+sysinfo.dwPageSize, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE); - if(NULL == p) { - // call debugger - __asm int 3; - } - DWORD old; - bool Res = VirtualProtect((void*)((char*)p + for_alloc), sysinfo.dwPageSize, PAGE_NOACCESS, &old); - if(false == Res) { - // call debugger - __asm int 3; - } - - void* ret = (void*)((char*)p + for_alloc-size); - - { - LLMutexLock lock(&mem_info::get().mutex); - mem_info::get().memory_info.insert(std::pair<void*, void*>(ret, p)); - } - - - return ret; -} - -void ll_aligned_free_fallback( void* ptr ) -{ - LLMutexLock lock(&mem_info::get().mutex); - VirtualFree(mem_info::get().memory_info.find(ptr)->second, 0, MEM_RELEASE); - mem_info::get().memory_info.erase(ptr); -} - -#endif +/**
+ * @file llmemory.cpp
+ * @brief Very special memory allocation/deallocation stuff here
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+
+#include "llthread.h"
+
+#if defined(LL_WINDOWS)
+# include <psapi.h>
+#elif defined(LL_DARWIN)
+# include <sys/types.h>
+# include <mach/task.h>
+# include <mach/mach_init.h>
+#include <mach/mach_host.h>
+#elif LL_LINUX
+# include <unistd.h>
+# include <sys/resource.h>
+#endif
+
+#include "llmemory.h"
+
+#include "llsys.h"
+#include "llframetimer.h"
+#include "lltrace.h"
+#include "llerror.h"
+//----------------------------------------------------------------------------
+
+//static
+U32Kilobytes LLMemory::sAvailPhysicalMemInKB(U32_MAX);
+U32Kilobytes LLMemory::sMaxPhysicalMemInKB(0);
+static LLTrace::SampleStatHandle<F64Megabytes> sAllocatedMem("allocated_mem", "active memory in use by application");
+static LLTrace::SampleStatHandle<F64Megabytes> sVirtualMem("virtual_mem", "virtual memory assigned to application");
+U32Kilobytes LLMemory::sAllocatedMemInKB(0);
+U32Kilobytes LLMemory::sAllocatedPageSizeInKB(0);
+U32Kilobytes LLMemory::sMaxHeapSizeInKB(U32_MAX);
+
+void ll_assert_aligned_func(uintptr_t ptr,U32 alignment)
+{
+#if defined(LL_WINDOWS) && defined(LL_DEBUG_BUFFER_OVERRUN)
+ //do not check
+ return;
+#else
+ #ifdef SHOW_ASSERT
+ // Redundant, place to set breakpoints.
+ if (ptr%alignment!=0)
+ {
+ LL_WARNS() << "alignment check failed" << LL_ENDL;
+ }
+ llassert(ptr%alignment==0);
+ #endif
+#endif
+}
+
+//static
+void LLMemory::initMaxHeapSizeGB(F32Gigabytes max_heap_size)
+{
+ sMaxHeapSizeInKB = U32Kilobytes::convert(max_heap_size);
+}
+
+//static
+void LLMemory::updateMemoryInfo()
+{
+ LL_PROFILE_ZONE_SCOPED
+#if LL_WINDOWS
+ PROCESS_MEMORY_COUNTERS counters;
+
+ if (!GetProcessMemoryInfo(GetCurrentProcess(), &counters, sizeof(counters)))
+ {
+ LL_WARNS() << "GetProcessMemoryInfo failed" << LL_ENDL;
+ return ;
+ }
+
+ sAllocatedMemInKB = U32Kilobytes::convert(U64Bytes(counters.WorkingSetSize));
+ sample(sAllocatedMem, sAllocatedMemInKB);
+ sAllocatedPageSizeInKB = U32Kilobytes::convert(U64Bytes(counters.PagefileUsage));
+ sample(sVirtualMem, sAllocatedPageSizeInKB);
+
+ U32Kilobytes avail_phys, avail_virtual;
+ LLMemoryInfo::getAvailableMemoryKB(avail_phys, avail_virtual) ;
+ sMaxPhysicalMemInKB = llmin(avail_phys + sAllocatedMemInKB, sMaxHeapSizeInKB);
+
+ if(sMaxPhysicalMemInKB > sAllocatedMemInKB)
+ {
+ sAvailPhysicalMemInKB = sMaxPhysicalMemInKB - sAllocatedMemInKB ;
+ }
+ else
+ {
+ sAvailPhysicalMemInKB = U32Kilobytes(0);
+ }
+
+#elif defined(LL_DARWIN)
+ task_vm_info info;
+ mach_msg_type_number_t infoCount = TASK_VM_INFO_COUNT;
+ // MACH_TASK_BASIC_INFO reports the same resident_size, but does not tell us the reusable bytes or phys_footprint.
+ if (task_info(mach_task_self(), TASK_VM_INFO, reinterpret_cast<task_info_t>(&info), &infoCount) == KERN_SUCCESS)
+ {
+ // Our Windows definition of PagefileUsage is documented by Microsoft as "the total amount of
+ // memory that the memory manager has committed for a running process", which is rss.
+ sAllocatedPageSizeInKB = U32Bytes(info.resident_size);
+
+ // Activity Monitor => Inspect Process => Real Memory Size appears to report resident_size
+ // Activity monitor => main window memory column appears to report phys_footprint, which spot checks as at least 30% less.
+ // I think that is because of compression, which isn't going to give us a consistent measurement. We want uncompressed totals.
+ //
+ // In between is resident_size - reusable. This is what Chrome source code uses, with source comments saying it is 'the "Real Memory" value
+ // reported for the app by the Memory Monitor in Instruments.' It is still about 8% bigger than phys_footprint.
+ //
+ // (On Windows, we use WorkingSetSize.)
+ sAllocatedMemInKB = U32Bytes(info.resident_size - info.reusable);
+ }
+ else
+ {
+ LL_WARNS() << "task_info failed" << LL_ENDL;
+ }
+
+ // Total installed and available physical memory are properties of the host, not just our process.
+ vm_statistics64_data_t vmstat;
+ mach_msg_type_number_t count = HOST_VM_INFO64_COUNT;
+ mach_port_t host = mach_host_self();
+ vm_size_t page_size;
+ host_page_size(host, &page_size);
+ kern_return_t result = host_statistics64(host, HOST_VM_INFO64, reinterpret_cast<host_info_t>(&vmstat), &count);
+ if (result == KERN_SUCCESS) {
+ // This is what Chrome reports as 'the "Physical Memory Free" value reported by the Memory Monitor in Instruments.'
+ // Note though that inactive pages are not included here and not yet free, but could become so under memory pressure.
+ sAvailPhysicalMemInKB = U32Bytes(vmstat.free_count * page_size);
+ sMaxPhysicalMemInKB = LLMemoryInfo::getHardwareMemSize();
+ }
+ else
+ {
+ LL_WARNS() << "task_info failed" << LL_ENDL;
+ }
+
+#else
+ //not valid for other systems for now.
+ sAllocatedMemInKB = U64Bytes(LLMemory::getCurrentRSS());
+ sMaxPhysicalMemInKB = U64Bytes(U32_MAX);
+ sAvailPhysicalMemInKB = U64Bytes(U32_MAX);
+#endif
+
+ return ;
+}
+
+//
+//this function is to test if there is enough space with the size in the virtual address space.
+//it does not do any real allocation
+//if success, it returns the address where the memory chunk can fit in;
+//otherwise it returns NULL.
+//
+//static
+void* LLMemory::tryToAlloc(void* address, U32 size)
+{
+#if LL_WINDOWS
+ address = VirtualAlloc(address, size, MEM_RESERVE | MEM_TOP_DOWN, PAGE_NOACCESS) ;
+ if(address)
+ {
+ if(!VirtualFree(address, 0, MEM_RELEASE))
+ {
+ LL_ERRS() << "error happens when free some memory reservation." << LL_ENDL ;
+ }
+ }
+ return address ;
+#else
+ return (void*)0x01 ; //skip checking
+#endif
+}
+
+//static
+void LLMemory::logMemoryInfo(bool update)
+{
+ LL_PROFILE_ZONE_SCOPED
+ if(update)
+ {
+ updateMemoryInfo() ;
+ }
+
+ LL_INFOS() << "Current allocated physical memory(KB): " << sAllocatedMemInKB << LL_ENDL ;
+ LL_INFOS() << "Current allocated page size (KB): " << sAllocatedPageSizeInKB << LL_ENDL ;
+ LL_INFOS() << "Current available physical memory(KB): " << sAvailPhysicalMemInKB << LL_ENDL ;
+ LL_INFOS() << "Current max usable memory(KB): " << sMaxPhysicalMemInKB << LL_ENDL ;
+}
+
+//static
+U32Kilobytes LLMemory::getAvailableMemKB()
+{
+ return sAvailPhysicalMemInKB ;
+}
+
+//static
+U32Kilobytes LLMemory::getMaxMemKB()
+{
+ return sMaxPhysicalMemInKB ;
+}
+
+//static
+U32Kilobytes LLMemory::getAllocatedMemKB()
+{
+ return sAllocatedMemInKB ;
+}
+
+//----------------------------------------------------------------------------
+
+#if defined(LL_WINDOWS)
+
+//static
+U64 LLMemory::getCurrentRSS()
+{
+ PROCESS_MEMORY_COUNTERS counters;
+
+ if (!GetProcessMemoryInfo(GetCurrentProcess(), &counters, sizeof(counters)))
+ {
+ LL_WARNS() << "GetProcessMemoryInfo failed" << LL_ENDL;
+ return 0;
+ }
+
+ return counters.WorkingSetSize;
+}
+
+#elif defined(LL_DARWIN)
+
+// if (sysctl(ctl, 2, &page_size, &size, NULL, 0) == -1)
+// {
+// LL_WARNS() << "Couldn't get page size" << LL_ENDL;
+// return 0;
+// } else {
+// return page_size;
+// }
+// }
+
+U64 LLMemory::getCurrentRSS()
+{
+ U64 residentSize = 0;
+ mach_task_basic_info_data_t basicInfo;
+ mach_msg_type_number_t basicInfoCount = MACH_TASK_BASIC_INFO_COUNT;
+ if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&basicInfo, &basicInfoCount) == KERN_SUCCESS)
+ {
+ residentSize = basicInfo.resident_size;
+ // 64-bit macos apps allocate 32 GB or more at startup, and this is reflected in virtual_size.
+ // basicInfo.virtual_size is not what we want.
+ }
+ else
+ {
+ LL_WARNS() << "task_info failed" << LL_ENDL;
+ }
+
+ return residentSize;
+}
+
+#elif defined(LL_LINUX)
+
+U64 LLMemory::getCurrentRSS()
+{
+ struct rusage usage;
+
+ if (getrusage(RUSAGE_SELF, &usage) != 0) {
+ // Error handling code could be here
+ return 0;
+ }
+
+ // ru_maxrss (since Linux 2.6.32)
+ // This is the maximum resident set size used (in kilobytes).
+ return usage.ru_maxrss * 1024;
+}
+
+#else
+
+U64 LLMemory::getCurrentRSS()
+{
+ return 0;
+}
+
+#endif
+
+//--------------------------------------------------------------------
+
+#if defined(LL_WINDOWS) && defined(LL_DEBUG_BUFFER_OVERRUN)
+
+#include <map>
+
+struct mem_info {
+ std::map<void*, void*> memory_info;
+ LLMutex mutex;
+
+ static mem_info& get() {
+ static mem_info instance;
+ return instance;
+ }
+
+private:
+ mem_info(){}
+};
+
+void* ll_aligned_malloc_fallback( size_t size, int align )
+{
+ SYSTEM_INFO sysinfo;
+ GetSystemInfo(&sysinfo);
+
+ unsigned int for_alloc = (size/sysinfo.dwPageSize + !!(size%sysinfo.dwPageSize)) * sysinfo.dwPageSize;
+
+ void *p = VirtualAlloc(NULL, for_alloc+sysinfo.dwPageSize, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
+ if(NULL == p) {
+ // call debugger
+ __asm int 3;
+ }
+ DWORD old;
+ bool Res = VirtualProtect((void*)((char*)p + for_alloc), sysinfo.dwPageSize, PAGE_NOACCESS, &old);
+ if(false == Res) {
+ // call debugger
+ __asm int 3;
+ }
+
+ void* ret = (void*)((char*)p + for_alloc-size);
+
+ {
+ LLMutexLock lock(&mem_info::get().mutex);
+ mem_info::get().memory_info.insert(std::pair<void*, void*>(ret, p));
+ }
+
+
+ return ret;
+}
+
+void ll_aligned_free_fallback( void* ptr )
+{
+ LLMutexLock lock(&mem_info::get().mutex);
+ VirtualFree(mem_info::get().memory_info.find(ptr)->second, 0, MEM_RELEASE);
+ mem_info::get().memory_info.erase(ptr);
+}
+
+#endif
diff --git a/indra/llcommon/llmemory.h b/indra/llcommon/llmemory.h index d4d72c243f..ea360881c6 100644 --- a/indra/llcommon/llmemory.h +++ b/indra/llcommon/llmemory.h @@ -1,418 +1,418 @@ -/** - * @file llmemory.h - * @brief Memory allocation/deallocation header-stuff goes here. - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ -#ifndef LLMEMORY_H -#define LLMEMORY_H - -#include "linden_common.h" -#include "llunits.h" -#include "stdtypes.h" -#if !LL_WINDOWS -#include <stdint.h> -#endif - -class LLMutex ; - -#if LL_WINDOWS && LL_DEBUG -#define LL_CHECK_MEMORY llassert(_CrtCheckMemory()); -#else -#define LL_CHECK_MEMORY -#endif - - -#if LL_WINDOWS -#define LL_ALIGN_OF __alignof -#else -#define LL_ALIGN_OF __align_of__ -#endif - -#if LL_WINDOWS -#define LL_DEFAULT_HEAP_ALIGN 8 -#elif LL_DARWIN -#define LL_DEFAULT_HEAP_ALIGN 16 -#elif LL_LINUX -#define LL_DEFAULT_HEAP_ALIGN 8 -#endif - - -LL_COMMON_API void ll_assert_aligned_func(uintptr_t ptr,U32 alignment); - -#ifdef SHOW_ASSERT -// This is incredibly expensive - in profiling Windows RWD builds, 30% -// of CPU time was in aligment checks. -//#define ASSERT_ALIGNMENT -#endif - -#ifdef ASSERT_ALIGNMENT -#define ll_assert_aligned(ptr,alignment) ll_assert_aligned_func(uintptr_t(ptr),((U32)alignment)) -#else -#define ll_assert_aligned(ptr,alignment) -#endif - -#include <xmmintrin.h> - -template <typename T> T* LL_NEXT_ALIGNED_ADDRESS(T* address) -{ - return reinterpret_cast<T*>( - (uintptr_t(address) + 0xF) & ~0xF); -} - -template <typename T> T* LL_NEXT_ALIGNED_ADDRESS_64(T* address) -{ - return reinterpret_cast<T*>( - (uintptr_t(address) + 0x3F) & ~0x3F); -} - -#if LL_LINUX || LL_DARWIN - -#define LL_ALIGN_PREFIX(x) -#define LL_ALIGN_POSTFIX(x) __attribute__((aligned(x))) - -#elif LL_WINDOWS - -#define LL_ALIGN_PREFIX(x) __declspec(align(x)) -#define LL_ALIGN_POSTFIX(x) - -#else -#error "LL_ALIGN_PREFIX and LL_ALIGN_POSTFIX undefined" -#endif - -#define LL_ALIGN_16(var) LL_ALIGN_PREFIX(16) var LL_ALIGN_POSTFIX(16) - -#define LL_ALIGN_NEW \ -public: \ - void* operator new(size_t size) \ - { \ - return ll_aligned_malloc_16(size); \ - } \ - \ - void operator delete(void* ptr) \ - { \ - ll_aligned_free_16(ptr); \ - } \ - \ - void* operator new[](size_t size) \ - { \ - return ll_aligned_malloc_16(size); \ - } \ - \ - void operator delete[](void* ptr) \ - { \ - ll_aligned_free_16(ptr); \ - } - - -//------------------------------------------------------------------------------------------------ -//------------------------------------------------------------------------------------------------ - // for enable buffer overrun detection predefine LL_DEBUG_BUFFER_OVERRUN in current library - // change preprocessor code to: #if 1 && defined(LL_WINDOWS) - -#if 0 && defined(LL_WINDOWS) - void* ll_aligned_malloc_fallback( size_t size, int align ); - void ll_aligned_free_fallback( void* ptr ); -//------------------------------------------------------------------------------------------------ -#else - inline void* ll_aligned_malloc_fallback( size_t size, int align ) - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY; - #if defined(LL_WINDOWS) - void* ret = _aligned_malloc(size, align); - #else - char* aligned = NULL; - void* mem = malloc( size + (align - 1) + sizeof(void*) ); - if (mem) - { - aligned = ((char*)mem) + sizeof(void*); - aligned += align - ((uintptr_t)aligned & (align - 1)); - - ((void**)aligned)[-1] = mem; - } - void* ret = aligned; - #endif - LL_PROFILE_ALLOC(ret, size); - return ret; - } - - inline void ll_aligned_free_fallback( void* ptr ) - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY; - LL_PROFILE_FREE(ptr); - #if defined(LL_WINDOWS) - _aligned_free(ptr); - #else - if (ptr) - { - free( ((void**)ptr)[-1] ); - } - #endif - } -#endif -//------------------------------------------------------------------------------------------------ -//------------------------------------------------------------------------------------------------ - -inline void* ll_aligned_malloc_16(size_t size) // returned hunk MUST be freed with ll_aligned_free_16(). -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY; -#if defined(LL_WINDOWS) - void* ret = _aligned_malloc(size, 16); -#elif defined(LL_DARWIN) - void* ret = malloc(size); // default osx malloc is 16 byte aligned. -#else - void *ret; - if (0 != posix_memalign(&ret, 16, size)) - return nullptr; -#endif - LL_PROFILE_ALLOC(ret, size); - return ret; -} - -inline void ll_aligned_free_16(void *p) -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY; - LL_PROFILE_FREE(p); -#if defined(LL_WINDOWS) - _aligned_free(p); -#elif defined(LL_DARWIN) - return free(p); -#else - free(p); // posix_memalign() is compatible with heap deallocator -#endif -} - -inline void* ll_aligned_realloc_16(void* ptr, size_t size, size_t old_size) // returned hunk MUST be freed with ll_aligned_free_16(). -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY; - LL_PROFILE_FREE(ptr); -#if defined(LL_WINDOWS) - void* ret = _aligned_realloc(ptr, size, 16); -#elif defined(LL_DARWIN) - void* ret = realloc(ptr,size); // default osx malloc is 16 byte aligned. -#else - //FIXME: memcpy is SLOW - void* ret = ll_aligned_malloc_16(size); - if (ptr) - { - if (ret) - { - // Only copy the size of the smallest memory block to avoid memory corruption. - memcpy(ret, ptr, llmin(old_size, size)); - } - ll_aligned_free_16(ptr); - } -#endif - LL_PROFILE_ALLOC(ptr, size); - return ret; -} - -inline void* ll_aligned_malloc_32(size_t size) // returned hunk MUST be freed with ll_aligned_free_32(). -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY; -#if defined(LL_WINDOWS) - void* ret = _aligned_malloc(size, 32); -#elif defined(LL_DARWIN) - void* ret = ll_aligned_malloc_fallback( size, 32 ); -#else - void *ret; - if (0 != posix_memalign(&ret, 32, size)) - return nullptr; -#endif - LL_PROFILE_ALLOC(ret, size); - return ret; -} - -inline void ll_aligned_free_32(void *p) -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY; - LL_PROFILE_FREE(p); -#if defined(LL_WINDOWS) - _aligned_free(p); -#elif defined(LL_DARWIN) - ll_aligned_free_fallback( p ); -#else - free(p); // posix_memalign() is compatible with heap deallocator -#endif -} - -// general purpose dispatch functions that are forced inline so they can compile down to a single call -template<size_t ALIGNMENT> -LL_FORCE_INLINE void* ll_aligned_malloc(size_t size) -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY; - void* ret; - if (LL_DEFAULT_HEAP_ALIGN % ALIGNMENT == 0) - { - ret = malloc(size); - LL_PROFILE_ALLOC(ret, size); - } - else if (ALIGNMENT == 16) - { - ret = ll_aligned_malloc_16(size); - } - else if (ALIGNMENT == 32) - { - ret = ll_aligned_malloc_32(size); - } - else - { - ret = ll_aligned_malloc_fallback(size, ALIGNMENT); - } - return ret; -} - -template<size_t ALIGNMENT> -LL_FORCE_INLINE void ll_aligned_free(void* ptr) -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY; - if (ALIGNMENT == LL_DEFAULT_HEAP_ALIGN) - { - LL_PROFILE_FREE(ptr); - free(ptr); - } - else if (ALIGNMENT == 16) - { - ll_aligned_free_16(ptr); - } - else if (ALIGNMENT == 32) - { - return ll_aligned_free_32(ptr); - } - else - { - return ll_aligned_free_fallback(ptr); - } -} - -// Copy words 16-byte blocks from src to dst. Source and destination MUST NOT OVERLAP. -// Source and dest must be 16-byte aligned and size must be multiple of 16. -// -inline void ll_memcpy_nonaliased_aligned_16(char* __restrict dst, const char* __restrict src, size_t bytes) -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY; - assert(src != NULL); - assert(dst != NULL); - assert(bytes > 0); - assert((bytes % sizeof(F32))== 0); - ll_assert_aligned(src,16); - ll_assert_aligned(dst,16); - - assert((src < dst) ? ((src + bytes) <= dst) : ((dst + bytes) <= src)); - assert(bytes%16==0); - - char* end = dst + bytes; - - if (bytes > 64) - { - - // Find start of 64b aligned area within block - // - void* begin_64 = LL_NEXT_ALIGNED_ADDRESS_64(dst); - - //at least 64 bytes before the end of the destination, switch to 16 byte copies - void* end_64 = end-64; - - // Prefetch the head of the 64b area now - // - _mm_prefetch((char*)begin_64, _MM_HINT_NTA); - _mm_prefetch((char*)begin_64 + 64, _MM_HINT_NTA); - _mm_prefetch((char*)begin_64 + 128, _MM_HINT_NTA); - _mm_prefetch((char*)begin_64 + 192, _MM_HINT_NTA); - - // Copy 16b chunks until we're 64b aligned - // - while (dst < begin_64) - { - - _mm_store_ps((F32*)dst, _mm_load_ps((F32*)src)); - dst += 16; - src += 16; - } - - // Copy 64b chunks up to your tail - // - // might be good to shmoo the 512b prefetch offset - // (characterize performance for various values) - // - while (dst < end_64) - { - _mm_prefetch((char*)src + 512, _MM_HINT_NTA); - _mm_prefetch((char*)dst + 512, _MM_HINT_NTA); - _mm_store_ps((F32*)dst, _mm_load_ps((F32*)src)); - _mm_store_ps((F32*)(dst + 16), _mm_load_ps((F32*)(src + 16))); - _mm_store_ps((F32*)(dst + 32), _mm_load_ps((F32*)(src + 32))); - _mm_store_ps((F32*)(dst + 48), _mm_load_ps((F32*)(src + 48))); - dst += 64; - src += 64; - } - } - - // Copy remainder 16b tail chunks (or ALL 16b chunks for sub-64b copies) - // - while (dst < end) - { - _mm_store_ps((F32*)dst, _mm_load_ps((F32*)src)); - dst += 16; - src += 16; - } -} - -#ifndef __DEBUG_PRIVATE_MEM__ -#define __DEBUG_PRIVATE_MEM__ 0 -#endif - -class LL_COMMON_API LLMemory -{ -public: - // Return the resident set size of the current process, in bytes. - // Return value is zero if not known. - static U64 getCurrentRSS(); - static void* tryToAlloc(void* address, U32 size); - static void initMaxHeapSizeGB(F32Gigabytes max_heap_size); - static void updateMemoryInfo() ; - static void logMemoryInfo(bool update = false); - - static U32Kilobytes getAvailableMemKB() ; - static U32Kilobytes getMaxMemKB() ; - static U32Kilobytes getAllocatedMemKB() ; -private: - static U32Kilobytes sAvailPhysicalMemInKB ; - static U32Kilobytes sMaxPhysicalMemInKB ; - static U32Kilobytes sAllocatedMemInKB; - static U32Kilobytes sAllocatedPageSizeInKB ; - - static U32Kilobytes sMaxHeapSizeInKB; -}; - -// 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=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+#ifndef LLMEMORY_H
+#define LLMEMORY_H
+
+#include "linden_common.h"
+#include "llunits.h"
+#include "stdtypes.h"
+#if !LL_WINDOWS
+#include <stdint.h>
+#endif
+
+class LLMutex ;
+
+#if LL_WINDOWS && LL_DEBUG
+#define LL_CHECK_MEMORY llassert(_CrtCheckMemory());
+#else
+#define LL_CHECK_MEMORY
+#endif
+
+
+#if LL_WINDOWS
+#define LL_ALIGN_OF __alignof
+#else
+#define LL_ALIGN_OF __align_of__
+#endif
+
+#if LL_WINDOWS
+#define LL_DEFAULT_HEAP_ALIGN 8
+#elif LL_DARWIN
+#define LL_DEFAULT_HEAP_ALIGN 16
+#elif LL_LINUX
+#define LL_DEFAULT_HEAP_ALIGN 8
+#endif
+
+
+LL_COMMON_API void ll_assert_aligned_func(uintptr_t ptr,U32 alignment);
+
+#ifdef SHOW_ASSERT
+// This is incredibly expensive - in profiling Windows RWD builds, 30%
+// of CPU time was in aligment checks.
+//#define ASSERT_ALIGNMENT
+#endif
+
+#ifdef ASSERT_ALIGNMENT
+#define ll_assert_aligned(ptr,alignment) ll_assert_aligned_func(uintptr_t(ptr),((U32)alignment))
+#else
+#define ll_assert_aligned(ptr,alignment)
+#endif
+
+#include <xmmintrin.h>
+
+template <typename T> T* LL_NEXT_ALIGNED_ADDRESS(T* address)
+{
+ return reinterpret_cast<T*>(
+ (uintptr_t(address) + 0xF) & ~0xF);
+}
+
+template <typename T> T* LL_NEXT_ALIGNED_ADDRESS_64(T* address)
+{
+ return reinterpret_cast<T*>(
+ (uintptr_t(address) + 0x3F) & ~0x3F);
+}
+
+#if LL_LINUX || LL_DARWIN
+
+#define LL_ALIGN_PREFIX(x)
+#define LL_ALIGN_POSTFIX(x) __attribute__((aligned(x)))
+
+#elif LL_WINDOWS
+
+#define LL_ALIGN_PREFIX(x) __declspec(align(x))
+#define LL_ALIGN_POSTFIX(x)
+
+#else
+#error "LL_ALIGN_PREFIX and LL_ALIGN_POSTFIX undefined"
+#endif
+
+#define LL_ALIGN_16(var) LL_ALIGN_PREFIX(16) var LL_ALIGN_POSTFIX(16)
+
+#define LL_ALIGN_NEW \
+public: \
+ void* operator new(size_t size) \
+ { \
+ return ll_aligned_malloc_16(size); \
+ } \
+ \
+ void operator delete(void* ptr) \
+ { \
+ ll_aligned_free_16(ptr); \
+ } \
+ \
+ void* operator new[](size_t size) \
+ { \
+ return ll_aligned_malloc_16(size); \
+ } \
+ \
+ void operator delete[](void* ptr) \
+ { \
+ ll_aligned_free_16(ptr); \
+ }
+
+
+//------------------------------------------------------------------------------------------------
+//------------------------------------------------------------------------------------------------
+ // for enable buffer overrun detection predefine LL_DEBUG_BUFFER_OVERRUN in current library
+ // change preprocessor code to: #if 1 && defined(LL_WINDOWS)
+
+#if 0 && defined(LL_WINDOWS)
+ void* ll_aligned_malloc_fallback( size_t size, int align );
+ void ll_aligned_free_fallback( void* ptr );
+//------------------------------------------------------------------------------------------------
+#else
+ inline void* ll_aligned_malloc_fallback( size_t size, int align )
+ {
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY;
+ #if defined(LL_WINDOWS)
+ void* ret = _aligned_malloc(size, align);
+ #else
+ char* aligned = NULL;
+ void* mem = malloc( size + (align - 1) + sizeof(void*) );
+ if (mem)
+ {
+ aligned = ((char*)mem) + sizeof(void*);
+ aligned += align - ((uintptr_t)aligned & (align - 1));
+
+ ((void**)aligned)[-1] = mem;
+ }
+ void* ret = aligned;
+ #endif
+ LL_PROFILE_ALLOC(ret, size);
+ return ret;
+ }
+
+ inline void ll_aligned_free_fallback( void* ptr )
+ {
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY;
+ LL_PROFILE_FREE(ptr);
+ #if defined(LL_WINDOWS)
+ _aligned_free(ptr);
+ #else
+ if (ptr)
+ {
+ free( ((void**)ptr)[-1] );
+ }
+ #endif
+ }
+#endif
+//------------------------------------------------------------------------------------------------
+//------------------------------------------------------------------------------------------------
+
+inline void* ll_aligned_malloc_16(size_t size) // returned hunk MUST be freed with ll_aligned_free_16().
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY;
+#if defined(LL_WINDOWS)
+ void* ret = _aligned_malloc(size, 16);
+#elif defined(LL_DARWIN)
+ void* ret = malloc(size); // default osx malloc is 16 byte aligned.
+#else
+ void *ret;
+ if (0 != posix_memalign(&ret, 16, size))
+ return nullptr;
+#endif
+ LL_PROFILE_ALLOC(ret, size);
+ return ret;
+}
+
+inline void ll_aligned_free_16(void *p)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY;
+ LL_PROFILE_FREE(p);
+#if defined(LL_WINDOWS)
+ _aligned_free(p);
+#elif defined(LL_DARWIN)
+ return free(p);
+#else
+ free(p); // posix_memalign() is compatible with heap deallocator
+#endif
+}
+
+inline void* ll_aligned_realloc_16(void* ptr, size_t size, size_t old_size) // returned hunk MUST be freed with ll_aligned_free_16().
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY;
+ LL_PROFILE_FREE(ptr);
+#if defined(LL_WINDOWS)
+ void* ret = _aligned_realloc(ptr, size, 16);
+#elif defined(LL_DARWIN)
+ void* ret = realloc(ptr,size); // default osx malloc is 16 byte aligned.
+#else
+ //FIXME: memcpy is SLOW
+ void* ret = ll_aligned_malloc_16(size);
+ if (ptr)
+ {
+ if (ret)
+ {
+ // Only copy the size of the smallest memory block to avoid memory corruption.
+ memcpy(ret, ptr, llmin(old_size, size));
+ }
+ ll_aligned_free_16(ptr);
+ }
+#endif
+ LL_PROFILE_ALLOC(ptr, size);
+ return ret;
+}
+
+inline void* ll_aligned_malloc_32(size_t size) // returned hunk MUST be freed with ll_aligned_free_32().
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY;
+#if defined(LL_WINDOWS)
+ void* ret = _aligned_malloc(size, 32);
+#elif defined(LL_DARWIN)
+ void* ret = ll_aligned_malloc_fallback( size, 32 );
+#else
+ void *ret;
+ if (0 != posix_memalign(&ret, 32, size))
+ return nullptr;
+#endif
+ LL_PROFILE_ALLOC(ret, size);
+ return ret;
+}
+
+inline void ll_aligned_free_32(void *p)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY;
+ LL_PROFILE_FREE(p);
+#if defined(LL_WINDOWS)
+ _aligned_free(p);
+#elif defined(LL_DARWIN)
+ ll_aligned_free_fallback( p );
+#else
+ free(p); // posix_memalign() is compatible with heap deallocator
+#endif
+}
+
+// general purpose dispatch functions that are forced inline so they can compile down to a single call
+template<size_t ALIGNMENT>
+LL_FORCE_INLINE void* ll_aligned_malloc(size_t size)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY;
+ void* ret;
+ if (LL_DEFAULT_HEAP_ALIGN % ALIGNMENT == 0)
+ {
+ ret = malloc(size);
+ LL_PROFILE_ALLOC(ret, size);
+ }
+ else if (ALIGNMENT == 16)
+ {
+ ret = ll_aligned_malloc_16(size);
+ }
+ else if (ALIGNMENT == 32)
+ {
+ ret = ll_aligned_malloc_32(size);
+ }
+ else
+ {
+ ret = ll_aligned_malloc_fallback(size, ALIGNMENT);
+ }
+ return ret;
+}
+
+template<size_t ALIGNMENT>
+LL_FORCE_INLINE void ll_aligned_free(void* ptr)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY;
+ if (ALIGNMENT == LL_DEFAULT_HEAP_ALIGN)
+ {
+ LL_PROFILE_FREE(ptr);
+ free(ptr);
+ }
+ else if (ALIGNMENT == 16)
+ {
+ ll_aligned_free_16(ptr);
+ }
+ else if (ALIGNMENT == 32)
+ {
+ return ll_aligned_free_32(ptr);
+ }
+ else
+ {
+ return ll_aligned_free_fallback(ptr);
+ }
+}
+
+// Copy words 16-byte blocks from src to dst. Source and destination MUST NOT OVERLAP.
+// Source and dest must be 16-byte aligned and size must be multiple of 16.
+//
+inline void ll_memcpy_nonaliased_aligned_16(char* __restrict dst, const char* __restrict src, size_t bytes)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY;
+ assert(src != NULL);
+ assert(dst != NULL);
+ assert(bytes > 0);
+ assert((bytes % sizeof(F32))== 0);
+ ll_assert_aligned(src,16);
+ ll_assert_aligned(dst,16);
+
+ assert((src < dst) ? ((src + bytes) <= dst) : ((dst + bytes) <= src));
+ assert(bytes%16==0);
+
+ char* end = dst + bytes;
+
+ if (bytes > 64)
+ {
+
+ // Find start of 64b aligned area within block
+ //
+ void* begin_64 = LL_NEXT_ALIGNED_ADDRESS_64(dst);
+
+ //at least 64 bytes before the end of the destination, switch to 16 byte copies
+ void* end_64 = end-64;
+
+ // Prefetch the head of the 64b area now
+ //
+ _mm_prefetch((char*)begin_64, _MM_HINT_NTA);
+ _mm_prefetch((char*)begin_64 + 64, _MM_HINT_NTA);
+ _mm_prefetch((char*)begin_64 + 128, _MM_HINT_NTA);
+ _mm_prefetch((char*)begin_64 + 192, _MM_HINT_NTA);
+
+ // Copy 16b chunks until we're 64b aligned
+ //
+ while (dst < begin_64)
+ {
+
+ _mm_store_ps((F32*)dst, _mm_load_ps((F32*)src));
+ dst += 16;
+ src += 16;
+ }
+
+ // Copy 64b chunks up to your tail
+ //
+ // might be good to shmoo the 512b prefetch offset
+ // (characterize performance for various values)
+ //
+ while (dst < end_64)
+ {
+ _mm_prefetch((char*)src + 512, _MM_HINT_NTA);
+ _mm_prefetch((char*)dst + 512, _MM_HINT_NTA);
+ _mm_store_ps((F32*)dst, _mm_load_ps((F32*)src));
+ _mm_store_ps((F32*)(dst + 16), _mm_load_ps((F32*)(src + 16)));
+ _mm_store_ps((F32*)(dst + 32), _mm_load_ps((F32*)(src + 32)));
+ _mm_store_ps((F32*)(dst + 48), _mm_load_ps((F32*)(src + 48)));
+ dst += 64;
+ src += 64;
+ }
+ }
+
+ // Copy remainder 16b tail chunks (or ALL 16b chunks for sub-64b copies)
+ //
+ while (dst < end)
+ {
+ _mm_store_ps((F32*)dst, _mm_load_ps((F32*)src));
+ dst += 16;
+ src += 16;
+ }
+}
+
+#ifndef __DEBUG_PRIVATE_MEM__
+#define __DEBUG_PRIVATE_MEM__ 0
+#endif
+
+class LL_COMMON_API LLMemory
+{
+public:
+ // Return the resident set size of the current process, in bytes.
+ // Return value is zero if not known.
+ static U64 getCurrentRSS();
+ static void* tryToAlloc(void* address, U32 size);
+ static void initMaxHeapSizeGB(F32Gigabytes max_heap_size);
+ static void updateMemoryInfo() ;
+ static void logMemoryInfo(bool update = false);
+
+ static U32Kilobytes getAvailableMemKB() ;
+ static U32Kilobytes getMaxMemKB() ;
+ static U32Kilobytes getAllocatedMemKB() ;
+private:
+ static U32Kilobytes sAvailPhysicalMemInKB ;
+ static U32Kilobytes sMaxPhysicalMemInKB ;
+ static U32Kilobytes sAllocatedMemInKB;
+ static U32Kilobytes sAllocatedPageSizeInKB ;
+
+ static U32Kilobytes sMaxHeapSizeInKB;
+};
+
+// 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.cpp b/indra/llcommon/llmemorystream.cpp index 707ac8fd0f..b3acc8c7d3 100644 --- a/indra/llcommon/llmemorystream.cpp +++ b/indra/llcommon/llmemorystream.cpp @@ -1,4 +1,4 @@ -/** +/** * @file llmemorystream.cpp * @author Phoenix * @date 2005-06-03 @@ -7,21 +7,21 @@ * $LicenseInfo:firstyear=2005&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -31,7 +31,7 @@ LLMemoryStreamBuf::LLMemoryStreamBuf(const U8* start, S32 length) { - reset(start, length); + reset(start, length); } LLMemoryStreamBuf::~LLMemoryStreamBuf() @@ -40,26 +40,26 @@ LLMemoryStreamBuf::~LLMemoryStreamBuf() void LLMemoryStreamBuf::reset(const U8* start, S32 length) { - setg((char*)start, (char*)start, (char*)start + length); + setg((char*)start, (char*)start, (char*)start + length); } int LLMemoryStreamBuf::underflow() { - //LL_DEBUGS() << "LLMemoryStreamBuf::underflow()" << LL_ENDL; - if(gptr() < egptr()) - { - return *gptr(); - } - return EOF; + //LL_DEBUGS() << "LLMemoryStreamBuf::underflow()" << LL_ENDL; + if(gptr() < egptr()) + { + return *gptr(); + } + return EOF; } -/** +/** * @class LLMemoryStreamBuf */ LLMemoryStream::LLMemoryStream(const U8* start, S32 length) : - std::istream(&mStreamBuf), - mStreamBuf(start, length) + std::istream(&mStreamBuf), + mStreamBuf(start, length) { } diff --git a/indra/llcommon/llmemorystream.h b/indra/llcommon/llmemorystream.h index e28f012192..6024bc5d37 100644 --- a/indra/llcommon/llmemorystream.h +++ b/indra/llcommon/llmemorystream.h @@ -1,4 +1,4 @@ -/** +/** * @file llmemorystream.h * @author Phoenix * @date 2005-06-03 @@ -7,21 +7,21 @@ * $LicenseInfo:firstyear=2005&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -29,7 +29,7 @@ #ifndef LL_LLMEMORYSTREAM_H #define LL_LLMEMORYSTREAM_H -/** +/** * This is a simple but effective optimization when you want to treat * a chunk of memory as an istream. I wrote this to avoid turing a * buffer into a string, and then throwing the string into an @@ -38,7 +38,7 @@ #include <iostream> -/** +/** * @class LLMemoryStreamBuf * @brief This implements a wrapper around a piece of memory for istreams * @@ -49,18 +49,18 @@ class LL_COMMON_API LLMemoryStreamBuf : public std::streambuf { public: - LLMemoryStreamBuf(const U8* start, S32 length); - ~LLMemoryStreamBuf(); + LLMemoryStreamBuf(const U8* start, S32 length); + ~LLMemoryStreamBuf(); - void reset(const U8* start, S32 length); + void reset(const U8* start, S32 length); protected: - int underflow(); - //std::streamsize xsgetn(char* dest, std::streamsize n); + int underflow(); + //std::streamsize xsgetn(char* dest, std::streamsize n); }; -/** +/** * @class LLMemoryStream * @brief This implements a wrapper around a piece of memory for istreams * @@ -71,11 +71,11 @@ protected: class LL_COMMON_API LLMemoryStream : public std::istream { public: - LLMemoryStream(const U8* start, S32 length); - ~LLMemoryStream(); + LLMemoryStream(const U8* start, S32 length); + ~LLMemoryStream(); protected: - LLMemoryStreamBuf mStreamBuf; + LLMemoryStreamBuf mStreamBuf; }; #endif // LL_LLMEMORYSTREAM_H diff --git a/indra/llcommon/llmetricperformancetester.cpp b/indra/llcommon/llmetricperformancetester.cpp index 839689bb87..addee9fdbf 100644 --- a/indra/llcommon/llmetricperformancetester.cpp +++ b/indra/llcommon/llmetricperformancetester.cpp @@ -1,333 +1,333 @@ -/** - * @file llmetricperformancetester.cpp - * @brief LLMetricPerformanceTesterBasic and LLMetricPerformanceTesterWithSession classes implementation - * - * $LicenseInfo:firstyear=2004&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" - -#include "indra_constants.h" -#include "llerror.h" -#include "llsdserialize.h" -#include "lltreeiterators.h" -#include "llmetricperformancetester.h" -#include "llfasttimer.h" - -//---------------------------------------------------------------------------------------------- -// LLMetricPerformanceTesterBasic : static methods and testers management -//---------------------------------------------------------------------------------------------- - -LLMetricPerformanceTesterBasic::name_tester_map_t LLMetricPerformanceTesterBasic::sTesterMap ; - -/*static*/ -void LLMetricPerformanceTesterBasic::cleanupClass() -{ - for (name_tester_map_t::value_type& pair : sTesterMap) - { - delete pair.second; - } - sTesterMap.clear() ; -} - -/*static*/ -bool LLMetricPerformanceTesterBasic::addTester(LLMetricPerformanceTesterBasic* tester) -{ - llassert_always(tester != NULL); - std::string name = tester->getTesterName() ; - if (getTester(name)) - { - LL_ERRS() << "Tester name is already used by some other tester : " << name << LL_ENDL ; - return false; - } - - sTesterMap.insert(std::make_pair(name, tester)); - return true; -} - -/*static*/ -void LLMetricPerformanceTesterBasic::deleteTester(std::string name) -{ - name_tester_map_t::iterator tester = sTesterMap.find(name); - if (tester != sTesterMap.end()) - { - delete tester->second; - sTesterMap.erase(tester); - } -} - -/*static*/ -LLMetricPerformanceTesterBasic* LLMetricPerformanceTesterBasic::getTester(std::string name) -{ - // Check for the requested metric name - name_tester_map_t::iterator found_it = sTesterMap.find(name) ; - if (found_it != sTesterMap.end()) - { - return found_it->second ; - } - return NULL ; -} - -/*static*/ -// Return true if this metric is requested or if the general default "catch all" metric is requested -bool LLMetricPerformanceTesterBasic::isMetricLogRequested(std::string name) -{ - return (LLTrace::BlockTimer::sMetricLog && ((LLTrace::BlockTimer::sLogName == name) || (LLTrace::BlockTimer::sLogName == DEFAULT_METRIC_NAME))); -} - -/*static*/ -LLSD LLMetricPerformanceTesterBasic::analyzeMetricPerformanceLog(std::istream& is) -{ - LLSD ret; - LLSD cur; - - while (!is.eof() && LLSDParser::PARSE_FAILURE != LLSDSerialize::fromXML(cur, is)) - { - for (LLSD::map_iterator iter = cur.beginMap(); iter != cur.endMap(); ++iter) - { - std::string label = iter->first; - - LLMetricPerformanceTesterBasic* tester = LLMetricPerformanceTesterBasic::getTester(iter->second["Name"].asString()) ; - if(tester) - { - ret[label]["Name"] = iter->second["Name"] ; - - auto num_of_metrics = tester->getNumberOfMetrics() ; - for(size_t index = 0 ; index < num_of_metrics ; index++) - { - ret[label][ tester->getMetricName(index) ] = iter->second[ tester->getMetricName(index) ] ; - } - } - } - } - - return ret; -} - -/*static*/ -void LLMetricPerformanceTesterBasic::doAnalysisMetrics(std::string baseline, std::string target, std::string output) -{ - if(!LLMetricPerformanceTesterBasic::hasMetricPerformanceTesters()) - { - return ; - } - - // Open baseline and current target, exit if one is inexistent - llifstream base_is(baseline.c_str()); - llifstream target_is(target.c_str()); - if (!base_is.is_open() || !target_is.is_open()) - { - LL_WARNS() << "'-analyzeperformance' error : baseline or current target file inexistent" << LL_ENDL; - base_is.close(); - target_is.close(); - return; - } - - //analyze baseline - LLSD base = analyzeMetricPerformanceLog(base_is); - base_is.close(); - - //analyze current - LLSD current = analyzeMetricPerformanceLog(target_is); - target_is.close(); - - //output comparision - llofstream os(output.c_str()); - - os << "Label, Metric, Base(B), Target(T), Diff(T-B), Percentage(100*T/B)\n"; - for (LLMetricPerformanceTesterBasic::name_tester_map_t::value_type& pair : LLMetricPerformanceTesterBasic::sTesterMap) - { - LLMetricPerformanceTesterBasic* tester = ((LLMetricPerformanceTesterBasic*)pair.second); - tester->analyzePerformance(&os, &base, ¤t) ; - } - - os.flush(); - os.close(); -} - - -//---------------------------------------------------------------------------------------------- -// LLMetricPerformanceTesterBasic : Tester instance methods -//---------------------------------------------------------------------------------------------- - -LLMetricPerformanceTesterBasic::LLMetricPerformanceTesterBasic(std::string name) : - mName(name), - mCount(0) -{ - if (mName == std::string()) - { - LL_ERRS() << "LLMetricPerformanceTesterBasic construction invalid : Empty name passed to constructor" << LL_ENDL ; - } - - mValidInstance = LLMetricPerformanceTesterBasic::addTester(this) ; -} - -LLMetricPerformanceTesterBasic::~LLMetricPerformanceTesterBasic() -{ -} - -void LLMetricPerformanceTesterBasic::preOutputTestResults(LLSD* sd) -{ - incrementCurrentCount() ; -} - -void LLMetricPerformanceTesterBasic::postOutputTestResults(LLSD* sd) -{ - LLTrace::BlockTimer::pushLog(*sd); -} - -void LLMetricPerformanceTesterBasic::outputTestResults() -{ - LLSD sd; - - preOutputTestResults(&sd) ; - outputTestRecord(&sd) ; - postOutputTestResults(&sd) ; -} - -void LLMetricPerformanceTesterBasic::addMetric(std::string str) -{ - mMetricStrings.push_back(str) ; -} - -/*virtual*/ -void LLMetricPerformanceTesterBasic::analyzePerformance(llofstream* os, LLSD* base, LLSD* current) -{ - resetCurrentCount() ; - - std::string current_label = getCurrentLabelName(); - bool in_base = (*base).has(current_label) ; - bool in_current = (*current).has(current_label) ; - - while(in_base || in_current) - { - LLSD::String label = current_label ; - - if(in_base && in_current) - { - *os << llformat("%s\n", label.c_str()) ; - - for(U32 index = 0 ; index < mMetricStrings.size() ; index++) - { - switch((*current)[label][ mMetricStrings[index] ].type()) - { - case LLSD::TypeInteger: - compareTestResults(os, mMetricStrings[index], - (S32)((*base)[label][ mMetricStrings[index] ].asInteger()), (S32)((*current)[label][ mMetricStrings[index] ].asInteger())) ; - break ; - case LLSD::TypeReal: - compareTestResults(os, mMetricStrings[index], - (F32)((*base)[label][ mMetricStrings[index] ].asReal()), (F32)((*current)[label][ mMetricStrings[index] ].asReal())) ; - break; - default: - LL_ERRS() << "unsupported metric " << mMetricStrings[index] << " LLSD type: " << (S32)(*current)[label][ mMetricStrings[index] ].type() << LL_ENDL ; - } - } - } - - incrementCurrentCount(); - current_label = getCurrentLabelName(); - in_base = (*base).has(current_label) ; - in_current = (*current).has(current_label) ; - } -} - -/*virtual*/ -void LLMetricPerformanceTesterBasic::compareTestResults(llofstream* os, std::string metric_string, S32 v_base, S32 v_current) -{ - *os << llformat(" ,%s, %d, %d, %d, %.4f\n", metric_string.c_str(), v_base, v_current, - v_current - v_base, (v_base != 0) ? 100.f * v_current / v_base : 0) ; -} - -/*virtual*/ -void LLMetricPerformanceTesterBasic::compareTestResults(llofstream* os, std::string metric_string, F32 v_base, F32 v_current) -{ - *os << llformat(" ,%s, %.4f, %.4f, %.4f, %.4f\n", metric_string.c_str(), v_base, v_current, - v_current - v_base, (fabs(v_base) > 0.0001f) ? 100.f * v_current / v_base : 0.f ) ; -} - -//---------------------------------------------------------------------------------------------- -// LLMetricPerformanceTesterWithSession -//---------------------------------------------------------------------------------------------- - -LLMetricPerformanceTesterWithSession::LLMetricPerformanceTesterWithSession(std::string name) : - LLMetricPerformanceTesterBasic(name), - mBaseSessionp(NULL), - mCurrentSessionp(NULL) -{ -} - -LLMetricPerformanceTesterWithSession::~LLMetricPerformanceTesterWithSession() -{ - if (mBaseSessionp) - { - delete mBaseSessionp ; - mBaseSessionp = NULL ; - } - if (mCurrentSessionp) - { - delete mCurrentSessionp ; - mCurrentSessionp = NULL ; - } -} - -/*virtual*/ -void LLMetricPerformanceTesterWithSession::analyzePerformance(llofstream* os, LLSD* base, LLSD* current) -{ - // Load the base session - resetCurrentCount() ; - mBaseSessionp = loadTestSession(base) ; - - // Load the current session - resetCurrentCount() ; - mCurrentSessionp = loadTestSession(current) ; - - if (!mBaseSessionp || !mCurrentSessionp) - { - LL_ERRS() << "Error loading test sessions." << LL_ENDL ; - } - - // Compare - compareTestSessions(os) ; - - // Release memory - if (mBaseSessionp) - { - delete mBaseSessionp ; - mBaseSessionp = NULL ; - } - if (mCurrentSessionp) - { - delete mCurrentSessionp ; - mCurrentSessionp = NULL ; - } -} - - -//---------------------------------------------------------------------------------------------- -// LLTestSession -//---------------------------------------------------------------------------------------------- - -LLMetricPerformanceTesterWithSession::LLTestSession::~LLTestSession() -{ -} - +/**
+ * @file llmetricperformancetester.cpp
+ * @brief LLMetricPerformanceTesterBasic and LLMetricPerformanceTesterWithSession classes implementation
+ *
+ * $LicenseInfo:firstyear=2004&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "indra_constants.h"
+#include "llerror.h"
+#include "llsdserialize.h"
+#include "lltreeiterators.h"
+#include "llmetricperformancetester.h"
+#include "llfasttimer.h"
+
+//----------------------------------------------------------------------------------------------
+// LLMetricPerformanceTesterBasic : static methods and testers management
+//----------------------------------------------------------------------------------------------
+
+LLMetricPerformanceTesterBasic::name_tester_map_t LLMetricPerformanceTesterBasic::sTesterMap ;
+
+/*static*/
+void LLMetricPerformanceTesterBasic::cleanupClass()
+{
+ for (name_tester_map_t::value_type& pair : sTesterMap)
+ {
+ delete pair.second;
+ }
+ sTesterMap.clear() ;
+}
+
+/*static*/
+bool LLMetricPerformanceTesterBasic::addTester(LLMetricPerformanceTesterBasic* tester)
+{
+ llassert_always(tester != NULL);
+ std::string name = tester->getTesterName() ;
+ if (getTester(name))
+ {
+ LL_ERRS() << "Tester name is already used by some other tester : " << name << LL_ENDL ;
+ return false;
+ }
+
+ sTesterMap.insert(std::make_pair(name, tester));
+ return true;
+}
+
+/*static*/
+void LLMetricPerformanceTesterBasic::deleteTester(std::string name)
+{
+ name_tester_map_t::iterator tester = sTesterMap.find(name);
+ if (tester != sTesterMap.end())
+ {
+ delete tester->second;
+ sTesterMap.erase(tester);
+ }
+}
+
+/*static*/
+LLMetricPerformanceTesterBasic* LLMetricPerformanceTesterBasic::getTester(std::string name)
+{
+ // Check for the requested metric name
+ name_tester_map_t::iterator found_it = sTesterMap.find(name) ;
+ if (found_it != sTesterMap.end())
+ {
+ return found_it->second ;
+ }
+ return NULL ;
+}
+
+/*static*/
+// Return true if this metric is requested or if the general default "catch all" metric is requested
+bool LLMetricPerformanceTesterBasic::isMetricLogRequested(std::string name)
+{
+ return (LLTrace::BlockTimer::sMetricLog && ((LLTrace::BlockTimer::sLogName == name) || (LLTrace::BlockTimer::sLogName == DEFAULT_METRIC_NAME)));
+}
+
+/*static*/
+LLSD LLMetricPerformanceTesterBasic::analyzeMetricPerformanceLog(std::istream& is)
+{
+ LLSD ret;
+ LLSD cur;
+
+ while (!is.eof() && LLSDParser::PARSE_FAILURE != LLSDSerialize::fromXML(cur, is))
+ {
+ for (LLSD::map_iterator iter = cur.beginMap(); iter != cur.endMap(); ++iter)
+ {
+ std::string label = iter->first;
+
+ LLMetricPerformanceTesterBasic* tester = LLMetricPerformanceTesterBasic::getTester(iter->second["Name"].asString()) ;
+ if(tester)
+ {
+ ret[label]["Name"] = iter->second["Name"] ;
+
+ auto num_of_metrics = tester->getNumberOfMetrics() ;
+ for(size_t index = 0 ; index < num_of_metrics ; index++)
+ {
+ ret[label][ tester->getMetricName(index) ] = iter->second[ tester->getMetricName(index) ] ;
+ }
+ }
+ }
+ }
+
+ return ret;
+}
+
+/*static*/
+void LLMetricPerformanceTesterBasic::doAnalysisMetrics(std::string baseline, std::string target, std::string output)
+{
+ if(!LLMetricPerformanceTesterBasic::hasMetricPerformanceTesters())
+ {
+ return ;
+ }
+
+ // Open baseline and current target, exit if one is inexistent
+ llifstream base_is(baseline.c_str());
+ llifstream target_is(target.c_str());
+ if (!base_is.is_open() || !target_is.is_open())
+ {
+ LL_WARNS() << "'-analyzeperformance' error : baseline or current target file inexistent" << LL_ENDL;
+ base_is.close();
+ target_is.close();
+ return;
+ }
+
+ //analyze baseline
+ LLSD base = analyzeMetricPerformanceLog(base_is);
+ base_is.close();
+
+ //analyze current
+ LLSD current = analyzeMetricPerformanceLog(target_is);
+ target_is.close();
+
+ //output comparision
+ llofstream os(output.c_str());
+
+ os << "Label, Metric, Base(B), Target(T), Diff(T-B), Percentage(100*T/B)\n";
+ for (LLMetricPerformanceTesterBasic::name_tester_map_t::value_type& pair : LLMetricPerformanceTesterBasic::sTesterMap)
+ {
+ LLMetricPerformanceTesterBasic* tester = ((LLMetricPerformanceTesterBasic*)pair.second);
+ tester->analyzePerformance(&os, &base, ¤t) ;
+ }
+
+ os.flush();
+ os.close();
+}
+
+
+//----------------------------------------------------------------------------------------------
+// LLMetricPerformanceTesterBasic : Tester instance methods
+//----------------------------------------------------------------------------------------------
+
+LLMetricPerformanceTesterBasic::LLMetricPerformanceTesterBasic(std::string name) :
+ mName(name),
+ mCount(0)
+{
+ if (mName == std::string())
+ {
+ LL_ERRS() << "LLMetricPerformanceTesterBasic construction invalid : Empty name passed to constructor" << LL_ENDL ;
+ }
+
+ mValidInstance = LLMetricPerformanceTesterBasic::addTester(this) ;
+}
+
+LLMetricPerformanceTesterBasic::~LLMetricPerformanceTesterBasic()
+{
+}
+
+void LLMetricPerformanceTesterBasic::preOutputTestResults(LLSD* sd)
+{
+ incrementCurrentCount() ;
+}
+
+void LLMetricPerformanceTesterBasic::postOutputTestResults(LLSD* sd)
+{
+ LLTrace::BlockTimer::pushLog(*sd);
+}
+
+void LLMetricPerformanceTesterBasic::outputTestResults()
+{
+ LLSD sd;
+
+ preOutputTestResults(&sd) ;
+ outputTestRecord(&sd) ;
+ postOutputTestResults(&sd) ;
+}
+
+void LLMetricPerformanceTesterBasic::addMetric(std::string str)
+{
+ mMetricStrings.push_back(str) ;
+}
+
+/*virtual*/
+void LLMetricPerformanceTesterBasic::analyzePerformance(llofstream* os, LLSD* base, LLSD* current)
+{
+ resetCurrentCount() ;
+
+ std::string current_label = getCurrentLabelName();
+ bool in_base = (*base).has(current_label) ;
+ bool in_current = (*current).has(current_label) ;
+
+ while(in_base || in_current)
+ {
+ LLSD::String label = current_label ;
+
+ if(in_base && in_current)
+ {
+ *os << llformat("%s\n", label.c_str()) ;
+
+ for(U32 index = 0 ; index < mMetricStrings.size() ; index++)
+ {
+ switch((*current)[label][ mMetricStrings[index] ].type())
+ {
+ case LLSD::TypeInteger:
+ compareTestResults(os, mMetricStrings[index],
+ (S32)((*base)[label][ mMetricStrings[index] ].asInteger()), (S32)((*current)[label][ mMetricStrings[index] ].asInteger())) ;
+ break ;
+ case LLSD::TypeReal:
+ compareTestResults(os, mMetricStrings[index],
+ (F32)((*base)[label][ mMetricStrings[index] ].asReal()), (F32)((*current)[label][ mMetricStrings[index] ].asReal())) ;
+ break;
+ default:
+ LL_ERRS() << "unsupported metric " << mMetricStrings[index] << " LLSD type: " << (S32)(*current)[label][ mMetricStrings[index] ].type() << LL_ENDL ;
+ }
+ }
+ }
+
+ incrementCurrentCount();
+ current_label = getCurrentLabelName();
+ in_base = (*base).has(current_label) ;
+ in_current = (*current).has(current_label) ;
+ }
+}
+
+/*virtual*/
+void LLMetricPerformanceTesterBasic::compareTestResults(llofstream* os, std::string metric_string, S32 v_base, S32 v_current)
+{
+ *os << llformat(" ,%s, %d, %d, %d, %.4f\n", metric_string.c_str(), v_base, v_current,
+ v_current - v_base, (v_base != 0) ? 100.f * v_current / v_base : 0) ;
+}
+
+/*virtual*/
+void LLMetricPerformanceTesterBasic::compareTestResults(llofstream* os, std::string metric_string, F32 v_base, F32 v_current)
+{
+ *os << llformat(" ,%s, %.4f, %.4f, %.4f, %.4f\n", metric_string.c_str(), v_base, v_current,
+ v_current - v_base, (fabs(v_base) > 0.0001f) ? 100.f * v_current / v_base : 0.f ) ;
+}
+
+//----------------------------------------------------------------------------------------------
+// LLMetricPerformanceTesterWithSession
+//----------------------------------------------------------------------------------------------
+
+LLMetricPerformanceTesterWithSession::LLMetricPerformanceTesterWithSession(std::string name) :
+ LLMetricPerformanceTesterBasic(name),
+ mBaseSessionp(NULL),
+ mCurrentSessionp(NULL)
+{
+}
+
+LLMetricPerformanceTesterWithSession::~LLMetricPerformanceTesterWithSession()
+{
+ if (mBaseSessionp)
+ {
+ delete mBaseSessionp ;
+ mBaseSessionp = NULL ;
+ }
+ if (mCurrentSessionp)
+ {
+ delete mCurrentSessionp ;
+ mCurrentSessionp = NULL ;
+ }
+}
+
+/*virtual*/
+void LLMetricPerformanceTesterWithSession::analyzePerformance(llofstream* os, LLSD* base, LLSD* current)
+{
+ // Load the base session
+ resetCurrentCount() ;
+ mBaseSessionp = loadTestSession(base) ;
+
+ // Load the current session
+ resetCurrentCount() ;
+ mCurrentSessionp = loadTestSession(current) ;
+
+ if (!mBaseSessionp || !mCurrentSessionp)
+ {
+ LL_ERRS() << "Error loading test sessions." << LL_ENDL ;
+ }
+
+ // Compare
+ compareTestSessions(os) ;
+
+ // Release memory
+ if (mBaseSessionp)
+ {
+ delete mBaseSessionp ;
+ mBaseSessionp = NULL ;
+ }
+ if (mCurrentSessionp)
+ {
+ delete mCurrentSessionp ;
+ mCurrentSessionp = NULL ;
+ }
+}
+
+
+//----------------------------------------------------------------------------------------------
+// LLTestSession
+//----------------------------------------------------------------------------------------------
+
+LLMetricPerformanceTesterWithSession::LLTestSession::~LLTestSession()
+{
+}
+
diff --git a/indra/llcommon/llmetricperformancetester.h b/indra/llcommon/llmetricperformancetester.h index c61e561c33..8989e657ce 100644 --- a/indra/llcommon/llmetricperformancetester.h +++ b/indra/llcommon/llmetricperformancetester.h @@ -1,215 +1,215 @@ -/** - * @file llmetricperformancetester.h - * @brief LLMetricPerformanceTesterBasic and LLMetricPerformanceTesterWithSession classes definition - * - * $LicenseInfo:firstyear=2004&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_METRICPERFORMANCETESTER_H -#define LL_METRICPERFORMANCETESTER_H - -char const* const DEFAULT_METRIC_NAME = "metric"; - -/** - * @class LLMetricPerformanceTesterBasic - * @brief Performance Metric Base Class - */ -class LL_COMMON_API LLMetricPerformanceTesterBasic -{ -public: - /** - * @brief Creates a basic tester instance. - * @param[in] name - Unique string identifying this tester instance. - */ - LLMetricPerformanceTesterBasic(std::string name); - virtual ~LLMetricPerformanceTesterBasic(); - - /** - * @return Returns true if the instance has been added to the tester map. - * Need to be tested after creation of a tester instance so to know if the tester is correctly handled. - * A tester might not be added to the map if another tester with the same name already exists. - */ - bool isValid() const { return mValidInstance; } - - /** - * @brief Write a set of test results to the log LLSD. - */ - void outputTestResults() ; - - /** - * @brief Compare the test results. - * By default, compares the test results against the baseline one by one, item by item, - * in the increasing order of the LLSD record counter, starting from the first one. - */ - virtual void analyzePerformance(llofstream* os, LLSD* base, LLSD* current) ; - - static void doAnalysisMetrics(std::string baseline, std::string target, std::string output) ; - - /** - * @return Returns the number of the test metrics in this tester instance. - */ - auto getNumberOfMetrics() const { return mMetricStrings.size() ;} - /** - * @return Returns the metric name at index - * @param[in] index - Index on the list of metrics managed by this tester instance. - */ - std::string getMetricName(size_t index) const { return mMetricStrings[index] ;} - -protected: - /** - * @return Returns the name of this tester instance. - */ - std::string getTesterName() const { return mName ;} - - /** - * @brief Insert a new metric to be managed by this tester instance. - * @param[in] str - Unique string identifying the new metric. - */ - void addMetric(std::string str) ; - - /** - * @brief Compare test results, provided in 2 flavors: compare integers and compare floats. - * @param[out] os - Formatted output string holding the compared values. - * @param[in] metric_string - Name of the metric. - * @param[in] v_base - Base value of the metric. - * @param[in] v_current - Current value of the metric. - */ - virtual void compareTestResults(llofstream* os, std::string metric_string, S32 v_base, S32 v_current) ; - virtual void compareTestResults(llofstream* os, std::string metric_string, F32 v_base, F32 v_current) ; - - /** - * @brief Reset internal record count. Count starts with 1. - */ - void resetCurrentCount() { mCount = 1; } - /** - * @brief Increment internal record count. - */ - void incrementCurrentCount() { mCount++; } - /** - * @return Returns the label to be used for the current count. It's "TesterName"-"Count". - */ - std::string getCurrentLabelName() const { return llformat("%s-%d", mName.c_str(), mCount) ;} - - /** - * @brief Write a test record to the LLSD. Implementers need to overload this method. - * @param[out] sd - The LLSD record to store metric data into. - */ - virtual void outputTestRecord(LLSD* sd) = 0 ; - -private: - void preOutputTestResults(LLSD* sd) ; - void postOutputTestResults(LLSD* sd) ; - static LLSD analyzeMetricPerformanceLog(std::istream& is) ; - - std::string mName ; // Name of this tester instance - S32 mCount ; // Current record count - bool mValidInstance; // true if the instance is managed by the map - std::vector< std::string > mMetricStrings ; // Metrics strings - -// Static members managing the collection of testers -public: - // Map of all the tester instances in use - typedef std::map< std::string, LLMetricPerformanceTesterBasic* > name_tester_map_t; - static name_tester_map_t sTesterMap ; - - /** - * @return Returns a pointer to the tester - * @param[in] name - Name of the tester instance queried. - */ - static LLMetricPerformanceTesterBasic* getTester(std::string name) ; - - /** - * @return Delete the named tester from the list - * @param[in] name - Name of the tester instance to delete. - */ - static void deleteTester(std::string name); - - /** - * @return Returns true if that metric *or* the default catch all metric has been requested to be logged - * @param[in] name - Name of the tester queried. - */ - static bool isMetricLogRequested(std::string name); - - /** - * @return Returns true if there's a tester defined, false otherwise. - */ - static bool hasMetricPerformanceTesters() { return !sTesterMap.empty() ;} - /** - * @brief Delete all testers and reset the tester map - */ - static void cleanupClass() ; - -private: - // Add a tester to the map. Returns false if adding fails. - static bool addTester(LLMetricPerformanceTesterBasic* tester) ; -}; - -/** - * @class LLMetricPerformanceTesterWithSession - * @brief Performance Metric Class with custom session - */ -class LL_COMMON_API LLMetricPerformanceTesterWithSession : public LLMetricPerformanceTesterBasic -{ -public: - /** - * @param[in] name - Unique string identifying this tester instance. - */ - LLMetricPerformanceTesterWithSession(std::string name); - virtual ~LLMetricPerformanceTesterWithSession(); - - /** - * @brief Compare the test results. - * This will be loading the base and current sessions and compare them using the virtual - * abstract methods loadTestSession() and compareTestSessions() - */ - virtual void analyzePerformance(llofstream* os, LLSD* base, LLSD* current) ; - -protected: - /** - * @class LLMetricPerformanceTesterWithSession::LLTestSession - * @brief Defines an interface for the two abstract virtual functions loadTestSession() and compareTestSessions() - */ - class LL_COMMON_API LLTestSession - { - public: - virtual ~LLTestSession() ; - }; - - /** - * @brief Convert an LLSD log into a test session. - * @param[in] log - The LLSD record - * @return Returns the record as a test session - */ - virtual LLMetricPerformanceTesterWithSession::LLTestSession* loadTestSession(LLSD* log) = 0; - - /** - * @brief Compare the base session and the target session. Assumes base and current sessions have been loaded. - * @param[out] os - The comparison result as a standard stream - */ - virtual void compareTestSessions(llofstream* os) = 0; - - LLTestSession* mBaseSessionp; - LLTestSession* mCurrentSessionp; -}; - -#endif - +/**
+ * @file llmetricperformancetester.h
+ * @brief LLMetricPerformanceTesterBasic and LLMetricPerformanceTesterWithSession classes definition
+ *
+ * $LicenseInfo:firstyear=2004&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_METRICPERFORMANCETESTER_H
+#define LL_METRICPERFORMANCETESTER_H
+
+char const* const DEFAULT_METRIC_NAME = "metric";
+
+/**
+ * @class LLMetricPerformanceTesterBasic
+ * @brief Performance Metric Base Class
+ */
+class LL_COMMON_API LLMetricPerformanceTesterBasic
+{
+public:
+ /**
+ * @brief Creates a basic tester instance.
+ * @param[in] name - Unique string identifying this tester instance.
+ */
+ LLMetricPerformanceTesterBasic(std::string name);
+ virtual ~LLMetricPerformanceTesterBasic();
+
+ /**
+ * @return Returns true if the instance has been added to the tester map.
+ * Need to be tested after creation of a tester instance so to know if the tester is correctly handled.
+ * A tester might not be added to the map if another tester with the same name already exists.
+ */
+ bool isValid() const { return mValidInstance; }
+
+ /**
+ * @brief Write a set of test results to the log LLSD.
+ */
+ void outputTestResults() ;
+
+ /**
+ * @brief Compare the test results.
+ * By default, compares the test results against the baseline one by one, item by item,
+ * in the increasing order of the LLSD record counter, starting from the first one.
+ */
+ virtual void analyzePerformance(llofstream* os, LLSD* base, LLSD* current) ;
+
+ static void doAnalysisMetrics(std::string baseline, std::string target, std::string output) ;
+
+ /**
+ * @return Returns the number of the test metrics in this tester instance.
+ */
+ auto getNumberOfMetrics() const { return mMetricStrings.size() ;}
+ /**
+ * @return Returns the metric name at index
+ * @param[in] index - Index on the list of metrics managed by this tester instance.
+ */
+ std::string getMetricName(size_t index) const { return mMetricStrings[index] ;}
+
+protected:
+ /**
+ * @return Returns the name of this tester instance.
+ */
+ std::string getTesterName() const { return mName ;}
+
+ /**
+ * @brief Insert a new metric to be managed by this tester instance.
+ * @param[in] str - Unique string identifying the new metric.
+ */
+ void addMetric(std::string str) ;
+
+ /**
+ * @brief Compare test results, provided in 2 flavors: compare integers and compare floats.
+ * @param[out] os - Formatted output string holding the compared values.
+ * @param[in] metric_string - Name of the metric.
+ * @param[in] v_base - Base value of the metric.
+ * @param[in] v_current - Current value of the metric.
+ */
+ virtual void compareTestResults(llofstream* os, std::string metric_string, S32 v_base, S32 v_current) ;
+ virtual void compareTestResults(llofstream* os, std::string metric_string, F32 v_base, F32 v_current) ;
+
+ /**
+ * @brief Reset internal record count. Count starts with 1.
+ */
+ void resetCurrentCount() { mCount = 1; }
+ /**
+ * @brief Increment internal record count.
+ */
+ void incrementCurrentCount() { mCount++; }
+ /**
+ * @return Returns the label to be used for the current count. It's "TesterName"-"Count".
+ */
+ std::string getCurrentLabelName() const { return llformat("%s-%d", mName.c_str(), mCount) ;}
+
+ /**
+ * @brief Write a test record to the LLSD. Implementers need to overload this method.
+ * @param[out] sd - The LLSD record to store metric data into.
+ */
+ virtual void outputTestRecord(LLSD* sd) = 0 ;
+
+private:
+ void preOutputTestResults(LLSD* sd) ;
+ void postOutputTestResults(LLSD* sd) ;
+ static LLSD analyzeMetricPerformanceLog(std::istream& is) ;
+
+ std::string mName ; // Name of this tester instance
+ S32 mCount ; // Current record count
+ bool mValidInstance; // true if the instance is managed by the map
+ std::vector< std::string > mMetricStrings ; // Metrics strings
+
+// Static members managing the collection of testers
+public:
+ // Map of all the tester instances in use
+ typedef std::map< std::string, LLMetricPerformanceTesterBasic* > name_tester_map_t;
+ static name_tester_map_t sTesterMap ;
+
+ /**
+ * @return Returns a pointer to the tester
+ * @param[in] name - Name of the tester instance queried.
+ */
+ static LLMetricPerformanceTesterBasic* getTester(std::string name) ;
+
+ /**
+ * @return Delete the named tester from the list
+ * @param[in] name - Name of the tester instance to delete.
+ */
+ static void deleteTester(std::string name);
+
+ /**
+ * @return Returns true if that metric *or* the default catch all metric has been requested to be logged
+ * @param[in] name - Name of the tester queried.
+ */
+ static bool isMetricLogRequested(std::string name);
+
+ /**
+ * @return Returns true if there's a tester defined, false otherwise.
+ */
+ static bool hasMetricPerformanceTesters() { return !sTesterMap.empty() ;}
+ /**
+ * @brief Delete all testers and reset the tester map
+ */
+ static void cleanupClass() ;
+
+private:
+ // Add a tester to the map. Returns false if adding fails.
+ static bool addTester(LLMetricPerformanceTesterBasic* tester) ;
+};
+
+/**
+ * @class LLMetricPerformanceTesterWithSession
+ * @brief Performance Metric Class with custom session
+ */
+class LL_COMMON_API LLMetricPerformanceTesterWithSession : public LLMetricPerformanceTesterBasic
+{
+public:
+ /**
+ * @param[in] name - Unique string identifying this tester instance.
+ */
+ LLMetricPerformanceTesterWithSession(std::string name);
+ virtual ~LLMetricPerformanceTesterWithSession();
+
+ /**
+ * @brief Compare the test results.
+ * This will be loading the base and current sessions and compare them using the virtual
+ * abstract methods loadTestSession() and compareTestSessions()
+ */
+ virtual void analyzePerformance(llofstream* os, LLSD* base, LLSD* current) ;
+
+protected:
+ /**
+ * @class LLMetricPerformanceTesterWithSession::LLTestSession
+ * @brief Defines an interface for the two abstract virtual functions loadTestSession() and compareTestSessions()
+ */
+ class LL_COMMON_API LLTestSession
+ {
+ public:
+ virtual ~LLTestSession() ;
+ };
+
+ /**
+ * @brief Convert an LLSD log into a test session.
+ * @param[in] log - The LLSD record
+ * @return Returns the record as a test session
+ */
+ virtual LLMetricPerformanceTesterWithSession::LLTestSession* loadTestSession(LLSD* log) = 0;
+
+ /**
+ * @brief Compare the base session and the target session. Assumes base and current sessions have been loaded.
+ * @param[out] os - The comparison result as a standard stream
+ */
+ virtual void compareTestSessions(llofstream* os) = 0;
+
+ LLTestSession* mBaseSessionp;
+ LLTestSession* mCurrentSessionp;
+};
+
+#endif
+
diff --git a/indra/llcommon/llmetrics.cpp b/indra/llcommon/llmetrics.cpp index d40afe5160..7c37b18bab 100644 --- a/indra/llcommon/llmetrics.cpp +++ b/indra/llcommon/llmetrics.cpp @@ -1,4 +1,4 @@ -/** +/** * @file llmetrics.cpp * @author Kelly * @date 2007-05-25 @@ -7,21 +7,21 @@ * $LicenseInfo:firstyear=2007&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -36,128 +36,128 @@ class LLMetricsImpl { public: - LLMetricsImpl() { } - ~LLMetricsImpl(); - - void recordEvent(const std::string& location, const std::string& mesg, bool success); - void printTotals(LLSD metadata); - void recordEventDetails(const std::string& location, - const std::string& mesg, - bool success, - LLSD stats); + LLMetricsImpl() { } + ~LLMetricsImpl(); + + void recordEvent(const std::string& location, const std::string& mesg, bool success); + void printTotals(LLSD metadata); + void recordEventDetails(const std::string& location, + const std::string& mesg, + bool success, + LLSD stats); private: - LLFrameTimer mLastPrintTimer; - LLSD mMetricsMap; + LLFrameTimer mLastPrintTimer; + LLSD mMetricsMap; }; LLMetricsImpl::~LLMetricsImpl() { } -void LLMetricsImpl::recordEventDetails(const std::string& location, - const std::string& mesg, - bool success, - LLSD stats) +void LLMetricsImpl::recordEventDetails(const std::string& location, + const std::string& mesg, + bool success, + LLSD stats) { - recordEvent(location,mesg,success); + recordEvent(location,mesg,success); - LLSD metrics = LLSD::emptyMap(); - metrics["location"] = location; - metrics["stats"] = stats; - - LL_INFOS() << "LLMETRICS: " << (LLSDNotationStreamer(metrics)) << LL_ENDL; + LLSD metrics = LLSD::emptyMap(); + metrics["location"] = location; + metrics["stats"] = stats; + + LL_INFOS() << "LLMETRICS: " << (LLSDNotationStreamer(metrics)) << LL_ENDL; } // Store this: // [ {'location_1':{'mesg_1':{'success':i10, 'fail':i0}, -// 'mesg_2':{'success':i10, 'fail':i0}}, +// 'mesg_2':{'success':i10, 'fail':i0}}, // {'location_2',{'mesg_3':{'success':i10, 'fail':i0}} ] void LLMetricsImpl::recordEvent(const std::string& location, const std::string& mesg, bool success) { - LLSD& stats = mMetricsMap[location][mesg]; - if (success) - { - stats["success"] = stats["success"].asInteger() + 1; - } - else - { - stats["fail"] = stats["fail"].asInteger() + 1; - } + LLSD& stats = mMetricsMap[location][mesg]; + if (success) + { + stats["success"] = stats["success"].asInteger() + 1; + } + else + { + stats["fail"] = stats["fail"].asInteger() + 1; + } } // Print this: // { 'meta': -// { 'elapsed_time':r3600.000 } +// { 'elapsed_time':r3600.000 } // 'stats': -// [ {'location':'location_1', 'mesg':'mesg_1', 'success':i10, 'fail':i0}, -// {'location':'location_1', 'mesg':'mesg_2', 'success':i10, 'fail':i0}, -// {'location':'location_2', 'mesg':'mesg_3', 'success':i10, 'fail':i0} ] } +// [ {'location':'location_1', 'mesg':'mesg_1', 'success':i10, 'fail':i0}, +// {'location':'location_1', 'mesg':'mesg_2', 'success':i10, 'fail':i0}, +// {'location':'location_2', 'mesg':'mesg_3', 'success':i10, 'fail':i0} ] } void LLMetricsImpl::printTotals(LLSD metadata) { - F32 elapsed_time = mLastPrintTimer.getElapsedTimeAndResetF32(); - metadata["elapsed_time"] = elapsed_time; - - LLSD out_sd = LLSD::emptyMap(); - out_sd["meta"] = metadata; - - LLSD stats = LLSD::emptyArray(); - - LLSD::map_const_iterator loc_it = mMetricsMap.beginMap(); - LLSD::map_const_iterator loc_end = mMetricsMap.endMap(); - for ( ; loc_it != loc_end; ++loc_it) - { - const std::string& location = (*loc_it).first; - - const LLSD& loc_map = (*loc_it).second; - LLSD::map_const_iterator mesg_it = loc_map.beginMap(); - LLSD::map_const_iterator mesg_end = loc_map.endMap(); - for ( ; mesg_it != mesg_end; ++mesg_it) - { - const std::string& mesg = (*mesg_it).first; - const LLSD& mesg_map = (*mesg_it).second; - - LLSD entry = LLSD::emptyMap(); - entry["location"] = location; - entry["mesg"] = mesg; - entry["success"] = mesg_map["success"]; - entry["fail"] = mesg_map["fail"]; - - stats.append(entry); - } - } - - out_sd["stats"] = stats; - - LL_INFOS() << "LLMETRICS: AGGREGATE: " << LLSDOStreamer<LLSDNotationFormatter>(out_sd) << LL_ENDL; + F32 elapsed_time = mLastPrintTimer.getElapsedTimeAndResetF32(); + metadata["elapsed_time"] = elapsed_time; + + LLSD out_sd = LLSD::emptyMap(); + out_sd["meta"] = metadata; + + LLSD stats = LLSD::emptyArray(); + + LLSD::map_const_iterator loc_it = mMetricsMap.beginMap(); + LLSD::map_const_iterator loc_end = mMetricsMap.endMap(); + for ( ; loc_it != loc_end; ++loc_it) + { + const std::string& location = (*loc_it).first; + + const LLSD& loc_map = (*loc_it).second; + LLSD::map_const_iterator mesg_it = loc_map.beginMap(); + LLSD::map_const_iterator mesg_end = loc_map.endMap(); + for ( ; mesg_it != mesg_end; ++mesg_it) + { + const std::string& mesg = (*mesg_it).first; + const LLSD& mesg_map = (*mesg_it).second; + + LLSD entry = LLSD::emptyMap(); + entry["location"] = location; + entry["mesg"] = mesg; + entry["success"] = mesg_map["success"]; + entry["fail"] = mesg_map["fail"]; + + stats.append(entry); + } + } + + out_sd["stats"] = stats; + + LL_INFOS() << "LLMETRICS: AGGREGATE: " << LLSDOStreamer<LLSDNotationFormatter>(out_sd) << LL_ENDL; } LLMetrics::LLMetrics() { - mImpl = new LLMetricsImpl(); + mImpl = new LLMetricsImpl(); } LLMetrics::~LLMetrics() { - delete mImpl; - mImpl = NULL; + delete mImpl; + mImpl = NULL; } void LLMetrics::recordEvent(const std::string& location, const std::string& mesg, bool success) { - if (mImpl) mImpl->recordEvent(location,mesg,success); + if (mImpl) mImpl->recordEvent(location,mesg,success); } void LLMetrics::printTotals(LLSD meta) { - if (mImpl) mImpl->printTotals(meta); + if (mImpl) mImpl->printTotals(meta); } -void LLMetrics::recordEventDetails(const std::string& location, - const std::string& mesg, - bool success, - LLSD stats) +void LLMetrics::recordEventDetails(const std::string& location, + const std::string& mesg, + bool success, + LLSD stats) { - if (mImpl) mImpl->recordEventDetails(location,mesg,success,stats); + if (mImpl) mImpl->recordEventDetails(location,mesg,success,stats); } diff --git a/indra/llcommon/llmetrics.h b/indra/llcommon/llmetrics.h index 85a6986049..a6df49fc6a 100644 --- a/indra/llcommon/llmetrics.h +++ b/indra/llcommon/llmetrics.h @@ -7,21 +7,21 @@ * $LicenseInfo:firstyear=2007&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -35,25 +35,25 @@ class LLSD; class LL_COMMON_API LLMetrics { public: - LLMetrics(); - virtual ~LLMetrics(); + LLMetrics(); + virtual ~LLMetrics(); - // Adds this event to aggregate totals and records details to syslog (LL_INFOS()) - virtual void recordEventDetails(const std::string& location, - const std::string& mesg, - bool success, - LLSD stats); + // Adds this event to aggregate totals and records details to syslog (LL_INFOS()) + virtual void recordEventDetails(const std::string& location, + const std::string& mesg, + bool success, + LLSD stats); - // Adds this event to aggregate totals - virtual void recordEvent(const std::string& location, const std::string& mesg, bool success); + // Adds this event to aggregate totals + virtual void recordEvent(const std::string& location, const std::string& mesg, bool success); - // Prints aggregate totals and resets the counts. - virtual void printTotals(LLSD meta); + // Prints aggregate totals and resets the counts. + virtual void printTotals(LLSD meta); private: - - LLMetricsImpl* mImpl; + + LLMetricsImpl* mImpl; }; #endif diff --git a/indra/llcommon/llmortician.cpp b/indra/llcommon/llmortician.cpp index b3e03bde3f..7ba023f052 100644 --- a/indra/llcommon/llmortician.cpp +++ b/indra/llcommon/llmortician.cpp @@ -1,106 +1,106 @@ -/** - * @file llmortician.cpp - * - * $LicenseInfo:firstyear=2005&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" -#include "llmortician.h" - -#include <list> - -std::list<LLMortician*> LLMortician::sGraveyard; - -bool LLMortician::sDestroyImmediate = false; - -LLMortician::~LLMortician() -{ - sGraveyard.remove(this); -} - -size_t LLMortician::logClass(std::stringstream &str) -{ - auto size = sGraveyard.size(); - str << "Mortician graveyard count: " << size; - str << " Zealous: " << (sDestroyImmediate ? "True" : "False"); - if (size == 0) - { - return size; - } - str << " Output:\n"; - std::list<LLMortician*>::iterator iter = sGraveyard.begin(); - std::list<LLMortician*>::iterator end = sGraveyard.end(); - while (iter!=end) - { - LLMortician* dead = *iter; - iter++; - // Be as detailed and safe as possible to figure out issues - str << "Pointer: " << dead; - if (dead) - { - try - { - str << " Is dead: " << (dead->isDead() ? "True" : "False"); - str << " Name: " << typeid(*dead).name(); - } - catch (...) - { - - } - } - str << "\n"; - } - str << "--------------------------------------------"; - return size; -} - -void LLMortician::updateClass() -{ - while (!sGraveyard.empty()) - { - LLMortician* dead = sGraveyard.front(); - delete dead; - } -} - -void LLMortician::die() -{ - // It is valid to call die() more than once on something that hasn't died yet - if (sDestroyImmediate) - { - // *NOTE: This is a hack to ensure destruction order on shutdown (relative to non-mortician controlled classes). - mIsDead = true; - delete this; - return; - } - else if (!mIsDead) - { - mIsDead = true; - sGraveyard.push_back(this); - } -} - -// static -void LLMortician::setZealous(bool b) -{ - sDestroyImmediate = b; -} +/**
+ * @file llmortician.cpp
+ *
+ * $LicenseInfo:firstyear=2005&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+#include "llmortician.h"
+
+#include <list>
+
+std::list<LLMortician*> LLMortician::sGraveyard;
+
+bool LLMortician::sDestroyImmediate = false;
+
+LLMortician::~LLMortician()
+{
+ sGraveyard.remove(this);
+}
+
+size_t LLMortician::logClass(std::stringstream &str)
+{
+ auto size = sGraveyard.size();
+ str << "Mortician graveyard count: " << size;
+ str << " Zealous: " << (sDestroyImmediate ? "True" : "False");
+ if (size == 0)
+ {
+ return size;
+ }
+ str << " Output:\n";
+ std::list<LLMortician*>::iterator iter = sGraveyard.begin();
+ std::list<LLMortician*>::iterator end = sGraveyard.end();
+ while (iter!=end)
+ {
+ LLMortician* dead = *iter;
+ iter++;
+ // Be as detailed and safe as possible to figure out issues
+ str << "Pointer: " << dead;
+ if (dead)
+ {
+ try
+ {
+ str << " Is dead: " << (dead->isDead() ? "True" : "False");
+ str << " Name: " << typeid(*dead).name();
+ }
+ catch (...)
+ {
+
+ }
+ }
+ str << "\n";
+ }
+ str << "--------------------------------------------";
+ return size;
+}
+
+void LLMortician::updateClass()
+{
+ while (!sGraveyard.empty())
+ {
+ LLMortician* dead = sGraveyard.front();
+ delete dead;
+ }
+}
+
+void LLMortician::die()
+{
+ // It is valid to call die() more than once on something that hasn't died yet
+ if (sDestroyImmediate)
+ {
+ // *NOTE: This is a hack to ensure destruction order on shutdown (relative to non-mortician controlled classes).
+ mIsDead = true;
+ delete this;
+ return;
+ }
+ else if (!mIsDead)
+ {
+ mIsDead = true;
+ sGraveyard.push_back(this);
+ }
+}
+
+// static
+void LLMortician::setZealous(bool b)
+{
+ sDestroyImmediate = b;
+}
diff --git a/indra/llcommon/llmortician.h b/indra/llcommon/llmortician.h index e772b7d9c5..4aea4e0292 100644 --- a/indra/llcommon/llmortician.h +++ b/indra/llcommon/llmortician.h @@ -1,56 +1,56 @@ -/** - * @file llmortician.h - * @brief Base class for delayed deletions. - * - * $LicenseInfo:firstyear=2005&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LLMORTICIAN_H -#define LLMORTICIAN_H - -#include "stdtypes.h" -#include <list> - -class LL_COMMON_API LLMortician -{ -public: - LLMortician() { mIsDead = false; } - static auto graveyardCount() { return sGraveyard.size(); }; - static size_t logClass(std::stringstream &str); - static void updateClass(); - virtual ~LLMortician(); - void die(); - bool isDead() { return mIsDead; } - - // sets destroy immediate true - static void setZealous(bool b); - -private: - static bool sDestroyImmediate; - - bool mIsDead; - - static std::list<LLMortician*> sGraveyard; -}; - -#endif - +/**
+ * @file llmortician.h
+ * @brief Base class for delayed deletions.
+ *
+ * $LicenseInfo:firstyear=2005&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LLMORTICIAN_H
+#define LLMORTICIAN_H
+
+#include "stdtypes.h"
+#include <list>
+
+class LL_COMMON_API LLMortician
+{
+public:
+ LLMortician() { mIsDead = false; }
+ static auto graveyardCount() { return sGraveyard.size(); };
+ static size_t logClass(std::stringstream &str);
+ static void updateClass();
+ virtual ~LLMortician();
+ void die();
+ bool isDead() { return mIsDead; }
+
+ // sets destroy immediate true
+ static void setZealous(bool b);
+
+private:
+ static bool sDestroyImmediate;
+
+ bool mIsDead;
+
+ static std::list<LLMortician*> sGraveyard;
+};
+
+#endif
+
diff --git a/indra/llcommon/llmutex.cpp b/indra/llcommon/llmutex.cpp index 3cfdc8304e..a7c2817e2f 100644 --- a/indra/llcommon/llmutex.cpp +++ b/indra/llcommon/llmutex.cpp @@ -1,418 +1,418 @@ -/** - * @file llmutex.cpp - * - * $LicenseInfo:firstyear=2004&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" - -#include "llmutex.h" -#include "llthread.h" -#include "lltimer.h" - - -//--------------------------------------------------------------------- -// -// LLMutex -// -LLMutex::LLMutex() : - mCount(0) -{ -} - -LLMutex::~LLMutex() -{ -} - -void LLMutex::lock() -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD - if(isSelfLocked()) - { //redundant lock - mCount++; - return; - } - - mMutex.lock(); - -#if MUTEX_DEBUG - // Have to have the lock before we can access the debug info - auto id = LLThread::currentID(); - if (mIsLocked[id]) - LL_ERRS() << "Already locked in Thread: " << id << LL_ENDL; - mIsLocked[id] = true; -#endif - - mLockingThread = LLThread::currentID(); -} - -void LLMutex::unlock() -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD - if (mCount > 0) - { //not the root unlock - mCount--; - return; - } - -#if MUTEX_DEBUG - // Access the debug info while we have the lock - auto id = LLThread::currentID(); - if (!mIsLocked[id]) - LL_ERRS() << "Not locked in Thread: " << id << LL_ENDL; - mIsLocked[id] = false; -#endif - - mLockingThread = LLThread::id_t(); - mMutex.unlock(); -} - -bool LLMutex::isLocked() -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD - if (!mMutex.try_lock()) - { - return true; - } - else - { - mMutex.unlock(); - return false; - } -} - -bool LLMutex::isSelfLocked() -{ - return mLockingThread == LLThread::currentID(); -} - -LLThread::id_t LLMutex::lockingThread() const -{ - return mLockingThread; -} - -bool LLMutex::trylock() -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD - if (isSelfLocked()) - { //redundant lock - mCount++; - return true; - } - - if (!mMutex.try_lock()) - { - return false; - } - -#if MUTEX_DEBUG - // Have to have the lock before we can access the debug info - auto id = LLThread::currentID(); - if (mIsLocked[id]) - LL_ERRS() << "Already locked in Thread: " << id << LL_ENDL; - mIsLocked[id] = true; -#endif - - mLockingThread = LLThread::currentID(); - return true; -} - - -//--------------------------------------------------------------------- -// -// LLSharedMutex -// -LLSharedMutex::LLSharedMutex() -: mLockingThreads(2) // Reserve 2 slots in the map hash table -, mIsShared(false) -{ -} - -bool LLSharedMutex::isLocked() const -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD - std::lock_guard<std::mutex> lock(mLockMutex); - - return !mLockingThreads.empty(); -} - -bool LLSharedMutex::isThreadLocked() const -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD - LLThread::id_t current_thread = LLThread::currentID(); - std::lock_guard<std::mutex> lock(mLockMutex); - - const_iterator it = mLockingThreads.find(current_thread); - return it != mLockingThreads.end(); -} - -void LLSharedMutex::lockShared() -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD - LLThread::id_t current_thread = LLThread::currentID(); - - mLockMutex.lock(); - iterator it = mLockingThreads.find(current_thread); - if (it != mLockingThreads.end()) - { - it->second++; - } - else - { - // Acquire the mutex immediately if the mutex is not locked exclusively - // or enter a locking state if the mutex is already locked exclusively - mLockMutex.unlock(); - mSharedMutex.lock_shared(); - mLockMutex.lock(); - // Continue after acquiring the mutex - mLockingThreads.emplace(std::make_pair(current_thread, 1)); - mIsShared = true; - } - mLockMutex.unlock(); -} - -void LLSharedMutex::lockExclusive() -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD - LLThread::id_t current_thread = LLThread::currentID(); - - mLockMutex.lock(); - iterator it = mLockingThreads.find(current_thread); - if (it != mLockingThreads.end()) - { - if (mIsShared) - { - // The mutex is already locked in the current thread - // but this lock is SHARED (not EXCLISIVE) - // We can't lock it again, the lock stays shared - // This can lead to a collision (theoretically) - llassert_always(!"The current thread is already locked SHARED and can't be locked EXCLUSIVE"); - } - it->second++; - } - else - { - // Acquire the mutex immediately if mLockingThreads is empty - // or enter a locking state if mLockingThreads is not empty - mLockMutex.unlock(); - mSharedMutex.lock(); - mLockMutex.lock(); - // Continue after acquiring the mutex (and possible quitting the locking state) - mLockingThreads.emplace(std::make_pair(current_thread, 1)); - mIsShared = false; - } - mLockMutex.unlock(); -} - -bool LLSharedMutex::trylockShared() -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD - LLThread::id_t current_thread = LLThread::currentID(); - std::lock_guard<std::mutex> lock(mLockMutex); - - iterator it = mLockingThreads.find(current_thread); - if (it != mLockingThreads.end()) - { - it->second++; - } - else - { - if (!mSharedMutex.try_lock_shared()) - return false; - - mLockingThreads.emplace(std::make_pair(current_thread, 1)); - mIsShared = true; - } - - return true; -} - -bool LLSharedMutex::trylockExclusive() -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD - LLThread::id_t current_thread = LLThread::currentID(); - std::lock_guard<std::mutex> lock(mLockMutex); - - if (mLockingThreads.size() == 1 && mLockingThreads.begin()->first == current_thread) - { - mLockingThreads.begin()->second++; - } - else - { - if (!mSharedMutex.try_lock()) - return false; - - mLockingThreads.emplace(std::make_pair(current_thread, 1)); - mIsShared = false; - } - - return true; -} - -void LLSharedMutex::unlockShared() -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD - LLThread::id_t current_thread = LLThread::currentID(); - std::lock_guard<std::mutex> lock(mLockMutex); - - iterator it = mLockingThreads.find(current_thread); - if (it != mLockingThreads.end()) - { - if (it->second > 1) - { - it->second--; - } - else - { - mLockingThreads.erase(it); - mSharedMutex.unlock_shared(); - } - } -} - -void LLSharedMutex::unlockExclusive() -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD - LLThread::id_t current_thread = LLThread::currentID(); - std::lock_guard<std::mutex> lock(mLockMutex); - - iterator it = mLockingThreads.find(current_thread); - if (it != mLockingThreads.end()) - { - if (it->second > 1) - { - it->second--; - } - else - { - mLockingThreads.erase(it); - mSharedMutex.unlock(); - } - } -} - - -//--------------------------------------------------------------------- -// -// LLCondition -// -LLCondition::LLCondition() : - LLMutex() -{ -} - -LLCondition::~LLCondition() -{ -} - -void LLCondition::wait() -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD - std::unique_lock< std::mutex > lock(mMutex); - mCond.wait(lock); -} - -void LLCondition::signal() -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD - mCond.notify_one(); -} - -void LLCondition::broadcast() -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD - mCond.notify_all(); -} - - -//--------------------------------------------------------------------- -// -// LLMutexTrylock -// -LLMutexTrylock::LLMutexTrylock(LLMutex* mutex) - : mMutex(mutex), - mLocked(false) -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD - if (mMutex) - mLocked = mMutex->trylock(); -} - -LLMutexTrylock::LLMutexTrylock(LLMutex* mutex, U32 aTries, U32 delay_ms) - : mMutex(mutex), - mLocked(false) -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD - if (!mMutex) - return; - - for (U32 i = 0; i < aTries; ++i) - { - mLocked = mMutex->trylock(); - if (mLocked) - break; - ms_sleep(delay_ms); - } -} - -LLMutexTrylock::~LLMutexTrylock() -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD - if (mMutex && mLocked) - mMutex->unlock(); -} - - -//--------------------------------------------------------------------- -// -// LLScopedLock -// -LLScopedLock::LLScopedLock(std::mutex* mutex) : mMutex(mutex) -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD - if(mutex) - { - mutex->lock(); - mLocked = true; - } - else - { - mLocked = false; - } -} - -LLScopedLock::~LLScopedLock() -{ - unlock(); -} - -void LLScopedLock::unlock() -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD - if(mLocked) - { - mMutex->unlock(); - mLocked = false; - } -} - -//============================================================================ +/**
+ * @file llmutex.cpp
+ *
+ * $LicenseInfo:firstyear=2004&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llmutex.h"
+#include "llthread.h"
+#include "lltimer.h"
+
+
+//---------------------------------------------------------------------
+//
+// LLMutex
+//
+LLMutex::LLMutex() :
+ mCount(0)
+{
+}
+
+LLMutex::~LLMutex()
+{
+}
+
+void LLMutex::lock()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
+ if(isSelfLocked())
+ { //redundant lock
+ mCount++;
+ return;
+ }
+
+ mMutex.lock();
+
+#if MUTEX_DEBUG
+ // Have to have the lock before we can access the debug info
+ auto id = LLThread::currentID();
+ if (mIsLocked[id])
+ LL_ERRS() << "Already locked in Thread: " << id << LL_ENDL;
+ mIsLocked[id] = true;
+#endif
+
+ mLockingThread = LLThread::currentID();
+}
+
+void LLMutex::unlock()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
+ if (mCount > 0)
+ { //not the root unlock
+ mCount--;
+ return;
+ }
+
+#if MUTEX_DEBUG
+ // Access the debug info while we have the lock
+ auto id = LLThread::currentID();
+ if (!mIsLocked[id])
+ LL_ERRS() << "Not locked in Thread: " << id << LL_ENDL;
+ mIsLocked[id] = false;
+#endif
+
+ mLockingThread = LLThread::id_t();
+ mMutex.unlock();
+}
+
+bool LLMutex::isLocked()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
+ if (!mMutex.try_lock())
+ {
+ return true;
+ }
+ else
+ {
+ mMutex.unlock();
+ return false;
+ }
+}
+
+bool LLMutex::isSelfLocked()
+{
+ return mLockingThread == LLThread::currentID();
+}
+
+LLThread::id_t LLMutex::lockingThread() const
+{
+ return mLockingThread;
+}
+
+bool LLMutex::trylock()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
+ if (isSelfLocked())
+ { //redundant lock
+ mCount++;
+ return true;
+ }
+
+ if (!mMutex.try_lock())
+ {
+ return false;
+ }
+
+#if MUTEX_DEBUG
+ // Have to have the lock before we can access the debug info
+ auto id = LLThread::currentID();
+ if (mIsLocked[id])
+ LL_ERRS() << "Already locked in Thread: " << id << LL_ENDL;
+ mIsLocked[id] = true;
+#endif
+
+ mLockingThread = LLThread::currentID();
+ return true;
+}
+
+
+//---------------------------------------------------------------------
+//
+// LLSharedMutex
+//
+LLSharedMutex::LLSharedMutex()
+: mLockingThreads(2) // Reserve 2 slots in the map hash table
+, mIsShared(false)
+{
+}
+
+bool LLSharedMutex::isLocked() const
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
+ std::lock_guard<std::mutex> lock(mLockMutex);
+
+ return !mLockingThreads.empty();
+}
+
+bool LLSharedMutex::isThreadLocked() const
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
+ LLThread::id_t current_thread = LLThread::currentID();
+ std::lock_guard<std::mutex> lock(mLockMutex);
+
+ const_iterator it = mLockingThreads.find(current_thread);
+ return it != mLockingThreads.end();
+}
+
+void LLSharedMutex::lockShared()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
+ LLThread::id_t current_thread = LLThread::currentID();
+
+ mLockMutex.lock();
+ iterator it = mLockingThreads.find(current_thread);
+ if (it != mLockingThreads.end())
+ {
+ it->second++;
+ }
+ else
+ {
+ // Acquire the mutex immediately if the mutex is not locked exclusively
+ // or enter a locking state if the mutex is already locked exclusively
+ mLockMutex.unlock();
+ mSharedMutex.lock_shared();
+ mLockMutex.lock();
+ // Continue after acquiring the mutex
+ mLockingThreads.emplace(std::make_pair(current_thread, 1));
+ mIsShared = true;
+ }
+ mLockMutex.unlock();
+}
+
+void LLSharedMutex::lockExclusive()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
+ LLThread::id_t current_thread = LLThread::currentID();
+
+ mLockMutex.lock();
+ iterator it = mLockingThreads.find(current_thread);
+ if (it != mLockingThreads.end())
+ {
+ if (mIsShared)
+ {
+ // The mutex is already locked in the current thread
+ // but this lock is SHARED (not EXCLISIVE)
+ // We can't lock it again, the lock stays shared
+ // This can lead to a collision (theoretically)
+ llassert_always(!"The current thread is already locked SHARED and can't be locked EXCLUSIVE");
+ }
+ it->second++;
+ }
+ else
+ {
+ // Acquire the mutex immediately if mLockingThreads is empty
+ // or enter a locking state if mLockingThreads is not empty
+ mLockMutex.unlock();
+ mSharedMutex.lock();
+ mLockMutex.lock();
+ // Continue after acquiring the mutex (and possible quitting the locking state)
+ mLockingThreads.emplace(std::make_pair(current_thread, 1));
+ mIsShared = false;
+ }
+ mLockMutex.unlock();
+}
+
+bool LLSharedMutex::trylockShared()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
+ LLThread::id_t current_thread = LLThread::currentID();
+ std::lock_guard<std::mutex> lock(mLockMutex);
+
+ iterator it = mLockingThreads.find(current_thread);
+ if (it != mLockingThreads.end())
+ {
+ it->second++;
+ }
+ else
+ {
+ if (!mSharedMutex.try_lock_shared())
+ return false;
+
+ mLockingThreads.emplace(std::make_pair(current_thread, 1));
+ mIsShared = true;
+ }
+
+ return true;
+}
+
+bool LLSharedMutex::trylockExclusive()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
+ LLThread::id_t current_thread = LLThread::currentID();
+ std::lock_guard<std::mutex> lock(mLockMutex);
+
+ if (mLockingThreads.size() == 1 && mLockingThreads.begin()->first == current_thread)
+ {
+ mLockingThreads.begin()->second++;
+ }
+ else
+ {
+ if (!mSharedMutex.try_lock())
+ return false;
+
+ mLockingThreads.emplace(std::make_pair(current_thread, 1));
+ mIsShared = false;
+ }
+
+ return true;
+}
+
+void LLSharedMutex::unlockShared()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
+ LLThread::id_t current_thread = LLThread::currentID();
+ std::lock_guard<std::mutex> lock(mLockMutex);
+
+ iterator it = mLockingThreads.find(current_thread);
+ if (it != mLockingThreads.end())
+ {
+ if (it->second > 1)
+ {
+ it->second--;
+ }
+ else
+ {
+ mLockingThreads.erase(it);
+ mSharedMutex.unlock_shared();
+ }
+ }
+}
+
+void LLSharedMutex::unlockExclusive()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
+ LLThread::id_t current_thread = LLThread::currentID();
+ std::lock_guard<std::mutex> lock(mLockMutex);
+
+ iterator it = mLockingThreads.find(current_thread);
+ if (it != mLockingThreads.end())
+ {
+ if (it->second > 1)
+ {
+ it->second--;
+ }
+ else
+ {
+ mLockingThreads.erase(it);
+ mSharedMutex.unlock();
+ }
+ }
+}
+
+
+//---------------------------------------------------------------------
+//
+// LLCondition
+//
+LLCondition::LLCondition() :
+ LLMutex()
+{
+}
+
+LLCondition::~LLCondition()
+{
+}
+
+void LLCondition::wait()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
+ std::unique_lock< std::mutex > lock(mMutex);
+ mCond.wait(lock);
+}
+
+void LLCondition::signal()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
+ mCond.notify_one();
+}
+
+void LLCondition::broadcast()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
+ mCond.notify_all();
+}
+
+
+//---------------------------------------------------------------------
+//
+// LLMutexTrylock
+//
+LLMutexTrylock::LLMutexTrylock(LLMutex* mutex)
+ : mMutex(mutex),
+ mLocked(false)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
+ if (mMutex)
+ mLocked = mMutex->trylock();
+}
+
+LLMutexTrylock::LLMutexTrylock(LLMutex* mutex, U32 aTries, U32 delay_ms)
+ : mMutex(mutex),
+ mLocked(false)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
+ if (!mMutex)
+ return;
+
+ for (U32 i = 0; i < aTries; ++i)
+ {
+ mLocked = mMutex->trylock();
+ if (mLocked)
+ break;
+ ms_sleep(delay_ms);
+ }
+}
+
+LLMutexTrylock::~LLMutexTrylock()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
+ if (mMutex && mLocked)
+ mMutex->unlock();
+}
+
+
+//---------------------------------------------------------------------
+//
+// LLScopedLock
+//
+LLScopedLock::LLScopedLock(std::mutex* mutex) : mMutex(mutex)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
+ if(mutex)
+ {
+ mutex->lock();
+ mLocked = true;
+ }
+ else
+ {
+ mLocked = false;
+ }
+}
+
+LLScopedLock::~LLScopedLock()
+{
+ unlock();
+}
+
+void LLScopedLock::unlock()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
+ if(mLocked)
+ {
+ mMutex->unlock();
+ mLocked = false;
+ }
+}
+
+//============================================================================
diff --git a/indra/llcommon/llmutex.h b/indra/llcommon/llmutex.h index 2b2435da4d..898ddab284 100644 --- a/indra/llcommon/llmutex.h +++ b/indra/llcommon/llmutex.h @@ -1,271 +1,271 @@ -/** - * @file llmutex.h - * @brief Base classes for mutex and condition handling. - * - * $LicenseInfo:firstyear=2004&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2012, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLMUTEX_H -#define LL_LLMUTEX_H - -#include "stdtypes.h" -#include "llthread.h" -#include <boost/noncopyable.hpp> - -#include "mutex.h" -#include <shared_mutex> -#include <unordered_map> -#include <condition_variable> - -//============================================================================ - -//#define MUTEX_DEBUG (LL_DEBUG || LL_RELEASE_WITH_DEBUG_INFO) -#define MUTEX_DEBUG 0 //disable mutex debugging as it's interfering with profiles - -#if MUTEX_DEBUG -#include <map> -#endif - -class LL_COMMON_API LLMutex -{ -public: - LLMutex(); - virtual ~LLMutex(); - - void lock(); // blocks - bool trylock(); // non-blocking, returns true if lock held. - void unlock(); // undefined behavior when called on mutex not being held - bool isLocked(); // non-blocking, but does do a lock/unlock so not free - bool isSelfLocked(); //return true if locked in a same thread - LLThread::id_t lockingThread() const; //get ID of locking thread - -protected: - std::mutex mMutex; - mutable U32 mCount; - mutable LLThread::id_t mLockingThread; - -#if MUTEX_DEBUG - std::unordered_map<LLThread::id_t, bool> mIsLocked; -#endif -}; - -//============================================================================ - -class LL_COMMON_API LLSharedMutex -{ -public: - LLSharedMutex(); - - bool isLocked() const; - bool isThreadLocked() const; - bool isShared() const { return mIsShared; } - - void lockShared(); - void lockExclusive(); - template<bool SHARED> void lock(); - - bool trylockShared(); - bool trylockExclusive(); - template<bool SHARED> bool trylock(); - - void unlockShared(); - void unlockExclusive(); - template<bool SHARED> void unlock(); - -private: - std::shared_mutex mSharedMutex; - mutable std::mutex mLockMutex; - std::unordered_map<LLThread::id_t, U32> mLockingThreads; - bool mIsShared; - - using iterator = std::unordered_map<LLThread::id_t, U32>::iterator; - using const_iterator = std::unordered_map<LLThread::id_t, U32>::const_iterator; -}; - -template<> -inline void LLSharedMutex::lock<true>() -{ - lockShared(); -} - -template<> -inline void LLSharedMutex::lock<false>() -{ - lockExclusive(); -} - -template<> -inline bool LLSharedMutex::trylock<true>() -{ - return trylockShared(); -} - -template<> -inline bool LLSharedMutex::trylock<false>() -{ - return trylockExclusive(); -} - -template<> -inline void LLSharedMutex::unlock<true>() -{ - unlockShared(); -} - -template<> -inline void LLSharedMutex::unlock<false>() -{ - unlockExclusive(); -} - -// Actually a condition/mutex pair (since each condition needs to be associated with a mutex). -class LL_COMMON_API LLCondition : public LLMutex -{ -public: - LLCondition(); - ~LLCondition(); - - void wait(); // blocks - void signal(); - void broadcast(); - -protected: - std::condition_variable mCond; -}; - -//============================================================================ - -class LLMutexLock -{ -public: - LLMutexLock(LLMutex* mutex) - { - mMutex = mutex; - - if (mMutex) - mMutex->lock(); - } - - ~LLMutexLock() - { - if (mMutex) - mMutex->unlock(); - } - -private: - LLMutex* mMutex; -}; - -//============================================================================ - -template<bool SHARED> -class LLSharedMutexLockTemplate -{ -public: - LLSharedMutexLockTemplate(LLSharedMutex* mutex) - : mSharedMutex(mutex) - { - if (mSharedMutex) - mSharedMutex->lock<SHARED>(); - } - - ~LLSharedMutexLockTemplate() - { - if (mSharedMutex) - mSharedMutex->unlock<SHARED>(); - } - -private: - LLSharedMutex* mSharedMutex; -}; - -using LLSharedMutexLock = LLSharedMutexLockTemplate<true>; -using LLExclusiveMutexLock = LLSharedMutexLockTemplate<false>; - -//============================================================================ - -// Scoped locking class similar in function to LLMutexLock but uses -// the trylock() method to conditionally acquire lock without -// blocking. Caller resolves the resulting condition by calling -// the isLocked() method and either punts or continues as indicated. -// -// Mostly of interest to callers needing to avoid stalls who can -// guarantee another attempt at a later time. - -class LLMutexTrylock -{ -public: - LLMutexTrylock(LLMutex* mutex); - LLMutexTrylock(LLMutex* mutex, U32 aTries, U32 delay_ms = 10); - ~LLMutexTrylock(); - - bool isLocked() const - { - return mLocked; - } - -private: - LLMutex* mMutex; - bool mLocked; -}; - -//============================================================================ - -/** -* @class LLScopedLock -* @brief Small class to help lock and unlock mutexes. -* -* 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 mutex. If you pass in NULL, - * this wrapper will not lock. - */ - LLScopedLock(std::mutex* 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; - std::mutex* mMutex; -}; - -#endif // LL_LLMUTEX_H +/**
+ * @file llmutex.h
+ * @brief Base classes for mutex and condition handling.
+ *
+ * $LicenseInfo:firstyear=2004&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2012, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLMUTEX_H
+#define LL_LLMUTEX_H
+
+#include "stdtypes.h"
+#include "llthread.h"
+#include <boost/noncopyable.hpp>
+
+#include "mutex.h"
+#include <shared_mutex>
+#include <unordered_map>
+#include <condition_variable>
+
+//============================================================================
+
+//#define MUTEX_DEBUG (LL_DEBUG || LL_RELEASE_WITH_DEBUG_INFO)
+#define MUTEX_DEBUG 0 //disable mutex debugging as it's interfering with profiles
+
+#if MUTEX_DEBUG
+#include <map>
+#endif
+
+class LL_COMMON_API LLMutex
+{
+public:
+ LLMutex();
+ virtual ~LLMutex();
+
+ void lock(); // blocks
+ bool trylock(); // non-blocking, returns true if lock held.
+ void unlock(); // undefined behavior when called on mutex not being held
+ bool isLocked(); // non-blocking, but does do a lock/unlock so not free
+ bool isSelfLocked(); //return true if locked in a same thread
+ LLThread::id_t lockingThread() const; //get ID of locking thread
+
+protected:
+ std::mutex mMutex;
+ mutable U32 mCount;
+ mutable LLThread::id_t mLockingThread;
+
+#if MUTEX_DEBUG
+ std::unordered_map<LLThread::id_t, bool> mIsLocked;
+#endif
+};
+
+//============================================================================
+
+class LL_COMMON_API LLSharedMutex
+{
+public:
+ LLSharedMutex();
+
+ bool isLocked() const;
+ bool isThreadLocked() const;
+ bool isShared() const { return mIsShared; }
+
+ void lockShared();
+ void lockExclusive();
+ template<bool SHARED> void lock();
+
+ bool trylockShared();
+ bool trylockExclusive();
+ template<bool SHARED> bool trylock();
+
+ void unlockShared();
+ void unlockExclusive();
+ template<bool SHARED> void unlock();
+
+private:
+ std::shared_mutex mSharedMutex;
+ mutable std::mutex mLockMutex;
+ std::unordered_map<LLThread::id_t, U32> mLockingThreads;
+ bool mIsShared;
+
+ using iterator = std::unordered_map<LLThread::id_t, U32>::iterator;
+ using const_iterator = std::unordered_map<LLThread::id_t, U32>::const_iterator;
+};
+
+template<>
+inline void LLSharedMutex::lock<true>()
+{
+ lockShared();
+}
+
+template<>
+inline void LLSharedMutex::lock<false>()
+{
+ lockExclusive();
+}
+
+template<>
+inline bool LLSharedMutex::trylock<true>()
+{
+ return trylockShared();
+}
+
+template<>
+inline bool LLSharedMutex::trylock<false>()
+{
+ return trylockExclusive();
+}
+
+template<>
+inline void LLSharedMutex::unlock<true>()
+{
+ unlockShared();
+}
+
+template<>
+inline void LLSharedMutex::unlock<false>()
+{
+ unlockExclusive();
+}
+
+// Actually a condition/mutex pair (since each condition needs to be associated with a mutex).
+class LL_COMMON_API LLCondition : public LLMutex
+{
+public:
+ LLCondition();
+ ~LLCondition();
+
+ void wait(); // blocks
+ void signal();
+ void broadcast();
+
+protected:
+ std::condition_variable mCond;
+};
+
+//============================================================================
+
+class LLMutexLock
+{
+public:
+ LLMutexLock(LLMutex* mutex)
+ {
+ mMutex = mutex;
+
+ if (mMutex)
+ mMutex->lock();
+ }
+
+ ~LLMutexLock()
+ {
+ if (mMutex)
+ mMutex->unlock();
+ }
+
+private:
+ LLMutex* mMutex;
+};
+
+//============================================================================
+
+template<bool SHARED>
+class LLSharedMutexLockTemplate
+{
+public:
+ LLSharedMutexLockTemplate(LLSharedMutex* mutex)
+ : mSharedMutex(mutex)
+ {
+ if (mSharedMutex)
+ mSharedMutex->lock<SHARED>();
+ }
+
+ ~LLSharedMutexLockTemplate()
+ {
+ if (mSharedMutex)
+ mSharedMutex->unlock<SHARED>();
+ }
+
+private:
+ LLSharedMutex* mSharedMutex;
+};
+
+using LLSharedMutexLock = LLSharedMutexLockTemplate<true>;
+using LLExclusiveMutexLock = LLSharedMutexLockTemplate<false>;
+
+//============================================================================
+
+// Scoped locking class similar in function to LLMutexLock but uses
+// the trylock() method to conditionally acquire lock without
+// blocking. Caller resolves the resulting condition by calling
+// the isLocked() method and either punts or continues as indicated.
+//
+// Mostly of interest to callers needing to avoid stalls who can
+// guarantee another attempt at a later time.
+
+class LLMutexTrylock
+{
+public:
+ LLMutexTrylock(LLMutex* mutex);
+ LLMutexTrylock(LLMutex* mutex, U32 aTries, U32 delay_ms = 10);
+ ~LLMutexTrylock();
+
+ bool isLocked() const
+ {
+ return mLocked;
+ }
+
+private:
+ LLMutex* mMutex;
+ bool mLocked;
+};
+
+//============================================================================
+
+/**
+* @class LLScopedLock
+* @brief Small class to help lock and unlock mutexes.
+*
+* 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 mutex. If you pass in NULL,
+ * this wrapper will not lock.
+ */
+ LLScopedLock(std::mutex* 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;
+ std::mutex* mMutex;
+};
+
+#endif // LL_LLMUTEX_H
diff --git a/indra/llcommon/llnametable.h b/indra/llcommon/llnametable.h index ca4c56f630..bcc6267ec5 100644 --- a/indra/llcommon/llnametable.h +++ b/indra/llcommon/llnametable.h @@ -1,103 +1,103 @@ -/** - * @file llnametable.h - * @brief LLNameTable class is a table to associate pointers with string names - * - * $LicenseInfo:firstyear=2000&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLNAMETABLE_H -#define LL_LLNAMETABLE_H - -#include <map> - -#include "string_table.h" - -template <class DATA> -class LLNameTable -{ -public: - LLNameTable() - : mNameMap() - { - } - - ~LLNameTable() - { - } - - void addEntry(const std::string& name, DATA data) - { - addEntry(name.c_str(), data); - } - - void addEntry(const char *name, DATA data) - { - char *tablename = gStringTable.addString(name); - mNameMap[tablename] = data; - } - - bool checkName(const std::string& name) const - { - return checkName(name.c_str()); - } - - // "logically const" even though it modifies the global nametable - bool checkName(const char *name) const - { - char *tablename = gStringTable.addString(name); - return mNameMap.find(tablename) != mNameMap.end(); - } - - DATA resolveName(const std::string& name) const - { - return resolveName(name.c_str()); - } - - // "logically const" even though it modifies the global nametable - DATA resolveName(const char *name) const - { - char *tablename = gStringTable.addString(name); - const_iter_t iter = mNameMap.find(tablename); - if (iter != mNameMap.end()) - return iter->second; - else - return 0; - } - - // O(N)! (currently only used in one place... (newsim/llstate.cpp)) - const char *resolveData(const DATA &data) const - { - for (const name_map_t::value_type& pair : mNameMap) - { - if (pair.second == data) - return pair.first; - } - return NULL; - } - - typedef std::map<const char *, DATA> name_map_t; - typedef typename std::map<const char *,DATA>::iterator iter_t; - typedef typename std::map<const char *,DATA>::const_iterator const_iter_t; - name_map_t mNameMap; -}; - -#endif +/**
+ * @file llnametable.h
+ * @brief LLNameTable class is a table to associate pointers with string names
+ *
+ * $LicenseInfo:firstyear=2000&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLNAMETABLE_H
+#define LL_LLNAMETABLE_H
+
+#include <map>
+
+#include "string_table.h"
+
+template <class DATA>
+class LLNameTable
+{
+public:
+ LLNameTable()
+ : mNameMap()
+ {
+ }
+
+ ~LLNameTable()
+ {
+ }
+
+ void addEntry(const std::string& name, DATA data)
+ {
+ addEntry(name.c_str(), data);
+ }
+
+ void addEntry(const char *name, DATA data)
+ {
+ char *tablename = gStringTable.addString(name);
+ mNameMap[tablename] = data;
+ }
+
+ bool checkName(const std::string& name) const
+ {
+ return checkName(name.c_str());
+ }
+
+ // "logically const" even though it modifies the global nametable
+ bool checkName(const char *name) const
+ {
+ char *tablename = gStringTable.addString(name);
+ return mNameMap.find(tablename) != mNameMap.end();
+ }
+
+ DATA resolveName(const std::string& name) const
+ {
+ return resolveName(name.c_str());
+ }
+
+ // "logically const" even though it modifies the global nametable
+ DATA resolveName(const char *name) const
+ {
+ char *tablename = gStringTable.addString(name);
+ const_iter_t iter = mNameMap.find(tablename);
+ if (iter != mNameMap.end())
+ return iter->second;
+ else
+ return 0;
+ }
+
+ // O(N)! (currently only used in one place... (newsim/llstate.cpp))
+ const char *resolveData(const DATA &data) const
+ {
+ for (const name_map_t::value_type& pair : mNameMap)
+ {
+ if (pair.second == data)
+ return pair.first;
+ }
+ return NULL;
+ }
+
+ typedef std::map<const char *, DATA> name_map_t;
+ typedef typename std::map<const char *,DATA>::iterator iter_t;
+ typedef typename std::map<const char *,DATA>::const_iterator const_iter_t;
+ name_map_t mNameMap;
+};
+
+#endif
diff --git a/indra/llcommon/llpointer.h b/indra/llcommon/llpointer.h index 64aceddf32..f5916f9d58 100644 --- a/indra/llcommon/llpointer.h +++ b/indra/llcommon/llpointer.h @@ -1,32 +1,32 @@ -/** +/** * @file llpointer.h * @brief A reference-counted pointer for objects derived from LLRefCount * * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #ifndef LLPOINTER_H #define LLPOINTER_H -#include "llerror.h" // *TODO: consider eliminating this +#include "llerror.h" // *TODO: consider eliminating this #include "llmutex.h" //---------------------------------------------------------------------------- @@ -46,299 +46,299 @@ template <class Type> class LLPointer { public: - LLPointer() : - mPointer(NULL) - { - } - - LLPointer(Type* ptr) : - mPointer(ptr) - { - ref(); - } - - LLPointer(const LLPointer<Type>& ptr) : - mPointer(ptr.mPointer) - { - ref(); - } - - // Support conversion up the type hierarchy. See Item 45 in Effective C++, 3rd Ed. - template<typename Subclass> - LLPointer(const LLPointer<Subclass>& ptr) : - mPointer(ptr.get()) - { - ref(); - } - - ~LLPointer() - { - unref(); - } - - Type* get() const { return mPointer; } - const Type* operator->() const { return mPointer; } - Type* operator->() { return mPointer; } - const Type& operator*() const { return *mPointer; } - Type& operator*() { return *mPointer; } - - operator BOOL() const { return (mPointer != NULL); } - operator bool() const { return (mPointer != NULL); } - bool operator!() const { return (mPointer == NULL); } - bool isNull() const { return (mPointer == NULL); } - bool notNull() const { return (mPointer != NULL); } - - operator Type*() const { return mPointer; } - bool operator !=(Type* ptr) const { return (mPointer != ptr); } - bool operator ==(Type* ptr) const { return (mPointer == ptr); } - bool operator ==(const LLPointer<Type>& ptr) const { return (mPointer == ptr.mPointer); } - bool operator < (const LLPointer<Type>& ptr) const { return (mPointer < ptr.mPointer); } - bool operator > (const LLPointer<Type>& ptr) const { return (mPointer > ptr.mPointer); } - - LLPointer<Type>& operator =(Type* ptr) - { - assign(ptr); - return *this; - } - - LLPointer<Type>& operator =(const LLPointer<Type>& ptr) - { - assign(ptr); - return *this; - } - - // support assignment up the type hierarchy. See Item 45 in Effective C++, 3rd Ed. - template<typename Subclass> - LLPointer<Type>& operator =(const LLPointer<Subclass>& ptr) - { - assign(ptr.get()); - return *this; - } - - // Just exchange the pointers, which will not change the reference counts. - static void swap(LLPointer<Type>& a, LLPointer<Type>& b) - { - Type* temp = a.mPointer; - a.mPointer = b.mPointer; - b.mPointer = temp; - } + LLPointer() : + mPointer(NULL) + { + } + + LLPointer(Type* ptr) : + mPointer(ptr) + { + ref(); + } + + LLPointer(const LLPointer<Type>& ptr) : + mPointer(ptr.mPointer) + { + ref(); + } + + // Support conversion up the type hierarchy. See Item 45 in Effective C++, 3rd Ed. + template<typename Subclass> + LLPointer(const LLPointer<Subclass>& ptr) : + mPointer(ptr.get()) + { + ref(); + } + + ~LLPointer() + { + unref(); + } + + Type* get() const { return mPointer; } + const Type* operator->() const { return mPointer; } + Type* operator->() { return mPointer; } + const Type& operator*() const { return *mPointer; } + Type& operator*() { return *mPointer; } + + operator BOOL() const { return (mPointer != NULL); } + operator bool() const { return (mPointer != NULL); } + bool operator!() const { return (mPointer == NULL); } + bool isNull() const { return (mPointer == NULL); } + bool notNull() const { return (mPointer != NULL); } + + operator Type*() const { return mPointer; } + bool operator !=(Type* ptr) const { return (mPointer != ptr); } + bool operator ==(Type* ptr) const { return (mPointer == ptr); } + bool operator ==(const LLPointer<Type>& ptr) const { return (mPointer == ptr.mPointer); } + bool operator < (const LLPointer<Type>& ptr) const { return (mPointer < ptr.mPointer); } + bool operator > (const LLPointer<Type>& ptr) const { return (mPointer > ptr.mPointer); } + + LLPointer<Type>& operator =(Type* ptr) + { + assign(ptr); + return *this; + } + + LLPointer<Type>& operator =(const LLPointer<Type>& ptr) + { + assign(ptr); + return *this; + } + + // support assignment up the type hierarchy. See Item 45 in Effective C++, 3rd Ed. + template<typename Subclass> + LLPointer<Type>& operator =(const LLPointer<Subclass>& ptr) + { + assign(ptr.get()); + return *this; + } + + // Just exchange the pointers, which will not change the reference counts. + static void swap(LLPointer<Type>& a, LLPointer<Type>& b) + { + Type* temp = a.mPointer; + a.mPointer = b.mPointer; + b.mPointer = temp; + } protected: #ifdef LL_LIBRARY_INCLUDE - void ref(); - void unref(); + void ref(); + void unref(); #else - void ref() - { - if (mPointer) - { - mPointer->ref(); - } - } - - void unref() - { - if (mPointer) - { - Type *temp = mPointer; - mPointer = NULL; - temp->unref(); - if (mPointer != NULL) - { - LL_WARNS() << "Unreference did assignment to non-NULL because of destructor" << LL_ENDL; - unref(); - } - } - } + void ref() + { + if (mPointer) + { + mPointer->ref(); + } + } + + void unref() + { + if (mPointer) + { + Type *temp = mPointer; + mPointer = NULL; + temp->unref(); + if (mPointer != NULL) + { + LL_WARNS() << "Unreference did assignment to non-NULL because of destructor" << LL_ENDL; + unref(); + } + } + } #endif // LL_LIBRARY_INCLUDE - void assign(const LLPointer<Type>& ptr) - { - if (mPointer != ptr.mPointer) - { - unref(); - mPointer = ptr.mPointer; - ref(); - } - } + void assign(const LLPointer<Type>& ptr) + { + if (mPointer != ptr.mPointer) + { + unref(); + mPointer = ptr.mPointer; + ref(); + } + } protected: - Type* mPointer; + Type* mPointer; }; template <class Type> class LLConstPointer { public: - LLConstPointer() : - mPointer(NULL) - { - } - - LLConstPointer(const Type* ptr) : - mPointer(ptr) - { - ref(); - } - - LLConstPointer(const LLConstPointer<Type>& ptr) : - mPointer(ptr.mPointer) - { - ref(); - } - - // support conversion up the type hierarchy. See Item 45 in Effective C++, 3rd Ed. - template<typename Subclass> - LLConstPointer(const LLConstPointer<Subclass>& ptr) : - mPointer(ptr.get()) - { - ref(); - } - - ~LLConstPointer() - { - unref(); - } - - const Type* get() const { return mPointer; } - const Type* operator->() const { return mPointer; } - const Type& operator*() const { return *mPointer; } - - operator BOOL() const { return (mPointer != NULL); } - operator bool() const { return (mPointer != NULL); } - bool operator!() const { return (mPointer == NULL); } - bool isNull() const { return (mPointer == NULL); } - bool notNull() const { return (mPointer != NULL); } - - operator const Type*() const { return mPointer; } - bool operator !=(const Type* ptr) const { return (mPointer != ptr); } - bool operator ==(const Type* ptr) const { return (mPointer == ptr); } - bool operator ==(const LLConstPointer<Type>& ptr) const { return (mPointer == ptr.mPointer); } - bool operator < (const LLConstPointer<Type>& ptr) const { return (mPointer < ptr.mPointer); } - bool operator > (const LLConstPointer<Type>& ptr) const { return (mPointer > ptr.mPointer); } - - LLConstPointer<Type>& operator =(const Type* ptr) - { - if( mPointer != ptr ) - { - unref(); - mPointer = ptr; - ref(); - } - - return *this; - } - - LLConstPointer<Type>& operator =(const LLConstPointer<Type>& ptr) - { - if( mPointer != ptr.mPointer ) - { - unref(); - mPointer = ptr.mPointer; - ref(); - } - return *this; - } - - // support assignment up the type hierarchy. See Item 45 in Effective C++, 3rd Ed. - template<typename Subclass> - LLConstPointer<Type>& operator =(const LLConstPointer<Subclass>& ptr) - { - if( mPointer != ptr.get() ) - { - unref(); - mPointer = ptr.get(); - ref(); - } - return *this; - } - - // Just exchange the pointers, which will not change the reference counts. - static void swap(LLConstPointer<Type>& a, LLConstPointer<Type>& b) - { - const Type* temp = a.mPointer; - a.mPointer = b.mPointer; - b.mPointer = temp; - } + LLConstPointer() : + mPointer(NULL) + { + } + + LLConstPointer(const Type* ptr) : + mPointer(ptr) + { + ref(); + } + + LLConstPointer(const LLConstPointer<Type>& ptr) : + mPointer(ptr.mPointer) + { + ref(); + } + + // support conversion up the type hierarchy. See Item 45 in Effective C++, 3rd Ed. + template<typename Subclass> + LLConstPointer(const LLConstPointer<Subclass>& ptr) : + mPointer(ptr.get()) + { + ref(); + } + + ~LLConstPointer() + { + unref(); + } + + const Type* get() const { return mPointer; } + const Type* operator->() const { return mPointer; } + const Type& operator*() const { return *mPointer; } + + operator BOOL() const { return (mPointer != NULL); } + operator bool() const { return (mPointer != NULL); } + bool operator!() const { return (mPointer == NULL); } + bool isNull() const { return (mPointer == NULL); } + bool notNull() const { return (mPointer != NULL); } + + operator const Type*() const { return mPointer; } + bool operator !=(const Type* ptr) const { return (mPointer != ptr); } + bool operator ==(const Type* ptr) const { return (mPointer == ptr); } + bool operator ==(const LLConstPointer<Type>& ptr) const { return (mPointer == ptr.mPointer); } + bool operator < (const LLConstPointer<Type>& ptr) const { return (mPointer < ptr.mPointer); } + bool operator > (const LLConstPointer<Type>& ptr) const { return (mPointer > ptr.mPointer); } + + LLConstPointer<Type>& operator =(const Type* ptr) + { + if( mPointer != ptr ) + { + unref(); + mPointer = ptr; + ref(); + } + + return *this; + } + + LLConstPointer<Type>& operator =(const LLConstPointer<Type>& ptr) + { + if( mPointer != ptr.mPointer ) + { + unref(); + mPointer = ptr.mPointer; + ref(); + } + return *this; + } + + // support assignment up the type hierarchy. See Item 45 in Effective C++, 3rd Ed. + template<typename Subclass> + LLConstPointer<Type>& operator =(const LLConstPointer<Subclass>& ptr) + { + if( mPointer != ptr.get() ) + { + unref(); + mPointer = ptr.get(); + ref(); + } + return *this; + } + + // Just exchange the pointers, which will not change the reference counts. + static void swap(LLConstPointer<Type>& a, LLConstPointer<Type>& b) + { + const Type* temp = a.mPointer; + a.mPointer = b.mPointer; + b.mPointer = temp; + } protected: #ifdef LL_LIBRARY_INCLUDE - void ref(); - void unref(); + void ref(); + void unref(); #else // LL_LIBRARY_INCLUDE - void ref() - { - if (mPointer) - { - mPointer->ref(); - } - } - - void unref() - { - if (mPointer) - { - const Type *temp = mPointer; - mPointer = NULL; - temp->unref(); - if (mPointer != NULL) - { - LL_WARNS() << "Unreference did assignment to non-NULL because of destructor" << LL_ENDL; - unref(); - } - } - } + void ref() + { + if (mPointer) + { + mPointer->ref(); + } + } + + void unref() + { + if (mPointer) + { + const Type *temp = mPointer; + mPointer = NULL; + temp->unref(); + if (mPointer != NULL) + { + LL_WARNS() << "Unreference did assignment to non-NULL because of destructor" << LL_ENDL; + unref(); + } + } + } #endif // LL_LIBRARY_INCLUDE protected: - const Type* mPointer; + const Type* mPointer; }; template<typename Type> class LLCopyOnWritePointer : public LLPointer<Type> { public: - typedef LLCopyOnWritePointer<Type> self_t; - typedef LLPointer<Type> pointer_t; - - LLCopyOnWritePointer() - : mStayUnique(false) - {} - - LLCopyOnWritePointer(Type* ptr) - : LLPointer<Type>(ptr), - mStayUnique(false) - {} - - LLCopyOnWritePointer(LLPointer<Type>& ptr) - : LLPointer<Type>(ptr), - mStayUnique(false) - { - if (ptr.mForceUnique) - { - makeUnique(); - } - } - - Type* write() - { - makeUnique(); - return pointer_t::mPointer; - } - - void makeUnique() - { - if (pointer_t::notNull() && pointer_t::mPointer->getNumRefs() > 1) - { - *(pointer_t* )(this) = new Type(*pointer_t::mPointer); - } - } - - const Type* operator->() const { return pointer_t::mPointer; } - const Type& operator*() const { return *pointer_t::mPointer; } - - void setStayUnique(bool stay) { makeUnique(); mStayUnique = stay; } + typedef LLCopyOnWritePointer<Type> self_t; + typedef LLPointer<Type> pointer_t; + + LLCopyOnWritePointer() + : mStayUnique(false) + {} + + LLCopyOnWritePointer(Type* ptr) + : LLPointer<Type>(ptr), + mStayUnique(false) + {} + + LLCopyOnWritePointer(LLPointer<Type>& ptr) + : LLPointer<Type>(ptr), + mStayUnique(false) + { + if (ptr.mForceUnique) + { + makeUnique(); + } + } + + Type* write() + { + makeUnique(); + return pointer_t::mPointer; + } + + void makeUnique() + { + if (pointer_t::notNull() && pointer_t::mPointer->getNumRefs() > 1) + { + *(pointer_t* )(this) = new Type(*pointer_t::mPointer); + } + } + + const Type* operator->() const { return pointer_t::mPointer; } + const Type& operator*() const { return *pointer_t::mPointer; } + + void setStayUnique(bool stay) { makeUnique(); mStayUnique = stay; } private: - bool mStayUnique; + bool mStayUnique; }; diff --git a/indra/llcommon/llpredicate.cpp b/indra/llcommon/llpredicate.cpp index 1278948e24..fe80b77b63 100644 --- a/indra/llcommon/llpredicate.cpp +++ b/indra/llcommon/llpredicate.cpp @@ -1,25 +1,25 @@ -/** +/** * @file llpredicate.cpp * @brief abstraction for filtering objects by predicates, with arbitrary boolean expressions * * $LicenseInfo:firstyear=2008&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -29,13 +29,13 @@ namespace LLPredicate { - const U32 cPredicateFlagsFromEnum[5] = - { - 0xAAAAaaaa, // 10101010101010101010101010101010 - 0xCCCCcccc, // 11001100110011001100110011001100 - 0xF0F0F0F0, // 11110000111100001111000011110000 - 0xFF00FF00, // 11111111000000001111111100000000 - 0xFFFF0000 // 11111111111111110000000000000000 - }; + const U32 cPredicateFlagsFromEnum[5] = + { + 0xAAAAaaaa, // 10101010101010101010101010101010 + 0xCCCCcccc, // 11001100110011001100110011001100 + 0xF0F0F0F0, // 11110000111100001111000011110000 + 0xFF00FF00, // 11111111000000001111111100000000 + 0xFFFF0000 // 11111111111111110000000000000000 + }; } diff --git a/indra/llcommon/llpredicate.h b/indra/llcommon/llpredicate.h index e6c56a5711..7c6874d279 100644 --- a/indra/llcommon/llpredicate.h +++ b/indra/llcommon/llpredicate.h @@ -1,25 +1,25 @@ -/** +/** * @file llpredicate.h * @brief abstraction for filtering objects by predicates, with arbitrary boolean expressions * * $LicenseInfo:firstyear=2008&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -31,179 +31,179 @@ namespace LLPredicate { - template<typename ENUM> class Rule; - - extern const U32 cPredicateFlagsFromEnum[5]; - - template<typename ENUM> - class Value - { - public: - typedef U32 predicate_flag_t; - static const S32 cMaxEnum = 5; - - Value(ENUM e, bool predicate_value = true) - : mPredicateFlags(predicate_value ? cPredicateFlagsFromEnum[e] : ~cPredicateFlagsFromEnum[e]) - { - llassert(0 <= e && e < cMaxEnum); - } - - Value() - : mPredicateFlags(0xFFFFffff) - {} - - Value operator!() const - { - Value new_value; - new_value.mPredicateFlags = ~mPredicateFlags; - return new_value; - } - - Value operator &&(const Value other) const - { - Value new_value; - new_value.mPredicateFlags = mPredicateFlags & other.mPredicateFlags; - return new_value; - } - - Value operator ||(const Value other) const - { - Value new_value; - new_value.mPredicateFlags = mPredicateFlags | other.mPredicateFlags; - return new_value; - } - - void set(ENUM e, bool value = true) - { - llassert(0 <= e && e < cMaxEnum); - predicate_flag_t flags_to_modify; - predicate_flag_t mask = cPredicateFlagsFromEnum[e]; - if (value) - { // add predicate "e" to flags that don't contain it already - flags_to_modify = (mPredicateFlags & ~mask); - // clear flags not containing e - mPredicateFlags &= mask; - // add back flags shifted to contain e - mPredicateFlags |= flags_to_modify << (0x1 << e); - } - else - { // remove predicate "e" from flags that contain it - flags_to_modify = (mPredicateFlags & mask); - // clear flags containing e - mPredicateFlags &= ~mask; - // add back flags shifted to not contain e - mPredicateFlags |= flags_to_modify >> (0x1 << e); - } - } - - void forget(ENUM e) - { - set(e, true); - U32 flags_with_predicate = mPredicateFlags; - set(e, false); - // ambiguous value is result of adding and removing predicate at the same time! - mPredicateFlags |= flags_with_predicate; - } - - bool allSet() const - { - return mPredicateFlags == ~0; - } - - bool noneSet() const - { - return mPredicateFlags == 0; - } - - bool someSet() const - { - return mPredicateFlags != 0; - } - - private: - predicate_flag_t mPredicateFlags; - }; - - template<typename ENUM> - class Rule - { - public: - Rule(ENUM value) - : mRule(value) - {} - - Rule(const Value<ENUM> other) - : mRule(other) - {} - - Rule() - {} - - void require(ENUM e, bool match) - { - mRule.set(e, match); - } - - void allow(ENUM e) - { - mRule.forget(e); - } - - bool check(const Value<ENUM> value) const - { - return (mRule && value).someSet(); - } - - bool requires(const Value<ENUM> value) const - { - return (mRule && value).someSet() && (!mRule && value).noneSet(); - } - - bool isAmbivalent(const Value<ENUM> value) const - { - return (mRule && value).someSet() && (!mRule && value).someSet(); - } - - bool acceptsAll() const - { - return mRule.allSet(); - } - - bool acceptsNone() const - { - return mRule.noneSet(); - } - - Rule operator!() const - { - Rule new_rule; - new_rule.mRule = !mRule; - return new_rule; - } - - Rule operator &&(const Rule other) const - { - Rule new_rule; - new_rule.mRule = mRule && other.mRule; - return new_rule; - } - - Rule operator ||(const Rule other) const - { - Rule new_rule; - new_rule.mRule = mRule || other.mRule; - return new_rule; - } - - private: - Value<ENUM> mRule; - }; + template<typename ENUM> class Rule; + + extern const U32 cPredicateFlagsFromEnum[5]; + + template<typename ENUM> + class Value + { + public: + typedef U32 predicate_flag_t; + static const S32 cMaxEnum = 5; + + Value(ENUM e, bool predicate_value = true) + : mPredicateFlags(predicate_value ? cPredicateFlagsFromEnum[e] : ~cPredicateFlagsFromEnum[e]) + { + llassert(0 <= e && e < cMaxEnum); + } + + Value() + : mPredicateFlags(0xFFFFffff) + {} + + Value operator!() const + { + Value new_value; + new_value.mPredicateFlags = ~mPredicateFlags; + return new_value; + } + + Value operator &&(const Value other) const + { + Value new_value; + new_value.mPredicateFlags = mPredicateFlags & other.mPredicateFlags; + return new_value; + } + + Value operator ||(const Value other) const + { + Value new_value; + new_value.mPredicateFlags = mPredicateFlags | other.mPredicateFlags; + return new_value; + } + + void set(ENUM e, bool value = true) + { + llassert(0 <= e && e < cMaxEnum); + predicate_flag_t flags_to_modify; + predicate_flag_t mask = cPredicateFlagsFromEnum[e]; + if (value) + { // add predicate "e" to flags that don't contain it already + flags_to_modify = (mPredicateFlags & ~mask); + // clear flags not containing e + mPredicateFlags &= mask; + // add back flags shifted to contain e + mPredicateFlags |= flags_to_modify << (0x1 << e); + } + else + { // remove predicate "e" from flags that contain it + flags_to_modify = (mPredicateFlags & mask); + // clear flags containing e + mPredicateFlags &= ~mask; + // add back flags shifted to not contain e + mPredicateFlags |= flags_to_modify >> (0x1 << e); + } + } + + void forget(ENUM e) + { + set(e, true); + U32 flags_with_predicate = mPredicateFlags; + set(e, false); + // ambiguous value is result of adding and removing predicate at the same time! + mPredicateFlags |= flags_with_predicate; + } + + bool allSet() const + { + return mPredicateFlags == ~0; + } + + bool noneSet() const + { + return mPredicateFlags == 0; + } + + bool someSet() const + { + return mPredicateFlags != 0; + } + + private: + predicate_flag_t mPredicateFlags; + }; + + template<typename ENUM> + class Rule + { + public: + Rule(ENUM value) + : mRule(value) + {} + + Rule(const Value<ENUM> other) + : mRule(other) + {} + + Rule() + {} + + void require(ENUM e, bool match) + { + mRule.set(e, match); + } + + void allow(ENUM e) + { + mRule.forget(e); + } + + bool check(const Value<ENUM> value) const + { + return (mRule && value).someSet(); + } + + bool requires(const Value<ENUM> value) const + { + return (mRule && value).someSet() && (!mRule && value).noneSet(); + } + + bool isAmbivalent(const Value<ENUM> value) const + { + return (mRule && value).someSet() && (!mRule && value).someSet(); + } + + bool acceptsAll() const + { + return mRule.allSet(); + } + + bool acceptsNone() const + { + return mRule.noneSet(); + } + + Rule operator!() const + { + Rule new_rule; + new_rule.mRule = !mRule; + return new_rule; + } + + Rule operator &&(const Rule other) const + { + Rule new_rule; + new_rule.mRule = mRule && other.mRule; + return new_rule; + } + + Rule operator ||(const Rule other) const + { + Rule new_rule; + new_rule.mRule = mRule || other.mRule; + return new_rule; + } + + private: + Value<ENUM> mRule; + }; } template<typename ENUM> LLPredicate::Value<ENUM> ll_make_predicate(ENUM e, bool predicate_value = true) { - return LLPredicate::Value<ENUM>(e, predicate_value); + return LLPredicate::Value<ENUM>(e, predicate_value); } diff --git a/indra/llcommon/llpreprocessor.h b/indra/llcommon/llpreprocessor.h index dc586b0008..a54408a852 100644 --- a/indra/llcommon/llpreprocessor.h +++ b/indra/llcommon/llpreprocessor.h @@ -1,4 +1,4 @@ -/** +/** * @file llpreprocessor.h * @brief This file should be included in all Linden Lab files and * should only contain special preprocessor directives @@ -6,21 +6,21 @@ * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -32,7 +32,7 @@ #ifdef LL_LINUX #define __ENABLE_WSTRING #include <endian.h> -#endif // LL_LINUX +#endif // LL_LINUX #if (defined(LL_WINDOWS) || (defined(LL_LINUX) && (__BYTE_ORDER == __LITTLE_ENDIAN)) || (defined(LL_DARWIN) && defined(__LITTLE_ENDIAN__))) #define LL_LITTLE_ENDIAN 1 @@ -64,31 +64,31 @@ // 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 + #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 + #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 + // 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 + // Not sure why this is different, but... + #ifndef MAX_PATH + #define MAX_PATH PATH_MAX + #endif // not MAX_PATH #endif @@ -117,33 +117,33 @@ #ifndef XML_STATIC #define XML_STATIC #endif -#endif // LL_WINDOWS +#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 : 4180) // qualifier applied to function type has no meaning; ignored -//#pragma warning( disable : 4284 ) // silly MS warning deep inside their <map> include file +#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 : 4180) // qualifier applied to function type has no meaning; ignored +//#pragma warning( disable : 4284 ) // silly MS warning deep inside their <map> include file #if ADDRESS_SIZE == 64 // That one is all over the place for x64 builds. #pragma warning( disable : 4267 ) // 'var' : conversion from 'size_t' to 'type', possible loss of data) #endif -#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 +#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 // Linker optimization with "extern template" generates these warnings -#pragma warning( disable : 4231 ) // nonstandard extension used : 'extern' before template explicit instantiation +#pragma warning( disable : 4231 ) // nonstandard extension used : 'extern' before template explicit instantiation #pragma warning( disable : 4506 ) // no definition for inline function // level 4 warnings that we need to disable: @@ -156,9 +156,9 @@ #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 -#pragma warning (disable : 4018) // '<' : signed/unsigned mismatch +#pragma warning (disable : 4018) // '<' : signed/unsigned mismatch -#endif // LL_MSVC +#endif // LL_MSVC #if LL_WINDOWS #define LL_DLLEXPORT __declspec(dllexport) @@ -232,4 +232,4 @@ #define LL_PRETTY_FUNCTION __PRETTY_FUNCTION__ #endif -#endif // not LL_LINDEN_PREPROCESSOR_H +#endif // not LL_LINDEN_PREPROCESSOR_H diff --git a/indra/llcommon/llpriqueuemap.h b/indra/llcommon/llpriqueuemap.h index 7dd43d87ca..a0a0855109 100644 --- a/indra/llcommon/llpriqueuemap.h +++ b/indra/llcommon/llpriqueuemap.h @@ -1,145 +1,145 @@ -/** - * @file llpriqueuemap.h - * @brief Priority queue implementation - * - * $LicenseInfo:firstyear=2003&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ -#ifndef LL_LLPRIQUEUEMAP_H -#define LL_LLPRIQUEUEMAP_H - -#include <map> - -// -// Priority queue, implemented under the hood as a -// map. Needs to be done this way because none of the -// standard STL containers provide a representation -// where it's easy to reprioritize. -// - -template <class DATA> -class LLPQMKey -{ -public: - LLPQMKey(const F32 priority, DATA data) : mPriority(priority), mData(data) - { - } - - bool operator<(const LLPQMKey &b) const - { - if (mPriority > b.mPriority) - { - return true; - } - if (mPriority < b.mPriority) - { - return false; - } - if (mData > b.mData) - { - return true; - } - return false; - } - - F32 mPriority; - DATA mData; -}; - -template <class DATA_TYPE> -class LLPriQueueMap -{ -public: - typedef typename std::map<LLPQMKey<DATA_TYPE>, DATA_TYPE>::iterator pqm_iter; - typedef std::pair<LLPQMKey<DATA_TYPE>, DATA_TYPE> pqm_pair; - typedef void (*set_pri_fn)(DATA_TYPE &data, const F32 priority); - typedef F32 (*get_pri_fn)(DATA_TYPE &data); - - - LLPriQueueMap(set_pri_fn set_pri, get_pri_fn get_pri) : mSetPriority(set_pri), mGetPriority(get_pri) - { - } - - void push(const F32 priority, DATA_TYPE data) - { -#ifdef _DEBUG - pqm_iter iter = mMap.find(LLPQMKey<DATA_TYPE>(priority, data)); - if (iter != mMap.end()) - { - LL_ERRS() << "Pushing already existing data onto queue!" << LL_ENDL; - } -#endif - mMap.insert(pqm_pair(LLPQMKey<DATA_TYPE>(priority, data), data)); - } - - bool pop(DATA_TYPE *datap) - { - pqm_iter iter; - iter = mMap.begin(); - if (iter == mMap.end()) - { - return false; - } - *datap = (*(iter)).second; - mMap.erase(iter); - - return true; - } - - void reprioritize(const F32 new_priority, DATA_TYPE data) - { - pqm_iter iter; - F32 cur_priority = mGetPriority(data); - LLPQMKey<DATA_TYPE> cur_key(cur_priority, data); - iter = mMap.find(cur_key); - if (iter == mMap.end()) - { - LL_WARNS() << "Data not on priority queue!" << LL_ENDL; - // OK, try iterating through all of the data and seeing if we just screwed up the priority - // somehow. - for (pqm_pair pair : mMap) - { - if (pair.second == data) - { - LL_ERRS() << "Data on priority queue but priority not matched!" << LL_ENDL; - } - } - return; - } - - mMap.erase(iter); - mSetPriority(data, new_priority); - push(new_priority, data); - } - - S32 getLength() const - { - return (S32)mMap.size(); - } - - // Hack: public for use by the transfer manager, ugh. - std::map<LLPQMKey<DATA_TYPE>, DATA_TYPE> mMap; -protected: - void (*mSetPriority)(DATA_TYPE &data, const F32 priority); - F32 (*mGetPriority)(DATA_TYPE &data); -}; - -#endif // LL_LLPRIQUEUEMAP_H +/**
+ * @file llpriqueuemap.h
+ * @brief Priority queue implementation
+ *
+ * $LicenseInfo:firstyear=2003&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+#ifndef LL_LLPRIQUEUEMAP_H
+#define LL_LLPRIQUEUEMAP_H
+
+#include <map>
+
+//
+// Priority queue, implemented under the hood as a
+// map. Needs to be done this way because none of the
+// standard STL containers provide a representation
+// where it's easy to reprioritize.
+//
+
+template <class DATA>
+class LLPQMKey
+{
+public:
+ LLPQMKey(const F32 priority, DATA data) : mPriority(priority), mData(data)
+ {
+ }
+
+ bool operator<(const LLPQMKey &b) const
+ {
+ if (mPriority > b.mPriority)
+ {
+ return true;
+ }
+ if (mPriority < b.mPriority)
+ {
+ return false;
+ }
+ if (mData > b.mData)
+ {
+ return true;
+ }
+ return false;
+ }
+
+ F32 mPriority;
+ DATA mData;
+};
+
+template <class DATA_TYPE>
+class LLPriQueueMap
+{
+public:
+ typedef typename std::map<LLPQMKey<DATA_TYPE>, DATA_TYPE>::iterator pqm_iter;
+ typedef std::pair<LLPQMKey<DATA_TYPE>, DATA_TYPE> pqm_pair;
+ typedef void (*set_pri_fn)(DATA_TYPE &data, const F32 priority);
+ typedef F32 (*get_pri_fn)(DATA_TYPE &data);
+
+
+ LLPriQueueMap(set_pri_fn set_pri, get_pri_fn get_pri) : mSetPriority(set_pri), mGetPriority(get_pri)
+ {
+ }
+
+ void push(const F32 priority, DATA_TYPE data)
+ {
+#ifdef _DEBUG
+ pqm_iter iter = mMap.find(LLPQMKey<DATA_TYPE>(priority, data));
+ if (iter != mMap.end())
+ {
+ LL_ERRS() << "Pushing already existing data onto queue!" << LL_ENDL;
+ }
+#endif
+ mMap.insert(pqm_pair(LLPQMKey<DATA_TYPE>(priority, data), data));
+ }
+
+ bool pop(DATA_TYPE *datap)
+ {
+ pqm_iter iter;
+ iter = mMap.begin();
+ if (iter == mMap.end())
+ {
+ return false;
+ }
+ *datap = (*(iter)).second;
+ mMap.erase(iter);
+
+ return true;
+ }
+
+ void reprioritize(const F32 new_priority, DATA_TYPE data)
+ {
+ pqm_iter iter;
+ F32 cur_priority = mGetPriority(data);
+ LLPQMKey<DATA_TYPE> cur_key(cur_priority, data);
+ iter = mMap.find(cur_key);
+ if (iter == mMap.end())
+ {
+ LL_WARNS() << "Data not on priority queue!" << LL_ENDL;
+ // OK, try iterating through all of the data and seeing if we just screwed up the priority
+ // somehow.
+ for (pqm_pair pair : mMap)
+ {
+ if (pair.second == data)
+ {
+ LL_ERRS() << "Data on priority queue but priority not matched!" << LL_ENDL;
+ }
+ }
+ return;
+ }
+
+ mMap.erase(iter);
+ mSetPriority(data, new_priority);
+ push(new_priority, data);
+ }
+
+ S32 getLength() const
+ {
+ return (S32)mMap.size();
+ }
+
+ // Hack: public for use by the transfer manager, ugh.
+ std::map<LLPQMKey<DATA_TYPE>, DATA_TYPE> mMap;
+protected:
+ void (*mSetPriority)(DATA_TYPE &data, const F32 priority);
+ F32 (*mGetPriority)(DATA_TYPE &data);
+};
+
+#endif // LL_LLPRIQUEUEMAP_H
diff --git a/indra/llcommon/llprocess.cpp b/indra/llcommon/llprocess.cpp index 3b0d6df0d4..80413bb841 100644 --- a/indra/llcommon/llprocess.cpp +++ b/indra/llcommon/llprocess.cpp @@ -1,1358 +1,1358 @@ -/** - * @file llprocess.cpp - * @brief Utility class for launching, terminating, and tracking the state of processes. - * - * $LicenseInfo:firstyear=2008&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" -#include "llprocess.h" -#include "llsdutil.h" -#include "llsdserialize.h" -#include "llsingleton.h" -#include "llstring.h" -#include "stringize.h" -#include "llapr.h" -#include "apr_signal.h" -#include "llevents.h" -#include "llexception.h" - -#include <boost/bind.hpp> -#include <boost/asio/streambuf.hpp> -#include <boost/asio/buffers_iterator.hpp> -#include <iostream> -#include <stdexcept> -#include <limits> -#include <algorithm> -#include <vector> -#include <typeinfo> -#include <utility> - -/***************************************************************************** -* Helpers -*****************************************************************************/ -static const char* whichfile_[] = { "stdin", "stdout", "stderr" }; -static std::string empty; -static LLProcess::Status interpret_status(int status); -static std::string getDesc(const LLProcess::Params& params); - -static std::string whichfile(LLProcess::FILESLOT index) -{ - if (index < LL_ARRAY_SIZE(whichfile_)) - return whichfile_[index]; - return STRINGIZE("file slot " << index); -} - -/** - * Ref-counted "mainloop" listener. As long as there are still outstanding - * LLProcess objects, keep listening on "mainloop" so we can keep polling APR - * for process status. - */ -class LLProcessListener -{ - LOG_CLASS(LLProcessListener); -public: - LLProcessListener(): - mCount(0) - {} - - void addPoll(const LLProcess&) - { - // Unconditionally increment mCount. If it was zero before - // incrementing, listen on "mainloop". - if (mCount++ == 0) - { - LL_DEBUGS("LLProcess") << "listening on \"mainloop\"" << LL_ENDL; - mConnection = LLEventPumps::instance().obtain("mainloop") - .listen("LLProcessListener", boost::bind(&LLProcessListener::tick, this, _1)); - } - } - - void dropPoll(const LLProcess&) - { - // Unconditionally decrement mCount. If it's zero after decrementing, - // stop listening on "mainloop". - if (--mCount == 0) - { - LL_DEBUGS("LLProcess") << "disconnecting from \"mainloop\"" << LL_ENDL; - mConnection.disconnect(); - } - } - -private: - /// called once per frame by the "mainloop" LLEventPump - bool tick(const LLSD&) - { - // Tell APR to sense whether each registered LLProcess is still - // running and call handle_status() appropriately. We should be able - // to get the same info from an apr_proc_wait(APR_NOWAIT) call; but at - // least in APR 1.4.2, testing suggests that even with APR_NOWAIT, - // apr_proc_wait() blocks the caller. We can't have that in the - // viewer. Hence the callback rigmarole. (Once we update APR, it's - // probably worth testing again.) Also -- although there's an - // apr_proc_other_child_refresh() call, i.e. get that information for - // one specific child, it accepts an 'apr_other_child_rec_t*' that's - // mentioned NOWHERE else in the documentation or header files! I - // would use the specific call in LLProcess::getStatus() if I knew - // how. As it is, each call to apr_proc_other_child_refresh_all() will - // call callbacks for ALL still-running child processes. That's why we - // centralize such calls, using "mainloop" to ensure it happens once - // per frame, and refcounting running LLProcess objects to remain - // registered only while needed. - LL_DEBUGS("LLProcess") << "calling apr_proc_other_child_refresh_all()" << LL_ENDL; - apr_proc_other_child_refresh_all(APR_OC_REASON_RUNNING); - return false; - } - - /// If this object is destroyed before mCount goes to zero, stop - /// listening on "mainloop" anyway. - LLTempBoundListener mConnection; - unsigned mCount; -}; -static LLProcessListener sProcessListener; - -/***************************************************************************** -* WritePipe and ReadPipe -*****************************************************************************/ -LLProcess::BasePipe::~BasePipe() {} -const LLProcess::BasePipe::size_type - // use funky syntax to call max() to avoid blighted max() macros - LLProcess::BasePipe::npos((std::numeric_limits<LLProcess::BasePipe::size_type>::max)()); - -class WritePipeImpl: public LLProcess::WritePipe -{ - LOG_CLASS(WritePipeImpl); -public: - WritePipeImpl(const std::string& desc, apr_file_t* pipe): - mDesc(desc), - mPipe(pipe), - // Essential to initialize our std::ostream with our special streambuf! - mStream(&mStreambuf) - { - mConnection = LLEventPumps::instance().obtain("mainloop") - .listen(LLEventPump::inventName("WritePipe"), - boost::bind(&WritePipeImpl::tick, this, _1)); - -#if ! LL_WINDOWS - // We can't count on every child process reading everything we try to - // write to it. And if the child terminates with WritePipe data still - // pending, unless we explicitly suppress it, Posix will hit us with - // SIGPIPE. That would terminate the viewer, boom. "Ignoring" it means - // APR gets the correct errno, passes it back to us, we log it, etc. - signal(SIGPIPE, SIG_IGN); -#endif - } - - virtual std::ostream& get_ostream() { return mStream; } - virtual size_type size() const { return mStreambuf.size(); } - - bool tick(const LLSD&) - { - typedef boost::asio::streambuf::const_buffers_type const_buffer_sequence; - // If there's anything to send, try to send it. - std::size_t total(mStreambuf.size()), consumed(0); - if (total) - { - const_buffer_sequence bufs = mStreambuf.data(); - // In general, our streambuf might contain a number of different - // physical buffers; iterate over those. - bool keepwriting = true; - for (const_buffer_sequence::const_iterator bufi(bufs.begin()), bufend(bufs.end()); - bufi != bufend && keepwriting; ++bufi) - { - // http://www.boost.org/doc/libs/1_49_0_beta1/doc/html/boost_asio/reference/buffer.html#boost_asio.reference.buffer.accessing_buffer_contents - // Although apr_file_write() accepts const void*, we - // manipulate const char* so we can increment the pointer. - const char* remainptr = boost::asio::buffer_cast<const char*>(*bufi); - std::size_t remainlen = boost::asio::buffer_size(*bufi); - while (remainlen) - { - // Tackle the current buffer in discrete chunks. On - // Windows, we've observed strange failures when trying to - // write big lengths (~1 MB) in a single operation. Even a - // 32K chunk seems too large. At some point along the way - // apr_file_write() returns 11 (Resource temporarily - // unavailable, i.e. EAGAIN) and says it wrote 0 bytes -- - // even though it did write the chunk! Our next write - // attempt retries with the same chunk, resulting in the - // chunk being duplicated at the child end. Using smaller - // chunks is empirically more reliable. - std::size_t towrite((std::min)(remainlen, std::size_t(4*1024))); - apr_size_t written(towrite); - apr_status_t err = apr_file_write(mPipe, remainptr, &written); - // EAGAIN is exactly what we want from a nonblocking pipe. - // Rather than waiting for data, it should return immediately. - if (! (err == APR_SUCCESS || APR_STATUS_IS_EAGAIN(err))) - { - LL_WARNS("LLProcess") << "apr_file_write(" << towrite << ") on " << mDesc - << " got " << err << ":" << LL_ENDL; - ll_apr_warn_status(err); - } - - // 'written' is modified to reflect the number of bytes actually - // written. Make sure we consume those later. (Don't consume them - // now, that would invalidate the buffer iterator sequence!) - consumed += written; - // don't forget to advance to next chunk of current buffer - remainptr += written; - remainlen -= written; - - char msgbuf[512]; - LL_DEBUGS("LLProcess") << "wrote " << written << " of " << towrite - << " bytes to " << mDesc - << " (original " << total << ")," - << " code " << err << ": " - << apr_strerror(err, msgbuf, sizeof(msgbuf)) - << LL_ENDL; - - // The parent end of this pipe is nonblocking. If we weren't able - // to write everything we wanted, don't keep banging on it -- that - // won't change until the child reads some. Wait for next tick(). - if (written < towrite) - { - keepwriting = false; // break outer loop over buffers too - break; - } - } // next chunk of current buffer - } // next buffer - // In all, we managed to write 'consumed' bytes. Remove them from the - // streambuf so we don't keep trying to send them. This could be - // anywhere from 0 up to mStreambuf.size(); anything we haven't yet - // sent, we'll try again later. - mStreambuf.consume(consumed); - } - - return false; - } - -private: - std::string mDesc; - apr_file_t* mPipe; - LLTempBoundListener mConnection; - boost::asio::streambuf mStreambuf; - std::ostream mStream; -}; - -class ReadPipeImpl: public LLProcess::ReadPipe -{ - LOG_CLASS(ReadPipeImpl); -public: - ReadPipeImpl(const std::string& desc, apr_file_t* pipe, LLProcess::FILESLOT index): - mDesc(desc), - mPipe(pipe), - mIndex(index), - // Essential to initialize our std::istream with our special streambuf! - mStream(&mStreambuf), - mPump("ReadPipe", true), // tweak name as needed to avoid collisions - mLimit(0), - mEOF(false) - { - mConnection = LLEventPumps::instance().obtain("mainloop") - .listen(LLEventPump::inventName("ReadPipe"), - boost::bind(&ReadPipeImpl::tick, this, _1)); - } - - ~ReadPipeImpl() - { - if (mConnection.connected()) - { - mConnection.disconnect(); - } - } - - // Much of the implementation is simply connecting the abstract virtual - // methods with implementation data concealed from the base class. - virtual std::istream& get_istream() { return mStream; } - virtual std::string getline() { return LLProcess::getline(mStream); } - virtual LLEventPump& getPump() { return mPump; } - virtual void setLimit(size_type limit) { mLimit = limit; } - virtual size_type getLimit() const { return mLimit; } - virtual size_type size() const { return mStreambuf.size(); } - - virtual std::string read(size_type len) - { - // Read specified number of bytes into a buffer. - size_type readlen((std::min)(size(), len)); - // Formally, &buffer[0] is invalid for a vector of size() 0. Exit - // early in that situation. - if (! readlen) - return ""; - // Make a buffer big enough. - std::vector<char> buffer(readlen); - mStream.read(&buffer[0], readlen); - // Since we've already clamped 'readlen', we can think of no reason - // why mStream.read() should read fewer than 'readlen' bytes. - // Nonetheless, use the actual retrieved length. - return std::string(&buffer[0], mStream.gcount()); - } - - virtual std::string peek(size_type offset=0, size_type len=npos) const - { - // Constrain caller's offset and len to overlap actual buffer content. - std::size_t real_offset = (std::min)(mStreambuf.size(), std::size_t(offset)); - size_type want_end = (len == npos)? npos : (real_offset + len); - std::size_t real_end = (std::min)(mStreambuf.size(), std::size_t(want_end)); - boost::asio::streambuf::const_buffers_type cbufs = mStreambuf.data(); - return std::string(boost::asio::buffers_begin(cbufs) + real_offset, - boost::asio::buffers_begin(cbufs) + real_end); - } - - virtual size_type find(const std::string& seek, size_type offset=0) const - { - // If we're passing a string of length 1, use find(char), which can - // use an O(n) std::find() rather than the O(n^2) std::search(). - if (seek.length() == 1) - { - return find(seek[0], offset); - } - - // If offset is beyond the whole buffer, can't even construct a valid - // iterator range; can't possibly find the string we seek. - if (offset > mStreambuf.size()) - { - return npos; - } - - boost::asio::streambuf::const_buffers_type cbufs = mStreambuf.data(); - boost::asio::buffers_iterator<boost::asio::streambuf::const_buffers_type> - begin(boost::asio::buffers_begin(cbufs)), - end (boost::asio::buffers_end(cbufs)), - found(std::search(begin + offset, end, seek.begin(), seek.end())); - return (found == end)? npos : (found - begin); - } - - virtual size_type find(char seek, size_type offset=0) const - { - // If offset is beyond the whole buffer, can't even construct a valid - // iterator range; can't possibly find the char we seek. - if (offset > mStreambuf.size()) - { - return npos; - } - - boost::asio::streambuf::const_buffers_type cbufs = mStreambuf.data(); - boost::asio::buffers_iterator<boost::asio::streambuf::const_buffers_type> - begin(boost::asio::buffers_begin(cbufs)), - end (boost::asio::buffers_end(cbufs)), - found(std::find(begin + offset, end, seek)); - return (found == end)? npos : (found - begin); - } - - bool tick(const LLSD&) - { - // Once we've hit EOF, skip all the rest of this. - if (mEOF) - return false; - - typedef boost::asio::streambuf::mutable_buffers_type mutable_buffer_sequence; - // Try, every time, to read into our streambuf. In fact, we have no - // idea how much data the child might be trying to send: keep trying - // until we're convinced we've temporarily exhausted the pipe. - enum PipeState { RETRY, EXHAUSTED, CLOSED }; - PipeState state = RETRY; - std::size_t committed(0); - do - { - // attempt to read an arbitrary size - mutable_buffer_sequence bufs = mStreambuf.prepare(4096); - // In general, the mutable_buffer_sequence returned by prepare() might - // contain a number of different physical buffers; iterate over those. - std::size_t tocommit(0); - for (mutable_buffer_sequence::const_iterator bufi(bufs.begin()), bufend(bufs.end()); - bufi != bufend; ++bufi) - { - // http://www.boost.org/doc/libs/1_49_0_beta1/doc/html/boost_asio/reference/buffer.html#boost_asio.reference.buffer.accessing_buffer_contents - std::size_t toread(boost::asio::buffer_size(*bufi)); - apr_size_t gotten(toread); - apr_status_t err = apr_file_read(mPipe, - boost::asio::buffer_cast<void*>(*bufi), - &gotten); - // EAGAIN is exactly what we want from a nonblocking pipe. - // Rather than waiting for data, it should return immediately. - if (! (err == APR_SUCCESS || APR_STATUS_IS_EAGAIN(err))) - { - // Handle EOF specially: it's part of normal-case processing. - if (err == APR_EOF) - { - LL_DEBUGS("LLProcess") << "EOF on " << mDesc << LL_ENDL; - } - else - { - LL_WARNS("LLProcess") << "apr_file_read(" << toread << ") on " << mDesc - << " got " << err << ":" << LL_ENDL; - ll_apr_warn_status(err); - } - // Either way, though, we won't need any more tick() calls. - mConnection.disconnect(); - // Ignore any subsequent calls we might get anyway. - mEOF = true; - state = CLOSED; // also break outer retry loop - break; - } - - // 'gotten' was modified to reflect the number of bytes actually - // received. Make sure we commit those later. (Don't commit them - // now, that would invalidate the buffer iterator sequence!) - tocommit += gotten; - LL_DEBUGS("LLProcess") << "filled " << gotten << " of " << toread - << " bytes from " << mDesc << LL_ENDL; - - // The parent end of this pipe is nonblocking. If we weren't even - // able to fill this buffer, don't loop to try to fill the next -- - // that won't change until the child writes more. Wait for next - // tick(). - if (gotten < toread) - { - // break outer retry loop too - state = EXHAUSTED; - break; - } - } - - // Don't forget to "commit" the data! - mStreambuf.commit(tocommit); - committed += tocommit; - - // state is changed from RETRY when we can't fill any one buffer - // of the mutable_buffer_sequence established by the current - // prepare() call -- whether due to error or not enough bytes. - // That is, if state is still RETRY, we've filled every physical - // buffer in the mutable_buffer_sequence. In that case, for all we - // know, the child might have still more data pending -- go for it! - } while (state == RETRY); - - // Once we recognize that the pipe is closed, make one more call to - // listener. The listener might be waiting for a particular substring - // to arrive, or a particular length of data or something. The event - // with "eof" == true announces that nothing further will arrive, so - // use it or lose it. - if (committed || state == CLOSED) - { - // If we actually received new data, publish it on our LLEventPump - // as advertised. Constrain it by mLimit. But show listener the - // actual accumulated buffer size, regardless of mLimit. - size_type datasize((std::min)(mLimit, size_type(mStreambuf.size()))); - mPump.post(LLSDMap - ("data", peek(0, datasize)) - ("len", LLSD::Integer(mStreambuf.size())) - ("slot", LLSD::Integer(mIndex)) - ("name", whichfile(mIndex)) - ("desc", mDesc) - ("eof", state == CLOSED)); - } - - return false; - } - -private: - std::string mDesc; - apr_file_t* mPipe; - LLProcess::FILESLOT mIndex; - LLTempBoundListener mConnection; - boost::asio::streambuf mStreambuf; - std::istream mStream; - LLEventStream mPump; - size_type mLimit; - bool mEOF; -}; - -/***************************************************************************** -* LLProcess itself -*****************************************************************************/ -/// Need an exception to avoid constructing an invalid LLProcess object, but -/// internal use only -struct LLProcessError: public LLException -{ - LLProcessError(const std::string& msg): LLException(msg) {} -}; - -LLProcessPtr LLProcess::create(const LLSDOrParams& params) -{ - try - { - return LLProcessPtr(new LLProcess(params)); - } - catch (const LLProcessError& e) - { - LL_WARNS("LLProcess") << e.what() << LL_ENDL; - - // If caller is requesting an event on process termination, send one - // indicating bad launch. This may prevent someone waiting forever for - // a termination post that can't arrive because the child never - // started. - if (params.postend.isProvided()) - { - LLEventPumps::instance().obtain(params.postend) - .post(LLSDMap - // no "id" - ("desc", getDesc(params)) - ("state", LLProcess::UNSTARTED) - // no "data" - ("string", e.what()) - ); - } - - return LLProcessPtr(); - } -} - -/// Call an apr function returning apr_status_t. On failure, log warning and -/// throw LLProcessError mentioning the function call that produced that -/// result. -#define chkapr(func) \ - if (ll_apr_warn_status(func)) \ - throw LLProcessError(#func " failed") - -LLProcess::LLProcess(const LLSDOrParams& params): - mAutokill(params.autokill), - // Because 'autokill' originally meant both 'autokill' and 'attached', to - // preserve existing semantics, we promise that mAttached defaults to the - // same setting as mAutokill. - mAttached(params.attached.isProvided()? params.attached : params.autokill), - mPool(NULL), - mPipes(NSLOTS) -{ - // Hmm, when you construct a ptr_vector with a size, it merely reserves - // space, it doesn't actually make it that big. Explicitly make it bigger. - // Because of ptr_vector's odd semantics, have to push_back(0) the right - // number of times! resize() wants to default-construct new BasePipe - // instances, which fails because it's pure virtual. But because of the - // constructor call, these push_back() calls should require no new - // allocation. - for (size_t i = 0; i < mPipes.capacity(); ++i) - mPipes.push_back(0); - - if (! params.validateBlock(true)) - { - LLTHROW(LLProcessError(STRINGIZE("not launched: failed parameter validation\n" - << LLSDNotationStreamer(params)))); - } - - mPostend = params.postend; - - apr_pool_create(&mPool, gAPRPoolp); - if (!mPool) - { - LLTHROW(LLProcessError(STRINGIZE("failed to create apr pool"))); - } - - apr_procattr_t *procattr = NULL; - chkapr(apr_procattr_create(&procattr, mPool)); - - // IQA-490, CHOP-900: On Windows, ask APR to jump through hoops to - // constrain the set of handles passed to the child process. Before we - // changed to APR, the Windows implementation of LLProcessLauncher called - // CreateProcess(bInheritHandles=false), meaning to pass NO open handles - // to the child process. Now that we support pipes, though, we must allow - // apr_proc_create() to pass bInheritHandles=true. But without taking - // special pains, that causes trouble in a number of ways, due to the fact - // that the viewer is constantly opening and closing files -- most of - // which CreateProcess() passes to every child process! -#if ! defined(APR_HAS_PROCATTR_CONSTRAIN_HANDLE_SET) - // Our special preprocessor symbol isn't even defined -- wrong APR - LL_WARNS("LLProcess") << "This version of APR lacks Linden " - << "apr_procattr_constrain_handle_set() extension" << LL_ENDL; -#else - chkapr(apr_procattr_constrain_handle_set(procattr, 1)); -#endif - - // For which of stdin, stdout, stderr should we create a pipe to the - // child? In the viewer, there are only a couple viable - // apr_procattr_io_set() alternatives: inherit the viewer's own stdxxx - // handle (APR_NO_PIPE, e.g. for stdout, stderr), or create a pipe that's - // blocking on the child end but nonblocking at the viewer end - // (APR_CHILD_BLOCK). - // Other major options could include explicitly creating a single APR pipe - // and passing it as both stdout and stderr (apr_procattr_child_out_set(), - // apr_procattr_child_err_set()), or accepting a filename, opening it and - // passing that apr_file_t (simple <, >, 2> redirect emulation). - std::vector<apr_int32_t> select; - for (const FileParam& fparam : params.files) - { - // Every iteration, we're going to append an item to 'select'. At the - // top of the loop, its size() is, in effect, an index. Use that to - // pick a string description for messages. - std::string which(whichfile(FILESLOT(select.size()))); - if (fparam.type().empty()) // inherit our file descriptor - { - select.push_back(APR_NO_PIPE); - } - else if (fparam.type() == "pipe") // anonymous pipe - { - if (! fparam.name().empty()) - { - LL_WARNS("LLProcess") << "For " << params.executable() - << ": internal names for reusing pipes ('" - << fparam.name() << "' for " << which - << ") are not yet supported -- creating distinct pipe" - << LL_ENDL; - } - // The viewer can't block for anything: the parent end MUST be - // nonblocking. As the APR documentation itself points out, it - // makes very little sense to set nonblocking I/O for the child - // end of a pipe: only a specially-written child could deal with - // that. - select.push_back(APR_CHILD_BLOCK); - } - else - { - LLTHROW(LLProcessError(STRINGIZE("For " << params.executable() - << ": unsupported FileParam for " << which - << ": type='" << fparam.type() - << "', name='" << fparam.name() << "'"))); - } - } - // By default, pass APR_NO_PIPE for unspecified slots. - while (select.size() < NSLOTS) - { - select.push_back(APR_NO_PIPE); - } - chkapr(apr_procattr_io_set(procattr, select[STDIN], select[STDOUT], select[STDERR])); - - // Thumbs down on implicitly invoking the shell to invoke the child. From - // our point of view, the other major alternative to APR_PROGRAM_PATH - // would be APR_PROGRAM_ENV: still copy environment, but require full - // executable pathname. I don't see a downside to searching the PATH, - // though: if our caller wants (e.g.) a specific Python interpreter, s/he - // can still pass the full pathname. - chkapr(apr_procattr_cmdtype_set(procattr, APR_PROGRAM_PATH)); - // YES, do extra work if necessary to report child exec() failures back to - // parent process. - chkapr(apr_procattr_error_check_set(procattr, 1)); - // Do not start a non-autokill child in detached state. On Posix - // platforms, this setting attempts to daemonize the new child, closing - // std handles and the like, and that's a bit more detachment than we - // want. autokill=false just means not to implicitly kill the child when - // the parent terminates! -// chkapr(apr_procattr_detach_set(procattr, mAutokill? 0 : 1)); - - if (mAutokill) - { -#if ! defined(APR_HAS_PROCATTR_AUTOKILL_SET) - // Our special preprocessor symbol isn't even defined -- wrong APR - LL_WARNS("LLProcess") << "This version of APR lacks Linden apr_procattr_autokill_set() extension" << LL_ENDL; -#elif ! APR_HAS_PROCATTR_AUTOKILL_SET - // Symbol is defined, but to 0: expect apr_procattr_autokill_set() to - // return APR_ENOTIMPL. -#else // APR_HAS_PROCATTR_AUTOKILL_SET nonzero - ll_apr_warn_status(apr_procattr_autokill_set(procattr, 1)); -#endif - } - - // In preparation for calling apr_proc_create(), we collect a number of - // const char* pointers obtained from std::string::c_str(). Turns out - // LLInitParam::Block's helpers Optional, Mandatory, Multiple et al. - // guarantee that converting to the wrapped type (std::string in our - // case), e.g. by calling operator(), returns a reference to *the same - // instance* of the wrapped type that's stored in our Block subclass. - // That's important! We know 'params' persists throughout this method - // call; but without that guarantee, we'd have to assume that converting - // one of its members to std::string might return a different (temp) - // instance. Capturing the c_str() from a temporary std::string is Bad Bad - // Bad. But armed with this knowledge, when you see params.cwd().c_str(), - // grit your teeth and smile and carry on. - - if (params.cwd.isProvided()) - { - chkapr(apr_procattr_dir_set(procattr, params.cwd().c_str())); - } - - // create an argv vector for the child process - std::vector<const char*> argv; - - // Add the executable path. See above remarks about c_str(). - argv.push_back(params.executable().c_str()); - - // Add arguments. See above remarks about c_str(). - for (const std::string& arg : params.args) - { - argv.push_back(arg.c_str()); - } - - // terminate with a null pointer - argv.push_back(NULL); - - // Launch! The NULL would be the environment block, if we were passing - // one. Hand-expand chkapr() macro so we can fill in the actual command - // string instead of the variable names. - if (ll_apr_warn_status(apr_proc_create(&mProcess, argv[0], &argv[0], NULL, procattr, - mPool))) - { - LLTHROW(LLProcessError(STRINGIZE(params << " failed"))); - } - - // arrange to call status_callback() - apr_proc_other_child_register(&mProcess, &LLProcess::status_callback, this, mProcess.in, - mPool); - // and make sure we poll it once per "mainloop" tick - sProcessListener.addPoll(*this); - mStatus.mState = RUNNING; - - mDesc = STRINGIZE(getDesc(params) << " (" << mProcess.pid << ')'); - LL_INFOS("LLProcess") << mDesc << ": launched " << params << LL_ENDL; - - // Unless caller explicitly turned off autokill (child should persist), - // take steps to terminate the child. This is all suspenders-and-belt: in - // theory our destructor should kill an autokill child, but in practice - // that doesn't always work (e.g. VWR-21538). - if (mAutokill) - { -/*==========================================================================*| - // NO: There may be an APR bug, not sure -- but at least on Mac, when - // gAPRPoolp is destroyed, OUR process receives SIGTERM! Apparently - // either our own PID is getting into the list of processes to kill() - // (unlikely), or somehow one of those PIDs is getting zeroed first, - // so that kill() sends SIGTERM to the whole process group -- this - // process included. I'd have to build and link with a debug version - // of APR to know for sure. It's too bad: this mechanism would be just - // right for dealing with static autokill LLProcessPtr variables, - // which aren't destroyed until after APR is no longer available. - - // Tie the lifespan of this child process to the lifespan of our APR - // pool: on destruction of the pool, forcibly kill the process. Tell - // APR to try SIGTERM and suspend 3 seconds. If that didn't work, use - // SIGKILL. - apr_pool_note_subprocess(gAPRPoolp, &mProcess, APR_KILL_AFTER_TIMEOUT); -|*==========================================================================*/ - - // On Windows, associate the new child process with our Job Object. - autokill(); - } - - // Instantiate the proper pipe I/O machinery - // want to be able to point to apr_proc_t::in, out, err by index - typedef apr_file_t* apr_proc_t::*apr_proc_file_ptr; - static apr_proc_file_ptr members[] = - { &apr_proc_t::in, &apr_proc_t::out, &apr_proc_t::err }; - for (size_t i = 0; i < NSLOTS; ++i) - { - if (select[i] != APR_CHILD_BLOCK) - continue; - std::string desc(STRINGIZE(mDesc << ' ' << whichfile(FILESLOT(i)))); - apr_file_t* pipe(mProcess.*(members[i])); - if (i == STDIN) - { - mPipes.replace(i, new WritePipeImpl(desc, pipe)); - } - else - { - mPipes.replace(i, new ReadPipeImpl(desc, pipe, FILESLOT(i))); - } - // Removed temporaily for Xcode 7 build tests: error was: - // "error: expression with side effects will be evaluated despite - // being used as an operand to 'typeid' [-Werror,-Wpotentially-evaluated-expression]"" - //LL_DEBUGS("LLProcess") << "Instantiating " << typeid(mPipes[i]).name() - // << "('" << desc << "')" << LL_ENDL; - } -} - -// Helper to obtain a description string, given a Params block -static std::string getDesc(const LLProcess::Params& params) -{ - // If caller specified a description string, by all means use it. - if (params.desc.isProvided()) - return params.desc; - - // Caller didn't say. Use the executable name -- but use just the filename - // part. On Mac, for instance, full pathnames get cumbersome. - return LLProcess::basename(params.executable); -} - -//static -std::string LLProcess::basename(const std::string& path) -{ - // If there are Linden utility functions to manipulate pathnames, I - // haven't found them -- and for this usage, Boost.Filesystem seems kind - // of heavyweight. - std::string::size_type delim = path.find_last_of("\\/"); - // If path contains no pathname delimiters, return the whole thing. - if (delim == std::string::npos) - return path; - - // Return just the part beyond the last delimiter. - return path.substr(delim + 1); -} - -LLProcess::~LLProcess() -{ - // In the Linden viewer, there's at least one static LLProcessPtr. Its - // destructor will be called *after* ll_cleanup_apr(). In such a case, - // unregistering is pointless (and fatal!) -- and kill(), which also - // relies on APR, is impossible. - if (! gAPRPoolp) - return; - - // Only in state RUNNING are we registered for callback. In UNSTARTED we - // haven't yet registered. And since receiving the callback is the only - // way we detect child termination, we only change from state RUNNING at - // the same time we unregister. - if (mStatus.mState == RUNNING) - { - // We're still registered for a callback: unregister. Do it before - // we even issue the kill(): even if kill() somehow prompted an - // instantaneous callback (unlikely), this object is going away! Any - // information updated in this object by such a callback is no longer - // available to any consumer anyway. - apr_proc_other_child_unregister(this); - // One less LLProcess to poll for - sProcessListener.dropPoll(*this); - } - - if (mAttached) - { - kill("destructor"); - } - - if (mPool) - { - apr_pool_destroy(mPool); - mPool = NULL; - } -} - -bool LLProcess::kill(const std::string& who) -{ - if (isRunning()) - { - LL_INFOS("LLProcess") << who << " killing " << mDesc << LL_ENDL; - -#if LL_WINDOWS - int sig = -1; -#else // Posix - int sig = SIGTERM; -#endif - - ll_apr_warn_status(apr_proc_kill(&mProcess, sig)); - } - - return ! isRunning(); -} - -//static -bool LLProcess::kill(const LLProcessPtr& p, const std::string& who) -{ - if (! p) - return true; // process dead! (was never running) - return p->kill(who); -} - -bool LLProcess::isRunning() const -{ - return getStatus().mState == RUNNING; -} - -//static -bool LLProcess::isRunning(const LLProcessPtr& p) -{ - if (! p) - return false; - return p->isRunning(); -} - -LLProcess::Status LLProcess::getStatus() const -{ - return mStatus; -} - -//static -LLProcess::Status LLProcess::getStatus(const LLProcessPtr& p) -{ - if (! p) - { - // default-constructed Status has mState == UNSTARTED - return Status(); - } - return p->getStatus(); -} - -std::string LLProcess::getStatusString() const -{ - return getStatusString(getStatus()); -} - -std::string LLProcess::getStatusString(const Status& status) const -{ - return getStatusString(mDesc, status); -} - -//static -std::string LLProcess::getStatusString(const std::string& desc, const LLProcessPtr& p) -{ - if (! p) - { - // default-constructed Status has mState == UNSTARTED - return getStatusString(desc, Status()); - } - return desc + " " + p->getStatusString(); -} - -//static -std::string LLProcess::getStatusString(const std::string& desc, const Status& status) -{ - if (status.mState == UNSTARTED) - return desc + " was never launched"; - - if (status.mState == RUNNING) - return desc + " running"; - - if (status.mState == EXITED) - return STRINGIZE(desc << " exited with code " << status.mData); - - if (status.mState == KILLED) -#if LL_WINDOWS - return STRINGIZE(desc << " killed with exception " << std::hex << status.mData); -#else - return STRINGIZE(desc << " killed by signal " << status.mData - << " (" << apr_signal_description_get(status.mData) << ")"); -#endif - - return STRINGIZE(desc << " in unknown state " << status.mState << " (" << status.mData << ")"); -} - -// Classic-C-style APR callback -void LLProcess::status_callback(int reason, void* data, int status) -{ - // Our only role is to bounce this static method call back into object - // space. - static_cast<LLProcess*>(data)->handle_status(reason, status); -} - -#define tabent(symbol) { symbol, #symbol } -static struct ReasonCode -{ - int code; - const char* name; -} reasons[] = -{ - tabent(APR_OC_REASON_DEATH), - tabent(APR_OC_REASON_UNWRITABLE), - tabent(APR_OC_REASON_RESTART), - tabent(APR_OC_REASON_UNREGISTER), - tabent(APR_OC_REASON_LOST), - tabent(APR_OC_REASON_RUNNING) -}; -#undef tabent - -// Object-oriented callback -void LLProcess::handle_status(int reason, int status) -{ - { - // This odd appearance of LL_DEBUGS is just to bracket a lookup that will - // only be performed if in fact we're going to produce the log message. - LL_DEBUGS("LLProcess") << empty; - std::string reason_str; - for (const ReasonCode& rcp : reasons) - { - if (reason == rcp.code) - { - reason_str = rcp.name; - break; - } - } - if (reason_str.empty()) - { - reason_str = STRINGIZE("unknown reason " << reason); - } - LL_CONT << mDesc << ": handle_status(" << reason_str << ", " << status << ")" << LL_ENDL; - } - - if (! (reason == APR_OC_REASON_DEATH || reason == APR_OC_REASON_LOST)) - { - // We're only interested in the call when the child terminates. - return; - } - - // Somewhat oddly, APR requires that you explicitly unregister even when - // it already knows the child has terminated. We must pass the same 'data' - // pointer as for the register() call, which was our 'this'. - apr_proc_other_child_unregister(this); - // don't keep polling for a terminated process - sProcessListener.dropPoll(*this); - // We overload mStatus.mState to indicate whether the child is registered - // for APR callback: only RUNNING means registered. Track that we've - // unregistered. We know the child has terminated; might be EXITED or - // KILLED; refine below. - mStatus.mState = EXITED; - - // Make last-gasp calls for each of the ReadPipes we have on hand. Since - // they're listening on "mainloop", we can be sure they'll eventually - // collect all pending data from the child. But we want to be able to - // guarantee to our consumer that by the time we post on the "postend" - // LLEventPump, our ReadPipes are already buffering all the data there - // will ever be from the child. That lets the "postend" listener decide - // what to do with that final data. - for (size_t i = 0; i < mPipes.size(); ++i) - { - std::string error; - ReadPipeImpl* ppipe = getPipePtr<ReadPipeImpl>(error, FILESLOT(i)); - if (ppipe) - { - static LLSD trivial; - ppipe->tick(trivial); - } - } - -// wi->rv = apr_proc_wait(wi->child, &wi->rc, &wi->why, APR_NOWAIT); - // It's just wrong to call apr_proc_wait() here. The only way APR knows to - // call us with APR_OC_REASON_DEATH is that it's already reaped this child - // process, so calling wait() will only produce "huh?" from the OS. We - // must rely on the status param passed in, which unfortunately comes - // straight from the OS wait() call, which means we have to decode it by - // hand. - mStatus = interpret_status(status); - LL_INFOS("LLProcess") << getStatusString() << LL_ENDL; - - // If caller requested notification on child termination, send it. - if (! mPostend.empty()) - { - LLEventPumps::instance().obtain(mPostend) - .post(LLSDMap - ("id", getProcessID()) - ("desc", mDesc) - ("state", mStatus.mState) - ("data", mStatus.mData) - ("string", getStatusString()) - ); - } -} - -LLProcess::id LLProcess::getProcessID() const -{ - return mProcess.pid; -} - -LLProcess::handle LLProcess::getProcessHandle() const -{ -#if LL_WINDOWS - return mProcess.hproc; -#else - return mProcess.pid; -#endif -} - -std::string LLProcess::getPipeName(FILESLOT) const -{ - // LLProcess::FileParam::type "npipe" is not yet implemented - return ""; -} - -template<class PIPETYPE> -PIPETYPE* LLProcess::getPipePtr(std::string& error, FILESLOT slot) -{ - if (slot >= NSLOTS) - { - error = STRINGIZE(mDesc << " has no slot " << slot); - return NULL; - } - if (mPipes.is_null(slot)) - { - error = STRINGIZE(mDesc << ' ' << whichfile(slot) << " not a monitored pipe"); - return NULL; - } - // Make sure we dynamic_cast in pointer domain so we can test, rather than - // accepting runtime's exception. - PIPETYPE* ppipe = dynamic_cast<PIPETYPE*>(&mPipes[slot]); - if (! ppipe) - { - error = STRINGIZE(mDesc << ' ' << whichfile(slot) << " not a " << typeid(PIPETYPE).name()); - return NULL; - } - - error.clear(); - return ppipe; -} - -template <class PIPETYPE> -PIPETYPE& LLProcess::getPipe(FILESLOT slot) -{ - std::string error; - PIPETYPE* wp = getPipePtr<PIPETYPE>(error, slot); - if (! wp) - { - LLTHROW(NoPipe(error)); - } - return *wp; -} - -template <class PIPETYPE> -boost::optional<PIPETYPE&> LLProcess::getOptPipe(FILESLOT slot) -{ - std::string error; - PIPETYPE* wp = getPipePtr<PIPETYPE>(error, slot); - if (! wp) - { - LL_DEBUGS("LLProcess") << error << LL_ENDL; - return boost::optional<PIPETYPE&>(); - } - return *wp; -} - -LLProcess::WritePipe& LLProcess::getWritePipe(FILESLOT slot) -{ - return getPipe<WritePipe>(slot); -} - -boost::optional<LLProcess::WritePipe&> LLProcess::getOptWritePipe(FILESLOT slot) -{ - return getOptPipe<WritePipe>(slot); -} - -LLProcess::ReadPipe& LLProcess::getReadPipe(FILESLOT slot) -{ - return getPipe<ReadPipe>(slot); -} - -boost::optional<LLProcess::ReadPipe&> LLProcess::getOptReadPipe(FILESLOT slot) -{ - return getOptPipe<ReadPipe>(slot); -} - -//static -std::string LLProcess::getline(std::istream& in) -{ - std::string line; - std::getline(in, line); - // Blur the distinction between "\r\n" and plain "\n". std::getline() will - // have eaten the "\n", but we could still end up with a trailing "\r". - std::string::size_type lastpos = line.find_last_not_of("\r"); - if (lastpos != std::string::npos) - { - // Found at least one character that's not a trailing '\r'. SKIP OVER - // IT and erase the rest of the line. - line.erase(lastpos+1); - } - return line; -} - -std::ostream& operator<<(std::ostream& out, const LLProcess::Params& params) -{ - if (params.cwd.isProvided()) - { - out << "cd " << LLStringUtil::quote(params.cwd) << ": "; - } - out << LLStringUtil::quote(params.executable); - for (const std::string& arg : params.args) - { - out << ' ' << LLStringUtil::quote(arg); - } - return out; -} - -/***************************************************************************** -* Windows specific -*****************************************************************************/ -#if LL_WINDOWS - -static std::string WindowsErrorString(const std::string& operation); - -void LLProcess::autokill() -{ - // hopefully now handled by apr_procattr_autokill_set() -} - -LLProcess::handle LLProcess::isRunning(handle h, const std::string& desc) -{ - // This direct Windows implementation is because we have no access to the - // apr_proc_t struct: we expect it's been destroyed. - if (! h) - return 0; - - DWORD waitresult = WaitForSingleObject(h, 0); - if(waitresult == WAIT_OBJECT_0) - { - // the process has completed. - if (! desc.empty()) - { - DWORD status = 0; - if (! GetExitCodeProcess(h, &status)) - { - LL_WARNS("LLProcess") << desc << " terminated, but " - << WindowsErrorString("GetExitCodeProcess()") << LL_ENDL; - } - { - LL_INFOS("LLProcess") << getStatusString(desc, interpret_status(status)) - << LL_ENDL; - } - } - CloseHandle(h); - return 0; - } - - return h; -} - -static LLProcess::Status interpret_status(int status) -{ - LLProcess::Status result; - - // This bit of code is cribbed from apr/threadproc/win32/proc.c, a - // function (unfortunately static) called why_from_exit_code(): - /* See WinNT.h STATUS_ACCESS_VIOLATION and family for how - * this class of failures was determined - */ - if ((status & 0xFFFF0000) == 0xC0000000) - { - result.mState = LLProcess::KILLED; - } - else - { - result.mState = LLProcess::EXITED; - } - result.mData = status; - - return result; -} - -/// GetLastError()/FormatMessage() boilerplate -static std::string WindowsErrorString(const std::string& operation) -{ - auto result = GetLastError(); - return STRINGIZE(operation << " failed (" << result << "): " - << windows_message<std::string>(result)); -} - -/***************************************************************************** -* Posix specific -*****************************************************************************/ -#else // Mac and linux - -#include <signal.h> -#include <fcntl.h> -#include <errno.h> -#include <sys/wait.h> - -void LLProcess::autokill() -{ - // What we ought to do here is to: - // 1. create a unique process group and run all autokill children in that - // group (see https://jira.secondlife.com/browse/SWAT-563); - // 2. figure out a way to intercept control when the viewer exits -- - // gracefully or not; - // 3. when the viewer exits, kill off the aforementioned process group. - - // It's point 2 that's troublesome. Although I've seen some signal- - // handling logic in the Posix viewer code, I haven't yet found any bit of - // code that's run no matter how the viewer exits (a try/finally for the - // whole process, as it were). -} - -// Attempt to reap a process ID -- returns true if the process has exited and been reaped, false otherwise. -static bool reap_pid(pid_t pid, LLProcess::Status* pstatus=NULL) -{ - LLProcess::Status dummy; - if (! pstatus) - { - // If caller doesn't want to see Status, give us a target anyway so we - // don't have to have a bunch of conditionals. - pstatus = &dummy; - } - - int status = 0; - pid_t wait_result = ::waitpid(pid, &status, WNOHANG); - if (wait_result == pid) - { - *pstatus = interpret_status(status); - return true; - } - if (wait_result == 0) - { - pstatus->mState = LLProcess::RUNNING; - pstatus->mData = 0; - return false; - } - - // Clear caller's Status block; caller must interpret UNSTARTED to mean - // "if this PID was ever valid, it no longer is." - *pstatus = LLProcess::Status(); - - // We've dealt with the success cases: we were able to reap the child - // (wait_result == pid) or it's still running (wait_result == 0). It may - // be that the child terminated but didn't hang around long enough for us - // to reap. In that case we still have no Status to report, but we can at - // least state that it's not running. - if (wait_result == -1 && errno == ECHILD) - { - // No such process -- this may mean we're ignoring SIGCHILD. - return true; - } - - // Uh, should never happen?! - LL_WARNS("LLProcess") << "LLProcess::reap_pid(): waitpid(" << pid << ") returned " - << wait_result << "; not meaningful?" << LL_ENDL; - // If caller is looping until this pid terminates, and if we can't find - // out, better to break the loop than to claim it's still running. - return true; -} - -LLProcess::id LLProcess::isRunning(id pid, const std::string& desc) -{ - // This direct Posix implementation is because we have no access to the - // apr_proc_t struct: we expect it's been destroyed. - if (! pid) - return 0; - - // Check whether the process has exited, and reap it if it has. - LLProcess::Status status; - if(reap_pid(pid, &status)) - { - // the process has exited. - if (! desc.empty()) - { - std::string statstr(desc + " apparently terminated: no status available"); - // We don't just pass UNSTARTED to getStatusString() because, in - // the context of reap_pid(), that state has special meaning. - if (status.mState != UNSTARTED) - { - statstr = getStatusString(desc, status); - } - LL_INFOS("LLProcess") << statstr << LL_ENDL; - } - return 0; - } - - return pid; -} - -static LLProcess::Status interpret_status(int status) -{ - LLProcess::Status result; - - if (WIFEXITED(status)) - { - result.mState = LLProcess::EXITED; - result.mData = WEXITSTATUS(status); - } - else if (WIFSIGNALED(status)) - { - result.mState = LLProcess::KILLED; - result.mData = WTERMSIG(status); - } - else // uh, shouldn't happen? - { - result.mState = LLProcess::EXITED; - result.mData = status; // someone else will have to decode - } - - return result; -} - -#endif // Posix +/**
+ * @file llprocess.cpp
+ * @brief Utility class for launching, terminating, and tracking the state of processes.
+ *
+ * $LicenseInfo:firstyear=2008&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+#include "llprocess.h"
+#include "llsdutil.h"
+#include "llsdserialize.h"
+#include "llsingleton.h"
+#include "llstring.h"
+#include "stringize.h"
+#include "llapr.h"
+#include "apr_signal.h"
+#include "llevents.h"
+#include "llexception.h"
+
+#include <boost/bind.hpp>
+#include <boost/asio/streambuf.hpp>
+#include <boost/asio/buffers_iterator.hpp>
+#include <iostream>
+#include <stdexcept>
+#include <limits>
+#include <algorithm>
+#include <vector>
+#include <typeinfo>
+#include <utility>
+
+/*****************************************************************************
+* Helpers
+*****************************************************************************/
+static const char* whichfile_[] = { "stdin", "stdout", "stderr" };
+static std::string empty;
+static LLProcess::Status interpret_status(int status);
+static std::string getDesc(const LLProcess::Params& params);
+
+static std::string whichfile(LLProcess::FILESLOT index)
+{
+ if (index < LL_ARRAY_SIZE(whichfile_))
+ return whichfile_[index];
+ return STRINGIZE("file slot " << index);
+}
+
+/**
+ * Ref-counted "mainloop" listener. As long as there are still outstanding
+ * LLProcess objects, keep listening on "mainloop" so we can keep polling APR
+ * for process status.
+ */
+class LLProcessListener
+{
+ LOG_CLASS(LLProcessListener);
+public:
+ LLProcessListener():
+ mCount(0)
+ {}
+
+ void addPoll(const LLProcess&)
+ {
+ // Unconditionally increment mCount. If it was zero before
+ // incrementing, listen on "mainloop".
+ if (mCount++ == 0)
+ {
+ LL_DEBUGS("LLProcess") << "listening on \"mainloop\"" << LL_ENDL;
+ mConnection = LLEventPumps::instance().obtain("mainloop")
+ .listen("LLProcessListener", boost::bind(&LLProcessListener::tick, this, _1));
+ }
+ }
+
+ void dropPoll(const LLProcess&)
+ {
+ // Unconditionally decrement mCount. If it's zero after decrementing,
+ // stop listening on "mainloop".
+ if (--mCount == 0)
+ {
+ LL_DEBUGS("LLProcess") << "disconnecting from \"mainloop\"" << LL_ENDL;
+ mConnection.disconnect();
+ }
+ }
+
+private:
+ /// called once per frame by the "mainloop" LLEventPump
+ bool tick(const LLSD&)
+ {
+ // Tell APR to sense whether each registered LLProcess is still
+ // running and call handle_status() appropriately. We should be able
+ // to get the same info from an apr_proc_wait(APR_NOWAIT) call; but at
+ // least in APR 1.4.2, testing suggests that even with APR_NOWAIT,
+ // apr_proc_wait() blocks the caller. We can't have that in the
+ // viewer. Hence the callback rigmarole. (Once we update APR, it's
+ // probably worth testing again.) Also -- although there's an
+ // apr_proc_other_child_refresh() call, i.e. get that information for
+ // one specific child, it accepts an 'apr_other_child_rec_t*' that's
+ // mentioned NOWHERE else in the documentation or header files! I
+ // would use the specific call in LLProcess::getStatus() if I knew
+ // how. As it is, each call to apr_proc_other_child_refresh_all() will
+ // call callbacks for ALL still-running child processes. That's why we
+ // centralize such calls, using "mainloop" to ensure it happens once
+ // per frame, and refcounting running LLProcess objects to remain
+ // registered only while needed.
+ LL_DEBUGS("LLProcess") << "calling apr_proc_other_child_refresh_all()" << LL_ENDL;
+ apr_proc_other_child_refresh_all(APR_OC_REASON_RUNNING);
+ return false;
+ }
+
+ /// If this object is destroyed before mCount goes to zero, stop
+ /// listening on "mainloop" anyway.
+ LLTempBoundListener mConnection;
+ unsigned mCount;
+};
+static LLProcessListener sProcessListener;
+
+/*****************************************************************************
+* WritePipe and ReadPipe
+*****************************************************************************/
+LLProcess::BasePipe::~BasePipe() {}
+const LLProcess::BasePipe::size_type
+ // use funky syntax to call max() to avoid blighted max() macros
+ LLProcess::BasePipe::npos((std::numeric_limits<LLProcess::BasePipe::size_type>::max)());
+
+class WritePipeImpl: public LLProcess::WritePipe
+{
+ LOG_CLASS(WritePipeImpl);
+public:
+ WritePipeImpl(const std::string& desc, apr_file_t* pipe):
+ mDesc(desc),
+ mPipe(pipe),
+ // Essential to initialize our std::ostream with our special streambuf!
+ mStream(&mStreambuf)
+ {
+ mConnection = LLEventPumps::instance().obtain("mainloop")
+ .listen(LLEventPump::inventName("WritePipe"),
+ boost::bind(&WritePipeImpl::tick, this, _1));
+
+#if ! LL_WINDOWS
+ // We can't count on every child process reading everything we try to
+ // write to it. And if the child terminates with WritePipe data still
+ // pending, unless we explicitly suppress it, Posix will hit us with
+ // SIGPIPE. That would terminate the viewer, boom. "Ignoring" it means
+ // APR gets the correct errno, passes it back to us, we log it, etc.
+ signal(SIGPIPE, SIG_IGN);
+#endif
+ }
+
+ virtual std::ostream& get_ostream() { return mStream; }
+ virtual size_type size() const { return mStreambuf.size(); }
+
+ bool tick(const LLSD&)
+ {
+ typedef boost::asio::streambuf::const_buffers_type const_buffer_sequence;
+ // If there's anything to send, try to send it.
+ std::size_t total(mStreambuf.size()), consumed(0);
+ if (total)
+ {
+ const_buffer_sequence bufs = mStreambuf.data();
+ // In general, our streambuf might contain a number of different
+ // physical buffers; iterate over those.
+ bool keepwriting = true;
+ for (const_buffer_sequence::const_iterator bufi(bufs.begin()), bufend(bufs.end());
+ bufi != bufend && keepwriting; ++bufi)
+ {
+ // http://www.boost.org/doc/libs/1_49_0_beta1/doc/html/boost_asio/reference/buffer.html#boost_asio.reference.buffer.accessing_buffer_contents
+ // Although apr_file_write() accepts const void*, we
+ // manipulate const char* so we can increment the pointer.
+ const char* remainptr = boost::asio::buffer_cast<const char*>(*bufi);
+ std::size_t remainlen = boost::asio::buffer_size(*bufi);
+ while (remainlen)
+ {
+ // Tackle the current buffer in discrete chunks. On
+ // Windows, we've observed strange failures when trying to
+ // write big lengths (~1 MB) in a single operation. Even a
+ // 32K chunk seems too large. At some point along the way
+ // apr_file_write() returns 11 (Resource temporarily
+ // unavailable, i.e. EAGAIN) and says it wrote 0 bytes --
+ // even though it did write the chunk! Our next write
+ // attempt retries with the same chunk, resulting in the
+ // chunk being duplicated at the child end. Using smaller
+ // chunks is empirically more reliable.
+ std::size_t towrite((std::min)(remainlen, std::size_t(4*1024)));
+ apr_size_t written(towrite);
+ apr_status_t err = apr_file_write(mPipe, remainptr, &written);
+ // EAGAIN is exactly what we want from a nonblocking pipe.
+ // Rather than waiting for data, it should return immediately.
+ if (! (err == APR_SUCCESS || APR_STATUS_IS_EAGAIN(err)))
+ {
+ LL_WARNS("LLProcess") << "apr_file_write(" << towrite << ") on " << mDesc
+ << " got " << err << ":" << LL_ENDL;
+ ll_apr_warn_status(err);
+ }
+
+ // 'written' is modified to reflect the number of bytes actually
+ // written. Make sure we consume those later. (Don't consume them
+ // now, that would invalidate the buffer iterator sequence!)
+ consumed += written;
+ // don't forget to advance to next chunk of current buffer
+ remainptr += written;
+ remainlen -= written;
+
+ char msgbuf[512];
+ LL_DEBUGS("LLProcess") << "wrote " << written << " of " << towrite
+ << " bytes to " << mDesc
+ << " (original " << total << "),"
+ << " code " << err << ": "
+ << apr_strerror(err, msgbuf, sizeof(msgbuf))
+ << LL_ENDL;
+
+ // The parent end of this pipe is nonblocking. If we weren't able
+ // to write everything we wanted, don't keep banging on it -- that
+ // won't change until the child reads some. Wait for next tick().
+ if (written < towrite)
+ {
+ keepwriting = false; // break outer loop over buffers too
+ break;
+ }
+ } // next chunk of current buffer
+ } // next buffer
+ // In all, we managed to write 'consumed' bytes. Remove them from the
+ // streambuf so we don't keep trying to send them. This could be
+ // anywhere from 0 up to mStreambuf.size(); anything we haven't yet
+ // sent, we'll try again later.
+ mStreambuf.consume(consumed);
+ }
+
+ return false;
+ }
+
+private:
+ std::string mDesc;
+ apr_file_t* mPipe;
+ LLTempBoundListener mConnection;
+ boost::asio::streambuf mStreambuf;
+ std::ostream mStream;
+};
+
+class ReadPipeImpl: public LLProcess::ReadPipe
+{
+ LOG_CLASS(ReadPipeImpl);
+public:
+ ReadPipeImpl(const std::string& desc, apr_file_t* pipe, LLProcess::FILESLOT index):
+ mDesc(desc),
+ mPipe(pipe),
+ mIndex(index),
+ // Essential to initialize our std::istream with our special streambuf!
+ mStream(&mStreambuf),
+ mPump("ReadPipe", true), // tweak name as needed to avoid collisions
+ mLimit(0),
+ mEOF(false)
+ {
+ mConnection = LLEventPumps::instance().obtain("mainloop")
+ .listen(LLEventPump::inventName("ReadPipe"),
+ boost::bind(&ReadPipeImpl::tick, this, _1));
+ }
+
+ ~ReadPipeImpl()
+ {
+ if (mConnection.connected())
+ {
+ mConnection.disconnect();
+ }
+ }
+
+ // Much of the implementation is simply connecting the abstract virtual
+ // methods with implementation data concealed from the base class.
+ virtual std::istream& get_istream() { return mStream; }
+ virtual std::string getline() { return LLProcess::getline(mStream); }
+ virtual LLEventPump& getPump() { return mPump; }
+ virtual void setLimit(size_type limit) { mLimit = limit; }
+ virtual size_type getLimit() const { return mLimit; }
+ virtual size_type size() const { return mStreambuf.size(); }
+
+ virtual std::string read(size_type len)
+ {
+ // Read specified number of bytes into a buffer.
+ size_type readlen((std::min)(size(), len));
+ // Formally, &buffer[0] is invalid for a vector of size() 0. Exit
+ // early in that situation.
+ if (! readlen)
+ return "";
+ // Make a buffer big enough.
+ std::vector<char> buffer(readlen);
+ mStream.read(&buffer[0], readlen);
+ // Since we've already clamped 'readlen', we can think of no reason
+ // why mStream.read() should read fewer than 'readlen' bytes.
+ // Nonetheless, use the actual retrieved length.
+ return std::string(&buffer[0], mStream.gcount());
+ }
+
+ virtual std::string peek(size_type offset=0, size_type len=npos) const
+ {
+ // Constrain caller's offset and len to overlap actual buffer content.
+ std::size_t real_offset = (std::min)(mStreambuf.size(), std::size_t(offset));
+ size_type want_end = (len == npos)? npos : (real_offset + len);
+ std::size_t real_end = (std::min)(mStreambuf.size(), std::size_t(want_end));
+ boost::asio::streambuf::const_buffers_type cbufs = mStreambuf.data();
+ return std::string(boost::asio::buffers_begin(cbufs) + real_offset,
+ boost::asio::buffers_begin(cbufs) + real_end);
+ }
+
+ virtual size_type find(const std::string& seek, size_type offset=0) const
+ {
+ // If we're passing a string of length 1, use find(char), which can
+ // use an O(n) std::find() rather than the O(n^2) std::search().
+ if (seek.length() == 1)
+ {
+ return find(seek[0], offset);
+ }
+
+ // If offset is beyond the whole buffer, can't even construct a valid
+ // iterator range; can't possibly find the string we seek.
+ if (offset > mStreambuf.size())
+ {
+ return npos;
+ }
+
+ boost::asio::streambuf::const_buffers_type cbufs = mStreambuf.data();
+ boost::asio::buffers_iterator<boost::asio::streambuf::const_buffers_type>
+ begin(boost::asio::buffers_begin(cbufs)),
+ end (boost::asio::buffers_end(cbufs)),
+ found(std::search(begin + offset, end, seek.begin(), seek.end()));
+ return (found == end)? npos : (found - begin);
+ }
+
+ virtual size_type find(char seek, size_type offset=0) const
+ {
+ // If offset is beyond the whole buffer, can't even construct a valid
+ // iterator range; can't possibly find the char we seek.
+ if (offset > mStreambuf.size())
+ {
+ return npos;
+ }
+
+ boost::asio::streambuf::const_buffers_type cbufs = mStreambuf.data();
+ boost::asio::buffers_iterator<boost::asio::streambuf::const_buffers_type>
+ begin(boost::asio::buffers_begin(cbufs)),
+ end (boost::asio::buffers_end(cbufs)),
+ found(std::find(begin + offset, end, seek));
+ return (found == end)? npos : (found - begin);
+ }
+
+ bool tick(const LLSD&)
+ {
+ // Once we've hit EOF, skip all the rest of this.
+ if (mEOF)
+ return false;
+
+ typedef boost::asio::streambuf::mutable_buffers_type mutable_buffer_sequence;
+ // Try, every time, to read into our streambuf. In fact, we have no
+ // idea how much data the child might be trying to send: keep trying
+ // until we're convinced we've temporarily exhausted the pipe.
+ enum PipeState { RETRY, EXHAUSTED, CLOSED };
+ PipeState state = RETRY;
+ std::size_t committed(0);
+ do
+ {
+ // attempt to read an arbitrary size
+ mutable_buffer_sequence bufs = mStreambuf.prepare(4096);
+ // In general, the mutable_buffer_sequence returned by prepare() might
+ // contain a number of different physical buffers; iterate over those.
+ std::size_t tocommit(0);
+ for (mutable_buffer_sequence::const_iterator bufi(bufs.begin()), bufend(bufs.end());
+ bufi != bufend; ++bufi)
+ {
+ // http://www.boost.org/doc/libs/1_49_0_beta1/doc/html/boost_asio/reference/buffer.html#boost_asio.reference.buffer.accessing_buffer_contents
+ std::size_t toread(boost::asio::buffer_size(*bufi));
+ apr_size_t gotten(toread);
+ apr_status_t err = apr_file_read(mPipe,
+ boost::asio::buffer_cast<void*>(*bufi),
+ &gotten);
+ // EAGAIN is exactly what we want from a nonblocking pipe.
+ // Rather than waiting for data, it should return immediately.
+ if (! (err == APR_SUCCESS || APR_STATUS_IS_EAGAIN(err)))
+ {
+ // Handle EOF specially: it's part of normal-case processing.
+ if (err == APR_EOF)
+ {
+ LL_DEBUGS("LLProcess") << "EOF on " << mDesc << LL_ENDL;
+ }
+ else
+ {
+ LL_WARNS("LLProcess") << "apr_file_read(" << toread << ") on " << mDesc
+ << " got " << err << ":" << LL_ENDL;
+ ll_apr_warn_status(err);
+ }
+ // Either way, though, we won't need any more tick() calls.
+ mConnection.disconnect();
+ // Ignore any subsequent calls we might get anyway.
+ mEOF = true;
+ state = CLOSED; // also break outer retry loop
+ break;
+ }
+
+ // 'gotten' was modified to reflect the number of bytes actually
+ // received. Make sure we commit those later. (Don't commit them
+ // now, that would invalidate the buffer iterator sequence!)
+ tocommit += gotten;
+ LL_DEBUGS("LLProcess") << "filled " << gotten << " of " << toread
+ << " bytes from " << mDesc << LL_ENDL;
+
+ // The parent end of this pipe is nonblocking. If we weren't even
+ // able to fill this buffer, don't loop to try to fill the next --
+ // that won't change until the child writes more. Wait for next
+ // tick().
+ if (gotten < toread)
+ {
+ // break outer retry loop too
+ state = EXHAUSTED;
+ break;
+ }
+ }
+
+ // Don't forget to "commit" the data!
+ mStreambuf.commit(tocommit);
+ committed += tocommit;
+
+ // state is changed from RETRY when we can't fill any one buffer
+ // of the mutable_buffer_sequence established by the current
+ // prepare() call -- whether due to error or not enough bytes.
+ // That is, if state is still RETRY, we've filled every physical
+ // buffer in the mutable_buffer_sequence. In that case, for all we
+ // know, the child might have still more data pending -- go for it!
+ } while (state == RETRY);
+
+ // Once we recognize that the pipe is closed, make one more call to
+ // listener. The listener might be waiting for a particular substring
+ // to arrive, or a particular length of data or something. The event
+ // with "eof" == true announces that nothing further will arrive, so
+ // use it or lose it.
+ if (committed || state == CLOSED)
+ {
+ // If we actually received new data, publish it on our LLEventPump
+ // as advertised. Constrain it by mLimit. But show listener the
+ // actual accumulated buffer size, regardless of mLimit.
+ size_type datasize((std::min)(mLimit, size_type(mStreambuf.size())));
+ mPump.post(LLSDMap
+ ("data", peek(0, datasize))
+ ("len", LLSD::Integer(mStreambuf.size()))
+ ("slot", LLSD::Integer(mIndex))
+ ("name", whichfile(mIndex))
+ ("desc", mDesc)
+ ("eof", state == CLOSED));
+ }
+
+ return false;
+ }
+
+private:
+ std::string mDesc;
+ apr_file_t* mPipe;
+ LLProcess::FILESLOT mIndex;
+ LLTempBoundListener mConnection;
+ boost::asio::streambuf mStreambuf;
+ std::istream mStream;
+ LLEventStream mPump;
+ size_type mLimit;
+ bool mEOF;
+};
+
+/*****************************************************************************
+* LLProcess itself
+*****************************************************************************/
+/// Need an exception to avoid constructing an invalid LLProcess object, but
+/// internal use only
+struct LLProcessError: public LLException
+{
+ LLProcessError(const std::string& msg): LLException(msg) {}
+};
+
+LLProcessPtr LLProcess::create(const LLSDOrParams& params)
+{
+ try
+ {
+ return LLProcessPtr(new LLProcess(params));
+ }
+ catch (const LLProcessError& e)
+ {
+ LL_WARNS("LLProcess") << e.what() << LL_ENDL;
+
+ // If caller is requesting an event on process termination, send one
+ // indicating bad launch. This may prevent someone waiting forever for
+ // a termination post that can't arrive because the child never
+ // started.
+ if (params.postend.isProvided())
+ {
+ LLEventPumps::instance().obtain(params.postend)
+ .post(LLSDMap
+ // no "id"
+ ("desc", getDesc(params))
+ ("state", LLProcess::UNSTARTED)
+ // no "data"
+ ("string", e.what())
+ );
+ }
+
+ return LLProcessPtr();
+ }
+}
+
+/// Call an apr function returning apr_status_t. On failure, log warning and
+/// throw LLProcessError mentioning the function call that produced that
+/// result.
+#define chkapr(func) \
+ if (ll_apr_warn_status(func)) \
+ throw LLProcessError(#func " failed")
+
+LLProcess::LLProcess(const LLSDOrParams& params):
+ mAutokill(params.autokill),
+ // Because 'autokill' originally meant both 'autokill' and 'attached', to
+ // preserve existing semantics, we promise that mAttached defaults to the
+ // same setting as mAutokill.
+ mAttached(params.attached.isProvided()? params.attached : params.autokill),
+ mPool(NULL),
+ mPipes(NSLOTS)
+{
+ // Hmm, when you construct a ptr_vector with a size, it merely reserves
+ // space, it doesn't actually make it that big. Explicitly make it bigger.
+ // Because of ptr_vector's odd semantics, have to push_back(0) the right
+ // number of times! resize() wants to default-construct new BasePipe
+ // instances, which fails because it's pure virtual. But because of the
+ // constructor call, these push_back() calls should require no new
+ // allocation.
+ for (size_t i = 0; i < mPipes.capacity(); ++i)
+ mPipes.push_back(0);
+
+ if (! params.validateBlock(true))
+ {
+ LLTHROW(LLProcessError(STRINGIZE("not launched: failed parameter validation\n"
+ << LLSDNotationStreamer(params))));
+ }
+
+ mPostend = params.postend;
+
+ apr_pool_create(&mPool, gAPRPoolp);
+ if (!mPool)
+ {
+ LLTHROW(LLProcessError(STRINGIZE("failed to create apr pool")));
+ }
+
+ apr_procattr_t *procattr = NULL;
+ chkapr(apr_procattr_create(&procattr, mPool));
+
+ // IQA-490, CHOP-900: On Windows, ask APR to jump through hoops to
+ // constrain the set of handles passed to the child process. Before we
+ // changed to APR, the Windows implementation of LLProcessLauncher called
+ // CreateProcess(bInheritHandles=false), meaning to pass NO open handles
+ // to the child process. Now that we support pipes, though, we must allow
+ // apr_proc_create() to pass bInheritHandles=true. But without taking
+ // special pains, that causes trouble in a number of ways, due to the fact
+ // that the viewer is constantly opening and closing files -- most of
+ // which CreateProcess() passes to every child process!
+#if ! defined(APR_HAS_PROCATTR_CONSTRAIN_HANDLE_SET)
+ // Our special preprocessor symbol isn't even defined -- wrong APR
+ LL_WARNS("LLProcess") << "This version of APR lacks Linden "
+ << "apr_procattr_constrain_handle_set() extension" << LL_ENDL;
+#else
+ chkapr(apr_procattr_constrain_handle_set(procattr, 1));
+#endif
+
+ // For which of stdin, stdout, stderr should we create a pipe to the
+ // child? In the viewer, there are only a couple viable
+ // apr_procattr_io_set() alternatives: inherit the viewer's own stdxxx
+ // handle (APR_NO_PIPE, e.g. for stdout, stderr), or create a pipe that's
+ // blocking on the child end but nonblocking at the viewer end
+ // (APR_CHILD_BLOCK).
+ // Other major options could include explicitly creating a single APR pipe
+ // and passing it as both stdout and stderr (apr_procattr_child_out_set(),
+ // apr_procattr_child_err_set()), or accepting a filename, opening it and
+ // passing that apr_file_t (simple <, >, 2> redirect emulation).
+ std::vector<apr_int32_t> select;
+ for (const FileParam& fparam : params.files)
+ {
+ // Every iteration, we're going to append an item to 'select'. At the
+ // top of the loop, its size() is, in effect, an index. Use that to
+ // pick a string description for messages.
+ std::string which(whichfile(FILESLOT(select.size())));
+ if (fparam.type().empty()) // inherit our file descriptor
+ {
+ select.push_back(APR_NO_PIPE);
+ }
+ else if (fparam.type() == "pipe") // anonymous pipe
+ {
+ if (! fparam.name().empty())
+ {
+ LL_WARNS("LLProcess") << "For " << params.executable()
+ << ": internal names for reusing pipes ('"
+ << fparam.name() << "' for " << which
+ << ") are not yet supported -- creating distinct pipe"
+ << LL_ENDL;
+ }
+ // The viewer can't block for anything: the parent end MUST be
+ // nonblocking. As the APR documentation itself points out, it
+ // makes very little sense to set nonblocking I/O for the child
+ // end of a pipe: only a specially-written child could deal with
+ // that.
+ select.push_back(APR_CHILD_BLOCK);
+ }
+ else
+ {
+ LLTHROW(LLProcessError(STRINGIZE("For " << params.executable()
+ << ": unsupported FileParam for " << which
+ << ": type='" << fparam.type()
+ << "', name='" << fparam.name() << "'")));
+ }
+ }
+ // By default, pass APR_NO_PIPE for unspecified slots.
+ while (select.size() < NSLOTS)
+ {
+ select.push_back(APR_NO_PIPE);
+ }
+ chkapr(apr_procattr_io_set(procattr, select[STDIN], select[STDOUT], select[STDERR]));
+
+ // Thumbs down on implicitly invoking the shell to invoke the child. From
+ // our point of view, the other major alternative to APR_PROGRAM_PATH
+ // would be APR_PROGRAM_ENV: still copy environment, but require full
+ // executable pathname. I don't see a downside to searching the PATH,
+ // though: if our caller wants (e.g.) a specific Python interpreter, s/he
+ // can still pass the full pathname.
+ chkapr(apr_procattr_cmdtype_set(procattr, APR_PROGRAM_PATH));
+ // YES, do extra work if necessary to report child exec() failures back to
+ // parent process.
+ chkapr(apr_procattr_error_check_set(procattr, 1));
+ // Do not start a non-autokill child in detached state. On Posix
+ // platforms, this setting attempts to daemonize the new child, closing
+ // std handles and the like, and that's a bit more detachment than we
+ // want. autokill=false just means not to implicitly kill the child when
+ // the parent terminates!
+// chkapr(apr_procattr_detach_set(procattr, mAutokill? 0 : 1));
+
+ if (mAutokill)
+ {
+#if ! defined(APR_HAS_PROCATTR_AUTOKILL_SET)
+ // Our special preprocessor symbol isn't even defined -- wrong APR
+ LL_WARNS("LLProcess") << "This version of APR lacks Linden apr_procattr_autokill_set() extension" << LL_ENDL;
+#elif ! APR_HAS_PROCATTR_AUTOKILL_SET
+ // Symbol is defined, but to 0: expect apr_procattr_autokill_set() to
+ // return APR_ENOTIMPL.
+#else // APR_HAS_PROCATTR_AUTOKILL_SET nonzero
+ ll_apr_warn_status(apr_procattr_autokill_set(procattr, 1));
+#endif
+ }
+
+ // In preparation for calling apr_proc_create(), we collect a number of
+ // const char* pointers obtained from std::string::c_str(). Turns out
+ // LLInitParam::Block's helpers Optional, Mandatory, Multiple et al.
+ // guarantee that converting to the wrapped type (std::string in our
+ // case), e.g. by calling operator(), returns a reference to *the same
+ // instance* of the wrapped type that's stored in our Block subclass.
+ // That's important! We know 'params' persists throughout this method
+ // call; but without that guarantee, we'd have to assume that converting
+ // one of its members to std::string might return a different (temp)
+ // instance. Capturing the c_str() from a temporary std::string is Bad Bad
+ // Bad. But armed with this knowledge, when you see params.cwd().c_str(),
+ // grit your teeth and smile and carry on.
+
+ if (params.cwd.isProvided())
+ {
+ chkapr(apr_procattr_dir_set(procattr, params.cwd().c_str()));
+ }
+
+ // create an argv vector for the child process
+ std::vector<const char*> argv;
+
+ // Add the executable path. See above remarks about c_str().
+ argv.push_back(params.executable().c_str());
+
+ // Add arguments. See above remarks about c_str().
+ for (const std::string& arg : params.args)
+ {
+ argv.push_back(arg.c_str());
+ }
+
+ // terminate with a null pointer
+ argv.push_back(NULL);
+
+ // Launch! The NULL would be the environment block, if we were passing
+ // one. Hand-expand chkapr() macro so we can fill in the actual command
+ // string instead of the variable names.
+ if (ll_apr_warn_status(apr_proc_create(&mProcess, argv[0], &argv[0], NULL, procattr,
+ mPool)))
+ {
+ LLTHROW(LLProcessError(STRINGIZE(params << " failed")));
+ }
+
+ // arrange to call status_callback()
+ apr_proc_other_child_register(&mProcess, &LLProcess::status_callback, this, mProcess.in,
+ mPool);
+ // and make sure we poll it once per "mainloop" tick
+ sProcessListener.addPoll(*this);
+ mStatus.mState = RUNNING;
+
+ mDesc = STRINGIZE(getDesc(params) << " (" << mProcess.pid << ')');
+ LL_INFOS("LLProcess") << mDesc << ": launched " << params << LL_ENDL;
+
+ // Unless caller explicitly turned off autokill (child should persist),
+ // take steps to terminate the child. This is all suspenders-and-belt: in
+ // theory our destructor should kill an autokill child, but in practice
+ // that doesn't always work (e.g. VWR-21538).
+ if (mAutokill)
+ {
+/*==========================================================================*|
+ // NO: There may be an APR bug, not sure -- but at least on Mac, when
+ // gAPRPoolp is destroyed, OUR process receives SIGTERM! Apparently
+ // either our own PID is getting into the list of processes to kill()
+ // (unlikely), or somehow one of those PIDs is getting zeroed first,
+ // so that kill() sends SIGTERM to the whole process group -- this
+ // process included. I'd have to build and link with a debug version
+ // of APR to know for sure. It's too bad: this mechanism would be just
+ // right for dealing with static autokill LLProcessPtr variables,
+ // which aren't destroyed until after APR is no longer available.
+
+ // Tie the lifespan of this child process to the lifespan of our APR
+ // pool: on destruction of the pool, forcibly kill the process. Tell
+ // APR to try SIGTERM and suspend 3 seconds. If that didn't work, use
+ // SIGKILL.
+ apr_pool_note_subprocess(gAPRPoolp, &mProcess, APR_KILL_AFTER_TIMEOUT);
+|*==========================================================================*/
+
+ // On Windows, associate the new child process with our Job Object.
+ autokill();
+ }
+
+ // Instantiate the proper pipe I/O machinery
+ // want to be able to point to apr_proc_t::in, out, err by index
+ typedef apr_file_t* apr_proc_t::*apr_proc_file_ptr;
+ static apr_proc_file_ptr members[] =
+ { &apr_proc_t::in, &apr_proc_t::out, &apr_proc_t::err };
+ for (size_t i = 0; i < NSLOTS; ++i)
+ {
+ if (select[i] != APR_CHILD_BLOCK)
+ continue;
+ std::string desc(STRINGIZE(mDesc << ' ' << whichfile(FILESLOT(i))));
+ apr_file_t* pipe(mProcess.*(members[i]));
+ if (i == STDIN)
+ {
+ mPipes.replace(i, new WritePipeImpl(desc, pipe));
+ }
+ else
+ {
+ mPipes.replace(i, new ReadPipeImpl(desc, pipe, FILESLOT(i)));
+ }
+ // Removed temporaily for Xcode 7 build tests: error was:
+ // "error: expression with side effects will be evaluated despite
+ // being used as an operand to 'typeid' [-Werror,-Wpotentially-evaluated-expression]""
+ //LL_DEBUGS("LLProcess") << "Instantiating " << typeid(mPipes[i]).name()
+ // << "('" << desc << "')" << LL_ENDL;
+ }
+}
+
+// Helper to obtain a description string, given a Params block
+static std::string getDesc(const LLProcess::Params& params)
+{
+ // If caller specified a description string, by all means use it.
+ if (params.desc.isProvided())
+ return params.desc;
+
+ // Caller didn't say. Use the executable name -- but use just the filename
+ // part. On Mac, for instance, full pathnames get cumbersome.
+ return LLProcess::basename(params.executable);
+}
+
+//static
+std::string LLProcess::basename(const std::string& path)
+{
+ // If there are Linden utility functions to manipulate pathnames, I
+ // haven't found them -- and for this usage, Boost.Filesystem seems kind
+ // of heavyweight.
+ std::string::size_type delim = path.find_last_of("\\/");
+ // If path contains no pathname delimiters, return the whole thing.
+ if (delim == std::string::npos)
+ return path;
+
+ // Return just the part beyond the last delimiter.
+ return path.substr(delim + 1);
+}
+
+LLProcess::~LLProcess()
+{
+ // In the Linden viewer, there's at least one static LLProcessPtr. Its
+ // destructor will be called *after* ll_cleanup_apr(). In such a case,
+ // unregistering is pointless (and fatal!) -- and kill(), which also
+ // relies on APR, is impossible.
+ if (! gAPRPoolp)
+ return;
+
+ // Only in state RUNNING are we registered for callback. In UNSTARTED we
+ // haven't yet registered. And since receiving the callback is the only
+ // way we detect child termination, we only change from state RUNNING at
+ // the same time we unregister.
+ if (mStatus.mState == RUNNING)
+ {
+ // We're still registered for a callback: unregister. Do it before
+ // we even issue the kill(): even if kill() somehow prompted an
+ // instantaneous callback (unlikely), this object is going away! Any
+ // information updated in this object by such a callback is no longer
+ // available to any consumer anyway.
+ apr_proc_other_child_unregister(this);
+ // One less LLProcess to poll for
+ sProcessListener.dropPoll(*this);
+ }
+
+ if (mAttached)
+ {
+ kill("destructor");
+ }
+
+ if (mPool)
+ {
+ apr_pool_destroy(mPool);
+ mPool = NULL;
+ }
+}
+
+bool LLProcess::kill(const std::string& who)
+{
+ if (isRunning())
+ {
+ LL_INFOS("LLProcess") << who << " killing " << mDesc << LL_ENDL;
+
+#if LL_WINDOWS
+ int sig = -1;
+#else // Posix
+ int sig = SIGTERM;
+#endif
+
+ ll_apr_warn_status(apr_proc_kill(&mProcess, sig));
+ }
+
+ return ! isRunning();
+}
+
+//static
+bool LLProcess::kill(const LLProcessPtr& p, const std::string& who)
+{
+ if (! p)
+ return true; // process dead! (was never running)
+ return p->kill(who);
+}
+
+bool LLProcess::isRunning() const
+{
+ return getStatus().mState == RUNNING;
+}
+
+//static
+bool LLProcess::isRunning(const LLProcessPtr& p)
+{
+ if (! p)
+ return false;
+ return p->isRunning();
+}
+
+LLProcess::Status LLProcess::getStatus() const
+{
+ return mStatus;
+}
+
+//static
+LLProcess::Status LLProcess::getStatus(const LLProcessPtr& p)
+{
+ if (! p)
+ {
+ // default-constructed Status has mState == UNSTARTED
+ return Status();
+ }
+ return p->getStatus();
+}
+
+std::string LLProcess::getStatusString() const
+{
+ return getStatusString(getStatus());
+}
+
+std::string LLProcess::getStatusString(const Status& status) const
+{
+ return getStatusString(mDesc, status);
+}
+
+//static
+std::string LLProcess::getStatusString(const std::string& desc, const LLProcessPtr& p)
+{
+ if (! p)
+ {
+ // default-constructed Status has mState == UNSTARTED
+ return getStatusString(desc, Status());
+ }
+ return desc + " " + p->getStatusString();
+}
+
+//static
+std::string LLProcess::getStatusString(const std::string& desc, const Status& status)
+{
+ if (status.mState == UNSTARTED)
+ return desc + " was never launched";
+
+ if (status.mState == RUNNING)
+ return desc + " running";
+
+ if (status.mState == EXITED)
+ return STRINGIZE(desc << " exited with code " << status.mData);
+
+ if (status.mState == KILLED)
+#if LL_WINDOWS
+ return STRINGIZE(desc << " killed with exception " << std::hex << status.mData);
+#else
+ return STRINGIZE(desc << " killed by signal " << status.mData
+ << " (" << apr_signal_description_get(status.mData) << ")");
+#endif
+
+ return STRINGIZE(desc << " in unknown state " << status.mState << " (" << status.mData << ")");
+}
+
+// Classic-C-style APR callback
+void LLProcess::status_callback(int reason, void* data, int status)
+{
+ // Our only role is to bounce this static method call back into object
+ // space.
+ static_cast<LLProcess*>(data)->handle_status(reason, status);
+}
+
+#define tabent(symbol) { symbol, #symbol }
+static struct ReasonCode
+{
+ int code;
+ const char* name;
+} reasons[] =
+{
+ tabent(APR_OC_REASON_DEATH),
+ tabent(APR_OC_REASON_UNWRITABLE),
+ tabent(APR_OC_REASON_RESTART),
+ tabent(APR_OC_REASON_UNREGISTER),
+ tabent(APR_OC_REASON_LOST),
+ tabent(APR_OC_REASON_RUNNING)
+};
+#undef tabent
+
+// Object-oriented callback
+void LLProcess::handle_status(int reason, int status)
+{
+ {
+ // This odd appearance of LL_DEBUGS is just to bracket a lookup that will
+ // only be performed if in fact we're going to produce the log message.
+ LL_DEBUGS("LLProcess") << empty;
+ std::string reason_str;
+ for (const ReasonCode& rcp : reasons)
+ {
+ if (reason == rcp.code)
+ {
+ reason_str = rcp.name;
+ break;
+ }
+ }
+ if (reason_str.empty())
+ {
+ reason_str = STRINGIZE("unknown reason " << reason);
+ }
+ LL_CONT << mDesc << ": handle_status(" << reason_str << ", " << status << ")" << LL_ENDL;
+ }
+
+ if (! (reason == APR_OC_REASON_DEATH || reason == APR_OC_REASON_LOST))
+ {
+ // We're only interested in the call when the child terminates.
+ return;
+ }
+
+ // Somewhat oddly, APR requires that you explicitly unregister even when
+ // it already knows the child has terminated. We must pass the same 'data'
+ // pointer as for the register() call, which was our 'this'.
+ apr_proc_other_child_unregister(this);
+ // don't keep polling for a terminated process
+ sProcessListener.dropPoll(*this);
+ // We overload mStatus.mState to indicate whether the child is registered
+ // for APR callback: only RUNNING means registered. Track that we've
+ // unregistered. We know the child has terminated; might be EXITED or
+ // KILLED; refine below.
+ mStatus.mState = EXITED;
+
+ // Make last-gasp calls for each of the ReadPipes we have on hand. Since
+ // they're listening on "mainloop", we can be sure they'll eventually
+ // collect all pending data from the child. But we want to be able to
+ // guarantee to our consumer that by the time we post on the "postend"
+ // LLEventPump, our ReadPipes are already buffering all the data there
+ // will ever be from the child. That lets the "postend" listener decide
+ // what to do with that final data.
+ for (size_t i = 0; i < mPipes.size(); ++i)
+ {
+ std::string error;
+ ReadPipeImpl* ppipe = getPipePtr<ReadPipeImpl>(error, FILESLOT(i));
+ if (ppipe)
+ {
+ static LLSD trivial;
+ ppipe->tick(trivial);
+ }
+ }
+
+// wi->rv = apr_proc_wait(wi->child, &wi->rc, &wi->why, APR_NOWAIT);
+ // It's just wrong to call apr_proc_wait() here. The only way APR knows to
+ // call us with APR_OC_REASON_DEATH is that it's already reaped this child
+ // process, so calling wait() will only produce "huh?" from the OS. We
+ // must rely on the status param passed in, which unfortunately comes
+ // straight from the OS wait() call, which means we have to decode it by
+ // hand.
+ mStatus = interpret_status(status);
+ LL_INFOS("LLProcess") << getStatusString() << LL_ENDL;
+
+ // If caller requested notification on child termination, send it.
+ if (! mPostend.empty())
+ {
+ LLEventPumps::instance().obtain(mPostend)
+ .post(LLSDMap
+ ("id", getProcessID())
+ ("desc", mDesc)
+ ("state", mStatus.mState)
+ ("data", mStatus.mData)
+ ("string", getStatusString())
+ );
+ }
+}
+
+LLProcess::id LLProcess::getProcessID() const
+{
+ return mProcess.pid;
+}
+
+LLProcess::handle LLProcess::getProcessHandle() const
+{
+#if LL_WINDOWS
+ return mProcess.hproc;
+#else
+ return mProcess.pid;
+#endif
+}
+
+std::string LLProcess::getPipeName(FILESLOT) const
+{
+ // LLProcess::FileParam::type "npipe" is not yet implemented
+ return "";
+}
+
+template<class PIPETYPE>
+PIPETYPE* LLProcess::getPipePtr(std::string& error, FILESLOT slot)
+{
+ if (slot >= NSLOTS)
+ {
+ error = STRINGIZE(mDesc << " has no slot " << slot);
+ return NULL;
+ }
+ if (mPipes.is_null(slot))
+ {
+ error = STRINGIZE(mDesc << ' ' << whichfile(slot) << " not a monitored pipe");
+ return NULL;
+ }
+ // Make sure we dynamic_cast in pointer domain so we can test, rather than
+ // accepting runtime's exception.
+ PIPETYPE* ppipe = dynamic_cast<PIPETYPE*>(&mPipes[slot]);
+ if (! ppipe)
+ {
+ error = STRINGIZE(mDesc << ' ' << whichfile(slot) << " not a " << typeid(PIPETYPE).name());
+ return NULL;
+ }
+
+ error.clear();
+ return ppipe;
+}
+
+template <class PIPETYPE>
+PIPETYPE& LLProcess::getPipe(FILESLOT slot)
+{
+ std::string error;
+ PIPETYPE* wp = getPipePtr<PIPETYPE>(error, slot);
+ if (! wp)
+ {
+ LLTHROW(NoPipe(error));
+ }
+ return *wp;
+}
+
+template <class PIPETYPE>
+boost::optional<PIPETYPE&> LLProcess::getOptPipe(FILESLOT slot)
+{
+ std::string error;
+ PIPETYPE* wp = getPipePtr<PIPETYPE>(error, slot);
+ if (! wp)
+ {
+ LL_DEBUGS("LLProcess") << error << LL_ENDL;
+ return boost::optional<PIPETYPE&>();
+ }
+ return *wp;
+}
+
+LLProcess::WritePipe& LLProcess::getWritePipe(FILESLOT slot)
+{
+ return getPipe<WritePipe>(slot);
+}
+
+boost::optional<LLProcess::WritePipe&> LLProcess::getOptWritePipe(FILESLOT slot)
+{
+ return getOptPipe<WritePipe>(slot);
+}
+
+LLProcess::ReadPipe& LLProcess::getReadPipe(FILESLOT slot)
+{
+ return getPipe<ReadPipe>(slot);
+}
+
+boost::optional<LLProcess::ReadPipe&> LLProcess::getOptReadPipe(FILESLOT slot)
+{
+ return getOptPipe<ReadPipe>(slot);
+}
+
+//static
+std::string LLProcess::getline(std::istream& in)
+{
+ std::string line;
+ std::getline(in, line);
+ // Blur the distinction between "\r\n" and plain "\n". std::getline() will
+ // have eaten the "\n", but we could still end up with a trailing "\r".
+ std::string::size_type lastpos = line.find_last_not_of("\r");
+ if (lastpos != std::string::npos)
+ {
+ // Found at least one character that's not a trailing '\r'. SKIP OVER
+ // IT and erase the rest of the line.
+ line.erase(lastpos+1);
+ }
+ return line;
+}
+
+std::ostream& operator<<(std::ostream& out, const LLProcess::Params& params)
+{
+ if (params.cwd.isProvided())
+ {
+ out << "cd " << LLStringUtil::quote(params.cwd) << ": ";
+ }
+ out << LLStringUtil::quote(params.executable);
+ for (const std::string& arg : params.args)
+ {
+ out << ' ' << LLStringUtil::quote(arg);
+ }
+ return out;
+}
+
+/*****************************************************************************
+* Windows specific
+*****************************************************************************/
+#if LL_WINDOWS
+
+static std::string WindowsErrorString(const std::string& operation);
+
+void LLProcess::autokill()
+{
+ // hopefully now handled by apr_procattr_autokill_set()
+}
+
+LLProcess::handle LLProcess::isRunning(handle h, const std::string& desc)
+{
+ // This direct Windows implementation is because we have no access to the
+ // apr_proc_t struct: we expect it's been destroyed.
+ if (! h)
+ return 0;
+
+ DWORD waitresult = WaitForSingleObject(h, 0);
+ if(waitresult == WAIT_OBJECT_0)
+ {
+ // the process has completed.
+ if (! desc.empty())
+ {
+ DWORD status = 0;
+ if (! GetExitCodeProcess(h, &status))
+ {
+ LL_WARNS("LLProcess") << desc << " terminated, but "
+ << WindowsErrorString("GetExitCodeProcess()") << LL_ENDL;
+ }
+ {
+ LL_INFOS("LLProcess") << getStatusString(desc, interpret_status(status))
+ << LL_ENDL;
+ }
+ }
+ CloseHandle(h);
+ return 0;
+ }
+
+ return h;
+}
+
+static LLProcess::Status interpret_status(int status)
+{
+ LLProcess::Status result;
+
+ // This bit of code is cribbed from apr/threadproc/win32/proc.c, a
+ // function (unfortunately static) called why_from_exit_code():
+ /* See WinNT.h STATUS_ACCESS_VIOLATION and family for how
+ * this class of failures was determined
+ */
+ if ((status & 0xFFFF0000) == 0xC0000000)
+ {
+ result.mState = LLProcess::KILLED;
+ }
+ else
+ {
+ result.mState = LLProcess::EXITED;
+ }
+ result.mData = status;
+
+ return result;
+}
+
+/// GetLastError()/FormatMessage() boilerplate
+static std::string WindowsErrorString(const std::string& operation)
+{
+ auto result = GetLastError();
+ return STRINGIZE(operation << " failed (" << result << "): "
+ << windows_message<std::string>(result));
+}
+
+/*****************************************************************************
+* Posix specific
+*****************************************************************************/
+#else // Mac and linux
+
+#include <signal.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/wait.h>
+
+void LLProcess::autokill()
+{
+ // What we ought to do here is to:
+ // 1. create a unique process group and run all autokill children in that
+ // group (see https://jira.secondlife.com/browse/SWAT-563);
+ // 2. figure out a way to intercept control when the viewer exits --
+ // gracefully or not;
+ // 3. when the viewer exits, kill off the aforementioned process group.
+
+ // It's point 2 that's troublesome. Although I've seen some signal-
+ // handling logic in the Posix viewer code, I haven't yet found any bit of
+ // code that's run no matter how the viewer exits (a try/finally for the
+ // whole process, as it were).
+}
+
+// Attempt to reap a process ID -- returns true if the process has exited and been reaped, false otherwise.
+static bool reap_pid(pid_t pid, LLProcess::Status* pstatus=NULL)
+{
+ LLProcess::Status dummy;
+ if (! pstatus)
+ {
+ // If caller doesn't want to see Status, give us a target anyway so we
+ // don't have to have a bunch of conditionals.
+ pstatus = &dummy;
+ }
+
+ int status = 0;
+ pid_t wait_result = ::waitpid(pid, &status, WNOHANG);
+ if (wait_result == pid)
+ {
+ *pstatus = interpret_status(status);
+ return true;
+ }
+ if (wait_result == 0)
+ {
+ pstatus->mState = LLProcess::RUNNING;
+ pstatus->mData = 0;
+ return false;
+ }
+
+ // Clear caller's Status block; caller must interpret UNSTARTED to mean
+ // "if this PID was ever valid, it no longer is."
+ *pstatus = LLProcess::Status();
+
+ // We've dealt with the success cases: we were able to reap the child
+ // (wait_result == pid) or it's still running (wait_result == 0). It may
+ // be that the child terminated but didn't hang around long enough for us
+ // to reap. In that case we still have no Status to report, but we can at
+ // least state that it's not running.
+ if (wait_result == -1 && errno == ECHILD)
+ {
+ // No such process -- this may mean we're ignoring SIGCHILD.
+ return true;
+ }
+
+ // Uh, should never happen?!
+ LL_WARNS("LLProcess") << "LLProcess::reap_pid(): waitpid(" << pid << ") returned "
+ << wait_result << "; not meaningful?" << LL_ENDL;
+ // If caller is looping until this pid terminates, and if we can't find
+ // out, better to break the loop than to claim it's still running.
+ return true;
+}
+
+LLProcess::id LLProcess::isRunning(id pid, const std::string& desc)
+{
+ // This direct Posix implementation is because we have no access to the
+ // apr_proc_t struct: we expect it's been destroyed.
+ if (! pid)
+ return 0;
+
+ // Check whether the process has exited, and reap it if it has.
+ LLProcess::Status status;
+ if(reap_pid(pid, &status))
+ {
+ // the process has exited.
+ if (! desc.empty())
+ {
+ std::string statstr(desc + " apparently terminated: no status available");
+ // We don't just pass UNSTARTED to getStatusString() because, in
+ // the context of reap_pid(), that state has special meaning.
+ if (status.mState != UNSTARTED)
+ {
+ statstr = getStatusString(desc, status);
+ }
+ LL_INFOS("LLProcess") << statstr << LL_ENDL;
+ }
+ return 0;
+ }
+
+ return pid;
+}
+
+static LLProcess::Status interpret_status(int status)
+{
+ LLProcess::Status result;
+
+ if (WIFEXITED(status))
+ {
+ result.mState = LLProcess::EXITED;
+ result.mData = WEXITSTATUS(status);
+ }
+ else if (WIFSIGNALED(status))
+ {
+ result.mState = LLProcess::KILLED;
+ result.mData = WTERMSIG(status);
+ }
+ else // uh, shouldn't happen?
+ {
+ result.mState = LLProcess::EXITED;
+ result.mData = status; // someone else will have to decode
+ }
+
+ return result;
+}
+
+#endif // Posix
diff --git a/indra/llcommon/llprocess.h b/indra/llcommon/llprocess.h index c57821bf52..166da8f424 100644 --- a/indra/llcommon/llprocess.h +++ b/indra/llcommon/llprocess.h @@ -1,29 +1,29 @@ -/** +/** * @file llprocess.h * @brief Utility class for launching, terminating, and tracking child processes. * * $LicenseInfo:firstyear=2008&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ - + #ifndef LL_LLPROCESS_H #define LL_LLPROCESS_H @@ -39,7 +39,7 @@ #include <iosfwd> // std::ostream #if LL_WINDOWS -#include "llwin32headerslean.h" // for HANDLE +#include "llwin32headerslean.h" // for HANDLE #elif LL_LINUX #if defined(Status) #undef Status @@ -71,503 +71,503 @@ typedef std::shared_ptr<LLProcess> LLProcessPtr; */ class LL_COMMON_API LLProcess: public boost::noncopyable { - LOG_CLASS(LLProcess); + LOG_CLASS(LLProcess); public: - /** - * Specify what to pass for each of child stdin, stdout, stderr. - * @see LLProcess::Params::files. - */ - struct FileParam: public LLInitParam::Block<FileParam> - { - /** - * type of file handle to pass to child process - * - * - "" (default): let the child inherit the same file handle used by - * this process. For instance, if passed as stdout, child stdout - * will be interleaved with stdout from this process. In this case, - * @a name is moot and should be left "". - * - * - "file": open an OS filesystem file with the specified @a name. - * <i>Not yet implemented.</i> - * - * - "pipe" or "tpipe" or "npipe": depends on @a name - * - * - @a name.empty(): construct an OS pipe used only for this slot - * of the forthcoming child process. - * - * - ! @a name.empty(): in a global registry, find or create (using - * the specified @a name) an OS pipe. The point of the (purely - * internal) @a name is that passing the same @a name in more than - * one slot for a given LLProcess -- or for slots in different - * LLProcess instances -- means the same pipe. For example, you - * might pass the same @a name value as both stdout and stderr to - * make the child process produce both on the same actual pipe. Or - * you might pass the same @a name as the stdout for one LLProcess - * and the stdin for another to connect the two child processes. - * Use LLProcess::getPipeName() to generate a unique name - * guaranteed not to already exist in the registry. <i>Not yet - * implemented.</i> - * - * The difference between "pipe", "tpipe" and "npipe" is as follows. - * - * - "pipe": direct LLProcess to monitor the parent end of the pipe, - * pumping nonblocking I/O every frame. The expectation (at least - * for stdout or stderr) is that the caller will listen for - * incoming data and consume it as it arrives. It's important not - * to neglect such a pipe, because it's buffered in memory. If you - * suspect the child may produce a great volume of output between - * frames, consider directing the child to write to a filesystem - * file instead, then read the file later. - * - * - "tpipe": do not engage LLProcess machinery to monitor the - * parent end of the pipe. A "tpipe" is used only to connect - * different child processes. As such, it makes little sense to - * pass an empty @a name. <i>Not yet implemented.</i> - * - * - "npipe": like "tpipe", but use an OS named pipe with a - * generated name. Note that @a name is the @em internal name of - * the pipe in our global registry -- it doesn't necessarily have - * anything to do with the pipe's name in the OS filesystem. Use - * LLProcess::getPipeName() to obtain the named pipe's OS - * filesystem name, e.g. to pass it as the @a name to another - * LLProcess instance using @a type "file". This supports usage - * like bash's <(subcommand...) or >(subcommand...) - * constructs. <i>Not yet implemented.</i> - * - * In all cases the open mode (read, write) is determined by the child - * slot you're filling. Child stdin means select the "read" end of a - * pipe, or open a filesystem file for reading; child stdout or stderr - * means select the "write" end of a pipe, or open a filesystem file - * for writing. - * - * Confusion such as passing the same pipe as the stdin of two - * processes (rather than stdout for one and stdin for the other) is - * explicitly permitted: it's up to the caller to construct meaningful - * LLProcess pipe graphs. - */ - Optional<std::string> type; - Optional<std::string> name; - - FileParam(const std::string& tp="", const std::string& nm=""): - type("type"), - name("name") - { - // If caller wants to specify values, use explicit assignment to - // set them rather than initialization. - if (! tp.empty()) type = tp; - if (! nm.empty()) name = nm; - } - }; - - /// Param block definition - struct Params: public LLInitParam::Block<Params> - { - Params(): - executable("executable"), - args("args"), - cwd("cwd"), - autokill("autokill", true), - attached("attached", true), - files("files"), - postend("postend"), - desc("desc") - {} - - /// pathname of executable - Mandatory<std::string> executable; - /** - * zero or more additional command-line arguments. Arguments are - * passed through as exactly as we can manage, whitespace and all. - * @note On Windows we manage this by implicitly double-quoting each - * argument while assembling the command line. - */ - Multiple<std::string> args; - /// current working directory, if need it changed - Optional<std::string> cwd; - /// implicitly kill child process on termination of parent, whether - /// voluntary or crash (default true) - Optional<bool> autokill; - /// implicitly kill process on destruction of LLProcess object - /// (default same as autokill) - /// - /// Originally, 'autokill' conflated two concepts: kill child process on - /// - destruction of its LLProcess object, and - /// - termination of parent process, voluntary or otherwise. - /// - /// It's useful to tease these apart. Some child processes are sent a - /// "clean up and terminate" message before the associated LLProcess - /// object is destroyed. A child process launched with attached=false - /// has an extra time window from the destruction of its LLProcess - /// until parent-process termination in which to perform its own - /// orderly shutdown, yet autokill=true still guarantees that we won't - /// accumulate orphan instances of such processes indefinitely. With - /// attached=true, if a child process cannot clean up between the - /// shutdown message and LLProcess destruction (presumably very soon - /// thereafter), it's forcibly killed anyway -- which can lead to - /// distressing user-visible crash indications. - /// - /// (The usefulness of attached=true with autokill=false is less - /// clear, but we don't prohibit that combination.) - Optional<bool> attached; - /** - * Up to three FileParam items: for child stdin, stdout, stderr. - * Passing two FileParam entries means default treatment for stderr, - * and so forth. - * - * @note LLInitParam::Block permits usage like this: - * @code - * LLProcess::Params params; - * ... - * params.files - * .add(LLProcess::FileParam()) // stdin - * .add(LLProcess::FileParam().type("pipe") // stdout - * .add(LLProcess::FileParam().type("file").name("error.log")); - * @endcode - * - * @note While it's theoretically plausible to pass additional open - * file handles to a child specifically written to expect them, our - * underlying implementation doesn't yet support that. - */ - Multiple<FileParam, AtMost<3> > files; - /** - * On child-process termination, if this LLProcess object still - * exists, post LLSD event to LLEventPump with specified name (default - * no event). Event contains at least: - * - * - "id" as obtained from getProcessID() - * - "desc" short string description of child (executable + pid) - * - "state" @c state enum value, from Status.mState - * - "data" if "state" is EXITED, exit code; if KILLED, on Posix, - * signal number - * - "string" English text describing "state" and "data" (e.g. "exited - * with code 0") - */ - Optional<std::string> postend; - /** - * Description of child process for logging purposes. It need not be - * unique; the logged description string will contain the PID as well. - * If this is omitted, a description will be derived from the - * executable name. - */ - Optional<std::string> desc; - }; - typedef LLSDParamAdapter<Params> LLSDOrParams; - - /** - * Factory accepting either plain LLSD::Map or Params block. - * MAY RETURN DEFAULT-CONSTRUCTED LLProcessPtr if params invalid! - */ - static LLProcessPtr create(const LLSDOrParams& params); - virtual ~LLProcess(); - - /// Is child process still running? - bool isRunning() const; - // static isRunning(LLProcessPtr), getStatus(LLProcessPtr), - // getStatusString(LLProcessPtr), kill(LLProcessPtr) handle the case in - // which the passed LLProcessPtr might be NULL (default-constructed). - static bool isRunning(const LLProcessPtr&); - - /** - * State of child process - */ - enum state - { - UNSTARTED, ///< initial value, invisible to consumer - RUNNING, ///< child process launched - EXITED, ///< child process terminated voluntarily - KILLED ///< child process terminated involuntarily - }; - - /** - * Status info - */ - struct Status - { - Status(): - mState(UNSTARTED), - mData(0) - {} - - state mState; ///< @see state - /** - * - for mState == EXITED: mData is exit() code - * - for mState == KILLED: mData is signal number (Posix) - * - otherwise: mData is undefined - */ - int mData; - }; - - /// Status query - Status getStatus() const; - static Status getStatus(const LLProcessPtr&); - /// English Status string query, for logging etc. - std::string getStatusString() const; - static std::string getStatusString(const std::string& desc, const LLProcessPtr&); - /// English Status string query for previously-captured Status - std::string getStatusString(const Status& status) const; - /// static English Status string query - static std::string getStatusString(const std::string& desc, const Status& status); - - // Attempt to kill the process -- returns true if the process is no longer running when it returns. - // Note that even if this returns false, the process may exit some time after it's called. - bool kill(const std::string& who=""); - static bool kill(const LLProcessPtr& p, const std::string& who=""); + /** + * Specify what to pass for each of child stdin, stdout, stderr. + * @see LLProcess::Params::files. + */ + struct FileParam: public LLInitParam::Block<FileParam> + { + /** + * type of file handle to pass to child process + * + * - "" (default): let the child inherit the same file handle used by + * this process. For instance, if passed as stdout, child stdout + * will be interleaved with stdout from this process. In this case, + * @a name is moot and should be left "". + * + * - "file": open an OS filesystem file with the specified @a name. + * <i>Not yet implemented.</i> + * + * - "pipe" or "tpipe" or "npipe": depends on @a name + * + * - @a name.empty(): construct an OS pipe used only for this slot + * of the forthcoming child process. + * + * - ! @a name.empty(): in a global registry, find or create (using + * the specified @a name) an OS pipe. The point of the (purely + * internal) @a name is that passing the same @a name in more than + * one slot for a given LLProcess -- or for slots in different + * LLProcess instances -- means the same pipe. For example, you + * might pass the same @a name value as both stdout and stderr to + * make the child process produce both on the same actual pipe. Or + * you might pass the same @a name as the stdout for one LLProcess + * and the stdin for another to connect the two child processes. + * Use LLProcess::getPipeName() to generate a unique name + * guaranteed not to already exist in the registry. <i>Not yet + * implemented.</i> + * + * The difference between "pipe", "tpipe" and "npipe" is as follows. + * + * - "pipe": direct LLProcess to monitor the parent end of the pipe, + * pumping nonblocking I/O every frame. The expectation (at least + * for stdout or stderr) is that the caller will listen for + * incoming data and consume it as it arrives. It's important not + * to neglect such a pipe, because it's buffered in memory. If you + * suspect the child may produce a great volume of output between + * frames, consider directing the child to write to a filesystem + * file instead, then read the file later. + * + * - "tpipe": do not engage LLProcess machinery to monitor the + * parent end of the pipe. A "tpipe" is used only to connect + * different child processes. As such, it makes little sense to + * pass an empty @a name. <i>Not yet implemented.</i> + * + * - "npipe": like "tpipe", but use an OS named pipe with a + * generated name. Note that @a name is the @em internal name of + * the pipe in our global registry -- it doesn't necessarily have + * anything to do with the pipe's name in the OS filesystem. Use + * LLProcess::getPipeName() to obtain the named pipe's OS + * filesystem name, e.g. to pass it as the @a name to another + * LLProcess instance using @a type "file". This supports usage + * like bash's <(subcommand...) or >(subcommand...) + * constructs. <i>Not yet implemented.</i> + * + * In all cases the open mode (read, write) is determined by the child + * slot you're filling. Child stdin means select the "read" end of a + * pipe, or open a filesystem file for reading; child stdout or stderr + * means select the "write" end of a pipe, or open a filesystem file + * for writing. + * + * Confusion such as passing the same pipe as the stdin of two + * processes (rather than stdout for one and stdin for the other) is + * explicitly permitted: it's up to the caller to construct meaningful + * LLProcess pipe graphs. + */ + Optional<std::string> type; + Optional<std::string> name; + + FileParam(const std::string& tp="", const std::string& nm=""): + type("type"), + name("name") + { + // If caller wants to specify values, use explicit assignment to + // set them rather than initialization. + if (! tp.empty()) type = tp; + if (! nm.empty()) name = nm; + } + }; + + /// Param block definition + struct Params: public LLInitParam::Block<Params> + { + Params(): + executable("executable"), + args("args"), + cwd("cwd"), + autokill("autokill", true), + attached("attached", true), + files("files"), + postend("postend"), + desc("desc") + {} + + /// pathname of executable + Mandatory<std::string> executable; + /** + * zero or more additional command-line arguments. Arguments are + * passed through as exactly as we can manage, whitespace and all. + * @note On Windows we manage this by implicitly double-quoting each + * argument while assembling the command line. + */ + Multiple<std::string> args; + /// current working directory, if need it changed + Optional<std::string> cwd; + /// implicitly kill child process on termination of parent, whether + /// voluntary or crash (default true) + Optional<bool> autokill; + /// implicitly kill process on destruction of LLProcess object + /// (default same as autokill) + /// + /// Originally, 'autokill' conflated two concepts: kill child process on + /// - destruction of its LLProcess object, and + /// - termination of parent process, voluntary or otherwise. + /// + /// It's useful to tease these apart. Some child processes are sent a + /// "clean up and terminate" message before the associated LLProcess + /// object is destroyed. A child process launched with attached=false + /// has an extra time window from the destruction of its LLProcess + /// until parent-process termination in which to perform its own + /// orderly shutdown, yet autokill=true still guarantees that we won't + /// accumulate orphan instances of such processes indefinitely. With + /// attached=true, if a child process cannot clean up between the + /// shutdown message and LLProcess destruction (presumably very soon + /// thereafter), it's forcibly killed anyway -- which can lead to + /// distressing user-visible crash indications. + /// + /// (The usefulness of attached=true with autokill=false is less + /// clear, but we don't prohibit that combination.) + Optional<bool> attached; + /** + * Up to three FileParam items: for child stdin, stdout, stderr. + * Passing two FileParam entries means default treatment for stderr, + * and so forth. + * + * @note LLInitParam::Block permits usage like this: + * @code + * LLProcess::Params params; + * ... + * params.files + * .add(LLProcess::FileParam()) // stdin + * .add(LLProcess::FileParam().type("pipe") // stdout + * .add(LLProcess::FileParam().type("file").name("error.log")); + * @endcode + * + * @note While it's theoretically plausible to pass additional open + * file handles to a child specifically written to expect them, our + * underlying implementation doesn't yet support that. + */ + Multiple<FileParam, AtMost<3> > files; + /** + * On child-process termination, if this LLProcess object still + * exists, post LLSD event to LLEventPump with specified name (default + * no event). Event contains at least: + * + * - "id" as obtained from getProcessID() + * - "desc" short string description of child (executable + pid) + * - "state" @c state enum value, from Status.mState + * - "data" if "state" is EXITED, exit code; if KILLED, on Posix, + * signal number + * - "string" English text describing "state" and "data" (e.g. "exited + * with code 0") + */ + Optional<std::string> postend; + /** + * Description of child process for logging purposes. It need not be + * unique; the logged description string will contain the PID as well. + * If this is omitted, a description will be derived from the + * executable name. + */ + Optional<std::string> desc; + }; + typedef LLSDParamAdapter<Params> LLSDOrParams; + + /** + * Factory accepting either plain LLSD::Map or Params block. + * MAY RETURN DEFAULT-CONSTRUCTED LLProcessPtr if params invalid! + */ + static LLProcessPtr create(const LLSDOrParams& params); + virtual ~LLProcess(); + + /// Is child process still running? + bool isRunning() const; + // static isRunning(LLProcessPtr), getStatus(LLProcessPtr), + // getStatusString(LLProcessPtr), kill(LLProcessPtr) handle the case in + // which the passed LLProcessPtr might be NULL (default-constructed). + static bool isRunning(const LLProcessPtr&); + + /** + * State of child process + */ + enum state + { + UNSTARTED, ///< initial value, invisible to consumer + RUNNING, ///< child process launched + EXITED, ///< child process terminated voluntarily + KILLED ///< child process terminated involuntarily + }; + + /** + * Status info + */ + struct Status + { + Status(): + mState(UNSTARTED), + mData(0) + {} + + state mState; ///< @see state + /** + * - for mState == EXITED: mData is exit() code + * - for mState == KILLED: mData is signal number (Posix) + * - otherwise: mData is undefined + */ + int mData; + }; + + /// Status query + Status getStatus() const; + static Status getStatus(const LLProcessPtr&); + /// English Status string query, for logging etc. + std::string getStatusString() const; + static std::string getStatusString(const std::string& desc, const LLProcessPtr&); + /// English Status string query for previously-captured Status + std::string getStatusString(const Status& status) const; + /// static English Status string query + static std::string getStatusString(const std::string& desc, const Status& status); + + // Attempt to kill the process -- returns true if the process is no longer running when it returns. + // Note that even if this returns false, the process may exit some time after it's called. + bool kill(const std::string& who=""); + static bool kill(const LLProcessPtr& p, const std::string& who=""); #if LL_WINDOWS - typedef int id; ///< as returned by getProcessID() - typedef HANDLE handle; ///< as returned by getProcessHandle() + typedef int id; ///< as returned by getProcessID() + typedef HANDLE handle; ///< as returned by getProcessHandle() #else - typedef pid_t id; - typedef pid_t handle; + typedef pid_t id; + typedef pid_t handle; #endif - /** - * Get an int-like id value. This is primarily intended for a human reader - * to differentiate processes. - */ - id getProcessID() const; - /** - * Get a "handle" of a kind that you might pass to platform-specific API - * functions to engage features not directly supported by LLProcess. - */ - handle getProcessHandle() const; - - /** - * Test if a process (@c handle obtained from getProcessHandle()) is still - * running. Return same nonzero @c handle value if still running, else - * zero, so you can test it like a bool. But if you want to update a - * stored variable as a side effect, you can write code like this: - * @code - * hchild = LLProcess::isRunning(hchild); - * @endcode - * @note This method is intended as a unit-test hook, not as the first of - * a whole set of operations supported on freestanding @c handle values. - * New functionality should be added as nonstatic members operating on - * the same data as getProcessHandle(). - * - * In particular, if child termination is detected by this static isRunning() - * rather than by nonstatic isRunning(), the LLProcess object won't be - * aware of the child's changed status and may encounter OS errors trying - * to obtain it. This static isRunning() is only intended for after the - * launching LLProcess object has been destroyed. - */ - static handle isRunning(handle, const std::string& desc=""); - - /// Provide symbolic access to child's file slots - enum FILESLOT { STDIN=0, STDOUT=1, STDERR=2, NSLOTS=3 }; - - /** - * For a pipe constructed with @a type "npipe", obtain the generated OS - * filesystem name for the specified pipe. Otherwise returns the empty - * string. @see LLProcess::FileParam::type - */ - std::string getPipeName(FILESLOT) const; - - /// base of ReadPipe, WritePipe - class LL_COMMON_API BasePipe - { - public: - virtual ~BasePipe() = 0; - - typedef std::size_t size_type; - static const size_type npos; - - /** - * Get accumulated buffer length. - * - * For WritePipe, is there still pending data to send to child? - * - * For ReadPipe, we often need to refrain from actually reading the - * std::istream returned by get_istream() until we've accumulated - * enough data to make it worthwhile. For instance, if we're expecting - * a number from the child, but the child happens to flush "12" before - * emitting "3\n", get_istream() >> myint could return 12 rather than - * 123! - */ - virtual size_type size() const = 0; - }; - - /// As returned by getWritePipe() or getOptWritePipe() - class WritePipe: public BasePipe - { - public: - /** - * Get ostream& on which to write to child's stdin. - * - * @usage - * @code - * myProcess->getWritePipe().get_ostream() << "Hello, child!" << std::endl; - * @endcode - */ - virtual std::ostream& get_ostream() = 0; - }; - - /// As returned by getReadPipe() or getOptReadPipe() - class ReadPipe: public BasePipe - { - public: - /** - * Get istream& on which to read from child's stdout or stderr. - * - * @usage - * @code - * std::string stuff; - * myProcess->getReadPipe().get_istream() >> stuff; - * @endcode - * - * You should be sure in advance that the ReadPipe in question can - * fill the request. @see getPump() - */ - virtual std::istream& get_istream() = 0; - - /** - * Like std::getline(get_istream(), line), but trims off trailing '\r' - * to make calling code less platform-sensitive. - */ - virtual std::string getline() = 0; - - /** - * Like get_istream().read(buffer, n), but returns std::string rather - * than requiring caller to construct a buffer, etc. - */ - virtual std::string read(size_type len) = 0; - - /** - * Peek at accumulated buffer data without consuming it. Optional - * parameters give you substr() functionality. - * - * @note You can discard buffer data using get_istream().ignore(n). - */ - virtual std::string peek(size_type offset=0, size_type len=npos) const = 0; - - /** - * Detect presence of a substring (or char) in accumulated buffer data - * without retrieving it. Optional offset allows you to search from - * specified position. - */ - template <typename SEEK> - bool contains(SEEK seek, size_type offset=0) const - { return find(seek, offset) != npos; } - - /** - * Search for a substring in accumulated buffer data without - * retrieving it. Returns size_type position at which found, or npos - * meaning not found. Optional offset allows you to search from - * specified position. - */ - virtual size_type find(const std::string& seek, size_type offset=0) const = 0; - - /** - * Search for a char in accumulated buffer data without retrieving it. - * Returns size_type position at which found, or npos meaning not - * found. Optional offset allows you to search from specified - * position. - */ - virtual size_type find(char seek, size_type offset=0) const = 0; - - /** - * Get LLEventPump& on which to listen for incoming data. The posted - * LLSD::Map event will contain: - * - * - "data" part of pending data; see setLimit() - * - "len" entire length of pending data, regardless of setLimit() - * - "slot" this ReadPipe's FILESLOT, e.g. LLProcess::STDOUT - * - "name" e.g. "stdout" - * - "desc" e.g. "SLPlugin (pid) stdout" - * - "eof" @c true means there no more data will arrive on this pipe, - * therefore no more events on this pump - * - * If the child sends "abc", and this ReadPipe posts "data"="abc", but - * you don't consume it by reading the std::istream returned by - * get_istream(), and the child next sends "def", ReadPipe will post - * "data"="abcdef". - */ - virtual LLEventPump& getPump() = 0; - - /** - * Set maximum length of buffer data that will be posted in the LLSD - * announcing arrival of new data from the child. If you call - * setLimit(5), and the child sends "abcdef", the LLSD event will - * contain "data"="abcde". However, you may still read the entire - * "abcdef" from get_istream(): this limit affects only the size of - * the data posted with the LLSD event. If you don't call this method, - * @em no data will be posted: the default is 0 bytes. - */ - virtual void setLimit(size_type limit) = 0; - - /** - * Query the current setLimit() limit. - */ - virtual size_type getLimit() const = 0; - }; - - /// Exception thrown by getWritePipe(), getReadPipe() if you didn't ask to - /// create a pipe at the corresponding FILESLOT. - struct NoPipe: public LLException - { - NoPipe(const std::string& what): LLException(what) {} - }; - - /** - * Get a reference to the (only) WritePipe for this LLProcess. @a slot, if - * specified, must be STDIN. Throws NoPipe if you did not request a "pipe" - * for child stdin. Use this method when you know how you created the - * LLProcess in hand. - */ - WritePipe& getWritePipe(FILESLOT slot=STDIN); - - /** - * Get a boost::optional<WritePipe&> to the (only) WritePipe for this - * LLProcess. @a slot, if specified, must be STDIN. The return value is - * empty if you did not request a "pipe" for child stdin. Use this method - * for inspecting an LLProcess you did not create. - */ - boost::optional<WritePipe&> getOptWritePipe(FILESLOT slot=STDIN); - - /** - * Get a reference to one of the ReadPipes for this LLProcess. @a slot, if - * specified, must be STDOUT or STDERR. Throws NoPipe if you did not - * request a "pipe" for child stdout or stderr. Use this method when you - * know how you created the LLProcess in hand. - */ - ReadPipe& getReadPipe(FILESLOT slot); - - /** - * Get a boost::optional<ReadPipe&> to one of the ReadPipes for this - * LLProcess. @a slot, if specified, must be STDOUT or STDERR. The return - * value is empty if you did not request a "pipe" for child stdout or - * stderr. Use this method for inspecting an LLProcess you did not create. - */ - boost::optional<ReadPipe&> getOptReadPipe(FILESLOT slot); - - /// little utilities that really should already be somewhere else in the - /// code base - static std::string basename(const std::string& path); - static std::string getline(std::istream&); + /** + * Get an int-like id value. This is primarily intended for a human reader + * to differentiate processes. + */ + id getProcessID() const; + /** + * Get a "handle" of a kind that you might pass to platform-specific API + * functions to engage features not directly supported by LLProcess. + */ + handle getProcessHandle() const; + + /** + * Test if a process (@c handle obtained from getProcessHandle()) is still + * running. Return same nonzero @c handle value if still running, else + * zero, so you can test it like a bool. But if you want to update a + * stored variable as a side effect, you can write code like this: + * @code + * hchild = LLProcess::isRunning(hchild); + * @endcode + * @note This method is intended as a unit-test hook, not as the first of + * a whole set of operations supported on freestanding @c handle values. + * New functionality should be added as nonstatic members operating on + * the same data as getProcessHandle(). + * + * In particular, if child termination is detected by this static isRunning() + * rather than by nonstatic isRunning(), the LLProcess object won't be + * aware of the child's changed status and may encounter OS errors trying + * to obtain it. This static isRunning() is only intended for after the + * launching LLProcess object has been destroyed. + */ + static handle isRunning(handle, const std::string& desc=""); + + /// Provide symbolic access to child's file slots + enum FILESLOT { STDIN=0, STDOUT=1, STDERR=2, NSLOTS=3 }; + + /** + * For a pipe constructed with @a type "npipe", obtain the generated OS + * filesystem name for the specified pipe. Otherwise returns the empty + * string. @see LLProcess::FileParam::type + */ + std::string getPipeName(FILESLOT) const; + + /// base of ReadPipe, WritePipe + class LL_COMMON_API BasePipe + { + public: + virtual ~BasePipe() = 0; + + typedef std::size_t size_type; + static const size_type npos; + + /** + * Get accumulated buffer length. + * + * For WritePipe, is there still pending data to send to child? + * + * For ReadPipe, we often need to refrain from actually reading the + * std::istream returned by get_istream() until we've accumulated + * enough data to make it worthwhile. For instance, if we're expecting + * a number from the child, but the child happens to flush "12" before + * emitting "3\n", get_istream() >> myint could return 12 rather than + * 123! + */ + virtual size_type size() const = 0; + }; + + /// As returned by getWritePipe() or getOptWritePipe() + class WritePipe: public BasePipe + { + public: + /** + * Get ostream& on which to write to child's stdin. + * + * @usage + * @code + * myProcess->getWritePipe().get_ostream() << "Hello, child!" << std::endl; + * @endcode + */ + virtual std::ostream& get_ostream() = 0; + }; + + /// As returned by getReadPipe() or getOptReadPipe() + class ReadPipe: public BasePipe + { + public: + /** + * Get istream& on which to read from child's stdout or stderr. + * + * @usage + * @code + * std::string stuff; + * myProcess->getReadPipe().get_istream() >> stuff; + * @endcode + * + * You should be sure in advance that the ReadPipe in question can + * fill the request. @see getPump() + */ + virtual std::istream& get_istream() = 0; + + /** + * Like std::getline(get_istream(), line), but trims off trailing '\r' + * to make calling code less platform-sensitive. + */ + virtual std::string getline() = 0; + + /** + * Like get_istream().read(buffer, n), but returns std::string rather + * than requiring caller to construct a buffer, etc. + */ + virtual std::string read(size_type len) = 0; + + /** + * Peek at accumulated buffer data without consuming it. Optional + * parameters give you substr() functionality. + * + * @note You can discard buffer data using get_istream().ignore(n). + */ + virtual std::string peek(size_type offset=0, size_type len=npos) const = 0; + + /** + * Detect presence of a substring (or char) in accumulated buffer data + * without retrieving it. Optional offset allows you to search from + * specified position. + */ + template <typename SEEK> + bool contains(SEEK seek, size_type offset=0) const + { return find(seek, offset) != npos; } + + /** + * Search for a substring in accumulated buffer data without + * retrieving it. Returns size_type position at which found, or npos + * meaning not found. Optional offset allows you to search from + * specified position. + */ + virtual size_type find(const std::string& seek, size_type offset=0) const = 0; + + /** + * Search for a char in accumulated buffer data without retrieving it. + * Returns size_type position at which found, or npos meaning not + * found. Optional offset allows you to search from specified + * position. + */ + virtual size_type find(char seek, size_type offset=0) const = 0; + + /** + * Get LLEventPump& on which to listen for incoming data. The posted + * LLSD::Map event will contain: + * + * - "data" part of pending data; see setLimit() + * - "len" entire length of pending data, regardless of setLimit() + * - "slot" this ReadPipe's FILESLOT, e.g. LLProcess::STDOUT + * - "name" e.g. "stdout" + * - "desc" e.g. "SLPlugin (pid) stdout" + * - "eof" @c true means there no more data will arrive on this pipe, + * therefore no more events on this pump + * + * If the child sends "abc", and this ReadPipe posts "data"="abc", but + * you don't consume it by reading the std::istream returned by + * get_istream(), and the child next sends "def", ReadPipe will post + * "data"="abcdef". + */ + virtual LLEventPump& getPump() = 0; + + /** + * Set maximum length of buffer data that will be posted in the LLSD + * announcing arrival of new data from the child. If you call + * setLimit(5), and the child sends "abcdef", the LLSD event will + * contain "data"="abcde". However, you may still read the entire + * "abcdef" from get_istream(): this limit affects only the size of + * the data posted with the LLSD event. If you don't call this method, + * @em no data will be posted: the default is 0 bytes. + */ + virtual void setLimit(size_type limit) = 0; + + /** + * Query the current setLimit() limit. + */ + virtual size_type getLimit() const = 0; + }; + + /// Exception thrown by getWritePipe(), getReadPipe() if you didn't ask to + /// create a pipe at the corresponding FILESLOT. + struct NoPipe: public LLException + { + NoPipe(const std::string& what): LLException(what) {} + }; + + /** + * Get a reference to the (only) WritePipe for this LLProcess. @a slot, if + * specified, must be STDIN. Throws NoPipe if you did not request a "pipe" + * for child stdin. Use this method when you know how you created the + * LLProcess in hand. + */ + WritePipe& getWritePipe(FILESLOT slot=STDIN); + + /** + * Get a boost::optional<WritePipe&> to the (only) WritePipe for this + * LLProcess. @a slot, if specified, must be STDIN. The return value is + * empty if you did not request a "pipe" for child stdin. Use this method + * for inspecting an LLProcess you did not create. + */ + boost::optional<WritePipe&> getOptWritePipe(FILESLOT slot=STDIN); + + /** + * Get a reference to one of the ReadPipes for this LLProcess. @a slot, if + * specified, must be STDOUT or STDERR. Throws NoPipe if you did not + * request a "pipe" for child stdout or stderr. Use this method when you + * know how you created the LLProcess in hand. + */ + ReadPipe& getReadPipe(FILESLOT slot); + + /** + * Get a boost::optional<ReadPipe&> to one of the ReadPipes for this + * LLProcess. @a slot, if specified, must be STDOUT or STDERR. The return + * value is empty if you did not request a "pipe" for child stdout or + * stderr. Use this method for inspecting an LLProcess you did not create. + */ + boost::optional<ReadPipe&> getOptReadPipe(FILESLOT slot); + + /// little utilities that really should already be somewhere else in the + /// code base + static std::string basename(const std::string& path); + static std::string getline(std::istream&); private: - /// constructor is private: use create() instead - LLProcess(const LLSDOrParams& params); - void autokill(); - // Classic-C-style APR callback - static void status_callback(int reason, void* data, int status); - // Object-oriented callback - void handle_status(int reason, int status); - // implementation for get[Opt][Read|Write]Pipe() - template <class PIPETYPE> - PIPETYPE& getPipe(FILESLOT slot); - template <class PIPETYPE> - boost::optional<PIPETYPE&> getOptPipe(FILESLOT slot); - template <class PIPETYPE> - PIPETYPE* getPipePtr(std::string& error, FILESLOT slot); - - std::string mDesc; - std::string mPostend; - apr_proc_t mProcess; - bool mAutokill, mAttached; - Status mStatus; - // explicitly want this ptr_vector to be able to store NULLs - typedef boost::ptr_vector< boost::nullable<BasePipe> > PipeVector; - PipeVector mPipes; + /// constructor is private: use create() instead + LLProcess(const LLSDOrParams& params); + void autokill(); + // Classic-C-style APR callback + static void status_callback(int reason, void* data, int status); + // Object-oriented callback + void handle_status(int reason, int status); + // implementation for get[Opt][Read|Write]Pipe() + template <class PIPETYPE> + PIPETYPE& getPipe(FILESLOT slot); + template <class PIPETYPE> + boost::optional<PIPETYPE&> getOptPipe(FILESLOT slot); + template <class PIPETYPE> + PIPETYPE* getPipePtr(std::string& error, FILESLOT slot); + + std::string mDesc; + std::string mPostend; + apr_proc_t mProcess; + bool mAutokill, mAttached; + Status mStatus; + // explicitly want this ptr_vector to be able to store NULLs + typedef boost::ptr_vector< boost::nullable<BasePipe> > PipeVector; + PipeVector mPipes; apr_pool_t* mPool; }; diff --git a/indra/llcommon/llprocessor.cpp b/indra/llcommon/llprocessor.cpp index 28f8bc2b93..9d53b9be35 100644 --- a/indra/llcommon/llprocessor.cpp +++ b/indra/llcommon/llprocessor.cpp @@ -1,25 +1,25 @@ -/** +/** * @file llprocessor.cpp * @brief Code to figure out the processor. Originally by Benjamin Jurke. * * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -34,199 +34,199 @@ //#include <memory> #if LL_WINDOWS -# include "llwin32headerslean.h" -# define _interlockedbittestandset _renamed_interlockedbittestandset -# define _interlockedbittestandreset _renamed_interlockedbittestandreset -# include <intrin.h> -# undef _interlockedbittestandset -# undef _interlockedbittestandreset +# include "llwin32headerslean.h" +# define _interlockedbittestandset _renamed_interlockedbittestandset +# define _interlockedbittestandreset _renamed_interlockedbittestandreset +# include <intrin.h> +# undef _interlockedbittestandset +# undef _interlockedbittestandreset #endif #include "llsd.h" class LLProcessorInfoImpl; // foward declaration for the mImpl; -namespace +namespace { - enum cpu_info - { - eBrandName = 0, - eFrequency, - eVendor, - eStepping, - eFamily, - eExtendedFamily, - eModel, - eExtendedModel, - eType, - eBrandID, - eFamilyName - }; - - - const char* cpu_info_names[] = - { - "Processor Name", - "Frequency", - "Vendor", - "Stepping", - "Family", - "Extended Family", - "Model", - "Extended Model", - "Type", - "Brand ID", - "Family Name" - }; - - enum cpu_config - { - eMaxID, - eMaxExtID, - eCLFLUSHCacheLineSize, - eAPICPhysicalID, - eCacheLineSize, - eL2Associativity, - eCacheSizeK, - eFeatureBits, - eExtFeatureBits - }; - - const char* cpu_config_names[] = - { - "Max Supported CPUID level", - "Max Supported Ext. CPUID level", - "CLFLUSH cache line size", - "APIC Physical ID", - "Cache Line Size", - "L2 Associativity", - "Cache Size", - "Feature Bits", - "Ext. Feature Bits" - }; - - - - // *NOTE:Mani - this contains the elements we reference directly and extensions beyond the first 32. - // The rest of the names are referenced by bit maks returned from cpuid. - enum cpu_features - { - eSSE_Ext=25, - eSSE2_Ext=26, - - eSSE3_Features=32, - eMONTIOR_MWAIT=33, - eCPLDebugStore=34, - eThermalMonitor2=35, - eAltivec=36, + enum cpu_info + { + eBrandName = 0, + eFrequency, + eVendor, + eStepping, + eFamily, + eExtendedFamily, + eModel, + eExtendedModel, + eType, + eBrandID, + eFamilyName + }; + + + const char* cpu_info_names[] = + { + "Processor Name", + "Frequency", + "Vendor", + "Stepping", + "Family", + "Extended Family", + "Model", + "Extended Model", + "Type", + "Brand ID", + "Family Name" + }; + + enum cpu_config + { + eMaxID, + eMaxExtID, + eCLFLUSHCacheLineSize, + eAPICPhysicalID, + eCacheLineSize, + eL2Associativity, + eCacheSizeK, + eFeatureBits, + eExtFeatureBits + }; + + const char* cpu_config_names[] = + { + "Max Supported CPUID level", + "Max Supported Ext. CPUID level", + "CLFLUSH cache line size", + "APIC Physical ID", + "Cache Line Size", + "L2 Associativity", + "Cache Size", + "Feature Bits", + "Ext. Feature Bits" + }; + + + + // *NOTE:Mani - this contains the elements we reference directly and extensions beyond the first 32. + // The rest of the names are referenced by bit maks returned from cpuid. + enum cpu_features + { + eSSE_Ext=25, + eSSE2_Ext=26, + + eSSE3_Features=32, + eMONTIOR_MWAIT=33, + eCPLDebugStore=34, + eThermalMonitor2=35, + eAltivec=36, eSSE3S_Features = 37, eSSE4_1_Features = 38, eSSE4_2_Features = 39, eSSE4a_Features = 40, - }; - - const char* cpu_feature_names[] = - { - "x87 FPU On Chip", - "Virtual-8086 Mode Enhancement", - "Debugging Extensions", - "Page Size Extensions", - "Time Stamp Counter", - "RDMSR and WRMSR Support", - "Physical Address Extensions", - "Machine Check Exception", - "CMPXCHG8B Instruction", - "APIC On Chip", - "Unknown1", - "SYSENTER and SYSEXIT", - "Memory Type Range Registers", - "PTE Global Bit", - "Machine Check Architecture", - "Conditional Move/Compare Instruction", - "Page Attribute Table", - "Page Size Extension", - "Processor Serial Number", - "CFLUSH Extension", - "Unknown2", - "Debug Store", - "Thermal Monitor and Clock Ctrl", - "MMX Technology", - "FXSAVE/FXRSTOR", - "SSE Extensions", - "SSE2 Extensions", - "Self Snoop", - "Hyper-threading Technology", - "Thermal Monitor", - "Unknown4", - "Pend. Brk. EN.", // 31 End of FeatureInfo bits - - "SSE3 New Instructions", // 32 - "MONITOR/MWAIT", - "CPL Qualified Debug Store", - "Thermal Monitor 2", - - "Altivec", + }; + + const char* cpu_feature_names[] = + { + "x87 FPU On Chip", + "Virtual-8086 Mode Enhancement", + "Debugging Extensions", + "Page Size Extensions", + "Time Stamp Counter", + "RDMSR and WRMSR Support", + "Physical Address Extensions", + "Machine Check Exception", + "CMPXCHG8B Instruction", + "APIC On Chip", + "Unknown1", + "SYSENTER and SYSEXIT", + "Memory Type Range Registers", + "PTE Global Bit", + "Machine Check Architecture", + "Conditional Move/Compare Instruction", + "Page Attribute Table", + "Page Size Extension", + "Processor Serial Number", + "CFLUSH Extension", + "Unknown2", + "Debug Store", + "Thermal Monitor and Clock Ctrl", + "MMX Technology", + "FXSAVE/FXRSTOR", + "SSE Extensions", + "SSE2 Extensions", + "Self Snoop", + "Hyper-threading Technology", + "Thermal Monitor", + "Unknown4", + "Pend. Brk. EN.", // 31 End of FeatureInfo bits + + "SSE3 New Instructions", // 32 + "MONITOR/MWAIT", + "CPL Qualified Debug Store", + "Thermal Monitor 2", + + "Altivec", "SSE3S Instructions", "SSE4.1 Instructions", "SSE4.2 Instructions", "SSE4a Instructions", - }; - - std::string intel_CPUFamilyName(int composed_family) - { - switch(composed_family) - { - case 3: return "Intel i386"; - case 4: return "Intel i486"; - case 5: return "Intel Pentium"; - case 6: return "Intel Pentium Pro/2/3, Core"; - case 7: return "Intel Itanium (IA-64)"; - case 0xF: return "Intel Pentium 4"; - case 0x10: return "Intel Itanium 2 (IA-64)"; - } - return STRINGIZE("Intel <unknown 0x" << std::hex << composed_family << ">"); - } - - std::string amd_CPUFamilyName(int composed_family) - { + }; + + std::string intel_CPUFamilyName(int composed_family) + { + switch(composed_family) + { + case 3: return "Intel i386"; + case 4: return "Intel i486"; + case 5: return "Intel Pentium"; + case 6: return "Intel Pentium Pro/2/3, Core"; + case 7: return "Intel Itanium (IA-64)"; + case 0xF: return "Intel Pentium 4"; + case 0x10: return "Intel Itanium 2 (IA-64)"; + } + return STRINGIZE("Intel <unknown 0x" << std::hex << composed_family << ">"); + } + + std::string amd_CPUFamilyName(int composed_family) + { // https://en.wikipedia.org/wiki/List_of_AMD_CPU_microarchitectures // https://developer.amd.com/resources/developer-guides-manuals/ - switch(composed_family) - { - case 4: return "AMD 80486/5x86"; - case 5: return "AMD K5/K6"; - case 6: return "AMD K7"; - case 0xF: return "AMD K8"; - case 0x10: return "AMD K8L"; - case 0x12: return "AMD K10"; - case 0x14: return "AMD Bobcat"; - case 0x15: return "AMD Bulldozer"; - case 0x16: return "AMD Jaguar"; - case 0x17: return "AMD Zen/Zen+/Zen2"; - case 0x18: return "AMD Hygon Dhyana"; - case 0x19: return "AMD Zen 3"; - } - return STRINGIZE("AMD <unknown 0x" << std::hex << composed_family << ">"); - } - - std::string compute_CPUFamilyName(const char* cpu_vendor, int family, int ext_family) - { - const char* intel_string = "GenuineIntel"; - const char* amd_string = "AuthenticAMD"; - if (LLStringUtil::startsWith(cpu_vendor, intel_string)) - { - U32 composed_family = family + ext_family; - return intel_CPUFamilyName(composed_family); - } - else if (LLStringUtil::startsWith(cpu_vendor, amd_string)) - { - U32 composed_family = (family == 0xF) - ? family + ext_family - : family; - return amd_CPUFamilyName(composed_family); - } - return STRINGIZE("Unrecognized CPU vendor <" << cpu_vendor << ">"); - } + switch(composed_family) + { + case 4: return "AMD 80486/5x86"; + case 5: return "AMD K5/K6"; + case 6: return "AMD K7"; + case 0xF: return "AMD K8"; + case 0x10: return "AMD K8L"; + case 0x12: return "AMD K10"; + case 0x14: return "AMD Bobcat"; + case 0x15: return "AMD Bulldozer"; + case 0x16: return "AMD Jaguar"; + case 0x17: return "AMD Zen/Zen+/Zen2"; + case 0x18: return "AMD Hygon Dhyana"; + case 0x19: return "AMD Zen 3"; + } + return STRINGIZE("AMD <unknown 0x" << std::hex << composed_family << ">"); + } + + std::string compute_CPUFamilyName(const char* cpu_vendor, int family, int ext_family) + { + const char* intel_string = "GenuineIntel"; + const char* amd_string = "AuthenticAMD"; + if (LLStringUtil::startsWith(cpu_vendor, intel_string)) + { + U32 composed_family = family + ext_family; + return intel_CPUFamilyName(composed_family); + } + else if (LLStringUtil::startsWith(cpu_vendor, amd_string)) + { + U32 composed_family = (family == 0xF) + ? family + ext_family + : family; + return amd_CPUFamilyName(composed_family); + } + return STRINGIZE("Unrecognized CPU vendor <" << cpu_vendor << ">"); + } } // end unnamed namespace @@ -235,28 +235,28 @@ namespace class LLProcessorInfoImpl { public: - LLProcessorInfoImpl() - { - mProcessorInfo["info"] = LLSD::emptyMap(); - mProcessorInfo["config"] = LLSD::emptyMap(); - mProcessorInfo["extension"] = LLSD::emptyMap(); - } - virtual ~LLProcessorInfoImpl() {} - - F64 getCPUFrequency() const - { - return getInfo(eFrequency, 0).asReal(); - } - - bool hasSSE() const - { - return hasExtension(cpu_feature_names[eSSE_Ext]); - } - - bool hasSSE2() const - { - return hasExtension(cpu_feature_names[eSSE2_Ext]); - } + LLProcessorInfoImpl() + { + mProcessorInfo["info"] = LLSD::emptyMap(); + mProcessorInfo["config"] = LLSD::emptyMap(); + mProcessorInfo["extension"] = LLSD::emptyMap(); + } + virtual ~LLProcessorInfoImpl() {} + + F64 getCPUFrequency() const + { + return getInfo(eFrequency, 0).asReal(); + } + + bool hasSSE() const + { + return hasExtension(cpu_feature_names[eSSE_Ext]); + } + + bool hasSSE2() const + { + return hasExtension(cpu_feature_names[eSSE2_Ext]); + } bool hasSSE3() const { @@ -283,229 +283,229 @@ public: return hasExtension(cpu_feature_names[eSSE4a_Features]); } - bool hasAltivec() const - { - return hasExtension("Altivec"); - } - - std::string getCPUFamilyName() const { return getInfo(eFamilyName, "Unset family").asString(); } - std::string getCPUBrandName() const { return getInfo(eBrandName, "Unset brand").asString(); } - - // This is virtual to support a different linux format. - // *NOTE:Mani - I didn't want to screw up server use of this data... - virtual std::string getCPUFeatureDescription() const - { - std::ostringstream out; - out << std::endl << std::endl; - out << "// CPU General Information" << std::endl; - out << "//////////////////////////" << std::endl; - out << "Processor Name: " << getCPUBrandName() << std::endl; - out << "Frequency: " << getCPUFrequency() << " MHz" << std::endl; - out << "Vendor: " << getInfo(eVendor, "Unset vendor").asString() << std::endl; - out << "Family: " << getCPUFamilyName() << " (" << getInfo(eFamily, 0) << ")" << std::endl; - out << "Extended family: " << getInfo(eExtendedFamily, 0) << std::endl; - out << "Model: " << getInfo(eModel, 0) << std::endl; - out << "Extended model: " << getInfo(eExtendedModel, 0) << std::endl; - out << "Type: " << getInfo(eType, 0) << std::endl; - out << "Brand ID: " << getInfo(eBrandID, 0) << std::endl; - out << std::endl; - out << "// CPU Configuration" << std::endl; - out << "//////////////////////////" << std::endl; - - // Iterate through the dictionary of configuration options. - LLSD configs = mProcessorInfo["config"]; - for(LLSD::map_const_iterator cfgItr = configs.beginMap(); cfgItr != configs.endMap(); ++cfgItr) - { - out << cfgItr->first << " = " << cfgItr->second << std::endl; - } - out << std::endl; - - out << "// CPU Extensions" << std::endl; - out << "//////////////////////////" << std::endl; - - for(LLSD::map_const_iterator itr = mProcessorInfo["extension"].beginMap(); itr != mProcessorInfo["extension"].endMap(); ++itr) - { - out << " " << itr->first << std::endl; - } - return out.str(); - } + bool hasAltivec() const + { + return hasExtension("Altivec"); + } + + std::string getCPUFamilyName() const { return getInfo(eFamilyName, "Unset family").asString(); } + std::string getCPUBrandName() const { return getInfo(eBrandName, "Unset brand").asString(); } + + // This is virtual to support a different linux format. + // *NOTE:Mani - I didn't want to screw up server use of this data... + virtual std::string getCPUFeatureDescription() const + { + std::ostringstream out; + out << std::endl << std::endl; + out << "// CPU General Information" << std::endl; + out << "//////////////////////////" << std::endl; + out << "Processor Name: " << getCPUBrandName() << std::endl; + out << "Frequency: " << getCPUFrequency() << " MHz" << std::endl; + out << "Vendor: " << getInfo(eVendor, "Unset vendor").asString() << std::endl; + out << "Family: " << getCPUFamilyName() << " (" << getInfo(eFamily, 0) << ")" << std::endl; + out << "Extended family: " << getInfo(eExtendedFamily, 0) << std::endl; + out << "Model: " << getInfo(eModel, 0) << std::endl; + out << "Extended model: " << getInfo(eExtendedModel, 0) << std::endl; + out << "Type: " << getInfo(eType, 0) << std::endl; + out << "Brand ID: " << getInfo(eBrandID, 0) << std::endl; + out << std::endl; + out << "// CPU Configuration" << std::endl; + out << "//////////////////////////" << std::endl; + + // Iterate through the dictionary of configuration options. + LLSD configs = mProcessorInfo["config"]; + for(LLSD::map_const_iterator cfgItr = configs.beginMap(); cfgItr != configs.endMap(); ++cfgItr) + { + out << cfgItr->first << " = " << cfgItr->second << std::endl; + } + out << std::endl; + + out << "// CPU Extensions" << std::endl; + out << "//////////////////////////" << std::endl; + + for(LLSD::map_const_iterator itr = mProcessorInfo["extension"].beginMap(); itr != mProcessorInfo["extension"].endMap(); ++itr) + { + out << " " << itr->first << std::endl; + } + return out.str(); + } protected: - void setInfo(cpu_info info_type, const LLSD& value) - { - setInfo(cpu_info_names[info_type], value); - } + void setInfo(cpu_info info_type, const LLSD& value) + { + setInfo(cpu_info_names[info_type], value); + } LLSD getInfo(cpu_info info_type, const LLSD& defaultVal) const - { - return getInfo(cpu_info_names[info_type], defaultVal); - } - - void setConfig(cpu_config config_type, const LLSD& value) - { - setConfig(cpu_config_names[config_type], value); - } - LLSD getConfig(cpu_config config_type, const LLSD& defaultVal) const - { - return getConfig(cpu_config_names[config_type], defaultVal); - } - - void setExtension(const std::string& name) { mProcessorInfo["extension"][name] = "true"; } - bool hasExtension(const std::string& name) const - { - return mProcessorInfo["extension"].has(name); - } + { + return getInfo(cpu_info_names[info_type], defaultVal); + } + + void setConfig(cpu_config config_type, const LLSD& value) + { + setConfig(cpu_config_names[config_type], value); + } + LLSD getConfig(cpu_config config_type, const LLSD& defaultVal) const + { + return getConfig(cpu_config_names[config_type], defaultVal); + } + + void setExtension(const std::string& name) { mProcessorInfo["extension"][name] = "true"; } + bool hasExtension(const std::string& name) const + { + return mProcessorInfo["extension"].has(name); + } private: - void setInfo(const std::string& name, const LLSD& value) { mProcessorInfo["info"][name]=value; } - LLSD getInfo(const std::string& name, const LLSD& defaultVal) const - { - if(mProcessorInfo["info"].has(name)) - { - return mProcessorInfo["info"][name]; - } - return defaultVal; - } - void setConfig(const std::string& name, const LLSD& value) { mProcessorInfo["config"][name]=value; } - LLSD getConfig(const std::string& name, const LLSD& defaultVal) const - { - LLSD r = mProcessorInfo["config"].get(name); - return r.isDefined() ? r : defaultVal; - } + void setInfo(const std::string& name, const LLSD& value) { mProcessorInfo["info"][name]=value; } + LLSD getInfo(const std::string& name, const LLSD& defaultVal) const + { + if(mProcessorInfo["info"].has(name)) + { + return mProcessorInfo["info"][name]; + } + return defaultVal; + } + void setConfig(const std::string& name, const LLSD& value) { mProcessorInfo["config"][name]=value; } + LLSD getConfig(const std::string& name, const LLSD& defaultVal) const + { + LLSD r = mProcessorInfo["config"].get(name); + return r.isDefined() ? r : defaultVal; + } private: - LLSD mProcessorInfo; + LLSD mProcessorInfo; }; #ifdef LL_MSVC -// LL_MSVC and not LLWINDOWS because some of the following code +// LL_MSVC and not LLWINDOWS because some of the following code // uses the MSVC compiler intrinsics __cpuid() and __rdtsc(). // Delays for the specified amount of milliseconds static void _Delay(unsigned int ms) { - LARGE_INTEGER freq, c1, c2; - __int64 x; - - // Get High-Res Timer frequency - if (!QueryPerformanceFrequency(&freq)) - return; - - // Convert ms to High-Res Timer value - x = freq.QuadPart/1000*ms; - - // Get first snapshot of High-Res Timer value - QueryPerformanceCounter(&c1); - do - { - // Get second snapshot - QueryPerformanceCounter(&c2); - }while(c2.QuadPart-c1.QuadPart < x); - // Loop while (second-first < x) + LARGE_INTEGER freq, c1, c2; + __int64 x; + + // Get High-Res Timer frequency + if (!QueryPerformanceFrequency(&freq)) + return; + + // Convert ms to High-Res Timer value + x = freq.QuadPart/1000*ms; + + // Get first snapshot of High-Res Timer value + QueryPerformanceCounter(&c1); + do + { + // Get second snapshot + QueryPerformanceCounter(&c2); + }while(c2.QuadPart-c1.QuadPart < x); + // Loop while (second-first < x) } static F64 calculate_cpu_frequency(U32 measure_msecs) { - if(measure_msecs == 0) - { - return 0; - } - - // After that we declare some vars and check the frequency of the high - // resolution timer for the measure process. - // If there"s no high-res timer, we exit. - unsigned __int64 starttime, endtime, timedif, freq, start, end, dif; - if (!QueryPerformanceFrequency((LARGE_INTEGER *) &freq)) - { - return 0; - } - - // Now we can init the measure process. We set the process and thread priority - // to the highest available level (Realtime priority). Also we focus the - // first processor in the multiprocessor system. - HANDLE hProcess = GetCurrentProcess(); - HANDLE hThread = GetCurrentThread(); - unsigned long dwCurPriorityClass = GetPriorityClass(hProcess); - int iCurThreadPriority = GetThreadPriority(hThread); - DWORD_PTR dwProcessMask, dwSystemMask, dwNewMask = 1; - GetProcessAffinityMask(hProcess, &dwProcessMask, &dwSystemMask); - - SetPriorityClass(hProcess, REALTIME_PRIORITY_CLASS); - SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL); - SetProcessAffinityMask(hProcess, dwNewMask); - - //// Now we call a CPUID to ensure, that all other prior called functions are - //// completed now (serialization) - //__asm cpuid - int cpu_info[4] = {-1}; - __cpuid(cpu_info, 0); - - // We ask the high-res timer for the start time - QueryPerformanceCounter((LARGE_INTEGER *) &starttime); - - // Then we get the current cpu clock and store it - start = __rdtsc(); - - // Now we wart for some msecs - _Delay(measure_msecs); - // Sleep(uiMeasureMSecs); - - // We ask for the end time - QueryPerformanceCounter((LARGE_INTEGER *) &endtime); - - // And also for the end cpu clock - end = __rdtsc(); - - // Now we can restore the default process and thread priorities - SetProcessAffinityMask(hProcess, dwProcessMask); - SetThreadPriority(hThread, iCurThreadPriority); - SetPriorityClass(hProcess, dwCurPriorityClass); - - // Then we calculate the time and clock differences - dif = end - start; - timedif = endtime - starttime; - - // And finally the frequency is the clock difference divided by the time - // difference. - F64 frequency = (F64)dif / (((F64)timedif) / freq); - - // At last we just return the frequency that is also stored in the call - // member var uqwFrequency - converted to MHz - return frequency / (F64)1000000; + if(measure_msecs == 0) + { + return 0; + } + + // After that we declare some vars and check the frequency of the high + // resolution timer for the measure process. + // If there"s no high-res timer, we exit. + unsigned __int64 starttime, endtime, timedif, freq, start, end, dif; + if (!QueryPerformanceFrequency((LARGE_INTEGER *) &freq)) + { + return 0; + } + + // Now we can init the measure process. We set the process and thread priority + // to the highest available level (Realtime priority). Also we focus the + // first processor in the multiprocessor system. + HANDLE hProcess = GetCurrentProcess(); + HANDLE hThread = GetCurrentThread(); + unsigned long dwCurPriorityClass = GetPriorityClass(hProcess); + int iCurThreadPriority = GetThreadPriority(hThread); + DWORD_PTR dwProcessMask, dwSystemMask, dwNewMask = 1; + GetProcessAffinityMask(hProcess, &dwProcessMask, &dwSystemMask); + + SetPriorityClass(hProcess, REALTIME_PRIORITY_CLASS); + SetThreadPriority(hThread, THREAD_PRIORITY_TIME_CRITICAL); + SetProcessAffinityMask(hProcess, dwNewMask); + + //// Now we call a CPUID to ensure, that all other prior called functions are + //// completed now (serialization) + //__asm cpuid + int cpu_info[4] = {-1}; + __cpuid(cpu_info, 0); + + // We ask the high-res timer for the start time + QueryPerformanceCounter((LARGE_INTEGER *) &starttime); + + // Then we get the current cpu clock and store it + start = __rdtsc(); + + // Now we wart for some msecs + _Delay(measure_msecs); + // Sleep(uiMeasureMSecs); + + // We ask for the end time + QueryPerformanceCounter((LARGE_INTEGER *) &endtime); + + // And also for the end cpu clock + end = __rdtsc(); + + // Now we can restore the default process and thread priorities + SetProcessAffinityMask(hProcess, dwProcessMask); + SetThreadPriority(hThread, iCurThreadPriority); + SetPriorityClass(hProcess, dwCurPriorityClass); + + // Then we calculate the time and clock differences + dif = end - start; + timedif = endtime - starttime; + + // And finally the frequency is the clock difference divided by the time + // difference. + F64 frequency = (F64)dif / (((F64)timedif) / freq); + + // At last we just return the frequency that is also stored in the call + // member var uqwFrequency - converted to MHz + return frequency / (F64)1000000; } // Windows implementation class LLProcessorInfoWindowsImpl : public LLProcessorInfoImpl { public: - LLProcessorInfoWindowsImpl() - { - getCPUIDInfo(); - setInfo(eFrequency, calculate_cpu_frequency(50)); - } + LLProcessorInfoWindowsImpl() + { + getCPUIDInfo(); + setInfo(eFrequency, calculate_cpu_frequency(50)); + } private: - void getCPUIDInfo() - { - // http://msdn.microsoft.com/en-us/library/hskdteyh(VS.80).aspx - - // __cpuid with an InfoType argument of 0 returns the number of - // valid Ids in cpu_info[0] and the CPU identification string in - // the other three array elements. The CPU identification string is - // not in linear order. The code below arranges the information - // in a human readable form. - int cpu_info[4] = {-1}; - __cpuid(cpu_info, 0); - unsigned int ids = (unsigned int)cpu_info[0]; - setConfig(eMaxID, (S32)ids); - - char cpu_vendor[0x20]; - memset(cpu_vendor, 0, sizeof(cpu_vendor)); - *((int*)cpu_vendor) = cpu_info[1]; - *((int*)(cpu_vendor+4)) = cpu_info[3]; - *((int*)(cpu_vendor+8)) = cpu_info[2]; - setInfo(eVendor, cpu_vendor); + void getCPUIDInfo() + { + // http://msdn.microsoft.com/en-us/library/hskdteyh(VS.80).aspx + + // __cpuid with an InfoType argument of 0 returns the number of + // valid Ids in cpu_info[0] and the CPU identification string in + // the other three array elements. The CPU identification string is + // not in linear order. The code below arranges the information + // in a human readable form. + int cpu_info[4] = {-1}; + __cpuid(cpu_info, 0); + unsigned int ids = (unsigned int)cpu_info[0]; + setConfig(eMaxID, (S32)ids); + + char cpu_vendor[0x20]; + memset(cpu_vendor, 0, sizeof(cpu_vendor)); + *((int*)cpu_vendor) = cpu_info[1]; + *((int*)(cpu_vendor+4)) = cpu_info[3]; + *((int*)(cpu_vendor+8)) = cpu_info[2]; + setInfo(eVendor, cpu_vendor); std::string cmp_vendor(cpu_vendor); bool is_amd = false; if (cmp_vendor == "AuthenticAMD") @@ -513,49 +513,49 @@ private: is_amd = true; } - // Get the information associated with each valid Id - for(unsigned int i=0; i<=ids; ++i) - { - __cpuid(cpu_info, i); - - // Interpret CPU feature information. - if (i == 1) - { - setInfo(eStepping, cpu_info[0] & 0xf); - setInfo(eModel, (cpu_info[0] >> 4) & 0xf); - int family = (cpu_info[0] >> 8) & 0xf; - setInfo(eFamily, family); - setInfo(eType, (cpu_info[0] >> 12) & 0x3); - setInfo(eExtendedModel, (cpu_info[0] >> 16) & 0xf); - int ext_family = (cpu_info[0] >> 20) & 0xff; - setInfo(eExtendedFamily, ext_family); - setInfo(eBrandID, cpu_info[1] & 0xff); - - setInfo(eFamilyName, compute_CPUFamilyName(cpu_vendor, family, ext_family)); - - setConfig(eCLFLUSHCacheLineSize, ((cpu_info[1] >> 8) & 0xff) * 8); - setConfig(eAPICPhysicalID, (cpu_info[1] >> 24) & 0xff); - - if(cpu_info[2] & 0x1) - { - setExtension(cpu_feature_names[eSSE3_Features]); - } - - if(cpu_info[2] & 0x8) - { + // Get the information associated with each valid Id + for(unsigned int i=0; i<=ids; ++i) + { + __cpuid(cpu_info, i); + + // Interpret CPU feature information. + if (i == 1) + { + setInfo(eStepping, cpu_info[0] & 0xf); + setInfo(eModel, (cpu_info[0] >> 4) & 0xf); + int family = (cpu_info[0] >> 8) & 0xf; + setInfo(eFamily, family); + setInfo(eType, (cpu_info[0] >> 12) & 0x3); + setInfo(eExtendedModel, (cpu_info[0] >> 16) & 0xf); + int ext_family = (cpu_info[0] >> 20) & 0xff; + setInfo(eExtendedFamily, ext_family); + setInfo(eBrandID, cpu_info[1] & 0xff); + + setInfo(eFamilyName, compute_CPUFamilyName(cpu_vendor, family, ext_family)); + + setConfig(eCLFLUSHCacheLineSize, ((cpu_info[1] >> 8) & 0xff) * 8); + setConfig(eAPICPhysicalID, (cpu_info[1] >> 24) & 0xff); + + if(cpu_info[2] & 0x1) + { + setExtension(cpu_feature_names[eSSE3_Features]); + } + + if(cpu_info[2] & 0x8) + { // intel specific SSE3 suplements - setExtension(cpu_feature_names[eMONTIOR_MWAIT]); - } - - if(cpu_info[2] & 0x10) - { - setExtension(cpu_feature_names[eCPLDebugStore]); - } - - if(cpu_info[2] & 0x100) - { - setExtension(cpu_feature_names[eThermalMonitor2]); - } + setExtension(cpu_feature_names[eMONTIOR_MWAIT]); + } + + if(cpu_info[2] & 0x10) + { + setExtension(cpu_feature_names[eCPLDebugStore]); + } + + if(cpu_info[2] & 0x100) + { + setExtension(cpu_feature_names[eThermalMonitor2]); + } if (cpu_info[2] & 0x200) { @@ -572,32 +572,32 @@ private: setExtension(cpu_feature_names[eSSE4_2_Features]); } - unsigned int feature_info = (unsigned int) cpu_info[3]; - for(unsigned int index = 0, bit = 1; index < eSSE3_Features; ++index, bit <<= 1) - { - if(feature_info & bit) - { - setExtension(cpu_feature_names[index]); - } - } - } - } - - // Calling __cpuid with 0x80000000 as the InfoType argument - // gets the number of valid extended IDs. - __cpuid(cpu_info, 0x80000000); - unsigned int ext_ids = cpu_info[0]; - setConfig(eMaxExtID, 0); - - char cpu_brand_string[0x40]; - memset(cpu_brand_string, 0, sizeof(cpu_brand_string)); - - // Get the information associated with each extended ID. - for(unsigned int i=0x80000000; i<=ext_ids; ++i) - { - __cpuid(cpu_info, i); - - // Interpret CPU brand string and cache information. + unsigned int feature_info = (unsigned int) cpu_info[3]; + for(unsigned int index = 0, bit = 1; index < eSSE3_Features; ++index, bit <<= 1) + { + if(feature_info & bit) + { + setExtension(cpu_feature_names[index]); + } + } + } + } + + // Calling __cpuid with 0x80000000 as the InfoType argument + // gets the number of valid extended IDs. + __cpuid(cpu_info, 0x80000000); + unsigned int ext_ids = cpu_info[0]; + setConfig(eMaxExtID, 0); + + char cpu_brand_string[0x40]; + memset(cpu_brand_string, 0, sizeof(cpu_brand_string)); + + // Get the information associated with each extended ID. + for(unsigned int i=0x80000000; i<=ext_ids; ++i) + { + __cpuid(cpu_info, i); + + // Interpret CPU brand string and cache information. if (i == 0x80000001) { if (is_amd) @@ -609,21 +609,21 @@ private: { memcpy(cpu_brand_string, cpu_info, sizeof(cpu_info)); } - else if (i == 0x80000003) - memcpy(cpu_brand_string + 16, cpu_info, sizeof(cpu_info)); - else if (i == 0x80000004) - { - memcpy(cpu_brand_string + 32, cpu_info, sizeof(cpu_info)); - setInfo(eBrandName, cpu_brand_string); - } - else if (i == 0x80000006) - { - setConfig(eCacheLineSize, cpu_info[2] & 0xff); - setConfig(eL2Associativity, (cpu_info[2] >> 12) & 0xf); - setConfig(eCacheSizeK, (cpu_info[2] >> 16) & 0xffff); - } - } - } + else if (i == 0x80000003) + memcpy(cpu_brand_string + 16, cpu_info, sizeof(cpu_info)); + else if (i == 0x80000004) + { + memcpy(cpu_brand_string + 32, cpu_info, sizeof(cpu_info)); + setInfo(eBrandName, cpu_brand_string); + } + else if (i == 0x80000006) + { + setConfig(eCacheLineSize, cpu_info[2] & 0xff); + setConfig(eL2Associativity, (cpu_info[2] >> 12) & 0xf); + setConfig(eCacheSizeK, (cpu_info[2] >> 16) & 0xffff); + } + } + } }; #elif LL_DARWIN @@ -634,126 +634,126 @@ private: class LLProcessorInfoDarwinImpl : public LLProcessorInfoImpl { public: - LLProcessorInfoDarwinImpl() - { - getCPUIDInfo(); - uint64_t frequency = getSysctlInt64("hw.cpufrequency"); - setInfo(eFrequency, (F64)frequency / (F64)1000000); - } + LLProcessorInfoDarwinImpl() + { + getCPUIDInfo(); + uint64_t frequency = getSysctlInt64("hw.cpufrequency"); + setInfo(eFrequency, (F64)frequency / (F64)1000000); + } - virtual ~LLProcessorInfoDarwinImpl() {} + virtual ~LLProcessorInfoDarwinImpl() {} private: - int getSysctlInt(const char* name) - { - int result = 0; - size_t len = sizeof(int); - int error = sysctlbyname(name, (void*)&result, &len, NULL, 0); - return error == -1 ? 0 : result; - } - - uint64_t getSysctlInt64(const char* name) - { - uint64_t value = 0; - size_t size = sizeof(value); - int result = sysctlbyname(name, (void*)&value, &size, NULL, 0); - if ( result == 0 ) - { - if ( size == sizeof( uint64_t ) ) - ; - else if ( size == sizeof( uint32_t ) ) - value = (uint64_t)(( uint32_t *)&value); - else if ( size == sizeof( uint16_t ) ) - value = (uint64_t)(( uint16_t *)&value); - else if ( size == sizeof( uint8_t ) ) - value = (uint64_t)(( uint8_t *)&value); - else - { - LL_WARNS() << "Unknown type returned from sysctl" << LL_ENDL; - } - } - - return result == -1 ? 0 : value; - } - - void getCPUIDInfo() - { - size_t len = 0; - - char cpu_brand_string[0x40]; - len = sizeof(cpu_brand_string); - memset(cpu_brand_string, 0, len); - sysctlbyname("machdep.cpu.brand_string", (void*)cpu_brand_string, &len, NULL, 0); - cpu_brand_string[0x3f] = 0; - setInfo(eBrandName, cpu_brand_string); - - char cpu_vendor[0x20]; - len = sizeof(cpu_vendor); - memset(cpu_vendor, 0, len); - sysctlbyname("machdep.cpu.vendor", (void*)cpu_vendor, &len, NULL, 0); - cpu_vendor[0x1f] = 0; - setInfo(eVendor, cpu_vendor); - - setInfo(eStepping, getSysctlInt("machdep.cpu.stepping")); - setInfo(eModel, getSysctlInt("machdep.cpu.model")); - int family = getSysctlInt("machdep.cpu.family"); - int ext_family = getSysctlInt("machdep.cpu.extfamily"); - setInfo(eFamily, family); - setInfo(eExtendedFamily, ext_family); - setInfo(eFamilyName, compute_CPUFamilyName(cpu_vendor, family, ext_family)); - setInfo(eExtendedModel, getSysctlInt("machdep.cpu.extmodel")); - setInfo(eBrandID, getSysctlInt("machdep.cpu.brand")); - setInfo(eType, 0); // ? where to find this? - - //setConfig(eCLFLUSHCacheLineSize, ((cpu_info[1] >> 8) & 0xff) * 8); - //setConfig(eAPICPhysicalID, (cpu_info[1] >> 24) & 0xff); - setConfig(eCacheLineSize, getSysctlInt("machdep.cpu.cache.linesize")); - setConfig(eL2Associativity, getSysctlInt("machdep.cpu.cache.L2_associativity")); - setConfig(eCacheSizeK, getSysctlInt("machdep.cpu.cache.size")); - - uint64_t feature_info = getSysctlInt64("machdep.cpu.feature_bits"); - S32 *feature_infos = (S32*)(&feature_info); - - setConfig(eFeatureBits, feature_infos[0]); - - for(unsigned int index = 0, bit = 1; index < eSSE3_Features; ++index, bit <<= 1) - { - if(feature_info & bit) - { - setExtension(cpu_feature_names[index]); - } - } - - // *NOTE:Mani - I didn't find any docs that assure me that machdep.cpu.feature_bits will always be - // The feature bits I think it is. Here's a test: + int getSysctlInt(const char* name) + { + int result = 0; + size_t len = sizeof(int); + int error = sysctlbyname(name, (void*)&result, &len, NULL, 0); + return error == -1 ? 0 : result; + } + + uint64_t getSysctlInt64(const char* name) + { + uint64_t value = 0; + size_t size = sizeof(value); + int result = sysctlbyname(name, (void*)&value, &size, NULL, 0); + if ( result == 0 ) + { + if ( size == sizeof( uint64_t ) ) + ; + else if ( size == sizeof( uint32_t ) ) + value = (uint64_t)(( uint32_t *)&value); + else if ( size == sizeof( uint16_t ) ) + value = (uint64_t)(( uint16_t *)&value); + else if ( size == sizeof( uint8_t ) ) + value = (uint64_t)(( uint8_t *)&value); + else + { + LL_WARNS() << "Unknown type returned from sysctl" << LL_ENDL; + } + } + + return result == -1 ? 0 : value; + } + + void getCPUIDInfo() + { + size_t len = 0; + + char cpu_brand_string[0x40]; + len = sizeof(cpu_brand_string); + memset(cpu_brand_string, 0, len); + sysctlbyname("machdep.cpu.brand_string", (void*)cpu_brand_string, &len, NULL, 0); + cpu_brand_string[0x3f] = 0; + setInfo(eBrandName, cpu_brand_string); + + char cpu_vendor[0x20]; + len = sizeof(cpu_vendor); + memset(cpu_vendor, 0, len); + sysctlbyname("machdep.cpu.vendor", (void*)cpu_vendor, &len, NULL, 0); + cpu_vendor[0x1f] = 0; + setInfo(eVendor, cpu_vendor); + + setInfo(eStepping, getSysctlInt("machdep.cpu.stepping")); + setInfo(eModel, getSysctlInt("machdep.cpu.model")); + int family = getSysctlInt("machdep.cpu.family"); + int ext_family = getSysctlInt("machdep.cpu.extfamily"); + setInfo(eFamily, family); + setInfo(eExtendedFamily, ext_family); + setInfo(eFamilyName, compute_CPUFamilyName(cpu_vendor, family, ext_family)); + setInfo(eExtendedModel, getSysctlInt("machdep.cpu.extmodel")); + setInfo(eBrandID, getSysctlInt("machdep.cpu.brand")); + setInfo(eType, 0); // ? where to find this? + + //setConfig(eCLFLUSHCacheLineSize, ((cpu_info[1] >> 8) & 0xff) * 8); + //setConfig(eAPICPhysicalID, (cpu_info[1] >> 24) & 0xff); + setConfig(eCacheLineSize, getSysctlInt("machdep.cpu.cache.linesize")); + setConfig(eL2Associativity, getSysctlInt("machdep.cpu.cache.L2_associativity")); + setConfig(eCacheSizeK, getSysctlInt("machdep.cpu.cache.size")); + + uint64_t feature_info = getSysctlInt64("machdep.cpu.feature_bits"); + S32 *feature_infos = (S32*)(&feature_info); + + setConfig(eFeatureBits, feature_infos[0]); + + for(unsigned int index = 0, bit = 1; index < eSSE3_Features; ++index, bit <<= 1) + { + if(feature_info & bit) + { + setExtension(cpu_feature_names[index]); + } + } + + // *NOTE:Mani - I didn't find any docs that assure me that machdep.cpu.feature_bits will always be + // The feature bits I think it is. Here's a test: #ifndef LL_RELEASE_FOR_DOWNLOAD - #if defined(__i386__) && defined(__PIC__) - /* %ebx may be the PIC register. */ - #define __cpuid(level, a, b, c, d) \ - __asm__ ("xchgl\t%%ebx, %1\n\t" \ - "cpuid\n\t" \ - "xchgl\t%%ebx, %1\n\t" \ - : "=a" (a), "=r" (b), "=c" (c), "=d" (d) \ - : "0" (level)) - #else - #define __cpuid(level, a, b, c, d) \ - __asm__ ("cpuid\n\t" \ - : "=a" (a), "=b" (b), "=c" (c), "=d" (d) \ - : "0" (level)) - #endif - - unsigned int eax, ebx, ecx, edx; - __cpuid(0x1, eax, ebx, ecx, edx); - if(feature_infos[0] != (S32)edx) - { - LL_WARNS() << "machdep.cpu.feature_bits doesn't match expected cpuid result!" << LL_ENDL; - } -#endif // LL_RELEASE_FOR_DOWNLOAD - - - uint64_t ext_feature_info = getSysctlInt64("machdep.cpu.extfeature_bits"); - S32 *ext_feature_infos = (S32*)(&ext_feature_info); - setConfig(eExtFeatureBits, ext_feature_infos[0]); + #if defined(__i386__) && defined(__PIC__) + /* %ebx may be the PIC register. */ + #define __cpuid(level, a, b, c, d) \ + __asm__ ("xchgl\t%%ebx, %1\n\t" \ + "cpuid\n\t" \ + "xchgl\t%%ebx, %1\n\t" \ + : "=a" (a), "=r" (b), "=c" (c), "=d" (d) \ + : "0" (level)) + #else + #define __cpuid(level, a, b, c, d) \ + __asm__ ("cpuid\n\t" \ + : "=a" (a), "=b" (b), "=c" (c), "=d" (d) \ + : "0" (level)) + #endif + + unsigned int eax, ebx, ecx, edx; + __cpuid(0x1, eax, ebx, ecx, edx); + if(feature_infos[0] != (S32)edx) + { + LL_WARNS() << "machdep.cpu.feature_bits doesn't match expected cpuid result!" << LL_ENDL; + } +#endif // LL_RELEASE_FOR_DOWNLOAD + + + uint64_t ext_feature_info = getSysctlInt64("machdep.cpu.extfeature_bits"); + S32 *ext_feature_infos = (S32*)(&ext_feature_info); + setConfig(eExtFeatureBits, ext_feature_infos[0]); char cpu_features[1024]; @@ -789,7 +789,7 @@ private: // Not supposed to happen? setExtension(cpu_feature_names[eSSE4a_Features]); } - } + } }; #elif LL_LINUX @@ -798,107 +798,107 @@ const char CPUINFO_FILE[] = "/proc/cpuinfo"; class LLProcessorInfoLinuxImpl : public LLProcessorInfoImpl { public: - LLProcessorInfoLinuxImpl() - { - get_proc_cpuinfo(); - } + LLProcessorInfoLinuxImpl() + { + get_proc_cpuinfo(); + } - virtual ~LLProcessorInfoLinuxImpl() {} + virtual ~LLProcessorInfoLinuxImpl() {} private: - void get_proc_cpuinfo() - { - std::map< std::string, std::string > cpuinfo; - LLFILE* cpuinfo_fp = LLFile::fopen(CPUINFO_FILE, "rb"); - if(cpuinfo_fp) - { - char line[MAX_STRING]; - memset(line, 0, MAX_STRING); - while(fgets(line, MAX_STRING, cpuinfo_fp)) - { - // /proc/cpuinfo on Linux looks like: - // name\t*: value\n - char* tabspot = strchr( line, '\t' ); - if (tabspot == NULL) - continue; - char* colspot = strchr( tabspot, ':' ); - if (colspot == NULL) - continue; - char* spacespot = strchr( colspot, ' ' ); - if (spacespot == NULL) - continue; - char* nlspot = strchr( line, '\n' ); - if (nlspot == NULL) - nlspot = line + strlen( line ); // Fallback to terminating NUL - std::string linename( line, tabspot ); - std::string llinename(linename); - LLStringUtil::toLower(llinename); - std::string lineval( spacespot + 1, nlspot ); - cpuinfo[ llinename ] = lineval; - } - fclose(cpuinfo_fp); - } + void get_proc_cpuinfo() + { + std::map< std::string, std::string > cpuinfo; + LLFILE* cpuinfo_fp = LLFile::fopen(CPUINFO_FILE, "rb"); + if(cpuinfo_fp) + { + char line[MAX_STRING]; + memset(line, 0, MAX_STRING); + while(fgets(line, MAX_STRING, cpuinfo_fp)) + { + // /proc/cpuinfo on Linux looks like: + // name\t*: value\n + char* tabspot = strchr( line, '\t' ); + if (tabspot == NULL) + continue; + char* colspot = strchr( tabspot, ':' ); + if (colspot == NULL) + continue; + char* spacespot = strchr( colspot, ' ' ); + if (spacespot == NULL) + continue; + char* nlspot = strchr( line, '\n' ); + if (nlspot == NULL) + nlspot = line + strlen( line ); // Fallback to terminating NUL + std::string linename( line, tabspot ); + std::string llinename(linename); + LLStringUtil::toLower(llinename); + std::string lineval( spacespot + 1, nlspot ); + cpuinfo[ llinename ] = lineval; + } + fclose(cpuinfo_fp); + } # if LL_X86 // *NOTE:Mani - eww, macros! srry. #define LLPI_SET_INFO_STRING(llpi_id, cpuinfo_id) \ - if (!cpuinfo[cpuinfo_id].empty()) \ - { setInfo(llpi_id, cpuinfo[cpuinfo_id]);} + if (!cpuinfo[cpuinfo_id].empty()) \ + { setInfo(llpi_id, cpuinfo[cpuinfo_id]);} #define LLPI_SET_INFO_INT(llpi_id, cpuinfo_id) \ - {\ - S32 result; \ - if (!cpuinfo[cpuinfo_id].empty() \ - && LLStringUtil::convertToS32(cpuinfo[cpuinfo_id], result)) \ - { setInfo(llpi_id, result);} \ - } - - F64 mhz; - if (LLStringUtil::convertToF64(cpuinfo["cpu mhz"], mhz) - && 200.0 < mhz && mhz < 10000.0) - { - setInfo(eFrequency,(F64)(mhz)); - } - - LLPI_SET_INFO_STRING(eBrandName, "model name"); - LLPI_SET_INFO_STRING(eVendor, "vendor_id"); - - LLPI_SET_INFO_INT(eStepping, "stepping"); - LLPI_SET_INFO_INT(eModel, "model"); - - - S32 family; - if (!cpuinfo["cpu family"].empty() - && LLStringUtil::convertToS32(cpuinfo["cpu family"], family)) - { - setInfo(eFamily, family); - } - - setInfo(eFamilyName, compute_CPUFamilyName(cpuinfo["vendor_id"].c_str(), family, 0)); - - // setInfo(eExtendedModel, getSysctlInt("machdep.cpu.extmodel")); - // setInfo(eBrandID, getSysctlInt("machdep.cpu.brand")); - // setInfo(eType, 0); // ? where to find this? - - //setConfig(eCLFLUSHCacheLineSize, ((cpu_info[1] >> 8) & 0xff) * 8); - //setConfig(eAPICPhysicalID, (cpu_info[1] >> 24) & 0xff); - //setConfig(eCacheLineSize, getSysctlInt("machdep.cpu.cache.linesize")); - //setConfig(eL2Associativity, getSysctlInt("machdep.cpu.cache.L2_associativity")); - //setConfig(eCacheSizeK, getSysctlInt("machdep.cpu.cache.size")); - - // Read extensions - std::string flags = " " + cpuinfo["flags"] + " "; - LLStringUtil::toLower(flags); - - if( flags.find( " sse " ) != std::string::npos ) - { - setExtension(cpu_feature_names[eSSE_Ext]); - } - - if( flags.find( " sse2 " ) != std::string::npos ) - { - setExtension(cpu_feature_names[eSSE2_Ext]); - } + {\ + S32 result; \ + if (!cpuinfo[cpuinfo_id].empty() \ + && LLStringUtil::convertToS32(cpuinfo[cpuinfo_id], result)) \ + { setInfo(llpi_id, result);} \ + } + + F64 mhz; + if (LLStringUtil::convertToF64(cpuinfo["cpu mhz"], mhz) + && 200.0 < mhz && mhz < 10000.0) + { + setInfo(eFrequency,(F64)(mhz)); + } + + LLPI_SET_INFO_STRING(eBrandName, "model name"); + LLPI_SET_INFO_STRING(eVendor, "vendor_id"); + + LLPI_SET_INFO_INT(eStepping, "stepping"); + LLPI_SET_INFO_INT(eModel, "model"); + + + S32 family; + if (!cpuinfo["cpu family"].empty() + && LLStringUtil::convertToS32(cpuinfo["cpu family"], family)) + { + setInfo(eFamily, family); + } + + setInfo(eFamilyName, compute_CPUFamilyName(cpuinfo["vendor_id"].c_str(), family, 0)); + + // setInfo(eExtendedModel, getSysctlInt("machdep.cpu.extmodel")); + // setInfo(eBrandID, getSysctlInt("machdep.cpu.brand")); + // setInfo(eType, 0); // ? where to find this? + + //setConfig(eCLFLUSHCacheLineSize, ((cpu_info[1] >> 8) & 0xff) * 8); + //setConfig(eAPICPhysicalID, (cpu_info[1] >> 24) & 0xff); + //setConfig(eCacheLineSize, getSysctlInt("machdep.cpu.cache.linesize")); + //setConfig(eL2Associativity, getSysctlInt("machdep.cpu.cache.L2_associativity")); + //setConfig(eCacheSizeK, getSysctlInt("machdep.cpu.cache.size")); + + // Read extensions + std::string flags = " " + cpuinfo["flags"] + " "; + LLStringUtil::toLower(flags); + + if( flags.find( " sse " ) != std::string::npos ) + { + setExtension(cpu_feature_names[eSSE_Ext]); + } + + if( flags.find( " sse2 " ) != std::string::npos ) + { + setExtension(cpu_feature_names[eSSE2_Ext]); + } if (flags.find(" pni ") != std::string::npos) { @@ -924,36 +924,36 @@ private: { setExtension(cpu_feature_names[eSSE4a_Features]); } - + # endif // LL_X86 - } - - std::string getCPUFeatureDescription() const - { - std::ostringstream s; - - // *NOTE:Mani - This is for linux only. - LLFILE* cpuinfo = LLFile::fopen(CPUINFO_FILE, "rb"); - if(cpuinfo) - { - char line[MAX_STRING]; - memset(line, 0, MAX_STRING); - while(fgets(line, MAX_STRING, cpuinfo)) - { - line[strlen(line)-1] = ' '; - s << line; - s << std::endl; - } - fclose(cpuinfo); - s << std::endl; - } - else - { - s << "Unable to collect processor information" << std::endl; - } - return s.str(); - } - + } + + std::string getCPUFeatureDescription() const + { + std::ostringstream s; + + // *NOTE:Mani - This is for linux only. + LLFILE* cpuinfo = LLFile::fopen(CPUINFO_FILE, "rb"); + if(cpuinfo) + { + char line[MAX_STRING]; + memset(line, 0, MAX_STRING); + while(fgets(line, MAX_STRING, cpuinfo)) + { + line[strlen(line)-1] = ' '; + s << line; + s << std::endl; + } + fclose(cpuinfo); + s << std::endl; + } + else + { + s << "Unable to collect processor information" << std::endl; + } + return s.str(); + } + }; @@ -963,20 +963,20 @@ private: // Interface definition LLProcessorInfo::LLProcessorInfo() : mImpl(NULL) { - // *NOTE:Mani - not thread safe. - if(!mImpl) - { + // *NOTE:Mani - not thread safe. + if(!mImpl) + { #ifdef LL_MSVC - static LLProcessorInfoWindowsImpl the_impl; - mImpl = &the_impl; + static LLProcessorInfoWindowsImpl the_impl; + mImpl = &the_impl; #elif LL_DARWIN - static LLProcessorInfoDarwinImpl the_impl; - mImpl = &the_impl; + static LLProcessorInfoDarwinImpl the_impl; + mImpl = &the_impl; #else - static LLProcessorInfoLinuxImpl the_impl; - mImpl = &the_impl; + static LLProcessorInfoLinuxImpl the_impl; + mImpl = &the_impl; #endif // LL_MSVC - } + } } diff --git a/indra/llcommon/llprocessor.h b/indra/llcommon/llprocessor.h index 1a473ddc97..f8ccf686c8 100644 --- a/indra/llcommon/llprocessor.h +++ b/indra/llcommon/llprocessor.h @@ -1,25 +1,25 @@ -/** +/** * @file llprocessor.h * @brief Code to figure out the processor. Originally by Benjamin Jurke. * * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -48,23 +48,23 @@ class LLProcessorInfoImpl; class LL_COMMON_API LLProcessorInfo { public: - LLProcessorInfo(); - ~LLProcessorInfo(); + LLProcessorInfo(); + ~LLProcessorInfo(); - F64MegahertzImplicit getCPUFrequency() const; - bool hasSSE() const; - bool hasSSE2() const; + F64MegahertzImplicit getCPUFrequency() const; + bool hasSSE() const; + bool hasSSE2() const; bool hasSSE3() const; bool hasSSE3S() const; bool hasSSE41() const; bool hasSSE42() const; bool hasSSE4a() const; - bool hasAltivec() const; - std::string getCPUFamilyName() const; - std::string getCPUBrandName() const; - std::string getCPUFeatureDescription() const; + bool hasAltivec() const; + std::string getCPUFamilyName() const; + std::string getCPUBrandName() const; + std::string getCPUFeatureDescription() const; private: - LLProcessorInfoImpl* mImpl; + LLProcessorInfoImpl* mImpl; }; #endif // LLPROCESSOR_H diff --git a/indra/llcommon/llprocinfo.cpp b/indra/llcommon/llprocinfo.cpp index c00f979b0b..69be00e14a 100644 --- a/indra/llcommon/llprocinfo.cpp +++ b/indra/llcommon/llprocinfo.cpp @@ -1,4 +1,4 @@ -/** +/** * @file llprocinfo.cpp * @brief Process, cpu and resource usage information APIs. * @author monty@lindenlab.com @@ -30,7 +30,7 @@ #if LL_WINDOWS -#define PSAPI_VERSION 1 +#define PSAPI_VERSION 1 #include "windows.h" #include "psapi.h" @@ -38,7 +38,7 @@ #include <sys/resource.h> #include <mach/mach.h> - + #else #include <sys/time.h> @@ -52,42 +52,42 @@ void LLProcInfo::getCPUUsage(time_type & user_time, time_type & system_time) { #if LL_WINDOWS - HANDLE self(GetCurrentProcess()); // Does not have to be closed - FILETIME ft_dummy, ft_system, ft_user; - - GetProcessTimes(self, &ft_dummy, &ft_dummy, &ft_system, &ft_user); - ULARGE_INTEGER uli; - uli.u.LowPart = ft_system.dwLowDateTime; - uli.u.HighPart = ft_system.dwHighDateTime; - system_time = uli.QuadPart / U64L(10); // Convert to uS - uli.u.LowPart = ft_user.dwLowDateTime; - uli.u.HighPart = ft_user.dwHighDateTime; - user_time = uli.QuadPart / U64L(10); - + HANDLE self(GetCurrentProcess()); // Does not have to be closed + FILETIME ft_dummy, ft_system, ft_user; + + GetProcessTimes(self, &ft_dummy, &ft_dummy, &ft_system, &ft_user); + ULARGE_INTEGER uli; + uli.u.LowPart = ft_system.dwLowDateTime; + uli.u.HighPart = ft_system.dwHighDateTime; + system_time = uli.QuadPart / U64L(10); // Convert to uS + uli.u.LowPart = ft_user.dwLowDateTime; + uli.u.HighPart = ft_user.dwHighDateTime; + user_time = uli.QuadPart / U64L(10); + #elif LL_DARWIN - struct rusage usage; + struct rusage usage; - if (getrusage(RUSAGE_SELF, &usage)) - { - user_time = system_time = time_type(0U); - return; - } - user_time = U64(usage.ru_utime.tv_sec) * U64L(1000000) + usage.ru_utime.tv_usec; - system_time = U64(usage.ru_stime.tv_sec) * U64L(1000000) + usage.ru_stime.tv_usec; + if (getrusage(RUSAGE_SELF, &usage)) + { + user_time = system_time = time_type(0U); + return; + } + user_time = U64(usage.ru_utime.tv_sec) * U64L(1000000) + usage.ru_utime.tv_usec; + system_time = U64(usage.ru_stime.tv_sec) * U64L(1000000) + usage.ru_stime.tv_usec; #else // Linux - struct rusage usage; + struct rusage usage; + + if (getrusage(RUSAGE_SELF, &usage)) + { + user_time = system_time = time_type(0U); + return; + } + user_time = U64(usage.ru_utime.tv_sec) * U64L(1000000) + usage.ru_utime.tv_usec; + system_time = U64(usage.ru_stime.tv_sec) * U64L(1000000) + usage.ru_stime.tv_usec; - if (getrusage(RUSAGE_SELF, &usage)) - { - user_time = system_time = time_type(0U); - return; - } - user_time = U64(usage.ru_utime.tv_sec) * U64L(1000000) + usage.ru_utime.tv_usec; - system_time = U64(usage.ru_stime.tv_sec) * U64L(1000000) + usage.ru_stime.tv_usec; - #endif // LL_WINDOWS/LL_DARWIN/Linux } diff --git a/indra/llcommon/llprocinfo.h b/indra/llcommon/llprocinfo.h index e78bcf490a..5955799812 100644 --- a/indra/llcommon/llprocinfo.h +++ b/indra/llcommon/llprocinfo.h @@ -1,4 +1,4 @@ -/** +/** * @file llprocinfo.h * @brief Interface to process/cpu/resource information services. * @author monty@lindenlab.com @@ -25,8 +25,8 @@ * $/LicenseInfo$ */ -#ifndef LL_PROCINFO_H -#define LL_PROCINFO_H +#ifndef LL_PROCINFO_H +#define LL_PROCINFO_H #include "linden_common.h" @@ -46,23 +46,23 @@ class LL_COMMON_API LLProcInfo { public: - /// Public types + /// Public types + + typedef U64 time_type; /// Relative microseconds - typedef U64 time_type; /// Relative microseconds - private: - LLProcInfo(); // Not defined - ~LLProcInfo(); // Not defined - LLProcInfo(const LLProcInfo &); // Not defined - void operator=(const LLProcInfo &); // Not defined + LLProcInfo(); // Not defined + ~LLProcInfo(); // Not defined + LLProcInfo(const LLProcInfo &); // Not defined + void operator=(const LLProcInfo &); // Not defined public: - /// Get accumulated system and user CPU time in - /// microseconds. Syscalls involved in every invocation. - /// - /// Threading: expected to be safe. - static void getCPUUsage(time_type & user_time, time_type & system_time); + /// Get accumulated system and user CPU time in + /// microseconds. Syscalls involved in every invocation. + /// + /// Threading: expected to be safe. + static void getCPUUsage(time_type & user_time, time_type & system_time); }; - -#endif // LL_PROCINFO_H + +#endif // LL_PROCINFO_H diff --git a/indra/llcommon/llptrto.cpp b/indra/llcommon/llptrto.cpp index b270291bd6..c4528a47a7 100644 --- a/indra/llcommon/llptrto.cpp +++ b/indra/llcommon/llptrto.cpp @@ -3,25 +3,25 @@ * @author Nat Goodspeed * @date 2008-08-20 * @brief Test for llptrto.h - * + * * $LicenseInfo:firstyear=2008&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llcommon/llptrto.h b/indra/llcommon/llptrto.h index 9ef279fdbf..b57a1ee7f4 100644 --- a/indra/llcommon/llptrto.h +++ b/indra/llcommon/llptrto.h @@ -5,25 +5,25 @@ * @brief LLPtrTo<TARGET> is a template helper to pick either TARGET* or -- when * TARGET is a subclass of LLRefCount or LLThreadSafeRefCount -- * LLPointer<TARGET>. LLPtrTo<> chooses whichever pointer type is best. - * + * * $LicenseInfo:firstyear=2008&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llcommon/llqueuedthread.cpp b/indra/llcommon/llqueuedthread.cpp index 38a54337a1..4a909f601a 100644 --- a/indra/llcommon/llqueuedthread.cpp +++ b/indra/llcommon/llqueuedthread.cpp @@ -1,587 +1,587 @@ -/** - * @file llqueuedthread.cpp - * - * $LicenseInfo:firstyear=2004&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" -#include "llqueuedthread.h" - -#include <chrono> - -#include "llstl.h" -#include "lltimer.h" // ms_sleep() -#include "llmutex.h" - -//============================================================================ - -// MAIN THREAD -LLQueuedThread::LLQueuedThread(const std::string& name, bool threaded, bool should_pause) : - LLThread(name), - mIdleThread(true), - mNextHandle(0), - mStarted(false), - mThreaded(threaded), - mRequestQueue(name, 1024 * 1024) -{ - llassert(threaded); // not threaded implementation is deprecated - mMainQueue = LL::WorkQueue::getInstance("mainloop"); - - if (mThreaded) - { - if(should_pause) - { - pause() ; //call this before start the thread. - } - - start(); - } -} - -// MAIN THREAD -LLQueuedThread::~LLQueuedThread() -{ - if (!mThreaded) - { - endThread(); - } - shutdown(); - // ~LLThread() will be called here -} - -void LLQueuedThread::shutdown() -{ - setQuitting(); - - unpause(); // MAIN THREAD - if (mThreaded) - { - if (mRequestQueue.size() == 0) - { - mRequestQueue.close(); - } - - S32 timeout = 100; - for ( ; timeout>0; timeout--) - { - if (isStopped()) - { - break; - } - ms_sleep(100); - LLThread::yield(); - } - if (timeout == 0) - { - LL_WARNS() << "~LLQueuedThread (" << mName << ") timed out!" << LL_ENDL; - } - } - else - { - mStatus = STOPPED; - } - - QueuedRequest* req; - S32 active_count = 0; - while ( (req = (QueuedRequest*)mRequestHash.pop_element()) ) - { - if (req->getStatus() == STATUS_QUEUED || req->getStatus() == STATUS_INPROGRESS) - { - ++active_count; - req->setStatus(STATUS_ABORTED); // avoid assert in deleteRequest - } - req->deleteRequest(); - } - if (active_count) - { - LL_WARNS() << "~LLQueuedThread() called with active requests: " << active_count << LL_ENDL; - } - - mRequestQueue.close(); -} - -//---------------------------------------------------------------------------- - -// MAIN THREAD -// virtual -size_t LLQueuedThread::update(F32 max_time_ms) -{ - LL_PROFILE_ZONE_SCOPED; - if (!mStarted) - { - if (!mThreaded) - { - startThread(); - mStarted = true; - } - } - return updateQueue(max_time_ms); -} - -size_t LLQueuedThread::updateQueue(F32 max_time_ms) -{ - LL_PROFILE_ZONE_SCOPED; - // Frame Update - if (mThreaded) - { - // schedule a call to threadedUpdate for every call to updateQueue - if (!isQuitting()) - { - mRequestQueue.post([=]() - { - LL_PROFILE_ZONE_NAMED_CATEGORY_THREAD("qt - update"); - mIdleThread = false; - threadedUpdate(); - mIdleThread = true; - } - ); - } - - if(getPending() > 0) - { - unpause(); - } - } - else - { - mRequestQueue.runFor(std::chrono::microseconds((int) (max_time_ms*1000.f))); - threadedUpdate(); - } - return getPending(); -} - -void LLQueuedThread::incQueue() -{ - // Something has been added to the queue - if (!isPaused()) - { - if (mThreaded) - { - wake(); // Wake the thread up if necessary. - } - } -} - -//virtual -// May be called from any thread -size_t LLQueuedThread::getPending() -{ - return mRequestQueue.size(); -} - -// MAIN thread -void LLQueuedThread::waitOnPending() -{ - while(1) - { - update(0); - - if (mIdleThread) - { - break; - } - if (mThreaded) - { - yield(); - } - } - return; -} - -// MAIN thread -void LLQueuedThread::printQueueStats() -{ - U32 size = mRequestQueue.size(); - if (size > 0) - { - LL_INFOS() << llformat("Pending Requests:%d ", mRequestQueue.size()) << LL_ENDL; - } - else - { - LL_INFOS() << "Queued Thread Idle" << LL_ENDL; - } -} - -// MAIN thread -LLQueuedThread::handle_t LLQueuedThread::generateHandle() -{ - U32 res = ++mNextHandle; - return res; -} - -// MAIN thread -bool LLQueuedThread::addRequest(QueuedRequest* req) -{ - LL_PROFILE_ZONE_SCOPED; - if (mStatus == QUITTING) - { - return false; - } - - lockData(); - req->setStatus(STATUS_QUEUED); - mRequestHash.insert(req); -#if _DEBUG -// LL_INFOS() << llformat("LLQueuedThread::Added req [%08d]",handle) << LL_ENDL; -#endif - unlockData(); - - llassert(!mDataLock->isSelfLocked()); - mRequestQueue.post([this, req]() { processRequest(req); }); - - return true; -} - -// MAIN thread -bool LLQueuedThread::waitForResult(LLQueuedThread::handle_t handle, bool auto_complete) -{ - LL_PROFILE_ZONE_SCOPED; - llassert (handle != nullHandle()); - bool res = false; - bool waspaused = isPaused(); - bool done = false; - while(!done) - { - update(0); // unpauses - lockData(); - QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle); - if (!req) - { - done = true; // request does not exist - } - else if (req->getStatus() == STATUS_COMPLETE) - { - res = true; - if (auto_complete) - { - mRequestHash.erase(handle); - req->deleteRequest(); -// check(); - } - done = true; - } - unlockData(); - - if (!done && mThreaded) - { - yield(); - } - } - if (waspaused) - { - pause(); - } - return res; -} - -// MAIN thread -LLQueuedThread::QueuedRequest* LLQueuedThread::getRequest(handle_t handle) -{ - if (handle == nullHandle()) - { - return 0; - } - lockData(); - QueuedRequest* res = (QueuedRequest*)mRequestHash.find(handle); - unlockData(); - return res; -} - -LLQueuedThread::status_t LLQueuedThread::getRequestStatus(handle_t handle) -{ - status_t res = STATUS_EXPIRED; - lockData(); - QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle); - if (req) - { - res = req->getStatus(); - } - unlockData(); - return res; -} - -void LLQueuedThread::abortRequest(handle_t handle, bool autocomplete) -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD; - lockData(); - QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle); - if (req) - { - req->setFlags(FLAG_ABORT | (autocomplete ? FLAG_AUTO_COMPLETE : 0)); - } - unlockData(); -} - -// MAIN thread -void LLQueuedThread::setFlags(handle_t handle, U32 flags) -{ - lockData(); - QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle); - if (req) - { - req->setFlags(flags); - } - unlockData(); -} - -bool LLQueuedThread::completeRequest(handle_t handle) -{ - LL_PROFILE_ZONE_SCOPED; - bool res = false; - lockData(); - QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle); - if (req) - { - llassert_always(req->getStatus() != STATUS_QUEUED); - llassert_always(req->getStatus() != STATUS_INPROGRESS); -#if _DEBUG -// LL_INFOS() << llformat("LLQueuedThread::Completed req [%08d]",handle) << LL_ENDL; -#endif - mRequestHash.erase(handle); - req->deleteRequest(); -// check(); - res = true; - } - unlockData(); - return res; -} - -bool LLQueuedThread::check() -{ -#if 0 // not a reliable check once mNextHandle wraps, just for quick and dirty debugging - for (int i=0; i<REQUEST_HASH_SIZE; i++) - { - LLSimpleHashEntry<handle_t>* entry = mRequestHash.get_element_at_index(i); - while (entry) - { - if (entry->getHashKey() > mNextHandle) - { - LL_ERRS() << "Hash Error" << LL_ENDL; - return false; - } - entry = entry->getNextEntry(); - } - } -#endif - return true; -} - -//============================================================================ -// Runs on its OWN thread - -void LLQueuedThread::processRequest(LLQueuedThread::QueuedRequest* req) -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD; - - mIdleThread = false; - //threadedUpdate(); - - // Get next request from pool - lockData(); - - if ((req->getFlags() & FLAG_ABORT) || (mStatus == QUITTING)) - { - LL_PROFILE_ZONE_NAMED_CATEGORY_THREAD("qtpr - abort"); - req->setStatus(STATUS_ABORTED); - req->finishRequest(false); - if (req->getFlags() & FLAG_AUTO_COMPLETE) - { - mRequestHash.erase(req); - req->deleteRequest(); -// check(); - } - unlockData(); - } - else - { - llassert_always(req->getStatus() == STATUS_QUEUED); - - if (req) - { - req->setStatus(STATUS_INPROGRESS); - } - unlockData(); - - // This is the only place we will call req->setStatus() after - // it has initially been seet to STATUS_QUEUED, so it is - // safe to access req. - if (req) - { - // process request - bool complete = req->processRequest(); - - if (complete) - { - LL_PROFILE_ZONE_NAMED_CATEGORY_THREAD("qtpr - complete"); - lockData(); - req->setStatus(STATUS_COMPLETE); - req->finishRequest(true); - if (req->getFlags() & FLAG_AUTO_COMPLETE) - { - mRequestHash.erase(req); - req->deleteRequest(); - // check(); - } - unlockData(); - } - else - { - LL_PROFILE_ZONE_NAMED_CATEGORY_THREAD("qtpr - retry"); - //put back on queue and try again in 0.1ms - lockData(); - req->setStatus(STATUS_QUEUED); - - unlockData(); - - llassert(!mDataLock->isSelfLocked()); - -#if 0 - // try again on next frame - // NOTE: tried using "post" with a time in the future, but this - // would invariably cause this thread to wait for a long time (10+ ms) - // while work is pending - bool ret = LL::WorkQueue::postMaybe( - mMainQueue, - [=]() - { - LL_PROFILE_ZONE_NAMED("processRequest - retry"); - mRequestQueue.post([=]() - { - LL_PROFILE_ZONE_NAMED("processRequest - retry"); // <-- not redundant, track retry on both queues - processRequest(req); - }); - }); - llassert(ret); -#else - using namespace std::chrono_literals; - auto retry_time = LL::WorkQueue::TimePoint::clock::now() + 16ms; - mRequestQueue.post([=] - { - LL_PROFILE_ZONE_NAMED("processRequest - retry"); - if (LL::WorkQueue::TimePoint::clock::now() < retry_time) - { - auto sleep_time = std::chrono::duration_cast<std::chrono::milliseconds>(retry_time - LL::WorkQueue::TimePoint::clock::now()); - - if (sleep_time.count() > 0) - { - ms_sleep(sleep_time.count()); - } - } - processRequest(req); - }); -#endif - - } - } - } - - mIdleThread = true; -} - -// virtual -bool LLQueuedThread::runCondition() -{ - // mRunCondition must be locked here - if (mRequestQueue.size() == 0 && mIdleThread) - return false; - else - return true; -} - -// virtual -void LLQueuedThread::run() -{ - // call checPause() immediately so we don't try to do anything before the class is fully constructed - checkPause(); - startThread(); - mStarted = true; - - - /*while (1) - { - LL_PROFILE_ZONE_SCOPED; - // this will block on the condition until runCondition() returns true, the thread is unpaused, or the thread leaves the RUNNING state. - checkPause(); - - mIdleThread = false; - - threadedUpdate(); - - auto pending_work = processNextRequest(); - - if (pending_work == 0) - { - //LL_PROFILE_ZONE_NAMED("LLQueuedThread - sleep"); - mIdleThread = true; - //ms_sleep(1); - } - //LLThread::yield(); // thread should yield after each request - }*/ - mRequestQueue.runUntilClose(); - - endThread(); - LL_INFOS() << "LLQueuedThread " << mName << " EXITING." << LL_ENDL; - - -} - -// virtual -void LLQueuedThread::startThread() -{ -} - -// virtual -void LLQueuedThread::endThread() -{ -} - -// virtual -void LLQueuedThread::threadedUpdate() -{ -} - -//============================================================================ - -LLQueuedThread::QueuedRequest::QueuedRequest(LLQueuedThread::handle_t handle, U32 flags) : - LLSimpleHashEntry<LLQueuedThread::handle_t>(handle), - mStatus(STATUS_UNKNOWN), - mFlags(flags) -{ -} - -LLQueuedThread::QueuedRequest::~QueuedRequest() -{ - llassert_always(mStatus == STATUS_DELETE); -} - -//virtual -void LLQueuedThread::QueuedRequest::finishRequest(bool completed) -{ -} - -//virtual -void LLQueuedThread::QueuedRequest::deleteRequest() -{ - llassert_always(mStatus != STATUS_INPROGRESS); - setStatus(STATUS_DELETE); - delete this; -} +/**
+ * @file llqueuedthread.cpp
+ *
+ * $LicenseInfo:firstyear=2004&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+#include "llqueuedthread.h"
+
+#include <chrono>
+
+#include "llstl.h"
+#include "lltimer.h" // ms_sleep()
+#include "llmutex.h"
+
+//============================================================================
+
+// MAIN THREAD
+LLQueuedThread::LLQueuedThread(const std::string& name, bool threaded, bool should_pause) :
+ LLThread(name),
+ mIdleThread(true),
+ mNextHandle(0),
+ mStarted(false),
+ mThreaded(threaded),
+ mRequestQueue(name, 1024 * 1024)
+{
+ llassert(threaded); // not threaded implementation is deprecated
+ mMainQueue = LL::WorkQueue::getInstance("mainloop");
+
+ if (mThreaded)
+ {
+ if(should_pause)
+ {
+ pause() ; //call this before start the thread.
+ }
+
+ start();
+ }
+}
+
+// MAIN THREAD
+LLQueuedThread::~LLQueuedThread()
+{
+ if (!mThreaded)
+ {
+ endThread();
+ }
+ shutdown();
+ // ~LLThread() will be called here
+}
+
+void LLQueuedThread::shutdown()
+{
+ setQuitting();
+
+ unpause(); // MAIN THREAD
+ if (mThreaded)
+ {
+ if (mRequestQueue.size() == 0)
+ {
+ mRequestQueue.close();
+ }
+
+ S32 timeout = 100;
+ for ( ; timeout>0; timeout--)
+ {
+ if (isStopped())
+ {
+ break;
+ }
+ ms_sleep(100);
+ LLThread::yield();
+ }
+ if (timeout == 0)
+ {
+ LL_WARNS() << "~LLQueuedThread (" << mName << ") timed out!" << LL_ENDL;
+ }
+ }
+ else
+ {
+ mStatus = STOPPED;
+ }
+
+ QueuedRequest* req;
+ S32 active_count = 0;
+ while ( (req = (QueuedRequest*)mRequestHash.pop_element()) )
+ {
+ if (req->getStatus() == STATUS_QUEUED || req->getStatus() == STATUS_INPROGRESS)
+ {
+ ++active_count;
+ req->setStatus(STATUS_ABORTED); // avoid assert in deleteRequest
+ }
+ req->deleteRequest();
+ }
+ if (active_count)
+ {
+ LL_WARNS() << "~LLQueuedThread() called with active requests: " << active_count << LL_ENDL;
+ }
+
+ mRequestQueue.close();
+}
+
+//----------------------------------------------------------------------------
+
+// MAIN THREAD
+// virtual
+size_t LLQueuedThread::update(F32 max_time_ms)
+{
+ LL_PROFILE_ZONE_SCOPED;
+ if (!mStarted)
+ {
+ if (!mThreaded)
+ {
+ startThread();
+ mStarted = true;
+ }
+ }
+ return updateQueue(max_time_ms);
+}
+
+size_t LLQueuedThread::updateQueue(F32 max_time_ms)
+{
+ LL_PROFILE_ZONE_SCOPED;
+ // Frame Update
+ if (mThreaded)
+ {
+ // schedule a call to threadedUpdate for every call to updateQueue
+ if (!isQuitting())
+ {
+ mRequestQueue.post([=]()
+ {
+ LL_PROFILE_ZONE_NAMED_CATEGORY_THREAD("qt - update");
+ mIdleThread = false;
+ threadedUpdate();
+ mIdleThread = true;
+ }
+ );
+ }
+
+ if(getPending() > 0)
+ {
+ unpause();
+ }
+ }
+ else
+ {
+ mRequestQueue.runFor(std::chrono::microseconds((int) (max_time_ms*1000.f)));
+ threadedUpdate();
+ }
+ return getPending();
+}
+
+void LLQueuedThread::incQueue()
+{
+ // Something has been added to the queue
+ if (!isPaused())
+ {
+ if (mThreaded)
+ {
+ wake(); // Wake the thread up if necessary.
+ }
+ }
+}
+
+//virtual
+// May be called from any thread
+size_t LLQueuedThread::getPending()
+{
+ return mRequestQueue.size();
+}
+
+// MAIN thread
+void LLQueuedThread::waitOnPending()
+{
+ while(1)
+ {
+ update(0);
+
+ if (mIdleThread)
+ {
+ break;
+ }
+ if (mThreaded)
+ {
+ yield();
+ }
+ }
+ return;
+}
+
+// MAIN thread
+void LLQueuedThread::printQueueStats()
+{
+ U32 size = mRequestQueue.size();
+ if (size > 0)
+ {
+ LL_INFOS() << llformat("Pending Requests:%d ", mRequestQueue.size()) << LL_ENDL;
+ }
+ else
+ {
+ LL_INFOS() << "Queued Thread Idle" << LL_ENDL;
+ }
+}
+
+// MAIN thread
+LLQueuedThread::handle_t LLQueuedThread::generateHandle()
+{
+ U32 res = ++mNextHandle;
+ return res;
+}
+
+// MAIN thread
+bool LLQueuedThread::addRequest(QueuedRequest* req)
+{
+ LL_PROFILE_ZONE_SCOPED;
+ if (mStatus == QUITTING)
+ {
+ return false;
+ }
+
+ lockData();
+ req->setStatus(STATUS_QUEUED);
+ mRequestHash.insert(req);
+#if _DEBUG
+// LL_INFOS() << llformat("LLQueuedThread::Added req [%08d]",handle) << LL_ENDL;
+#endif
+ unlockData();
+
+ llassert(!mDataLock->isSelfLocked());
+ mRequestQueue.post([this, req]() { processRequest(req); });
+
+ return true;
+}
+
+// MAIN thread
+bool LLQueuedThread::waitForResult(LLQueuedThread::handle_t handle, bool auto_complete)
+{
+ LL_PROFILE_ZONE_SCOPED;
+ llassert (handle != nullHandle());
+ bool res = false;
+ bool waspaused = isPaused();
+ bool done = false;
+ while(!done)
+ {
+ update(0); // unpauses
+ lockData();
+ QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle);
+ if (!req)
+ {
+ done = true; // request does not exist
+ }
+ else if (req->getStatus() == STATUS_COMPLETE)
+ {
+ res = true;
+ if (auto_complete)
+ {
+ mRequestHash.erase(handle);
+ req->deleteRequest();
+// check();
+ }
+ done = true;
+ }
+ unlockData();
+
+ if (!done && mThreaded)
+ {
+ yield();
+ }
+ }
+ if (waspaused)
+ {
+ pause();
+ }
+ return res;
+}
+
+// MAIN thread
+LLQueuedThread::QueuedRequest* LLQueuedThread::getRequest(handle_t handle)
+{
+ if (handle == nullHandle())
+ {
+ return 0;
+ }
+ lockData();
+ QueuedRequest* res = (QueuedRequest*)mRequestHash.find(handle);
+ unlockData();
+ return res;
+}
+
+LLQueuedThread::status_t LLQueuedThread::getRequestStatus(handle_t handle)
+{
+ status_t res = STATUS_EXPIRED;
+ lockData();
+ QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle);
+ if (req)
+ {
+ res = req->getStatus();
+ }
+ unlockData();
+ return res;
+}
+
+void LLQueuedThread::abortRequest(handle_t handle, bool autocomplete)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD;
+ lockData();
+ QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle);
+ if (req)
+ {
+ req->setFlags(FLAG_ABORT | (autocomplete ? FLAG_AUTO_COMPLETE : 0));
+ }
+ unlockData();
+}
+
+// MAIN thread
+void LLQueuedThread::setFlags(handle_t handle, U32 flags)
+{
+ lockData();
+ QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle);
+ if (req)
+ {
+ req->setFlags(flags);
+ }
+ unlockData();
+}
+
+bool LLQueuedThread::completeRequest(handle_t handle)
+{
+ LL_PROFILE_ZONE_SCOPED;
+ bool res = false;
+ lockData();
+ QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle);
+ if (req)
+ {
+ llassert_always(req->getStatus() != STATUS_QUEUED);
+ llassert_always(req->getStatus() != STATUS_INPROGRESS);
+#if _DEBUG
+// LL_INFOS() << llformat("LLQueuedThread::Completed req [%08d]",handle) << LL_ENDL;
+#endif
+ mRequestHash.erase(handle);
+ req->deleteRequest();
+// check();
+ res = true;
+ }
+ unlockData();
+ return res;
+}
+
+bool LLQueuedThread::check()
+{
+#if 0 // not a reliable check once mNextHandle wraps, just for quick and dirty debugging
+ for (int i=0; i<REQUEST_HASH_SIZE; i++)
+ {
+ LLSimpleHashEntry<handle_t>* entry = mRequestHash.get_element_at_index(i);
+ while (entry)
+ {
+ if (entry->getHashKey() > mNextHandle)
+ {
+ LL_ERRS() << "Hash Error" << LL_ENDL;
+ return false;
+ }
+ entry = entry->getNextEntry();
+ }
+ }
+#endif
+ return true;
+}
+
+//============================================================================
+// Runs on its OWN thread
+
+void LLQueuedThread::processRequest(LLQueuedThread::QueuedRequest* req)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD;
+
+ mIdleThread = false;
+ //threadedUpdate();
+
+ // Get next request from pool
+ lockData();
+
+ if ((req->getFlags() & FLAG_ABORT) || (mStatus == QUITTING))
+ {
+ LL_PROFILE_ZONE_NAMED_CATEGORY_THREAD("qtpr - abort");
+ req->setStatus(STATUS_ABORTED);
+ req->finishRequest(false);
+ if (req->getFlags() & FLAG_AUTO_COMPLETE)
+ {
+ mRequestHash.erase(req);
+ req->deleteRequest();
+// check();
+ }
+ unlockData();
+ }
+ else
+ {
+ llassert_always(req->getStatus() == STATUS_QUEUED);
+
+ if (req)
+ {
+ req->setStatus(STATUS_INPROGRESS);
+ }
+ unlockData();
+
+ // This is the only place we will call req->setStatus() after
+ // it has initially been seet to STATUS_QUEUED, so it is
+ // safe to access req.
+ if (req)
+ {
+ // process request
+ bool complete = req->processRequest();
+
+ if (complete)
+ {
+ LL_PROFILE_ZONE_NAMED_CATEGORY_THREAD("qtpr - complete");
+ lockData();
+ req->setStatus(STATUS_COMPLETE);
+ req->finishRequest(true);
+ if (req->getFlags() & FLAG_AUTO_COMPLETE)
+ {
+ mRequestHash.erase(req);
+ req->deleteRequest();
+ // check();
+ }
+ unlockData();
+ }
+ else
+ {
+ LL_PROFILE_ZONE_NAMED_CATEGORY_THREAD("qtpr - retry");
+ //put back on queue and try again in 0.1ms
+ lockData();
+ req->setStatus(STATUS_QUEUED);
+
+ unlockData();
+
+ llassert(!mDataLock->isSelfLocked());
+
+#if 0
+ // try again on next frame
+ // NOTE: tried using "post" with a time in the future, but this
+ // would invariably cause this thread to wait for a long time (10+ ms)
+ // while work is pending
+ bool ret = LL::WorkQueue::postMaybe(
+ mMainQueue,
+ [=]()
+ {
+ LL_PROFILE_ZONE_NAMED("processRequest - retry");
+ mRequestQueue.post([=]()
+ {
+ LL_PROFILE_ZONE_NAMED("processRequest - retry"); // <-- not redundant, track retry on both queues
+ processRequest(req);
+ });
+ });
+ llassert(ret);
+#else
+ using namespace std::chrono_literals;
+ auto retry_time = LL::WorkQueue::TimePoint::clock::now() + 16ms;
+ mRequestQueue.post([=]
+ {
+ LL_PROFILE_ZONE_NAMED("processRequest - retry");
+ if (LL::WorkQueue::TimePoint::clock::now() < retry_time)
+ {
+ auto sleep_time = std::chrono::duration_cast<std::chrono::milliseconds>(retry_time - LL::WorkQueue::TimePoint::clock::now());
+
+ if (sleep_time.count() > 0)
+ {
+ ms_sleep(sleep_time.count());
+ }
+ }
+ processRequest(req);
+ });
+#endif
+
+ }
+ }
+ }
+
+ mIdleThread = true;
+}
+
+// virtual
+bool LLQueuedThread::runCondition()
+{
+ // mRunCondition must be locked here
+ if (mRequestQueue.size() == 0 && mIdleThread)
+ return false;
+ else
+ return true;
+}
+
+// virtual
+void LLQueuedThread::run()
+{
+ // call checPause() immediately so we don't try to do anything before the class is fully constructed
+ checkPause();
+ startThread();
+ mStarted = true;
+
+
+ /*while (1)
+ {
+ LL_PROFILE_ZONE_SCOPED;
+ // this will block on the condition until runCondition() returns true, the thread is unpaused, or the thread leaves the RUNNING state.
+ checkPause();
+
+ mIdleThread = false;
+
+ threadedUpdate();
+
+ auto pending_work = processNextRequest();
+
+ if (pending_work == 0)
+ {
+ //LL_PROFILE_ZONE_NAMED("LLQueuedThread - sleep");
+ mIdleThread = true;
+ //ms_sleep(1);
+ }
+ //LLThread::yield(); // thread should yield after each request
+ }*/
+ mRequestQueue.runUntilClose();
+
+ endThread();
+ LL_INFOS() << "LLQueuedThread " << mName << " EXITING." << LL_ENDL;
+
+
+}
+
+// virtual
+void LLQueuedThread::startThread()
+{
+}
+
+// virtual
+void LLQueuedThread::endThread()
+{
+}
+
+// virtual
+void LLQueuedThread::threadedUpdate()
+{
+}
+
+//============================================================================
+
+LLQueuedThread::QueuedRequest::QueuedRequest(LLQueuedThread::handle_t handle, U32 flags) :
+ LLSimpleHashEntry<LLQueuedThread::handle_t>(handle),
+ mStatus(STATUS_UNKNOWN),
+ mFlags(flags)
+{
+}
+
+LLQueuedThread::QueuedRequest::~QueuedRequest()
+{
+ llassert_always(mStatus == STATUS_DELETE);
+}
+
+//virtual
+void LLQueuedThread::QueuedRequest::finishRequest(bool completed)
+{
+}
+
+//virtual
+void LLQueuedThread::QueuedRequest::deleteRequest()
+{
+ llassert_always(mStatus != STATUS_INPROGRESS);
+ setStatus(STATUS_DELETE);
+ delete this;
+}
diff --git a/indra/llcommon/llqueuedthread.h b/indra/llcommon/llqueuedthread.h index 70d87e7c04..0f69f7640f 100644 --- a/indra/llcommon/llqueuedthread.h +++ b/indra/llcommon/llqueuedthread.h @@ -1,178 +1,178 @@ -/** - * @file llqueuedthread.h - * @brief - * - * $LicenseInfo:firstyear=2004&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLQUEUEDTHREAD_H -#define LL_LLQUEUEDTHREAD_H - -#include <queue> -#include <string> -#include <map> -#include <set> - -#include "llatomic.h" - -#include "llthread.h" -#include "llsimplehash.h" -#include "workqueue.h" - -//============================================================================ -// Note: ~LLQueuedThread is O(N) N=# of queued threads, assumed to be small -// It is assumed that LLQueuedThreads are rarely created/destroyed. - -class LL_COMMON_API LLQueuedThread : public LLThread -{ - //------------------------------------------------------------------------ -public: - enum status_t { - STATUS_EXPIRED = -1, - STATUS_UNKNOWN = 0, - STATUS_QUEUED = 1, - STATUS_INPROGRESS = 2, - STATUS_COMPLETE = 3, - STATUS_ABORTED = 4, - STATUS_DELETE = 5 - }; - enum flags_t { - FLAG_AUTO_COMPLETE = 1, - FLAG_AUTO_DELETE = 2, // child-class dependent - FLAG_ABORT = 4 - }; - - typedef U32 handle_t; - - //------------------------------------------------------------------------ -public: - - class LL_COMMON_API QueuedRequest : public LLSimpleHashEntry<handle_t> - { - friend class LLQueuedThread; - - protected: - virtual ~QueuedRequest(); // use deleteRequest() - - public: - QueuedRequest(handle_t handle, U32 flags = 0); - - status_t getStatus() - { - return mStatus; - } - U32 getFlags() const - { - return mFlags; - } - - protected: - status_t setStatus(status_t newstatus) - { - status_t oldstatus = mStatus; - mStatus = newstatus; - return oldstatus; - } - void setFlags(U32 flags) - { - // NOTE: flags are |'d - mFlags |= flags; - } - - virtual bool processRequest() = 0; // Return true when request has completed - virtual void finishRequest(bool completed); // Always called from thread after request has completed or aborted - virtual void deleteRequest(); // Only method to delete a request - - protected: - LLAtomicBase<status_t> mStatus; - U32 mFlags; - }; - - //------------------------------------------------------------------------ - -public: - static handle_t nullHandle() { return handle_t(0); } - -public: - LLQueuedThread(const std::string& name, bool threaded = true, bool should_pause = false); - virtual ~LLQueuedThread(); - virtual void shutdown(); - -private: - // No copy constructor or copy assignment - LLQueuedThread(const LLQueuedThread&); - LLQueuedThread& operator=(const LLQueuedThread&); - - virtual bool runCondition(void); - virtual void run(void); - virtual void startThread(void); - virtual void endThread(void); - virtual void threadedUpdate(void); - -protected: - handle_t generateHandle(); - bool addRequest(QueuedRequest* req); - void processRequest(QueuedRequest* req); - void incQueue(); - -public: - bool waitForResult(handle_t handle, bool auto_complete = true); - - virtual size_t update(F32 max_time_ms); - size_t updateQueue(F32 max_time_ms); - - void waitOnPending(); - void printQueueStats(); - - virtual size_t getPending(); - bool getThreaded() { return mThreaded; } - - // Request accessors - status_t getRequestStatus(handle_t handle); - void abortRequest(handle_t handle, bool autocomplete); - void setFlags(handle_t handle, U32 flags); - bool completeRequest(handle_t handle); - // This is public for support classes like LLWorkerThread, - // but generally the methods above should be used. - QueuedRequest* getRequest(handle_t handle); - - // debug (see source) - bool check(); - -protected: - bool mThreaded; // if false, run on main thread and do updates during update() - bool mStarted; // required when mThreaded is false to call startThread() from update() - LLAtomicBool mIdleThread; // request queue is empty (or we are quitting) and the thread is idle - - //typedef std::set<QueuedRequest*, queued_request_less> request_queue_t; - //request_queue_t mRequestQueue; - LL::WorkQueue mRequestQueue; - LL::WorkQueue::weak_t mMainQueue; - - enum { REQUEST_HASH_SIZE = 512 }; // must be power of 2 - typedef LLSimpleHash<handle_t, REQUEST_HASH_SIZE> request_hash_t; - request_hash_t mRequestHash; - - handle_t mNextHandle; -}; - -#endif // LL_LLQUEUEDTHREAD_H +/**
+ * @file llqueuedthread.h
+ * @brief
+ *
+ * $LicenseInfo:firstyear=2004&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLQUEUEDTHREAD_H
+#define LL_LLQUEUEDTHREAD_H
+
+#include <queue>
+#include <string>
+#include <map>
+#include <set>
+
+#include "llatomic.h"
+
+#include "llthread.h"
+#include "llsimplehash.h"
+#include "workqueue.h"
+
+//============================================================================
+// Note: ~LLQueuedThread is O(N) N=# of queued threads, assumed to be small
+// It is assumed that LLQueuedThreads are rarely created/destroyed.
+
+class LL_COMMON_API LLQueuedThread : public LLThread
+{
+ //------------------------------------------------------------------------
+public:
+ enum status_t {
+ STATUS_EXPIRED = -1,
+ STATUS_UNKNOWN = 0,
+ STATUS_QUEUED = 1,
+ STATUS_INPROGRESS = 2,
+ STATUS_COMPLETE = 3,
+ STATUS_ABORTED = 4,
+ STATUS_DELETE = 5
+ };
+ enum flags_t {
+ FLAG_AUTO_COMPLETE = 1,
+ FLAG_AUTO_DELETE = 2, // child-class dependent
+ FLAG_ABORT = 4
+ };
+
+ typedef U32 handle_t;
+
+ //------------------------------------------------------------------------
+public:
+
+ class LL_COMMON_API QueuedRequest : public LLSimpleHashEntry<handle_t>
+ {
+ friend class LLQueuedThread;
+
+ protected:
+ virtual ~QueuedRequest(); // use deleteRequest()
+
+ public:
+ QueuedRequest(handle_t handle, U32 flags = 0);
+
+ status_t getStatus()
+ {
+ return mStatus;
+ }
+ U32 getFlags() const
+ {
+ return mFlags;
+ }
+
+ protected:
+ status_t setStatus(status_t newstatus)
+ {
+ status_t oldstatus = mStatus;
+ mStatus = newstatus;
+ return oldstatus;
+ }
+ void setFlags(U32 flags)
+ {
+ // NOTE: flags are |'d
+ mFlags |= flags;
+ }
+
+ virtual bool processRequest() = 0; // Return true when request has completed
+ virtual void finishRequest(bool completed); // Always called from thread after request has completed or aborted
+ virtual void deleteRequest(); // Only method to delete a request
+
+ protected:
+ LLAtomicBase<status_t> mStatus;
+ U32 mFlags;
+ };
+
+ //------------------------------------------------------------------------
+
+public:
+ static handle_t nullHandle() { return handle_t(0); }
+
+public:
+ LLQueuedThread(const std::string& name, bool threaded = true, bool should_pause = false);
+ virtual ~LLQueuedThread();
+ virtual void shutdown();
+
+private:
+ // No copy constructor or copy assignment
+ LLQueuedThread(const LLQueuedThread&);
+ LLQueuedThread& operator=(const LLQueuedThread&);
+
+ virtual bool runCondition(void);
+ virtual void run(void);
+ virtual void startThread(void);
+ virtual void endThread(void);
+ virtual void threadedUpdate(void);
+
+protected:
+ handle_t generateHandle();
+ bool addRequest(QueuedRequest* req);
+ void processRequest(QueuedRequest* req);
+ void incQueue();
+
+public:
+ bool waitForResult(handle_t handle, bool auto_complete = true);
+
+ virtual size_t update(F32 max_time_ms);
+ size_t updateQueue(F32 max_time_ms);
+
+ void waitOnPending();
+ void printQueueStats();
+
+ virtual size_t getPending();
+ bool getThreaded() { return mThreaded; }
+
+ // Request accessors
+ status_t getRequestStatus(handle_t handle);
+ void abortRequest(handle_t handle, bool autocomplete);
+ void setFlags(handle_t handle, U32 flags);
+ bool completeRequest(handle_t handle);
+ // This is public for support classes like LLWorkerThread,
+ // but generally the methods above should be used.
+ QueuedRequest* getRequest(handle_t handle);
+
+ // debug (see source)
+ bool check();
+
+protected:
+ bool mThreaded; // if false, run on main thread and do updates during update()
+ bool mStarted; // required when mThreaded is false to call startThread() from update()
+ LLAtomicBool mIdleThread; // request queue is empty (or we are quitting) and the thread is idle
+
+ //typedef std::set<QueuedRequest*, queued_request_less> request_queue_t;
+ //request_queue_t mRequestQueue;
+ LL::WorkQueue mRequestQueue;
+ LL::WorkQueue::weak_t mMainQueue;
+
+ enum { REQUEST_HASH_SIZE = 512 }; // must be power of 2
+ typedef LLSimpleHash<handle_t, REQUEST_HASH_SIZE> request_hash_t;
+ request_hash_t mRequestHash;
+
+ handle_t mNextHandle;
+};
+
+#endif // LL_LLQUEUEDTHREAD_H
diff --git a/indra/llcommon/llrand.cpp b/indra/llcommon/llrand.cpp index 0192111574..25d75af568 100644 --- a/indra/llcommon/llrand.cpp +++ b/indra/llcommon/llrand.cpp @@ -1,25 +1,25 @@ -/** +/** * @file llrand.cpp * @brief Global random generator. * * $LicenseInfo:firstyear=2000&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -69,25 +69,25 @@ inline REAL ll_internal_random(); template <> inline F64 ll_internal_random<F64>() { - // *HACK: Through experimentation, we have found that dual core - // CPUs (or at least multi-threaded processes) seem to - // occasionally give an obviously incorrect random number -- like - // 5^15 or something. Sooooo, clamp it as described above. - F64 rv{ gRandomGenerator() }; - if(!((rv >= 0.0) && (rv < 1.0))) return fmod(rv, 1.0); - return rv; + // *HACK: Through experimentation, we have found that dual core + // CPUs (or at least multi-threaded processes) seem to + // occasionally give an obviously incorrect random number -- like + // 5^15 or something. Sooooo, clamp it as described above. + F64 rv{ gRandomGenerator() }; + if(!((rv >= 0.0) && (rv < 1.0))) return fmod(rv, 1.0); + return rv; } template <> inline F32 ll_internal_random<F32>() { - // *HACK: clamp the result as described above. - // Per Monty, it's important to clamp using the correct fmodf() rather - // than expanding to F64 for fmod() and then truncating back to F32. Prior - // to this change, we were getting sporadic ll_frand() == 1.0 results. - F32 rv{ narrow<F32>(gRandomGenerator()) }; - if(!((rv >= 0.0f) && (rv < 1.0f))) return fmodf(rv, 1.0f); - return rv; + // *HACK: clamp the result as described above. + // Per Monty, it's important to clamp using the correct fmodf() rather + // than expanding to F64 for fmod() and then truncating back to F32. Prior + // to this change, we were getting sporadic ll_frand() == 1.0 results. + F32 rv{ narrow<F32>(gRandomGenerator()) }; + if(!((rv >= 0.0f) && (rv < 1.0f))) return fmodf(rv, 1.0f); + return rv; } /*------------------------------ F64 aliases -------------------------------*/ @@ -98,7 +98,7 @@ inline F64 ll_internal_random_double() F64 ll_drand() { - return ll_internal_random_double(); + return ll_internal_random_double(); } /*------------------------------ F32 aliases -------------------------------*/ @@ -109,37 +109,37 @@ inline F32 ll_internal_random_float() F32 ll_frand() { - return ll_internal_random_float(); + return ll_internal_random_float(); } /*-------------------------- clamped random range --------------------------*/ S32 ll_rand() { - return ll_rand(RAND_MAX); + return ll_rand(RAND_MAX); } S32 ll_rand(S32 val) { - // The clamping rules are described above. - S32 rv = (S32)(ll_internal_random_double() * val); - if(rv == val) return 0; - return rv; + // The clamping rules are described above. + S32 rv = (S32)(ll_internal_random_double() * val); + if(rv == val) return 0; + return rv; } template <typename REAL> REAL ll_grand(REAL val) { - // The clamping rules are described above. - REAL rv = ll_internal_random<REAL>() * val; - if(val > 0) - { - if(rv >= val) return REAL(); - } - else - { - if(rv <= val) return REAL(); - } - return rv; + // The clamping rules are described above. + REAL rv = ll_internal_random<REAL>() * val; + if(val > 0) + { + if(rv >= val) return REAL(); + } + else + { + if(rv <= val) return REAL(); + } + return rv; } F32 ll_frand(F32 val) diff --git a/indra/llcommon/llrand.h b/indra/llcommon/llrand.h index ad317d5bf7..625ae0f5f4 100644 --- a/indra/llcommon/llrand.h +++ b/indra/llcommon/llrand.h @@ -1,25 +1,25 @@ -/** +/** * @file llrand.h * @brief Information, functions, and typedefs for randomness. * * $LicenseInfo:firstyear=2000&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -42,18 +42,18 @@ * fairly trivial operations to try to limit compiler optimizations, * so these numbers are only good for relative comparisons. * - * usec/inter algorithm - * 0.21 boost::minstd_rand0 - * 0.039 boost:lagged_fibonacci19937 - * 0.036 boost:lagged_fibonacci607 - * 0.44 boost::hellekalek1995 - * 0.44 boost::ecuyer1988 - * 0.042 boost::rand48 - * 0.043 boost::mt11213b - * 0.028 stdlib random() - * 0.05 stdlib lrand48() - * 0.034 stdlib rand() - * 0.020 the old & lame LLRand + * usec/inter algorithm + * 0.21 boost::minstd_rand0 + * 0.039 boost:lagged_fibonacci19937 + * 0.036 boost:lagged_fibonacci607 + * 0.44 boost::hellekalek1995 + * 0.44 boost::ecuyer1988 + * 0.042 boost::rand48 + * 0.043 boost::mt11213b + * 0.028 stdlib random() + * 0.05 stdlib lrand48() + * 0.034 stdlib rand() + * 0.020 the old & lame LLRand */ /** @@ -100,13 +100,13 @@ F64 LL_COMMON_API ll_drand(F64 val); */ typedef boost::lagged_fibonacci607 LLRandLagFib607; -/**< +/**< * lengh of cycle: 2^32,000 * memory: 607*sizeof(double) (about 5K) */ typedef boost::lagged_fibonacci2281 LLRandLagFib2281; -/**< +/**< * lengh of cycle: 2^120,000 * memory: 2281*sizeof(double) (about 17K) */ diff --git a/indra/llcommon/llrefcount.cpp b/indra/llcommon/llrefcount.cpp index 3da94e7a8d..6b546aac63 100644 --- a/indra/llcommon/llrefcount.cpp +++ b/indra/llcommon/llrefcount.cpp @@ -1,25 +1,25 @@ -/** +/** * @file llrefcount.cpp * @brief Base class for reference counted objects for use with LLPointer * * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -33,26 +33,26 @@ const S32 gMaxRefCount = LL_REFCOUNT_FREE; LLRefCount::LLRefCount(const LLRefCount& other) -: mRef(0) +: mRef(0) { } LLRefCount& LLRefCount::operator=(const LLRefCount&) { - // do nothing, since ref count is specific to *this* reference - return *this; + // do nothing, since ref count is specific to *this* reference + return *this; } LLRefCount::LLRefCount() : - mRef(0) + mRef(0) { } LLRefCount::~LLRefCount() { - if (mRef != LL_REFCOUNT_FREE && mRef != 0) - { - LL_ERRS() << "deleting non-zero reference" << LL_ENDL; - } + if (mRef != LL_REFCOUNT_FREE && mRef != 0) + { + LL_ERRS() << "deleting non-zero reference" << LL_ENDL; + } } diff --git a/indra/llcommon/llrefcount.h b/indra/llcommon/llrefcount.h index 15e7175fc8..33c9e956b1 100644 --- a/indra/llcommon/llrefcount.h +++ b/indra/llcommon/llrefcount.h @@ -1,25 +1,25 @@ -/** +/** * @file llrefcount.h * @brief Base class for reference counted objects for use with LLPointer * * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -44,42 +44,42 @@ extern const S32 gMaxRefCount; class LL_COMMON_API LLRefCount { protected: - LLRefCount(const LLRefCount& other); - LLRefCount& operator=(const LLRefCount&); - virtual ~LLRefCount(); // use unref() - + LLRefCount(const LLRefCount& other); + LLRefCount& operator=(const LLRefCount&); + virtual ~LLRefCount(); // use unref() + public: - LLRefCount(); - - inline void ref() const - { - llassert(mRef != LL_REFCOUNT_FREE); // object is deleted - mRef++; - llassert(mRef < gMaxRefCount); // ref count excessive, likely memory leak - } - - inline S32 unref() const - { - llassert(mRef != LL_REFCOUNT_FREE); // object is deleted - llassert(mRef > 0); // ref count below 1, likely corrupted - if (0 == --mRef) - { - mRef = LL_REFCOUNT_FREE; // set to nonsense yet recognizable value to aid in debugging - delete this; - return 0; - } - return mRef; - } - - //NOTE: when passing around a const LLRefCount object, this can return different results - // at different types, since mRef is mutable - S32 getNumRefs() const - { - return mRef; - } + LLRefCount(); + + inline void ref() const + { + llassert(mRef != LL_REFCOUNT_FREE); // object is deleted + mRef++; + llassert(mRef < gMaxRefCount); // ref count excessive, likely memory leak + } + + inline S32 unref() const + { + llassert(mRef != LL_REFCOUNT_FREE); // object is deleted + llassert(mRef > 0); // ref count below 1, likely corrupted + if (0 == --mRef) + { + mRef = LL_REFCOUNT_FREE; // set to nonsense yet recognizable value to aid in debugging + delete this; + return 0; + } + return mRef; + } + + //NOTE: when passing around a const LLRefCount object, this can return different results + // at different types, since mRef is mutable + S32 getNumRefs() const + { + return mRef; + } private: - mutable S32 mRef; + mutable S32 mRef; }; @@ -90,50 +90,50 @@ private: class LL_COMMON_API LLThreadSafeRefCount { public: - static void initThreadSafeRefCount(); // creates sMutex - static void cleanupThreadSafeRefCount(); // destroys sMutex + static void initThreadSafeRefCount(); // creates sMutex + static void cleanupThreadSafeRefCount(); // destroys sMutex private: - static LLMutex* sMutex; + static LLMutex* sMutex; protected: - virtual ~LLThreadSafeRefCount(); // use unref() + virtual ~LLThreadSafeRefCount(); // use unref() public: - LLThreadSafeRefCount(); - LLThreadSafeRefCount(const LLThreadSafeRefCount&); - LLThreadSafeRefCount& operator=(const LLThreadSafeRefCount& ref) - { - mRef = 0; - return *this; - } - - void ref() - { - mRef++; - } - - void unref() - { - llassert(mRef >= 1); - if ((--mRef) == 0) - { - // If we hit zero, the caller should be the only smart pointer owning the object and we can delete it. - // It is technically possible for a vanilla pointer to mess this up, or another thread to - // jump in, find this object, create another smart pointer and end up dangling, but if - // the code is that bad and not thread-safe, it's trouble already. - delete this; - } - } - - S32 getNumRefs() const - { - const S32 currentVal = mRef.CurrentValue(); - return currentVal; - } + LLThreadSafeRefCount(); + LLThreadSafeRefCount(const LLThreadSafeRefCount&); + LLThreadSafeRefCount& operator=(const LLThreadSafeRefCount& ref) + { + mRef = 0; + return *this; + } + + void ref() + { + mRef++; + } + + void unref() + { + llassert(mRef >= 1); + if ((--mRef) == 0) + { + // If we hit zero, the caller should be the only smart pointer owning the object and we can delete it. + // It is technically possible for a vanilla pointer to mess this up, or another thread to + // jump in, find this object, create another smart pointer and end up dangling, but if + // the code is that bad and not thread-safe, it's trouble already. + delete this; + } + } + + S32 getNumRefs() const + { + const S32 currentVal = mRef.CurrentValue(); + return currentVal; + } private: - LLAtomicS32 mRef; + LLAtomicS32 mRef; }; /** @@ -142,12 +142,12 @@ private: */ inline void intrusive_ptr_add_ref(LLThreadSafeRefCount* p) { - p->ref(); + p->ref(); } inline void intrusive_ptr_release(LLThreadSafeRefCount* p) { - p->unref(); + p->unref(); } /** @@ -156,12 +156,12 @@ inline void intrusive_ptr_release(LLThreadSafeRefCount* p) */ inline void intrusive_ptr_add_ref(LLRefCount* p) { - p->ref(); + p->ref(); } inline void intrusive_ptr_release(LLRefCount* p) { - p->unref(); + p->unref(); } #endif diff --git a/indra/llcommon/llregex.h b/indra/llcommon/llregex.h index 2b7f5e47c2..18da304aee 100644 --- a/indra/llcommon/llregex.h +++ b/indra/llcommon/llregex.h @@ -1,24 +1,24 @@ -/** +/** * @file llregex.h * * $LicenseInfo:firstyear=2021&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2021, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -30,60 +30,60 @@ template <typename S, typename M, typename R> LL_COMMON_API bool ll_regex_match(const S& string, M& match, const R& regex) { - try - { - return boost::regex_match(string, match, regex); - } - catch (const std::runtime_error& e) - { - LL_WARNS() << "error matching with '" << regex.str() << "': " - << e.what() << ":\n'" << string << "'" << LL_ENDL; - return false; - } + try + { + return boost::regex_match(string, match, regex); + } + catch (const std::runtime_error& e) + { + LL_WARNS() << "error matching with '" << regex.str() << "': " + << e.what() << ":\n'" << string << "'" << LL_ENDL; + return false; + } } template <typename S, typename R> LL_COMMON_API bool ll_regex_match(const S& string, const R& regex) { - try - { - return boost::regex_match(string, regex); - } - catch (const std::runtime_error& e) - { - LL_WARNS() << "error matching with '" << regex.str() << "': " - << e.what() << ":\n'" << string << "'" << LL_ENDL; - return false; - } + try + { + return boost::regex_match(string, regex); + } + catch (const std::runtime_error& e) + { + LL_WARNS() << "error matching with '" << regex.str() << "': " + << e.what() << ":\n'" << string << "'" << LL_ENDL; + return false; + } } template <typename S, typename M, typename R> bool ll_regex_search(const S& string, M& match, const R& regex) { - try - { - return boost::regex_search(string, match, regex); - } - catch (const std::runtime_error& e) - { - LL_WARNS() << "error searching with '" << regex.str() << "': " - << e.what() << ":\n'" << string << "'" << LL_ENDL; - return false; - } + try + { + return boost::regex_search(string, match, regex); + } + catch (const std::runtime_error& e) + { + LL_WARNS() << "error searching with '" << regex.str() << "': " + << e.what() << ":\n'" << string << "'" << LL_ENDL; + return false; + } } template <typename S, typename R> bool ll_regex_search(const S& string, const R& regex) { - try - { - return boost::regex_search(string, regex); - } - catch (const std::runtime_error& e) - { - LL_WARNS() << "error searching with '" << regex.str() << "': " - << e.what() << ":\n'" << string << "'" << LL_ENDL; - return false; - } + try + { + return boost::regex_search(string, regex); + } + catch (const std::runtime_error& e) + { + LL_WARNS() << "error searching with '" << regex.str() << "': " + << e.what() << ":\n'" << string << "'" << LL_ENDL; + return false; + } } #endif // LLREGEX_H diff --git a/indra/llcommon/llregistry.h b/indra/llcommon/llregistry.h index 89d56373e1..977c8d1935 100644 --- a/indra/llcommon/llregistry.h +++ b/indra/llcommon/llregistry.h @@ -1,353 +1,353 @@ -/** - * @file llregistry.h - * @brief template classes for registering name, value pairs in nested scopes, statically, etc. - * - * $LicenseInfo:firstyear=2001&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLREGISTRY_H -#define LL_LLREGISTRY_H - -#include <list> - -#include "llsingleton.h" -#include "llstl.h" - -template <typename T> -struct LLRegistryDefaultComparator -{ - bool operator()(const T& lhs, const T& rhs) const - { - using std::less; - return less<T>()(lhs, rhs); - } -}; - -template <typename KEY, typename VALUE, typename COMPARATOR = LLRegistryDefaultComparator<KEY> > -class LLRegistry -{ -public: - typedef LLRegistry<KEY, VALUE, COMPARATOR> registry_t; - typedef const KEY& ref_const_key_t; - typedef const VALUE& ref_const_value_t; - typedef const VALUE* ptr_const_value_t; - typedef VALUE* ptr_value_t; - - class Registrar - { - friend class LLRegistry<KEY, VALUE, COMPARATOR>; - public: - typedef std::map<KEY, VALUE, COMPARATOR> registry_map_t; - - bool add(ref_const_key_t key, ref_const_value_t value) - { - if (!mMap.insert(std::make_pair(key, value)).second) - { - LL_WARNS() << "Tried to register " << key << " but it was already registered!" << LL_ENDL; - return false; - } - return true; - } - - void remove(ref_const_key_t key) - { - mMap.erase(key); - } - - void replace(ref_const_key_t key, ref_const_value_t value) - { - mMap[key] = value; - } - - typename registry_map_t::const_iterator beginItems() const - { - return mMap.begin(); - } - - typename registry_map_t::const_iterator endItems() const - { - return mMap.end(); - } - - protected: - ptr_value_t getValue(ref_const_key_t key) - { - typename registry_map_t::iterator found_it = mMap.find(key); - if (found_it != mMap.end()) - { - return &(found_it->second); - } - return NULL; - } - - ptr_const_value_t getValue(ref_const_key_t key) const - { - typename registry_map_t::const_iterator found_it = mMap.find(key); - if (found_it != mMap.end()) - { - return &(found_it->second); - } - return NULL; - } - - // if the registry is used to store pointers, and null values are valid entries - // then use this function to check the existence of an entry - bool exists(ref_const_key_t key) const - { - return mMap.find(key) != mMap.end(); - } - - bool empty() const - { - return mMap.empty(); - } - - protected: - // use currentRegistrar() or defaultRegistrar() - Registrar() {} - ~Registrar() {} - - private: - registry_map_t mMap; - }; - - typedef typename std::list<Registrar*> scope_list_t; - typedef typename std::list<Registrar*>::iterator scope_list_iterator_t; - typedef typename std::list<Registrar*>::const_iterator scope_list_const_iterator_t; - - LLRegistry() - {} - - ~LLRegistry() {} - - ptr_value_t getValue(ref_const_key_t key) - { - for(Registrar* scope : mActiveScopes) - { - ptr_value_t valuep = scope->getValue(key); - if (valuep != NULL) return valuep; - } - return mDefaultRegistrar.getValue(key); - } - - ptr_const_value_t getValue(ref_const_key_t key) const - { - for(const Registrar* scope : mActiveScopes) - { - ptr_const_value_t valuep = scope->getValue(key); - if (valuep != NULL) return valuep; - } - return mDefaultRegistrar.getValue(key); - } - - bool exists(ref_const_key_t key) const - { - for(const Registrar* scope : mActiveScopes) - { - if (scope->exists(key)) return true; - } - - return mDefaultRegistrar.exists(key); - } - - bool empty() const - { - for(const Registrar* scope : mActiveScopes) - { - if (!scope->empty()) return false; - } - - return mDefaultRegistrar.empty(); - } - - - Registrar& defaultRegistrar() - { - return mDefaultRegistrar; - } - - const Registrar& defaultRegistrar() const - { - return mDefaultRegistrar; - } - - - Registrar& currentRegistrar() - { - if (!mActiveScopes.empty()) - { - return *mActiveScopes.front(); - } - - return mDefaultRegistrar; - } - - const Registrar& currentRegistrar() const - { - if (!mActiveScopes.empty()) - { - return *mActiveScopes.front(); - } - - return mDefaultRegistrar; - } - - -protected: - void addScope(Registrar* scope) - { - // newer scopes go up front - mActiveScopes.insert(mActiveScopes.begin(), scope); - } - - void removeScope(Registrar* scope) - { - // O(N) but should be near the beggining and N should be small and this is safer than storing iterators - scope_list_iterator_t iter = std::find(mActiveScopes.begin(), mActiveScopes.end(), scope); - if (iter != mActiveScopes.end()) - { - mActiveScopes.erase(iter); - } - } - -private: - scope_list_t mActiveScopes; - Registrar mDefaultRegistrar; -}; - -template <typename KEY, typename VALUE, typename DERIVED_TYPE, typename COMPARATOR = LLRegistryDefaultComparator<KEY> > -class LLRegistrySingleton - : public LLRegistry<KEY, VALUE, COMPARATOR>, - public LLSingleton<DERIVED_TYPE> -{ - // This LLRegistrySingleton doesn't use LLSINGLETON(LLRegistrySingleton) - // because the concrete class is actually DERIVED_TYPE, not - // LLRegistrySingleton. So each concrete subclass needs - // LLSINGLETON(whatever) -- not this intermediate base class. -public: - typedef LLRegistry<KEY, VALUE, COMPARATOR> registry_t; - typedef const KEY& ref_const_key_t; - typedef const VALUE& ref_const_value_t; - typedef VALUE* ptr_value_t; - typedef const VALUE* ptr_const_value_t; - typedef LLSingleton<DERIVED_TYPE> singleton_t; - - class ScopedRegistrar : public registry_t::Registrar - { - public: - ScopedRegistrar(bool push_scope = true) - { - if (push_scope) - { - pushScope(); - } - } - - ~ScopedRegistrar() - { - if (singleton_t::instanceExists()) - { - popScope(); - } - } - - void pushScope() - { - singleton_t::instance().addScope(this); - } - - void popScope() - { - singleton_t::instance().removeScope(this); - } - - ptr_value_t getValueFromScope(ref_const_key_t key) - { - return getValue(key); - } - - ptr_const_value_t getValueFromScope(ref_const_key_t key) const - { - return getValue(key); - } - - private: - typename std::list<typename registry_t::Registrar*>::iterator mListIt; - }; - - class StaticRegistrar : public registry_t::Registrar - { - public: - virtual ~StaticRegistrar() {} - StaticRegistrar(ref_const_key_t key, ref_const_value_t value) - { - if (singleton_t::instance().exists(key)) - { - LL_ERRS() << "Duplicate registry entry under key \"" << key << "\"" << LL_ENDL; - } - singleton_t::instance().mStaticScope->add(key, value); - } - }; - - // convenience functions - typedef typename LLRegistry<KEY, VALUE, COMPARATOR>::Registrar& ref_registrar_t; - static ref_registrar_t currentRegistrar() - { - return singleton_t::instance().registry_t::currentRegistrar(); - } - - static ref_registrar_t defaultRegistrar() - { - return singleton_t::instance().registry_t::defaultRegistrar(); - } - - static ptr_value_t getValue(ref_const_key_t key) - { - return singleton_t::instance().registry_t::getValue(key); - } - -protected: - // DERIVED_TYPE needs to derive from LLRegistrySingleton - LLRegistrySingleton() - : mStaticScope(NULL) - {} - - virtual void initSingleton() - { - mStaticScope = new ScopedRegistrar(); - } - - virtual ~LLRegistrySingleton() - { - delete mStaticScope; - } - -private: - ScopedRegistrar* mStaticScope; -}; - -// helper macro for doing static registration -#define GLUED_TOKEN(x, y) x ## y -#define GLUE_TOKENS(x, y) GLUED_TOKEN(x, y) -#define LLREGISTER_STATIC(REGISTRY, KEY, VALUE) static REGISTRY::StaticRegistrar GLUE_TOKENS(reg, __LINE__)(KEY, VALUE); - -#endif +/**
+ * @file llregistry.h
+ * @brief template classes for registering name, value pairs in nested scopes, statically, etc.
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLREGISTRY_H
+#define LL_LLREGISTRY_H
+
+#include <list>
+
+#include "llsingleton.h"
+#include "llstl.h"
+
+template <typename T>
+struct LLRegistryDefaultComparator
+{
+ bool operator()(const T& lhs, const T& rhs) const
+ {
+ using std::less;
+ return less<T>()(lhs, rhs);
+ }
+};
+
+template <typename KEY, typename VALUE, typename COMPARATOR = LLRegistryDefaultComparator<KEY> >
+class LLRegistry
+{
+public:
+ typedef LLRegistry<KEY, VALUE, COMPARATOR> registry_t;
+ typedef const KEY& ref_const_key_t;
+ typedef const VALUE& ref_const_value_t;
+ typedef const VALUE* ptr_const_value_t;
+ typedef VALUE* ptr_value_t;
+
+ class Registrar
+ {
+ friend class LLRegistry<KEY, VALUE, COMPARATOR>;
+ public:
+ typedef std::map<KEY, VALUE, COMPARATOR> registry_map_t;
+
+ bool add(ref_const_key_t key, ref_const_value_t value)
+ {
+ if (!mMap.insert(std::make_pair(key, value)).second)
+ {
+ LL_WARNS() << "Tried to register " << key << " but it was already registered!" << LL_ENDL;
+ return false;
+ }
+ return true;
+ }
+
+ void remove(ref_const_key_t key)
+ {
+ mMap.erase(key);
+ }
+
+ void replace(ref_const_key_t key, ref_const_value_t value)
+ {
+ mMap[key] = value;
+ }
+
+ typename registry_map_t::const_iterator beginItems() const
+ {
+ return mMap.begin();
+ }
+
+ typename registry_map_t::const_iterator endItems() const
+ {
+ return mMap.end();
+ }
+
+ protected:
+ ptr_value_t getValue(ref_const_key_t key)
+ {
+ typename registry_map_t::iterator found_it = mMap.find(key);
+ if (found_it != mMap.end())
+ {
+ return &(found_it->second);
+ }
+ return NULL;
+ }
+
+ ptr_const_value_t getValue(ref_const_key_t key) const
+ {
+ typename registry_map_t::const_iterator found_it = mMap.find(key);
+ if (found_it != mMap.end())
+ {
+ return &(found_it->second);
+ }
+ return NULL;
+ }
+
+ // if the registry is used to store pointers, and null values are valid entries
+ // then use this function to check the existence of an entry
+ bool exists(ref_const_key_t key) const
+ {
+ return mMap.find(key) != mMap.end();
+ }
+
+ bool empty() const
+ {
+ return mMap.empty();
+ }
+
+ protected:
+ // use currentRegistrar() or defaultRegistrar()
+ Registrar() {}
+ ~Registrar() {}
+
+ private:
+ registry_map_t mMap;
+ };
+
+ typedef typename std::list<Registrar*> scope_list_t;
+ typedef typename std::list<Registrar*>::iterator scope_list_iterator_t;
+ typedef typename std::list<Registrar*>::const_iterator scope_list_const_iterator_t;
+
+ LLRegistry()
+ {}
+
+ ~LLRegistry() {}
+
+ ptr_value_t getValue(ref_const_key_t key)
+ {
+ for(Registrar* scope : mActiveScopes)
+ {
+ ptr_value_t valuep = scope->getValue(key);
+ if (valuep != NULL) return valuep;
+ }
+ return mDefaultRegistrar.getValue(key);
+ }
+
+ ptr_const_value_t getValue(ref_const_key_t key) const
+ {
+ for(const Registrar* scope : mActiveScopes)
+ {
+ ptr_const_value_t valuep = scope->getValue(key);
+ if (valuep != NULL) return valuep;
+ }
+ return mDefaultRegistrar.getValue(key);
+ }
+
+ bool exists(ref_const_key_t key) const
+ {
+ for(const Registrar* scope : mActiveScopes)
+ {
+ if (scope->exists(key)) return true;
+ }
+
+ return mDefaultRegistrar.exists(key);
+ }
+
+ bool empty() const
+ {
+ for(const Registrar* scope : mActiveScopes)
+ {
+ if (!scope->empty()) return false;
+ }
+
+ return mDefaultRegistrar.empty();
+ }
+
+
+ Registrar& defaultRegistrar()
+ {
+ return mDefaultRegistrar;
+ }
+
+ const Registrar& defaultRegistrar() const
+ {
+ return mDefaultRegistrar;
+ }
+
+
+ Registrar& currentRegistrar()
+ {
+ if (!mActiveScopes.empty())
+ {
+ return *mActiveScopes.front();
+ }
+
+ return mDefaultRegistrar;
+ }
+
+ const Registrar& currentRegistrar() const
+ {
+ if (!mActiveScopes.empty())
+ {
+ return *mActiveScopes.front();
+ }
+
+ return mDefaultRegistrar;
+ }
+
+
+protected:
+ void addScope(Registrar* scope)
+ {
+ // newer scopes go up front
+ mActiveScopes.insert(mActiveScopes.begin(), scope);
+ }
+
+ void removeScope(Registrar* scope)
+ {
+ // O(N) but should be near the beggining and N should be small and this is safer than storing iterators
+ scope_list_iterator_t iter = std::find(mActiveScopes.begin(), mActiveScopes.end(), scope);
+ if (iter != mActiveScopes.end())
+ {
+ mActiveScopes.erase(iter);
+ }
+ }
+
+private:
+ scope_list_t mActiveScopes;
+ Registrar mDefaultRegistrar;
+};
+
+template <typename KEY, typename VALUE, typename DERIVED_TYPE, typename COMPARATOR = LLRegistryDefaultComparator<KEY> >
+class LLRegistrySingleton
+ : public LLRegistry<KEY, VALUE, COMPARATOR>,
+ public LLSingleton<DERIVED_TYPE>
+{
+ // This LLRegistrySingleton doesn't use LLSINGLETON(LLRegistrySingleton)
+ // because the concrete class is actually DERIVED_TYPE, not
+ // LLRegistrySingleton. So each concrete subclass needs
+ // LLSINGLETON(whatever) -- not this intermediate base class.
+public:
+ typedef LLRegistry<KEY, VALUE, COMPARATOR> registry_t;
+ typedef const KEY& ref_const_key_t;
+ typedef const VALUE& ref_const_value_t;
+ typedef VALUE* ptr_value_t;
+ typedef const VALUE* ptr_const_value_t;
+ typedef LLSingleton<DERIVED_TYPE> singleton_t;
+
+ class ScopedRegistrar : public registry_t::Registrar
+ {
+ public:
+ ScopedRegistrar(bool push_scope = true)
+ {
+ if (push_scope)
+ {
+ pushScope();
+ }
+ }
+
+ ~ScopedRegistrar()
+ {
+ if (singleton_t::instanceExists())
+ {
+ popScope();
+ }
+ }
+
+ void pushScope()
+ {
+ singleton_t::instance().addScope(this);
+ }
+
+ void popScope()
+ {
+ singleton_t::instance().removeScope(this);
+ }
+
+ ptr_value_t getValueFromScope(ref_const_key_t key)
+ {
+ return getValue(key);
+ }
+
+ ptr_const_value_t getValueFromScope(ref_const_key_t key) const
+ {
+ return getValue(key);
+ }
+
+ private:
+ typename std::list<typename registry_t::Registrar*>::iterator mListIt;
+ };
+
+ class StaticRegistrar : public registry_t::Registrar
+ {
+ public:
+ virtual ~StaticRegistrar() {}
+ StaticRegistrar(ref_const_key_t key, ref_const_value_t value)
+ {
+ if (singleton_t::instance().exists(key))
+ {
+ LL_ERRS() << "Duplicate registry entry under key \"" << key << "\"" << LL_ENDL;
+ }
+ singleton_t::instance().mStaticScope->add(key, value);
+ }
+ };
+
+ // convenience functions
+ typedef typename LLRegistry<KEY, VALUE, COMPARATOR>::Registrar& ref_registrar_t;
+ static ref_registrar_t currentRegistrar()
+ {
+ return singleton_t::instance().registry_t::currentRegistrar();
+ }
+
+ static ref_registrar_t defaultRegistrar()
+ {
+ return singleton_t::instance().registry_t::defaultRegistrar();
+ }
+
+ static ptr_value_t getValue(ref_const_key_t key)
+ {
+ return singleton_t::instance().registry_t::getValue(key);
+ }
+
+protected:
+ // DERIVED_TYPE needs to derive from LLRegistrySingleton
+ LLRegistrySingleton()
+ : mStaticScope(NULL)
+ {}
+
+ virtual void initSingleton()
+ {
+ mStaticScope = new ScopedRegistrar();
+ }
+
+ virtual ~LLRegistrySingleton()
+ {
+ delete mStaticScope;
+ }
+
+private:
+ ScopedRegistrar* mStaticScope;
+};
+
+// helper macro for doing static registration
+#define GLUED_TOKEN(x, y) x ## y
+#define GLUE_TOKENS(x, y) GLUED_TOKEN(x, y)
+#define LLREGISTER_STATIC(REGISTRY, KEY, VALUE) static REGISTRY::StaticRegistrar GLUE_TOKENS(reg, __LINE__)(KEY, VALUE);
+
+#endif
diff --git a/indra/llcommon/llrun.cpp b/indra/llcommon/llrun.cpp index a3b3fccf4b..82005b16bc 100644 --- a/indra/llcommon/llrun.cpp +++ b/indra/llcommon/llrun.cpp @@ -1,4 +1,4 @@ -/** +/** * @file llrun.cpp * @author Phoenix * @date 2006-02-16 @@ -7,21 +7,21 @@ * $LicenseInfo:firstyear=2006&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -33,136 +33,136 @@ static const LLRunner::run_handle_t INVALID_RUN_HANDLE = 0; -/** +/** * LLRunner */ LLRunner::LLRunner() : - mNextHandle(1) + mNextHandle(1) { } LLRunner::~LLRunner() { - mRunOnce.clear(); - mRunEvery.clear(); + mRunOnce.clear(); + mRunEvery.clear(); } size_t LLRunner::run() { - // We collect all of the runnables which should be run. Since the - // runnables are allowed to adjust the run list, we need to copy - // them into a temporary structure which then iterates over them - // to call out of this method into the runnables. - F64 now = LLFrameTimer::getTotalSeconds(); - run_list_t run_now; + // We collect all of the runnables which should be run. Since the + // runnables are allowed to adjust the run list, we need to copy + // them into a temporary structure which then iterates over them + // to call out of this method into the runnables. + F64 now = LLFrameTimer::getTotalSeconds(); + run_list_t run_now; - // Collect the run once. We erase the matching ones now because - // it's easier. If we find a reason to keep them around for a - // while, we can restructure this method. - LLRunner::run_list_t::iterator iter = mRunOnce.begin(); - for( ; iter != mRunOnce.end(); ) - { - if(now > (*iter).mNextRunAt) - { - run_now.push_back(*iter); - iter = mRunOnce.erase(iter); - } - else - { - ++iter; - } - } + // Collect the run once. We erase the matching ones now because + // it's easier. If we find a reason to keep them around for a + // while, we can restructure this method. + LLRunner::run_list_t::iterator iter = mRunOnce.begin(); + for( ; iter != mRunOnce.end(); ) + { + if(now > (*iter).mNextRunAt) + { + run_now.push_back(*iter); + iter = mRunOnce.erase(iter); + } + else + { + ++iter; + } + } - // Collect the ones that repeat. - iter = mRunEvery.begin(); - LLRunner::run_list_t::iterator end = mRunEvery.end(); - for( ; iter != end; ++iter ) - { - if(now > (*iter).mNextRunAt) - { - (*iter).mNextRunAt = now + (*iter).mIncrement; - run_now.push_back(*iter); - } - } + // Collect the ones that repeat. + iter = mRunEvery.begin(); + LLRunner::run_list_t::iterator end = mRunEvery.end(); + for( ; iter != end; ++iter ) + { + if(now > (*iter).mNextRunAt) + { + (*iter).mNextRunAt = now + (*iter).mIncrement; + run_now.push_back(*iter); + } + } - // Now, run them. - iter = run_now.begin(); - end = run_now.end(); - for( ; iter != end; ++iter ) - { - (*iter).mRunnable->run(this, (*iter).mHandle); - } - return run_now.size(); + // Now, run them. + iter = run_now.begin(); + end = run_now.end(); + for( ; iter != end; ++iter ) + { + (*iter).mRunnable->run(this, (*iter).mHandle); + } + return run_now.size(); } LLRunner::run_handle_t LLRunner::addRunnable( - run_ptr_t runnable, - ERunSchedule schedule, - F64 seconds) + run_ptr_t runnable, + ERunSchedule schedule, + F64 seconds) { - if(!runnable) return INVALID_RUN_HANDLE; - run_handle_t handle = mNextHandle++; - F64 next_run = LLFrameTimer::getTotalSeconds() + seconds; - LLRunInfo info(handle, runnable, schedule, next_run, seconds); - switch(schedule) - { - case RUN_IN: - // We could optimize this a bit by sorting this on entry. - mRunOnce.push_back(info); - break; - case RUN_EVERY: - mRunEvery.push_back(info); - break; - default: - handle = INVALID_RUN_HANDLE; - break; - } - return handle; + if(!runnable) return INVALID_RUN_HANDLE; + run_handle_t handle = mNextHandle++; + F64 next_run = LLFrameTimer::getTotalSeconds() + seconds; + LLRunInfo info(handle, runnable, schedule, next_run, seconds); + switch(schedule) + { + case RUN_IN: + // We could optimize this a bit by sorting this on entry. + mRunOnce.push_back(info); + break; + case RUN_EVERY: + mRunEvery.push_back(info); + break; + default: + handle = INVALID_RUN_HANDLE; + break; + } + return handle; } LLRunner::run_ptr_t LLRunner::removeRunnable(LLRunner::run_handle_t handle) { - LLRunner::run_ptr_t rv; - LLRunner::run_list_t::iterator iter = mRunOnce.begin(); - LLRunner::run_list_t::iterator end = mRunOnce.end(); - for( ; iter != end; ++iter) - { - if((*iter).mHandle == handle) - { - rv = (*iter).mRunnable; - mRunOnce.erase(iter); - return rv; - } - } + LLRunner::run_ptr_t rv; + LLRunner::run_list_t::iterator iter = mRunOnce.begin(); + LLRunner::run_list_t::iterator end = mRunOnce.end(); + for( ; iter != end; ++iter) + { + if((*iter).mHandle == handle) + { + rv = (*iter).mRunnable; + mRunOnce.erase(iter); + return rv; + } + } - iter = mRunEvery.begin(); - end = mRunEvery.end(); - for( ; iter != end; ++iter) - { - if((*iter).mHandle == handle) - { - rv = (*iter).mRunnable; - mRunEvery.erase(iter); - return rv; - } - } - return rv; + iter = mRunEvery.begin(); + end = mRunEvery.end(); + for( ; iter != end; ++iter) + { + if((*iter).mHandle == handle) + { + rv = (*iter).mRunnable; + mRunEvery.erase(iter); + return rv; + } + } + return rv; } -/** +/** * LLRunner::LLRunInfo */ LLRunner::LLRunInfo::LLRunInfo( - run_handle_t handle, - run_ptr_t runnable, - ERunSchedule schedule, - F64 next_run_after, - F64 increment) : - mHandle(handle), - mRunnable(runnable), - mSchedule(schedule), - mNextRunAt(next_run_after), - mIncrement(increment) + run_handle_t handle, + run_ptr_t runnable, + ERunSchedule schedule, + F64 next_run_after, + F64 increment) : + mHandle(handle), + mRunnable(runnable), + mSchedule(schedule), + mNextRunAt(next_run_after), + mIncrement(increment) { } diff --git a/indra/llcommon/llrun.h b/indra/llcommon/llrun.h index 42e3d9b47a..8061117ad5 100644 --- a/indra/llcommon/llrun.h +++ b/indra/llcommon/llrun.h @@ -1,4 +1,4 @@ -/** +/** * @file llrun.h * @author Phoenix * @date 2006-02-16 @@ -7,21 +7,21 @@ * $LicenseInfo:firstyear=2006&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -34,7 +34,7 @@ class LLRunnable; -/** +/** * @class LLRunner * @brief This class manages a set of LLRunnable objects. * @@ -45,96 +45,96 @@ class LLRunnable; class LL_COMMON_API LLRunner { public: - /** - * @brief The pointer to a runnable. - */ - typedef std::shared_ptr<LLRunnable> run_ptr_t; - - /** - * @brief The handle for use in the API. - */ - typedef S64 run_handle_t; - - /** - * @brief Constructor. - */ - LLRunner(); - - /** - * @brief Destructor. - */ - ~LLRunner(); - - /** - * @brief Enumeration which specifies when to run. - */ - enum ERunSchedule - { - // The runnable will run in N seconds - RUN_IN, - - // The run every N seconds - RUN_EVERY, - - // A count of the run types - RUN_SCHEDULE_COUNT - }; - - /** - * @brief Run the runnables which are scheduled to run - * - * @return Returns the number of runnables run. - */ - size_t run(); - - /** - * @brief Add a runnable to the run list. - * - * The handle of the runnable is unique to each addition. If the - * same runnable is added a second time with the same or different - * schedule, this method will return a new handle. - * @param runnable The runnable to run() on schedule. - * @param schedule Specifies the run schedule. - * @param seconds When to run the runnable as interpreted by schedule. - * @return Returns the handle to the runnable. handle == 0 means failure. - */ - run_handle_t addRunnable( - run_ptr_t runnable, - ERunSchedule schedule, - F64 seconds); - - /** - * @brief Remove the specified runnable. - * - * @param handle The handle of the runnable to remove. - * @return Returns the pointer to the runnable removed which may - * be empty. - */ - run_ptr_t removeRunnable(run_handle_t handle); + /** + * @brief The pointer to a runnable. + */ + typedef std::shared_ptr<LLRunnable> run_ptr_t; + + /** + * @brief The handle for use in the API. + */ + typedef S64 run_handle_t; + + /** + * @brief Constructor. + */ + LLRunner(); + + /** + * @brief Destructor. + */ + ~LLRunner(); + + /** + * @brief Enumeration which specifies when to run. + */ + enum ERunSchedule + { + // The runnable will run in N seconds + RUN_IN, + + // The run every N seconds + RUN_EVERY, + + // A count of the run types + RUN_SCHEDULE_COUNT + }; + + /** + * @brief Run the runnables which are scheduled to run + * + * @return Returns the number of runnables run. + */ + size_t run(); + + /** + * @brief Add a runnable to the run list. + * + * The handle of the runnable is unique to each addition. If the + * same runnable is added a second time with the same or different + * schedule, this method will return a new handle. + * @param runnable The runnable to run() on schedule. + * @param schedule Specifies the run schedule. + * @param seconds When to run the runnable as interpreted by schedule. + * @return Returns the handle to the runnable. handle == 0 means failure. + */ + run_handle_t addRunnable( + run_ptr_t runnable, + ERunSchedule schedule, + F64 seconds); + + /** + * @brief Remove the specified runnable. + * + * @param handle The handle of the runnable to remove. + * @return Returns the pointer to the runnable removed which may + * be empty. + */ + run_ptr_t removeRunnable(run_handle_t handle); protected: - struct LLRunInfo - { - run_handle_t mHandle; - run_ptr_t mRunnable; - ERunSchedule mSchedule; - F64 mNextRunAt; - F64 mIncrement; - LLRunInfo( - run_handle_t handle, - run_ptr_t runnable, - ERunSchedule schedule, - F64 next_run_at, - F64 increment); - }; - typedef std::vector<LLRunInfo> run_list_t; - run_list_t mRunOnce; - run_list_t mRunEvery; - run_handle_t mNextHandle; + struct LLRunInfo + { + run_handle_t mHandle; + run_ptr_t mRunnable; + ERunSchedule mSchedule; + F64 mNextRunAt; + F64 mIncrement; + LLRunInfo( + run_handle_t handle, + run_ptr_t runnable, + ERunSchedule schedule, + F64 next_run_at, + F64 increment); + }; + typedef std::vector<LLRunInfo> run_list_t; + run_list_t mRunOnce; + run_list_t mRunEvery; + run_handle_t mNextHandle; }; -/** +/** * @class LLRunnable * @brief Abstract base class for running some scheduled process. * @@ -146,17 +146,17 @@ protected: class LL_COMMON_API LLRunnable { public: - LLRunnable(); - virtual ~LLRunnable(); - - /** - * @brief Do the process. - * - * This method will be called from the LLRunner according to - * @param runner The Runner which call run(). - * @param handle The handle this run instance is run under. - */ - virtual void run(LLRunner* runner, S64 handle) = 0; + LLRunnable(); + virtual ~LLRunnable(); + + /** + * @brief Do the process. + * + * This method will be called from the LLRunner according to + * @param runner The Runner which call run(). + * @param handle The handle this run instance is run under. + */ + virtual void run(LLRunner* runner, S64 handle) = 0; }; #endif // LL_LLRUN_H diff --git a/indra/llcommon/llsafehandle.h b/indra/llcommon/llsafehandle.h index 9550e6253e..1d7e4f8220 100644 --- a/indra/llcommon/llsafehandle.h +++ b/indra/llcommon/llsafehandle.h @@ -1,32 +1,32 @@ -/** +/** * @file llsafehandle.h * @brief Reference-counted object where Object() is valid, not NULL. * * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #ifndef LLSAFEHANDLE_H #define LLSAFEHANDLE_H -#include "llerror.h" // *TODO: consider eliminating this +#include "llerror.h" // *TODO: consider eliminating this #include "llsingleton.h" /*==========================================================================*| @@ -57,142 +57,142 @@ not ever affect subsequent computations. // when error checking occurs at a different granularity or in a different part of the code // than when referencing an object via a LLSafeHandle. -template <class Type> +template <class Type> class LLSafeHandle { public: - LLSafeHandle() : - mPointer(NULL) - { - } - - LLSafeHandle(Type* ptr) : - mPointer(NULL) - { - assign(ptr); - } - - LLSafeHandle(const LLSafeHandle<Type>& ptr) : - mPointer(NULL) - { - assign(ptr.mPointer); - } - - // support conversion up the type hierarchy. See Item 45 in Effective C++, 3rd Ed. - template<typename Subclass> - LLSafeHandle(const LLSafeHandle<Subclass>& ptr) : - mPointer(NULL) - { - assign(ptr.get()); - } - - ~LLSafeHandle() - { - unref(); - } - - const Type* operator->() const { return nonNull(mPointer); } - Type* operator->() { return nonNull(mPointer); } - - Type* get() const { return mPointer; } - void clear() { assign(NULL); } - // we disallow these operations as they expose our null objects to direct manipulation - // and bypass the reference counting semantics - //const Type& operator*() const { return *nonNull(mPointer); } - //Type& operator*() { return *nonNull(mPointer); } - - operator BOOL() const { return mPointer != NULL; } - operator bool() const { return mPointer != NULL; } - bool operator!() const { return mPointer == NULL; } - bool isNull() const { return mPointer == NULL; } - bool notNull() const { return mPointer != NULL; } - - - operator Type*() const { return mPointer; } - operator const Type*() const { return mPointer; } - bool operator !=(Type* ptr) const { return (mPointer != ptr); } - bool operator ==(Type* ptr) const { return (mPointer == ptr); } - bool operator ==(const LLSafeHandle<Type>& ptr) const { return (mPointer == ptr.mPointer); } - bool operator < (const LLSafeHandle<Type>& ptr) const { return (mPointer < ptr.mPointer); } - bool operator > (const LLSafeHandle<Type>& ptr) const { return (mPointer > ptr.mPointer); } - - LLSafeHandle<Type>& operator =(Type* ptr) - { - assign(ptr); - return *this; - } - - LLSafeHandle<Type>& operator =(const LLSafeHandle<Type>& ptr) - { - assign(ptr.mPointer); - return *this; - } - - // support assignment up the type hierarchy. See Item 45 in Effective C++, 3rd Ed. - template<typename Subclass> - LLSafeHandle<Type>& operator =(const LLSafeHandle<Subclass>& ptr) - { - assign(ptr.get()); - return *this; - } + LLSafeHandle() : + mPointer(NULL) + { + } + + LLSafeHandle(Type* ptr) : + mPointer(NULL) + { + assign(ptr); + } + + LLSafeHandle(const LLSafeHandle<Type>& ptr) : + mPointer(NULL) + { + assign(ptr.mPointer); + } + + // support conversion up the type hierarchy. See Item 45 in Effective C++, 3rd Ed. + template<typename Subclass> + LLSafeHandle(const LLSafeHandle<Subclass>& ptr) : + mPointer(NULL) + { + assign(ptr.get()); + } + + ~LLSafeHandle() + { + unref(); + } + + const Type* operator->() const { return nonNull(mPointer); } + Type* operator->() { return nonNull(mPointer); } + + Type* get() const { return mPointer; } + void clear() { assign(NULL); } + // we disallow these operations as they expose our null objects to direct manipulation + // and bypass the reference counting semantics + //const Type& operator*() const { return *nonNull(mPointer); } + //Type& operator*() { return *nonNull(mPointer); } + + operator BOOL() const { return mPointer != NULL; } + operator bool() const { return mPointer != NULL; } + bool operator!() const { return mPointer == NULL; } + bool isNull() const { return mPointer == NULL; } + bool notNull() const { return mPointer != NULL; } + + + operator Type*() const { return mPointer; } + operator const Type*() const { return mPointer; } + bool operator !=(Type* ptr) const { return (mPointer != ptr); } + bool operator ==(Type* ptr) const { return (mPointer == ptr); } + bool operator ==(const LLSafeHandle<Type>& ptr) const { return (mPointer == ptr.mPointer); } + bool operator < (const LLSafeHandle<Type>& ptr) const { return (mPointer < ptr.mPointer); } + bool operator > (const LLSafeHandle<Type>& ptr) const { return (mPointer > ptr.mPointer); } + + LLSafeHandle<Type>& operator =(Type* ptr) + { + assign(ptr); + return *this; + } + + LLSafeHandle<Type>& operator =(const LLSafeHandle<Type>& ptr) + { + assign(ptr.mPointer); + return *this; + } + + // support assignment up the type hierarchy. See Item 45 in Effective C++, 3rd Ed. + template<typename Subclass> + LLSafeHandle<Type>& operator =(const LLSafeHandle<Subclass>& ptr) + { + assign(ptr.get()); + return *this; + } protected: - void ref() - { - if (mPointer) - { - mPointer->ref(); - } - } - - void unref() - { - if (mPointer) - { - Type *tempp = mPointer; - mPointer = NULL; - tempp->unref(); - if (mPointer != NULL) - { - LL_WARNS() << "Unreference did assignment to non-NULL because of destructor" << LL_ENDL; - unref(); - } - } - } - - void assign(Type* ptr) - { - if( mPointer != ptr ) - { - unref(); - mPointer = ptr; - ref(); - } - } - - // Define an LLSingleton whose sole purpose is to hold a "null instance" - // of the subject Type: the canonical instance to dereference if this - // LLSafeHandle actually holds a null pointer. We use LLSingleton - // specifically so that the "null instance" can be cleaned up at a well- - // defined time, specifically LLSingletonBase::deleteAll(). - // Of course, as with any LLSingleton, the "null instance" is only - // instantiated on demand -- in this case, if you actually try to - // dereference an LLSafeHandle containing null. - class NullInstanceHolder: public LLSingleton<NullInstanceHolder> - { - LLSINGLETON_EMPTY_CTOR(NullInstanceHolder); - ~NullInstanceHolder() {} - public: - Type mNullInstance; - }; - - static Type* nonNull(Type* ptr) - { - return ptr? ptr : &NullInstanceHolder::instance().mNullInstance; - } + void ref() + { + if (mPointer) + { + mPointer->ref(); + } + } + + void unref() + { + if (mPointer) + { + Type *tempp = mPointer; + mPointer = NULL; + tempp->unref(); + if (mPointer != NULL) + { + LL_WARNS() << "Unreference did assignment to non-NULL because of destructor" << LL_ENDL; + unref(); + } + } + } + + void assign(Type* ptr) + { + if( mPointer != ptr ) + { + unref(); + mPointer = ptr; + ref(); + } + } + + // Define an LLSingleton whose sole purpose is to hold a "null instance" + // of the subject Type: the canonical instance to dereference if this + // LLSafeHandle actually holds a null pointer. We use LLSingleton + // specifically so that the "null instance" can be cleaned up at a well- + // defined time, specifically LLSingletonBase::deleteAll(). + // Of course, as with any LLSingleton, the "null instance" is only + // instantiated on demand -- in this case, if you actually try to + // dereference an LLSafeHandle containing null. + class NullInstanceHolder: public LLSingleton<NullInstanceHolder> + { + LLSINGLETON_EMPTY_CTOR(NullInstanceHolder); + ~NullInstanceHolder() {} + public: + Type mNullInstance; + }; + + static Type* nonNull(Type* ptr) + { + return ptr? ptr : &NullInstanceHolder::instance().mNullInstance; + } protected: - Type* mPointer; + Type* mPointer; }; #endif diff --git a/indra/llcommon/llsd.cpp b/indra/llcommon/llsd.cpp index 590915e9d2..663ceac22b 100644 --- a/indra/llcommon/llsd.cpp +++ b/indra/llcommon/llsd.cpp @@ -1,25 +1,25 @@ -/** +/** * @file llsd.cpp * @brief LLSD flexible data system * * $LicenseInfo:firstyear=2005&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -53,13 +53,13 @@ bool was_negative(size_t i) #endif #ifdef NAME_UNNAMED_NAMESPACE -namespace LLSDUnnamedNamespace +namespace LLSDUnnamedNamespace #else -namespace +namespace #endif { - class ImplMap; - class ImplArray; + class ImplMap; + class ImplArray; } #ifdef NAME_UNNAMED_NAMESPACE @@ -70,740 +70,740 @@ namespace llsd { // statics -S32 sLLSDAllocationCount = 0; +S32 sLLSDAllocationCount = 0; S32 sLLSDNetObjects = 0; } // namespace llsd -#define ALLOC_LLSD_OBJECT { llsd::sLLSDNetObjects++; llsd::sLLSDAllocationCount++; } -#define FREE_LLSD_OBJECT { llsd::sLLSDNetObjects--; } +#define ALLOC_LLSD_OBJECT { llsd::sLLSDNetObjects++; llsd::sLLSDAllocationCount++; } +#define FREE_LLSD_OBJECT { llsd::sLLSDNetObjects--; } class LLSD::Impl - /**< This class is the abstract base class of the implementation of LLSD - It provides the reference counting implementation, and the default - implementation of most methods for most data types. It also serves - as a working implementation of the Undefined type. - - */ + /**< This class is the abstract base class of the implementation of LLSD + It provides the reference counting implementation, and the default + implementation of most methods for most data types. It also serves + as a working implementation of the Undefined type. + + */ { protected: - Impl(); - - enum StaticAllocationMarker { STATIC_USAGE_COUNT = 0xFFFFFFFF }; - Impl(StaticAllocationMarker); - ///< This constructor is used for static objects and causes the - // suppresses adjusting the debugging counters when they are - // finally initialized. - - virtual ~Impl(); - - bool shared() const { return (mUseCount > 1) && (mUseCount != STATIC_USAGE_COUNT); } - - U32 mUseCount; + Impl(); + + enum StaticAllocationMarker { STATIC_USAGE_COUNT = 0xFFFFFFFF }; + Impl(StaticAllocationMarker); + ///< This constructor is used for static objects and causes the + // suppresses adjusting the debugging counters when they are + // finally initialized. + + virtual ~Impl(); + + bool shared() const { return (mUseCount > 1) && (mUseCount != STATIC_USAGE_COUNT); } + + U32 mUseCount; public: - static void reset(Impl*& var, Impl* impl); - ///< safely set var to refer to the new impl (possibly shared) - - static Impl& safe( Impl*); - static const Impl& safe(const Impl*); - ///< since a NULL Impl* is used for undefined, this ensures there is - // always an object you call virtual member functions on - - virtual ImplMap& makeMap(Impl*& var); - virtual ImplArray& makeArray(Impl*& var); - ///< sure var is a modifiable, non-shared map or array - - virtual LLSD::Type type() const { return LLSD::TypeUndefined; } - - static void assignUndefined(LLSD::Impl*& var); - static void assign(LLSD::Impl*& var, const LLSD::Impl* other); - - virtual void assign(Impl*& var, LLSD::Boolean); - virtual void assign(Impl*& var, LLSD::Integer); - virtual void assign(Impl*& var, LLSD::Real); - virtual void assign(Impl*& var, const LLSD::String&); - virtual void assign(Impl*& var, const LLSD::UUID&); - virtual void assign(Impl*& var, const LLSD::Date&); - virtual void assign(Impl*& var, const LLSD::URI&); - virtual void assign(Impl*& var, const LLSD::Binary&); - ///< If the receiver is the right type and unshared, these are simple - // data assignments, othewise the default implementation handless - // constructing the proper Impl subclass - - virtual Boolean asBoolean() const { return false; } - virtual Integer asInteger() const { return 0; } - virtual Real asReal() const { return 0.0; } - virtual String asString() const { return std::string(); } - virtual UUID asUUID() const { return LLUUID(); } - virtual Date asDate() const { return LLDate(); } - virtual URI asURI() const { return LLURI(); } - virtual const Binary& asBinary() const { static const std::vector<U8> empty; return empty; } - - virtual const String& asStringRef() const { static const std::string empty; return empty; } - - virtual bool has(const String&) const { return false; } - virtual LLSD get(const String&) const { return LLSD(); } - virtual LLSD getKeys() const { return LLSD::emptyArray(); } - virtual void erase(const String&) { } - virtual const LLSD& ref(const String&) const{ return undef(); } - - virtual size_t size() const { return 0; } - virtual LLSD get(size_t) const { return LLSD(); } - virtual void erase(size_t) { } - virtual const LLSD& ref(size_t) const { return undef(); } - - virtual LLSD::map_const_iterator beginMap() const { return endMap(); } - virtual LLSD::map_const_iterator endMap() const { static const std::map<String, LLSD> empty; return empty.end(); } - virtual LLSD::array_const_iterator beginArray() const { return endArray(); } - virtual LLSD::array_const_iterator endArray() const { static const std::vector<LLSD> empty; return empty.end(); } - - virtual void dumpStats() const; - virtual void calcStats(S32 type_counts[], S32 share_counts[]) const; - // Container subclasses contain LLSD objects, rather than directly - // containing Impl objects. This helper forwards through LLSD. - void calcStats(const LLSD& llsd, S32 type_counts[], S32 share_counts[]) const - { - safe(llsd.impl).calcStats(type_counts, share_counts); - } - - static const Impl& getImpl(const LLSD& llsd) { return safe(llsd.impl); } - static Impl& getImpl(LLSD& llsd) { return safe(llsd.impl); } - - static const LLSD& undef(); - - static U32 sAllocationCount; - static U32 sOutstandingCount; + static void reset(Impl*& var, Impl* impl); + ///< safely set var to refer to the new impl (possibly shared) + + static Impl& safe( Impl*); + static const Impl& safe(const Impl*); + ///< since a NULL Impl* is used for undefined, this ensures there is + // always an object you call virtual member functions on + + virtual ImplMap& makeMap(Impl*& var); + virtual ImplArray& makeArray(Impl*& var); + ///< sure var is a modifiable, non-shared map or array + + virtual LLSD::Type type() const { return LLSD::TypeUndefined; } + + static void assignUndefined(LLSD::Impl*& var); + static void assign(LLSD::Impl*& var, const LLSD::Impl* other); + + virtual void assign(Impl*& var, LLSD::Boolean); + virtual void assign(Impl*& var, LLSD::Integer); + virtual void assign(Impl*& var, LLSD::Real); + virtual void assign(Impl*& var, const LLSD::String&); + virtual void assign(Impl*& var, const LLSD::UUID&); + virtual void assign(Impl*& var, const LLSD::Date&); + virtual void assign(Impl*& var, const LLSD::URI&); + virtual void assign(Impl*& var, const LLSD::Binary&); + ///< If the receiver is the right type and unshared, these are simple + // data assignments, othewise the default implementation handless + // constructing the proper Impl subclass + + virtual Boolean asBoolean() const { return false; } + virtual Integer asInteger() const { return 0; } + virtual Real asReal() const { return 0.0; } + virtual String asString() const { return std::string(); } + virtual UUID asUUID() const { return LLUUID(); } + virtual Date asDate() const { return LLDate(); } + virtual URI asURI() const { return LLURI(); } + virtual const Binary& asBinary() const { static const std::vector<U8> empty; return empty; } + + virtual const String& asStringRef() const { static const std::string empty; return empty; } + + virtual bool has(const String&) const { return false; } + virtual LLSD get(const String&) const { return LLSD(); } + virtual LLSD getKeys() const { return LLSD::emptyArray(); } + virtual void erase(const String&) { } + virtual const LLSD& ref(const String&) const{ return undef(); } + + virtual size_t size() const { return 0; } + virtual LLSD get(size_t) const { return LLSD(); } + virtual void erase(size_t) { } + virtual const LLSD& ref(size_t) const { return undef(); } + + virtual LLSD::map_const_iterator beginMap() const { return endMap(); } + virtual LLSD::map_const_iterator endMap() const { static const std::map<String, LLSD> empty; return empty.end(); } + virtual LLSD::array_const_iterator beginArray() const { return endArray(); } + virtual LLSD::array_const_iterator endArray() const { static const std::vector<LLSD> empty; return empty.end(); } + + virtual void dumpStats() const; + virtual void calcStats(S32 type_counts[], S32 share_counts[]) const; + // Container subclasses contain LLSD objects, rather than directly + // containing Impl objects. This helper forwards through LLSD. + void calcStats(const LLSD& llsd, S32 type_counts[], S32 share_counts[]) const + { + safe(llsd.impl).calcStats(type_counts, share_counts); + } + + static const Impl& getImpl(const LLSD& llsd) { return safe(llsd.impl); } + static Impl& getImpl(LLSD& llsd) { return safe(llsd.impl); } + + static const LLSD& undef(); + + static U32 sAllocationCount; + static U32 sOutstandingCount; }; #ifdef NAME_UNNAMED_NAMESPACE -namespace LLSDUnnamedNamespace +namespace LLSDUnnamedNamespace #else -namespace +namespace #endif { - template<LLSD::Type T, class Data, class DataRef = Data> - class ImplBase : public LLSD::Impl - ///< This class handles most of the work for a subclass of Impl - // for a given simple data type. Subclasses of this provide the - // conversion functions and a constructor. - { - protected: - Data mValue; - - typedef ImplBase Base; - - public: - ImplBase(DataRef value) : mValue(value) { } - - virtual LLSD::Type type() const { return T; } - - using LLSD::Impl::assign; // Unhiding base class virtuals... - virtual void assign(LLSD::Impl*& var, DataRef value) { - if (shared()) - { - Impl::assign(var, value); - } - else - { - mValue = value; - } - } - }; - - - class ImplBoolean - : public ImplBase<LLSD::TypeBoolean, LLSD::Boolean> - { - public: - ImplBoolean(LLSD::Boolean v) : Base(v) { } - - virtual LLSD::Boolean asBoolean() const { return mValue; } - virtual LLSD::Integer asInteger() const { return mValue ? 1 : 0; } - virtual LLSD::Real asReal() const { return mValue ? 1 : 0; } - virtual LLSD::String asString() const; - }; - - LLSD::String ImplBoolean::asString() const - // *NOTE: The reason that false is not converted to "false" is - // because that would break roundtripping, - // e.g. LLSD(false).asString().asBoolean(). There are many - // reasons for wanting LLSD("false").asBoolean() == true, such - // as "everything else seems to work that way". - { return mValue ? "true" : ""; } - - - class ImplInteger - : public ImplBase<LLSD::TypeInteger, LLSD::Integer> - { - public: - ImplInteger(LLSD::Integer v) : Base(v) { } - - virtual LLSD::Boolean asBoolean() const { return mValue != 0; } - virtual LLSD::Integer asInteger() const { return mValue; } - virtual LLSD::Real asReal() const { return mValue; } - virtual LLSD::String asString() const; - }; - - LLSD::String ImplInteger::asString() const - { return llformat("%d", mValue); } - - - class ImplReal - : public ImplBase<LLSD::TypeReal, LLSD::Real> - { - public: - ImplReal(LLSD::Real v) : Base(v) { } - - virtual LLSD::Boolean asBoolean() const; - virtual LLSD::Integer asInteger() const; - virtual LLSD::Real asReal() const { return mValue; } - virtual LLSD::String asString() const; - }; - - LLSD::Boolean ImplReal::asBoolean() const - { return !llisnan(mValue) && mValue != 0.0; } - - LLSD::Integer ImplReal::asInteger() const - { return !llisnan(mValue) ? (LLSD::Integer)mValue : 0; } - - LLSD::String ImplReal::asString() const - { return llformat("%lg", mValue); } - - - class ImplString - : public ImplBase<LLSD::TypeString, LLSD::String, const LLSD::String&> - { - public: - ImplString(const LLSD::String& v) : Base(v) { } - - virtual LLSD::Boolean asBoolean() const { return !mValue.empty(); } - virtual LLSD::Integer asInteger() const; - virtual LLSD::Real asReal() const; - virtual LLSD::String asString() const { return mValue; } - virtual LLSD::UUID asUUID() const { return LLUUID(mValue); } - virtual LLSD::Date asDate() const { return LLDate(mValue); } - virtual LLSD::URI asURI() const { return LLURI(mValue); } - virtual size_t size() const { return mValue.size(); } - virtual const LLSD::String& asStringRef() const { return mValue; } - }; - - LLSD::Integer ImplString::asInteger() const - { - // This must treat "1.23" not as an error, but as a number, which is - // then truncated down to an integer. Hence, this code doesn't call - // std::istringstream::operator>>(int&), which would not consume the - // ".23" portion. - - return (int)asReal(); - } - - LLSD::Real ImplString::asReal() const - { - F64 v = 0.0; - std::istringstream i_stream(mValue); - i_stream >> v; - - // we would probably like to ignore all trailing whitespace as - // well, but for now, simply eat the next character, and make - // sure we reached the end of the string. - // *NOTE: gcc 2.95 does not generate an eof() event on the - // stream operation above, so we manually get here to force it - // across platforms. - int c = i_stream.get(); - return ((EOF ==c) ? v : 0.0); - } - - - class ImplUUID - : public ImplBase<LLSD::TypeUUID, LLSD::UUID, const LLSD::UUID&> - { - public: - ImplUUID(const LLSD::UUID& v) : Base(v) { } - - virtual LLSD::String asString() const{ return mValue.asString(); } - virtual LLSD::UUID asUUID() const { return mValue; } - }; - - - class ImplDate - : public ImplBase<LLSD::TypeDate, LLSD::Date, const LLSD::Date&> - { - public: - ImplDate(const LLSD::Date& v) - : ImplBase<LLSD::TypeDate, LLSD::Date, const LLSD::Date&>(v) - { } - - virtual LLSD::Integer asInteger() const - { - return (LLSD::Integer)(mValue.secondsSinceEpoch()); - } - virtual LLSD::Real asReal() const - { - return mValue.secondsSinceEpoch(); - } - virtual LLSD::String asString() const{ return mValue.asString(); } - virtual LLSD::Date asDate() const { return mValue; } - }; - - - class ImplURI - : public ImplBase<LLSD::TypeURI, LLSD::URI, const LLSD::URI&> - { - public: - ImplURI(const LLSD::URI& v) : Base(v) { } - - virtual LLSD::String asString() const{ return mValue.asString(); } - virtual LLSD::URI asURI() const { return mValue; } - }; - - - class ImplBinary - : public ImplBase<LLSD::TypeBinary, LLSD::Binary, const LLSD::Binary&> - { - public: - ImplBinary(const LLSD::Binary& v) : Base(v) { } - - virtual const LLSD::Binary& asBinary() const{ return mValue; } - }; - - - class ImplMap : public LLSD::Impl - { - private: - typedef std::map<LLSD::String, LLSD> DataMap; - - DataMap mData; - - protected: - ImplMap(const DataMap& data) : mData(data) { } - - public: - ImplMap() { } - - virtual ImplMap& makeMap(LLSD::Impl*&); - - virtual LLSD::Type type() const { return LLSD::TypeMap; } - - virtual LLSD::Boolean asBoolean() const { return !mData.empty(); } - - virtual bool has(const LLSD::String&) const; - - using LLSD::Impl::get; // Unhiding get(size_t) - using LLSD::Impl::erase; // Unhiding erase(size_t) - using LLSD::Impl::ref; // Unhiding ref(size_t) - virtual LLSD get(const LLSD::String&) const; - virtual LLSD getKeys() const; - void insert(const LLSD::String& k, const LLSD& v); - virtual void erase(const LLSD::String&); - LLSD& ref(const LLSD::String&); - virtual const LLSD& ref(const LLSD::String&) const; - - virtual size_t size() const { return mData.size(); } - - LLSD::map_iterator beginMap() { return mData.begin(); } - LLSD::map_iterator endMap() { return mData.end(); } - virtual LLSD::map_const_iterator beginMap() const { return mData.begin(); } - virtual LLSD::map_const_iterator endMap() const { return mData.end(); } - - virtual void dumpStats() const; - virtual void calcStats(S32 type_counts[], S32 share_counts[]) const; - }; - - ImplMap& ImplMap::makeMap(LLSD::Impl*& var) - { + template<LLSD::Type T, class Data, class DataRef = Data> + class ImplBase : public LLSD::Impl + ///< This class handles most of the work for a subclass of Impl + // for a given simple data type. Subclasses of this provide the + // conversion functions and a constructor. + { + protected: + Data mValue; + + typedef ImplBase Base; + + public: + ImplBase(DataRef value) : mValue(value) { } + + virtual LLSD::Type type() const { return T; } + + using LLSD::Impl::assign; // Unhiding base class virtuals... + virtual void assign(LLSD::Impl*& var, DataRef value) { + if (shared()) + { + Impl::assign(var, value); + } + else + { + mValue = value; + } + } + }; + + + class ImplBoolean + : public ImplBase<LLSD::TypeBoolean, LLSD::Boolean> + { + public: + ImplBoolean(LLSD::Boolean v) : Base(v) { } + + virtual LLSD::Boolean asBoolean() const { return mValue; } + virtual LLSD::Integer asInteger() const { return mValue ? 1 : 0; } + virtual LLSD::Real asReal() const { return mValue ? 1 : 0; } + virtual LLSD::String asString() const; + }; + + LLSD::String ImplBoolean::asString() const + // *NOTE: The reason that false is not converted to "false" is + // because that would break roundtripping, + // e.g. LLSD(false).asString().asBoolean(). There are many + // reasons for wanting LLSD("false").asBoolean() == true, such + // as "everything else seems to work that way". + { return mValue ? "true" : ""; } + + + class ImplInteger + : public ImplBase<LLSD::TypeInteger, LLSD::Integer> + { + public: + ImplInteger(LLSD::Integer v) : Base(v) { } + + virtual LLSD::Boolean asBoolean() const { return mValue != 0; } + virtual LLSD::Integer asInteger() const { return mValue; } + virtual LLSD::Real asReal() const { return mValue; } + virtual LLSD::String asString() const; + }; + + LLSD::String ImplInteger::asString() const + { return llformat("%d", mValue); } + + + class ImplReal + : public ImplBase<LLSD::TypeReal, LLSD::Real> + { + public: + ImplReal(LLSD::Real v) : Base(v) { } + + virtual LLSD::Boolean asBoolean() const; + virtual LLSD::Integer asInteger() const; + virtual LLSD::Real asReal() const { return mValue; } + virtual LLSD::String asString() const; + }; + + LLSD::Boolean ImplReal::asBoolean() const + { return !llisnan(mValue) && mValue != 0.0; } + + LLSD::Integer ImplReal::asInteger() const + { return !llisnan(mValue) ? (LLSD::Integer)mValue : 0; } + + LLSD::String ImplReal::asString() const + { return llformat("%lg", mValue); } + + + class ImplString + : public ImplBase<LLSD::TypeString, LLSD::String, const LLSD::String&> + { + public: + ImplString(const LLSD::String& v) : Base(v) { } + + virtual LLSD::Boolean asBoolean() const { return !mValue.empty(); } + virtual LLSD::Integer asInteger() const; + virtual LLSD::Real asReal() const; + virtual LLSD::String asString() const { return mValue; } + virtual LLSD::UUID asUUID() const { return LLUUID(mValue); } + virtual LLSD::Date asDate() const { return LLDate(mValue); } + virtual LLSD::URI asURI() const { return LLURI(mValue); } + virtual size_t size() const { return mValue.size(); } + virtual const LLSD::String& asStringRef() const { return mValue; } + }; + + LLSD::Integer ImplString::asInteger() const + { + // This must treat "1.23" not as an error, but as a number, which is + // then truncated down to an integer. Hence, this code doesn't call + // std::istringstream::operator>>(int&), which would not consume the + // ".23" portion. + + return (int)asReal(); + } + + LLSD::Real ImplString::asReal() const + { + F64 v = 0.0; + std::istringstream i_stream(mValue); + i_stream >> v; + + // we would probably like to ignore all trailing whitespace as + // well, but for now, simply eat the next character, and make + // sure we reached the end of the string. + // *NOTE: gcc 2.95 does not generate an eof() event on the + // stream operation above, so we manually get here to force it + // across platforms. + int c = i_stream.get(); + return ((EOF ==c) ? v : 0.0); + } + + + class ImplUUID + : public ImplBase<LLSD::TypeUUID, LLSD::UUID, const LLSD::UUID&> + { + public: + ImplUUID(const LLSD::UUID& v) : Base(v) { } + + virtual LLSD::String asString() const{ return mValue.asString(); } + virtual LLSD::UUID asUUID() const { return mValue; } + }; + + + class ImplDate + : public ImplBase<LLSD::TypeDate, LLSD::Date, const LLSD::Date&> + { + public: + ImplDate(const LLSD::Date& v) + : ImplBase<LLSD::TypeDate, LLSD::Date, const LLSD::Date&>(v) + { } + + virtual LLSD::Integer asInteger() const + { + return (LLSD::Integer)(mValue.secondsSinceEpoch()); + } + virtual LLSD::Real asReal() const + { + return mValue.secondsSinceEpoch(); + } + virtual LLSD::String asString() const{ return mValue.asString(); } + virtual LLSD::Date asDate() const { return mValue; } + }; + + + class ImplURI + : public ImplBase<LLSD::TypeURI, LLSD::URI, const LLSD::URI&> + { + public: + ImplURI(const LLSD::URI& v) : Base(v) { } + + virtual LLSD::String asString() const{ return mValue.asString(); } + virtual LLSD::URI asURI() const { return mValue; } + }; + + + class ImplBinary + : public ImplBase<LLSD::TypeBinary, LLSD::Binary, const LLSD::Binary&> + { + public: + ImplBinary(const LLSD::Binary& v) : Base(v) { } + + virtual const LLSD::Binary& asBinary() const{ return mValue; } + }; + + + class ImplMap : public LLSD::Impl + { + private: + typedef std::map<LLSD::String, LLSD> DataMap; + + DataMap mData; + + protected: + ImplMap(const DataMap& data) : mData(data) { } + + public: + ImplMap() { } + + virtual ImplMap& makeMap(LLSD::Impl*&); + + virtual LLSD::Type type() const { return LLSD::TypeMap; } + + virtual LLSD::Boolean asBoolean() const { return !mData.empty(); } + + virtual bool has(const LLSD::String&) const; + + using LLSD::Impl::get; // Unhiding get(size_t) + using LLSD::Impl::erase; // Unhiding erase(size_t) + using LLSD::Impl::ref; // Unhiding ref(size_t) + virtual LLSD get(const LLSD::String&) const; + virtual LLSD getKeys() const; + void insert(const LLSD::String& k, const LLSD& v); + virtual void erase(const LLSD::String&); + LLSD& ref(const LLSD::String&); + virtual const LLSD& ref(const LLSD::String&) const; + + virtual size_t size() const { return mData.size(); } + + LLSD::map_iterator beginMap() { return mData.begin(); } + LLSD::map_iterator endMap() { return mData.end(); } + virtual LLSD::map_const_iterator beginMap() const { return mData.begin(); } + virtual LLSD::map_const_iterator endMap() const { return mData.end(); } + + virtual void dumpStats() const; + virtual void calcStats(S32 type_counts[], S32 share_counts[]) const; + }; + + ImplMap& ImplMap::makeMap(LLSD::Impl*& var) + { LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD; - if (shared()) - { - ImplMap* i = new ImplMap(mData); - Impl::assign(var, i); - return *i; - } - else - { - return *this; - } - } - - bool ImplMap::has(const LLSD::String& k) const - { + if (shared()) + { + ImplMap* i = new ImplMap(mData); + Impl::assign(var, i); + return *i; + } + else + { + return *this; + } + } + + bool ImplMap::has(const LLSD::String& k) const + { LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD; - DataMap::const_iterator i = mData.find(k); - return i != mData.end(); - } - - LLSD ImplMap::get(const LLSD::String& k) const - { + DataMap::const_iterator i = mData.find(k); + return i != mData.end(); + } + + LLSD ImplMap::get(const LLSD::String& k) const + { LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD; - DataMap::const_iterator i = mData.find(k); - return (i != mData.end()) ? i->second : LLSD(); - } + DataMap::const_iterator i = mData.find(k); + return (i != mData.end()) ? i->second : LLSD(); + } - LLSD ImplMap::getKeys() const - { + LLSD ImplMap::getKeys() const + { LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD; - LLSD keys = LLSD::emptyArray(); - DataMap::const_iterator iter = mData.begin(); - while (iter != mData.end()) - { - keys.append((*iter).first); - iter++; - } - return keys; - } - - void ImplMap::insert(const LLSD::String& k, const LLSD& v) - { + LLSD keys = LLSD::emptyArray(); + DataMap::const_iterator iter = mData.begin(); + while (iter != mData.end()) + { + keys.append((*iter).first); + iter++; + } + return keys; + } + + void ImplMap::insert(const LLSD::String& k, const LLSD& v) + { LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD; - mData.insert(DataMap::value_type(k, v)); - } - - void ImplMap::erase(const LLSD::String& k) - { + mData.insert(DataMap::value_type(k, v)); + } + + void ImplMap::erase(const LLSD::String& k) + { LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD; - mData.erase(k); - } - - LLSD& ImplMap::ref(const LLSD::String& k) - { - return mData[k]; - } - - const LLSD& ImplMap::ref(const LLSD::String& k) const - { - DataMap::const_iterator i = mData.lower_bound(k); - if (i == mData.end() || mData.key_comp()(k, i->first)) - { - return undef(); - } - - return i->second; - } - - void ImplMap::dumpStats() const - { - std::cout << "Map size: " << mData.size() << std::endl; - - std::cout << "LLSD Net Objects: " << llsd::sLLSDNetObjects << std::endl; - std::cout << "LLSD allocations: " << llsd::sLLSDAllocationCount << std::endl; - - std::cout << "LLSD::Impl Net Objects: " << sOutstandingCount << std::endl; - std::cout << "LLSD::Impl allocations: " << sAllocationCount << std::endl; - - Impl::dumpStats(); - } - - void ImplMap::calcStats(S32 type_counts[], S32 share_counts[]) const - { - LLSD::map_const_iterator iter = beginMap(); - while (iter != endMap()) - { - //std::cout << " " << (*iter).first << ": " << (*iter).second << std::endl; - Impl::calcStats((*iter).second, type_counts, share_counts); - iter++; - } - - // Add in the values for this map - Impl::calcStats(type_counts, share_counts); - } - - - class ImplArray : public LLSD::Impl - { - private: - typedef std::vector<LLSD> DataVector; - - DataVector mData; - - protected: - ImplArray(const DataVector& data) : mData(data) { } - - public: - ImplArray() { } - - virtual ImplArray& makeArray(Impl*&); - - virtual LLSD::Type type() const { return LLSD::TypeArray; } - - virtual LLSD::Boolean asBoolean() const { return !mData.empty(); } - - using LLSD::Impl::get; // Unhiding get(LLSD::String) - using LLSD::Impl::erase; // Unhiding erase(LLSD::String) - using LLSD::Impl::ref; // Unhiding ref(LLSD::String) - virtual size_t size() const; - virtual LLSD get(size_t) const; - void set(size_t, const LLSD&); - void insert(size_t, const LLSD&); - LLSD& append(const LLSD&); - virtual void erase(size_t); - LLSD& ref(size_t); - virtual const LLSD& ref(size_t) const; - - LLSD::array_iterator beginArray() { return mData.begin(); } - LLSD::array_iterator endArray() { return mData.end(); } - LLSD::reverse_array_iterator rbeginArray() { return mData.rbegin(); } - LLSD::reverse_array_iterator rendArray() { return mData.rend(); } - virtual LLSD::array_const_iterator beginArray() const { return mData.begin(); } - virtual LLSD::array_const_iterator endArray() const { return mData.end(); } - - virtual void calcStats(S32 type_counts[], S32 share_counts[]) const; - }; - - ImplArray& ImplArray::makeArray(Impl*& var) - { - if (shared()) - { - ImplArray* i = new ImplArray(mData); - Impl::assign(var, i); - return *i; - } - else - { - return *this; - } - } - - size_t ImplArray::size() const { return mData.size(); } - - LLSD ImplArray::get(size_t i) const - { - NEGATIVE_RETURN(i, LLSD()); - DataVector::size_type index = i; - - return (index < mData.size()) ? mData[index] : LLSD(); - } - - void ImplArray::set(size_t i, const LLSD& v) - { - NEGATIVE_EXIT(i); - DataVector::size_type index = i; - - if (index >= mData.size()) - { - mData.resize(index + 1); - } - - mData[index] = v; - } - - void ImplArray::insert(size_t i, const LLSD& v) - { - NEGATIVE_EXIT(i); - DataVector::size_type index = i; - - if (index >= mData.size()) // tbd - sanity check limit for index ? - { - mData.resize(index + 1); - } - - mData.insert(mData.begin() + index, v); - } - - LLSD& ImplArray::append(const LLSD& v) - { - mData.push_back(v); - return mData.back(); - } - - void ImplArray::erase(size_t i) - { - NEGATIVE_EXIT(i); - DataVector::size_type index = i; - - if (index < mData.size()) - { - mData.erase(mData.begin() + index); - } - } - - LLSD& ImplArray::ref(size_t i) - { - DataVector::size_type index = was_negative(i)? 0 : i; - - if (index >= mData.size()) - { - mData.resize(index + 1); - } - - return mData[index]; - } - - const LLSD& ImplArray::ref(size_t i) const - { - NEGATIVE_RETURN(i, undef()); - DataVector::size_type index = i; - - if (index >= mData.size()) - { - return undef(); - } - - return mData[index]; - } - - void ImplArray::calcStats(S32 type_counts[], S32 share_counts[]) const - { - LLSD::array_const_iterator iter = beginArray(); - while (iter != endArray()) - { // Add values for all items held in the array - Impl::calcStats((*iter), type_counts, share_counts); - iter++; - } - - // Add in the values for this array - Impl::calcStats(type_counts, share_counts); - } + mData.erase(k); + } + + LLSD& ImplMap::ref(const LLSD::String& k) + { + return mData[k]; + } + + const LLSD& ImplMap::ref(const LLSD::String& k) const + { + DataMap::const_iterator i = mData.lower_bound(k); + if (i == mData.end() || mData.key_comp()(k, i->first)) + { + return undef(); + } + + return i->second; + } + + void ImplMap::dumpStats() const + { + std::cout << "Map size: " << mData.size() << std::endl; + + std::cout << "LLSD Net Objects: " << llsd::sLLSDNetObjects << std::endl; + std::cout << "LLSD allocations: " << llsd::sLLSDAllocationCount << std::endl; + + std::cout << "LLSD::Impl Net Objects: " << sOutstandingCount << std::endl; + std::cout << "LLSD::Impl allocations: " << sAllocationCount << std::endl; + + Impl::dumpStats(); + } + + void ImplMap::calcStats(S32 type_counts[], S32 share_counts[]) const + { + LLSD::map_const_iterator iter = beginMap(); + while (iter != endMap()) + { + //std::cout << " " << (*iter).first << ": " << (*iter).second << std::endl; + Impl::calcStats((*iter).second, type_counts, share_counts); + iter++; + } + + // Add in the values for this map + Impl::calcStats(type_counts, share_counts); + } + + + class ImplArray : public LLSD::Impl + { + private: + typedef std::vector<LLSD> DataVector; + + DataVector mData; + + protected: + ImplArray(const DataVector& data) : mData(data) { } + + public: + ImplArray() { } + + virtual ImplArray& makeArray(Impl*&); + + virtual LLSD::Type type() const { return LLSD::TypeArray; } + + virtual LLSD::Boolean asBoolean() const { return !mData.empty(); } + + using LLSD::Impl::get; // Unhiding get(LLSD::String) + using LLSD::Impl::erase; // Unhiding erase(LLSD::String) + using LLSD::Impl::ref; // Unhiding ref(LLSD::String) + virtual size_t size() const; + virtual LLSD get(size_t) const; + void set(size_t, const LLSD&); + void insert(size_t, const LLSD&); + LLSD& append(const LLSD&); + virtual void erase(size_t); + LLSD& ref(size_t); + virtual const LLSD& ref(size_t) const; + + LLSD::array_iterator beginArray() { return mData.begin(); } + LLSD::array_iterator endArray() { return mData.end(); } + LLSD::reverse_array_iterator rbeginArray() { return mData.rbegin(); } + LLSD::reverse_array_iterator rendArray() { return mData.rend(); } + virtual LLSD::array_const_iterator beginArray() const { return mData.begin(); } + virtual LLSD::array_const_iterator endArray() const { return mData.end(); } + + virtual void calcStats(S32 type_counts[], S32 share_counts[]) const; + }; + + ImplArray& ImplArray::makeArray(Impl*& var) + { + if (shared()) + { + ImplArray* i = new ImplArray(mData); + Impl::assign(var, i); + return *i; + } + else + { + return *this; + } + } + + size_t ImplArray::size() const { return mData.size(); } + + LLSD ImplArray::get(size_t i) const + { + NEGATIVE_RETURN(i, LLSD()); + DataVector::size_type index = i; + + return (index < mData.size()) ? mData[index] : LLSD(); + } + + void ImplArray::set(size_t i, const LLSD& v) + { + NEGATIVE_EXIT(i); + DataVector::size_type index = i; + + if (index >= mData.size()) + { + mData.resize(index + 1); + } + + mData[index] = v; + } + + void ImplArray::insert(size_t i, const LLSD& v) + { + NEGATIVE_EXIT(i); + DataVector::size_type index = i; + + if (index >= mData.size()) // tbd - sanity check limit for index ? + { + mData.resize(index + 1); + } + + mData.insert(mData.begin() + index, v); + } + + LLSD& ImplArray::append(const LLSD& v) + { + mData.push_back(v); + return mData.back(); + } + + void ImplArray::erase(size_t i) + { + NEGATIVE_EXIT(i); + DataVector::size_type index = i; + + if (index < mData.size()) + { + mData.erase(mData.begin() + index); + } + } + + LLSD& ImplArray::ref(size_t i) + { + DataVector::size_type index = was_negative(i)? 0 : i; + + if (index >= mData.size()) + { + mData.resize(index + 1); + } + + return mData[index]; + } + + const LLSD& ImplArray::ref(size_t i) const + { + NEGATIVE_RETURN(i, undef()); + DataVector::size_type index = i; + + if (index >= mData.size()) + { + return undef(); + } + + return mData[index]; + } + + void ImplArray::calcStats(S32 type_counts[], S32 share_counts[]) const + { + LLSD::array_const_iterator iter = beginArray(); + while (iter != endArray()) + { // Add values for all items held in the array + Impl::calcStats((*iter), type_counts, share_counts); + iter++; + } + + // Add in the values for this array + Impl::calcStats(type_counts, share_counts); + } } LLSD::Impl::Impl() - : mUseCount(0) + : mUseCount(0) { - ++sAllocationCount; - ++sOutstandingCount; + ++sAllocationCount; + ++sOutstandingCount; } LLSD::Impl::Impl(StaticAllocationMarker) - : mUseCount(0) + : mUseCount(0) { } LLSD::Impl::~Impl() { - --sOutstandingCount; + --sOutstandingCount; } void LLSD::Impl::reset(Impl*& var, Impl* impl) { - if (impl && impl->mUseCount != STATIC_USAGE_COUNT) - { - ++impl->mUseCount; - } - if (var && var->mUseCount != STATIC_USAGE_COUNT && --var->mUseCount == 0) - { - delete var; - } - var = impl; + if (impl && impl->mUseCount != STATIC_USAGE_COUNT) + { + ++impl->mUseCount; + } + if (var && var->mUseCount != STATIC_USAGE_COUNT && --var->mUseCount == 0) + { + delete var; + } + var = impl; } LLSD::Impl& LLSD::Impl::safe(Impl* impl) { - static Impl theUndefined(STATIC_USAGE_COUNT); - return impl ? *impl : theUndefined; + static Impl theUndefined(STATIC_USAGE_COUNT); + return impl ? *impl : theUndefined; } const LLSD::Impl& LLSD::Impl::safe(const Impl* impl) { - static Impl theUndefined(STATIC_USAGE_COUNT); - return impl ? *impl : theUndefined; + static Impl theUndefined(STATIC_USAGE_COUNT); + return impl ? *impl : theUndefined; } ImplMap& LLSD::Impl::makeMap(Impl*& var) { LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD; - ImplMap* im = new ImplMap; - reset(var, im); - return *im; + ImplMap* im = new ImplMap; + reset(var, im); + return *im; } ImplArray& LLSD::Impl::makeArray(Impl*& var) { - ImplArray* ia = new ImplArray; - reset(var, ia); - return *ia; + ImplArray* ia = new ImplArray; + reset(var, ia); + return *ia; } void LLSD::Impl::assign(Impl*& var, const Impl* other) { - reset(var, const_cast<Impl*>(other)); + reset(var, const_cast<Impl*>(other)); } void LLSD::Impl::assignUndefined(Impl*& var) { - reset(var, 0); + reset(var, 0); } void LLSD::Impl::assign(Impl*& var, LLSD::Boolean v) { - reset(var, new ImplBoolean(v)); + reset(var, new ImplBoolean(v)); } void LLSD::Impl::assign(Impl*& var, LLSD::Integer v) { - reset(var, new ImplInteger(v)); + reset(var, new ImplInteger(v)); } void LLSD::Impl::assign(Impl*& var, LLSD::Real v) { - reset(var, new ImplReal(v)); + reset(var, new ImplReal(v)); } void LLSD::Impl::assign(Impl*& var, const LLSD::String& v) { - reset(var, new ImplString(v)); + reset(var, new ImplString(v)); } void LLSD::Impl::assign(Impl*& var, const LLSD::UUID& v) { - reset(var, new ImplUUID(v)); + reset(var, new ImplUUID(v)); } void LLSD::Impl::assign(Impl*& var, const LLSD::Date& v) { - reset(var, new ImplDate(v)); + reset(var, new ImplDate(v)); } void LLSD::Impl::assign(Impl*& var, const LLSD::URI& v) { - reset(var, new ImplURI(v)); + reset(var, new ImplURI(v)); } void LLSD::Impl::assign(Impl*& var, const LLSD::Binary& v) { - reset(var, new ImplBinary(v)); + reset(var, new ImplBinary(v)); } const LLSD& LLSD::Impl::undef() { - static const LLSD immutableUndefined; - return immutableUndefined; + static const LLSD immutableUndefined; + return immutableUndefined; } void LLSD::Impl::dumpStats() const { - S32 type_counts[LLSD::TypeLLSDNumTypes + 1]; - memset(&type_counts, 0, sizeof(type_counts)); - - S32 share_counts[LLSD::TypeLLSDNumTypes + 1]; - memset(&share_counts, 0, sizeof(share_counts)); - - // Add info from all the values this object has - calcStats(type_counts, share_counts); - - S32 type_index = LLSD::TypeLLSDTypeBegin; - while (type_index != LLSD::TypeLLSDTypeEnd) - { - std::cout << LLSD::typeString((LLSD::Type)type_index) << " type " - << type_counts[type_index] << " objects, " - << share_counts[type_index] << " shared" - << std::endl; - type_index++; - } + S32 type_counts[LLSD::TypeLLSDNumTypes + 1]; + memset(&type_counts, 0, sizeof(type_counts)); + + S32 share_counts[LLSD::TypeLLSDNumTypes + 1]; + memset(&share_counts, 0, sizeof(share_counts)); + + // Add info from all the values this object has + calcStats(type_counts, share_counts); + + S32 type_index = LLSD::TypeLLSDTypeBegin; + while (type_index != LLSD::TypeLLSDTypeEnd) + { + std::cout << LLSD::typeString((LLSD::Type)type_index) << " type " + << type_counts[type_index] << " objects, " + << share_counts[type_index] << " shared" + << std::endl; + type_index++; + } } void LLSD::Impl::calcStats(S32 type_counts[], S32 share_counts[]) const { - S32 tp = S32(type()); - if (0 <= tp && tp < LLSD::TypeLLSDNumTypes) - { - type_counts[tp]++; - if (shared()) - { - share_counts[tp]++; - } - } + S32 tp = S32(type()); + if (0 <= tp && tp < LLSD::TypeLLSDNumTypes) + { + type_counts[tp]++; + if (shared()) + { + share_counts[tp]++; + } + } } @@ -813,218 +813,218 @@ U32 LLSD::Impl::sOutstandingCount = 0; #ifdef NAME_UNNAMED_NAMESPACE -namespace LLSDUnnamedNamespace +namespace LLSDUnnamedNamespace #else -namespace +namespace #endif { - inline LLSD::Impl& safe(LLSD::Impl* impl) - { return LLSD::Impl::safe(impl); } - - inline ImplMap& makeMap(LLSD::Impl*& var) - { return safe(var).makeMap(var); } - - inline ImplArray& makeArray(LLSD::Impl*& var) - { return safe(var).makeArray(var); } + inline LLSD::Impl& safe(LLSD::Impl* impl) + { return LLSD::Impl::safe(impl); } + + inline ImplMap& makeMap(LLSD::Impl*& var) + { return safe(var).makeMap(var); } + + inline ImplArray& makeArray(LLSD::Impl*& var) + { return safe(var).makeArray(var); } } -LLSD::LLSD() : impl(0) { ALLOC_LLSD_OBJECT; } -LLSD::~LLSD() { FREE_LLSD_OBJECT; Impl::reset(impl, 0); } +LLSD::LLSD() : impl(0) { ALLOC_LLSD_OBJECT; } +LLSD::~LLSD() { FREE_LLSD_OBJECT; Impl::reset(impl, 0); } LLSD::LLSD(const LLSD& other) : impl(0) { ALLOC_LLSD_OBJECT; assign(other); } -void LLSD::assign(const LLSD& other) { Impl::assign(impl, other.impl); } +void LLSD::assign(const LLSD& other) { Impl::assign(impl, other.impl); } -void LLSD::clear() { Impl::assignUndefined(impl); } +void LLSD::clear() { Impl::assignUndefined(impl); } -LLSD::Type LLSD::type() const { return safe(impl).type(); } +LLSD::Type LLSD::type() const { return safe(impl).type(); } // Scalar Constructors -LLSD::LLSD(Boolean v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); } -LLSD::LLSD(Integer v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); } -LLSD::LLSD(Real v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); } -LLSD::LLSD(const UUID& v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); } -LLSD::LLSD(const String& v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); } -LLSD::LLSD(const Date& v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); } -LLSD::LLSD(const URI& v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); } -LLSD::LLSD(const Binary& v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); } +LLSD::LLSD(Boolean v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); } +LLSD::LLSD(Integer v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); } +LLSD::LLSD(Real v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); } +LLSD::LLSD(const UUID& v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); } +LLSD::LLSD(const String& v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); } +LLSD::LLSD(const Date& v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); } +LLSD::LLSD(const URI& v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); } +LLSD::LLSD(const Binary& v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); } // Scalar Assignment -void LLSD::assign(Boolean v) { safe(impl).assign(impl, v); } -void LLSD::assign(Integer v) { safe(impl).assign(impl, v); } -void LLSD::assign(Real v) { safe(impl).assign(impl, v); } -void LLSD::assign(const String& v) { safe(impl).assign(impl, v); } -void LLSD::assign(const UUID& v) { safe(impl).assign(impl, v); } -void LLSD::assign(const Date& v) { safe(impl).assign(impl, v); } -void LLSD::assign(const URI& v) { safe(impl).assign(impl, v); } -void LLSD::assign(const Binary& v) { safe(impl).assign(impl, v); } +void LLSD::assign(Boolean v) { safe(impl).assign(impl, v); } +void LLSD::assign(Integer v) { safe(impl).assign(impl, v); } +void LLSD::assign(Real v) { safe(impl).assign(impl, v); } +void LLSD::assign(const String& v) { safe(impl).assign(impl, v); } +void LLSD::assign(const UUID& v) { safe(impl).assign(impl, v); } +void LLSD::assign(const Date& v) { safe(impl).assign(impl, v); } +void LLSD::assign(const URI& v) { safe(impl).assign(impl, v); } +void LLSD::assign(const Binary& v) { safe(impl).assign(impl, v); } // Scalar Accessors -LLSD::Boolean LLSD::asBoolean() const { return safe(impl).asBoolean(); } -LLSD::Integer LLSD::asInteger() const { return safe(impl).asInteger(); } -LLSD::Real LLSD::asReal() const { return safe(impl).asReal(); } -LLSD::String LLSD::asString() const { return safe(impl).asString(); } -LLSD::UUID LLSD::asUUID() const { return safe(impl).asUUID(); } -LLSD::Date LLSD::asDate() const { return safe(impl).asDate(); } -LLSD::URI LLSD::asURI() const { return safe(impl).asURI(); } -const LLSD::Binary& LLSD::asBinary() const { return safe(impl).asBinary(); } +LLSD::Boolean LLSD::asBoolean() const { return safe(impl).asBoolean(); } +LLSD::Integer LLSD::asInteger() const { return safe(impl).asInteger(); } +LLSD::Real LLSD::asReal() const { return safe(impl).asReal(); } +LLSD::String LLSD::asString() const { return safe(impl).asString(); } +LLSD::UUID LLSD::asUUID() const { return safe(impl).asUUID(); } +LLSD::Date LLSD::asDate() const { return safe(impl).asDate(); } +LLSD::URI LLSD::asURI() const { return safe(impl).asURI(); } +const LLSD::Binary& LLSD::asBinary() const { return safe(impl).asBinary(); } const LLSD::String& LLSD::asStringRef() const { return safe(impl).asStringRef(); } // const char * helpers -LLSD::LLSD(const char* v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); } +LLSD::LLSD(const char* v) : impl(0) { ALLOC_LLSD_OBJECT; assign(v); } void LLSD::assign(const char* v) { - if(v) assign(std::string(v)); - else assign(std::string()); + if(v) assign(std::string(v)); + else assign(std::string()); } LLSD LLSD::emptyMap() { - LLSD v; - makeMap(v.impl); - return v; + LLSD v; + makeMap(v.impl); + return v; } -bool LLSD::has(const String& k) const { return safe(impl).has(k); } -LLSD LLSD::get(const String& k) const { return safe(impl).get(k); } -LLSD LLSD::getKeys() const { return safe(impl).getKeys(); } -void LLSD::insert(const String& k, const LLSD& v) { makeMap(impl).insert(k, v); } +bool LLSD::has(const String& k) const { return safe(impl).has(k); } +LLSD LLSD::get(const String& k) const { return safe(impl).get(k); } +LLSD LLSD::getKeys() const { return safe(impl).getKeys(); } +void LLSD::insert(const String& k, const LLSD& v) { makeMap(impl).insert(k, v); } LLSD& LLSD::with(const String& k, const LLSD& v) - { - makeMap(impl).insert(k, v); - return *this; - } -void LLSD::erase(const String& k) { makeMap(impl).erase(k); } + { + makeMap(impl).insert(k, v); + return *this; + } +void LLSD::erase(const String& k) { makeMap(impl).erase(k); } LLSD& LLSD::operator[](const String& k) -{ +{ LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD; - return makeMap(impl).ref(k); + return makeMap(impl).ref(k); } const LLSD& LLSD::operator[](const String& k) const -{ +{ LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD; - return safe(impl).ref(k); + return safe(impl).ref(k); } LLSD LLSD::emptyArray() { - LLSD v; - makeArray(v.impl); - return v; + LLSD v; + makeArray(v.impl); + return v; } -size_t LLSD::size() const { return safe(impl).size(); } - -LLSD LLSD::get(Integer i) const { return safe(impl).get(i); } +size_t LLSD::size() const { return safe(impl).size(); } + +LLSD LLSD::get(Integer i) const { return safe(impl).get(i); } void LLSD::set(Integer i, const LLSD& v){ makeArray(impl).set(i, v); } void LLSD::insert(Integer i, const LLSD& v) { makeArray(impl).insert(i, v); } LLSD& LLSD::with(Integer i, const LLSD& v) - { - makeArray(impl).insert(i, v); - return *this; - } -LLSD& LLSD::append(const LLSD& v) { return makeArray(impl).append(v); } -void LLSD::erase(Integer i) { makeArray(impl).erase(i); } + { + makeArray(impl).insert(i, v); + return *this; + } +LLSD& LLSD::append(const LLSD& v) { return makeArray(impl).append(v); } +void LLSD::erase(Integer i) { makeArray(impl).erase(i); } LLSD& LLSD::operator[](size_t i) -{ +{ LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD; - return makeArray(impl).ref(i); + return makeArray(impl).ref(i); } const LLSD& LLSD::operator[](size_t i) const -{ +{ LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD; return safe(impl).ref(i); } static const char *llsd_dump(const LLSD &llsd, bool useXMLFormat) { - // sStorage is used to hold the string representation of the llsd last - // passed into this function. If this function is never called (the - // normal case when not debugging), nothing is allocated. Otherwise - // sStorage will point to the result of the last call. This will actually - // be one leak, but since this is used only when running under the - // debugger, it should not be an issue. - static char *sStorage = NULL; - delete[] sStorage; - std::string out_string; - { - std::ostringstream out; - if (useXMLFormat) - out << LLSDXMLStreamer(llsd); - else - out << LLSDNotationStreamer(llsd); - out_string = out.str(); - } - auto len = out_string.length(); - sStorage = new char[len + 1]; - memcpy(sStorage, out_string.c_str(), len); - sStorage[len] = '\0'; - return sStorage; + // sStorage is used to hold the string representation of the llsd last + // passed into this function. If this function is never called (the + // normal case when not debugging), nothing is allocated. Otherwise + // sStorage will point to the result of the last call. This will actually + // be one leak, but since this is used only when running under the + // debugger, it should not be an issue. + static char *sStorage = NULL; + delete[] sStorage; + std::string out_string; + { + std::ostringstream out; + if (useXMLFormat) + out << LLSDXMLStreamer(llsd); + else + out << LLSDNotationStreamer(llsd); + out_string = out.str(); + } + auto len = out_string.length(); + sStorage = new char[len + 1]; + memcpy(sStorage, out_string.c_str(), len); + sStorage[len] = '\0'; + return sStorage; } /// Returns XML version of llsd -- only to be called from debugger const char *LLSD::dumpXML(const LLSD &llsd) { - return llsd_dump(llsd, true); + return llsd_dump(llsd, true); } /// Returns Notation version of llsd -- only to be called from debugger const char *LLSD::dump(const LLSD &llsd) { - return llsd_dump(llsd, false); + return llsd_dump(llsd, false); } -LLSD::map_iterator LLSD::beginMap() { return makeMap(impl).beginMap(); } -LLSD::map_iterator LLSD::endMap() { return makeMap(impl).endMap(); } -LLSD::map_const_iterator LLSD::beginMap() const { return safe(impl).beginMap(); } -LLSD::map_const_iterator LLSD::endMap() const { return safe(impl).endMap(); } +LLSD::map_iterator LLSD::beginMap() { return makeMap(impl).beginMap(); } +LLSD::map_iterator LLSD::endMap() { return makeMap(impl).endMap(); } +LLSD::map_const_iterator LLSD::beginMap() const { return safe(impl).beginMap(); } +LLSD::map_const_iterator LLSD::endMap() const { return safe(impl).endMap(); } -LLSD::array_iterator LLSD::beginArray() { return makeArray(impl).beginArray(); } -LLSD::array_iterator LLSD::endArray() { return makeArray(impl).endArray(); } -LLSD::array_const_iterator LLSD::beginArray() const{ return safe(impl).beginArray(); } -LLSD::array_const_iterator LLSD::endArray() const { return safe(impl).endArray(); } +LLSD::array_iterator LLSD::beginArray() { return makeArray(impl).beginArray(); } +LLSD::array_iterator LLSD::endArray() { return makeArray(impl).endArray(); } +LLSD::array_const_iterator LLSD::beginArray() const{ return safe(impl).beginArray(); } +LLSD::array_const_iterator LLSD::endArray() const { return safe(impl).endArray(); } -LLSD::reverse_array_iterator LLSD::rbeginArray() { return makeArray(impl).rbeginArray(); } -LLSD::reverse_array_iterator LLSD::rendArray() { return makeArray(impl).rendArray(); } +LLSD::reverse_array_iterator LLSD::rbeginArray() { return makeArray(impl).rbeginArray(); } +LLSD::reverse_array_iterator LLSD::rendArray() { return makeArray(impl).rendArray(); } namespace llsd { -U32 allocationCount() { return LLSD::Impl::sAllocationCount; } -U32 outstandingCount() { return LLSD::Impl::sOutstandingCount; } +U32 allocationCount() { return LLSD::Impl::sAllocationCount; } +U32 outstandingCount() { return LLSD::Impl::sOutstandingCount; } // Diagnostic dump of contents in an LLSD object -void dumpStats(const LLSD& llsd) { LLSD::Impl::getImpl(llsd).dumpStats(); } +void dumpStats(const LLSD& llsd) { LLSD::Impl::getImpl(llsd).dumpStats(); } } // namespace llsd // static -std::string LLSD::typeString(Type type) +std::string LLSD::typeString(Type type) { - static const char * sTypeNameArray[] = { - "Undefined", - "Boolean", - "Integer", - "Real", - "String", - "UUID", - "Date", - "URI", - "Binary", - "Map", - "Array" - }; - - if (0 <= type && type < LL_ARRAY_SIZE(sTypeNameArray)) - { - return sTypeNameArray[type]; - } - return STRINGIZE("** invalid type value " << type); + static const char * sTypeNameArray[] = { + "Undefined", + "Boolean", + "Integer", + "Real", + "String", + "UUID", + "Date", + "URI", + "Binary", + "Map", + "Array" + }; + + if (0 <= type && type < LL_ARRAY_SIZE(sTypeNameArray)) + { + return sTypeNameArray[type]; + } + return STRINGIZE("** invalid type value " << type); } diff --git a/indra/llcommon/llsd.h b/indra/llcommon/llsd.h index 8ed254919c..a5e735b561 100644 --- a/indra/llcommon/llsd.h +++ b/indra/llcommon/llsd.h @@ -1,25 +1,25 @@ -/** +/** * @file llsd.h * @brief LLSD flexible data system. * * $LicenseInfo:firstyear=2005&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -38,450 +38,450 @@ #include "lluuid.h" /** - LLSD provides a flexible data system similar to the data facilities of - dynamic languages like Perl and Python. It is created to support exchange - of structured data between loosely coupled systems. (Here, "loosely coupled" - means not compiled together into the same module.) - - Data in such exchanges must be highly tolerant of changes on either side - such as: - - recompilation - - implementation in a different langauge - - addition of extra parameters - - execution of older versions (with fewer parameters) - - To this aim, the C++ API of LLSD strives to be very easy to use, and to - default to "the right thing" wherever possible. It is extremely tolerant - of errors and unexpected situations. - - The fundamental class is LLSD. LLSD is a value holding object. It holds - one value that is either undefined, one of the scalar types, or a map or an - array. LLSD objects have value semantics (copying them copies the value, - though it can be considered efficient, due to sharing), and mutable. - - Undefined is the singular value given to LLSD objects that are not - initialized with any data. It is also used as the return value for - operations that return an LLSD. - - The scalar data types are: - - Boolean - true or false - - Integer - a 32 bit signed integer - - Real - a 64 IEEE 754 floating point value - - UUID - a 128 unique value - - String - a sequence of zero or more Unicode chracters - - Date - an absolute point in time, UTC, - with resolution to the second - - URI - a String that is a URI - - Binary - a sequence of zero or more octets (unsigned bytes) - - A map is a dictionary mapping String keys to LLSD values. The keys are - unique within a map, and have only one value (though that value could be - an LLSD array). - - An array is a sequence of zero or more LLSD values. - - Thread Safety - - In general, these LLSD classes offer *less* safety than STL container - classes. Implementations prior to this one were unsafe even when - completely unrelated LLSD trees were in two threads due to reference - sharing of special 'undefined' values that participated in the reference - counting mechanism. - - The dereference-before-refcount and aggressive tree sharing also make - it impractical to share an LLSD across threads. A strategy of passing - ownership or a copy to another thread is still difficult due to a lack - of a cloning interface but it can be done with some care. - - One way of transferring ownership is as follows: - - void method(const LLSD input) { - ... - LLSD * xfer_tree = new LLSD(); - { - // Top-level values - (* xfer_tree)['label'] = "Some text"; - (* xfer_tree)['mode'] = APP_MODE_CONSTANT; - - // There will be a second-level - LLSD subtree(LLSD::emptyMap()); - (* xfer_tree)['subtree'] = subtree; - - // Do *not* copy from LLSD objects via LLSD - // intermediaries. Only use plain-old-data - // types as intermediaries to prevent reference - // sharing. - subtree['value1'] = input['value1'].asInteger(); - subtree['value2'] = input['value2'].asString(); - - // Close scope and drop 'subtree's reference. - // Only xfer_tree has a reference to the second - // level data. - } - ... - // Transfer the LLSD pointer to another thread. Ownership - // transfers, this thread no longer has a reference to any - // part of the xfer_tree and there's nothing to free or - // release here. Receiving thread does need to delete the - // pointer when it is done with the LLSD. Transfer - // mechanism must perform correct data ordering operations - // as dictated by architecture. - other_thread.sendMessageAndPointer("Take This", xfer_tree); - xfer_tree = NULL; - - - Avoid this pattern which provides half of a race condition: - - void method(const LLSD input) { - ... - LLSD xfer_tree(LLSD::emptyMap()); - xfer_tree['label'] = "Some text"; - xfer_tree['mode'] = APP_MODE_CONSTANT; - ... - other_thread.sendMessageAndPointer("Take This", xfer_tree); - - - @nosubgrouping + LLSD provides a flexible data system similar to the data facilities of + dynamic languages like Perl and Python. It is created to support exchange + of structured data between loosely coupled systems. (Here, "loosely coupled" + means not compiled together into the same module.) + + Data in such exchanges must be highly tolerant of changes on either side + such as: + - recompilation + - implementation in a different langauge + - addition of extra parameters + - execution of older versions (with fewer parameters) + + To this aim, the C++ API of LLSD strives to be very easy to use, and to + default to "the right thing" wherever possible. It is extremely tolerant + of errors and unexpected situations. + + The fundamental class is LLSD. LLSD is a value holding object. It holds + one value that is either undefined, one of the scalar types, or a map or an + array. LLSD objects have value semantics (copying them copies the value, + though it can be considered efficient, due to sharing), and mutable. + + Undefined is the singular value given to LLSD objects that are not + initialized with any data. It is also used as the return value for + operations that return an LLSD. + + The scalar data types are: + - Boolean - true or false + - Integer - a 32 bit signed integer + - Real - a 64 IEEE 754 floating point value + - UUID - a 128 unique value + - String - a sequence of zero or more Unicode chracters + - Date - an absolute point in time, UTC, + with resolution to the second + - URI - a String that is a URI + - Binary - a sequence of zero or more octets (unsigned bytes) + + A map is a dictionary mapping String keys to LLSD values. The keys are + unique within a map, and have only one value (though that value could be + an LLSD array). + + An array is a sequence of zero or more LLSD values. + + Thread Safety + + In general, these LLSD classes offer *less* safety than STL container + classes. Implementations prior to this one were unsafe even when + completely unrelated LLSD trees were in two threads due to reference + sharing of special 'undefined' values that participated in the reference + counting mechanism. + + The dereference-before-refcount and aggressive tree sharing also make + it impractical to share an LLSD across threads. A strategy of passing + ownership or a copy to another thread is still difficult due to a lack + of a cloning interface but it can be done with some care. + + One way of transferring ownership is as follows: + + void method(const LLSD input) { + ... + LLSD * xfer_tree = new LLSD(); + { + // Top-level values + (* xfer_tree)['label'] = "Some text"; + (* xfer_tree)['mode'] = APP_MODE_CONSTANT; + + // There will be a second-level + LLSD subtree(LLSD::emptyMap()); + (* xfer_tree)['subtree'] = subtree; + + // Do *not* copy from LLSD objects via LLSD + // intermediaries. Only use plain-old-data + // types as intermediaries to prevent reference + // sharing. + subtree['value1'] = input['value1'].asInteger(); + subtree['value2'] = input['value2'].asString(); + + // Close scope and drop 'subtree's reference. + // Only xfer_tree has a reference to the second + // level data. + } + ... + // Transfer the LLSD pointer to another thread. Ownership + // transfers, this thread no longer has a reference to any + // part of the xfer_tree and there's nothing to free or + // release here. Receiving thread does need to delete the + // pointer when it is done with the LLSD. Transfer + // mechanism must perform correct data ordering operations + // as dictated by architecture. + other_thread.sendMessageAndPointer("Take This", xfer_tree); + xfer_tree = NULL; + + + Avoid this pattern which provides half of a race condition: + + void method(const LLSD input) { + ... + LLSD xfer_tree(LLSD::emptyMap()); + xfer_tree['label'] = "Some text"; + xfer_tree['mode'] = APP_MODE_CONSTANT; + ... + other_thread.sendMessageAndPointer("Take This", xfer_tree); + + + @nosubgrouping */ // Normally undefined, used for diagnostics -//#define LLSD_DEBUG_INFO 1 +//#define LLSD_DEBUG_INFO 1 class LL_COMMON_API LLSD { public: - LLSD(); ///< initially Undefined - ~LLSD(); ///< this class may NOT be subclassed - - /** @name Copyable and Assignable */ - //@{ - LLSD(const LLSD&); - void assign(const LLSD& other); - LLSD& operator=(const LLSD& other) { assign(other); return *this; } - - //@} - - void clear(); ///< resets to Undefined - - - /** @name Scalar Types - The scalar types, and how they map onto C++ - */ - //@{ - typedef bool Boolean; - typedef S32 Integer; - typedef F64 Real; - typedef std::string String; - typedef LLUUID UUID; - typedef LLDate Date; - typedef LLURI URI; - typedef std::vector<U8> Binary; - //@} - - /** @name Scalar Constructors */ - //@{ - LLSD(Boolean); - LLSD(Integer); - LLSD(Real); - LLSD(const String&); - LLSD(const UUID&); - LLSD(const Date&); - LLSD(const URI&); - LLSD(const Binary&); - //@} - - /** @name Convenience Constructors */ - //@{ - // support construction from size_t et al. - template <typename VALUE, - typename std::enable_if<std::is_integral<VALUE>::value && - ! std::is_same<VALUE, Boolean>::value, - bool>::type = true> - LLSD(VALUE v): LLSD(Integer(narrow<VALUE>(v))) {} - // support construction from F32 et al. - template <typename VALUE, - typename std::enable_if<std::is_floating_point<VALUE>::value, - bool>::type = true> - LLSD(VALUE v): LLSD(Real(narrow<VALUE>(v))) {} - //@} - - /** @name Scalar Assignment */ - //@{ - void assign(Boolean); - void assign(Integer); - void assign(Real); - void assign(const String&); - void assign(const UUID&); - void assign(const Date&); - void assign(const URI&); - void assign(const Binary&); - - LLSD& operator=(Boolean v) { assign(v); return *this; } - LLSD& operator=(Integer v) { assign(v); return *this; } - LLSD& operator=(Real v) { assign(v); return *this; } - LLSD& operator=(const String& v) { assign(v); return *this; } - LLSD& operator=(const UUID& v) { assign(v); return *this; } - LLSD& operator=(const Date& v) { assign(v); return *this; } - LLSD& operator=(const URI& v) { assign(v); return *this; } - LLSD& operator=(const Binary& v) { assign(v); return *this; } - //@} - - /** - @name Scalar Accessors - @brief Fetch a scalar value, converting if needed and possible - - Conversion among the basic types, Boolean, Integer, Real and String, is - fully defined. Each type can be converted to another with a reasonable - interpretation. These conversions can be used as a convenience even - when you know the data is in one format, but you want it in another. Of - course, many of these conversions lose information. - - Note: These conversions are not the same as Perl's. In particular, when - converting a String to a Boolean, only the empty string converts to - false. Converting the String "0" to Boolean results in true. - - Conversion to and from UUID, Date, and URI is only defined to and from - String. Conversion is defined to be information preserving for valid - values of those types. These conversions can be used when one needs to - convert data to or from another system that cannot handle these types - natively, but can handle strings. - - Conversion to and from Binary isn't defined. - - Conversion of the Undefined value to any scalar type results in a - reasonable null or zero value for the type. - */ - //@{ - Boolean asBoolean() const; - Integer asInteger() const; - Real asReal() const; - String asString() const; - UUID asUUID() const; - Date asDate() const; - URI asURI() const; - const Binary& asBinary() const; - - // asStringRef on any non-string type will return a ref to an empty string. - const String& asStringRef() const; - - operator Boolean() const { return asBoolean(); } - operator Integer() const { return asInteger(); } - operator Real() const { return asReal(); } - operator String() const { return asString(); } - operator UUID() const { return asUUID(); } - operator Date() const { return asDate(); } - operator URI() const { return asURI(); } - operator Binary() const { return asBinary(); } - - // This is needed because most platforms do not automatically - // convert the boolean negation as a bool in an if statement. - bool operator!() const {return !asBoolean();} - //@} - - /** @name Character Pointer Helpers - These are helper routines to make working with char* as easy as - working with strings. - */ - //@{ - LLSD(const char*); - void assign(const char*); - LLSD& operator=(const char* v) { assign(v); return *this; } - //@} - - /** @name Map Values */ - //@{ - static LLSD emptyMap(); - - bool has(const String&) const; - LLSD get(const String&) const; - LLSD getKeys() const; // Return an LLSD array with keys as strings - void insert(const String&, const LLSD&); - void erase(const String&); - LLSD& with(const String&, const LLSD&); - - LLSD& operator[](const String&); - LLSD& operator[](const char* c) + LLSD(); ///< initially Undefined + ~LLSD(); ///< this class may NOT be subclassed + + /** @name Copyable and Assignable */ + //@{ + LLSD(const LLSD&); + void assign(const LLSD& other); + LLSD& operator=(const LLSD& other) { assign(other); return *this; } + + //@} + + void clear(); ///< resets to Undefined + + + /** @name Scalar Types + The scalar types, and how they map onto C++ + */ + //@{ + typedef bool Boolean; + typedef S32 Integer; + typedef F64 Real; + typedef std::string String; + typedef LLUUID UUID; + typedef LLDate Date; + typedef LLURI URI; + typedef std::vector<U8> Binary; + //@} + + /** @name Scalar Constructors */ + //@{ + LLSD(Boolean); + LLSD(Integer); + LLSD(Real); + LLSD(const String&); + LLSD(const UUID&); + LLSD(const Date&); + LLSD(const URI&); + LLSD(const Binary&); + //@} + + /** @name Convenience Constructors */ + //@{ + // support construction from size_t et al. + template <typename VALUE, + typename std::enable_if<std::is_integral<VALUE>::value && + ! std::is_same<VALUE, Boolean>::value, + bool>::type = true> + LLSD(VALUE v): LLSD(Integer(narrow<VALUE>(v))) {} + // support construction from F32 et al. + template <typename VALUE, + typename std::enable_if<std::is_floating_point<VALUE>::value, + bool>::type = true> + LLSD(VALUE v): LLSD(Real(narrow<VALUE>(v))) {} + //@} + + /** @name Scalar Assignment */ + //@{ + void assign(Boolean); + void assign(Integer); + void assign(Real); + void assign(const String&); + void assign(const UUID&); + void assign(const Date&); + void assign(const URI&); + void assign(const Binary&); + + LLSD& operator=(Boolean v) { assign(v); return *this; } + LLSD& operator=(Integer v) { assign(v); return *this; } + LLSD& operator=(Real v) { assign(v); return *this; } + LLSD& operator=(const String& v) { assign(v); return *this; } + LLSD& operator=(const UUID& v) { assign(v); return *this; } + LLSD& operator=(const Date& v) { assign(v); return *this; } + LLSD& operator=(const URI& v) { assign(v); return *this; } + LLSD& operator=(const Binary& v) { assign(v); return *this; } + //@} + + /** + @name Scalar Accessors + @brief Fetch a scalar value, converting if needed and possible + + Conversion among the basic types, Boolean, Integer, Real and String, is + fully defined. Each type can be converted to another with a reasonable + interpretation. These conversions can be used as a convenience even + when you know the data is in one format, but you want it in another. Of + course, many of these conversions lose information. + + Note: These conversions are not the same as Perl's. In particular, when + converting a String to a Boolean, only the empty string converts to + false. Converting the String "0" to Boolean results in true. + + Conversion to and from UUID, Date, and URI is only defined to and from + String. Conversion is defined to be information preserving for valid + values of those types. These conversions can be used when one needs to + convert data to or from another system that cannot handle these types + natively, but can handle strings. + + Conversion to and from Binary isn't defined. + + Conversion of the Undefined value to any scalar type results in a + reasonable null or zero value for the type. + */ + //@{ + Boolean asBoolean() const; + Integer asInteger() const; + Real asReal() const; + String asString() const; + UUID asUUID() const; + Date asDate() const; + URI asURI() const; + const Binary& asBinary() const; + + // asStringRef on any non-string type will return a ref to an empty string. + const String& asStringRef() const; + + operator Boolean() const { return asBoolean(); } + operator Integer() const { return asInteger(); } + operator Real() const { return asReal(); } + operator String() const { return asString(); } + operator UUID() const { return asUUID(); } + operator Date() const { return asDate(); } + operator URI() const { return asURI(); } + operator Binary() const { return asBinary(); } + + // This is needed because most platforms do not automatically + // convert the boolean negation as a bool in an if statement. + bool operator!() const {return !asBoolean();} + //@} + + /** @name Character Pointer Helpers + These are helper routines to make working with char* as easy as + working with strings. + */ + //@{ + LLSD(const char*); + void assign(const char*); + LLSD& operator=(const char* v) { assign(v); return *this; } + //@} + + /** @name Map Values */ + //@{ + static LLSD emptyMap(); + + bool has(const String&) const; + LLSD get(const String&) const; + LLSD getKeys() const; // Return an LLSD array with keys as strings + void insert(const String&, const LLSD&); + void erase(const String&); + LLSD& with(const String&, const LLSD&); + + LLSD& operator[](const String&); + LLSD& operator[](const char* c) { LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD; return (*this)[String(c)]; } - const LLSD& operator[](const String&) const; - const LLSD& operator[](const char* c) const + const LLSD& operator[](const String&) const; + const LLSD& operator[](const char* c) const { LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD; return (*this)[String(c)]; } - //@} - - /** @name Array Values */ - //@{ - static LLSD emptyArray(); - - LLSD get(Integer) const; - void set(Integer, const LLSD&); - void insert(Integer, const LLSD&); - LLSD& append(const LLSD&); - void erase(Integer); - LLSD& with(Integer, const LLSD&); - - // accept size_t so we can index relative to size() - const LLSD& operator[](size_t) const; - LLSD& operator[](size_t); - // template overloads to support int literals, U32 et al. - template <typename IDX, - typename std::enable_if<std::is_convertible<IDX, size_t>::value, - bool>::type = true> - const LLSD& operator[](IDX i) const { return (*this)[size_t(i)]; } - template <typename IDX, - typename std::enable_if<std::is_convertible<IDX, size_t>::value, - bool>::type = true> - LLSD& operator[](IDX i) { return (*this)[size_t(i)]; } - //@} - - /** @name Iterators */ - //@{ - size_t size() const; - - typedef std::map<String, LLSD>::iterator map_iterator; - typedef std::map<String, LLSD>::const_iterator map_const_iterator; - - map_iterator beginMap(); - map_iterator endMap(); - map_const_iterator beginMap() const; - map_const_iterator endMap() const; - - typedef std::vector<LLSD>::iterator array_iterator; - typedef std::vector<LLSD>::const_iterator array_const_iterator; - typedef std::vector<LLSD>::reverse_iterator reverse_array_iterator; - - array_iterator beginArray(); - array_iterator endArray(); - array_const_iterator beginArray() const; - array_const_iterator endArray() const; - - reverse_array_iterator rbeginArray(); - reverse_array_iterator rendArray(); - //@} - - /** @name Type Testing */ - //@{ - enum Type { - TypeUndefined = 0, - TypeBoolean, - TypeInteger, - TypeReal, - TypeString, - TypeUUID, - TypeDate, - TypeURI, - TypeBinary, - TypeMap, - TypeArray, - TypeLLSDTypeEnd, - TypeLLSDTypeBegin = TypeUndefined, - TypeLLSDNumTypes = (TypeLLSDTypeEnd - TypeLLSDTypeBegin) - }; - - Type type() const; - - bool isUndefined() const { return type() == TypeUndefined; } - bool isDefined() const { return type() != TypeUndefined; } - bool isBoolean() const { return type() == TypeBoolean; } - bool isInteger() const { return type() == TypeInteger; } - bool isReal() const { return type() == TypeReal; } - bool isString() const { return type() == TypeString; } - bool isUUID() const { return type() == TypeUUID; } - bool isDate() const { return type() == TypeDate; } - bool isURI() const { return type() == TypeURI; } - bool isBinary() const { return type() == TypeBinary; } - bool isMap() const { return type() == TypeMap; } - bool isArray() const { return type() == TypeArray; } - //@} - - /** @name Automatic Cast Protection - These are not implemented on purpose. Without them, C++ can perform - some conversions that are clearly not what the programmer intended. - - If you get a linker error about these being missing, you have made - mistake in your code. DO NOT IMPLEMENT THESE FUNCTIONS as a fix. - - All of these problems stem from trying to support char* in LLSD or in - std::string. There are too many automatic casts that will lead to - using an arbitrary pointer or scalar type to std::string. - */ - //@{ - LLSD(const void*); ///< construct from aribrary pointers - void assign(const void*); ///< assign from arbitrary pointers - LLSD& operator=(const void*); ///< assign from arbitrary pointers - - bool has(Integer) const; ///< has() only works for Maps - //@} - - /** @name Implementation */ - //@{ + //@} + + /** @name Array Values */ + //@{ + static LLSD emptyArray(); + + LLSD get(Integer) const; + void set(Integer, const LLSD&); + void insert(Integer, const LLSD&); + LLSD& append(const LLSD&); + void erase(Integer); + LLSD& with(Integer, const LLSD&); + + // accept size_t so we can index relative to size() + const LLSD& operator[](size_t) const; + LLSD& operator[](size_t); + // template overloads to support int literals, U32 et al. + template <typename IDX, + typename std::enable_if<std::is_convertible<IDX, size_t>::value, + bool>::type = true> + const LLSD& operator[](IDX i) const { return (*this)[size_t(i)]; } + template <typename IDX, + typename std::enable_if<std::is_convertible<IDX, size_t>::value, + bool>::type = true> + LLSD& operator[](IDX i) { return (*this)[size_t(i)]; } + //@} + + /** @name Iterators */ + //@{ + size_t size() const; + + typedef std::map<String, LLSD>::iterator map_iterator; + typedef std::map<String, LLSD>::const_iterator map_const_iterator; + + map_iterator beginMap(); + map_iterator endMap(); + map_const_iterator beginMap() const; + map_const_iterator endMap() const; + + typedef std::vector<LLSD>::iterator array_iterator; + typedef std::vector<LLSD>::const_iterator array_const_iterator; + typedef std::vector<LLSD>::reverse_iterator reverse_array_iterator; + + array_iterator beginArray(); + array_iterator endArray(); + array_const_iterator beginArray() const; + array_const_iterator endArray() const; + + reverse_array_iterator rbeginArray(); + reverse_array_iterator rendArray(); + //@} + + /** @name Type Testing */ + //@{ + enum Type { + TypeUndefined = 0, + TypeBoolean, + TypeInteger, + TypeReal, + TypeString, + TypeUUID, + TypeDate, + TypeURI, + TypeBinary, + TypeMap, + TypeArray, + TypeLLSDTypeEnd, + TypeLLSDTypeBegin = TypeUndefined, + TypeLLSDNumTypes = (TypeLLSDTypeEnd - TypeLLSDTypeBegin) + }; + + Type type() const; + + bool isUndefined() const { return type() == TypeUndefined; } + bool isDefined() const { return type() != TypeUndefined; } + bool isBoolean() const { return type() == TypeBoolean; } + bool isInteger() const { return type() == TypeInteger; } + bool isReal() const { return type() == TypeReal; } + bool isString() const { return type() == TypeString; } + bool isUUID() const { return type() == TypeUUID; } + bool isDate() const { return type() == TypeDate; } + bool isURI() const { return type() == TypeURI; } + bool isBinary() const { return type() == TypeBinary; } + bool isMap() const { return type() == TypeMap; } + bool isArray() const { return type() == TypeArray; } + //@} + + /** @name Automatic Cast Protection + These are not implemented on purpose. Without them, C++ can perform + some conversions that are clearly not what the programmer intended. + + If you get a linker error about these being missing, you have made + mistake in your code. DO NOT IMPLEMENT THESE FUNCTIONS as a fix. + + All of these problems stem from trying to support char* in LLSD or in + std::string. There are too many automatic casts that will lead to + using an arbitrary pointer or scalar type to std::string. + */ + //@{ + LLSD(const void*); ///< construct from aribrary pointers + void assign(const void*); ///< assign from arbitrary pointers + LLSD& operator=(const void*); ///< assign from arbitrary pointers + + bool has(Integer) const; ///< has() only works for Maps + //@} + + /** @name Implementation */ + //@{ public: - class Impl; + class Impl; private: - Impl* impl; - friend class LLSD::Impl; - //@} + Impl* impl; + friend class LLSD::Impl; + //@} private: - /** @name Debugging Interface */ - //@{ - /// Returns XML version of llsd -- only to be called from debugger - static const char *dumpXML(const LLSD &llsd); + /** @name Debugging Interface */ + //@{ + /// Returns XML version of llsd -- only to be called from debugger + static const char *dumpXML(const LLSD &llsd); - /// Returns Notation version of llsd -- only to be called from debugger - static const char *dump(const LLSD &llsd); - //@} + /// Returns Notation version of llsd -- only to be called from debugger + static const char *dump(const LLSD &llsd); + //@} public: - static std::string typeString(Type type); // Return human-readable type as a string + static std::string typeString(Type type); // Return human-readable type as a string }; struct llsd_select_bool { - LLSD::Boolean operator()(const LLSD& sd) const - { - return sd.asBoolean(); - } + LLSD::Boolean operator()(const LLSD& sd) const + { + return sd.asBoolean(); + } }; struct llsd_select_integer { - LLSD::Integer operator()(const LLSD& sd) const - { - return sd.asInteger(); - } + LLSD::Integer operator()(const LLSD& sd) const + { + return sd.asInteger(); + } }; struct llsd_select_real { - LLSD::Real operator()(const LLSD& sd) const - { - return sd.asReal(); - } + LLSD::Real operator()(const LLSD& sd) const + { + return sd.asReal(); + } }; struct llsd_select_float { - F32 operator()(const LLSD& sd) const - { - return (F32)sd.asReal(); - } + F32 operator()(const LLSD& sd) const + { + return (F32)sd.asReal(); + } }; struct llsd_select_uuid { - LLSD::UUID operator()(const LLSD& sd) const - { - return sd.asUUID(); - } + LLSD::UUID operator()(const LLSD& sd) const + { + return sd.asUUID(); + } }; struct llsd_select_string { - LLSD::String operator()(const LLSD& sd) const - { - return sd.asString(); - } + LLSD::String operator()(const LLSD& sd) const + { + return sd.asString(); + } }; LL_COMMON_API std::ostream& operator<<(std::ostream& s, const LLSD& llsd); @@ -492,30 +492,30 @@ namespace llsd #ifdef LLSD_DEBUG_INFO /** @name Unit Testing Interface */ //@{ - LL_COMMON_API void dumpStats(const LLSD&); ///< Output information on object and usage - - /// @warn THE FOLLOWING COUNTS WILL NOT BE ACCURATE IN A MULTI-THREADED - /// ENVIRONMENT. - /// - /// These counts track LLSD::Impl (hidden) objects. - LL_COMMON_API U32 allocationCount(); ///< how many Impls have been made - LL_COMMON_API U32 outstandingCount(); ///< how many Impls are still alive - - /// These counts track LLSD (public) objects. - LL_COMMON_API extern S32 sLLSDAllocationCount; ///< Number of LLSD objects ever created - LL_COMMON_API extern S32 sLLSDNetObjects; ///< Number of LLSD objects that exist + LL_COMMON_API void dumpStats(const LLSD&); ///< Output information on object and usage + + /// @warn THE FOLLOWING COUNTS WILL NOT BE ACCURATE IN A MULTI-THREADED + /// ENVIRONMENT. + /// + /// These counts track LLSD::Impl (hidden) objects. + LL_COMMON_API U32 allocationCount(); ///< how many Impls have been made + LL_COMMON_API U32 outstandingCount(); ///< how many Impls are still alive + + /// These counts track LLSD (public) objects. + LL_COMMON_API extern S32 sLLSDAllocationCount; ///< Number of LLSD objects ever created + LL_COMMON_API extern S32 sLLSDNetObjects; ///< Number of LLSD objects that exist #endif //@} } // namespace llsd /** QUESTIONS & TO DOS - - Would Binary be more convenient as unsigned char* buffer semantics? - - Should Binary be convertible to/from String, and if so how? - - as UTF8 encoded strings (making not like UUID<->String) - - as Base64 or Base96 encoded (making like UUID<->String) - - Conversions to std::string and LLUUID do not result in easy assignment - to std::string, std::string or LLUUID due to non-unique conversion paths + - Would Binary be more convenient as unsigned char* buffer semantics? + - Should Binary be convertible to/from String, and if so how? + - as UTF8 encoded strings (making not like UUID<->String) + - as Base64 or Base96 encoded (making like UUID<->String) + - Conversions to std::string and LLUUID do not result in easy assignment + to std::string, std::string or LLUUID due to non-unique conversion paths */ #endif // LL_LLSD_NEW_H diff --git a/indra/llcommon/llsdparam.cpp b/indra/llcommon/llsdparam.cpp index 30f49b27ea..b981be4d0a 100644 --- a/indra/llcommon/llsdparam.cpp +++ b/indra/llcommon/llsdparam.cpp @@ -1,26 +1,26 @@ -/** +/** * @file llsdparam.cpp - * @brief parameter block abstraction for creating complex objects and + * @brief parameter block abstraction for creating complex objects and * parsing construction parameters from xml and LLSD * * $LicenseInfo:firstyear=2008&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -32,9 +32,9 @@ #include "llsdutil.h" #include "boost/bind.hpp" -static LLInitParam::Parser::parser_read_func_map_t sReadFuncs; -static LLInitParam::Parser::parser_write_func_map_t sWriteFuncs; -static LLInitParam::Parser::parser_inspect_func_map_t sInspectFuncs; +static LLInitParam::Parser::parser_read_func_map_t sReadFuncs; +static LLInitParam::Parser::parser_write_func_map_t sWriteFuncs; +static LLInitParam::Parser::parser_inspect_func_map_t sInspectFuncs; static const LLSD NO_VALUE_MARKER; // @@ -43,95 +43,95 @@ static const LLSD NO_VALUE_MARKER; LLParamSDParser::LLParamSDParser() : Parser(sReadFuncs, sWriteFuncs, sInspectFuncs) { - using boost::bind; - - if (sReadFuncs.empty()) - { - registerParserFuncs<LLInitParam::Flag>(readFlag, &LLParamSDParser::writeFlag); - registerParserFuncs<S32>(readS32, &LLParamSDParser::writeTypedValue<S32>); - registerParserFuncs<U32>(readU32, &LLParamSDParser::writeU32Param); - registerParserFuncs<F32>(readF32, &LLParamSDParser::writeTypedValue<F32>); - registerParserFuncs<F64>(readF64, &LLParamSDParser::writeTypedValue<F64>); - registerParserFuncs<bool>(readBool, &LLParamSDParser::writeTypedValue<bool>); - registerParserFuncs<std::string>(readString, &LLParamSDParser::writeTypedValue<std::string>); - registerParserFuncs<LLUUID>(readUUID, &LLParamSDParser::writeTypedValue<LLUUID>); - registerParserFuncs<LLDate>(readDate, &LLParamSDParser::writeTypedValue<LLDate>); - registerParserFuncs<LLURI>(readURI, &LLParamSDParser::writeTypedValue<LLURI>); - registerParserFuncs<LLSD>(readSD, &LLParamSDParser::writeTypedValue<LLSD>); - } + using boost::bind; + + if (sReadFuncs.empty()) + { + registerParserFuncs<LLInitParam::Flag>(readFlag, &LLParamSDParser::writeFlag); + registerParserFuncs<S32>(readS32, &LLParamSDParser::writeTypedValue<S32>); + registerParserFuncs<U32>(readU32, &LLParamSDParser::writeU32Param); + registerParserFuncs<F32>(readF32, &LLParamSDParser::writeTypedValue<F32>); + registerParserFuncs<F64>(readF64, &LLParamSDParser::writeTypedValue<F64>); + registerParserFuncs<bool>(readBool, &LLParamSDParser::writeTypedValue<bool>); + registerParserFuncs<std::string>(readString, &LLParamSDParser::writeTypedValue<std::string>); + registerParserFuncs<LLUUID>(readUUID, &LLParamSDParser::writeTypedValue<LLUUID>); + registerParserFuncs<LLDate>(readDate, &LLParamSDParser::writeTypedValue<LLDate>); + registerParserFuncs<LLURI>(readURI, &LLParamSDParser::writeTypedValue<LLURI>); + registerParserFuncs<LLSD>(readSD, &LLParamSDParser::writeTypedValue<LLSD>); + } } // special case handling of U32 due to ambiguous LLSD::assign overload bool LLParamSDParser::writeU32Param(LLParamSDParser::parser_t& parser, const void* val_ptr, parser_t::name_stack_t& name_stack) { - LLParamSDParser& sdparser = static_cast<LLParamSDParser&>(parser); - if (!sdparser.mWriteRootSD) return false; - - parser_t::name_stack_range_t range(name_stack.begin(), name_stack.end()); - LLSD& sd_to_write = LLParamSDParserUtilities::getSDWriteNode(*sdparser.mWriteRootSD, range); - sd_to_write.assign((S32)*((const U32*)val_ptr)); - - return true; + LLParamSDParser& sdparser = static_cast<LLParamSDParser&>(parser); + if (!sdparser.mWriteRootSD) return false; + + parser_t::name_stack_range_t range(name_stack.begin(), name_stack.end()); + LLSD& sd_to_write = LLParamSDParserUtilities::getSDWriteNode(*sdparser.mWriteRootSD, range); + sd_to_write.assign((S32)*((const U32*)val_ptr)); + + return true; } bool LLParamSDParser::writeFlag(LLParamSDParser::parser_t& parser, const void* val_ptr, parser_t::name_stack_t& name_stack) { - LLParamSDParser& sdparser = static_cast<LLParamSDParser&>(parser); - if (!sdparser.mWriteRootSD) return false; + LLParamSDParser& sdparser = static_cast<LLParamSDParser&>(parser); + if (!sdparser.mWriteRootSD) return false; - parser_t::name_stack_range_t range(name_stack.begin(), name_stack.end()); - LLParamSDParserUtilities::getSDWriteNode(*sdparser.mWriteRootSD, range); + parser_t::name_stack_range_t range(name_stack.begin(), name_stack.end()); + LLParamSDParserUtilities::getSDWriteNode(*sdparser.mWriteRootSD, range); - return true; + return true; } void LLParamSDParser::submit(LLInitParam::BaseBlock& block, const LLSD& sd, LLInitParam::Parser::name_stack_t& name_stack) { - mCurReadSD = &sd; - block.submitValue(name_stack, *this); + mCurReadSD = &sd; + block.submitValue(name_stack, *this); } void LLParamSDParser::readSD(const LLSD& sd, LLInitParam::BaseBlock& block, bool silent) { - mCurReadSD = NULL; - mNameStack.clear(); - setParseSilently(silent); + mCurReadSD = NULL; + mNameStack.clear(); + setParseSilently(silent); - LLParamSDParserUtilities::readSDValues(boost::bind(&LLParamSDParser::submit, this, boost::ref(block), _1, _2), sd, mNameStack); - //readSDValues(sd, block); + LLParamSDParserUtilities::readSDValues(boost::bind(&LLParamSDParser::submit, this, boost::ref(block), _1, _2), sd, mNameStack); + //readSDValues(sd, block); } void LLParamSDParser::writeSDImpl(LLSD& sd, const LLInitParam::BaseBlock& block, const LLInitParam::predicate_rule_t rules, const LLInitParam::BaseBlock* diff_block) { - mNameStack.clear(); - mWriteRootSD = &sd; + mNameStack.clear(); + mWriteRootSD = &sd; - name_stack_t name_stack; - block.serializeBlock(*this, name_stack, rules, diff_block); + name_stack_t name_stack; + block.serializeBlock(*this, name_stack, rules, diff_block); } /*virtual*/ std::string LLParamSDParser::getCurrentElementName() { - std::string full_name = "sd"; - for (name_stack_t::value_type& stack_pair : mNameStack) - { - full_name += llformat("[%s]", stack_pair.first.c_str()); - } + std::string full_name = "sd"; + for (name_stack_t::value_type& stack_pair : mNameStack) + { + full_name += llformat("[%s]", stack_pair.first.c_str()); + } - return full_name; + return full_name; } bool LLParamSDParser::readFlag(Parser& parser, void* val_ptr) { - LLParamSDParser& self = static_cast<LLParamSDParser&>(parser); - return self.mCurReadSD == &NO_VALUE_MARKER; + LLParamSDParser& self = static_cast<LLParamSDParser&>(parser); + return self.mCurReadSD == &NO_VALUE_MARKER; } bool LLParamSDParser::readS32(Parser& parser, void* val_ptr) { - LLParamSDParser& self = static_cast<LLParamSDParser&>(parser); + LLParamSDParser& self = static_cast<LLParamSDParser&>(parser); *((S32*)val_ptr) = self.mCurReadSD->asInteger(); return true; @@ -139,7 +139,7 @@ bool LLParamSDParser::readS32(Parser& parser, void* val_ptr) bool LLParamSDParser::readU32(Parser& parser, void* val_ptr) { - LLParamSDParser& self = static_cast<LLParamSDParser&>(parser); + LLParamSDParser& self = static_cast<LLParamSDParser&>(parser); *((U32*)val_ptr) = self.mCurReadSD->asInteger(); return true; @@ -147,7 +147,7 @@ bool LLParamSDParser::readU32(Parser& parser, void* val_ptr) bool LLParamSDParser::readF32(Parser& parser, void* val_ptr) { - LLParamSDParser& self = static_cast<LLParamSDParser&>(parser); + LLParamSDParser& self = static_cast<LLParamSDParser&>(parser); *((F32*)val_ptr) = self.mCurReadSD->asReal(); return true; @@ -155,7 +155,7 @@ bool LLParamSDParser::readF32(Parser& parser, void* val_ptr) bool LLParamSDParser::readF64(Parser& parser, void* val_ptr) { - LLParamSDParser& self = static_cast<LLParamSDParser&>(parser); + LLParamSDParser& self = static_cast<LLParamSDParser&>(parser); *((F64*)val_ptr) = self.mCurReadSD->asReal(); return true; @@ -163,7 +163,7 @@ bool LLParamSDParser::readF64(Parser& parser, void* val_ptr) bool LLParamSDParser::readBool(Parser& parser, void* val_ptr) { - LLParamSDParser& self = static_cast<LLParamSDParser&>(parser); + LLParamSDParser& self = static_cast<LLParamSDParser&>(parser); *((bool*)val_ptr) = self.mCurReadSD->asBoolean(); return true; @@ -171,170 +171,170 @@ bool LLParamSDParser::readBool(Parser& parser, void* val_ptr) bool LLParamSDParser::readString(Parser& parser, void* val_ptr) { - LLParamSDParser& self = static_cast<LLParamSDParser&>(parser); + LLParamSDParser& self = static_cast<LLParamSDParser&>(parser); - *((std::string*)val_ptr) = self.mCurReadSD->asString(); + *((std::string*)val_ptr) = self.mCurReadSD->asString(); return true; } bool LLParamSDParser::readUUID(Parser& parser, void* val_ptr) { - LLParamSDParser& self = static_cast<LLParamSDParser&>(parser); + LLParamSDParser& self = static_cast<LLParamSDParser&>(parser); - *((LLUUID*)val_ptr) = self.mCurReadSD->asUUID(); + *((LLUUID*)val_ptr) = self.mCurReadSD->asUUID(); return true; } bool LLParamSDParser::readDate(Parser& parser, void* val_ptr) { - LLParamSDParser& self = static_cast<LLParamSDParser&>(parser); + LLParamSDParser& self = static_cast<LLParamSDParser&>(parser); - *((LLDate*)val_ptr) = self.mCurReadSD->asDate(); + *((LLDate*)val_ptr) = self.mCurReadSD->asDate(); return true; } bool LLParamSDParser::readURI(Parser& parser, void* val_ptr) { - LLParamSDParser& self = static_cast<LLParamSDParser&>(parser); + LLParamSDParser& self = static_cast<LLParamSDParser&>(parser); - *((LLURI*)val_ptr) = self.mCurReadSD->asURI(); + *((LLURI*)val_ptr) = self.mCurReadSD->asURI(); return true; } bool LLParamSDParser::readSD(Parser& parser, void* val_ptr) { - LLParamSDParser& self = static_cast<LLParamSDParser&>(parser); + LLParamSDParser& self = static_cast<LLParamSDParser&>(parser); - *((LLSD*)val_ptr) = *self.mCurReadSD; + *((LLSD*)val_ptr) = *self.mCurReadSD; return true; } // static LLSD& LLParamSDParserUtilities::getSDWriteNode(LLSD& input, LLInitParam::Parser::name_stack_range_t& name_stack_range) { - LLSD* sd_to_write = &input; - - for (LLInitParam::Parser::name_stack_t::iterator it = name_stack_range.first; - it != name_stack_range.second; - ++it) - { - bool new_traversal = it->second; - - LLSD* child_sd; - if (it->first.empty()) - { - child_sd = sd_to_write; - if (child_sd->isUndefined()) - { - *child_sd = LLSD::emptyArray(); - } - if (new_traversal) - { - // write to new element at end - sd_to_write = &(*child_sd)[child_sd->size()]; - } - else - { - // write to last of existing elements, or first element if empty - sd_to_write = &(*child_sd)[llmax(0, child_sd->size() - 1)]; - } - } - else - { - sd_to_write = &(*sd_to_write)[it->first]; - } - it->second = false; - } - - return *sd_to_write; + LLSD* sd_to_write = &input; + + for (LLInitParam::Parser::name_stack_t::iterator it = name_stack_range.first; + it != name_stack_range.second; + ++it) + { + bool new_traversal = it->second; + + LLSD* child_sd; + if (it->first.empty()) + { + child_sd = sd_to_write; + if (child_sd->isUndefined()) + { + *child_sd = LLSD::emptyArray(); + } + if (new_traversal) + { + // write to new element at end + sd_to_write = &(*child_sd)[child_sd->size()]; + } + else + { + // write to last of existing elements, or first element if empty + sd_to_write = &(*child_sd)[llmax(0, child_sd->size() - 1)]; + } + } + else + { + sd_to_write = &(*sd_to_write)[it->first]; + } + it->second = false; + } + + return *sd_to_write; } //static void LLParamSDParserUtilities::readSDValues(read_sd_cb_t cb, const LLSD& sd, LLInitParam::Parser::name_stack_t& stack) { - if (sd.isMap()) - { - for (LLSD::map_const_iterator it = sd.beginMap(); - it != sd.endMap(); - ++it) - { - stack.push_back(make_pair(it->first, true)); - readSDValues(cb, it->second, stack); - stack.pop_back(); - } - } - else if (sd.isArray()) - { - for (LLSD::array_const_iterator it = sd.beginArray(); - it != sd.endArray(); - ++it) - { - stack.push_back(make_pair(std::string(), true)); - readSDValues(cb, *it, stack); - stack.pop_back(); - } - } - else if (sd.isUndefined()) - { - if (!cb.empty()) - { - cb(NO_VALUE_MARKER, stack); - } - } - else - { - if (!cb.empty()) - { - cb(sd, stack); - } - } + if (sd.isMap()) + { + for (LLSD::map_const_iterator it = sd.beginMap(); + it != sd.endMap(); + ++it) + { + stack.push_back(make_pair(it->first, true)); + readSDValues(cb, it->second, stack); + stack.pop_back(); + } + } + else if (sd.isArray()) + { + for (LLSD::array_const_iterator it = sd.beginArray(); + it != sd.endArray(); + ++it) + { + stack.push_back(make_pair(std::string(), true)); + readSDValues(cb, *it, stack); + stack.pop_back(); + } + } + else if (sd.isUndefined()) + { + if (!cb.empty()) + { + cb(NO_VALUE_MARKER, stack); + } + } + else + { + if (!cb.empty()) + { + cb(sd, stack); + } + } } //static void LLParamSDParserUtilities::readSDValues(read_sd_cb_t cb, const LLSD& sd) { - LLInitParam::Parser::name_stack_t stack = LLInitParam::Parser::name_stack_t(); - readSDValues(cb, sd, stack); + LLInitParam::Parser::name_stack_t stack = LLInitParam::Parser::name_stack_t(); + readSDValues(cb, sd, stack); } namespace LLInitParam { - // LLSD specialization - // block param interface - bool ParamValue<LLSD, NOT_BLOCK>::deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack, bool new_name) - { - if (name_stack.first == name_stack.second - && p.readValue<LLSD>(mValue)) - { - return true; - } - - LLSD& sd = LLParamSDParserUtilities::getSDWriteNode(mValue, name_stack); - - LLSD::String string; - - if (p.readValue<LLSD::String>(string)) - { - sd = string; - return true; - } - return false; - } - - //static - void ParamValue<LLSD, NOT_BLOCK>::serializeElement(Parser& p, const LLSD& sd, Parser::name_stack_t& name_stack) - { - p.writeValue<LLSD::String>(sd.asString(), name_stack); - } - - bool ParamValue<LLSD, NOT_BLOCK>::serializeBlock(Parser& p, Parser::name_stack_t& name_stack_range, const predicate_rule_t predicate_rule, const BaseBlock* diff_block) const - { - // attempt to write LLSD out directly - if (!p.writeValue<LLSD>(mValue, name_stack_range)) - { - // otherwise read from LLSD value and serialize out to parser (which could be LLSD, XUI, etc) - LLParamSDParserUtilities::readSDValues(boost::bind(&serializeElement, boost::ref(p), _1, _2), mValue, name_stack_range); - } - return true; - } + // LLSD specialization + // block param interface + bool ParamValue<LLSD, NOT_BLOCK>::deserializeBlock(Parser& p, Parser::name_stack_range_t& name_stack, bool new_name) + { + if (name_stack.first == name_stack.second + && p.readValue<LLSD>(mValue)) + { + return true; + } + + LLSD& sd = LLParamSDParserUtilities::getSDWriteNode(mValue, name_stack); + + LLSD::String string; + + if (p.readValue<LLSD::String>(string)) + { + sd = string; + return true; + } + return false; + } + + //static + void ParamValue<LLSD, NOT_BLOCK>::serializeElement(Parser& p, const LLSD& sd, Parser::name_stack_t& name_stack) + { + p.writeValue<LLSD::String>(sd.asString(), name_stack); + } + + bool ParamValue<LLSD, NOT_BLOCK>::serializeBlock(Parser& p, Parser::name_stack_t& name_stack_range, const predicate_rule_t predicate_rule, const BaseBlock* diff_block) const + { + // attempt to write LLSD out directly + if (!p.writeValue<LLSD>(mValue, name_stack_range)) + { + // otherwise read from LLSD value and serialize out to parser (which could be LLSD, XUI, etc) + LLParamSDParserUtilities::readSDValues(boost::bind(&serializeElement, boost::ref(p), _1, _2), mValue, name_stack_range); + } + return true; + } } diff --git a/indra/llcommon/llsdparam.h b/indra/llcommon/llsdparam.h index 82a623a8a0..21ebb9a258 100644 --- a/indra/llcommon/llsdparam.h +++ b/indra/llcommon/llsdparam.h @@ -1,26 +1,26 @@ -/** +/** * @file llsdparam.h - * @brief parameter block abstraction for creating complex objects and + * @brief parameter block abstraction for creating complex objects and * parsing construction parameters from xml and LLSD * * $LicenseInfo:firstyear=2008&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -34,79 +34,79 @@ struct LL_COMMON_API LLParamSDParserUtilities { - static LLSD& getSDWriteNode(LLSD& input, LLInitParam::Parser::name_stack_range_t& name_stack_range); + static LLSD& getSDWriteNode(LLSD& input, LLInitParam::Parser::name_stack_range_t& name_stack_range); - typedef boost::function<void (const LLSD&, LLInitParam::Parser::name_stack_t&)> read_sd_cb_t; - static void readSDValues(read_sd_cb_t cb, const LLSD& sd, LLInitParam::Parser::name_stack_t& stack); - static void readSDValues(read_sd_cb_t cb, const LLSD& sd); + typedef boost::function<void (const LLSD&, LLInitParam::Parser::name_stack_t&)> read_sd_cb_t; + static void readSDValues(read_sd_cb_t cb, const LLSD& sd, LLInitParam::Parser::name_stack_t& stack); + static void readSDValues(read_sd_cb_t cb, const LLSD& sd); }; -class LL_COMMON_API LLParamSDParser -: public LLInitParam::Parser +class LL_COMMON_API LLParamSDParser +: public LLInitParam::Parser { LOG_CLASS(LLParamSDParser); typedef LLInitParam::Parser parser_t; public: - LLParamSDParser(); - void readSD(const LLSD& sd, LLInitParam::BaseBlock& block, bool silent = false); - template<typename BLOCK> - void writeSD(LLSD& sd, - const BLOCK& block, - const LLInitParam::predicate_rule_t rules = LLInitParam::default_parse_rules(), - const LLInitParam::BaseBlock* diff_block = NULL) - { - if (!diff_block - && !rules.isAmbivalent(LLInitParam::HAS_DEFAULT_VALUE)) - { - diff_block = &LLInitParam::defaultValue<BLOCK>(); - } - writeSDImpl(sd, block, rules, diff_block); - } - - /*virtual*/ std::string getCurrentElementName(); - /*virtual*/ std::string getCurrentFileName(){ return LLStringUtil::null; } + LLParamSDParser(); + void readSD(const LLSD& sd, LLInitParam::BaseBlock& block, bool silent = false); + template<typename BLOCK> + void writeSD(LLSD& sd, + const BLOCK& block, + const LLInitParam::predicate_rule_t rules = LLInitParam::default_parse_rules(), + const LLInitParam::BaseBlock* diff_block = NULL) + { + if (!diff_block + && !rules.isAmbivalent(LLInitParam::HAS_DEFAULT_VALUE)) + { + diff_block = &LLInitParam::defaultValue<BLOCK>(); + } + writeSDImpl(sd, block, rules, diff_block); + } + + /*virtual*/ std::string getCurrentElementName(); + /*virtual*/ std::string getCurrentFileName(){ return LLStringUtil::null; } private: - void writeSDImpl(LLSD& sd, - const LLInitParam::BaseBlock& block, - const LLInitParam::predicate_rule_t, - const LLInitParam::BaseBlock* diff_block); - - void submit(LLInitParam::BaseBlock& block, const LLSD& sd, LLInitParam::Parser::name_stack_t& name_stack); - - template<typename T> - static bool writeTypedValue(Parser& parser, const void* val_ptr, parser_t::name_stack_t& name_stack) - { - LLParamSDParser& sdparser = static_cast<LLParamSDParser&>(parser); - if (!sdparser.mWriteRootSD) return false; - - LLInitParam::Parser::name_stack_range_t range(name_stack.begin(), name_stack.end()); - LLSD& sd_to_write = LLParamSDParserUtilities::getSDWriteNode(*sdparser.mWriteRootSD, range); - - sd_to_write.assign(*((const T*)val_ptr)); - return true; - } - - static bool writeU32Param(Parser& parser, const void* value_ptr, parser_t::name_stack_t& name_stack); - static bool writeFlag(Parser& parser, const void* value_ptr, parser_t::name_stack_t& name_stack); - - static bool readFlag(Parser& parser, void* val_ptr); - static bool readS32(Parser& parser, void* val_ptr); - static bool readU32(Parser& parser, void* val_ptr); - static bool readF32(Parser& parser, void* val_ptr); - static bool readF64(Parser& parser, void* val_ptr); - static bool readBool(Parser& parser, void* val_ptr); - static bool readString(Parser& parser, void* val_ptr); - static bool readUUID(Parser& parser, void* val_ptr); - static bool readDate(Parser& parser, void* val_ptr); - static bool readURI(Parser& parser, void* val_ptr); - static bool readSD(Parser& parser, void* val_ptr); - - Parser::name_stack_t mNameStack; - const LLSD* mCurReadSD; - LLSD* mWriteRootSD; + void writeSDImpl(LLSD& sd, + const LLInitParam::BaseBlock& block, + const LLInitParam::predicate_rule_t, + const LLInitParam::BaseBlock* diff_block); + + void submit(LLInitParam::BaseBlock& block, const LLSD& sd, LLInitParam::Parser::name_stack_t& name_stack); + + template<typename T> + static bool writeTypedValue(Parser& parser, const void* val_ptr, parser_t::name_stack_t& name_stack) + { + LLParamSDParser& sdparser = static_cast<LLParamSDParser&>(parser); + if (!sdparser.mWriteRootSD) return false; + + LLInitParam::Parser::name_stack_range_t range(name_stack.begin(), name_stack.end()); + LLSD& sd_to_write = LLParamSDParserUtilities::getSDWriteNode(*sdparser.mWriteRootSD, range); + + sd_to_write.assign(*((const T*)val_ptr)); + return true; + } + + static bool writeU32Param(Parser& parser, const void* value_ptr, parser_t::name_stack_t& name_stack); + static bool writeFlag(Parser& parser, const void* value_ptr, parser_t::name_stack_t& name_stack); + + static bool readFlag(Parser& parser, void* val_ptr); + static bool readS32(Parser& parser, void* val_ptr); + static bool readU32(Parser& parser, void* val_ptr); + static bool readF32(Parser& parser, void* val_ptr); + static bool readF64(Parser& parser, void* val_ptr); + static bool readBool(Parser& parser, void* val_ptr); + static bool readString(Parser& parser, void* val_ptr); + static bool readUUID(Parser& parser, void* val_ptr); + static bool readDate(Parser& parser, void* val_ptr); + static bool readURI(Parser& parser, void* val_ptr); + static bool readSD(Parser& parser, void* val_ptr); + + Parser::name_stack_t mNameStack; + const LLSD* mCurReadSD; + LLSD* mWriteRootSD; }; @@ -114,29 +114,29 @@ template<typename T> class LLSDParamAdapter : public T { public: - LLSDParamAdapter() {} - LLSDParamAdapter(const LLSD& sd) - { + LLSDParamAdapter() {} + LLSDParamAdapter(const LLSD& sd) + { LL_PROFILE_ZONE_SCOPED; - LLParamSDParser parser; - // don't spam for implicit parsing of LLSD, as we want to allow arbitrary freeform data and ignore most of it - bool parse_silently = true; - parser.readSD(sd, *this, parse_silently); - } - - operator LLSD() const - { - LLParamSDParser parser; - LLSD sd; - parser.writeSD(sd, *this); - return sd; - } - - LLSDParamAdapter(const T& val) - : T(val) - { - T::operator=(val); - } + LLParamSDParser parser; + // don't spam for implicit parsing of LLSD, as we want to allow arbitrary freeform data and ignore most of it + bool parse_silently = true; + parser.readSD(sd, *this, parse_silently); + } + + operator LLSD() const + { + LLParamSDParser parser; + LLSD sd; + parser.writeSD(sd, *this); + return sd; + } + + LLSDParamAdapter(const T& val) + : T(val) + { + T::operator=(val); + } }; #endif // LL_LLSDPARAM_H diff --git a/indra/llcommon/llsdserialize.cpp b/indra/llcommon/llsdserialize.cpp index 756fd7c678..612f71e8bc 100644 --- a/indra/llcommon/llsdserialize.cpp +++ b/indra/llcommon/llsdserialize.cpp @@ -1,2470 +1,2470 @@ -/** - * @file llsdserialize.cpp - * @author Phoenix - * @date 2006-03-05 - * @brief Implementation of LLSD parsers and formatters - * - * $LicenseInfo:firstyear=2006&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" -#include "llsdserialize.h" -#include "llpointer.h" -#include "llstreamtools.h" // for fullread - -#include <iostream> -#include "apr_base64.h" - -#include <boost/iostreams/device/array.hpp> -#include <boost/iostreams/stream.hpp> - -#ifdef LL_USESYSTEMLIBS -# include <zlib.h> -#else -# include "zlib-ng/zlib.h" // for davep's dirty little zip functions -#endif - -#if !LL_WINDOWS -#include <netinet/in.h> // htonl & ntohl -#endif - -#include "lldate.h" -#include "llmemorystream.h" -#include "llsd.h" -#include "llstring.h" -#include "lluri.h" - -// File constants -static const size_t MAX_HDR_LEN = 20; -static const S32 UNZIP_LLSD_MAX_DEPTH = 96; -static const char LEGACY_NON_HEADER[] = "<llsd>"; -const std::string LLSD_BINARY_HEADER("LLSD/Binary"); -const std::string LLSD_XML_HEADER("LLSD/XML"); -const std::string LLSD_NOTATION_HEADER("llsd/notation"); - -//used to deflate a gzipped asset (currently used for navmeshes) -#define windowBits 15 -#define ENABLE_ZLIB_GZIP 32 - -// If we published this in llsdserialize.h, we could use it in the -// implementation of LLSDOStreamer's operator<<(). -template <class Formatter> -void format_using(const LLSD& data, std::ostream& ostr, - LLSDFormatter::EFormatterOptions options=LLSDFormatter::OPTIONS_PRETTY_BINARY) -{ - LLPointer<Formatter> f{ new Formatter }; - f->format(data, ostr, options); -} - -template <class Parser> -S32 parse_using(std::istream& istr, LLSD& data, size_t max_bytes, S32 max_depth=-1) -{ - LLPointer<Parser> p{ new Parser }; - return p->parse(istr, data, max_bytes, max_depth); -} - -/** - * LLSDSerialize - */ - -// static -void LLSDSerialize::serialize(const LLSD& sd, std::ostream& str, ELLSD_Serialize type, - LLSDFormatter::EFormatterOptions options) -{ - LLPointer<LLSDFormatter> f = NULL; - - switch (type) - { - case LLSD_BINARY: - str << "<? " << LLSD_BINARY_HEADER << " ?>\n"; - f = new LLSDBinaryFormatter; - break; - - case LLSD_XML: - str << "<? " << LLSD_XML_HEADER << " ?>\n"; - f = new LLSDXMLFormatter; - break; - - case LLSD_NOTATION: - str << "<? " << LLSD_NOTATION_HEADER << " ?>\n"; - f = new LLSDNotationFormatter; - break; - - default: - LL_WARNS() << "serialize request for unknown ELLSD_Serialize" << LL_ENDL; - } - - if (f.notNull()) - { - f->format(sd, str, options); - } -} - -// static -bool LLSDSerialize::deserialize(LLSD& sd, std::istream& str, llssize max_bytes) -{ - char hdr_buf[MAX_HDR_LEN + 1] = ""; /* Flawfinder: ignore */ - bool fail_if_not_legacy = false; - - /* - * Get the first line before anything. Don't read more than max_bytes: - * this get() overload reads no more than (count-1) bytes into the - * specified buffer. In the usual case when max_bytes exceeds - * sizeof(hdr_buf), get() will read no more than sizeof(hdr_buf)-2. - */ - llssize max_hdr_read = MAX_HDR_LEN; - if (max_bytes != LLSDSerialize::SIZE_UNLIMITED) - { - max_hdr_read = llmin(max_bytes + 1, max_hdr_read); - } - str.get(hdr_buf, max_hdr_read, '\n'); - auto inbuf = str.gcount(); - - // https://en.cppreference.com/w/cpp/io/basic_istream/get - // When the get() above sees the specified delimiter '\n', it stops there - // without pulling it from the stream. If it turns out that the stream - // does NOT contain a header, and the content includes meaningful '\n', - // it's important to pull that into hdr_buf too. - if (inbuf < max_bytes && str.get(hdr_buf[inbuf])) - { - // got the delimiting '\n' - ++inbuf; - // None of the following requires that hdr_buf contain a final '\0' - // byte. We could store one if needed, since even the incremented - // inbuf won't exceed sizeof(hdr_buf)-1, but there's no need. - } - std::string header{ hdr_buf, static_cast<std::string::size_type>(inbuf) }; - if (str.fail()) - { - str.clear(); - fail_if_not_legacy = true; - } - - if (!strncasecmp(LEGACY_NON_HEADER, hdr_buf, strlen(LEGACY_NON_HEADER))) /* Flawfinder: ignore */ - { // Create a LLSD XML parser, and parse the first chunk read above. - LLSDXMLParser x; - x.parsePart(hdr_buf, inbuf); // Parse the first part that was already read - auto parsed = x.parse(str, sd, max_bytes - inbuf); // Parse the rest of it - // Formally we should probably check (parsed != PARSE_FAILURE && - // parsed > 0), but since PARSE_FAILURE is -1, this suffices. - return (parsed > 0); - } - - if (fail_if_not_legacy) - { - LL_WARNS() << "deserialize LLSD parse failure" << LL_ENDL; - return false; - } - - /* - * Remove the newline chars - */ - std::string::size_type lastchar = header.find_last_not_of("\r\n"); - if (lastchar != std::string::npos) - { - // It's important that find_last_not_of() returns size_type, which is - // why lastchar explicitly declares the type above. erase(size_type) - // erases from that offset to the end of the string, whereas - // erase(iterator) erases only a single character. - header.erase(lastchar+1); - } - - // trim off the <? ... ?> header syntax - auto start = header.find_first_not_of("<? "); - if (start != std::string::npos) - { - auto end = header.find_first_of(" ?", start); - if (end != std::string::npos) - { - header = header.substr(start, end - start); - ws(str); - } - } - /* - * Create the parser as appropriate - */ - if (0 == LLStringUtil::compareInsensitive(header, LLSD_BINARY_HEADER)) - { - return (parse_using<LLSDBinaryParser>(str, sd, max_bytes-inbuf) > 0); - } - else if (0 == LLStringUtil::compareInsensitive(header, LLSD_XML_HEADER)) - { - return (parse_using<LLSDXMLParser>(str, sd, max_bytes-inbuf) > 0); - } - else if (0 == LLStringUtil::compareInsensitive(header, LLSD_NOTATION_HEADER)) - { - return (parse_using<LLSDNotationParser>(str, sd, max_bytes-inbuf) > 0); - } - else // no header we recognize - { - LLPointer<LLSDParser> p; - if (inbuf && hdr_buf[0] == '<') - { - // looks like XML - LL_DEBUGS() << "deserialize request with no header, assuming XML" << LL_ENDL; - p = new LLSDXMLParser; - } - else - { - // assume notation - LL_DEBUGS() << "deserialize request with no header, assuming notation" << LL_ENDL; - p = new LLSDNotationParser; - } - // Since we've already read 'inbuf' bytes into 'hdr_buf', prepend that - // data to whatever remains in 'str'. - LLMemoryStreamBuf already(reinterpret_cast<const U8*>(hdr_buf), inbuf); - cat_streambuf prebuff(&already, str.rdbuf()); - std::istream prepend(&prebuff); -#if 1 - return (p->parse(prepend, sd, max_bytes) > 0); -#else - // debugging the reconstituted 'prepend' stream - // allocate a buffer that we hope is big enough for the whole thing - std::vector<char> wholemsg((max_bytes == size_t(SIZE_UNLIMITED))? 1024 : max_bytes); - prepend.read(wholemsg.data(), std::min(max_bytes, wholemsg.size())); - LLMemoryStream replay(reinterpret_cast<const U8*>(wholemsg.data()), prepend.gcount()); - auto success{ p->parse(replay, sd, prepend.gcount()) > 0 }; - { - LL_DEBUGS() << (success? "parsed: $$" : "failed: '") - << std::string(wholemsg.data(), llmin(prepend.gcount(), 100)) << "$$" - << LL_ENDL; - } - return success; -#endif - } -} - -/** - * Endian handlers - */ -#if LL_BIG_ENDIAN -U64 ll_htonll(U64 hostlonglong) { return hostlonglong; } -U64 ll_ntohll(U64 netlonglong) { return netlonglong; } -F64 ll_htond(F64 hostlonglong) { return hostlonglong; } -F64 ll_ntohd(F64 netlonglong) { return netlonglong; } -#else -// I read some comments one a indicating that doing an integer add -// here would be faster than a bitwise or. For now, the or has -// programmer clarity, since the intended outcome matches the -// operation. -U64 ll_htonll(U64 hostlonglong) -{ - return ((U64)(htonl((U32)((hostlonglong >> 32) & 0xFFFFFFFF))) | - ((U64)(htonl((U32)(hostlonglong & 0xFFFFFFFF))) << 32)); -} -U64 ll_ntohll(U64 netlonglong) -{ - return ((U64)(ntohl((U32)((netlonglong >> 32) & 0xFFFFFFFF))) | - ((U64)(ntohl((U32)(netlonglong & 0xFFFFFFFF))) << 32)); -} -union LLEndianSwapper -{ - F64 d; - U64 i; -}; -F64 ll_htond(F64 hostdouble) -{ - LLEndianSwapper tmp; - tmp.d = hostdouble; - tmp.i = ll_htonll(tmp.i); - return tmp.d; -} -F64 ll_ntohd(F64 netdouble) -{ - LLEndianSwapper tmp; - tmp.d = netdouble; - tmp.i = ll_ntohll(tmp.i); - return tmp.d; -} -#endif - -/** - * Local functions. - */ -/** - * @brief Figure out what kind of string it is (raw or delimited) and handoff. - * - * @param istr The stream to read from. - * @param value [out] The string which was found. - * @param max_bytes The maximum possible length of the string. Passing in - * a negative value will skip this check. - * @return Returns number of bytes read off of the stream. Returns - * PARSE_FAILURE (-1) on failure. - */ -llssize deserialize_string(std::istream& istr, std::string& value, llssize max_bytes); - -/** - * @brief Parse a delimited string. - * - * @param istr The stream to read from, with the delimiter already popped. - * @param value [out] The string which was found. - * @param d The delimiter to use. - * @return Returns number of bytes read off of the stream. Returns - * PARSE_FAILURE (-1) on failure. - */ -llssize deserialize_string_delim(std::istream& istr, std::string& value, char d); - -/** - * @brief Read a raw string off the stream. - * - * @param istr The stream to read from, with the (len) parameter - * leading the stream. - * @param value [out] The string which was found. - * @param d The delimiter to use. - * @param max_bytes The maximum possible length of the string. Passing in - * a negative value will skip this check. - * @return Returns number of bytes read off of the stream. Returns - * PARSE_FAILURE (-1) on failure. - */ -llssize deserialize_string_raw( - std::istream& istr, - std::string& value, - llssize max_bytes); - -/** - * @brief helper method for dealing with the different notation boolean format. - * - * @param istr The stream to read from with the leading character stripped. - * @param data [out] the result of the parse. - * @param compare The string to compare the boolean against - * @param vale The value to assign to data if the parse succeeds. - * @return Returns number of bytes read off of the stream. Returns - * PARSE_FAILURE (-1) on failure. - */ -llssize deserialize_boolean( - std::istream& istr, - LLSD& data, - const std::string& compare, - bool value); - -/** - * @brief Do notation escaping of a string to an ostream. - * - * @param value The string to escape and serialize - * @param str The stream to serialize to. - */ -void serialize_string(const std::string& value, std::ostream& str); - - -/** - * Local constants. - */ -static const std::string NOTATION_TRUE_SERIAL("true"); -static const std::string NOTATION_FALSE_SERIAL("false"); - -static const char BINARY_TRUE_SERIAL = '1'; -static const char BINARY_FALSE_SERIAL = '0'; - - -/** - * LLSDParser - */ -LLSDParser::LLSDParser() - : mCheckLimits(true), mMaxBytesLeft(0), mParseLines(false) -{ -} - -// virtual -LLSDParser::~LLSDParser() -{ } - -S32 LLSDParser::parse(std::istream& istr, LLSD& data, llssize max_bytes, S32 max_depth) -{ - mCheckLimits = LLSDSerialize::SIZE_UNLIMITED != max_bytes; - mMaxBytesLeft = max_bytes; - return doParse(istr, data, max_depth); -} - - -// Parse using routine to get() lines, faster than parse() -S32 LLSDParser::parseLines(std::istream& istr, LLSD& data) -{ - mCheckLimits = false; - mParseLines = true; - return doParse(istr, data); -} - - -int LLSDParser::get(std::istream& istr) const -{ - if(mCheckLimits) --mMaxBytesLeft; - return istr.get(); -} - -std::istream& LLSDParser::get( - std::istream& istr, - char* s, - std::streamsize n, - char delim) const -{ - istr.get(s, n, delim); - if(mCheckLimits) mMaxBytesLeft -= istr.gcount(); - return istr; -} - -std::istream& LLSDParser::get( - std::istream& istr, - std::streambuf& sb, - char delim) const -{ - istr.get(sb, delim); - if(mCheckLimits) mMaxBytesLeft -= istr.gcount(); - return istr; -} - -std::istream& LLSDParser::ignore(std::istream& istr) const -{ - istr.ignore(); - if(mCheckLimits) --mMaxBytesLeft; - return istr; -} - -std::istream& LLSDParser::putback(std::istream& istr, char c) const -{ - istr.putback(c); - if(mCheckLimits) ++mMaxBytesLeft; - return istr; -} - -std::istream& LLSDParser::read( - std::istream& istr, - char* s, - std::streamsize n) const -{ - istr.read(s, n); - if(mCheckLimits) mMaxBytesLeft -= istr.gcount(); - return istr; -} - -void LLSDParser::account(llssize bytes) const -{ - if(mCheckLimits) mMaxBytesLeft -= bytes; -} - - -/** - * LLSDNotationParser - */ -LLSDNotationParser::LLSDNotationParser() -{ -} - -// virtual -LLSDNotationParser::~LLSDNotationParser() -{ } - -// virtual -S32 LLSDNotationParser::doParse(std::istream& istr, LLSD& data, S32 max_depth) const -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD - // map: { string:object, string:object } - // array: [ object, object, object ] - // undef: ! - // boolean: true | false | 1 | 0 | T | F | t | f | TRUE | FALSE - // integer: i#### - // real: r#### - // uuid: u#### - // string: "g'day" | 'have a "nice" day' | s(size)"raw data" - // uri: l"escaped" - // date: d"YYYY-MM-DDTHH:MM:SS.FFZ" - // binary: b##"ff3120ab1" | b(size)"raw data" - char c; - c = istr.peek(); - if (max_depth == 0) - { - return PARSE_FAILURE; - } - while(isspace(c)) - { - // pop the whitespace. - c = get(istr); - c = istr.peek(); - continue; - } - if(!istr.good()) - { - return 0; - } - S32 parse_count = 1; - switch(c) - { - case '{': - { - S32 child_count = parseMap(istr, data, max_depth - 1); - if((child_count == PARSE_FAILURE) || data.isUndefined()) - { - parse_count = PARSE_FAILURE; - } - else - { - parse_count += child_count; - } - if(istr.fail()) - { - LL_INFOS() << "STREAM FAILURE reading map." << LL_ENDL; - parse_count = PARSE_FAILURE; - } - break; - } - - case '[': - { - S32 child_count = parseArray(istr, data, max_depth - 1); - if((child_count == PARSE_FAILURE) || data.isUndefined()) - { - parse_count = PARSE_FAILURE; - } - else - { - parse_count += child_count; - } - if(istr.fail()) - { - LL_INFOS() << "STREAM FAILURE reading array." << LL_ENDL; - parse_count = PARSE_FAILURE; - } - break; - } - - case '!': - c = get(istr); - data.clear(); - break; - - case '0': - c = get(istr); - data = false; - break; - - case 'F': - case 'f': - ignore(istr); - c = istr.peek(); - if(isalpha(c)) - { - auto cnt = deserialize_boolean( - istr, - data, - NOTATION_FALSE_SERIAL, - false); - if(PARSE_FAILURE == cnt) parse_count = cnt; - else account(cnt); - } - else - { - data = false; - } - if(istr.fail()) - { - LL_INFOS() << "STREAM FAILURE reading boolean." << LL_ENDL; - parse_count = PARSE_FAILURE; - } - break; - - case '1': - c = get(istr); - data = true; - break; - - case 'T': - case 't': - ignore(istr); - c = istr.peek(); - if(isalpha(c)) - { - auto cnt = deserialize_boolean(istr,data,NOTATION_TRUE_SERIAL,true); - if(PARSE_FAILURE == cnt) parse_count = cnt; - else account(cnt); - } - else - { - data = true; - } - if(istr.fail()) - { - LL_INFOS() << "STREAM FAILURE reading boolean." << LL_ENDL; - parse_count = PARSE_FAILURE; - } - break; - - case 'i': - { - c = get(istr); - S32 integer = 0; - istr >> integer; - data = integer; - if(istr.fail()) - { - LL_INFOS() << "STREAM FAILURE reading integer." << LL_ENDL; - parse_count = PARSE_FAILURE; - } - break; - } - - case 'r': - { - c = get(istr); - F64 real = 0.0; - istr >> real; - data = real; - if(istr.fail()) - { - LL_INFOS() << "STREAM FAILURE reading real." << LL_ENDL; - parse_count = PARSE_FAILURE; - } - break; - } - - case 'u': - { - c = get(istr); - LLUUID id; - istr >> id; - data = id; - if(istr.fail()) - { - LL_INFOS() << "STREAM FAILURE reading uuid." << LL_ENDL; - parse_count = PARSE_FAILURE; - } - break; - } - - case '\"': - case '\'': - case 's': - if(!parseString(istr, data)) - { - parse_count = PARSE_FAILURE; - } - if(istr.fail()) - { - LL_INFOS() << "STREAM FAILURE reading string." << LL_ENDL; - parse_count = PARSE_FAILURE; - } - break; - - case 'l': - { - c = get(istr); // pop the 'l' - c = get(istr); // pop the delimiter - std::string str; - auto cnt = deserialize_string_delim(istr, str, c); - if(PARSE_FAILURE == cnt) - { - parse_count = PARSE_FAILURE; - } - else - { - data = LLURI(str); - account(cnt); - } - if(istr.fail()) - { - LL_INFOS() << "STREAM FAILURE reading link." << LL_ENDL; - parse_count = PARSE_FAILURE; - } - break; - } - - case 'd': - { - c = get(istr); // pop the 'd' - c = get(istr); // pop the delimiter - std::string str; - auto cnt = deserialize_string_delim(istr, str, c); - if(PARSE_FAILURE == cnt) - { - parse_count = PARSE_FAILURE; - } - else - { - data = LLDate(str); - account(cnt); - } - if(istr.fail()) - { - LL_INFOS() << "STREAM FAILURE reading date." << LL_ENDL; - parse_count = PARSE_FAILURE; - } - break; - } - - case 'b': - if(!parseBinary(istr, data)) - { - parse_count = PARSE_FAILURE; - } - if(istr.fail()) - { - LL_INFOS() << "STREAM FAILURE reading data." << LL_ENDL; - parse_count = PARSE_FAILURE; - } - break; - - default: - parse_count = PARSE_FAILURE; - LL_INFOS() << "Unrecognized character while parsing: int(" << int(c) - << ")" << LL_ENDL; - break; - } - if(PARSE_FAILURE == parse_count) - { - data.clear(); - } - return parse_count; -} - -S32 LLSDNotationParser::parseMap(std::istream& istr, LLSD& map, S32 max_depth) const -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD - // map: { string:object, string:object } - map = LLSD::emptyMap(); - S32 parse_count = 0; - char c = get(istr); - if(c == '{') - { - // eat commas, white - bool found_name = false; - std::string name; - c = get(istr); - while(c != '}' && istr.good()) - { - if(!found_name) - { - if((c == '\"') || (c == '\'') || (c == 's')) - { - putback(istr, c); - found_name = true; - auto count = deserialize_string(istr, name, mMaxBytesLeft); - if(PARSE_FAILURE == count) return PARSE_FAILURE; - account(count); - } - c = get(istr); - } - else - { - if(isspace(c) || (c == ':')) - { - c = get(istr); - continue; - } - putback(istr, c); - LLSD child; - S32 count = doParse(istr, child, max_depth); - if(count > 0) - { - // There must be a value for every key, thus - // child_count must be greater than 0. - parse_count += count; - map.insert(name, child); - } - else - { - return PARSE_FAILURE; - } - found_name = false; - c = get(istr); - } - } - if(c != '}') - { - map.clear(); - return PARSE_FAILURE; - } - } - return parse_count; -} - -S32 LLSDNotationParser::parseArray(std::istream& istr, LLSD& array, S32 max_depth) const -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD - // array: [ object, object, object ] - array = LLSD::emptyArray(); - S32 parse_count = 0; - char c = get(istr); - if(c == '[') - { - // eat commas, white - c = get(istr); - while((c != ']') && istr.good()) - { - LLSD child; - if(isspace(c) || (c == ',')) - { - c = get(istr); - continue; - } - putback(istr, c); - S32 count = doParse(istr, child, max_depth); - if(PARSE_FAILURE == count) - { - return PARSE_FAILURE; - } - else - { - parse_count += count; - array.append(child); - } - c = get(istr); - } - if(c != ']') - { - return PARSE_FAILURE; - } - } - return parse_count; -} - -bool LLSDNotationParser::parseString(std::istream& istr, LLSD& data) const -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD - std::string value; - auto count = deserialize_string(istr, value, mMaxBytesLeft); - if(PARSE_FAILURE == count) return false; - account(count); - data = value; - return true; -} - -bool LLSDNotationParser::parseBinary(std::istream& istr, LLSD& data) const -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD - // binary: b##"ff3120ab1" - // or: b(len)"..." - - // I want to manually control those values here to make sure the - // parser doesn't break when someone changes a constant somewhere - // else. - const U32 BINARY_BUFFER_SIZE = 256; - const U32 STREAM_GET_COUNT = 255; - - // need to read the base out. - char buf[BINARY_BUFFER_SIZE]; /* Flawfinder: ignore */ - get(istr, buf, STREAM_GET_COUNT, '"'); - char c = get(istr); - if(c != '"') return false; - if(0 == strncmp("b(", buf, 2)) - { - // We probably have a valid raw binary stream. determine - // the size, and read it. - auto len = strtol(buf + 2, NULL, 0); - if(mCheckLimits && (len > mMaxBytesLeft)) return false; - std::vector<U8> value; - if(len) - { - value.resize(len); - account(fullread(istr, (char *)&value[0], len)); - } - c = get(istr); // strip off the trailing double-quote - data = value; - } - else if(0 == strncmp("b64", buf, 3)) - { - // *FIX: A bit inefficient, but works for now. To make the - // format better, I would need to add a hint into the - // serialization format that indicated how long it was. - std::stringstream coded_stream; - get(istr, *(coded_stream.rdbuf()), '\"'); - c = get(istr); - std::string encoded(coded_stream.str()); - S32 len = apr_base64_decode_len(encoded.c_str()); - std::vector<U8> value; - if(len) - { - value.resize(len); - len = apr_base64_decode_binary(&value[0], encoded.c_str()); - value.resize(len); - } - data = value; - } - else if(0 == strncmp("b16", buf, 3)) - { - // yay, base 16. We pop the next character which is either a - // double quote or base 16 data. If it's a double quote, we're - // done parsing. If it's not, put the data back, and read the - // stream until the next double quote. - char* read; /*Flawfinder: ignore*/ - U8 byte; - U8 byte_buffer[BINARY_BUFFER_SIZE]; - U8* write; - std::vector<U8> value; - c = get(istr); - while(c != '"') - { - putback(istr, c); - read = buf; - write = byte_buffer; - get(istr, buf, STREAM_GET_COUNT, '"'); - c = get(istr); - while(*read != '\0') /*Flawfinder: ignore*/ - { - byte = hex_as_nybble(*read++); - byte = byte << 4; - byte |= hex_as_nybble(*read++); - *write++ = byte; - } - // copy the data out of the byte buffer - value.insert(value.end(), byte_buffer, write); - } - data = value; - } - else - { - return false; - } - return true; -} - - -/** - * LLSDBinaryParser - */ -LLSDBinaryParser::LLSDBinaryParser() -{ -} - -// virtual -LLSDBinaryParser::~LLSDBinaryParser() -{ -} - -// virtual -S32 LLSDBinaryParser::doParse(std::istream& istr, LLSD& data, S32 max_depth) const -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD -/** - * Undefined: '!'<br> - * Boolean: '1' for true '0' for false<br> - * Integer: 'i' + 4 bytes network byte order<br> - * Real: 'r' + 8 bytes IEEE double<br> - * UUID: 'u' + 16 byte unsigned integer<br> - * String: 's' + 4 byte integer size + string<br> - * strings also secretly support the notation format - * Date: 'd' + 8 byte IEEE double for seconds since epoch<br> - * URI: 'l' + 4 byte integer size + string uri<br> - * Binary: 'b' + 4 byte integer size + binary data<br> - * Array: '[' + 4 byte integer size + all values + ']'<br> - * Map: '{' + 4 byte integer size every(key + value) + '}'<br> - * map keys are serialized as s + 4 byte integer size + string or in the - * notation format. - */ - char c; - c = get(istr); - if(!istr.good()) - { - return 0; - } - if (max_depth == 0) - { - return PARSE_FAILURE; - } - S32 parse_count = 1; - switch(c) - { - case '{': - { - S32 child_count = parseMap(istr, data, max_depth - 1); - if((child_count == PARSE_FAILURE) || data.isUndefined()) - { - parse_count = PARSE_FAILURE; - } - else - { - parse_count += child_count; - } - if(istr.fail()) - { - LL_INFOS() << "STREAM FAILURE reading binary map." << LL_ENDL; - parse_count = PARSE_FAILURE; - } - break; - } - - case '[': - { - S32 child_count = parseArray(istr, data, max_depth - 1); - if((child_count == PARSE_FAILURE) || data.isUndefined()) - { - parse_count = PARSE_FAILURE; - } - else - { - parse_count += child_count; - } - if(istr.fail()) - { - LL_INFOS() << "STREAM FAILURE reading binary array." << LL_ENDL; - parse_count = PARSE_FAILURE; - } - break; - } - - case '!': - data.clear(); - break; - - case '0': - data = false; - break; - - case '1': - data = true; - break; - - case 'i': - { - U32 value_nbo = 0; - read(istr, (char*)&value_nbo, sizeof(U32)); /*Flawfinder: ignore*/ - data = (S32)ntohl(value_nbo); - if(istr.fail()) - { - LL_INFOS() << "STREAM FAILURE reading binary integer." << LL_ENDL; - } - break; - } - - case 'r': - { - F64 real_nbo = 0.0; - read(istr, (char*)&real_nbo, sizeof(F64)); /*Flawfinder: ignore*/ - data = ll_ntohd(real_nbo); - if(istr.fail()) - { - LL_INFOS() << "STREAM FAILURE reading binary real." << LL_ENDL; - } - break; - } - - case 'u': - { - LLUUID id; - read(istr, (char*)(&id.mData), UUID_BYTES); /*Flawfinder: ignore*/ - data = id; - if(istr.fail()) - { - LL_INFOS() << "STREAM FAILURE reading binary uuid." << LL_ENDL; - } - break; - } - - case '\'': - case '"': - { - std::string value; - auto cnt = deserialize_string_delim(istr, value, c); - if(PARSE_FAILURE == cnt) - { - parse_count = PARSE_FAILURE; - } - else - { - data = value; - account(cnt); - } - if(istr.fail()) - { - LL_INFOS() << "STREAM FAILURE reading binary (notation-style) string." - << LL_ENDL; - parse_count = PARSE_FAILURE; - } - break; - } - - case 's': - { - std::string value; - if(parseString(istr, value)) - { - data = value; - } - else - { - parse_count = PARSE_FAILURE; - } - if(istr.fail()) - { - LL_INFOS() << "STREAM FAILURE reading binary string." << LL_ENDL; - parse_count = PARSE_FAILURE; - } - break; - } - - case 'l': - { - std::string value; - if(parseString(istr, value)) - { - data = LLURI(value); - } - else - { - parse_count = PARSE_FAILURE; - } - if(istr.fail()) - { - LL_INFOS() << "STREAM FAILURE reading binary link." << LL_ENDL; - parse_count = PARSE_FAILURE; - } - break; - } - - case 'd': - { - F64 real = 0.0; - read(istr, (char*)&real, sizeof(F64)); /*Flawfinder: ignore*/ - data = LLDate(real); - if(istr.fail()) - { - LL_INFOS() << "STREAM FAILURE reading binary date." << LL_ENDL; - parse_count = PARSE_FAILURE; - } - break; - } - - case 'b': - { - // We probably have a valid raw binary stream. determine - // the size, and read it. - U32 size_nbo = 0; - read(istr, (char*)&size_nbo, sizeof(U32)); /*Flawfinder: ignore*/ - S32 size = (S32)ntohl(size_nbo); - if(mCheckLimits && (size > mMaxBytesLeft)) - { - parse_count = PARSE_FAILURE; - } - else - { - std::vector<U8> value; - if(size > 0) - { - value.resize(size); - account(fullread(istr, (char*)&value[0], size)); - } - data = value; - } - if(istr.fail()) - { - LL_INFOS() << "STREAM FAILURE reading binary." << LL_ENDL; - parse_count = PARSE_FAILURE; - } - break; - } - - default: - parse_count = PARSE_FAILURE; - LL_INFOS() << "Unrecognized character while parsing: int(" << int(c) - << ")" << LL_ENDL; - break; - } - if(PARSE_FAILURE == parse_count) - { - data.clear(); - } - return parse_count; -} - -S32 LLSDBinaryParser::parseMap(std::istream& istr, LLSD& map, S32 max_depth) const -{ - map = LLSD::emptyMap(); - U32 value_nbo = 0; - read(istr, (char*)&value_nbo, sizeof(U32)); /*Flawfinder: ignore*/ - S32 size = (S32)ntohl(value_nbo); - S32 parse_count = 0; - S32 count = 0; - char c = get(istr); - while(c != '}' && (count < size) && istr.good()) - { - std::string name; - switch(c) - { - case 'k': - if(!parseString(istr, name)) - { - return PARSE_FAILURE; - } - break; - case '\'': - case '"': - { - auto cnt = deserialize_string_delim(istr, name, c); - if(PARSE_FAILURE == cnt) return PARSE_FAILURE; - account(cnt); - break; - } - } - LLSD child; - S32 child_count = doParse(istr, child, max_depth); - if(child_count > 0) - { - // There must be a value for every key, thus child_count - // must be greater than 0. - parse_count += child_count; - map.insert(name, child); - } - else - { - return PARSE_FAILURE; - } - ++count; - c = get(istr); - } - if((c != '}') || (count < size)) - { - // Make sure it is correctly terminated and we parsed as many - // as were said to be there. - return PARSE_FAILURE; - } - return parse_count; -} - -S32 LLSDBinaryParser::parseArray(std::istream& istr, LLSD& array, S32 max_depth) const -{ - array = LLSD::emptyArray(); - U32 value_nbo = 0; - read(istr, (char*)&value_nbo, sizeof(U32)); /*Flawfinder: ignore*/ - S32 size = (S32)ntohl(value_nbo); - - // *FIX: This would be a good place to reserve some space in the - // array... - - S32 parse_count = 0; - S32 count = 0; - char c = istr.peek(); - while((c != ']') && (count < size) && istr.good()) - { - LLSD child; - S32 child_count = doParse(istr, child, max_depth); - if(PARSE_FAILURE == child_count) - { - return PARSE_FAILURE; - } - if(child_count) - { - parse_count += child_count; - array.append(child); - } - ++count; - c = istr.peek(); - } - c = get(istr); - if((c != ']') || (count < size)) - { - // Make sure it is correctly terminated and we parsed as many - // as were said to be there. - return PARSE_FAILURE; - } - return parse_count; -} - -bool LLSDBinaryParser::parseString( - std::istream& istr, - std::string& value) const -{ - // *FIX: This is memory inefficient. - U32 value_nbo = 0; - read(istr, (char*)&value_nbo, sizeof(U32)); /*Flawfinder: ignore*/ - S32 size = (S32)ntohl(value_nbo); - if(mCheckLimits && (size > mMaxBytesLeft)) return false; - if(size < 0) return false; - std::vector<char> buf; - if(size) - { - buf.resize(size); - account(fullread(istr, &buf[0], size)); - value.assign(buf.begin(), buf.end()); - } - return true; -} - - -/** - * LLSDFormatter - */ -LLSDFormatter::LLSDFormatter(bool boolAlpha, const std::string& realFmt, EFormatterOptions options): - mOptions(options) -{ - boolalpha(boolAlpha); - realFormat(realFmt); -} - -// virtual -LLSDFormatter::~LLSDFormatter() -{ } - -void LLSDFormatter::boolalpha(bool alpha) -{ - mBoolAlpha = alpha; -} - -void LLSDFormatter::realFormat(const std::string& format) -{ - mRealFormat = format; -} - -S32 LLSDFormatter::format(const LLSD& data, std::ostream& ostr) const -{ - // pass options captured by constructor - return format(data, ostr, mOptions); -} - -S32 LLSDFormatter::format(const LLSD& data, std::ostream& ostr, EFormatterOptions options) const -{ - return format_impl(data, ostr, options, 0); -} - -void LLSDFormatter::formatReal(LLSD::Real real, std::ostream& ostr) const -{ - std::string buffer = llformat(mRealFormat.c_str(), real); - ostr << buffer; -} - -/** - * LLSDNotationFormatter - */ -LLSDNotationFormatter::LLSDNotationFormatter(bool boolAlpha, const std::string& realFormat, - EFormatterOptions options): - LLSDFormatter(boolAlpha, realFormat, options) -{ -} - -// virtual -LLSDNotationFormatter::~LLSDNotationFormatter() -{ } - -// static -std::string LLSDNotationFormatter::escapeString(const std::string& in) -{ - std::ostringstream ostr; - serialize_string(in, ostr); - return ostr.str(); -} - -S32 LLSDNotationFormatter::format_impl(const LLSD& data, std::ostream& ostr, - EFormatterOptions options, U32 level) const -{ - S32 format_count = 1; - std::string pre; - std::string post; - - if (options & LLSDFormatter::OPTIONS_PRETTY) - { - for (U32 i = 0; i < level; i++) - { - pre += " "; - } - post = "\n"; - } - - switch(data.type()) - { - case LLSD::TypeMap: - { - if (0 != level) ostr << post << pre; - ostr << "{"; - std::string inner_pre; - if (options & LLSDFormatter::OPTIONS_PRETTY) - { - inner_pre = pre + " "; - } - - bool need_comma = false; - LLSD::map_const_iterator iter = data.beginMap(); - LLSD::map_const_iterator end = data.endMap(); - for(; iter != end; ++iter) - { - if(need_comma) ostr << ","; - need_comma = true; - ostr << post << inner_pre << '\''; - serialize_string((*iter).first, ostr); - ostr << "':"; - format_count += format_impl((*iter).second, ostr, options, level + 2); - } - ostr << post << pre << "}"; - break; - } - - case LLSD::TypeArray: - { - ostr << post << pre << "["; - bool need_comma = false; - LLSD::array_const_iterator iter = data.beginArray(); - LLSD::array_const_iterator end = data.endArray(); - for(; iter != end; ++iter) - { - if(need_comma) ostr << ","; - need_comma = true; - format_count += format_impl(*iter, ostr, options, level + 1); - } - ostr << "]"; - break; - } - - case LLSD::TypeUndefined: - ostr << "!"; - break; - - case LLSD::TypeBoolean: - if(mBoolAlpha || -#if( LL_WINDOWS || __GNUC__ > 2) - (ostr.flags() & std::ios::boolalpha) -#else - (ostr.flags() & 0x0100) -#endif - ) - { - ostr << (data.asBoolean() - ? NOTATION_TRUE_SERIAL : NOTATION_FALSE_SERIAL); - } - else - { - ostr << (data.asBoolean() ? 1 : 0); - } - break; - - case LLSD::TypeInteger: - ostr << "i" << data.asInteger(); - break; - - case LLSD::TypeReal: - ostr << "r"; - if(mRealFormat.empty()) - { - ostr << data.asReal(); - } - else - { - formatReal(data.asReal(), ostr); - } - break; - - case LLSD::TypeUUID: - ostr << "u" << data.asUUID(); - break; - - case LLSD::TypeString: - ostr << '\''; - serialize_string(data.asStringRef(), ostr); - ostr << '\''; - break; - - case LLSD::TypeDate: - ostr << "d\"" << data.asDate() << "\""; - break; - - case LLSD::TypeURI: - ostr << "l\""; - serialize_string(data.asString(), ostr); - ostr << "\""; - break; - - case LLSD::TypeBinary: - { - // *FIX: memory inefficient. - const std::vector<U8>& buffer = data.asBinary(); - if (options & LLSDFormatter::OPTIONS_PRETTY_BINARY) - { - ostr << "b16\""; - if (! buffer.empty()) - { - std::ios_base::fmtflags old_flags = ostr.flags(); - ostr.setf( std::ios::hex, std::ios::basefield ); - // It shouldn't strictly matter whether the emitted hex digits - // are uppercase; LLSDNotationParser handles either; but as of - // 2020-05-13, Python's llbase.llsd requires uppercase hex. - ostr << std::uppercase; - auto oldfill(ostr.fill('0')); - auto oldwidth(ostr.width()); - for (size_t i = 0; i < buffer.size(); i++) - { - // have to restate setw() before every conversion - ostr << std::setw(2) << (int) buffer[i]; - } - ostr.width(oldwidth); - ostr.fill(oldfill); - ostr.flags(old_flags); - } - } - else // ! OPTIONS_PRETTY_BINARY - { - ostr << "b(" << buffer.size() << ")\""; - if (! buffer.empty()) - { - ostr.write((const char*)&buffer[0], buffer.size()); - } - } - ostr << "\""; - break; - } - - default: - // *NOTE: This should never happen. - ostr << "!"; - break; - } - return format_count; -} - -/** - * LLSDBinaryFormatter - */ -LLSDBinaryFormatter::LLSDBinaryFormatter(bool boolAlpha, const std::string& realFormat, - EFormatterOptions options): - LLSDFormatter(boolAlpha, realFormat, options) -{ -} - -// virtual -LLSDBinaryFormatter::~LLSDBinaryFormatter() -{ } - -// virtual -S32 LLSDBinaryFormatter::format_impl(const LLSD& data, std::ostream& ostr, - EFormatterOptions options, U32 level) const -{ - S32 format_count = 1; - switch(data.type()) - { - case LLSD::TypeMap: - { - ostr.put('{'); - U32 size_nbo = htonl(data.size()); - ostr.write((const char*)(&size_nbo), sizeof(U32)); - LLSD::map_const_iterator iter = data.beginMap(); - LLSD::map_const_iterator end = data.endMap(); - for(; iter != end; ++iter) - { - ostr.put('k'); - formatString((*iter).first, ostr); - format_count += format_impl((*iter).second, ostr, options, level+1); - } - ostr.put('}'); - break; - } - - case LLSD::TypeArray: - { - ostr.put('['); - U32 size_nbo = htonl(data.size()); - ostr.write((const char*)(&size_nbo), sizeof(U32)); - LLSD::array_const_iterator iter = data.beginArray(); - LLSD::array_const_iterator end = data.endArray(); - for(; iter != end; ++iter) - { - format_count += format_impl(*iter, ostr, options, level+1); - } - ostr.put(']'); - break; - } - - case LLSD::TypeUndefined: - ostr.put('!'); - break; - - case LLSD::TypeBoolean: - if(data.asBoolean()) ostr.put(BINARY_TRUE_SERIAL); - else ostr.put(BINARY_FALSE_SERIAL); - break; - - case LLSD::TypeInteger: - { - ostr.put('i'); - U32 value_nbo = htonl(data.asInteger()); - ostr.write((const char*)(&value_nbo), sizeof(U32)); - break; - } - - case LLSD::TypeReal: - { - ostr.put('r'); - F64 value_nbo = ll_htond(data.asReal()); - ostr.write((const char*)(&value_nbo), sizeof(F64)); - break; - } - - case LLSD::TypeUUID: - { - ostr.put('u'); - LLUUID temp = data.asUUID(); - ostr.write((const char*)(&(temp.mData)), UUID_BYTES); - break; - } - - case LLSD::TypeString: - ostr.put('s'); - formatString(data.asStringRef(), ostr); - break; - - case LLSD::TypeDate: - { - ostr.put('d'); - F64 value = data.asReal(); - ostr.write((const char*)(&value), sizeof(F64)); - break; - } - - case LLSD::TypeURI: - ostr.put('l'); - formatString(data.asString(), ostr); - break; - - case LLSD::TypeBinary: - { - ostr.put('b'); - const std::vector<U8>& buffer = data.asBinary(); - U32 size_nbo = htonl(buffer.size()); - ostr.write((const char*)(&size_nbo), sizeof(U32)); - if(buffer.size()) ostr.write((const char*)&buffer[0], buffer.size()); - break; - } - - default: - // *NOTE: This should never happen. - ostr.put('!'); - break; - } - return format_count; -} - -void LLSDBinaryFormatter::formatString( - const std::string& string, - std::ostream& ostr) const -{ - U32 size_nbo = htonl(string.size()); - ostr.write((const char*)(&size_nbo), sizeof(U32)); - ostr.write(string.c_str(), string.size()); -} - -/** - * local functions - */ -llssize deserialize_string(std::istream& istr, std::string& value, llssize max_bytes) -{ - int c = istr.get(); - if(istr.fail()) - { - // No data in stream, bail out but mention the character we - // grabbed. - return LLSDParser::PARSE_FAILURE; - } - - llssize rv = LLSDParser::PARSE_FAILURE; - switch(c) - { - case '\'': - case '"': - rv = deserialize_string_delim(istr, value, c); - break; - case 's': - // technically, less than max_bytes, but this is just meant to - // catch egregious protocol errors. parse errors will be - // caught in the case of incorrect counts. - rv = deserialize_string_raw(istr, value, max_bytes); - break; - default: - break; - } - if(LLSDParser::PARSE_FAILURE == rv) return rv; - return rv + 1; // account for the character grabbed at the top. -} - -llssize deserialize_string_delim( - std::istream& istr, - std::string& value, - char delim) -{ - std::ostringstream write_buffer; - bool found_escape = false; - bool found_hex = false; - bool found_digit = false; - U8 byte = 0; - llssize count = 0; - - while (true) - { - int next_byte = istr.get(); - ++count; - - if(istr.fail()) - { - // If our stream is empty, break out - value = write_buffer.str(); - return LLSDParser::PARSE_FAILURE; - } - - char next_char = (char)next_byte; // Now that we know it's not EOF - - if(found_escape) - { - // next character(s) is a special sequence. - if(found_hex) - { - if(found_digit) - { - found_digit = false; - found_hex = false; - found_escape = false; - byte = byte << 4; - byte |= hex_as_nybble(next_char); - write_buffer << byte; - byte = 0; - } - else - { - // next character is the first nybble of - // - found_digit = true; - byte = hex_as_nybble(next_char); - } - } - else if(next_char == 'x') - { - found_hex = true; - } - else - { - switch(next_char) - { - case 'a': - write_buffer << '\a'; - break; - case 'b': - write_buffer << '\b'; - break; - case 'f': - write_buffer << '\f'; - break; - case 'n': - write_buffer << '\n'; - break; - case 'r': - write_buffer << '\r'; - break; - case 't': - write_buffer << '\t'; - break; - case 'v': - write_buffer << '\v'; - break; - default: - write_buffer << next_char; - break; - } - found_escape = false; - } - } - else if(next_char == '\\') - { - found_escape = true; - } - else if(next_char == delim) - { - break; - } - else - { - write_buffer << next_char; - } - } - - value = write_buffer.str(); - return count; -} - -llssize deserialize_string_raw( - std::istream& istr, - std::string& value, - llssize max_bytes) -{ - llssize count = 0; - const S32 BUF_LEN = 20; - char buf[BUF_LEN]; /* Flawfinder: ignore */ - istr.get(buf, BUF_LEN - 1, ')'); - count += istr.gcount(); - int c = istr.get(); - c = istr.get(); - count += 2; - if(((c == '"') || (c == '\'')) && (buf[0] == '(')) - { - // We probably have a valid raw string. determine - // the size, and read it. - // *FIX: This is memory inefficient. - auto len = strtol(buf + 1, NULL, 0); - if((max_bytes>0)&&(len>max_bytes)) return LLSDParser::PARSE_FAILURE; - std::vector<char> buf; - if(len) - { - buf.resize(len); - count += fullread(istr, (char *)&buf[0], len); - value.assign(buf.begin(), buf.end()); - } - c = istr.get(); - ++count; - if(!((c == '"') || (c == '\''))) - { - return LLSDParser::PARSE_FAILURE; - } - } - else - { - return LLSDParser::PARSE_FAILURE; - } - return count; -} - -static const char* NOTATION_STRING_CHARACTERS[256] = -{ - "\\x00", // 0 - "\\x01", // 1 - "\\x02", // 2 - "\\x03", // 3 - "\\x04", // 4 - "\\x05", // 5 - "\\x06", // 6 - "\\a", // 7 - "\\b", // 8 - "\\t", // 9 - "\\n", // 10 - "\\v", // 11 - "\\f", // 12 - "\\r", // 13 - "\\x0e", // 14 - "\\x0f", // 15 - "\\x10", // 16 - "\\x11", // 17 - "\\x12", // 18 - "\\x13", // 19 - "\\x14", // 20 - "\\x15", // 21 - "\\x16", // 22 - "\\x17", // 23 - "\\x18", // 24 - "\\x19", // 25 - "\\x1a", // 26 - "\\x1b", // 27 - "\\x1c", // 28 - "\\x1d", // 29 - "\\x1e", // 30 - "\\x1f", // 31 - " ", // 32 - "!", // 33 - "\"", // 34 - "#", // 35 - "$", // 36 - "%", // 37 - "&", // 38 - "\\'", // 39 - "(", // 40 - ")", // 41 - "*", // 42 - "+", // 43 - ",", // 44 - "-", // 45 - ".", // 46 - "/", // 47 - "0", // 48 - "1", // 49 - "2", // 50 - "3", // 51 - "4", // 52 - "5", // 53 - "6", // 54 - "7", // 55 - "8", // 56 - "9", // 57 - ":", // 58 - ";", // 59 - "<", // 60 - "=", // 61 - ">", // 62 - "?", // 63 - "@", // 64 - "A", // 65 - "B", // 66 - "C", // 67 - "D", // 68 - "E", // 69 - "F", // 70 - "G", // 71 - "H", // 72 - "I", // 73 - "J", // 74 - "K", // 75 - "L", // 76 - "M", // 77 - "N", // 78 - "O", // 79 - "P", // 80 - "Q", // 81 - "R", // 82 - "S", // 83 - "T", // 84 - "U", // 85 - "V", // 86 - "W", // 87 - "X", // 88 - "Y", // 89 - "Z", // 90 - "[", // 91 - "\\\\", // 92 - "]", // 93 - "^", // 94 - "_", // 95 - "`", // 96 - "a", // 97 - "b", // 98 - "c", // 99 - "d", // 100 - "e", // 101 - "f", // 102 - "g", // 103 - "h", // 104 - "i", // 105 - "j", // 106 - "k", // 107 - "l", // 108 - "m", // 109 - "n", // 110 - "o", // 111 - "p", // 112 - "q", // 113 - "r", // 114 - "s", // 115 - "t", // 116 - "u", // 117 - "v", // 118 - "w", // 119 - "x", // 120 - "y", // 121 - "z", // 122 - "{", // 123 - "|", // 124 - "}", // 125 - "~", // 126 - "\\x7f", // 127 - "\\x80", // 128 - "\\x81", // 129 - "\\x82", // 130 - "\\x83", // 131 - "\\x84", // 132 - "\\x85", // 133 - "\\x86", // 134 - "\\x87", // 135 - "\\x88", // 136 - "\\x89", // 137 - "\\x8a", // 138 - "\\x8b", // 139 - "\\x8c", // 140 - "\\x8d", // 141 - "\\x8e", // 142 - "\\x8f", // 143 - "\\x90", // 144 - "\\x91", // 145 - "\\x92", // 146 - "\\x93", // 147 - "\\x94", // 148 - "\\x95", // 149 - "\\x96", // 150 - "\\x97", // 151 - "\\x98", // 152 - "\\x99", // 153 - "\\x9a", // 154 - "\\x9b", // 155 - "\\x9c", // 156 - "\\x9d", // 157 - "\\x9e", // 158 - "\\x9f", // 159 - "\\xa0", // 160 - "\\xa1", // 161 - "\\xa2", // 162 - "\\xa3", // 163 - "\\xa4", // 164 - "\\xa5", // 165 - "\\xa6", // 166 - "\\xa7", // 167 - "\\xa8", // 168 - "\\xa9", // 169 - "\\xaa", // 170 - "\\xab", // 171 - "\\xac", // 172 - "\\xad", // 173 - "\\xae", // 174 - "\\xaf", // 175 - "\\xb0", // 176 - "\\xb1", // 177 - "\\xb2", // 178 - "\\xb3", // 179 - "\\xb4", // 180 - "\\xb5", // 181 - "\\xb6", // 182 - "\\xb7", // 183 - "\\xb8", // 184 - "\\xb9", // 185 - "\\xba", // 186 - "\\xbb", // 187 - "\\xbc", // 188 - "\\xbd", // 189 - "\\xbe", // 190 - "\\xbf", // 191 - "\\xc0", // 192 - "\\xc1", // 193 - "\\xc2", // 194 - "\\xc3", // 195 - "\\xc4", // 196 - "\\xc5", // 197 - "\\xc6", // 198 - "\\xc7", // 199 - "\\xc8", // 200 - "\\xc9", // 201 - "\\xca", // 202 - "\\xcb", // 203 - "\\xcc", // 204 - "\\xcd", // 205 - "\\xce", // 206 - "\\xcf", // 207 - "\\xd0", // 208 - "\\xd1", // 209 - "\\xd2", // 210 - "\\xd3", // 211 - "\\xd4", // 212 - "\\xd5", // 213 - "\\xd6", // 214 - "\\xd7", // 215 - "\\xd8", // 216 - "\\xd9", // 217 - "\\xda", // 218 - "\\xdb", // 219 - "\\xdc", // 220 - "\\xdd", // 221 - "\\xde", // 222 - "\\xdf", // 223 - "\\xe0", // 224 - "\\xe1", // 225 - "\\xe2", // 226 - "\\xe3", // 227 - "\\xe4", // 228 - "\\xe5", // 229 - "\\xe6", // 230 - "\\xe7", // 231 - "\\xe8", // 232 - "\\xe9", // 233 - "\\xea", // 234 - "\\xeb", // 235 - "\\xec", // 236 - "\\xed", // 237 - "\\xee", // 238 - "\\xef", // 239 - "\\xf0", // 240 - "\\xf1", // 241 - "\\xf2", // 242 - "\\xf3", // 243 - "\\xf4", // 244 - "\\xf5", // 245 - "\\xf6", // 246 - "\\xf7", // 247 - "\\xf8", // 248 - "\\xf9", // 249 - "\\xfa", // 250 - "\\xfb", // 251 - "\\xfc", // 252 - "\\xfd", // 253 - "\\xfe", // 254 - "\\xff" // 255 -}; - -void serialize_string(const std::string& value, std::ostream& str) -{ - std::string::const_iterator it = value.begin(); - std::string::const_iterator end = value.end(); - U8 c; - for(; it != end; ++it) - { - c = (U8)(*it); - str << NOTATION_STRING_CHARACTERS[c]; - } -} - -llssize deserialize_boolean( - std::istream& istr, - LLSD& data, - const std::string& compare, - bool value) -{ - // - // this method is a little goofy, because it gets the stream at - // the point where the t or f has already been - // consumed. Basically, parse for a patch to the string passed in - // starting at index 1. If it's a match: - // * assign data to value - // * return the number of bytes read - // otherwise: - // * set data to LLSD::null - // * return LLSDParser::PARSE_FAILURE (-1) - // - llssize bytes_read = 0; - std::string::size_type ii = 0; - char c = istr.peek(); - while((++ii < compare.size()) - && (tolower(c) == (int)compare[ii]) - && istr.good()) - { - istr.ignore(); - ++bytes_read; - c = istr.peek(); - } - if(compare.size() != ii) - { - data.clear(); - return LLSDParser::PARSE_FAILURE; - } - data = value; - return bytes_read; -} - -std::ostream& operator<<(std::ostream& s, const LLSD& llsd) -{ - s << LLSDNotationStreamer(llsd); - return s; -} - - -//dirty little zippers -- yell at davep if these are horrid - -//return a string containing gzipped bytes of binary serialized LLSD -// VERY inefficient -- creates several copies of LLSD block in memory -std::string zip_llsd(LLSD& data) -{ - std::stringstream llsd_strm; - - LLSDSerialize::toBinary(data, llsd_strm); - - const U32 CHUNK = 65536; - - z_stream strm; - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; - strm.opaque = Z_NULL; - - S32 ret = deflateInit(&strm, Z_BEST_COMPRESSION); - if (ret != Z_OK) - { - LL_WARNS() << "Failed to compress LLSD block." << LL_ENDL; - return std::string(); - } - - std::string source = llsd_strm.str(); - - U8 out[CHUNK]; - - strm.avail_in = narrow<size_t>(source.size()); - strm.next_in = (U8*) source.data(); - U8* output = NULL; - - U32 cur_size = 0; - - U32 have = 0; - - do - { - strm.avail_out = CHUNK; - strm.next_out = out; - - ret = deflate(&strm, Z_FINISH); - if (ret == Z_OK || ret == Z_STREAM_END) - { //copy result into output - if (strm.avail_out >= CHUNK) - { - deflateEnd(&strm); - if(output) - free(output); - LL_WARNS() << "Failed to compress LLSD block." << LL_ENDL; - return std::string(); - } - - have = CHUNK-strm.avail_out; - U8* new_output = (U8*) realloc(output, cur_size+have); - if (new_output == NULL) - { - LL_WARNS() << "Failed to compress LLSD block: can't reallocate memory, current size: " << cur_size << " bytes; requested " << cur_size + have << " bytes." << LL_ENDL; - deflateEnd(&strm); - if (output) - { - free(output); - } - return std::string(); - } - output = new_output; - memcpy(output+cur_size, out, have); - cur_size += have; - } - else - { - deflateEnd(&strm); - if(output) - free(output); - LL_WARNS() << "Failed to compress LLSD block." << LL_ENDL; - return std::string(); - } - } - while (ret == Z_OK); - - std::string::size_type size = cur_size; - - std::string result((char*) output, size); - deflateEnd(&strm); - if(output) - free(output); - - return result; -} - -//decompress a block of LLSD from provided istream -// not very efficient -- creats a copy of decompressed LLSD block in memory -// and deserializes from that copy using LLSDSerialize -LLUZipHelper::EZipRresult LLUZipHelper::unzip_llsd(LLSD& data, std::istream& is, S32 size) -{ - std::unique_ptr<U8[]> in = std::unique_ptr<U8[]>(new(std::nothrow) U8[size]); - if (!in) - { - return ZR_MEM_ERROR; - } - is.read((char*) in.get(), size); - - return unzip_llsd(data, in.get(), size); -} - -LLUZipHelper::EZipRresult LLUZipHelper::unzip_llsd(LLSD& data, const U8* in, S32 size) -{ - U8* result = NULL; - llssize cur_size = 0; - z_stream strm; - - constexpr U32 CHUNK = 1024 * 512; - - static thread_local std::unique_ptr<U8[]> out; - if (!out) - { - out = std::unique_ptr<U8[]>(new(std::nothrow) U8[CHUNK]); - } - - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; - strm.opaque = Z_NULL; - strm.avail_in = size; - strm.next_in = const_cast<U8*>(in); - - S32 ret = inflateInit(&strm); - - do - { - strm.avail_out = CHUNK; - strm.next_out = out.get(); - ret = inflate(&strm, Z_NO_FLUSH); - switch (ret) - { - case Z_NEED_DICT: - case Z_DATA_ERROR: - { - inflateEnd(&strm); - free(result); - return ZR_DATA_ERROR; - } - case Z_STREAM_ERROR: - case Z_BUF_ERROR: - { - inflateEnd(&strm); - free(result); - return ZR_BUFFER_ERROR; - } - - case Z_MEM_ERROR: - { - inflateEnd(&strm); - free(result); - return ZR_MEM_ERROR; - } - } - - U32 have = CHUNK-strm.avail_out; - - U8* new_result = (U8*)realloc(result, cur_size + have); - if (new_result == NULL) - { - inflateEnd(&strm); - if (result) - { - free(result); - } - return ZR_MEM_ERROR; - } - result = new_result; - memcpy(result+cur_size, out.get(), have); - cur_size += have; - - } while (ret == Z_OK && ret != Z_STREAM_END); - - inflateEnd(&strm); - - if (ret != Z_STREAM_END) - { - free(result); - return ZR_DATA_ERROR; - } - - //result now points to the decompressed LLSD block - { - char* result_ptr = strip_deprecated_header((char*)result, cur_size); - - boost::iostreams::stream<boost::iostreams::array_source> istrm(result_ptr, cur_size); - - if (!LLSDSerialize::fromBinary(data, istrm, cur_size, UNZIP_LLSD_MAX_DEPTH)) - { - free(result); - return ZR_PARSE_ERROR; - } - } - - free(result); - return ZR_OK; -} -//This unzip function will only work with a gzip header and trailer - while the contents -//of the actual compressed data is the same for either format (gzip vs zlib ), the headers -//and trailers are different for the formats. -U8* unzip_llsdNavMesh( bool& valid, size_t& outsize, std::istream& is, S32 size ) -{ - if (size == 0) - { - LL_WARNS() << "No data to unzip." << LL_ENDL; - return NULL; - } - - U8* result = NULL; - U32 cur_size = 0; - z_stream strm; - - const U32 CHUNK = 0x4000; - - U8 *in = new(std::nothrow) U8[size]; - if (in == NULL) - { - LL_WARNS() << "Memory allocation failure." << LL_ENDL; - return NULL; - } - is.read((char*) in, size); - - U8 out[CHUNK]; - - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; - strm.opaque = Z_NULL; - strm.avail_in = size; - strm.next_in = in; - - - S32 ret = inflateInit2(&strm, windowBits | ENABLE_ZLIB_GZIP ); - do - { - strm.avail_out = CHUNK; - strm.next_out = out; - ret = inflate(&strm, Z_NO_FLUSH); - if (ret == Z_STREAM_ERROR) - { - inflateEnd(&strm); - free(result); - delete [] in; - valid = false; - } - - switch (ret) - { - case Z_NEED_DICT: - ret = Z_DATA_ERROR; - case Z_DATA_ERROR: - case Z_MEM_ERROR: - inflateEnd(&strm); - free(result); - delete [] in; - valid = false; - break; - } - - U32 have = CHUNK-strm.avail_out; - - U8* new_result = (U8*) realloc(result, cur_size + have); - if (new_result == NULL) - { - LL_WARNS() << "Failed to unzip LLSD NavMesh block: can't reallocate memory, current size: " << cur_size - << " bytes; requested " << cur_size + have - << " bytes; total syze: ." << size << " bytes." - << LL_ENDL; - inflateEnd(&strm); - if (result) - { - free(result); - } - delete[] in; - valid = false; - return NULL; - } - result = new_result; - memcpy(result+cur_size, out, have); - cur_size += have; - - } while (ret == Z_OK); - - inflateEnd(&strm); - delete [] in; - - if (ret != Z_STREAM_END) - { - free(result); - valid = false; - return NULL; - } - - //result now points to the decompressed LLSD block - { - outsize= cur_size; - valid = true; - } - - return result; -} - -char* strip_deprecated_header(char* in, llssize& cur_size, llssize* header_size) -{ - const char* deprecated_header = "<? LLSD/Binary ?>"; - constexpr size_t deprecated_header_size = 17; - - if (cur_size > deprecated_header_size - && memcmp(in, deprecated_header, deprecated_header_size) == 0) - { - in = in + deprecated_header_size; - cur_size = cur_size - deprecated_header_size; - if (header_size) - { - *header_size = deprecated_header_size + 1; - } - } - - return in; -} - +/**
+ * @file llsdserialize.cpp
+ * @author Phoenix
+ * @date 2006-03-05
+ * @brief Implementation of LLSD parsers and formatters
+ *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+#include "llsdserialize.h"
+#include "llpointer.h"
+#include "llstreamtools.h" // for fullread
+
+#include <iostream>
+#include "apr_base64.h"
+
+#include <boost/iostreams/device/array.hpp>
+#include <boost/iostreams/stream.hpp>
+
+#ifdef LL_USESYSTEMLIBS
+# include <zlib.h>
+#else
+# include "zlib-ng/zlib.h" // for davep's dirty little zip functions
+#endif
+
+#if !LL_WINDOWS
+#include <netinet/in.h> // htonl & ntohl
+#endif
+
+#include "lldate.h"
+#include "llmemorystream.h"
+#include "llsd.h"
+#include "llstring.h"
+#include "lluri.h"
+
+// File constants
+static const size_t MAX_HDR_LEN = 20;
+static const S32 UNZIP_LLSD_MAX_DEPTH = 96;
+static const char LEGACY_NON_HEADER[] = "<llsd>";
+const std::string LLSD_BINARY_HEADER("LLSD/Binary");
+const std::string LLSD_XML_HEADER("LLSD/XML");
+const std::string LLSD_NOTATION_HEADER("llsd/notation");
+
+//used to deflate a gzipped asset (currently used for navmeshes)
+#define windowBits 15
+#define ENABLE_ZLIB_GZIP 32
+
+// If we published this in llsdserialize.h, we could use it in the
+// implementation of LLSDOStreamer's operator<<().
+template <class Formatter>
+void format_using(const LLSD& data, std::ostream& ostr,
+ LLSDFormatter::EFormatterOptions options=LLSDFormatter::OPTIONS_PRETTY_BINARY)
+{
+ LLPointer<Formatter> f{ new Formatter };
+ f->format(data, ostr, options);
+}
+
+template <class Parser>
+S32 parse_using(std::istream& istr, LLSD& data, size_t max_bytes, S32 max_depth=-1)
+{
+ LLPointer<Parser> p{ new Parser };
+ return p->parse(istr, data, max_bytes, max_depth);
+}
+
+/**
+ * LLSDSerialize
+ */
+
+// static
+void LLSDSerialize::serialize(const LLSD& sd, std::ostream& str, ELLSD_Serialize type,
+ LLSDFormatter::EFormatterOptions options)
+{
+ LLPointer<LLSDFormatter> f = NULL;
+
+ switch (type)
+ {
+ case LLSD_BINARY:
+ str << "<? " << LLSD_BINARY_HEADER << " ?>\n";
+ f = new LLSDBinaryFormatter;
+ break;
+
+ case LLSD_XML:
+ str << "<? " << LLSD_XML_HEADER << " ?>\n";
+ f = new LLSDXMLFormatter;
+ break;
+
+ case LLSD_NOTATION:
+ str << "<? " << LLSD_NOTATION_HEADER << " ?>\n";
+ f = new LLSDNotationFormatter;
+ break;
+
+ default:
+ LL_WARNS() << "serialize request for unknown ELLSD_Serialize" << LL_ENDL;
+ }
+
+ if (f.notNull())
+ {
+ f->format(sd, str, options);
+ }
+}
+
+// static
+bool LLSDSerialize::deserialize(LLSD& sd, std::istream& str, llssize max_bytes)
+{
+ char hdr_buf[MAX_HDR_LEN + 1] = ""; /* Flawfinder: ignore */
+ bool fail_if_not_legacy = false;
+
+ /*
+ * Get the first line before anything. Don't read more than max_bytes:
+ * this get() overload reads no more than (count-1) bytes into the
+ * specified buffer. In the usual case when max_bytes exceeds
+ * sizeof(hdr_buf), get() will read no more than sizeof(hdr_buf)-2.
+ */
+ llssize max_hdr_read = MAX_HDR_LEN;
+ if (max_bytes != LLSDSerialize::SIZE_UNLIMITED)
+ {
+ max_hdr_read = llmin(max_bytes + 1, max_hdr_read);
+ }
+ str.get(hdr_buf, max_hdr_read, '\n');
+ auto inbuf = str.gcount();
+
+ // https://en.cppreference.com/w/cpp/io/basic_istream/get
+ // When the get() above sees the specified delimiter '\n', it stops there
+ // without pulling it from the stream. If it turns out that the stream
+ // does NOT contain a header, and the content includes meaningful '\n',
+ // it's important to pull that into hdr_buf too.
+ if (inbuf < max_bytes && str.get(hdr_buf[inbuf]))
+ {
+ // got the delimiting '\n'
+ ++inbuf;
+ // None of the following requires that hdr_buf contain a final '\0'
+ // byte. We could store one if needed, since even the incremented
+ // inbuf won't exceed sizeof(hdr_buf)-1, but there's no need.
+ }
+ std::string header{ hdr_buf, static_cast<std::string::size_type>(inbuf) };
+ if (str.fail())
+ {
+ str.clear();
+ fail_if_not_legacy = true;
+ }
+
+ if (!strncasecmp(LEGACY_NON_HEADER, hdr_buf, strlen(LEGACY_NON_HEADER))) /* Flawfinder: ignore */
+ { // Create a LLSD XML parser, and parse the first chunk read above.
+ LLSDXMLParser x;
+ x.parsePart(hdr_buf, inbuf); // Parse the first part that was already read
+ auto parsed = x.parse(str, sd, max_bytes - inbuf); // Parse the rest of it
+ // Formally we should probably check (parsed != PARSE_FAILURE &&
+ // parsed > 0), but since PARSE_FAILURE is -1, this suffices.
+ return (parsed > 0);
+ }
+
+ if (fail_if_not_legacy)
+ {
+ LL_WARNS() << "deserialize LLSD parse failure" << LL_ENDL;
+ return false;
+ }
+
+ /*
+ * Remove the newline chars
+ */
+ std::string::size_type lastchar = header.find_last_not_of("\r\n");
+ if (lastchar != std::string::npos)
+ {
+ // It's important that find_last_not_of() returns size_type, which is
+ // why lastchar explicitly declares the type above. erase(size_type)
+ // erases from that offset to the end of the string, whereas
+ // erase(iterator) erases only a single character.
+ header.erase(lastchar+1);
+ }
+
+ // trim off the <? ... ?> header syntax
+ auto start = header.find_first_not_of("<? ");
+ if (start != std::string::npos)
+ {
+ auto end = header.find_first_of(" ?", start);
+ if (end != std::string::npos)
+ {
+ header = header.substr(start, end - start);
+ ws(str);
+ }
+ }
+ /*
+ * Create the parser as appropriate
+ */
+ if (0 == LLStringUtil::compareInsensitive(header, LLSD_BINARY_HEADER))
+ {
+ return (parse_using<LLSDBinaryParser>(str, sd, max_bytes-inbuf) > 0);
+ }
+ else if (0 == LLStringUtil::compareInsensitive(header, LLSD_XML_HEADER))
+ {
+ return (parse_using<LLSDXMLParser>(str, sd, max_bytes-inbuf) > 0);
+ }
+ else if (0 == LLStringUtil::compareInsensitive(header, LLSD_NOTATION_HEADER))
+ {
+ return (parse_using<LLSDNotationParser>(str, sd, max_bytes-inbuf) > 0);
+ }
+ else // no header we recognize
+ {
+ LLPointer<LLSDParser> p;
+ if (inbuf && hdr_buf[0] == '<')
+ {
+ // looks like XML
+ LL_DEBUGS() << "deserialize request with no header, assuming XML" << LL_ENDL;
+ p = new LLSDXMLParser;
+ }
+ else
+ {
+ // assume notation
+ LL_DEBUGS() << "deserialize request with no header, assuming notation" << LL_ENDL;
+ p = new LLSDNotationParser;
+ }
+ // Since we've already read 'inbuf' bytes into 'hdr_buf', prepend that
+ // data to whatever remains in 'str'.
+ LLMemoryStreamBuf already(reinterpret_cast<const U8*>(hdr_buf), inbuf);
+ cat_streambuf prebuff(&already, str.rdbuf());
+ std::istream prepend(&prebuff);
+#if 1
+ return (p->parse(prepend, sd, max_bytes) > 0);
+#else
+ // debugging the reconstituted 'prepend' stream
+ // allocate a buffer that we hope is big enough for the whole thing
+ std::vector<char> wholemsg((max_bytes == size_t(SIZE_UNLIMITED))? 1024 : max_bytes);
+ prepend.read(wholemsg.data(), std::min(max_bytes, wholemsg.size()));
+ LLMemoryStream replay(reinterpret_cast<const U8*>(wholemsg.data()), prepend.gcount());
+ auto success{ p->parse(replay, sd, prepend.gcount()) > 0 };
+ {
+ LL_DEBUGS() << (success? "parsed: $$" : "failed: '")
+ << std::string(wholemsg.data(), llmin(prepend.gcount(), 100)) << "$$"
+ << LL_ENDL;
+ }
+ return success;
+#endif
+ }
+}
+
+/**
+ * Endian handlers
+ */
+#if LL_BIG_ENDIAN
+U64 ll_htonll(U64 hostlonglong) { return hostlonglong; }
+U64 ll_ntohll(U64 netlonglong) { return netlonglong; }
+F64 ll_htond(F64 hostlonglong) { return hostlonglong; }
+F64 ll_ntohd(F64 netlonglong) { return netlonglong; }
+#else
+// I read some comments one a indicating that doing an integer add
+// here would be faster than a bitwise or. For now, the or has
+// programmer clarity, since the intended outcome matches the
+// operation.
+U64 ll_htonll(U64 hostlonglong)
+{
+ return ((U64)(htonl((U32)((hostlonglong >> 32) & 0xFFFFFFFF))) |
+ ((U64)(htonl((U32)(hostlonglong & 0xFFFFFFFF))) << 32));
+}
+U64 ll_ntohll(U64 netlonglong)
+{
+ return ((U64)(ntohl((U32)((netlonglong >> 32) & 0xFFFFFFFF))) |
+ ((U64)(ntohl((U32)(netlonglong & 0xFFFFFFFF))) << 32));
+}
+union LLEndianSwapper
+{
+ F64 d;
+ U64 i;
+};
+F64 ll_htond(F64 hostdouble)
+{
+ LLEndianSwapper tmp;
+ tmp.d = hostdouble;
+ tmp.i = ll_htonll(tmp.i);
+ return tmp.d;
+}
+F64 ll_ntohd(F64 netdouble)
+{
+ LLEndianSwapper tmp;
+ tmp.d = netdouble;
+ tmp.i = ll_ntohll(tmp.i);
+ return tmp.d;
+}
+#endif
+
+/**
+ * Local functions.
+ */
+/**
+ * @brief Figure out what kind of string it is (raw or delimited) and handoff.
+ *
+ * @param istr The stream to read from.
+ * @param value [out] The string which was found.
+ * @param max_bytes The maximum possible length of the string. Passing in
+ * a negative value will skip this check.
+ * @return Returns number of bytes read off of the stream. Returns
+ * PARSE_FAILURE (-1) on failure.
+ */
+llssize deserialize_string(std::istream& istr, std::string& value, llssize max_bytes);
+
+/**
+ * @brief Parse a delimited string.
+ *
+ * @param istr The stream to read from, with the delimiter already popped.
+ * @param value [out] The string which was found.
+ * @param d The delimiter to use.
+ * @return Returns number of bytes read off of the stream. Returns
+ * PARSE_FAILURE (-1) on failure.
+ */
+llssize deserialize_string_delim(std::istream& istr, std::string& value, char d);
+
+/**
+ * @brief Read a raw string off the stream.
+ *
+ * @param istr The stream to read from, with the (len) parameter
+ * leading the stream.
+ * @param value [out] The string which was found.
+ * @param d The delimiter to use.
+ * @param max_bytes The maximum possible length of the string. Passing in
+ * a negative value will skip this check.
+ * @return Returns number of bytes read off of the stream. Returns
+ * PARSE_FAILURE (-1) on failure.
+ */
+llssize deserialize_string_raw(
+ std::istream& istr,
+ std::string& value,
+ llssize max_bytes);
+
+/**
+ * @brief helper method for dealing with the different notation boolean format.
+ *
+ * @param istr The stream to read from with the leading character stripped.
+ * @param data [out] the result of the parse.
+ * @param compare The string to compare the boolean against
+ * @param vale The value to assign to data if the parse succeeds.
+ * @return Returns number of bytes read off of the stream. Returns
+ * PARSE_FAILURE (-1) on failure.
+ */
+llssize deserialize_boolean(
+ std::istream& istr,
+ LLSD& data,
+ const std::string& compare,
+ bool value);
+
+/**
+ * @brief Do notation escaping of a string to an ostream.
+ *
+ * @param value The string to escape and serialize
+ * @param str The stream to serialize to.
+ */
+void serialize_string(const std::string& value, std::ostream& str);
+
+
+/**
+ * Local constants.
+ */
+static const std::string NOTATION_TRUE_SERIAL("true");
+static const std::string NOTATION_FALSE_SERIAL("false");
+
+static const char BINARY_TRUE_SERIAL = '1';
+static const char BINARY_FALSE_SERIAL = '0';
+
+
+/**
+ * LLSDParser
+ */
+LLSDParser::LLSDParser()
+ : mCheckLimits(true), mMaxBytesLeft(0), mParseLines(false)
+{
+}
+
+// virtual
+LLSDParser::~LLSDParser()
+{ }
+
+S32 LLSDParser::parse(std::istream& istr, LLSD& data, llssize max_bytes, S32 max_depth)
+{
+ mCheckLimits = LLSDSerialize::SIZE_UNLIMITED != max_bytes;
+ mMaxBytesLeft = max_bytes;
+ return doParse(istr, data, max_depth);
+}
+
+
+// Parse using routine to get() lines, faster than parse()
+S32 LLSDParser::parseLines(std::istream& istr, LLSD& data)
+{
+ mCheckLimits = false;
+ mParseLines = true;
+ return doParse(istr, data);
+}
+
+
+int LLSDParser::get(std::istream& istr) const
+{
+ if(mCheckLimits) --mMaxBytesLeft;
+ return istr.get();
+}
+
+std::istream& LLSDParser::get(
+ std::istream& istr,
+ char* s,
+ std::streamsize n,
+ char delim) const
+{
+ istr.get(s, n, delim);
+ if(mCheckLimits) mMaxBytesLeft -= istr.gcount();
+ return istr;
+}
+
+std::istream& LLSDParser::get(
+ std::istream& istr,
+ std::streambuf& sb,
+ char delim) const
+{
+ istr.get(sb, delim);
+ if(mCheckLimits) mMaxBytesLeft -= istr.gcount();
+ return istr;
+}
+
+std::istream& LLSDParser::ignore(std::istream& istr) const
+{
+ istr.ignore();
+ if(mCheckLimits) --mMaxBytesLeft;
+ return istr;
+}
+
+std::istream& LLSDParser::putback(std::istream& istr, char c) const
+{
+ istr.putback(c);
+ if(mCheckLimits) ++mMaxBytesLeft;
+ return istr;
+}
+
+std::istream& LLSDParser::read(
+ std::istream& istr,
+ char* s,
+ std::streamsize n) const
+{
+ istr.read(s, n);
+ if(mCheckLimits) mMaxBytesLeft -= istr.gcount();
+ return istr;
+}
+
+void LLSDParser::account(llssize bytes) const
+{
+ if(mCheckLimits) mMaxBytesLeft -= bytes;
+}
+
+
+/**
+ * LLSDNotationParser
+ */
+LLSDNotationParser::LLSDNotationParser()
+{
+}
+
+// virtual
+LLSDNotationParser::~LLSDNotationParser()
+{ }
+
+// virtual
+S32 LLSDNotationParser::doParse(std::istream& istr, LLSD& data, S32 max_depth) const
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD
+ // map: { string:object, string:object }
+ // array: [ object, object, object ]
+ // undef: !
+ // boolean: true | false | 1 | 0 | T | F | t | f | TRUE | FALSE
+ // integer: i####
+ // real: r####
+ // uuid: u####
+ // string: "g'day" | 'have a "nice" day' | s(size)"raw data"
+ // uri: l"escaped"
+ // date: d"YYYY-MM-DDTHH:MM:SS.FFZ"
+ // binary: b##"ff3120ab1" | b(size)"raw data"
+ char c;
+ c = istr.peek();
+ if (max_depth == 0)
+ {
+ return PARSE_FAILURE;
+ }
+ while(isspace(c))
+ {
+ // pop the whitespace.
+ c = get(istr);
+ c = istr.peek();
+ continue;
+ }
+ if(!istr.good())
+ {
+ return 0;
+ }
+ S32 parse_count = 1;
+ switch(c)
+ {
+ case '{':
+ {
+ S32 child_count = parseMap(istr, data, max_depth - 1);
+ if((child_count == PARSE_FAILURE) || data.isUndefined())
+ {
+ parse_count = PARSE_FAILURE;
+ }
+ else
+ {
+ parse_count += child_count;
+ }
+ if(istr.fail())
+ {
+ LL_INFOS() << "STREAM FAILURE reading map." << LL_ENDL;
+ parse_count = PARSE_FAILURE;
+ }
+ break;
+ }
+
+ case '[':
+ {
+ S32 child_count = parseArray(istr, data, max_depth - 1);
+ if((child_count == PARSE_FAILURE) || data.isUndefined())
+ {
+ parse_count = PARSE_FAILURE;
+ }
+ else
+ {
+ parse_count += child_count;
+ }
+ if(istr.fail())
+ {
+ LL_INFOS() << "STREAM FAILURE reading array." << LL_ENDL;
+ parse_count = PARSE_FAILURE;
+ }
+ break;
+ }
+
+ case '!':
+ c = get(istr);
+ data.clear();
+ break;
+
+ case '0':
+ c = get(istr);
+ data = false;
+ break;
+
+ case 'F':
+ case 'f':
+ ignore(istr);
+ c = istr.peek();
+ if(isalpha(c))
+ {
+ auto cnt = deserialize_boolean(
+ istr,
+ data,
+ NOTATION_FALSE_SERIAL,
+ false);
+ if(PARSE_FAILURE == cnt) parse_count = cnt;
+ else account(cnt);
+ }
+ else
+ {
+ data = false;
+ }
+ if(istr.fail())
+ {
+ LL_INFOS() << "STREAM FAILURE reading boolean." << LL_ENDL;
+ parse_count = PARSE_FAILURE;
+ }
+ break;
+
+ case '1':
+ c = get(istr);
+ data = true;
+ break;
+
+ case 'T':
+ case 't':
+ ignore(istr);
+ c = istr.peek();
+ if(isalpha(c))
+ {
+ auto cnt = deserialize_boolean(istr,data,NOTATION_TRUE_SERIAL,true);
+ if(PARSE_FAILURE == cnt) parse_count = cnt;
+ else account(cnt);
+ }
+ else
+ {
+ data = true;
+ }
+ if(istr.fail())
+ {
+ LL_INFOS() << "STREAM FAILURE reading boolean." << LL_ENDL;
+ parse_count = PARSE_FAILURE;
+ }
+ break;
+
+ case 'i':
+ {
+ c = get(istr);
+ S32 integer = 0;
+ istr >> integer;
+ data = integer;
+ if(istr.fail())
+ {
+ LL_INFOS() << "STREAM FAILURE reading integer." << LL_ENDL;
+ parse_count = PARSE_FAILURE;
+ }
+ break;
+ }
+
+ case 'r':
+ {
+ c = get(istr);
+ F64 real = 0.0;
+ istr >> real;
+ data = real;
+ if(istr.fail())
+ {
+ LL_INFOS() << "STREAM FAILURE reading real." << LL_ENDL;
+ parse_count = PARSE_FAILURE;
+ }
+ break;
+ }
+
+ case 'u':
+ {
+ c = get(istr);
+ LLUUID id;
+ istr >> id;
+ data = id;
+ if(istr.fail())
+ {
+ LL_INFOS() << "STREAM FAILURE reading uuid." << LL_ENDL;
+ parse_count = PARSE_FAILURE;
+ }
+ break;
+ }
+
+ case '\"':
+ case '\'':
+ case 's':
+ if(!parseString(istr, data))
+ {
+ parse_count = PARSE_FAILURE;
+ }
+ if(istr.fail())
+ {
+ LL_INFOS() << "STREAM FAILURE reading string." << LL_ENDL;
+ parse_count = PARSE_FAILURE;
+ }
+ break;
+
+ case 'l':
+ {
+ c = get(istr); // pop the 'l'
+ c = get(istr); // pop the delimiter
+ std::string str;
+ auto cnt = deserialize_string_delim(istr, str, c);
+ if(PARSE_FAILURE == cnt)
+ {
+ parse_count = PARSE_FAILURE;
+ }
+ else
+ {
+ data = LLURI(str);
+ account(cnt);
+ }
+ if(istr.fail())
+ {
+ LL_INFOS() << "STREAM FAILURE reading link." << LL_ENDL;
+ parse_count = PARSE_FAILURE;
+ }
+ break;
+ }
+
+ case 'd':
+ {
+ c = get(istr); // pop the 'd'
+ c = get(istr); // pop the delimiter
+ std::string str;
+ auto cnt = deserialize_string_delim(istr, str, c);
+ if(PARSE_FAILURE == cnt)
+ {
+ parse_count = PARSE_FAILURE;
+ }
+ else
+ {
+ data = LLDate(str);
+ account(cnt);
+ }
+ if(istr.fail())
+ {
+ LL_INFOS() << "STREAM FAILURE reading date." << LL_ENDL;
+ parse_count = PARSE_FAILURE;
+ }
+ break;
+ }
+
+ case 'b':
+ if(!parseBinary(istr, data))
+ {
+ parse_count = PARSE_FAILURE;
+ }
+ if(istr.fail())
+ {
+ LL_INFOS() << "STREAM FAILURE reading data." << LL_ENDL;
+ parse_count = PARSE_FAILURE;
+ }
+ break;
+
+ default:
+ parse_count = PARSE_FAILURE;
+ LL_INFOS() << "Unrecognized character while parsing: int(" << int(c)
+ << ")" << LL_ENDL;
+ break;
+ }
+ if(PARSE_FAILURE == parse_count)
+ {
+ data.clear();
+ }
+ return parse_count;
+}
+
+S32 LLSDNotationParser::parseMap(std::istream& istr, LLSD& map, S32 max_depth) const
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD
+ // map: { string:object, string:object }
+ map = LLSD::emptyMap();
+ S32 parse_count = 0;
+ char c = get(istr);
+ if(c == '{')
+ {
+ // eat commas, white
+ bool found_name = false;
+ std::string name;
+ c = get(istr);
+ while(c != '}' && istr.good())
+ {
+ if(!found_name)
+ {
+ if((c == '\"') || (c == '\'') || (c == 's'))
+ {
+ putback(istr, c);
+ found_name = true;
+ auto count = deserialize_string(istr, name, mMaxBytesLeft);
+ if(PARSE_FAILURE == count) return PARSE_FAILURE;
+ account(count);
+ }
+ c = get(istr);
+ }
+ else
+ {
+ if(isspace(c) || (c == ':'))
+ {
+ c = get(istr);
+ continue;
+ }
+ putback(istr, c);
+ LLSD child;
+ S32 count = doParse(istr, child, max_depth);
+ if(count > 0)
+ {
+ // There must be a value for every key, thus
+ // child_count must be greater than 0.
+ parse_count += count;
+ map.insert(name, child);
+ }
+ else
+ {
+ return PARSE_FAILURE;
+ }
+ found_name = false;
+ c = get(istr);
+ }
+ }
+ if(c != '}')
+ {
+ map.clear();
+ return PARSE_FAILURE;
+ }
+ }
+ return parse_count;
+}
+
+S32 LLSDNotationParser::parseArray(std::istream& istr, LLSD& array, S32 max_depth) const
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD
+ // array: [ object, object, object ]
+ array = LLSD::emptyArray();
+ S32 parse_count = 0;
+ char c = get(istr);
+ if(c == '[')
+ {
+ // eat commas, white
+ c = get(istr);
+ while((c != ']') && istr.good())
+ {
+ LLSD child;
+ if(isspace(c) || (c == ','))
+ {
+ c = get(istr);
+ continue;
+ }
+ putback(istr, c);
+ S32 count = doParse(istr, child, max_depth);
+ if(PARSE_FAILURE == count)
+ {
+ return PARSE_FAILURE;
+ }
+ else
+ {
+ parse_count += count;
+ array.append(child);
+ }
+ c = get(istr);
+ }
+ if(c != ']')
+ {
+ return PARSE_FAILURE;
+ }
+ }
+ return parse_count;
+}
+
+bool LLSDNotationParser::parseString(std::istream& istr, LLSD& data) const
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD
+ std::string value;
+ auto count = deserialize_string(istr, value, mMaxBytesLeft);
+ if(PARSE_FAILURE == count) return false;
+ account(count);
+ data = value;
+ return true;
+}
+
+bool LLSDNotationParser::parseBinary(std::istream& istr, LLSD& data) const
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD
+ // binary: b##"ff3120ab1"
+ // or: b(len)"..."
+
+ // I want to manually control those values here to make sure the
+ // parser doesn't break when someone changes a constant somewhere
+ // else.
+ const U32 BINARY_BUFFER_SIZE = 256;
+ const U32 STREAM_GET_COUNT = 255;
+
+ // need to read the base out.
+ char buf[BINARY_BUFFER_SIZE]; /* Flawfinder: ignore */
+ get(istr, buf, STREAM_GET_COUNT, '"');
+ char c = get(istr);
+ if(c != '"') return false;
+ if(0 == strncmp("b(", buf, 2))
+ {
+ // We probably have a valid raw binary stream. determine
+ // the size, and read it.
+ auto len = strtol(buf + 2, NULL, 0);
+ if(mCheckLimits && (len > mMaxBytesLeft)) return false;
+ std::vector<U8> value;
+ if(len)
+ {
+ value.resize(len);
+ account(fullread(istr, (char *)&value[0], len));
+ }
+ c = get(istr); // strip off the trailing double-quote
+ data = value;
+ }
+ else if(0 == strncmp("b64", buf, 3))
+ {
+ // *FIX: A bit inefficient, but works for now. To make the
+ // format better, I would need to add a hint into the
+ // serialization format that indicated how long it was.
+ std::stringstream coded_stream;
+ get(istr, *(coded_stream.rdbuf()), '\"');
+ c = get(istr);
+ std::string encoded(coded_stream.str());
+ S32 len = apr_base64_decode_len(encoded.c_str());
+ std::vector<U8> value;
+ if(len)
+ {
+ value.resize(len);
+ len = apr_base64_decode_binary(&value[0], encoded.c_str());
+ value.resize(len);
+ }
+ data = value;
+ }
+ else if(0 == strncmp("b16", buf, 3))
+ {
+ // yay, base 16. We pop the next character which is either a
+ // double quote or base 16 data. If it's a double quote, we're
+ // done parsing. If it's not, put the data back, and read the
+ // stream until the next double quote.
+ char* read; /*Flawfinder: ignore*/
+ U8 byte;
+ U8 byte_buffer[BINARY_BUFFER_SIZE];
+ U8* write;
+ std::vector<U8> value;
+ c = get(istr);
+ while(c != '"')
+ {
+ putback(istr, c);
+ read = buf;
+ write = byte_buffer;
+ get(istr, buf, STREAM_GET_COUNT, '"');
+ c = get(istr);
+ while(*read != '\0') /*Flawfinder: ignore*/
+ {
+ byte = hex_as_nybble(*read++);
+ byte = byte << 4;
+ byte |= hex_as_nybble(*read++);
+ *write++ = byte;
+ }
+ // copy the data out of the byte buffer
+ value.insert(value.end(), byte_buffer, write);
+ }
+ data = value;
+ }
+ else
+ {
+ return false;
+ }
+ return true;
+}
+
+
+/**
+ * LLSDBinaryParser
+ */
+LLSDBinaryParser::LLSDBinaryParser()
+{
+}
+
+// virtual
+LLSDBinaryParser::~LLSDBinaryParser()
+{
+}
+
+// virtual
+S32 LLSDBinaryParser::doParse(std::istream& istr, LLSD& data, S32 max_depth) const
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD
+/**
+ * Undefined: '!'<br>
+ * Boolean: '1' for true '0' for false<br>
+ * Integer: 'i' + 4 bytes network byte order<br>
+ * Real: 'r' + 8 bytes IEEE double<br>
+ * UUID: 'u' + 16 byte unsigned integer<br>
+ * String: 's' + 4 byte integer size + string<br>
+ * strings also secretly support the notation format
+ * Date: 'd' + 8 byte IEEE double for seconds since epoch<br>
+ * URI: 'l' + 4 byte integer size + string uri<br>
+ * Binary: 'b' + 4 byte integer size + binary data<br>
+ * Array: '[' + 4 byte integer size + all values + ']'<br>
+ * Map: '{' + 4 byte integer size every(key + value) + '}'<br>
+ * map keys are serialized as s + 4 byte integer size + string or in the
+ * notation format.
+ */
+ char c;
+ c = get(istr);
+ if(!istr.good())
+ {
+ return 0;
+ }
+ if (max_depth == 0)
+ {
+ return PARSE_FAILURE;
+ }
+ S32 parse_count = 1;
+ switch(c)
+ {
+ case '{':
+ {
+ S32 child_count = parseMap(istr, data, max_depth - 1);
+ if((child_count == PARSE_FAILURE) || data.isUndefined())
+ {
+ parse_count = PARSE_FAILURE;
+ }
+ else
+ {
+ parse_count += child_count;
+ }
+ if(istr.fail())
+ {
+ LL_INFOS() << "STREAM FAILURE reading binary map." << LL_ENDL;
+ parse_count = PARSE_FAILURE;
+ }
+ break;
+ }
+
+ case '[':
+ {
+ S32 child_count = parseArray(istr, data, max_depth - 1);
+ if((child_count == PARSE_FAILURE) || data.isUndefined())
+ {
+ parse_count = PARSE_FAILURE;
+ }
+ else
+ {
+ parse_count += child_count;
+ }
+ if(istr.fail())
+ {
+ LL_INFOS() << "STREAM FAILURE reading binary array." << LL_ENDL;
+ parse_count = PARSE_FAILURE;
+ }
+ break;
+ }
+
+ case '!':
+ data.clear();
+ break;
+
+ case '0':
+ data = false;
+ break;
+
+ case '1':
+ data = true;
+ break;
+
+ case 'i':
+ {
+ U32 value_nbo = 0;
+ read(istr, (char*)&value_nbo, sizeof(U32)); /*Flawfinder: ignore*/
+ data = (S32)ntohl(value_nbo);
+ if(istr.fail())
+ {
+ LL_INFOS() << "STREAM FAILURE reading binary integer." << LL_ENDL;
+ }
+ break;
+ }
+
+ case 'r':
+ {
+ F64 real_nbo = 0.0;
+ read(istr, (char*)&real_nbo, sizeof(F64)); /*Flawfinder: ignore*/
+ data = ll_ntohd(real_nbo);
+ if(istr.fail())
+ {
+ LL_INFOS() << "STREAM FAILURE reading binary real." << LL_ENDL;
+ }
+ break;
+ }
+
+ case 'u':
+ {
+ LLUUID id;
+ read(istr, (char*)(&id.mData), UUID_BYTES); /*Flawfinder: ignore*/
+ data = id;
+ if(istr.fail())
+ {
+ LL_INFOS() << "STREAM FAILURE reading binary uuid." << LL_ENDL;
+ }
+ break;
+ }
+
+ case '\'':
+ case '"':
+ {
+ std::string value;
+ auto cnt = deserialize_string_delim(istr, value, c);
+ if(PARSE_FAILURE == cnt)
+ {
+ parse_count = PARSE_FAILURE;
+ }
+ else
+ {
+ data = value;
+ account(cnt);
+ }
+ if(istr.fail())
+ {
+ LL_INFOS() << "STREAM FAILURE reading binary (notation-style) string."
+ << LL_ENDL;
+ parse_count = PARSE_FAILURE;
+ }
+ break;
+ }
+
+ case 's':
+ {
+ std::string value;
+ if(parseString(istr, value))
+ {
+ data = value;
+ }
+ else
+ {
+ parse_count = PARSE_FAILURE;
+ }
+ if(istr.fail())
+ {
+ LL_INFOS() << "STREAM FAILURE reading binary string." << LL_ENDL;
+ parse_count = PARSE_FAILURE;
+ }
+ break;
+ }
+
+ case 'l':
+ {
+ std::string value;
+ if(parseString(istr, value))
+ {
+ data = LLURI(value);
+ }
+ else
+ {
+ parse_count = PARSE_FAILURE;
+ }
+ if(istr.fail())
+ {
+ LL_INFOS() << "STREAM FAILURE reading binary link." << LL_ENDL;
+ parse_count = PARSE_FAILURE;
+ }
+ break;
+ }
+
+ case 'd':
+ {
+ F64 real = 0.0;
+ read(istr, (char*)&real, sizeof(F64)); /*Flawfinder: ignore*/
+ data = LLDate(real);
+ if(istr.fail())
+ {
+ LL_INFOS() << "STREAM FAILURE reading binary date." << LL_ENDL;
+ parse_count = PARSE_FAILURE;
+ }
+ break;
+ }
+
+ case 'b':
+ {
+ // We probably have a valid raw binary stream. determine
+ // the size, and read it.
+ U32 size_nbo = 0;
+ read(istr, (char*)&size_nbo, sizeof(U32)); /*Flawfinder: ignore*/
+ S32 size = (S32)ntohl(size_nbo);
+ if(mCheckLimits && (size > mMaxBytesLeft))
+ {
+ parse_count = PARSE_FAILURE;
+ }
+ else
+ {
+ std::vector<U8> value;
+ if(size > 0)
+ {
+ value.resize(size);
+ account(fullread(istr, (char*)&value[0], size));
+ }
+ data = value;
+ }
+ if(istr.fail())
+ {
+ LL_INFOS() << "STREAM FAILURE reading binary." << LL_ENDL;
+ parse_count = PARSE_FAILURE;
+ }
+ break;
+ }
+
+ default:
+ parse_count = PARSE_FAILURE;
+ LL_INFOS() << "Unrecognized character while parsing: int(" << int(c)
+ << ")" << LL_ENDL;
+ break;
+ }
+ if(PARSE_FAILURE == parse_count)
+ {
+ data.clear();
+ }
+ return parse_count;
+}
+
+S32 LLSDBinaryParser::parseMap(std::istream& istr, LLSD& map, S32 max_depth) const
+{
+ map = LLSD::emptyMap();
+ U32 value_nbo = 0;
+ read(istr, (char*)&value_nbo, sizeof(U32)); /*Flawfinder: ignore*/
+ S32 size = (S32)ntohl(value_nbo);
+ S32 parse_count = 0;
+ S32 count = 0;
+ char c = get(istr);
+ while(c != '}' && (count < size) && istr.good())
+ {
+ std::string name;
+ switch(c)
+ {
+ case 'k':
+ if(!parseString(istr, name))
+ {
+ return PARSE_FAILURE;
+ }
+ break;
+ case '\'':
+ case '"':
+ {
+ auto cnt = deserialize_string_delim(istr, name, c);
+ if(PARSE_FAILURE == cnt) return PARSE_FAILURE;
+ account(cnt);
+ break;
+ }
+ }
+ LLSD child;
+ S32 child_count = doParse(istr, child, max_depth);
+ if(child_count > 0)
+ {
+ // There must be a value for every key, thus child_count
+ // must be greater than 0.
+ parse_count += child_count;
+ map.insert(name, child);
+ }
+ else
+ {
+ return PARSE_FAILURE;
+ }
+ ++count;
+ c = get(istr);
+ }
+ if((c != '}') || (count < size))
+ {
+ // Make sure it is correctly terminated and we parsed as many
+ // as were said to be there.
+ return PARSE_FAILURE;
+ }
+ return parse_count;
+}
+
+S32 LLSDBinaryParser::parseArray(std::istream& istr, LLSD& array, S32 max_depth) const
+{
+ array = LLSD::emptyArray();
+ U32 value_nbo = 0;
+ read(istr, (char*)&value_nbo, sizeof(U32)); /*Flawfinder: ignore*/
+ S32 size = (S32)ntohl(value_nbo);
+
+ // *FIX: This would be a good place to reserve some space in the
+ // array...
+
+ S32 parse_count = 0;
+ S32 count = 0;
+ char c = istr.peek();
+ while((c != ']') && (count < size) && istr.good())
+ {
+ LLSD child;
+ S32 child_count = doParse(istr, child, max_depth);
+ if(PARSE_FAILURE == child_count)
+ {
+ return PARSE_FAILURE;
+ }
+ if(child_count)
+ {
+ parse_count += child_count;
+ array.append(child);
+ }
+ ++count;
+ c = istr.peek();
+ }
+ c = get(istr);
+ if((c != ']') || (count < size))
+ {
+ // Make sure it is correctly terminated and we parsed as many
+ // as were said to be there.
+ return PARSE_FAILURE;
+ }
+ return parse_count;
+}
+
+bool LLSDBinaryParser::parseString(
+ std::istream& istr,
+ std::string& value) const
+{
+ // *FIX: This is memory inefficient.
+ U32 value_nbo = 0;
+ read(istr, (char*)&value_nbo, sizeof(U32)); /*Flawfinder: ignore*/
+ S32 size = (S32)ntohl(value_nbo);
+ if(mCheckLimits && (size > mMaxBytesLeft)) return false;
+ if(size < 0) return false;
+ std::vector<char> buf;
+ if(size)
+ {
+ buf.resize(size);
+ account(fullread(istr, &buf[0], size));
+ value.assign(buf.begin(), buf.end());
+ }
+ return true;
+}
+
+
+/**
+ * LLSDFormatter
+ */
+LLSDFormatter::LLSDFormatter(bool boolAlpha, const std::string& realFmt, EFormatterOptions options):
+ mOptions(options)
+{
+ boolalpha(boolAlpha);
+ realFormat(realFmt);
+}
+
+// virtual
+LLSDFormatter::~LLSDFormatter()
+{ }
+
+void LLSDFormatter::boolalpha(bool alpha)
+{
+ mBoolAlpha = alpha;
+}
+
+void LLSDFormatter::realFormat(const std::string& format)
+{
+ mRealFormat = format;
+}
+
+S32 LLSDFormatter::format(const LLSD& data, std::ostream& ostr) const
+{
+ // pass options captured by constructor
+ return format(data, ostr, mOptions);
+}
+
+S32 LLSDFormatter::format(const LLSD& data, std::ostream& ostr, EFormatterOptions options) const
+{
+ return format_impl(data, ostr, options, 0);
+}
+
+void LLSDFormatter::formatReal(LLSD::Real real, std::ostream& ostr) const
+{
+ std::string buffer = llformat(mRealFormat.c_str(), real);
+ ostr << buffer;
+}
+
+/**
+ * LLSDNotationFormatter
+ */
+LLSDNotationFormatter::LLSDNotationFormatter(bool boolAlpha, const std::string& realFormat,
+ EFormatterOptions options):
+ LLSDFormatter(boolAlpha, realFormat, options)
+{
+}
+
+// virtual
+LLSDNotationFormatter::~LLSDNotationFormatter()
+{ }
+
+// static
+std::string LLSDNotationFormatter::escapeString(const std::string& in)
+{
+ std::ostringstream ostr;
+ serialize_string(in, ostr);
+ return ostr.str();
+}
+
+S32 LLSDNotationFormatter::format_impl(const LLSD& data, std::ostream& ostr,
+ EFormatterOptions options, U32 level) const
+{
+ S32 format_count = 1;
+ std::string pre;
+ std::string post;
+
+ if (options & LLSDFormatter::OPTIONS_PRETTY)
+ {
+ for (U32 i = 0; i < level; i++)
+ {
+ pre += " ";
+ }
+ post = "\n";
+ }
+
+ switch(data.type())
+ {
+ case LLSD::TypeMap:
+ {
+ if (0 != level) ostr << post << pre;
+ ostr << "{";
+ std::string inner_pre;
+ if (options & LLSDFormatter::OPTIONS_PRETTY)
+ {
+ inner_pre = pre + " ";
+ }
+
+ bool need_comma = false;
+ LLSD::map_const_iterator iter = data.beginMap();
+ LLSD::map_const_iterator end = data.endMap();
+ for(; iter != end; ++iter)
+ {
+ if(need_comma) ostr << ",";
+ need_comma = true;
+ ostr << post << inner_pre << '\'';
+ serialize_string((*iter).first, ostr);
+ ostr << "':";
+ format_count += format_impl((*iter).second, ostr, options, level + 2);
+ }
+ ostr << post << pre << "}";
+ break;
+ }
+
+ case LLSD::TypeArray:
+ {
+ ostr << post << pre << "[";
+ bool need_comma = false;
+ LLSD::array_const_iterator iter = data.beginArray();
+ LLSD::array_const_iterator end = data.endArray();
+ for(; iter != end; ++iter)
+ {
+ if(need_comma) ostr << ",";
+ need_comma = true;
+ format_count += format_impl(*iter, ostr, options, level + 1);
+ }
+ ostr << "]";
+ break;
+ }
+
+ case LLSD::TypeUndefined:
+ ostr << "!";
+ break;
+
+ case LLSD::TypeBoolean:
+ if(mBoolAlpha ||
+#if( LL_WINDOWS || __GNUC__ > 2)
+ (ostr.flags() & std::ios::boolalpha)
+#else
+ (ostr.flags() & 0x0100)
+#endif
+ )
+ {
+ ostr << (data.asBoolean()
+ ? NOTATION_TRUE_SERIAL : NOTATION_FALSE_SERIAL);
+ }
+ else
+ {
+ ostr << (data.asBoolean() ? 1 : 0);
+ }
+ break;
+
+ case LLSD::TypeInteger:
+ ostr << "i" << data.asInteger();
+ break;
+
+ case LLSD::TypeReal:
+ ostr << "r";
+ if(mRealFormat.empty())
+ {
+ ostr << data.asReal();
+ }
+ else
+ {
+ formatReal(data.asReal(), ostr);
+ }
+ break;
+
+ case LLSD::TypeUUID:
+ ostr << "u" << data.asUUID();
+ break;
+
+ case LLSD::TypeString:
+ ostr << '\'';
+ serialize_string(data.asStringRef(), ostr);
+ ostr << '\'';
+ break;
+
+ case LLSD::TypeDate:
+ ostr << "d\"" << data.asDate() << "\"";
+ break;
+
+ case LLSD::TypeURI:
+ ostr << "l\"";
+ serialize_string(data.asString(), ostr);
+ ostr << "\"";
+ break;
+
+ case LLSD::TypeBinary:
+ {
+ // *FIX: memory inefficient.
+ const std::vector<U8>& buffer = data.asBinary();
+ if (options & LLSDFormatter::OPTIONS_PRETTY_BINARY)
+ {
+ ostr << "b16\"";
+ if (! buffer.empty())
+ {
+ std::ios_base::fmtflags old_flags = ostr.flags();
+ ostr.setf( std::ios::hex, std::ios::basefield );
+ // It shouldn't strictly matter whether the emitted hex digits
+ // are uppercase; LLSDNotationParser handles either; but as of
+ // 2020-05-13, Python's llbase.llsd requires uppercase hex.
+ ostr << std::uppercase;
+ auto oldfill(ostr.fill('0'));
+ auto oldwidth(ostr.width());
+ for (size_t i = 0; i < buffer.size(); i++)
+ {
+ // have to restate setw() before every conversion
+ ostr << std::setw(2) << (int) buffer[i];
+ }
+ ostr.width(oldwidth);
+ ostr.fill(oldfill);
+ ostr.flags(old_flags);
+ }
+ }
+ else // ! OPTIONS_PRETTY_BINARY
+ {
+ ostr << "b(" << buffer.size() << ")\"";
+ if (! buffer.empty())
+ {
+ ostr.write((const char*)&buffer[0], buffer.size());
+ }
+ }
+ ostr << "\"";
+ break;
+ }
+
+ default:
+ // *NOTE: This should never happen.
+ ostr << "!";
+ break;
+ }
+ return format_count;
+}
+
+/**
+ * LLSDBinaryFormatter
+ */
+LLSDBinaryFormatter::LLSDBinaryFormatter(bool boolAlpha, const std::string& realFormat,
+ EFormatterOptions options):
+ LLSDFormatter(boolAlpha, realFormat, options)
+{
+}
+
+// virtual
+LLSDBinaryFormatter::~LLSDBinaryFormatter()
+{ }
+
+// virtual
+S32 LLSDBinaryFormatter::format_impl(const LLSD& data, std::ostream& ostr,
+ EFormatterOptions options, U32 level) const
+{
+ S32 format_count = 1;
+ switch(data.type())
+ {
+ case LLSD::TypeMap:
+ {
+ ostr.put('{');
+ U32 size_nbo = htonl(data.size());
+ ostr.write((const char*)(&size_nbo), sizeof(U32));
+ LLSD::map_const_iterator iter = data.beginMap();
+ LLSD::map_const_iterator end = data.endMap();
+ for(; iter != end; ++iter)
+ {
+ ostr.put('k');
+ formatString((*iter).first, ostr);
+ format_count += format_impl((*iter).second, ostr, options, level+1);
+ }
+ ostr.put('}');
+ break;
+ }
+
+ case LLSD::TypeArray:
+ {
+ ostr.put('[');
+ U32 size_nbo = htonl(data.size());
+ ostr.write((const char*)(&size_nbo), sizeof(U32));
+ LLSD::array_const_iterator iter = data.beginArray();
+ LLSD::array_const_iterator end = data.endArray();
+ for(; iter != end; ++iter)
+ {
+ format_count += format_impl(*iter, ostr, options, level+1);
+ }
+ ostr.put(']');
+ break;
+ }
+
+ case LLSD::TypeUndefined:
+ ostr.put('!');
+ break;
+
+ case LLSD::TypeBoolean:
+ if(data.asBoolean()) ostr.put(BINARY_TRUE_SERIAL);
+ else ostr.put(BINARY_FALSE_SERIAL);
+ break;
+
+ case LLSD::TypeInteger:
+ {
+ ostr.put('i');
+ U32 value_nbo = htonl(data.asInteger());
+ ostr.write((const char*)(&value_nbo), sizeof(U32));
+ break;
+ }
+
+ case LLSD::TypeReal:
+ {
+ ostr.put('r');
+ F64 value_nbo = ll_htond(data.asReal());
+ ostr.write((const char*)(&value_nbo), sizeof(F64));
+ break;
+ }
+
+ case LLSD::TypeUUID:
+ {
+ ostr.put('u');
+ LLUUID temp = data.asUUID();
+ ostr.write((const char*)(&(temp.mData)), UUID_BYTES);
+ break;
+ }
+
+ case LLSD::TypeString:
+ ostr.put('s');
+ formatString(data.asStringRef(), ostr);
+ break;
+
+ case LLSD::TypeDate:
+ {
+ ostr.put('d');
+ F64 value = data.asReal();
+ ostr.write((const char*)(&value), sizeof(F64));
+ break;
+ }
+
+ case LLSD::TypeURI:
+ ostr.put('l');
+ formatString(data.asString(), ostr);
+ break;
+
+ case LLSD::TypeBinary:
+ {
+ ostr.put('b');
+ const std::vector<U8>& buffer = data.asBinary();
+ U32 size_nbo = htonl(buffer.size());
+ ostr.write((const char*)(&size_nbo), sizeof(U32));
+ if(buffer.size()) ostr.write((const char*)&buffer[0], buffer.size());
+ break;
+ }
+
+ default:
+ // *NOTE: This should never happen.
+ ostr.put('!');
+ break;
+ }
+ return format_count;
+}
+
+void LLSDBinaryFormatter::formatString(
+ const std::string& string,
+ std::ostream& ostr) const
+{
+ U32 size_nbo = htonl(string.size());
+ ostr.write((const char*)(&size_nbo), sizeof(U32));
+ ostr.write(string.c_str(), string.size());
+}
+
+/**
+ * local functions
+ */
+llssize deserialize_string(std::istream& istr, std::string& value, llssize max_bytes)
+{
+ int c = istr.get();
+ if(istr.fail())
+ {
+ // No data in stream, bail out but mention the character we
+ // grabbed.
+ return LLSDParser::PARSE_FAILURE;
+ }
+
+ llssize rv = LLSDParser::PARSE_FAILURE;
+ switch(c)
+ {
+ case '\'':
+ case '"':
+ rv = deserialize_string_delim(istr, value, c);
+ break;
+ case 's':
+ // technically, less than max_bytes, but this is just meant to
+ // catch egregious protocol errors. parse errors will be
+ // caught in the case of incorrect counts.
+ rv = deserialize_string_raw(istr, value, max_bytes);
+ break;
+ default:
+ break;
+ }
+ if(LLSDParser::PARSE_FAILURE == rv) return rv;
+ return rv + 1; // account for the character grabbed at the top.
+}
+
+llssize deserialize_string_delim(
+ std::istream& istr,
+ std::string& value,
+ char delim)
+{
+ std::ostringstream write_buffer;
+ bool found_escape = false;
+ bool found_hex = false;
+ bool found_digit = false;
+ U8 byte = 0;
+ llssize count = 0;
+
+ while (true)
+ {
+ int next_byte = istr.get();
+ ++count;
+
+ if(istr.fail())
+ {
+ // If our stream is empty, break out
+ value = write_buffer.str();
+ return LLSDParser::PARSE_FAILURE;
+ }
+
+ char next_char = (char)next_byte; // Now that we know it's not EOF
+
+ if(found_escape)
+ {
+ // next character(s) is a special sequence.
+ if(found_hex)
+ {
+ if(found_digit)
+ {
+ found_digit = false;
+ found_hex = false;
+ found_escape = false;
+ byte = byte << 4;
+ byte |= hex_as_nybble(next_char);
+ write_buffer << byte;
+ byte = 0;
+ }
+ else
+ {
+ // next character is the first nybble of
+ //
+ found_digit = true;
+ byte = hex_as_nybble(next_char);
+ }
+ }
+ else if(next_char == 'x')
+ {
+ found_hex = true;
+ }
+ else
+ {
+ switch(next_char)
+ {
+ case 'a':
+ write_buffer << '\a';
+ break;
+ case 'b':
+ write_buffer << '\b';
+ break;
+ case 'f':
+ write_buffer << '\f';
+ break;
+ case 'n':
+ write_buffer << '\n';
+ break;
+ case 'r':
+ write_buffer << '\r';
+ break;
+ case 't':
+ write_buffer << '\t';
+ break;
+ case 'v':
+ write_buffer << '\v';
+ break;
+ default:
+ write_buffer << next_char;
+ break;
+ }
+ found_escape = false;
+ }
+ }
+ else if(next_char == '\\')
+ {
+ found_escape = true;
+ }
+ else if(next_char == delim)
+ {
+ break;
+ }
+ else
+ {
+ write_buffer << next_char;
+ }
+ }
+
+ value = write_buffer.str();
+ return count;
+}
+
+llssize deserialize_string_raw(
+ std::istream& istr,
+ std::string& value,
+ llssize max_bytes)
+{
+ llssize count = 0;
+ const S32 BUF_LEN = 20;
+ char buf[BUF_LEN]; /* Flawfinder: ignore */
+ istr.get(buf, BUF_LEN - 1, ')');
+ count += istr.gcount();
+ int c = istr.get();
+ c = istr.get();
+ count += 2;
+ if(((c == '"') || (c == '\'')) && (buf[0] == '('))
+ {
+ // We probably have a valid raw string. determine
+ // the size, and read it.
+ // *FIX: This is memory inefficient.
+ auto len = strtol(buf + 1, NULL, 0);
+ if((max_bytes>0)&&(len>max_bytes)) return LLSDParser::PARSE_FAILURE;
+ std::vector<char> buf;
+ if(len)
+ {
+ buf.resize(len);
+ count += fullread(istr, (char *)&buf[0], len);
+ value.assign(buf.begin(), buf.end());
+ }
+ c = istr.get();
+ ++count;
+ if(!((c == '"') || (c == '\'')))
+ {
+ return LLSDParser::PARSE_FAILURE;
+ }
+ }
+ else
+ {
+ return LLSDParser::PARSE_FAILURE;
+ }
+ return count;
+}
+
+static const char* NOTATION_STRING_CHARACTERS[256] =
+{
+ "\\x00", // 0
+ "\\x01", // 1
+ "\\x02", // 2
+ "\\x03", // 3
+ "\\x04", // 4
+ "\\x05", // 5
+ "\\x06", // 6
+ "\\a", // 7
+ "\\b", // 8
+ "\\t", // 9
+ "\\n", // 10
+ "\\v", // 11
+ "\\f", // 12
+ "\\r", // 13
+ "\\x0e", // 14
+ "\\x0f", // 15
+ "\\x10", // 16
+ "\\x11", // 17
+ "\\x12", // 18
+ "\\x13", // 19
+ "\\x14", // 20
+ "\\x15", // 21
+ "\\x16", // 22
+ "\\x17", // 23
+ "\\x18", // 24
+ "\\x19", // 25
+ "\\x1a", // 26
+ "\\x1b", // 27
+ "\\x1c", // 28
+ "\\x1d", // 29
+ "\\x1e", // 30
+ "\\x1f", // 31
+ " ", // 32
+ "!", // 33
+ "\"", // 34
+ "#", // 35
+ "$", // 36
+ "%", // 37
+ "&", // 38
+ "\\'", // 39
+ "(", // 40
+ ")", // 41
+ "*", // 42
+ "+", // 43
+ ",", // 44
+ "-", // 45
+ ".", // 46
+ "/", // 47
+ "0", // 48
+ "1", // 49
+ "2", // 50
+ "3", // 51
+ "4", // 52
+ "5", // 53
+ "6", // 54
+ "7", // 55
+ "8", // 56
+ "9", // 57
+ ":", // 58
+ ";", // 59
+ "<", // 60
+ "=", // 61
+ ">", // 62
+ "?", // 63
+ "@", // 64
+ "A", // 65
+ "B", // 66
+ "C", // 67
+ "D", // 68
+ "E", // 69
+ "F", // 70
+ "G", // 71
+ "H", // 72
+ "I", // 73
+ "J", // 74
+ "K", // 75
+ "L", // 76
+ "M", // 77
+ "N", // 78
+ "O", // 79
+ "P", // 80
+ "Q", // 81
+ "R", // 82
+ "S", // 83
+ "T", // 84
+ "U", // 85
+ "V", // 86
+ "W", // 87
+ "X", // 88
+ "Y", // 89
+ "Z", // 90
+ "[", // 91
+ "\\\\", // 92
+ "]", // 93
+ "^", // 94
+ "_", // 95
+ "`", // 96
+ "a", // 97
+ "b", // 98
+ "c", // 99
+ "d", // 100
+ "e", // 101
+ "f", // 102
+ "g", // 103
+ "h", // 104
+ "i", // 105
+ "j", // 106
+ "k", // 107
+ "l", // 108
+ "m", // 109
+ "n", // 110
+ "o", // 111
+ "p", // 112
+ "q", // 113
+ "r", // 114
+ "s", // 115
+ "t", // 116
+ "u", // 117
+ "v", // 118
+ "w", // 119
+ "x", // 120
+ "y", // 121
+ "z", // 122
+ "{", // 123
+ "|", // 124
+ "}", // 125
+ "~", // 126
+ "\\x7f", // 127
+ "\\x80", // 128
+ "\\x81", // 129
+ "\\x82", // 130
+ "\\x83", // 131
+ "\\x84", // 132
+ "\\x85", // 133
+ "\\x86", // 134
+ "\\x87", // 135
+ "\\x88", // 136
+ "\\x89", // 137
+ "\\x8a", // 138
+ "\\x8b", // 139
+ "\\x8c", // 140
+ "\\x8d", // 141
+ "\\x8e", // 142
+ "\\x8f", // 143
+ "\\x90", // 144
+ "\\x91", // 145
+ "\\x92", // 146
+ "\\x93", // 147
+ "\\x94", // 148
+ "\\x95", // 149
+ "\\x96", // 150
+ "\\x97", // 151
+ "\\x98", // 152
+ "\\x99", // 153
+ "\\x9a", // 154
+ "\\x9b", // 155
+ "\\x9c", // 156
+ "\\x9d", // 157
+ "\\x9e", // 158
+ "\\x9f", // 159
+ "\\xa0", // 160
+ "\\xa1", // 161
+ "\\xa2", // 162
+ "\\xa3", // 163
+ "\\xa4", // 164
+ "\\xa5", // 165
+ "\\xa6", // 166
+ "\\xa7", // 167
+ "\\xa8", // 168
+ "\\xa9", // 169
+ "\\xaa", // 170
+ "\\xab", // 171
+ "\\xac", // 172
+ "\\xad", // 173
+ "\\xae", // 174
+ "\\xaf", // 175
+ "\\xb0", // 176
+ "\\xb1", // 177
+ "\\xb2", // 178
+ "\\xb3", // 179
+ "\\xb4", // 180
+ "\\xb5", // 181
+ "\\xb6", // 182
+ "\\xb7", // 183
+ "\\xb8", // 184
+ "\\xb9", // 185
+ "\\xba", // 186
+ "\\xbb", // 187
+ "\\xbc", // 188
+ "\\xbd", // 189
+ "\\xbe", // 190
+ "\\xbf", // 191
+ "\\xc0", // 192
+ "\\xc1", // 193
+ "\\xc2", // 194
+ "\\xc3", // 195
+ "\\xc4", // 196
+ "\\xc5", // 197
+ "\\xc6", // 198
+ "\\xc7", // 199
+ "\\xc8", // 200
+ "\\xc9", // 201
+ "\\xca", // 202
+ "\\xcb", // 203
+ "\\xcc", // 204
+ "\\xcd", // 205
+ "\\xce", // 206
+ "\\xcf", // 207
+ "\\xd0", // 208
+ "\\xd1", // 209
+ "\\xd2", // 210
+ "\\xd3", // 211
+ "\\xd4", // 212
+ "\\xd5", // 213
+ "\\xd6", // 214
+ "\\xd7", // 215
+ "\\xd8", // 216
+ "\\xd9", // 217
+ "\\xda", // 218
+ "\\xdb", // 219
+ "\\xdc", // 220
+ "\\xdd", // 221
+ "\\xde", // 222
+ "\\xdf", // 223
+ "\\xe0", // 224
+ "\\xe1", // 225
+ "\\xe2", // 226
+ "\\xe3", // 227
+ "\\xe4", // 228
+ "\\xe5", // 229
+ "\\xe6", // 230
+ "\\xe7", // 231
+ "\\xe8", // 232
+ "\\xe9", // 233
+ "\\xea", // 234
+ "\\xeb", // 235
+ "\\xec", // 236
+ "\\xed", // 237
+ "\\xee", // 238
+ "\\xef", // 239
+ "\\xf0", // 240
+ "\\xf1", // 241
+ "\\xf2", // 242
+ "\\xf3", // 243
+ "\\xf4", // 244
+ "\\xf5", // 245
+ "\\xf6", // 246
+ "\\xf7", // 247
+ "\\xf8", // 248
+ "\\xf9", // 249
+ "\\xfa", // 250
+ "\\xfb", // 251
+ "\\xfc", // 252
+ "\\xfd", // 253
+ "\\xfe", // 254
+ "\\xff" // 255
+};
+
+void serialize_string(const std::string& value, std::ostream& str)
+{
+ std::string::const_iterator it = value.begin();
+ std::string::const_iterator end = value.end();
+ U8 c;
+ for(; it != end; ++it)
+ {
+ c = (U8)(*it);
+ str << NOTATION_STRING_CHARACTERS[c];
+ }
+}
+
+llssize deserialize_boolean(
+ std::istream& istr,
+ LLSD& data,
+ const std::string& compare,
+ bool value)
+{
+ //
+ // this method is a little goofy, because it gets the stream at
+ // the point where the t or f has already been
+ // consumed. Basically, parse for a patch to the string passed in
+ // starting at index 1. If it's a match:
+ // * assign data to value
+ // * return the number of bytes read
+ // otherwise:
+ // * set data to LLSD::null
+ // * return LLSDParser::PARSE_FAILURE (-1)
+ //
+ llssize bytes_read = 0;
+ std::string::size_type ii = 0;
+ char c = istr.peek();
+ while((++ii < compare.size())
+ && (tolower(c) == (int)compare[ii])
+ && istr.good())
+ {
+ istr.ignore();
+ ++bytes_read;
+ c = istr.peek();
+ }
+ if(compare.size() != ii)
+ {
+ data.clear();
+ return LLSDParser::PARSE_FAILURE;
+ }
+ data = value;
+ return bytes_read;
+}
+
+std::ostream& operator<<(std::ostream& s, const LLSD& llsd)
+{
+ s << LLSDNotationStreamer(llsd);
+ return s;
+}
+
+
+//dirty little zippers -- yell at davep if these are horrid
+
+//return a string containing gzipped bytes of binary serialized LLSD
+// VERY inefficient -- creates several copies of LLSD block in memory
+std::string zip_llsd(LLSD& data)
+{
+ std::stringstream llsd_strm;
+
+ LLSDSerialize::toBinary(data, llsd_strm);
+
+ const U32 CHUNK = 65536;
+
+ z_stream strm;
+ strm.zalloc = Z_NULL;
+ strm.zfree = Z_NULL;
+ strm.opaque = Z_NULL;
+
+ S32 ret = deflateInit(&strm, Z_BEST_COMPRESSION);
+ if (ret != Z_OK)
+ {
+ LL_WARNS() << "Failed to compress LLSD block." << LL_ENDL;
+ return std::string();
+ }
+
+ std::string source = llsd_strm.str();
+
+ U8 out[CHUNK];
+
+ strm.avail_in = narrow<size_t>(source.size());
+ strm.next_in = (U8*) source.data();
+ U8* output = NULL;
+
+ U32 cur_size = 0;
+
+ U32 have = 0;
+
+ do
+ {
+ strm.avail_out = CHUNK;
+ strm.next_out = out;
+
+ ret = deflate(&strm, Z_FINISH);
+ if (ret == Z_OK || ret == Z_STREAM_END)
+ { //copy result into output
+ if (strm.avail_out >= CHUNK)
+ {
+ deflateEnd(&strm);
+ if(output)
+ free(output);
+ LL_WARNS() << "Failed to compress LLSD block." << LL_ENDL;
+ return std::string();
+ }
+
+ have = CHUNK-strm.avail_out;
+ U8* new_output = (U8*) realloc(output, cur_size+have);
+ if (new_output == NULL)
+ {
+ LL_WARNS() << "Failed to compress LLSD block: can't reallocate memory, current size: " << cur_size << " bytes; requested " << cur_size + have << " bytes." << LL_ENDL;
+ deflateEnd(&strm);
+ if (output)
+ {
+ free(output);
+ }
+ return std::string();
+ }
+ output = new_output;
+ memcpy(output+cur_size, out, have);
+ cur_size += have;
+ }
+ else
+ {
+ deflateEnd(&strm);
+ if(output)
+ free(output);
+ LL_WARNS() << "Failed to compress LLSD block." << LL_ENDL;
+ return std::string();
+ }
+ }
+ while (ret == Z_OK);
+
+ std::string::size_type size = cur_size;
+
+ std::string result((char*) output, size);
+ deflateEnd(&strm);
+ if(output)
+ free(output);
+
+ return result;
+}
+
+//decompress a block of LLSD from provided istream
+// not very efficient -- creats a copy of decompressed LLSD block in memory
+// and deserializes from that copy using LLSDSerialize
+LLUZipHelper::EZipRresult LLUZipHelper::unzip_llsd(LLSD& data, std::istream& is, S32 size)
+{
+ std::unique_ptr<U8[]> in = std::unique_ptr<U8[]>(new(std::nothrow) U8[size]);
+ if (!in)
+ {
+ return ZR_MEM_ERROR;
+ }
+ is.read((char*) in.get(), size);
+
+ return unzip_llsd(data, in.get(), size);
+}
+
+LLUZipHelper::EZipRresult LLUZipHelper::unzip_llsd(LLSD& data, const U8* in, S32 size)
+{
+ U8* result = NULL;
+ llssize cur_size = 0;
+ z_stream strm;
+
+ constexpr U32 CHUNK = 1024 * 512;
+
+ static thread_local std::unique_ptr<U8[]> out;
+ if (!out)
+ {
+ out = std::unique_ptr<U8[]>(new(std::nothrow) U8[CHUNK]);
+ }
+
+ strm.zalloc = Z_NULL;
+ strm.zfree = Z_NULL;
+ strm.opaque = Z_NULL;
+ strm.avail_in = size;
+ strm.next_in = const_cast<U8*>(in);
+
+ S32 ret = inflateInit(&strm);
+
+ do
+ {
+ strm.avail_out = CHUNK;
+ strm.next_out = out.get();
+ ret = inflate(&strm, Z_NO_FLUSH);
+ switch (ret)
+ {
+ case Z_NEED_DICT:
+ case Z_DATA_ERROR:
+ {
+ inflateEnd(&strm);
+ free(result);
+ return ZR_DATA_ERROR;
+ }
+ case Z_STREAM_ERROR:
+ case Z_BUF_ERROR:
+ {
+ inflateEnd(&strm);
+ free(result);
+ return ZR_BUFFER_ERROR;
+ }
+
+ case Z_MEM_ERROR:
+ {
+ inflateEnd(&strm);
+ free(result);
+ return ZR_MEM_ERROR;
+ }
+ }
+
+ U32 have = CHUNK-strm.avail_out;
+
+ U8* new_result = (U8*)realloc(result, cur_size + have);
+ if (new_result == NULL)
+ {
+ inflateEnd(&strm);
+ if (result)
+ {
+ free(result);
+ }
+ return ZR_MEM_ERROR;
+ }
+ result = new_result;
+ memcpy(result+cur_size, out.get(), have);
+ cur_size += have;
+
+ } while (ret == Z_OK && ret != Z_STREAM_END);
+
+ inflateEnd(&strm);
+
+ if (ret != Z_STREAM_END)
+ {
+ free(result);
+ return ZR_DATA_ERROR;
+ }
+
+ //result now points to the decompressed LLSD block
+ {
+ char* result_ptr = strip_deprecated_header((char*)result, cur_size);
+
+ boost::iostreams::stream<boost::iostreams::array_source> istrm(result_ptr, cur_size);
+
+ if (!LLSDSerialize::fromBinary(data, istrm, cur_size, UNZIP_LLSD_MAX_DEPTH))
+ {
+ free(result);
+ return ZR_PARSE_ERROR;
+ }
+ }
+
+ free(result);
+ return ZR_OK;
+}
+//This unzip function will only work with a gzip header and trailer - while the contents
+//of the actual compressed data is the same for either format (gzip vs zlib ), the headers
+//and trailers are different for the formats.
+U8* unzip_llsdNavMesh( bool& valid, size_t& outsize, std::istream& is, S32 size )
+{
+ if (size == 0)
+ {
+ LL_WARNS() << "No data to unzip." << LL_ENDL;
+ return NULL;
+ }
+
+ U8* result = NULL;
+ U32 cur_size = 0;
+ z_stream strm;
+
+ const U32 CHUNK = 0x4000;
+
+ U8 *in = new(std::nothrow) U8[size];
+ if (in == NULL)
+ {
+ LL_WARNS() << "Memory allocation failure." << LL_ENDL;
+ return NULL;
+ }
+ is.read((char*) in, size);
+
+ U8 out[CHUNK];
+
+ strm.zalloc = Z_NULL;
+ strm.zfree = Z_NULL;
+ strm.opaque = Z_NULL;
+ strm.avail_in = size;
+ strm.next_in = in;
+
+
+ S32 ret = inflateInit2(&strm, windowBits | ENABLE_ZLIB_GZIP );
+ do
+ {
+ strm.avail_out = CHUNK;
+ strm.next_out = out;
+ ret = inflate(&strm, Z_NO_FLUSH);
+ if (ret == Z_STREAM_ERROR)
+ {
+ inflateEnd(&strm);
+ free(result);
+ delete [] in;
+ valid = false;
+ }
+
+ switch (ret)
+ {
+ case Z_NEED_DICT:
+ ret = Z_DATA_ERROR;
+ case Z_DATA_ERROR:
+ case Z_MEM_ERROR:
+ inflateEnd(&strm);
+ free(result);
+ delete [] in;
+ valid = false;
+ break;
+ }
+
+ U32 have = CHUNK-strm.avail_out;
+
+ U8* new_result = (U8*) realloc(result, cur_size + have);
+ if (new_result == NULL)
+ {
+ LL_WARNS() << "Failed to unzip LLSD NavMesh block: can't reallocate memory, current size: " << cur_size
+ << " bytes; requested " << cur_size + have
+ << " bytes; total syze: ." << size << " bytes."
+ << LL_ENDL;
+ inflateEnd(&strm);
+ if (result)
+ {
+ free(result);
+ }
+ delete[] in;
+ valid = false;
+ return NULL;
+ }
+ result = new_result;
+ memcpy(result+cur_size, out, have);
+ cur_size += have;
+
+ } while (ret == Z_OK);
+
+ inflateEnd(&strm);
+ delete [] in;
+
+ if (ret != Z_STREAM_END)
+ {
+ free(result);
+ valid = false;
+ return NULL;
+ }
+
+ //result now points to the decompressed LLSD block
+ {
+ outsize= cur_size;
+ valid = true;
+ }
+
+ return result;
+}
+
+char* strip_deprecated_header(char* in, llssize& cur_size, llssize* header_size)
+{
+ const char* deprecated_header = "<? LLSD/Binary ?>";
+ constexpr size_t deprecated_header_size = 17;
+
+ if (cur_size > deprecated_header_size
+ && memcmp(in, deprecated_header, deprecated_header_size) == 0)
+ {
+ in = in + deprecated_header_size;
+ cur_size = cur_size - deprecated_header_size;
+ if (header_size)
+ {
+ *header_size = deprecated_header_size + 1;
+ }
+ }
+
+ return in;
+}
+
diff --git a/indra/llcommon/llsdserialize.h b/indra/llcommon/llsdserialize.h index 676b7bfd6a..12dd3c96ec 100644 --- a/indra/llcommon/llsdserialize.h +++ b/indra/llcommon/llsdserialize.h @@ -1,4 +1,4 @@ -/** +/** * @file llsdserialize.h * @author Phoenix * @date 2006-02-26 @@ -7,21 +7,21 @@ * $LicenseInfo:firstyear=2006&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -34,574 +34,574 @@ #include "llrefcount.h" #include "llsd.h" -/** +/** * @class LLSDParser * @brief Abstract base class for LLSD parsers. */ class LL_COMMON_API LLSDParser : public LLRefCount { protected: - /** - * @brief Destructor - */ - virtual ~LLSDParser(); + /** + * @brief Destructor + */ + virtual ~LLSDParser(); public: - /** - * @brief Anonymous enum to indicate parsing failure. - */ - enum - { - PARSE_FAILURE = -1 - }; - - /** - * @brief Constructor - */ - LLSDParser(); - - /** - * @brief Call this method to parse a stream for LLSD. - * - * This method parses the istream for a structured data. This - * method assumes that the istream is a complete llsd object -- - * for example an opened and closed map with an arbitrary nesting - * of elements. This method will return after reading one data - * object, allowing continued reading from the stream by the - * caller. - * @param istr The input stream. - * @param data[out] The newly parse structured data. - * @param max_bytes The maximum number of bytes that will be in - * the stream. Pass in LLSDSerialize::SIZE_UNLIMITED (-1) to set no - * byte limit. - * @return Returns the number of LLSD objects parsed into - * data. Returns PARSE_FAILURE (-1) on parse failure. - */ - S32 parse(std::istream& istr, LLSD& data, llssize max_bytes, S32 max_depth = -1); - - /** Like parse(), but uses a different call (istream.getline()) to read by lines - * This API is better suited for XML, where the parse cannot tell - * where the document actually ends. - */ - S32 parseLines(std::istream& istr, LLSD& data); - - /** - * @brief Resets the parser so parse() or parseLines() can be called again for another <llsd> chunk. - */ - void reset() { doReset(); }; + /** + * @brief Anonymous enum to indicate parsing failure. + */ + enum + { + PARSE_FAILURE = -1 + }; + + /** + * @brief Constructor + */ + LLSDParser(); + + /** + * @brief Call this method to parse a stream for LLSD. + * + * This method parses the istream for a structured data. This + * method assumes that the istream is a complete llsd object -- + * for example an opened and closed map with an arbitrary nesting + * of elements. This method will return after reading one data + * object, allowing continued reading from the stream by the + * caller. + * @param istr The input stream. + * @param data[out] The newly parse structured data. + * @param max_bytes The maximum number of bytes that will be in + * the stream. Pass in LLSDSerialize::SIZE_UNLIMITED (-1) to set no + * byte limit. + * @return Returns the number of LLSD objects parsed into + * data. Returns PARSE_FAILURE (-1) on parse failure. + */ + S32 parse(std::istream& istr, LLSD& data, llssize max_bytes, S32 max_depth = -1); + + /** Like parse(), but uses a different call (istream.getline()) to read by lines + * This API is better suited for XML, where the parse cannot tell + * where the document actually ends. + */ + S32 parseLines(std::istream& istr, LLSD& data); + + /** + * @brief Resets the parser so parse() or parseLines() can be called again for another <llsd> chunk. + */ + void reset() { doReset(); }; protected: - /** - * @brief Pure virtual base for doing the parse. - * - * This method parses the istream for a structured data. This - * method assumes that the istream is a complete llsd object -- - * for example an opened and closed map with an arbitrary nesting - * of elements. This method will return after reading one data - * object, allowing continued reading from the stream by the - * caller. - * @param istr The input stream. - * @param data[out] The newly parse structured data. - * @param max_depth Max depth parser will check before exiting - * with parse error, -1 - unlimited. - * @return Returns the number of LLSD objects parsed into - * data. Returns PARSE_FAILURE (-1) on parse failure. - */ - virtual S32 doParse(std::istream& istr, LLSD& data, S32 max_depth = -1) const = 0; - - /** - * @brief Virtual default function for resetting the parser - */ - virtual void doReset() {}; - - /* @name Simple istream helper methods - * - * These helper methods exist to help correctly use the - * mMaxBytesLeft without really thinking about it for most simple - * operations. Use of the streamtools in llstreamtools.h will - * require custom wrapping. - */ - //@{ - /** - * @brief get a byte off the stream - * - * @param istr The istream to work with. - * @return returns the next character. - */ - int get(std::istream& istr) const; - - /** - * @brief get several bytes off the stream into a buffer. - * - * @param istr The istream to work with. - * @param s The buffer to get into - * @param n Extract maximum of n-1 bytes and null temrinate. - * @param delim Delimiter to get until found. - * @return Returns istr. - */ - std::istream& get( - std::istream& istr, - char* s, - std::streamsize n, - char delim) const; - - /** - * @brief get several bytes off the stream into a streambuf - * - * @param istr The istream to work with. - * @param sb The streambuf to read into - * @param delim Delimiter to get until found. - * @return Returns istr. - */ - std::istream& get( - std::istream& istr, - std::streambuf& sb, - char delim) const; - - /** - * @brief ignore the next byte on the istream - * - * @param istr The istream to work with. - * @return Returns istr. - */ - std::istream& ignore(std::istream& istr) const; - - /** - * @brief put the last character retrieved back on the stream - * - * @param istr The istream to work with. - * @param c The character to put back - * @return Returns istr. - */ - std::istream& putback(std::istream& istr, char c) const; - - /** - * @brief read a block of n characters into a buffer - * - * @param istr The istream to work with. - * @param s The buffer to read into - * @param n The number of bytes to read. - * @return Returns istr. - */ - std::istream& read(std::istream& istr, char* s, std::streamsize n) const; - //@} + /** + * @brief Pure virtual base for doing the parse. + * + * This method parses the istream for a structured data. This + * method assumes that the istream is a complete llsd object -- + * for example an opened and closed map with an arbitrary nesting + * of elements. This method will return after reading one data + * object, allowing continued reading from the stream by the + * caller. + * @param istr The input stream. + * @param data[out] The newly parse structured data. + * @param max_depth Max depth parser will check before exiting + * with parse error, -1 - unlimited. + * @return Returns the number of LLSD objects parsed into + * data. Returns PARSE_FAILURE (-1) on parse failure. + */ + virtual S32 doParse(std::istream& istr, LLSD& data, S32 max_depth = -1) const = 0; + + /** + * @brief Virtual default function for resetting the parser + */ + virtual void doReset() {}; + + /* @name Simple istream helper methods + * + * These helper methods exist to help correctly use the + * mMaxBytesLeft without really thinking about it for most simple + * operations. Use of the streamtools in llstreamtools.h will + * require custom wrapping. + */ + //@{ + /** + * @brief get a byte off the stream + * + * @param istr The istream to work with. + * @return returns the next character. + */ + int get(std::istream& istr) const; + + /** + * @brief get several bytes off the stream into a buffer. + * + * @param istr The istream to work with. + * @param s The buffer to get into + * @param n Extract maximum of n-1 bytes and null temrinate. + * @param delim Delimiter to get until found. + * @return Returns istr. + */ + std::istream& get( + std::istream& istr, + char* s, + std::streamsize n, + char delim) const; + + /** + * @brief get several bytes off the stream into a streambuf + * + * @param istr The istream to work with. + * @param sb The streambuf to read into + * @param delim Delimiter to get until found. + * @return Returns istr. + */ + std::istream& get( + std::istream& istr, + std::streambuf& sb, + char delim) const; + + /** + * @brief ignore the next byte on the istream + * + * @param istr The istream to work with. + * @return Returns istr. + */ + std::istream& ignore(std::istream& istr) const; + + /** + * @brief put the last character retrieved back on the stream + * + * @param istr The istream to work with. + * @param c The character to put back + * @return Returns istr. + */ + std::istream& putback(std::istream& istr, char c) const; + + /** + * @brief read a block of n characters into a buffer + * + * @param istr The istream to work with. + * @param s The buffer to read into + * @param n The number of bytes to read. + * @return Returns istr. + */ + std::istream& read(std::istream& istr, char* s, std::streamsize n) const; + //@} protected: - /** - * @brief Accunt for bytes read outside of the istream helpers. - * - * Conceptually const since it only modifies mutable members. - * @param bytes The number of bytes read. - */ - void account(llssize bytes) const; + /** + * @brief Accunt for bytes read outside of the istream helpers. + * + * Conceptually const since it only modifies mutable members. + * @param bytes The number of bytes read. + */ + void account(llssize bytes) const; protected: - /** - * @brief boolean to set if byte counts should be checked during parsing. - */ - bool mCheckLimits; - - /** - * @brief The maximum number of bytes left to be parsed. - */ - mutable llssize mMaxBytesLeft; - - /** - * @brief Use line-based reading to get text - */ - bool mParseLines; + /** + * @brief boolean to set if byte counts should be checked during parsing. + */ + bool mCheckLimits; + + /** + * @brief The maximum number of bytes left to be parsed. + */ + mutable llssize mMaxBytesLeft; + + /** + * @brief Use line-based reading to get text + */ + bool mParseLines; }; -/** +/** * @class LLSDNotationParser * @brief Parser which handles the original notation format for LLSD. */ class LL_COMMON_API LLSDNotationParser : public LLSDParser { protected: - /** - * @brief Destructor - */ - virtual ~LLSDNotationParser(); + /** + * @brief Destructor + */ + virtual ~LLSDNotationParser(); public: - /** - * @brief Constructor - */ - LLSDNotationParser(); + /** + * @brief Constructor + */ + LLSDNotationParser(); protected: - /** - * @brief Call this method to parse a stream for LLSD. - * - * This method parses the istream for a structured data. This - * method assumes that the istream is a complete llsd object -- - * for example an opened and closed map with an arbitrary nesting - * of elements. This method will return after reading one data - * object, allowing continued reading from the stream by the - * caller. - * @param istr The input stream. - * @param data[out] The newly parse structured data. Undefined on failure. - * @param max_depth Max depth parser will check before exiting - * with parse error, -1 - unlimited. - * @return Returns the number of LLSD objects parsed into - * data. Returns PARSE_FAILURE (-1) on parse failure. - */ - virtual S32 doParse(std::istream& istr, LLSD& data, S32 max_depth = -1) const; + /** + * @brief Call this method to parse a stream for LLSD. + * + * This method parses the istream for a structured data. This + * method assumes that the istream is a complete llsd object -- + * for example an opened and closed map with an arbitrary nesting + * of elements. This method will return after reading one data + * object, allowing continued reading from the stream by the + * caller. + * @param istr The input stream. + * @param data[out] The newly parse structured data. Undefined on failure. + * @param max_depth Max depth parser will check before exiting + * with parse error, -1 - unlimited. + * @return Returns the number of LLSD objects parsed into + * data. Returns PARSE_FAILURE (-1) on parse failure. + */ + virtual S32 doParse(std::istream& istr, LLSD& data, S32 max_depth = -1) const; private: - /** - * @brief Parse a map from the istream - * - * @param istr The input stream. - * @param map The map to add the parsed data. - * @param max_depth Allowed parsing depth. - * @return Returns The number of LLSD objects parsed into data. - */ - S32 parseMap(std::istream& istr, LLSD& map, S32 max_depth) const; - - /** - * @brief Parse an array from the istream. - * - * @param istr The input stream. - * @param array The array to append the parsed data. - * @param max_depth Allowed parsing depth. - * @return Returns The number of LLSD objects parsed into data. - */ - S32 parseArray(std::istream& istr, LLSD& array, S32 max_depth) const; - - /** - * @brief Parse a string from the istream and assign it to data. - * - * @param istr The input stream. - * @param data[out] The data to assign. - * @return Retuns true if a complete string was parsed. - */ - bool parseString(std::istream& istr, LLSD& data) const; - - /** - * @brief Parse binary data from the stream. - * - * @param istr The input stream. - * @param data[out] The data to assign. - * @return Retuns true if a complete blob was parsed. - */ - bool parseBinary(std::istream& istr, LLSD& data) const; + /** + * @brief Parse a map from the istream + * + * @param istr The input stream. + * @param map The map to add the parsed data. + * @param max_depth Allowed parsing depth. + * @return Returns The number of LLSD objects parsed into data. + */ + S32 parseMap(std::istream& istr, LLSD& map, S32 max_depth) const; + + /** + * @brief Parse an array from the istream. + * + * @param istr The input stream. + * @param array The array to append the parsed data. + * @param max_depth Allowed parsing depth. + * @return Returns The number of LLSD objects parsed into data. + */ + S32 parseArray(std::istream& istr, LLSD& array, S32 max_depth) const; + + /** + * @brief Parse a string from the istream and assign it to data. + * + * @param istr The input stream. + * @param data[out] The data to assign. + * @return Retuns true if a complete string was parsed. + */ + bool parseString(std::istream& istr, LLSD& data) const; + + /** + * @brief Parse binary data from the stream. + * + * @param istr The input stream. + * @param data[out] The data to assign. + * @return Retuns true if a complete blob was parsed. + */ + bool parseBinary(std::istream& istr, LLSD& data) const; }; -/** +/** * @class LLSDXMLParser * @brief Parser which handles XML format LLSD. */ class LL_COMMON_API LLSDXMLParser : public LLSDParser { protected: - /** - * @brief Destructor - */ - virtual ~LLSDXMLParser(); + /** + * @brief Destructor + */ + virtual ~LLSDXMLParser(); public: - /** - * @brief Constructor - */ - LLSDXMLParser(bool emit_errors=true); + /** + * @brief Constructor + */ + LLSDXMLParser(bool emit_errors=true); protected: - /** - * @brief Call this method to parse a stream for LLSD. - * - * This method parses the istream for a structured data. This - * method assumes that the istream is a complete llsd object -- - * for example an opened and closed map with an arbitrary nesting - * of elements. This method will return after reading one data - * object, allowing continued reading from the stream by the - * caller. - * @param istr The input stream. - * @param data[out] The newly parse structured data. - * @param max_depth Max depth parser will check before exiting - * with parse error, -1 - unlimited. - * @return Returns the number of LLSD objects parsed into - * data. Returns PARSE_FAILURE (-1) on parse failure. - */ - virtual S32 doParse(std::istream& istr, LLSD& data, S32 max_depth = -1) const; - - /** - * @brief Virtual default function for resetting the parser - */ - virtual void doReset(); + /** + * @brief Call this method to parse a stream for LLSD. + * + * This method parses the istream for a structured data. This + * method assumes that the istream is a complete llsd object -- + * for example an opened and closed map with an arbitrary nesting + * of elements. This method will return after reading one data + * object, allowing continued reading from the stream by the + * caller. + * @param istr The input stream. + * @param data[out] The newly parse structured data. + * @param max_depth Max depth parser will check before exiting + * with parse error, -1 - unlimited. + * @return Returns the number of LLSD objects parsed into + * data. Returns PARSE_FAILURE (-1) on parse failure. + */ + virtual S32 doParse(std::istream& istr, LLSD& data, S32 max_depth = -1) const; + + /** + * @brief Virtual default function for resetting the parser + */ + virtual void doReset(); private: - class Impl; - Impl& impl; + class Impl; + Impl& impl; - void parsePart(const char* buf, llssize len); - friend class LLSDSerialize; + void parsePart(const char* buf, llssize len); + friend class LLSDSerialize; }; -/** +/** * @class LLSDBinaryParser * @brief Parser which handles binary formatted LLSD. */ class LL_COMMON_API LLSDBinaryParser : public LLSDParser { protected: - /** - * @brief Destructor - */ - virtual ~LLSDBinaryParser(); + /** + * @brief Destructor + */ + virtual ~LLSDBinaryParser(); public: - /** - * @brief Constructor - */ - LLSDBinaryParser(); + /** + * @brief Constructor + */ + LLSDBinaryParser(); protected: - /** - * @brief Call this method to parse a stream for LLSD. - * - * This method parses the istream for a structured data. This - * method assumes that the istream is a complete llsd object -- - * for example an opened and closed map with an arbitrary nesting - * of elements. This method will return after reading one data - * object, allowing continued reading from the stream by the - * caller. - * @param istr The input stream. - * @param data[out] The newly parse structured data. - * @param max_depth Max depth parser will check before exiting - * with parse error, -1 - unlimited. - * @return Returns the number of LLSD objects parsed into - * data. Returns -1 on parse failure. - */ - virtual S32 doParse(std::istream& istr, LLSD& data, S32 max_depth = -1) const; + /** + * @brief Call this method to parse a stream for LLSD. + * + * This method parses the istream for a structured data. This + * method assumes that the istream is a complete llsd object -- + * for example an opened and closed map with an arbitrary nesting + * of elements. This method will return after reading one data + * object, allowing continued reading from the stream by the + * caller. + * @param istr The input stream. + * @param data[out] The newly parse structured data. + * @param max_depth Max depth parser will check before exiting + * with parse error, -1 - unlimited. + * @return Returns the number of LLSD objects parsed into + * data. Returns -1 on parse failure. + */ + virtual S32 doParse(std::istream& istr, LLSD& data, S32 max_depth = -1) const; private: - /** - * @brief Parse a map from the istream - * - * @param istr The input stream. - * @param map The map to add the parsed data. - * @param max_depth Allowed parsing depth. - * @return Returns The number of LLSD objects parsed into data. - */ - S32 parseMap(std::istream& istr, LLSD& map, S32 max_depth) const; - - /** - * @brief Parse an array from the istream. - * - * @param istr The input stream. - * @param array The array to append the parsed data. - * @param max_depth Allowed parsing depth. - * @return Returns The number of LLSD objects parsed into data. - */ - S32 parseArray(std::istream& istr, LLSD& array, S32 max_depth) const; - - /** - * @brief Parse a string from the istream and assign it to data. - * - * @param istr The input stream. - * @param value[out] The string to assign. - * @return Retuns true if a complete string was parsed. - */ - bool parseString(std::istream& istr, std::string& value) const; + /** + * @brief Parse a map from the istream + * + * @param istr The input stream. + * @param map The map to add the parsed data. + * @param max_depth Allowed parsing depth. + * @return Returns The number of LLSD objects parsed into data. + */ + S32 parseMap(std::istream& istr, LLSD& map, S32 max_depth) const; + + /** + * @brief Parse an array from the istream. + * + * @param istr The input stream. + * @param array The array to append the parsed data. + * @param max_depth Allowed parsing depth. + * @return Returns The number of LLSD objects parsed into data. + */ + S32 parseArray(std::istream& istr, LLSD& array, S32 max_depth) const; + + /** + * @brief Parse a string from the istream and assign it to data. + * + * @param istr The input stream. + * @param value[out] The string to assign. + * @return Retuns true if a complete string was parsed. + */ + bool parseString(std::istream& istr, std::string& value) const; }; -/** +/** * @class LLSDFormatter * @brief Abstract base class for formatting LLSD. */ class LL_COMMON_API LLSDFormatter : public LLRefCount { protected: - /** - * @brief Destructor - */ - virtual ~LLSDFormatter(); + /** + * @brief Destructor + */ + virtual ~LLSDFormatter(); public: - /** - * Options for output - */ - typedef enum e_formatter_options_type - { - OPTIONS_NONE = 0, - OPTIONS_PRETTY = 1, - OPTIONS_PRETTY_BINARY = 2 - } EFormatterOptions; - - /** - * @brief Constructor - */ - LLSDFormatter(bool boolAlpha=false, const std::string& realFormat="", - EFormatterOptions options=OPTIONS_PRETTY_BINARY); - - /** - * @brief Set the boolean serialization format. - * - * @param alpha Serializes boolean as alpha if true. - */ - void boolalpha(bool alpha); - - /** - * @brief Set the real format - * - * By default, the formatter will use default double serialization - * which is frequently frustrating for many applications. You can - * set the precision on the stream independently, but that still - * might not work depending on the value. - * EXAMPLES:<br> - * %.2f<br> - * @param format A format string which follows the printf format - * rules. Specify an empty string to return to default formatting. - */ - void realFormat(const std::string& format); - - /** - * @brief Call this method to format an LLSD to a stream with options as - * set by the constructor. - * - * @param data The data to write. - * @param ostr The destination stream for the data. - * @return Returns The number of LLSD objects formatted out - */ - S32 format(const LLSD& data, std::ostream& ostr) const; - - /** - * @brief Call this method to format an LLSD to a stream, passing options - * explicitly. - * - * @param data The data to write. - * @param ostr The destination stream for the data. - * @param options OPTIONS_NONE to emit LLSD::Binary as raw bytes - * @return Returns The number of LLSD objects formatted out - */ - virtual S32 format(const LLSD& data, std::ostream& ostr, EFormatterOptions options) const; + /** + * Options for output + */ + typedef enum e_formatter_options_type + { + OPTIONS_NONE = 0, + OPTIONS_PRETTY = 1, + OPTIONS_PRETTY_BINARY = 2 + } EFormatterOptions; + + /** + * @brief Constructor + */ + LLSDFormatter(bool boolAlpha=false, const std::string& realFormat="", + EFormatterOptions options=OPTIONS_PRETTY_BINARY); + + /** + * @brief Set the boolean serialization format. + * + * @param alpha Serializes boolean as alpha if true. + */ + void boolalpha(bool alpha); + + /** + * @brief Set the real format + * + * By default, the formatter will use default double serialization + * which is frequently frustrating for many applications. You can + * set the precision on the stream independently, but that still + * might not work depending on the value. + * EXAMPLES:<br> + * %.2f<br> + * @param format A format string which follows the printf format + * rules. Specify an empty string to return to default formatting. + */ + void realFormat(const std::string& format); + + /** + * @brief Call this method to format an LLSD to a stream with options as + * set by the constructor. + * + * @param data The data to write. + * @param ostr The destination stream for the data. + * @return Returns The number of LLSD objects formatted out + */ + S32 format(const LLSD& data, std::ostream& ostr) const; + + /** + * @brief Call this method to format an LLSD to a stream, passing options + * explicitly. + * + * @param data The data to write. + * @param ostr The destination stream for the data. + * @param options OPTIONS_NONE to emit LLSD::Binary as raw bytes + * @return Returns The number of LLSD objects formatted out + */ + virtual S32 format(const LLSD& data, std::ostream& ostr, EFormatterOptions options) const; protected: - /** - * @brief Implementation to format the data. This is called recursively. - * - * @param data The data to write. - * @param ostr The destination stream for the data. - * @return Returns The number of LLSD objects formatted out - */ - virtual S32 format_impl(const LLSD& data, std::ostream& ostr, EFormatterOptions options, - U32 level) const = 0; - - /** - * @brief Helper method which appropriately obeys the real format. - * - * @param real The real value to format. - * @param ostr The destination stream for the data. - */ - void formatReal(LLSD::Real real, std::ostream& ostr) const; - - bool mBoolAlpha; - std::string mRealFormat; - EFormatterOptions mOptions; + /** + * @brief Implementation to format the data. This is called recursively. + * + * @param data The data to write. + * @param ostr The destination stream for the data. + * @return Returns The number of LLSD objects formatted out + */ + virtual S32 format_impl(const LLSD& data, std::ostream& ostr, EFormatterOptions options, + U32 level) const = 0; + + /** + * @brief Helper method which appropriately obeys the real format. + * + * @param real The real value to format. + * @param ostr The destination stream for the data. + */ + void formatReal(LLSD::Real real, std::ostream& ostr) const; + + bool mBoolAlpha; + std::string mRealFormat; + EFormatterOptions mOptions; }; -/** +/** * @class LLSDNotationFormatter * @brief Formatter which outputs the original notation format for LLSD. */ class LL_COMMON_API LLSDNotationFormatter : public LLSDFormatter { protected: - /** - * @brief Destructor - */ - virtual ~LLSDNotationFormatter(); + /** + * @brief Destructor + */ + virtual ~LLSDNotationFormatter(); public: - /** - * @brief Constructor - */ - LLSDNotationFormatter(bool boolAlpha=false, const std::string& realFormat="", - EFormatterOptions options=OPTIONS_PRETTY_BINARY); - - /** - * @brief Helper static method to return a notation escaped string - * - * This method will return the notation escaped string, but not - * the surrounding serialization identifiers such as a double or - * single quote. It will be up to the caller to embed those as - * appropriate. - * @param in The raw, unescaped string. - * @return Returns an escaped string appropriate for serialization. - */ - static std::string escapeString(const std::string& in); + /** + * @brief Constructor + */ + LLSDNotationFormatter(bool boolAlpha=false, const std::string& realFormat="", + EFormatterOptions options=OPTIONS_PRETTY_BINARY); + + /** + * @brief Helper static method to return a notation escaped string + * + * This method will return the notation escaped string, but not + * the surrounding serialization identifiers such as a double or + * single quote. It will be up to the caller to embed those as + * appropriate. + * @param in The raw, unescaped string. + * @return Returns an escaped string appropriate for serialization. + */ + static std::string escapeString(const std::string& in); protected: - /** - * @brief Implementation to format the data. This is called recursively. - * - * @param data The data to write. - * @param ostr The destination stream for the data. - * @return Returns The number of LLSD objects formatted out - */ - S32 format_impl(const LLSD& data, std::ostream& ostr, EFormatterOptions options, - U32 level) const override; + /** + * @brief Implementation to format the data. This is called recursively. + * + * @param data The data to write. + * @param ostr The destination stream for the data. + * @return Returns The number of LLSD objects formatted out + */ + S32 format_impl(const LLSD& data, std::ostream& ostr, EFormatterOptions options, + U32 level) const override; }; -/** +/** * @class LLSDXMLFormatter * @brief Formatter which outputs the LLSD as XML. */ class LL_COMMON_API LLSDXMLFormatter : public LLSDFormatter { protected: - /** - * @brief Destructor - */ - virtual ~LLSDXMLFormatter(); + /** + * @brief Destructor + */ + virtual ~LLSDXMLFormatter(); public: - /** - * @brief Constructor - */ - LLSDXMLFormatter(bool boolAlpha=false, const std::string& realFormat="", - EFormatterOptions options=OPTIONS_PRETTY_BINARY); - - /** - * @brief Helper static method to return an xml escaped string - * - * @param in A valid UTF-8 string. - * @return Returns an escaped string appropriate for serialization. - */ - static std::string escapeString(const std::string& in); - - /** - * @brief Call this method to format an LLSD to a stream. - * - * @param data The data to write. - * @param ostr The destination stream for the data. - * @return Returns The number of LLSD objects formatted out - */ - S32 format(const LLSD& data, std::ostream& ostr, EFormatterOptions options) const override; - - // also pull down base-class format() method that isn't overridden - using LLSDFormatter::format; + /** + * @brief Constructor + */ + LLSDXMLFormatter(bool boolAlpha=false, const std::string& realFormat="", + EFormatterOptions options=OPTIONS_PRETTY_BINARY); + + /** + * @brief Helper static method to return an xml escaped string + * + * @param in A valid UTF-8 string. + * @return Returns an escaped string appropriate for serialization. + */ + static std::string escapeString(const std::string& in); + + /** + * @brief Call this method to format an LLSD to a stream. + * + * @param data The data to write. + * @param ostr The destination stream for the data. + * @return Returns The number of LLSD objects formatted out + */ + S32 format(const LLSD& data, std::ostream& ostr, EFormatterOptions options) const override; + + // also pull down base-class format() method that isn't overridden + using LLSDFormatter::format; protected: - /** - * @brief Implementation to format the data. This is called recursively. - * - * @param data The data to write. - * @param ostr The destination stream for the data. - * @return Returns The number of LLSD objects formatted out - */ - S32 format_impl(const LLSD& data, std::ostream& ostr, EFormatterOptions options, - U32 level) const override; + /** + * @brief Implementation to format the data. This is called recursively. + * + * @param data The data to write. + * @param ostr The destination stream for the data. + * @return Returns The number of LLSD objects formatted out + */ + S32 format_impl(const LLSD& data, std::ostream& ostr, EFormatterOptions options, + U32 level) const override; }; -/** +/** * @class LLSDBinaryFormatter * @brief Formatter which outputs the LLSD as a binary notation format. * @@ -628,42 +628,42 @@ protected: class LL_COMMON_API LLSDBinaryFormatter : public LLSDFormatter { protected: - /** - * @brief Destructor - */ - virtual ~LLSDBinaryFormatter(); + /** + * @brief Destructor + */ + virtual ~LLSDBinaryFormatter(); public: - /** - * @brief Constructor - */ - LLSDBinaryFormatter(bool boolAlpha=false, const std::string& realFormat="", - EFormatterOptions options=OPTIONS_PRETTY_BINARY); + /** + * @brief Constructor + */ + LLSDBinaryFormatter(bool boolAlpha=false, const std::string& realFormat="", + EFormatterOptions options=OPTIONS_PRETTY_BINARY); protected: - /** - * @brief Implementation to format the data. This is called recursively. - * - * @param data The data to write. - * @param ostr The destination stream for the data. - * @return Returns The number of LLSD objects formatted out - */ - S32 format_impl(const LLSD& data, std::ostream& ostr, EFormatterOptions options, - U32 level) const override; - - /** - * @brief Helper method to serialize strings - * - * This method serializes a network byte order size and the raw - * string contents. - * @param string The string to write. - * @param ostr The destination stream for the data. - */ - void formatString(const std::string& string, std::ostream& ostr) const; + /** + * @brief Implementation to format the data. This is called recursively. + * + * @param data The data to write. + * @param ostr The destination stream for the data. + * @return Returns The number of LLSD objects formatted out + */ + S32 format_impl(const LLSD& data, std::ostream& ostr, EFormatterOptions options, + U32 level) const override; + + /** + * @brief Helper method to serialize strings + * + * This method serializes a network byte order size and the raw + * string contents. + * @param string The string to write. + * @param ostr The destination stream for the data. + */ + void formatString(const std::string& string, std::ostream& ostr) const; }; -/** +/** * @class LLSDNotationStreamFormatter * @brief Formatter which is specialized for use on streams which * outputs the original notation format for LLSD. @@ -674,7 +674,7 @@ protected: * LLSD sd;<br> * sd["foo"] = "bar";<br> * std::stringstream params;<br> - * params << "[{'version':i1}," << LLSDOStreamer<LLSDNotationFormatter>(sd) + * params << "[{'version':i1}," << LLSDOStreamer<LLSDNotationFormatter>(sd) * << "]"; * </code> * @@ -687,165 +687,165 @@ template <class Formatter> class LLSDOStreamer { public: - /** - * @brief Constructor - */ - LLSDOStreamer(const LLSD& data, - LLSDFormatter::EFormatterOptions options=LLSDFormatter::OPTIONS_PRETTY_BINARY) : - mSD(data), mOptions(options) {} - - /** - * @brief Stream operator. - * - * Use this inline during construction during a stream operation. - * @param str The destination stream for serialized output. - * @param The formatter which will output it's LLSD. - * @return Returns the stream passed in after streaming mSD. - */ - friend std::ostream& operator<<( - std::ostream& out, - const LLSDOStreamer<Formatter>& streamer) - { - LLPointer<Formatter> f = new Formatter; - f->format(streamer.mSD, out, streamer.mOptions); - return out; - } + /** + * @brief Constructor + */ + LLSDOStreamer(const LLSD& data, + LLSDFormatter::EFormatterOptions options=LLSDFormatter::OPTIONS_PRETTY_BINARY) : + mSD(data), mOptions(options) {} + + /** + * @brief Stream operator. + * + * Use this inline during construction during a stream operation. + * @param str The destination stream for serialized output. + * @param The formatter which will output it's LLSD. + * @return Returns the stream passed in after streaming mSD. + */ + friend std::ostream& operator<<( + std::ostream& out, + const LLSDOStreamer<Formatter>& streamer) + { + LLPointer<Formatter> f = new Formatter; + f->format(streamer.mSD, out, streamer.mOptions); + return out; + } protected: - LLSD mSD; - LLSDFormatter::EFormatterOptions mOptions; + LLSD mSD; + LLSDFormatter::EFormatterOptions mOptions; }; -typedef LLSDOStreamer<LLSDNotationFormatter> LLSDNotationStreamer; -typedef LLSDOStreamer<LLSDXMLFormatter> LLSDXMLStreamer; +typedef LLSDOStreamer<LLSDNotationFormatter> LLSDNotationStreamer; +typedef LLSDOStreamer<LLSDXMLFormatter> LLSDXMLStreamer; -/** +/** * @class LLSDSerialize * @brief Serializer / deserializer for the various LLSD formats */ class LL_COMMON_API LLSDSerialize { public: - enum ELLSD_Serialize - { + enum ELLSD_Serialize + { LLSD_BINARY, LLSD_XML, LLSD_NOTATION - }; - - /** - * @brief anonymouse enumeration for useful max_bytes constants. - */ - enum - { - // Setting an unlimited size is discouraged and should only be - // used when reading cin or another stream source which does - // not provide access to size. - SIZE_UNLIMITED = -1, - }; - - /* - * Generic in/outs - */ - static void serialize(const LLSD& sd, std::ostream& str, ELLSD_Serialize, - LLSDFormatter::EFormatterOptions options=LLSDFormatter::OPTIONS_PRETTY_BINARY); - - /** - * @brief Examine a stream, and parse 1 sd object out based on contents. - * - * @param sd [out] The data found on the stream - * @param str The incoming stream - * @param max_bytes the maximum number of bytes to parse - * @return Returns true if the stream appears to contain valid data - */ - static bool deserialize(LLSD& sd, std::istream& str, llssize max_bytes); - - /* - * Notation Methods - */ - static S32 toNotation(const LLSD& sd, std::ostream& str) - { - LLPointer<LLSDNotationFormatter> f = new LLSDNotationFormatter; - return f->format(sd, str, LLSDFormatter::OPTIONS_NONE); - } - static S32 toPrettyNotation(const LLSD& sd, std::ostream& str) - { - LLPointer<LLSDNotationFormatter> f = new LLSDNotationFormatter; - return f->format(sd, str, LLSDFormatter::OPTIONS_PRETTY); - } - static S32 toPrettyBinaryNotation(const LLSD& sd, std::ostream& str) - { - LLPointer<LLSDNotationFormatter> f = new LLSDNotationFormatter; - return f->format(sd, str, - LLSDFormatter::EFormatterOptions(LLSDFormatter::OPTIONS_PRETTY | - LLSDFormatter::OPTIONS_PRETTY_BINARY)); - } - static S32 fromNotation(LLSD& sd, std::istream& str, llssize max_bytes) - { - LLPointer<LLSDNotationParser> p = new LLSDNotationParser; - return p->parse(str, sd, max_bytes); - } - static LLSD fromNotation(std::istream& str, llssize max_bytes) - { - LLPointer<LLSDNotationParser> p = new LLSDNotationParser; - LLSD sd; - (void)p->parse(str, sd, max_bytes); - return sd; - } - - /* - * XML Methods - */ - static S32 toXML(const LLSD& sd, std::ostream& str) - { - LLPointer<LLSDXMLFormatter> f = new LLSDXMLFormatter; - return f->format(sd, str, LLSDFormatter::OPTIONS_NONE); - } - static S32 toPrettyXML(const LLSD& sd, std::ostream& str) - { - LLPointer<LLSDXMLFormatter> f = new LLSDXMLFormatter; - return f->format(sd, str, LLSDFormatter::OPTIONS_PRETTY); - } - - static S32 fromXMLEmbedded(LLSD& sd, std::istream& str, bool emit_errors=true) - { - // no need for max_bytes since xml formatting is not - // subvertable by bad sizes. - LLPointer<LLSDXMLParser> p = new LLSDXMLParser(emit_errors); - return p->parse(str, sd, LLSDSerialize::SIZE_UNLIMITED); - } - // Line oriented parser, 30% faster than fromXML(), but can - // only be used when you know you have the complete XML - // document available in the stream. - static S32 fromXMLDocument(LLSD& sd, std::istream& str, bool emit_errors=true) - { - LLPointer<LLSDXMLParser> p = new LLSDXMLParser(emit_errors); - return p->parseLines(str, sd); - } - static S32 fromXML(LLSD& sd, std::istream& str, bool emit_errors=true) - { - return fromXMLEmbedded(sd, str, emit_errors); -// return fromXMLDocument(sd, str, emit_errors); - } - - /* - * Binary Methods - */ - static S32 toBinary(const LLSD& sd, std::ostream& str) - { - LLPointer<LLSDBinaryFormatter> f = new LLSDBinaryFormatter; - return f->format(sd, str, LLSDFormatter::OPTIONS_NONE); - } - static S32 fromBinary(LLSD& sd, std::istream& str, llssize max_bytes, S32 max_depth = -1) - { - LLPointer<LLSDBinaryParser> p = new LLSDBinaryParser; - return p->parse(str, sd, max_bytes, max_depth); - } - static LLSD fromBinary(std::istream& str, llssize max_bytes, S32 max_depth = -1) - { - LLPointer<LLSDBinaryParser> p = new LLSDBinaryParser; - LLSD sd; - (void)p->parse(str, sd, max_bytes, max_depth); - return sd; - } + }; + + /** + * @brief anonymouse enumeration for useful max_bytes constants. + */ + enum + { + // Setting an unlimited size is discouraged and should only be + // used when reading cin or another stream source which does + // not provide access to size. + SIZE_UNLIMITED = -1, + }; + + /* + * Generic in/outs + */ + static void serialize(const LLSD& sd, std::ostream& str, ELLSD_Serialize, + LLSDFormatter::EFormatterOptions options=LLSDFormatter::OPTIONS_PRETTY_BINARY); + + /** + * @brief Examine a stream, and parse 1 sd object out based on contents. + * + * @param sd [out] The data found on the stream + * @param str The incoming stream + * @param max_bytes the maximum number of bytes to parse + * @return Returns true if the stream appears to contain valid data + */ + static bool deserialize(LLSD& sd, std::istream& str, llssize max_bytes); + + /* + * Notation Methods + */ + static S32 toNotation(const LLSD& sd, std::ostream& str) + { + LLPointer<LLSDNotationFormatter> f = new LLSDNotationFormatter; + return f->format(sd, str, LLSDFormatter::OPTIONS_NONE); + } + static S32 toPrettyNotation(const LLSD& sd, std::ostream& str) + { + LLPointer<LLSDNotationFormatter> f = new LLSDNotationFormatter; + return f->format(sd, str, LLSDFormatter::OPTIONS_PRETTY); + } + static S32 toPrettyBinaryNotation(const LLSD& sd, std::ostream& str) + { + LLPointer<LLSDNotationFormatter> f = new LLSDNotationFormatter; + return f->format(sd, str, + LLSDFormatter::EFormatterOptions(LLSDFormatter::OPTIONS_PRETTY | + LLSDFormatter::OPTIONS_PRETTY_BINARY)); + } + static S32 fromNotation(LLSD& sd, std::istream& str, llssize max_bytes) + { + LLPointer<LLSDNotationParser> p = new LLSDNotationParser; + return p->parse(str, sd, max_bytes); + } + static LLSD fromNotation(std::istream& str, llssize max_bytes) + { + LLPointer<LLSDNotationParser> p = new LLSDNotationParser; + LLSD sd; + (void)p->parse(str, sd, max_bytes); + return sd; + } + + /* + * XML Methods + */ + static S32 toXML(const LLSD& sd, std::ostream& str) + { + LLPointer<LLSDXMLFormatter> f = new LLSDXMLFormatter; + return f->format(sd, str, LLSDFormatter::OPTIONS_NONE); + } + static S32 toPrettyXML(const LLSD& sd, std::ostream& str) + { + LLPointer<LLSDXMLFormatter> f = new LLSDXMLFormatter; + return f->format(sd, str, LLSDFormatter::OPTIONS_PRETTY); + } + + static S32 fromXMLEmbedded(LLSD& sd, std::istream& str, bool emit_errors=true) + { + // no need for max_bytes since xml formatting is not + // subvertable by bad sizes. + LLPointer<LLSDXMLParser> p = new LLSDXMLParser(emit_errors); + return p->parse(str, sd, LLSDSerialize::SIZE_UNLIMITED); + } + // Line oriented parser, 30% faster than fromXML(), but can + // only be used when you know you have the complete XML + // document available in the stream. + static S32 fromXMLDocument(LLSD& sd, std::istream& str, bool emit_errors=true) + { + LLPointer<LLSDXMLParser> p = new LLSDXMLParser(emit_errors); + return p->parseLines(str, sd); + } + static S32 fromXML(LLSD& sd, std::istream& str, bool emit_errors=true) + { + return fromXMLEmbedded(sd, str, emit_errors); +// return fromXMLDocument(sd, str, emit_errors); + } + + /* + * Binary Methods + */ + static S32 toBinary(const LLSD& sd, std::ostream& str) + { + LLPointer<LLSDBinaryFormatter> f = new LLSDBinaryFormatter; + return f->format(sd, str, LLSDFormatter::OPTIONS_NONE); + } + static S32 fromBinary(LLSD& sd, std::istream& str, llssize max_bytes, S32 max_depth = -1) + { + LLPointer<LLSDBinaryParser> p = new LLSDBinaryParser; + return p->parse(str, sd, max_bytes, max_depth); + } + static LLSD fromBinary(std::istream& str, llssize max_bytes, S32 max_depth = -1) + { + LLPointer<LLSDBinaryParser> p = new LLSDBinaryParser; + LLSD sd; + (void)p->parse(str, sd, max_bytes, max_depth); + return sd; + } }; class LL_COMMON_API LLUZipHelper : public LLRefCount @@ -858,12 +858,12 @@ public: ZR_SIZE_ERROR, ZR_DATA_ERROR, ZR_PARSE_ERROR, - ZR_BUFFER_ERROR, - ZR_VERSION_ERROR + ZR_BUFFER_ERROR, + ZR_VERSION_ERROR } EZipRresult; // return OK or reason for failure static EZipRresult unzip_llsd(LLSD& data, std::istream& is, S32 size); - static EZipRresult unzip_llsd(LLSD& data, const U8* in, S32 size); + static EZipRresult unzip_llsd(LLSD& data, const U8* in, S32 size); }; //dirty little zip functions -- yell at davep diff --git a/indra/llcommon/llsdserialize_xml.cpp b/indra/llcommon/llsdserialize_xml.cpp index db61f4ae41..88cbb3b984 100644 --- a/indra/llcommon/llsdserialize_xml.cpp +++ b/indra/llcommon/llsdserialize_xml.cpp @@ -1,25 +1,25 @@ -/** +/** * @file llsdserialize_xml.cpp * @brief XML parsers and formatters for LLSD * * $LicenseInfo:firstyear=2006&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -58,195 +58,195 @@ LLSDXMLFormatter::~LLSDXMLFormatter() // virtual S32 LLSDXMLFormatter::format(const LLSD& data, std::ostream& ostr, - EFormatterOptions options) const + EFormatterOptions options) const { - std::streamsize old_precision = ostr.precision(25); - - std::string post; - if (options & LLSDFormatter::OPTIONS_PRETTY) - { - post = "\n"; - } - ostr << "<llsd>" << post; - S32 rv = format_impl(data, ostr, options, 1); - ostr << "</llsd>\n"; - - ostr.precision(old_precision); - return rv; + std::streamsize old_precision = ostr.precision(25); + + std::string post; + if (options & LLSDFormatter::OPTIONS_PRETTY) + { + post = "\n"; + } + ostr << "<llsd>" << post; + S32 rv = format_impl(data, ostr, options, 1); + ostr << "</llsd>\n"; + + ostr.precision(old_precision); + return rv; } S32 LLSDXMLFormatter::format_impl(const LLSD& data, std::ostream& ostr, - EFormatterOptions options, U32 level) const + EFormatterOptions options, U32 level) const { - S32 format_count = 1; - std::string pre; - std::string post; - - if (options & LLSDFormatter::OPTIONS_PRETTY) - { - for (U32 i = 0; i < level; i++) - { - pre += " "; - } - post = "\n"; - } - - switch(data.type()) - { - case LLSD::TypeMap: - if(0 == data.size()) - { - ostr << pre << "<map />" << post; - } - else - { - ostr << pre << "<map>" << post; - LLSD::map_const_iterator iter = data.beginMap(); - LLSD::map_const_iterator end = data.endMap(); - for(; iter != end; ++iter) - { - ostr << pre << "<key>" << escapeString((*iter).first) << "</key>" << post; - format_count += format_impl((*iter).second, ostr, options, level + 1); - } - ostr << pre << "</map>" << post; - } - break; - - case LLSD::TypeArray: - if(0 == data.size()) - { - ostr << pre << "<array />" << post; - } - else - { - ostr << pre << "<array>" << post; - LLSD::array_const_iterator iter = data.beginArray(); - LLSD::array_const_iterator end = data.endArray(); - for(; iter != end; ++iter) - { - format_count += format_impl(*iter, ostr, options, level + 1); - } - ostr << pre << "</array>" << post; - } - break; - - case LLSD::TypeUndefined: - ostr << pre << "<undef />" << post; - break; - - case LLSD::TypeBoolean: - ostr << pre << "<boolean>"; - if(mBoolAlpha || - (ostr.flags() & std::ios::boolalpha) - ) - { - ostr << (data.asBoolean() ? "true" : "false"); - } - else - { - ostr << (data.asBoolean() ? 1 : 0); - } - ostr << "</boolean>" << post; - break; - - case LLSD::TypeInteger: - ostr << pre << "<integer>" << data.asInteger() << "</integer>" << post; - break; - - case LLSD::TypeReal: - ostr << pre << "<real>"; - if(mRealFormat.empty()) - { - ostr << data.asReal(); - } - else - { - formatReal(data.asReal(), ostr); - } - ostr << "</real>" << post; - break; - - case LLSD::TypeUUID: - if(data.asUUID().isNull()) ostr << pre << "<uuid />" << post; - else ostr << pre << "<uuid>" << data.asUUID() << "</uuid>" << post; - break; - - case LLSD::TypeString: - if(data.asStringRef().empty()) ostr << pre << "<string />" << post; - else ostr << pre << "<string>" << escapeString(data.asStringRef()) <<"</string>" << post; - break; - - case LLSD::TypeDate: - ostr << pre << "<date>" << data.asDate() << "</date>" << post; - break; - - case LLSD::TypeURI: - ostr << pre << "<uri>" << escapeString(data.asString()) << "</uri>" << post; - break; - - case LLSD::TypeBinary: - { - const LLSD::Binary& buffer = data.asBinary(); - if(buffer.empty()) - { - ostr << pre << "<binary />" << post; - } - else - { - // *FIX: memory inefficient. - // *TODO: convert to use LLBase64 - ostr << pre << "<binary encoding=\"base64\">"; - int b64_buffer_length = apr_base64_encode_len(narrow<size_t>(buffer.size())); - char* b64_buffer = new char[b64_buffer_length]; - b64_buffer_length = apr_base64_encode_binary( - b64_buffer, - &buffer[0], - narrow<size_t>(buffer.size())); - ostr.write(b64_buffer, b64_buffer_length - 1); - delete[] b64_buffer; - ostr << "</binary>" << post; - } - break; - } - default: - // *NOTE: This should never happen. - ostr << pre << "<undef />" << post; - break; - } - return format_count; + S32 format_count = 1; + std::string pre; + std::string post; + + if (options & LLSDFormatter::OPTIONS_PRETTY) + { + for (U32 i = 0; i < level; i++) + { + pre += " "; + } + post = "\n"; + } + + switch(data.type()) + { + case LLSD::TypeMap: + if(0 == data.size()) + { + ostr << pre << "<map />" << post; + } + else + { + ostr << pre << "<map>" << post; + LLSD::map_const_iterator iter = data.beginMap(); + LLSD::map_const_iterator end = data.endMap(); + for(; iter != end; ++iter) + { + ostr << pre << "<key>" << escapeString((*iter).first) << "</key>" << post; + format_count += format_impl((*iter).second, ostr, options, level + 1); + } + ostr << pre << "</map>" << post; + } + break; + + case LLSD::TypeArray: + if(0 == data.size()) + { + ostr << pre << "<array />" << post; + } + else + { + ostr << pre << "<array>" << post; + LLSD::array_const_iterator iter = data.beginArray(); + LLSD::array_const_iterator end = data.endArray(); + for(; iter != end; ++iter) + { + format_count += format_impl(*iter, ostr, options, level + 1); + } + ostr << pre << "</array>" << post; + } + break; + + case LLSD::TypeUndefined: + ostr << pre << "<undef />" << post; + break; + + case LLSD::TypeBoolean: + ostr << pre << "<boolean>"; + if(mBoolAlpha || + (ostr.flags() & std::ios::boolalpha) + ) + { + ostr << (data.asBoolean() ? "true" : "false"); + } + else + { + ostr << (data.asBoolean() ? 1 : 0); + } + ostr << "</boolean>" << post; + break; + + case LLSD::TypeInteger: + ostr << pre << "<integer>" << data.asInteger() << "</integer>" << post; + break; + + case LLSD::TypeReal: + ostr << pre << "<real>"; + if(mRealFormat.empty()) + { + ostr << data.asReal(); + } + else + { + formatReal(data.asReal(), ostr); + } + ostr << "</real>" << post; + break; + + case LLSD::TypeUUID: + if(data.asUUID().isNull()) ostr << pre << "<uuid />" << post; + else ostr << pre << "<uuid>" << data.asUUID() << "</uuid>" << post; + break; + + case LLSD::TypeString: + if(data.asStringRef().empty()) ostr << pre << "<string />" << post; + else ostr << pre << "<string>" << escapeString(data.asStringRef()) <<"</string>" << post; + break; + + case LLSD::TypeDate: + ostr << pre << "<date>" << data.asDate() << "</date>" << post; + break; + + case LLSD::TypeURI: + ostr << pre << "<uri>" << escapeString(data.asString()) << "</uri>" << post; + break; + + case LLSD::TypeBinary: + { + const LLSD::Binary& buffer = data.asBinary(); + if(buffer.empty()) + { + ostr << pre << "<binary />" << post; + } + else + { + // *FIX: memory inefficient. + // *TODO: convert to use LLBase64 + ostr << pre << "<binary encoding=\"base64\">"; + int b64_buffer_length = apr_base64_encode_len(narrow<size_t>(buffer.size())); + char* b64_buffer = new char[b64_buffer_length]; + b64_buffer_length = apr_base64_encode_binary( + b64_buffer, + &buffer[0], + narrow<size_t>(buffer.size())); + ostr.write(b64_buffer, b64_buffer_length - 1); + delete[] b64_buffer; + ostr << "</binary>" << post; + } + break; + } + default: + // *NOTE: This should never happen. + ostr << pre << "<undef />" << post; + break; + } + return format_count; } // static std::string LLSDXMLFormatter::escapeString(const std::string& in) { - std::ostringstream out; - std::string::const_iterator it = in.begin(); - std::string::const_iterator end = in.end(); - for(; it != end; ++it) - { - switch((*it)) - { - case '<': - out << "<"; - break; - case '>': - out << ">"; - break; - case '&': - out << "&"; - break; - case '\'': - out << "'"; - break; - case '"': - out << """; - break; - default: - out << (*it); - break; - } - } - return out.str(); + std::ostringstream out; + std::string::const_iterator it = in.begin(); + std::string::const_iterator end = in.end(); + for(; it != end; ++it) + { + switch((*it)) + { + case '<': + out << "<"; + break; + case '>': + out << ">"; + break; + case '&': + out << "&"; + break; + case '\'': + out << "'"; + break; + case '"': + out << """; + break; + default: + out << (*it); + break; + } + } + return out.str(); } @@ -254,161 +254,161 @@ std::string LLSDXMLFormatter::escapeString(const std::string& in) class LLSDXMLParser::Impl { public: - Impl(bool emit_errors); - ~Impl(); - - S32 parse(std::istream& input, LLSD& data); - S32 parseLines(std::istream& input, LLSD& data); + Impl(bool emit_errors); + ~Impl(); + + S32 parse(std::istream& input, LLSD& data); + S32 parseLines(std::istream& input, LLSD& data); - void parsePart(const char *buf, llssize len); - - void reset(); + void parsePart(const char *buf, llssize len); + + void reset(); private: - void startElementHandler(const XML_Char* name, const XML_Char** attributes); - void endElementHandler(const XML_Char* name); - void characterDataHandler(const XML_Char* data, int length); - - static void sStartElementHandler( - void* userData, const XML_Char* name, const XML_Char** attributes); - static void sEndElementHandler( - void* userData, const XML_Char* name); - static void sCharacterDataHandler( - void* userData, const XML_Char* data, int length); - - void startSkipping(); - - enum Element { - ELEMENT_LLSD, - ELEMENT_UNDEF, - ELEMENT_BOOL, - ELEMENT_INTEGER, - ELEMENT_REAL, - ELEMENT_STRING, - ELEMENT_UUID, - ELEMENT_DATE, - ELEMENT_URI, - ELEMENT_BINARY, - ELEMENT_MAP, - ELEMENT_ARRAY, - ELEMENT_KEY, - ELEMENT_UNKNOWN - }; - static Element readElement(const XML_Char* name); - - static const XML_Char* findAttribute(const XML_Char* name, const XML_Char** pairs); - - bool mEmitErrors; - - XML_Parser mParser; - - LLSD mResult; - S32 mParseCount; - - bool mInLLSDElement; // true if we're on LLSD - bool mGracefullStop; // true if we found the </llsd - - typedef std::deque<LLSD*> LLSDRefStack; - LLSDRefStack mStack; - - int mDepth; - bool mSkipping; - int mSkipThrough; - - std::string mCurrentKey; // Current XML <tag> - std::string mCurrentContent; // String data between <tag> and </tag> + void startElementHandler(const XML_Char* name, const XML_Char** attributes); + void endElementHandler(const XML_Char* name); + void characterDataHandler(const XML_Char* data, int length); + + static void sStartElementHandler( + void* userData, const XML_Char* name, const XML_Char** attributes); + static void sEndElementHandler( + void* userData, const XML_Char* name); + static void sCharacterDataHandler( + void* userData, const XML_Char* data, int length); + + void startSkipping(); + + enum Element { + ELEMENT_LLSD, + ELEMENT_UNDEF, + ELEMENT_BOOL, + ELEMENT_INTEGER, + ELEMENT_REAL, + ELEMENT_STRING, + ELEMENT_UUID, + ELEMENT_DATE, + ELEMENT_URI, + ELEMENT_BINARY, + ELEMENT_MAP, + ELEMENT_ARRAY, + ELEMENT_KEY, + ELEMENT_UNKNOWN + }; + static Element readElement(const XML_Char* name); + + static const XML_Char* findAttribute(const XML_Char* name, const XML_Char** pairs); + + bool mEmitErrors; + + XML_Parser mParser; + + LLSD mResult; + S32 mParseCount; + + bool mInLLSDElement; // true if we're on LLSD + bool mGracefullStop; // true if we found the </llsd + + typedef std::deque<LLSD*> LLSDRefStack; + LLSDRefStack mStack; + + int mDepth; + bool mSkipping; + int mSkipThrough; + + std::string mCurrentKey; // Current XML <tag> + std::string mCurrentContent; // String data between <tag> and </tag> }; LLSDXMLParser::Impl::Impl(bool emit_errors) - : mEmitErrors(emit_errors) + : mEmitErrors(emit_errors) { - mParser = XML_ParserCreate(NULL); - reset(); + mParser = XML_ParserCreate(NULL); + reset(); } LLSDXMLParser::Impl::~Impl() { - XML_ParserFree(mParser); + XML_ParserFree(mParser); } inline bool is_eol(char c) { - return (c == '\n' || c == '\r'); + return (c == '\n' || c == '\r'); } void clear_eol(std::istream& input) { - char c = input.peek(); - while (input.good() && is_eol(c)) - { - input.get(c); - c = input.peek(); - } + char c = input.peek(); + while (input.good() && is_eol(c)) + { + input.get(c); + c = input.peek(); + } } static unsigned get_till_eol(std::istream& input, char *buf, unsigned bufsize) { - unsigned count = 0; - while (count < bufsize && input.good()) - { - char c = input.get(); - buf[count++] = c; - if (is_eol(c)) - break; - } - return count; + unsigned count = 0; + while (count < bufsize && input.good()) + { + char c = input.get(); + buf[count++] = c; + if (is_eol(c)) + break; + } + return count; } S32 LLSDXMLParser::Impl::parse(std::istream& input, LLSD& data) { - XML_Status status; - - static const int BUFFER_SIZE = 1024; - void* buffer = NULL; - int count = 0; - while (input.good() && !input.eof()) - { - buffer = XML_GetBuffer(mParser, BUFFER_SIZE); - - /* - * If we happened to end our last buffer right at the end of the llsd, but the - * stream is still going we will get a null buffer here. Check for mGracefullStop. - */ - if (!buffer) - { - break; - } - count = get_till_eol(input, (char *)buffer, BUFFER_SIZE); - if (!count) - { - break; - } - status = XML_ParseBuffer(mParser, count, false); - - if (status == XML_STATUS_ERROR) - { - break; - } - } - - // *FIX.: This code is buggy - if the stream was empty or not - // good, there is not buffer to parse, both the call to - // XML_ParseBuffer and the buffer manipulations are illegal - // futhermore, it isn't clear that the expat buffer semantics are - // preserved - - status = XML_ParseBuffer(mParser, 0, true); - if (status == XML_STATUS_ERROR && !mGracefullStop) - { - if (buffer) - { - ((char*) buffer)[count ? count - 1 : 0] = '\0'; + XML_Status status; + + static const int BUFFER_SIZE = 1024; + void* buffer = NULL; + int count = 0; + while (input.good() && !input.eof()) + { + buffer = XML_GetBuffer(mParser, BUFFER_SIZE); + + /* + * If we happened to end our last buffer right at the end of the llsd, but the + * stream is still going we will get a null buffer here. Check for mGracefullStop. + */ + if (!buffer) + { + break; + } + count = get_till_eol(input, (char *)buffer, BUFFER_SIZE); + if (!count) + { + break; + } + status = XML_ParseBuffer(mParser, count, false); + + if (status == XML_STATUS_ERROR) + { + break; + } + } + + // *FIX.: This code is buggy - if the stream was empty or not + // good, there is not buffer to parse, both the call to + // XML_ParseBuffer and the buffer manipulations are illegal + // futhermore, it isn't clear that the expat buffer semantics are + // preserved + + status = XML_ParseBuffer(mParser, 0, true); + if (status == XML_STATUS_ERROR && !mGracefullStop) + { + if (buffer) + { + ((char*) buffer)[count ? count - 1 : 0] = '\0'; if (mEmitErrors) { LL_INFOS() << "LLSDXMLParser::Impl::parse: XML_STATUS_ERROR parsing:" << (char*)buffer << LL_ENDL; } - } + } else { if (mEmitErrors) @@ -416,159 +416,159 @@ S32 LLSDXMLParser::Impl::parse(std::istream& input, LLSD& data) LL_INFOS() << "LLSDXMLParser::Impl::parse: XML_STATUS_ERROR, null buffer" << LL_ENDL; } } - data = LLSD(); - return LLSDParser::PARSE_FAILURE; - } + data = LLSD(); + return LLSDParser::PARSE_FAILURE; + } - clear_eol(input); - data = mResult; - return mParseCount; + clear_eol(input); + data = mResult; + return mParseCount; } S32 LLSDXMLParser::Impl::parseLines(std::istream& input, LLSD& data) { - XML_Status status = XML_STATUS_OK; - - data = LLSD(); - - static const int BUFFER_SIZE = 1024; - - //static char last_buffer[ BUFFER_SIZE ]; - //std::streamsize last_num_read; - - // Must get rid of any leading \n, otherwise the stream gets into an error/eof state - clear_eol(input); - - while( !mGracefullStop - && input.good() - && !input.eof()) - { - void* buffer = XML_GetBuffer(mParser, BUFFER_SIZE); - /* - * If we happened to end our last buffer right at the end of the llsd, but the - * stream is still going we will get a null buffer here. Check for mGracefullStop. - * -- I don't think this is actually true - zero 2008-05-09 - */ - if (!buffer) - { - break; - } - - // Get one line - input.getline((char*)buffer, BUFFER_SIZE); - std::streamsize num_read = input.gcount(); - - //memcpy( last_buffer, buffer, num_read ); - //last_num_read = num_read; - - if ( num_read > 0 ) - { - if (!input.good() ) - { // Clear state that's set when we run out of buffer - input.clear(); - } - - // Re-insert with the \n that was absorbed by getline() - char * text = (char *) buffer; - if ( text[num_read - 1] == 0) - { - text[num_read - 1] = '\n'; - } - } - - status = XML_ParseBuffer(mParser, (int)num_read, false); - if (status == XML_STATUS_ERROR) - { - break; - } - } - - if (status != XML_STATUS_ERROR - && !mGracefullStop) - { // Parse last bit - status = XML_ParseBuffer(mParser, 0, true); - } - - if (status == XML_STATUS_ERROR - && !mGracefullStop) - { - if (mEmitErrors) - { - LL_INFOS() << "LLSDXMLParser::Impl::parseLines: XML_STATUS_ERROR" << LL_ENDL; - } - return LLSDParser::PARSE_FAILURE; - } - - clear_eol(input); - data = mResult; - return mParseCount; + XML_Status status = XML_STATUS_OK; + + data = LLSD(); + + static const int BUFFER_SIZE = 1024; + + //static char last_buffer[ BUFFER_SIZE ]; + //std::streamsize last_num_read; + + // Must get rid of any leading \n, otherwise the stream gets into an error/eof state + clear_eol(input); + + while( !mGracefullStop + && input.good() + && !input.eof()) + { + void* buffer = XML_GetBuffer(mParser, BUFFER_SIZE); + /* + * If we happened to end our last buffer right at the end of the llsd, but the + * stream is still going we will get a null buffer here. Check for mGracefullStop. + * -- I don't think this is actually true - zero 2008-05-09 + */ + if (!buffer) + { + break; + } + + // Get one line + input.getline((char*)buffer, BUFFER_SIZE); + std::streamsize num_read = input.gcount(); + + //memcpy( last_buffer, buffer, num_read ); + //last_num_read = num_read; + + if ( num_read > 0 ) + { + if (!input.good() ) + { // Clear state that's set when we run out of buffer + input.clear(); + } + + // Re-insert with the \n that was absorbed by getline() + char * text = (char *) buffer; + if ( text[num_read - 1] == 0) + { + text[num_read - 1] = '\n'; + } + } + + status = XML_ParseBuffer(mParser, (int)num_read, false); + if (status == XML_STATUS_ERROR) + { + break; + } + } + + if (status != XML_STATUS_ERROR + && !mGracefullStop) + { // Parse last bit + status = XML_ParseBuffer(mParser, 0, true); + } + + if (status == XML_STATUS_ERROR + && !mGracefullStop) + { + if (mEmitErrors) + { + LL_INFOS() << "LLSDXMLParser::Impl::parseLines: XML_STATUS_ERROR" << LL_ENDL; + } + return LLSDParser::PARSE_FAILURE; + } + + clear_eol(input); + data = mResult; + return mParseCount; } void LLSDXMLParser::Impl::reset() { - mResult.clear(); - mParseCount = 0; - - mInLLSDElement = false; - mDepth = 0; - - mGracefullStop = false; - - mStack.clear(); - - mSkipping = false; - - mCurrentKey.clear(); - - XML_ParserReset(mParser, "utf-8"); - XML_SetUserData(mParser, this); - XML_SetElementHandler(mParser, sStartElementHandler, sEndElementHandler); - XML_SetCharacterDataHandler(mParser, sCharacterDataHandler); + mResult.clear(); + mParseCount = 0; + + mInLLSDElement = false; + mDepth = 0; + + mGracefullStop = false; + + mStack.clear(); + + mSkipping = false; + + mCurrentKey.clear(); + + XML_ParserReset(mParser, "utf-8"); + XML_SetUserData(mParser, this); + XML_SetElementHandler(mParser, sStartElementHandler, sEndElementHandler); + XML_SetCharacterDataHandler(mParser, sCharacterDataHandler); } void LLSDXMLParser::Impl::startSkipping() { - mSkipping = true; - mSkipThrough = mDepth; + mSkipping = true; + mSkipThrough = mDepth; } const XML_Char* LLSDXMLParser::Impl::findAttribute(const XML_Char* name, const XML_Char** pairs) { - while (NULL != pairs && NULL != *pairs) - { - if(0 == strcmp(name, *pairs)) - { - return *(pairs + 1); - } - pairs += 2; - } - return NULL; + while (NULL != pairs && NULL != *pairs) + { + if(0 == strcmp(name, *pairs)) + { + return *(pairs + 1); + } + pairs += 2; + } + return NULL; } void LLSDXMLParser::Impl::parsePart(const char* buf, llssize len) { - if ( buf != NULL - && len > 0 ) - { - XML_Status status = XML_Parse(mParser, buf, len, false); - if (status == XML_STATUS_ERROR) - { - LL_INFOS() << "Unexpected XML parsing error at start" << LL_ENDL; - } - } + if ( buf != NULL + && len > 0 ) + { + XML_Status status = XML_Parse(mParser, buf, len, false); + if (status == XML_STATUS_ERROR) + { + LL_INFOS() << "Unexpected XML parsing error at start" << LL_ENDL; + } + } } // Performance testing code -//#define XML_PARSER_PERFORMANCE_TESTS +//#define XML_PARSER_PERFORMANCE_TESTS #ifdef XML_PARSER_PERFORMANCE_TESTS extern U64 totalTime(); -U64 readElementTime = 0; +U64 readElementTime = 0; U64 startElementTime = 0; U64 endElementTime = 0; U64 charDataTime = 0; @@ -577,333 +577,333 @@ U64 parseTime = 0; class XML_Timer { public: - XML_Timer( U64 * sum ) : mSum( sum ) - { - mStart = totalTime(); - } - ~XML_Timer() - { - *mSum += (totalTime() - mStart); - } - - U64 * mSum; - U64 mStart; + XML_Timer( U64 * sum ) : mSum( sum ) + { + mStart = totalTime(); + } + ~XML_Timer() + { + *mSum += (totalTime() - mStart); + } + + U64 * mSum; + U64 mStart; }; #endif // XML_PARSER_PERFORMANCE_TESTS void LLSDXMLParser::Impl::startElementHandler(const XML_Char* name, const XML_Char** attributes) { - #ifdef XML_PARSER_PERFORMANCE_TESTS - XML_Timer timer( &startElementTime ); - #endif // XML_PARSER_PERFORMANCE_TESTS - - ++mDepth; - if (mSkipping) - { - return; - } - - Element element = readElement(name); - - mCurrentContent.clear(); - - switch (element) - { - case ELEMENT_LLSD: - if (mInLLSDElement) { return startSkipping(); } - mInLLSDElement = true; - return; - - case ELEMENT_KEY: - if (mStack.empty() || !(mStack.back()->isMap())) - { - return startSkipping(); - } - return; - - case ELEMENT_BINARY: - { - const XML_Char* encoding = findAttribute("encoding", attributes); - if(encoding && strcmp("base64", encoding) != 0) { return startSkipping(); } - break; - } - - default: - // all rest are values, fall through - ; - } - - - if (!mInLLSDElement) { return startSkipping(); } - - if (mStack.empty()) - { - mStack.push_back(&mResult); - } - else if (mStack.back()->isMap()) - { - if (mCurrentKey.empty()) { return startSkipping(); } - - LLSD& map = *mStack.back(); - LLSD& newElement = map[mCurrentKey]; - mStack.push_back(&newElement); - - mCurrentKey.clear(); - } - else if (mStack.back()->isArray()) - { - LLSD& array = *mStack.back(); - array.append(LLSD()); - LLSD& newElement = array[array.size()-1]; - mStack.push_back(&newElement); - } - else { - // improperly nested value in a non-structure - return startSkipping(); - } - - ++mParseCount; - switch (element) - { - case ELEMENT_MAP: - *mStack.back() = LLSD::emptyMap(); - break; - - case ELEMENT_ARRAY: - *mStack.back() = LLSD::emptyArray(); - break; - - default: - // all the other values will be set in the end element handler - ; - } + #ifdef XML_PARSER_PERFORMANCE_TESTS + XML_Timer timer( &startElementTime ); + #endif // XML_PARSER_PERFORMANCE_TESTS + + ++mDepth; + if (mSkipping) + { + return; + } + + Element element = readElement(name); + + mCurrentContent.clear(); + + switch (element) + { + case ELEMENT_LLSD: + if (mInLLSDElement) { return startSkipping(); } + mInLLSDElement = true; + return; + + case ELEMENT_KEY: + if (mStack.empty() || !(mStack.back()->isMap())) + { + return startSkipping(); + } + return; + + case ELEMENT_BINARY: + { + const XML_Char* encoding = findAttribute("encoding", attributes); + if(encoding && strcmp("base64", encoding) != 0) { return startSkipping(); } + break; + } + + default: + // all rest are values, fall through + ; + } + + + if (!mInLLSDElement) { return startSkipping(); } + + if (mStack.empty()) + { + mStack.push_back(&mResult); + } + else if (mStack.back()->isMap()) + { + if (mCurrentKey.empty()) { return startSkipping(); } + + LLSD& map = *mStack.back(); + LLSD& newElement = map[mCurrentKey]; + mStack.push_back(&newElement); + + mCurrentKey.clear(); + } + else if (mStack.back()->isArray()) + { + LLSD& array = *mStack.back(); + array.append(LLSD()); + LLSD& newElement = array[array.size()-1]; + mStack.push_back(&newElement); + } + else { + // improperly nested value in a non-structure + return startSkipping(); + } + + ++mParseCount; + switch (element) + { + case ELEMENT_MAP: + *mStack.back() = LLSD::emptyMap(); + break; + + case ELEMENT_ARRAY: + *mStack.back() = LLSD::emptyArray(); + break; + + default: + // all the other values will be set in the end element handler + ; + } } void LLSDXMLParser::Impl::endElementHandler(const XML_Char* name) { - #ifdef XML_PARSER_PERFORMANCE_TESTS - XML_Timer timer( &endElementTime ); - #endif // XML_PARSER_PERFORMANCE_TESTS - - --mDepth; - if (mSkipping) - { - if (mDepth < mSkipThrough) - { - mSkipping = false; - } - return; - } - - Element element = readElement(name); - - switch (element) - { - case ELEMENT_LLSD: - if (mInLLSDElement) - { - mInLLSDElement = false; - mGracefullStop = true; - XML_StopParser(mParser, false); - } - return; - - case ELEMENT_KEY: - mCurrentKey = mCurrentContent; - return; - - default: - // all rest are values, fall through - ; - } - - if (!mInLLSDElement) { return; } - - LLSD& value = *mStack.back(); - mStack.pop_back(); - - switch (element) - { - case ELEMENT_UNDEF: - value.clear(); - break; - - case ELEMENT_BOOL: - value = (mCurrentContent == "true" || mCurrentContent == "1"); - break; - - case ELEMENT_INTEGER: - { - S32 i; - // sscanf okay here with different locales - ints don't change for different locale settings like floats do. - if ( sscanf(mCurrentContent.c_str(), "%d", &i ) == 1 ) - { // See if sscanf works - it's faster - value = i; - } - else - { - value = LLSD(mCurrentContent).asInteger(); - } - } - break; - - case ELEMENT_REAL: - { - value = LLSD(mCurrentContent).asReal(); - // removed since this breaks when locale has decimal separator that isn't '.' - // investigated changing local to something compatible each time but deemed higher - // risk that just using LLSD.asReal() each time. - //F64 r; - //if ( sscanf(mCurrentContent.c_str(), "%lf", &r ) == 1 ) - //{ // See if sscanf works - it's faster - // value = r; - //} - //else - //{ - // value = LLSD(mCurrentContent).asReal(); - //} - } - break; - - case ELEMENT_STRING: - value = mCurrentContent; - break; - - case ELEMENT_UUID: - value = LLSD(mCurrentContent).asUUID(); - break; - - case ELEMENT_DATE: - value = LLSD(mCurrentContent).asDate(); - break; - - case ELEMENT_URI: - value = LLSD(mCurrentContent).asURI(); - break; - - case ELEMENT_BINARY: - { - // 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], stripped.c_str()); - data.resize(len); - value = data; - break; - } - - case ELEMENT_UNKNOWN: - value.clear(); - break; - - default: - // other values, map and array, have already been set - break; - } - - mCurrentContent.clear(); + #ifdef XML_PARSER_PERFORMANCE_TESTS + XML_Timer timer( &endElementTime ); + #endif // XML_PARSER_PERFORMANCE_TESTS + + --mDepth; + if (mSkipping) + { + if (mDepth < mSkipThrough) + { + mSkipping = false; + } + return; + } + + Element element = readElement(name); + + switch (element) + { + case ELEMENT_LLSD: + if (mInLLSDElement) + { + mInLLSDElement = false; + mGracefullStop = true; + XML_StopParser(mParser, false); + } + return; + + case ELEMENT_KEY: + mCurrentKey = mCurrentContent; + return; + + default: + // all rest are values, fall through + ; + } + + if (!mInLLSDElement) { return; } + + LLSD& value = *mStack.back(); + mStack.pop_back(); + + switch (element) + { + case ELEMENT_UNDEF: + value.clear(); + break; + + case ELEMENT_BOOL: + value = (mCurrentContent == "true" || mCurrentContent == "1"); + break; + + case ELEMENT_INTEGER: + { + S32 i; + // sscanf okay here with different locales - ints don't change for different locale settings like floats do. + if ( sscanf(mCurrentContent.c_str(), "%d", &i ) == 1 ) + { // See if sscanf works - it's faster + value = i; + } + else + { + value = LLSD(mCurrentContent).asInteger(); + } + } + break; + + case ELEMENT_REAL: + { + value = LLSD(mCurrentContent).asReal(); + // removed since this breaks when locale has decimal separator that isn't '.' + // investigated changing local to something compatible each time but deemed higher + // risk that just using LLSD.asReal() each time. + //F64 r; + //if ( sscanf(mCurrentContent.c_str(), "%lf", &r ) == 1 ) + //{ // See if sscanf works - it's faster + // value = r; + //} + //else + //{ + // value = LLSD(mCurrentContent).asReal(); + //} + } + break; + + case ELEMENT_STRING: + value = mCurrentContent; + break; + + case ELEMENT_UUID: + value = LLSD(mCurrentContent).asUUID(); + break; + + case ELEMENT_DATE: + value = LLSD(mCurrentContent).asDate(); + break; + + case ELEMENT_URI: + value = LLSD(mCurrentContent).asURI(); + break; + + case ELEMENT_BINARY: + { + // 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], stripped.c_str()); + data.resize(len); + value = data; + break; + } + + case ELEMENT_UNKNOWN: + value.clear(); + break; + + default: + // other values, map and array, have already been set + break; + } + + mCurrentContent.clear(); } void LLSDXMLParser::Impl::characterDataHandler(const XML_Char* data, int length) { - #ifdef XML_PARSER_PERFORMANCE_TESTS - XML_Timer timer( &charDataTime ); - #endif // XML_PARSER_PERFORMANCE_TESTS + #ifdef XML_PARSER_PERFORMANCE_TESTS + XML_Timer timer( &charDataTime ); + #endif // XML_PARSER_PERFORMANCE_TESTS - mCurrentContent.append(data, length); + mCurrentContent.append(data, length); } void LLSDXMLParser::Impl::sStartElementHandler( - void* userData, const XML_Char* name, const XML_Char** attributes) + void* userData, const XML_Char* name, const XML_Char** attributes) { - ((LLSDXMLParser::Impl*)userData)->startElementHandler(name, attributes); + ((LLSDXMLParser::Impl*)userData)->startElementHandler(name, attributes); } void LLSDXMLParser::Impl::sEndElementHandler( - void* userData, const XML_Char* name) + void* userData, const XML_Char* name) { - ((LLSDXMLParser::Impl*)userData)->endElementHandler(name); + ((LLSDXMLParser::Impl*)userData)->endElementHandler(name); } void LLSDXMLParser::Impl::sCharacterDataHandler( - void* userData, const XML_Char* data, int length) + void* userData, const XML_Char* data, int length) { - ((LLSDXMLParser::Impl*)userData)->characterDataHandler(data, length); + ((LLSDXMLParser::Impl*)userData)->characterDataHandler(data, length); } /* - This code is time critical - - This is a sample of tag occurances of text in simstate file with ~8000 objects. - A tag pair (<key>something</key>) counts is counted as two: - - key - 2680178 - real - 1818362 - integer - 906078 - array - 295682 - map - 191818 - uuid - 177903 - binary - 175748 - string - 53482 - undef - 40353 - boolean - 33874 - llsd - 16332 - uri - 38 - date - 1 + This code is time critical + + This is a sample of tag occurances of text in simstate file with ~8000 objects. + A tag pair (<key>something</key>) counts is counted as two: + + key - 2680178 + real - 1818362 + integer - 906078 + array - 295682 + map - 191818 + uuid - 177903 + binary - 175748 + string - 53482 + undef - 40353 + boolean - 33874 + llsd - 16332 + uri - 38 + date - 1 */ LLSDXMLParser::Impl::Element LLSDXMLParser::Impl::readElement(const XML_Char* name) { - #ifdef XML_PARSER_PERFORMANCE_TESTS - XML_Timer timer( &readElementTime ); - #endif // XML_PARSER_PERFORMANCE_TESTS - - XML_Char c = *name; - switch (c) - { - case 'k': - if (strcmp(name, "key") == 0) { return ELEMENT_KEY; } - break; - case 'r': - if (strcmp(name, "real") == 0) { return ELEMENT_REAL; } - break; - case 'i': - if (strcmp(name, "integer") == 0) { return ELEMENT_INTEGER; } - break; - case 'a': - if (strcmp(name, "array") == 0) { return ELEMENT_ARRAY; } - break; - case 'm': - if (strcmp(name, "map") == 0) { return ELEMENT_MAP; } - break; - case 'u': - if (strcmp(name, "uuid") == 0) { return ELEMENT_UUID; } - if (strcmp(name, "undef") == 0) { return ELEMENT_UNDEF; } - if (strcmp(name, "uri") == 0) { return ELEMENT_URI; } - break; - case 'b': - if (strcmp(name, "binary") == 0) { return ELEMENT_BINARY; } - if (strcmp(name, "boolean") == 0) { return ELEMENT_BOOL; } - break; - case 's': - if (strcmp(name, "string") == 0) { return ELEMENT_STRING; } - break; - case 'l': - if (strcmp(name, "llsd") == 0) { return ELEMENT_LLSD; } - break; - case 'd': - if (strcmp(name, "date") == 0) { return ELEMENT_DATE; } - break; - } - return ELEMENT_UNKNOWN; + #ifdef XML_PARSER_PERFORMANCE_TESTS + XML_Timer timer( &readElementTime ); + #endif // XML_PARSER_PERFORMANCE_TESTS + + XML_Char c = *name; + switch (c) + { + case 'k': + if (strcmp(name, "key") == 0) { return ELEMENT_KEY; } + break; + case 'r': + if (strcmp(name, "real") == 0) { return ELEMENT_REAL; } + break; + case 'i': + if (strcmp(name, "integer") == 0) { return ELEMENT_INTEGER; } + break; + case 'a': + if (strcmp(name, "array") == 0) { return ELEMENT_ARRAY; } + break; + case 'm': + if (strcmp(name, "map") == 0) { return ELEMENT_MAP; } + break; + case 'u': + if (strcmp(name, "uuid") == 0) { return ELEMENT_UUID; } + if (strcmp(name, "undef") == 0) { return ELEMENT_UNDEF; } + if (strcmp(name, "uri") == 0) { return ELEMENT_URI; } + break; + case 'b': + if (strcmp(name, "binary") == 0) { return ELEMENT_BINARY; } + if (strcmp(name, "boolean") == 0) { return ELEMENT_BOOL; } + break; + case 's': + if (strcmp(name, "string") == 0) { return ELEMENT_STRING; } + break; + case 'l': + if (strcmp(name, "llsd") == 0) { return ELEMENT_LLSD; } + break; + case 'd': + if (strcmp(name, "date") == 0) { return ELEMENT_DATE; } + break; + } + return ELEMENT_UNKNOWN; } @@ -919,34 +919,34 @@ LLSDXMLParser::LLSDXMLParser(bool emit_errors /* = true */) : impl(* new Impl(em LLSDXMLParser::~LLSDXMLParser() { - delete &impl; + delete &impl; } void LLSDXMLParser::parsePart(const char *buf, llssize len) { - impl.parsePart(buf, len); + impl.parsePart(buf, len); } // virtual S32 LLSDXMLParser::doParse(std::istream& input, LLSD& data, S32 max_depth) const { - LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD + LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD - #ifdef XML_PARSER_PERFORMANCE_TESTS - XML_Timer timer( &parseTime ); - #endif // XML_PARSER_PERFORMANCE_TESTS + #ifdef XML_PARSER_PERFORMANCE_TESTS + XML_Timer timer( &parseTime ); + #endif // XML_PARSER_PERFORMANCE_TESTS - if (mParseLines) - { - // Use line-based reading (faster code) - return impl.parseLines(input, data); - } + if (mParseLines) + { + // Use line-based reading (faster code) + return impl.parseLines(input, data); + } - return impl.parse(input, data); + return impl.parse(input, data); } -// virtual +// virtual void LLSDXMLParser::doReset() { - impl.reset(); + impl.reset(); } diff --git a/indra/llcommon/llsdserialize_xml.h b/indra/llcommon/llsdserialize_xml.h index dcc5f3d3c7..6c1a74c0e3 100644 --- a/indra/llcommon/llsdserialize_xml.h +++ b/indra/llcommon/llsdserialize_xml.h @@ -1,25 +1,25 @@ -/** +/** * @file llsdserialize_xml.h * @brief XML parsers and formatters for LLSD * * $LicenseInfo:firstyear=2006&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llcommon/llsdutil.cpp b/indra/llcommon/llsdutil.cpp index f31b9a1d14..4dd47ad1ac 100644 --- a/indra/llcommon/llsdutil.cpp +++ b/indra/llcommon/llsdutil.cpp @@ -1,1083 +1,1083 @@ -/** - * @file llsdutil.cpp - * @author Phoenix - * @date 2006-05-24 - * @brief Implementation of classes, functions, etc, for using structured data. - * - * $LicenseInfo:firstyear=2006&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" - -#include "llsdutil.h" -#include <sstream> - -#if LL_WINDOWS -# define WIN32_LEAN_AND_MEAN -# include <winsock2.h> // for htonl -#elif LL_LINUX -# include <netinet/in.h> -#elif LL_DARWIN -# include <arpa/inet.h> -#endif - -#include "llsdserialize.h" -#include "stringize.h" -#include "is_approx_equal_fraction.h" - -#include <map> -#include <set> -#include <boost/range.hpp> - -// U32 -LLSD ll_sd_from_U32(const U32 val) -{ - std::vector<U8> v; - U32 net_order = htonl(val); - - v.resize(4); - memcpy(&(v[0]), &net_order, 4); /* Flawfinder: ignore */ - - return LLSD(v); -} - -U32 ll_U32_from_sd(const LLSD& sd) -{ - U32 ret; - std::vector<U8> v = sd.asBinary(); - if (v.size() < 4) - { - return 0; - } - memcpy(&ret, &(v[0]), 4); /* Flawfinder: ignore */ - ret = ntohl(ret); - return ret; -} - -//U64 -LLSD ll_sd_from_U64(const U64 val) -{ - std::vector<U8> v; - U32 high, low; - - high = (U32)(val >> 32); - low = (U32)val; - high = htonl(high); - low = htonl(low); - - v.resize(8); - memcpy(&(v[0]), &high, 4); /* Flawfinder: ignore */ - memcpy(&(v[4]), &low, 4); /* Flawfinder: ignore */ - - return LLSD(v); -} - -U64 ll_U64_from_sd(const LLSD& sd) -{ - U32 high, low; - std::vector<U8> v = sd.asBinary(); - - if (v.size() < 8) - { - return 0; - } - - memcpy(&high, &(v[0]), 4); /* Flawfinder: ignore */ - memcpy(&low, &(v[4]), 4); /* Flawfinder: ignore */ - high = ntohl(high); - low = ntohl(low); - - return ((U64)high) << 32 | low; -} - -// IP Address (stored in net order in a U32, so don't need swizzling) -LLSD ll_sd_from_ipaddr(const U32 val) -{ - std::vector<U8> v; - - v.resize(4); - memcpy(&(v[0]), &val, 4); /* Flawfinder: ignore */ - - return LLSD(v); -} - -U32 ll_ipaddr_from_sd(const LLSD& sd) -{ - U32 ret; - std::vector<U8> v = sd.asBinary(); - if (v.size() < 4) - { - return 0; - } - memcpy(&ret, &(v[0]), 4); /* Flawfinder: ignore */ - return ret; -} - -// Converts an LLSD binary to an LLSD string -LLSD ll_string_from_binary(const LLSD& sd) -{ - std::vector<U8> value = sd.asBinary(); - std::string str; - str.resize(value.size()); - memcpy(&str[0], &value[0], value.size()); - return str; -} - -// Converts an LLSD string to an LLSD binary -LLSD ll_binary_from_string(const LLSD& sd) -{ - std::vector<U8> binary_value; - - std::string string_value = sd.asString(); - for (const U8 c : string_value) - { - binary_value.push_back(c); - } - - binary_value.push_back('\0'); - - return binary_value; -} - -char* ll_print_sd(const LLSD& sd) -{ - const U32 bufferSize = 10 * 1024; - static char buffer[bufferSize]; - std::ostringstream stream; - //stream.rdbuf()->pubsetbuf(buffer, bufferSize); - stream << LLSDOStreamer<LLSDXMLFormatter>(sd); - stream << std::ends; - strncpy(buffer, stream.str().c_str(), bufferSize); - buffer[bufferSize - 1] = '\0'; - return buffer; -} - -char* ll_pretty_print_sd_ptr(const LLSD* sd) -{ - if (sd) - { - return ll_pretty_print_sd(*sd); - } - return NULL; -} - -char* ll_pretty_print_sd(const LLSD& sd) -{ - const U32 bufferSize = 100 * 1024; - static char buffer[bufferSize]; - std::ostringstream stream; - //stream.rdbuf()->pubsetbuf(buffer, bufferSize); - stream << LLSDOStreamer<LLSDXMLFormatter>(sd, LLSDFormatter::OPTIONS_PRETTY); - stream << std::ends; - strncpy(buffer, stream.str().c_str(), bufferSize); - buffer[bufferSize - 1] = '\0'; - return buffer; -} - -std::string ll_stream_notation_sd(const LLSD& sd) -{ - std::ostringstream stream; - stream << LLSDOStreamer<LLSDNotationFormatter>(sd); - return stream.str(); -} - - -//compares the structure of an LLSD to a template LLSD and stores the -//"valid" values in a 3rd LLSD. Default values pulled from the template -//if the tested LLSD does not contain the key/value pair. -//Excess values in the test LLSD are ignored in the resultant_llsd. -//If the llsd to test has a specific key to a map and the values -//are not of the same type, false is returned or if the LLSDs are not -//of the same value. Ordering of arrays matters -//Otherwise, returns true -bool compare_llsd_with_template( - const LLSD& llsd_to_test, - const LLSD& template_llsd, - LLSD& resultant_llsd) -{ - LL_PROFILE_ZONE_SCOPED - - if ( - llsd_to_test.isUndefined() && - template_llsd.isDefined() ) - { - resultant_llsd = template_llsd; - return true; - } - else if ( llsd_to_test.type() != template_llsd.type() ) - { - resultant_llsd = LLSD(); - return false; - } - - if ( llsd_to_test.isArray() ) - { - //they are both arrays - //we loop over all the items in the template - //verifying that the to_test has a subset (in the same order) - //any shortcoming in the testing_llsd are just taken - //to be the rest of the template - LLSD data; - LLSD::array_const_iterator test_iter; - LLSD::array_const_iterator template_iter; - - resultant_llsd = LLSD::emptyArray(); - test_iter = llsd_to_test.beginArray(); - - for ( - template_iter = template_llsd.beginArray(); - (template_iter != template_llsd.endArray() && - test_iter != llsd_to_test.endArray()); - ++template_iter) - { - if ( !compare_llsd_with_template( - *test_iter, - *template_iter, - data) ) - { - resultant_llsd = LLSD(); - return false; - } - else - { - resultant_llsd.append(data); - } - - ++test_iter; - } - - //so either the test or the template ended - //we do another loop now to the end of the template - //grabbing the default values - for (; - template_iter != template_llsd.endArray(); - ++template_iter) - { - resultant_llsd.append(*template_iter); - } - } - else if ( llsd_to_test.isMap() ) - { - //now we loop over the keys of the two maps - //any excess is taken from the template - //excess is ignored in the test - LLSD value; - LLSD::map_const_iterator template_iter; - - resultant_llsd = LLSD::emptyMap(); - for ( - template_iter = template_llsd.beginMap(); - template_iter != template_llsd.endMap(); - ++template_iter) - { - if ( llsd_to_test.has(template_iter->first) ) - { - //the test LLSD has the same key - if ( !compare_llsd_with_template( - llsd_to_test[template_iter->first], - template_iter->second, - value) ) - { - resultant_llsd = LLSD(); - return false; - } - else - { - resultant_llsd[template_iter->first] = value; - } - } - else - { - //test llsd doesn't have it...take the - //template as default value - resultant_llsd[template_iter->first] = - template_iter->second; - } - } - } - else - { - //of same type...take the test llsd's value - resultant_llsd = llsd_to_test; - } - - - return true; -} - -// filter_llsd_with_template() is a direct clone (copy-n-paste) of -// compare_llsd_with_template with the following differences: -// (1) bool vs BOOL return types -// (2) A map with the key value "*" is a special value and maps any key in the -// test llsd that doesn't have an explicitly matching key in the template. -// (3) The element of an array with exactly one element is taken as a template -// for *all* the elements of the test array. If the template array is of -// different size, compare_llsd_with_template() semantics apply. -bool filter_llsd_with_template( - const LLSD & llsd_to_test, - const LLSD & template_llsd, - LLSD & resultant_llsd) -{ - LL_PROFILE_ZONE_SCOPED - - if (llsd_to_test.isUndefined() && template_llsd.isDefined()) - { - resultant_llsd = template_llsd; - return true; - } - else if (llsd_to_test.type() != template_llsd.type()) - { - resultant_llsd = LLSD(); - return false; - } - - if (llsd_to_test.isArray()) - { - //they are both arrays - //we loop over all the items in the template - //verifying that the to_test has a subset (in the same order) - //any shortcoming in the testing_llsd are just taken - //to be the rest of the template - LLSD data; - LLSD::array_const_iterator test_iter; - LLSD::array_const_iterator template_iter; - - resultant_llsd = LLSD::emptyArray(); - test_iter = llsd_to_test.beginArray(); - - if (1 == template_llsd.size()) - { - // If the template has a single item, treat it as - // the template for *all* items in the test LLSD. - template_iter = template_llsd.beginArray(); - - for (; test_iter != llsd_to_test.endArray(); ++test_iter) - { - if (! filter_llsd_with_template(*test_iter, *template_iter, data)) - { - resultant_llsd = LLSD(); - return false; - } - else - { - resultant_llsd.append(data); - } - } - } - else - { - // Traditional compare_llsd_with_template matching - - for (template_iter = template_llsd.beginArray(); - template_iter != template_llsd.endArray() && - test_iter != llsd_to_test.endArray(); - ++template_iter, ++test_iter) - { - if (! filter_llsd_with_template(*test_iter, *template_iter, data)) - { - resultant_llsd = LLSD(); - return false; - } - else - { - resultant_llsd.append(data); - } - } - - //so either the test or the template ended - //we do another loop now to the end of the template - //grabbing the default values - for (; - template_iter != template_llsd.endArray(); - ++template_iter) - { - resultant_llsd.append(*template_iter); - } - } - } - else if (llsd_to_test.isMap()) - { - resultant_llsd = LLSD::emptyMap(); - - //now we loop over the keys of the two maps - //any excess is taken from the template - //excess is ignored in the test - - // Special tag for wildcarded LLSD map key templates - const LLSD::String wildcard_tag("*"); - - const bool template_has_wildcard = template_llsd.has(wildcard_tag); - LLSD wildcard_value; - LLSD value; - - const LLSD::map_const_iterator template_iter_end(template_llsd.endMap()); - for (LLSD::map_const_iterator template_iter(template_llsd.beginMap()); - template_iter_end != template_iter; - ++template_iter) - { - if (wildcard_tag == template_iter->first) - { - wildcard_value = template_iter->second; - } - else if (llsd_to_test.has(template_iter->first)) - { - //the test LLSD has the same key - if (! filter_llsd_with_template(llsd_to_test[template_iter->first], - template_iter->second, - value)) - { - resultant_llsd = LLSD(); - return false; - } - else - { - resultant_llsd[template_iter->first] = value; - } - } - else if (! template_has_wildcard) - { - // test llsd doesn't have it...take the - // template as default value - resultant_llsd[template_iter->first] = template_iter->second; - } - } - if (template_has_wildcard) - { - LLSD sub_value; - LLSD::map_const_iterator test_iter; - - for (test_iter = llsd_to_test.beginMap(); - test_iter != llsd_to_test.endMap(); - ++test_iter) - { - if (resultant_llsd.has(test_iter->first)) - { - // Final value has test key, assume more specific - // template matched and we shouldn't modify it again. - continue; - } - else if (! filter_llsd_with_template(test_iter->second, - wildcard_value, - sub_value)) - { - // Test value doesn't match wildcarded template - resultant_llsd = LLSD(); - return false; - } - else - { - // Test value matches template, add the actuals. - resultant_llsd[test_iter->first] = sub_value; - } - } - } - } - else - { - //of same type...take the test llsd's value - resultant_llsd = llsd_to_test; - } - - 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() - { - LL_PROFILE_ZONE_SCOPED - - 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 - { - LL_PROFILE_ZONE_SCOPED - - 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 -{ - LL_PROFILE_ZONE_SCOPED - - // 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) -{ - LL_PROFILE_ZONE_SCOPED - - // 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, int bits) -{ - LL_PROFILE_ZONE_SCOPED - - // We're comparing strict equality of LLSD representation rather than - // performing any conversions. So if the types aren't equal, the LLSD - // values aren't equal. - if (lhs.type() != rhs.type()) - { - return false; - } - - // Here we know both types are equal. Now compare values. - switch (lhs.type()) - { - case LLSD::TypeUndefined: - // Both are TypeUndefined. There's nothing more to know. - return true; - - case LLSD::TypeReal: - // This is where the 'bits' argument comes in handy. If passed - // explicitly, it means to use is_approx_equal_fraction() to compare. - if (bits >= 0) - { - return is_approx_equal_fraction(lhs.asReal(), rhs.asReal(), bits); - } - // Otherwise we compare bit representations, and the usual caveats - // about comparing floating-point numbers apply. Omitting 'bits' when - // comparing Real values is only useful when we expect identical bit - // representation for a given Real value, e.g. for integer-valued - // Reals. - return (lhs.asReal() == rhs.asReal()); - -#define COMPARE_SCALAR(type) \ - case LLSD::Type##type: \ - /* LLSD::URI has operator!=() but not operator==() */ \ - /* rely on the optimizer for all others */ \ - return (! (lhs.as##type() != rhs.as##type())) - - COMPARE_SCALAR(Boolean); - COMPARE_SCALAR(Integer); - COMPARE_SCALAR(String); - COMPARE_SCALAR(UUID); - COMPARE_SCALAR(Date); - COMPARE_SCALAR(URI); - COMPARE_SCALAR(Binary); - -#undef COMPARE_SCALAR - - case LLSD::TypeArray: - { - LLSD::array_const_iterator - lai(lhs.beginArray()), laend(lhs.endArray()), - rai(rhs.beginArray()), raend(rhs.endArray()); - // Compare array elements, walking the two arrays in parallel. - for ( ; lai != laend && rai != raend; ++lai, ++rai) - { - // If any one array element is unequal, the arrays are unequal. - if (! llsd_equals(*lai, *rai, bits)) - return false; - } - // Here we've reached the end of one or the other array. They're equal - // only if they're BOTH at end: that is, if they have equal length too. - return (lai == laend && rai == raend); - } - - case LLSD::TypeMap: - { - // Build a set of all rhs keys. - std::set<LLSD::String> rhskeys; - for (LLSD::map_const_iterator rmi(rhs.beginMap()), rmend(rhs.endMap()); - rmi != rmend; ++rmi) - { - rhskeys.insert(rmi->first); - } - // Now walk all the lhs keys. - for (LLSD::map_const_iterator lmi(lhs.beginMap()), lmend(lhs.endMap()); - lmi != lmend; ++lmi) - { - // Try to erase this lhs key from the set of rhs keys. If rhs has - // no such key, the maps are unequal. erase(key) returns count of - // items erased. - if (rhskeys.erase(lmi->first) != 1) - return false; - // Both maps have the current key. Compare values. - if (! llsd_equals(lmi->second, rhs[lmi->first], bits)) - return false; - } - // We've now established that all the lhs keys have equal values in - // both maps. The maps are equal unless rhs contains a superset of - // those keys. - return rhskeys.empty(); - } - - default: - // We expect that every possible type() value is specifically handled - // above. Failing to extend this switch to support a new LLSD type is - // an error that must be brought to the coder's attention. - LL_ERRS("llsd_equals") << "llsd_equals(" << lhs << ", " << rhs << ", " << bits << "): " - "unknown type " << lhs.type() << LL_ENDL; - return false; // pacify the compiler - } -} - -/***************************************************************************** -* llsd::drill() -*****************************************************************************/ -namespace llsd -{ - -LLSD& drill_ref(LLSD& blob, const LLSD& rawPath) -{ - LL_PROFILE_ZONE_SCOPED - - // Treat rawPath uniformly as an array. If it's not already an array, - // store it as the only entry in one. (But let's say Undefined means an - // empty array.) - LLSD path; - if (rawPath.isArray() || rawPath.isUndefined()) - { - path = rawPath; - } - else - { - path.append(rawPath); - } - - // Need to indicate a current destination -- but that current destination - // must change as we step through the path array. Where normally we'd use - // an LLSD& to capture a subscripted LLSD lvalue, this time we must - // instead use a pointer -- since it must be reassigned. - // Start by pointing to the input blob exactly as is. - LLSD* located{&blob}; - - // Extract the element of interest by walking path. Use an explicit index - // so that, in case of a bogus type in path, we can identify the specific - // path entry that's bad. - for (LLSD::Integer i = 0; i < path.size(); ++i) - { - LL_PROFILE_ZONE_NUM( i ) - - const LLSD& key{path[i]}; - if (key.isString()) - { - // a string path element is a map key - located = &((*located)[key.asString()]); - } - else if (key.isInteger()) - { - // an integer path element is an array index - located = &((*located)[key.asInteger()]); - } - else - { - // What do we do with Real or Array or Map or ...? - // As it's a coder error -- not a user error -- rub the coder's - // face in it so it gets fixed. - LL_ERRS("llsdutil") << "drill(" << blob << ", " << rawPath - << "): path[" << i << "] bad type " - << sTypes.lookup(key.type()) << LL_ENDL; - } - } - - // dereference the pointer to return a reference to the element we found - return *located; -} - -LLSD drill(const LLSD& blob, const LLSD& path) -{ - LL_PROFILE_ZONE_SCOPED - - // drill_ref() does exactly what we want. Temporarily cast away - // const-ness and use that. - return drill_ref(const_cast<LLSD&>(blob), path); -} - -} // namespace llsd - -// Construct a deep partial clone of of an LLSD object. primitive types share -// references, however maps, arrays and binary objects are duplicated. An optional -// filter may be include to exclude/include keys in a map. -LLSD llsd_clone(LLSD value, LLSD filter) -{ - LL_PROFILE_ZONE_SCOPED - - LLSD clone; - bool has_filter(filter.isMap()); - - switch (value.type()) - { - case LLSD::TypeMap: - clone = LLSD::emptyMap(); - for (LLSD::map_const_iterator itm = value.beginMap(); itm != value.endMap(); ++itm) - { - if (has_filter) - { - if (filter.has((*itm).first)) - { - if (!filter[(*itm).first].asBoolean()) - continue; - } - else if (filter.has("*")) - { - if (!filter["*"].asBoolean()) - continue; - } - else - { - continue; - } - } - clone[(*itm).first] = llsd_clone((*itm).second, filter); - } - break; - case LLSD::TypeArray: - clone = LLSD::emptyArray(); - for (LLSD::array_const_iterator ita = value.beginArray(); ita != value.endArray(); ++ita) - { - clone.append(llsd_clone(*ita, filter)); - } - break; - - case LLSD::TypeBinary: - { - LLSD::Binary bin(value.asBinary().begin(), value.asBinary().end()); - clone = LLSD::Binary(bin); - break; - } - default: - clone = value; - } - - return clone; -} - -LLSD llsd_shallow(LLSD value, LLSD filter) -{ - LLSD shallow; - bool has_filter(filter.isMap()); - - if (value.isMap()) - { - shallow = LLSD::emptyMap(); - for (LLSD::map_const_iterator itm = value.beginMap(); itm != value.endMap(); ++itm) - { - if (has_filter) - { - if (filter.has((*itm).first)) - { - if (!filter[(*itm).first].asBoolean()) - continue; - } - else if (filter.has("*")) - { - if (!filter["*"].asBoolean()) - continue; - } - else - { - continue; - } - } - shallow[(*itm).first] = (*itm).second; - } - } - else if (value.isArray()) - { - shallow = LLSD::emptyArray(); - for (LLSD::array_const_iterator ita = value.beginArray(); ita != value.endArray(); ++ita) - { - shallow.append(*ita); - } - } - else - { - return value; - } - - return shallow; -} - -LLSD LL::apply_llsd_fix(size_t arity, const LLSD& args) -{ - // LLSD supports a number of types, two of which are aggregates: Map and - // Array. We don't try to support Map: supporting Map would seem to - // promise that we could somehow match the string key to 'func's parameter - // names. Uh sorry, maybe in some future version of C++ with reflection. - if (args.isMap()) - { - LLTHROW(LL::apply_error("LL::apply(function, Map LLSD) unsupported")); - } - // We expect an LLSD array, but what the heck, treat isUndefined() as a - // zero-length array for calling a nullary 'func'. - if (args.isUndefined() || args.isArray()) - { - // this works because LLSD().size() == 0 - if (args.size() != arity) - { - LLTHROW(LL::apply_error(stringize("LL::apply(function(", arity, " args), ", - args.size(), "-entry LLSD array)"))); - } - return args; - } - - // args is one of the scalar types - // scalar_LLSD.size() == 0, so don't test that here. - // You can pass a scalar LLSD only to a unary 'func'. - if (arity != 1) - { - LLTHROW(LL::apply_error(stringize("LL::apply(function(", arity, " args), " - "LLSD ", LLSD::typeString(args.type()), ")"))); - } - // make an array of it - return llsd::array(args); -} +/**
+ * @file llsdutil.cpp
+ * @author Phoenix
+ * @date 2006-05-24
+ * @brief Implementation of classes, functions, etc, for using structured data.
+ *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llsdutil.h"
+#include <sstream>
+
+#if LL_WINDOWS
+# define WIN32_LEAN_AND_MEAN
+# include <winsock2.h> // for htonl
+#elif LL_LINUX
+# include <netinet/in.h>
+#elif LL_DARWIN
+# include <arpa/inet.h>
+#endif
+
+#include "llsdserialize.h"
+#include "stringize.h"
+#include "is_approx_equal_fraction.h"
+
+#include <map>
+#include <set>
+#include <boost/range.hpp>
+
+// U32
+LLSD ll_sd_from_U32(const U32 val)
+{
+ std::vector<U8> v;
+ U32 net_order = htonl(val);
+
+ v.resize(4);
+ memcpy(&(v[0]), &net_order, 4); /* Flawfinder: ignore */
+
+ return LLSD(v);
+}
+
+U32 ll_U32_from_sd(const LLSD& sd)
+{
+ U32 ret;
+ std::vector<U8> v = sd.asBinary();
+ if (v.size() < 4)
+ {
+ return 0;
+ }
+ memcpy(&ret, &(v[0]), 4); /* Flawfinder: ignore */
+ ret = ntohl(ret);
+ return ret;
+}
+
+//U64
+LLSD ll_sd_from_U64(const U64 val)
+{
+ std::vector<U8> v;
+ U32 high, low;
+
+ high = (U32)(val >> 32);
+ low = (U32)val;
+ high = htonl(high);
+ low = htonl(low);
+
+ v.resize(8);
+ memcpy(&(v[0]), &high, 4); /* Flawfinder: ignore */
+ memcpy(&(v[4]), &low, 4); /* Flawfinder: ignore */
+
+ return LLSD(v);
+}
+
+U64 ll_U64_from_sd(const LLSD& sd)
+{
+ U32 high, low;
+ std::vector<U8> v = sd.asBinary();
+
+ if (v.size() < 8)
+ {
+ return 0;
+ }
+
+ memcpy(&high, &(v[0]), 4); /* Flawfinder: ignore */
+ memcpy(&low, &(v[4]), 4); /* Flawfinder: ignore */
+ high = ntohl(high);
+ low = ntohl(low);
+
+ return ((U64)high) << 32 | low;
+}
+
+// IP Address (stored in net order in a U32, so don't need swizzling)
+LLSD ll_sd_from_ipaddr(const U32 val)
+{
+ std::vector<U8> v;
+
+ v.resize(4);
+ memcpy(&(v[0]), &val, 4); /* Flawfinder: ignore */
+
+ return LLSD(v);
+}
+
+U32 ll_ipaddr_from_sd(const LLSD& sd)
+{
+ U32 ret;
+ std::vector<U8> v = sd.asBinary();
+ if (v.size() < 4)
+ {
+ return 0;
+ }
+ memcpy(&ret, &(v[0]), 4); /* Flawfinder: ignore */
+ return ret;
+}
+
+// Converts an LLSD binary to an LLSD string
+LLSD ll_string_from_binary(const LLSD& sd)
+{
+ std::vector<U8> value = sd.asBinary();
+ std::string str;
+ str.resize(value.size());
+ memcpy(&str[0], &value[0], value.size());
+ return str;
+}
+
+// Converts an LLSD string to an LLSD binary
+LLSD ll_binary_from_string(const LLSD& sd)
+{
+ std::vector<U8> binary_value;
+
+ std::string string_value = sd.asString();
+ for (const U8 c : string_value)
+ {
+ binary_value.push_back(c);
+ }
+
+ binary_value.push_back('\0');
+
+ return binary_value;
+}
+
+char* ll_print_sd(const LLSD& sd)
+{
+ const U32 bufferSize = 10 * 1024;
+ static char buffer[bufferSize];
+ std::ostringstream stream;
+ //stream.rdbuf()->pubsetbuf(buffer, bufferSize);
+ stream << LLSDOStreamer<LLSDXMLFormatter>(sd);
+ stream << std::ends;
+ strncpy(buffer, stream.str().c_str(), bufferSize);
+ buffer[bufferSize - 1] = '\0';
+ return buffer;
+}
+
+char* ll_pretty_print_sd_ptr(const LLSD* sd)
+{
+ if (sd)
+ {
+ return ll_pretty_print_sd(*sd);
+ }
+ return NULL;
+}
+
+char* ll_pretty_print_sd(const LLSD& sd)
+{
+ const U32 bufferSize = 100 * 1024;
+ static char buffer[bufferSize];
+ std::ostringstream stream;
+ //stream.rdbuf()->pubsetbuf(buffer, bufferSize);
+ stream << LLSDOStreamer<LLSDXMLFormatter>(sd, LLSDFormatter::OPTIONS_PRETTY);
+ stream << std::ends;
+ strncpy(buffer, stream.str().c_str(), bufferSize);
+ buffer[bufferSize - 1] = '\0';
+ return buffer;
+}
+
+std::string ll_stream_notation_sd(const LLSD& sd)
+{
+ std::ostringstream stream;
+ stream << LLSDOStreamer<LLSDNotationFormatter>(sd);
+ return stream.str();
+}
+
+
+//compares the structure of an LLSD to a template LLSD and stores the
+//"valid" values in a 3rd LLSD. Default values pulled from the template
+//if the tested LLSD does not contain the key/value pair.
+//Excess values in the test LLSD are ignored in the resultant_llsd.
+//If the llsd to test has a specific key to a map and the values
+//are not of the same type, false is returned or if the LLSDs are not
+//of the same value. Ordering of arrays matters
+//Otherwise, returns true
+bool compare_llsd_with_template(
+ const LLSD& llsd_to_test,
+ const LLSD& template_llsd,
+ LLSD& resultant_llsd)
+{
+ LL_PROFILE_ZONE_SCOPED
+
+ if (
+ llsd_to_test.isUndefined() &&
+ template_llsd.isDefined() )
+ {
+ resultant_llsd = template_llsd;
+ return true;
+ }
+ else if ( llsd_to_test.type() != template_llsd.type() )
+ {
+ resultant_llsd = LLSD();
+ return false;
+ }
+
+ if ( llsd_to_test.isArray() )
+ {
+ //they are both arrays
+ //we loop over all the items in the template
+ //verifying that the to_test has a subset (in the same order)
+ //any shortcoming in the testing_llsd are just taken
+ //to be the rest of the template
+ LLSD data;
+ LLSD::array_const_iterator test_iter;
+ LLSD::array_const_iterator template_iter;
+
+ resultant_llsd = LLSD::emptyArray();
+ test_iter = llsd_to_test.beginArray();
+
+ for (
+ template_iter = template_llsd.beginArray();
+ (template_iter != template_llsd.endArray() &&
+ test_iter != llsd_to_test.endArray());
+ ++template_iter)
+ {
+ if ( !compare_llsd_with_template(
+ *test_iter,
+ *template_iter,
+ data) )
+ {
+ resultant_llsd = LLSD();
+ return false;
+ }
+ else
+ {
+ resultant_llsd.append(data);
+ }
+
+ ++test_iter;
+ }
+
+ //so either the test or the template ended
+ //we do another loop now to the end of the template
+ //grabbing the default values
+ for (;
+ template_iter != template_llsd.endArray();
+ ++template_iter)
+ {
+ resultant_llsd.append(*template_iter);
+ }
+ }
+ else if ( llsd_to_test.isMap() )
+ {
+ //now we loop over the keys of the two maps
+ //any excess is taken from the template
+ //excess is ignored in the test
+ LLSD value;
+ LLSD::map_const_iterator template_iter;
+
+ resultant_llsd = LLSD::emptyMap();
+ for (
+ template_iter = template_llsd.beginMap();
+ template_iter != template_llsd.endMap();
+ ++template_iter)
+ {
+ if ( llsd_to_test.has(template_iter->first) )
+ {
+ //the test LLSD has the same key
+ if ( !compare_llsd_with_template(
+ llsd_to_test[template_iter->first],
+ template_iter->second,
+ value) )
+ {
+ resultant_llsd = LLSD();
+ return false;
+ }
+ else
+ {
+ resultant_llsd[template_iter->first] = value;
+ }
+ }
+ else
+ {
+ //test llsd doesn't have it...take the
+ //template as default value
+ resultant_llsd[template_iter->first] =
+ template_iter->second;
+ }
+ }
+ }
+ else
+ {
+ //of same type...take the test llsd's value
+ resultant_llsd = llsd_to_test;
+ }
+
+
+ return true;
+}
+
+// filter_llsd_with_template() is a direct clone (copy-n-paste) of
+// compare_llsd_with_template with the following differences:
+// (1) bool vs BOOL return types
+// (2) A map with the key value "*" is a special value and maps any key in the
+// test llsd that doesn't have an explicitly matching key in the template.
+// (3) The element of an array with exactly one element is taken as a template
+// for *all* the elements of the test array. If the template array is of
+// different size, compare_llsd_with_template() semantics apply.
+bool filter_llsd_with_template(
+ const LLSD & llsd_to_test,
+ const LLSD & template_llsd,
+ LLSD & resultant_llsd)
+{
+ LL_PROFILE_ZONE_SCOPED
+
+ if (llsd_to_test.isUndefined() && template_llsd.isDefined())
+ {
+ resultant_llsd = template_llsd;
+ return true;
+ }
+ else if (llsd_to_test.type() != template_llsd.type())
+ {
+ resultant_llsd = LLSD();
+ return false;
+ }
+
+ if (llsd_to_test.isArray())
+ {
+ //they are both arrays
+ //we loop over all the items in the template
+ //verifying that the to_test has a subset (in the same order)
+ //any shortcoming in the testing_llsd are just taken
+ //to be the rest of the template
+ LLSD data;
+ LLSD::array_const_iterator test_iter;
+ LLSD::array_const_iterator template_iter;
+
+ resultant_llsd = LLSD::emptyArray();
+ test_iter = llsd_to_test.beginArray();
+
+ if (1 == template_llsd.size())
+ {
+ // If the template has a single item, treat it as
+ // the template for *all* items in the test LLSD.
+ template_iter = template_llsd.beginArray();
+
+ for (; test_iter != llsd_to_test.endArray(); ++test_iter)
+ {
+ if (! filter_llsd_with_template(*test_iter, *template_iter, data))
+ {
+ resultant_llsd = LLSD();
+ return false;
+ }
+ else
+ {
+ resultant_llsd.append(data);
+ }
+ }
+ }
+ else
+ {
+ // Traditional compare_llsd_with_template matching
+
+ for (template_iter = template_llsd.beginArray();
+ template_iter != template_llsd.endArray() &&
+ test_iter != llsd_to_test.endArray();
+ ++template_iter, ++test_iter)
+ {
+ if (! filter_llsd_with_template(*test_iter, *template_iter, data))
+ {
+ resultant_llsd = LLSD();
+ return false;
+ }
+ else
+ {
+ resultant_llsd.append(data);
+ }
+ }
+
+ //so either the test or the template ended
+ //we do another loop now to the end of the template
+ //grabbing the default values
+ for (;
+ template_iter != template_llsd.endArray();
+ ++template_iter)
+ {
+ resultant_llsd.append(*template_iter);
+ }
+ }
+ }
+ else if (llsd_to_test.isMap())
+ {
+ resultant_llsd = LLSD::emptyMap();
+
+ //now we loop over the keys of the two maps
+ //any excess is taken from the template
+ //excess is ignored in the test
+
+ // Special tag for wildcarded LLSD map key templates
+ const LLSD::String wildcard_tag("*");
+
+ const bool template_has_wildcard = template_llsd.has(wildcard_tag);
+ LLSD wildcard_value;
+ LLSD value;
+
+ const LLSD::map_const_iterator template_iter_end(template_llsd.endMap());
+ for (LLSD::map_const_iterator template_iter(template_llsd.beginMap());
+ template_iter_end != template_iter;
+ ++template_iter)
+ {
+ if (wildcard_tag == template_iter->first)
+ {
+ wildcard_value = template_iter->second;
+ }
+ else if (llsd_to_test.has(template_iter->first))
+ {
+ //the test LLSD has the same key
+ if (! filter_llsd_with_template(llsd_to_test[template_iter->first],
+ template_iter->second,
+ value))
+ {
+ resultant_llsd = LLSD();
+ return false;
+ }
+ else
+ {
+ resultant_llsd[template_iter->first] = value;
+ }
+ }
+ else if (! template_has_wildcard)
+ {
+ // test llsd doesn't have it...take the
+ // template as default value
+ resultant_llsd[template_iter->first] = template_iter->second;
+ }
+ }
+ if (template_has_wildcard)
+ {
+ LLSD sub_value;
+ LLSD::map_const_iterator test_iter;
+
+ for (test_iter = llsd_to_test.beginMap();
+ test_iter != llsd_to_test.endMap();
+ ++test_iter)
+ {
+ if (resultant_llsd.has(test_iter->first))
+ {
+ // Final value has test key, assume more specific
+ // template matched and we shouldn't modify it again.
+ continue;
+ }
+ else if (! filter_llsd_with_template(test_iter->second,
+ wildcard_value,
+ sub_value))
+ {
+ // Test value doesn't match wildcarded template
+ resultant_llsd = LLSD();
+ return false;
+ }
+ else
+ {
+ // Test value matches template, add the actuals.
+ resultant_llsd[test_iter->first] = sub_value;
+ }
+ }
+ }
+ }
+ else
+ {
+ //of same type...take the test llsd's value
+ resultant_llsd = llsd_to_test;
+ }
+
+ 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()
+ {
+ LL_PROFILE_ZONE_SCOPED
+
+ 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
+ {
+ LL_PROFILE_ZONE_SCOPED
+
+ 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
+{
+ LL_PROFILE_ZONE_SCOPED
+
+ // 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)
+{
+ LL_PROFILE_ZONE_SCOPED
+
+ // 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, int bits)
+{
+ LL_PROFILE_ZONE_SCOPED
+
+ // We're comparing strict equality of LLSD representation rather than
+ // performing any conversions. So if the types aren't equal, the LLSD
+ // values aren't equal.
+ if (lhs.type() != rhs.type())
+ {
+ return false;
+ }
+
+ // Here we know both types are equal. Now compare values.
+ switch (lhs.type())
+ {
+ case LLSD::TypeUndefined:
+ // Both are TypeUndefined. There's nothing more to know.
+ return true;
+
+ case LLSD::TypeReal:
+ // This is where the 'bits' argument comes in handy. If passed
+ // explicitly, it means to use is_approx_equal_fraction() to compare.
+ if (bits >= 0)
+ {
+ return is_approx_equal_fraction(lhs.asReal(), rhs.asReal(), bits);
+ }
+ // Otherwise we compare bit representations, and the usual caveats
+ // about comparing floating-point numbers apply. Omitting 'bits' when
+ // comparing Real values is only useful when we expect identical bit
+ // representation for a given Real value, e.g. for integer-valued
+ // Reals.
+ return (lhs.asReal() == rhs.asReal());
+
+#define COMPARE_SCALAR(type) \
+ case LLSD::Type##type: \
+ /* LLSD::URI has operator!=() but not operator==() */ \
+ /* rely on the optimizer for all others */ \
+ return (! (lhs.as##type() != rhs.as##type()))
+
+ COMPARE_SCALAR(Boolean);
+ COMPARE_SCALAR(Integer);
+ COMPARE_SCALAR(String);
+ COMPARE_SCALAR(UUID);
+ COMPARE_SCALAR(Date);
+ COMPARE_SCALAR(URI);
+ COMPARE_SCALAR(Binary);
+
+#undef COMPARE_SCALAR
+
+ case LLSD::TypeArray:
+ {
+ LLSD::array_const_iterator
+ lai(lhs.beginArray()), laend(lhs.endArray()),
+ rai(rhs.beginArray()), raend(rhs.endArray());
+ // Compare array elements, walking the two arrays in parallel.
+ for ( ; lai != laend && rai != raend; ++lai, ++rai)
+ {
+ // If any one array element is unequal, the arrays are unequal.
+ if (! llsd_equals(*lai, *rai, bits))
+ return false;
+ }
+ // Here we've reached the end of one or the other array. They're equal
+ // only if they're BOTH at end: that is, if they have equal length too.
+ return (lai == laend && rai == raend);
+ }
+
+ case LLSD::TypeMap:
+ {
+ // Build a set of all rhs keys.
+ std::set<LLSD::String> rhskeys;
+ for (LLSD::map_const_iterator rmi(rhs.beginMap()), rmend(rhs.endMap());
+ rmi != rmend; ++rmi)
+ {
+ rhskeys.insert(rmi->first);
+ }
+ // Now walk all the lhs keys.
+ for (LLSD::map_const_iterator lmi(lhs.beginMap()), lmend(lhs.endMap());
+ lmi != lmend; ++lmi)
+ {
+ // Try to erase this lhs key from the set of rhs keys. If rhs has
+ // no such key, the maps are unequal. erase(key) returns count of
+ // items erased.
+ if (rhskeys.erase(lmi->first) != 1)
+ return false;
+ // Both maps have the current key. Compare values.
+ if (! llsd_equals(lmi->second, rhs[lmi->first], bits))
+ return false;
+ }
+ // We've now established that all the lhs keys have equal values in
+ // both maps. The maps are equal unless rhs contains a superset of
+ // those keys.
+ return rhskeys.empty();
+ }
+
+ default:
+ // We expect that every possible type() value is specifically handled
+ // above. Failing to extend this switch to support a new LLSD type is
+ // an error that must be brought to the coder's attention.
+ LL_ERRS("llsd_equals") << "llsd_equals(" << lhs << ", " << rhs << ", " << bits << "): "
+ "unknown type " << lhs.type() << LL_ENDL;
+ return false; // pacify the compiler
+ }
+}
+
+/*****************************************************************************
+* llsd::drill()
+*****************************************************************************/
+namespace llsd
+{
+
+LLSD& drill_ref(LLSD& blob, const LLSD& rawPath)
+{
+ LL_PROFILE_ZONE_SCOPED
+
+ // Treat rawPath uniformly as an array. If it's not already an array,
+ // store it as the only entry in one. (But let's say Undefined means an
+ // empty array.)
+ LLSD path;
+ if (rawPath.isArray() || rawPath.isUndefined())
+ {
+ path = rawPath;
+ }
+ else
+ {
+ path.append(rawPath);
+ }
+
+ // Need to indicate a current destination -- but that current destination
+ // must change as we step through the path array. Where normally we'd use
+ // an LLSD& to capture a subscripted LLSD lvalue, this time we must
+ // instead use a pointer -- since it must be reassigned.
+ // Start by pointing to the input blob exactly as is.
+ LLSD* located{&blob};
+
+ // Extract the element of interest by walking path. Use an explicit index
+ // so that, in case of a bogus type in path, we can identify the specific
+ // path entry that's bad.
+ for (LLSD::Integer i = 0; i < path.size(); ++i)
+ {
+ LL_PROFILE_ZONE_NUM( i )
+
+ const LLSD& key{path[i]};
+ if (key.isString())
+ {
+ // a string path element is a map key
+ located = &((*located)[key.asString()]);
+ }
+ else if (key.isInteger())
+ {
+ // an integer path element is an array index
+ located = &((*located)[key.asInteger()]);
+ }
+ else
+ {
+ // What do we do with Real or Array or Map or ...?
+ // As it's a coder error -- not a user error -- rub the coder's
+ // face in it so it gets fixed.
+ LL_ERRS("llsdutil") << "drill(" << blob << ", " << rawPath
+ << "): path[" << i << "] bad type "
+ << sTypes.lookup(key.type()) << LL_ENDL;
+ }
+ }
+
+ // dereference the pointer to return a reference to the element we found
+ return *located;
+}
+
+LLSD drill(const LLSD& blob, const LLSD& path)
+{
+ LL_PROFILE_ZONE_SCOPED
+
+ // drill_ref() does exactly what we want. Temporarily cast away
+ // const-ness and use that.
+ return drill_ref(const_cast<LLSD&>(blob), path);
+}
+
+} // namespace llsd
+
+// Construct a deep partial clone of of an LLSD object. primitive types share
+// references, however maps, arrays and binary objects are duplicated. An optional
+// filter may be include to exclude/include keys in a map.
+LLSD llsd_clone(LLSD value, LLSD filter)
+{
+ LL_PROFILE_ZONE_SCOPED
+
+ LLSD clone;
+ bool has_filter(filter.isMap());
+
+ switch (value.type())
+ {
+ case LLSD::TypeMap:
+ clone = LLSD::emptyMap();
+ for (LLSD::map_const_iterator itm = value.beginMap(); itm != value.endMap(); ++itm)
+ {
+ if (has_filter)
+ {
+ if (filter.has((*itm).first))
+ {
+ if (!filter[(*itm).first].asBoolean())
+ continue;
+ }
+ else if (filter.has("*"))
+ {
+ if (!filter["*"].asBoolean())
+ continue;
+ }
+ else
+ {
+ continue;
+ }
+ }
+ clone[(*itm).first] = llsd_clone((*itm).second, filter);
+ }
+ break;
+ case LLSD::TypeArray:
+ clone = LLSD::emptyArray();
+ for (LLSD::array_const_iterator ita = value.beginArray(); ita != value.endArray(); ++ita)
+ {
+ clone.append(llsd_clone(*ita, filter));
+ }
+ break;
+
+ case LLSD::TypeBinary:
+ {
+ LLSD::Binary bin(value.asBinary().begin(), value.asBinary().end());
+ clone = LLSD::Binary(bin);
+ break;
+ }
+ default:
+ clone = value;
+ }
+
+ return clone;
+}
+
+LLSD llsd_shallow(LLSD value, LLSD filter)
+{
+ LLSD shallow;
+ bool has_filter(filter.isMap());
+
+ if (value.isMap())
+ {
+ shallow = LLSD::emptyMap();
+ for (LLSD::map_const_iterator itm = value.beginMap(); itm != value.endMap(); ++itm)
+ {
+ if (has_filter)
+ {
+ if (filter.has((*itm).first))
+ {
+ if (!filter[(*itm).first].asBoolean())
+ continue;
+ }
+ else if (filter.has("*"))
+ {
+ if (!filter["*"].asBoolean())
+ continue;
+ }
+ else
+ {
+ continue;
+ }
+ }
+ shallow[(*itm).first] = (*itm).second;
+ }
+ }
+ else if (value.isArray())
+ {
+ shallow = LLSD::emptyArray();
+ for (LLSD::array_const_iterator ita = value.beginArray(); ita != value.endArray(); ++ita)
+ {
+ shallow.append(*ita);
+ }
+ }
+ else
+ {
+ return value;
+ }
+
+ return shallow;
+}
+
+LLSD LL::apply_llsd_fix(size_t arity, const LLSD& args)
+{
+ // LLSD supports a number of types, two of which are aggregates: Map and
+ // Array. We don't try to support Map: supporting Map would seem to
+ // promise that we could somehow match the string key to 'func's parameter
+ // names. Uh sorry, maybe in some future version of C++ with reflection.
+ if (args.isMap())
+ {
+ LLTHROW(LL::apply_error("LL::apply(function, Map LLSD) unsupported"));
+ }
+ // We expect an LLSD array, but what the heck, treat isUndefined() as a
+ // zero-length array for calling a nullary 'func'.
+ if (args.isUndefined() || args.isArray())
+ {
+ // this works because LLSD().size() == 0
+ if (args.size() != arity)
+ {
+ LLTHROW(LL::apply_error(stringize("LL::apply(function(", arity, " args), ",
+ args.size(), "-entry LLSD array)")));
+ }
+ return args;
+ }
+
+ // args is one of the scalar types
+ // scalar_LLSD.size() == 0, so don't test that here.
+ // You can pass a scalar LLSD only to a unary 'func'.
+ if (arity != 1)
+ {
+ LLTHROW(LL::apply_error(stringize("LL::apply(function(", arity, " args), "
+ "LLSD ", LLSD::typeString(args.type()), ")")));
+ }
+ // make an array of it
+ return llsd::array(args);
+}
diff --git a/indra/llcommon/llsdutil.h b/indra/llcommon/llsdutil.h index b7afa59d79..aa234e2f62 100644 --- a/indra/llcommon/llsdutil.h +++ b/indra/llcommon/llsdutil.h @@ -1,674 +1,674 @@ -/** - * @file llsdutil.h - * @author Phoenix - * @date 2006-05-24 - * @brief Utility classes, functions, etc, for using structured data. - * - * $LicenseInfo:firstyear=2006&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLSDUTIL_H -#define LL_LLSDUTIL_H - -#include "apply.h" // LL::invoke() -#include "function_types.h" // LL::function_arity -#include "llsd.h" -#include <boost/functional/hash.hpp> -#include <cassert> -#include <memory> // std::shared_ptr -#include <type_traits> -#include <vector> - -// U32 -LL_COMMON_API LLSD ll_sd_from_U32(const U32); -LL_COMMON_API U32 ll_U32_from_sd(const LLSD& sd); - -// U64 -LL_COMMON_API LLSD ll_sd_from_U64(const U64); -LL_COMMON_API U64 ll_U64_from_sd(const LLSD& sd); - -// IP Address -LL_COMMON_API LLSD ll_sd_from_ipaddr(const U32); -LL_COMMON_API U32 ll_ipaddr_from_sd(const LLSD& sd); - -// Binary to string -LL_COMMON_API LLSD ll_string_from_binary(const LLSD& sd); - -//String to binary -LL_COMMON_API LLSD ll_binary_from_string(const LLSD& sd); - -// Serializes sd to static buffer and returns pointer, useful for gdb debugging. -LL_COMMON_API char* ll_print_sd(const LLSD& sd); - -// Serializes sd to static buffer and returns pointer, using "pretty printing" mode. -LL_COMMON_API char* ll_pretty_print_sd_ptr(const LLSD* sd); -LL_COMMON_API char* ll_pretty_print_sd(const LLSD& sd); - -LL_COMMON_API std::string ll_stream_notation_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 -//are pulled from the template. Extra keys/values in the test -//are ignored in the resultant LLSD. Ordering of arrays matters -//Returns false if the test is of same type but values differ in type -//Otherwise, returns true - -LL_COMMON_API bool compare_llsd_with_template( - const LLSD& llsd_to_test, - const LLSD& template_llsd, - LLSD& resultant_llsd); - -// filter_llsd_with_template() is a direct clone (copy-n-paste) of -// compare_llsd_with_template with the following differences: -// (1) bool vs BOOL return types -// (2) A map with the key value "*" is a special value and maps any key in the -// test llsd that doesn't have an explicitly matching key in the template. -// (3) The element of an array with exactly one element is taken as a template -// for *all* the elements of the test array. If the template array is of -// different size, compare_llsd_with_template() semantics apply. -bool filter_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. If you want to compare LLSD::Real values for approximate -/// equality rather than bitwise equality, pass @a bits as for -/// is_approx_equal_fraction(). -LL_COMMON_API bool llsd_equals(const LLSD& lhs, const LLSD& rhs, int bits=-1); -/// If you don't care about LLSD::Real equality -inline bool operator==(const LLSD& lhs, const LLSD& rhs) -{ - return llsd_equals(lhs, rhs); -} -inline bool operator!=(const LLSD& lhs, const LLSD& rhs) -{ - // operator!=() should always be the negation of operator==() - return ! (lhs == 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) -{ - LLSD dest; - for (; iter != end; ++iter) - { - dest.append(*iter); - } - return dest; -} - -namespace llsd -{ - -/** - * Drill down to locate an element in 'blob' according to 'path', where 'path' - * is one of the following: - * - * - LLSD::String: 'blob' is an LLSD::Map. Find the entry with key 'path'. - * - LLSD::Integer: 'blob' is an LLSD::Array. Find the entry with index 'path'. - * - Any other 'path' type will be interpreted as LLSD::Array, and 'blob' is a - * nested structure. For each element of 'path': - * - If it's an LLSD::Integer, select the entry with that index from an - * LLSD::Array at that level. - * - If it's an LLSD::String, select the entry with that key from an - * LLSD::Map at that level. - * - Anything else is an error. - * - * By implication, if path.isUndefined() or otherwise equivalent to an empty - * LLSD::Array, drill[_ref]() returns 'blob' as is. - */ -LLSD drill(const LLSD& blob, const LLSD& path); -LLSD& drill_ref( LLSD& blob, const LLSD& path); - -} - -namespace llsd -{ - -/** - * Construct an LLSD::Array inline, using modern C++ variadic arguments. - */ - -// recursion tail -inline -void array_(LLSD&) {} - -// recursive call -template <typename T0, typename... Ts> -void array_(LLSD& data, T0&& v0, Ts&&... vs) -{ - data.append(std::forward<T0>(v0)); - array_(data, std::forward<Ts>(vs)...); -} - -// public interface -template <typename... Ts> -LLSD array(Ts&&... vs) -{ - LLSD data; - array_(data, std::forward<Ts>(vs)...); - return data; -} - -} // namespace llsd - -/***************************************************************************** -* LLSDMap -*****************************************************************************/ -/** - * Construct an LLSD::Map inline, with implicit conversion to LLSD. Usage: - * - * @code - * void somefunc(const LLSD&); - * ... - * somefunc(LLSDMap("alpha", "abc")("number", 17)("pi", 3.14)); - * @endcode - * - * For completeness, LLSDMap() with no args constructs an empty map, so - * <tt>LLSDMap()("alpha", "abc")("number", 17)("pi", 3.14)</tt> produces a map - * equivalent to the above. But for most purposes, LLSD() is already - * equivalent to an empty map, and if you explicitly want an empty isMap(), - * there's LLSD::emptyMap(). However, supporting a no-args LLSDMap() - * constructor follows the principle of least astonishment. - */ -class LLSDMap -{ -public: - LLSDMap(): - _data(LLSD::emptyMap()) - {} - LLSDMap(const LLSD::String& key, const LLSD& value): - _data(LLSD::emptyMap()) - { - _data[key] = value; - } - - LLSDMap& operator()(const LLSD::String& key, const LLSD& value) - { - _data[key] = value; - return *this; - } - - operator LLSD() const { return _data; } - LLSD get() const { return _data; } - -private: - LLSD _data; -}; - -namespace llsd -{ - -/** - * Construct an LLSD::Map inline, using modern C++ variadic arguments. - */ - -// recursion tail -inline -void map_(LLSD&) {} - -// recursive call -template <typename T0, typename... Ts> -void map_(LLSD& data, const LLSD::String& k0, T0&& v0, Ts&&... vs) -{ - data[k0] = v0; - map_(data, std::forward<Ts>(vs)...); -} - -// public interface -template <typename... Ts> -LLSD map(Ts&&... vs) -{ - LLSD data; - map_(data, std::forward<Ts>(vs)...); - return data; -} - -} // namespace llsd - -/***************************************************************************** -* LLSDParam -*****************************************************************************/ -struct LLSDParamBase -{ - virtual ~LLSDParamBase() {} -}; - -/** - * LLSDParam is a customization point for passing LLSD values to function - * parameters of more or less arbitrary type. LLSD provides a small set of - * native conversions; but if a generic algorithm explicitly constructs an - * LLSDParam object in the function's argument list, a consumer can provide - * LLSDParam specializations to support more different parameter types than - * LLSD's native conversions. - * - * Usage: - * - * @code - * void somefunc(const paramtype&); - * ... - * somefunc(..., LLSDParam<paramtype>(someLLSD), ...); - * @endcode - */ -template <typename T> -class LLSDParam: public LLSDParamBase -{ -public: - /** - * Default implementation converts to T on construction, saves converted - * value for later retrieval - */ - LLSDParam(const LLSD& value): - value_(value) - {} - - operator T() const { return value_; } - -private: - T value_; -}; - -/** - * LLSDParam<LLSD> is for when you don't already have the target parameter - * type in hand. Instantiate LLSDParam<LLSD>(your LLSD object), and the - * templated conversion operator will try to select a more specific LLSDParam - * specialization. - */ -template <> -class LLSDParam<LLSD>: public LLSDParamBase -{ -private: - LLSD value_; - // LLSDParam<LLSD>::operator T() works by instantiating an LLSDParam<T> on - // demand. Returning that engages LLSDParam<T>::operator T(), producing - // the desired result. But LLSDParam<const char*> owns a std::string whose - // c_str() is returned by its operator const char*(). If we return a temp - // LLSDParam<const char*>, the compiler can destroy it right away, as soon - // as we've called operator const char*(). That's a problem! That - // invalidates the const char* we've just passed to the subject function. - // This LLSDParam<LLSD> is presumably guaranteed to survive until the - // subject function has returned, so we must ensure that any constructed - // LLSDParam<T> lives just as long as this LLSDParam<LLSD> does. Putting - // each LLSDParam<T> on the heap and capturing a smart pointer in a vector - // works. We would have liked to use std::unique_ptr, but vector entries - // must be copyable. - // (Alternatively we could assume that every instance of LLSDParam<LLSD> - // will be asked for at most ONE conversion. We could store a scalar - // std::unique_ptr and, when constructing an new LLSDParam<T>, assert that - // the unique_ptr is empty. But some future change in usage patterns, and - // consequent failure of that assertion, would be very mysterious. Instead - // of explaining how to fix it, just fix it now.) - mutable std::vector<std::shared_ptr<LLSDParamBase>> converters_; - -public: - LLSDParam(const LLSD& value): value_(value) {} - - /// if we're literally being asked for an LLSD parameter, avoid infinite - /// recursion - operator LLSD() const { return value_; } - - /// otherwise, instantiate a more specific LLSDParam<T> to convert; that - /// preserves the existing customization mechanism - template <typename T> - operator T() const - { - // capture 'ptr' with the specific subclass type because converters_ - // only stores LLSDParamBase pointers - auto ptr{ std::make_shared<LLSDParam<std::decay_t<T>>>(value_) }; - // keep the new converter alive until we ourselves are destroyed - converters_.push_back(ptr); - return *ptr; - } -}; - -/** - * Turns out that several target types could accept an LLSD param using any of - * a few different conversions, e.g. LLUUID's constructor can accept LLUUID or - * std::string. Therefore, the compiler can't decide which LLSD conversion - * operator to choose, even though to us it seems obvious. But that's okay, we - * can specialize LLSDParam for such target types, explicitly specifying the - * desired conversion -- that's part of what LLSDParam is all about. Turns out - * we have to do that enough to make it worthwhile generalizing. Use a macro - * because I need to specify one of the asReal, etc., explicit conversion - * methods as well as a type. If I'm overlooking a clever way to implement - * that using a template instead, feel free to reimplement. - */ -#define LLSDParam_for(T, AS) \ -template <> \ -class LLSDParam<T>: public LLSDParamBase \ -{ \ -public: \ - LLSDParam(const LLSD& value): \ - value_((T)value.AS()) \ - {} \ - \ - operator T() const { return value_; } \ - \ -private: \ - T value_; \ -} - -LLSDParam_for(float, asReal); -LLSDParam_for(LLUUID, asUUID); -LLSDParam_for(LLDate, asDate); -LLSDParam_for(LLURI, asURI); -LLSDParam_for(LLSD::Binary, asBinary); - -/** - * LLSDParam<const char*> is an example of the kind of conversion you can - * support with LLSDParam beyond native LLSD conversions. Normally you can't - * pass an LLSD object to a function accepting const char* -- but you can - * safely pass an LLSDParam<const char*>(yourLLSD). - */ -template <> -class LLSDParam<const char*>: public LLSDParamBase -{ -private: - // The difference here is that we store a std::string rather than a const - // char*. It's important that the LLSDParam object own the std::string. - std::string value_; - // We don't bother storing the incoming LLSD object, but we do have to - // distinguish whether value_ is an empty string because the LLSD object - // contains an empty string or because it's isUndefined(). - bool undefined_; - -public: - LLSDParam(const LLSD& value): - value_(value), - undefined_(value.isUndefined()) - {} - - // The const char* we retrieve is for storage owned by our value_ member. - // That's how we guarantee that the const char* is valid for the lifetime - // of this LLSDParam object. Constructing your LLSDParam in the argument - // list should ensure that the LLSDParam object will persist for the - // duration of the function call. - operator const char*() const - { - if (undefined_) - { - // By default, an isUndefined() LLSD object's asString() method - // will produce an empty string. But for a function accepting - // const char*, it's often important to be able to pass NULL, and - // isUndefined() seems like the best way. If you want to pass an - // empty string, you can still pass LLSD(""). Without this special - // case, though, no LLSD value could pass NULL. - return NULL; - } - return value_.c_str(); - } -}; - -namespace llsd -{ - -/***************************************************************************** -* range-based for-loop helpers for LLSD -*****************************************************************************/ -/// Usage: for (LLSD item : inArray(someLLSDarray)) { ... } -class inArray -{ -public: - inArray(const LLSD& array): - _array(array) - {} - - typedef LLSD::array_const_iterator const_iterator; - typedef LLSD::array_iterator iterator; - - iterator begin() { return _array.beginArray(); } - iterator end() { return _array.endArray(); } - const_iterator begin() const { return _array.beginArray(); } - const_iterator end() const { return _array.endArray(); } - -private: - LLSD _array; -}; - -/// MapEntry is what you get from dereferencing an LLSD::map_[const_]iterator. -typedef std::map<LLSD::String, LLSD>::value_type MapEntry; - -/// Usage: for([const] MapEntry& e : inMap(someLLSDmap)) { ... } -class inMap -{ -public: - inMap(const LLSD& map): - _map(map) - {} - - typedef LLSD::map_const_iterator const_iterator; - typedef LLSD::map_iterator iterator; - - iterator begin() { return _map.beginMap(); } - iterator end() { return _map.endMap(); } - const_iterator begin() const { return _map.beginMap(); } - const_iterator end() const { return _map.endMap(); } - -private: - LLSD _map; -}; - -} // namespace llsd - - -// Creates a deep clone of an LLSD object. Maps, Arrays and binary objects -// are duplicated, atomic primitives (Boolean, Integer, Real, etc) simply -// use a shared reference. -// Optionally a filter may be specified to control what is duplicated. The -// map takes the form "keyname/boolean". -// If the value is true the value will be duplicated otherwise it will be skipped -// when encountered in a map. A key name of "*" can be specified as a wild card -// and will specify the default behavior. If no wild card is given and the clone -// encounters a name not in the filter, that value will be skipped. -LLSD llsd_clone(LLSD value, LLSD filter = LLSD()); - -// Creates a shallow copy of a map or array. If passed any other type of LLSD -// object it simply returns that value. See llsd_clone for a description of -// the filter parameter. -LLSD llsd_shallow(LLSD value, LLSD filter = LLSD()); - -namespace llsd -{ - -// llsd namespace aliases -inline -LLSD clone (LLSD value, LLSD filter=LLSD()) { return llsd_clone (value, filter); } -inline -LLSD shallow(LLSD value, LLSD filter=LLSD()) { return llsd_shallow(value, filter); } - -} // namespace llsd - -// Specialization for generating a hash value from an LLSD block. -namespace boost -{ -template <> -struct hash<LLSD> -{ - typedef LLSD argument_type; - typedef std::size_t result_type; - result_type operator()(argument_type const& s) const - { - result_type seed(0); - - LLSD::Type stype = s.type(); - boost::hash_combine(seed, (S32)stype); - - switch (stype) - { - case LLSD::TypeBoolean: - boost::hash_combine(seed, s.asBoolean()); - break; - case LLSD::TypeInteger: - boost::hash_combine(seed, s.asInteger()); - break; - case LLSD::TypeReal: - boost::hash_combine(seed, s.asReal()); - break; - case LLSD::TypeURI: - case LLSD::TypeString: - boost::hash_combine(seed, s.asString()); - break; - case LLSD::TypeUUID: - boost::hash_combine(seed, s.asUUID()); - break; - case LLSD::TypeDate: - boost::hash_combine(seed, s.asDate().secondsSinceEpoch()); - break; - case LLSD::TypeBinary: - { - const LLSD::Binary &b(s.asBinary()); - boost::hash_range(seed, b.begin(), b.end()); - break; - } - case LLSD::TypeMap: - { - for (LLSD::map_const_iterator itm = s.beginMap(); itm != s.endMap(); ++itm) - { - boost::hash_combine(seed, (*itm).first); - boost::hash_combine(seed, (*itm).second); - } - break; - } - case LLSD::TypeArray: - for (LLSD::array_const_iterator ita = s.beginArray(); ita != s.endArray(); ++ita) - { - boost::hash_combine(seed, (*ita)); - } - break; - case LLSD::TypeUndefined: - default: - break; - } - - return seed; - } -}; -} - -namespace LL -{ - -/***************************************************************************** -* apply(function, LLSD array) -*****************************************************************************/ -// validate incoming LLSD blob, and return an LLSD array suitable to pass to -// the function of interest -LLSD apply_llsd_fix(size_t arity, const LLSD& args); - -// Derived from https://stackoverflow.com/a/20441189 -// and https://en.cppreference.com/w/cpp/utility/apply . -// We can't simply make a tuple from the LLSD array and then apply() that -// tuple to the function -- how would make_tuple() deduce the correct -// parameter type for each entry? We must go directly to the target function. -template <typename CALLABLE, std::size_t... I> -auto apply_impl(CALLABLE&& func, const LLSD& array, std::index_sequence<I...>) -{ - // call func(unpacked args), using generic LLSDParam<LLSD> to convert each - // entry in 'array' to the target parameter type - return std::forward<CALLABLE>(func)(LLSDParam<LLSD>(array[I])...); -} - -// use apply_n<ARITY>(function, LLSD) to call a specific arity of a variadic -// function with (that many) items from the passed LLSD array -template <size_t ARITY, typename CALLABLE> -auto apply_n(CALLABLE&& func, const LLSD& args) -{ - return apply_impl(std::forward<CALLABLE>(func), - apply_llsd_fix(ARITY, args), - std::make_index_sequence<ARITY>()); -} - -/** - * apply(function, LLSD) goes beyond C++17 std::apply(). For this case - * @a function @emph cannot be variadic: the compiler must know at compile - * time how many arguments to pass. This isn't Python. (But see apply_n() to - * pass a specific number of args to a variadic function.) - */ -template <typename CALLABLE> -auto apply(CALLABLE&& func, const LLSD& args) -{ - // infer arity from the definition of func - constexpr auto arity = function_arity< - typename std::remove_reference<CALLABLE>::type>::value; - // now that we have a compile-time arity, apply_n() works - return apply_n<arity>(std::forward<CALLABLE>(func), args); -} - -} // namespace LL - -#endif // LL_LLSDUTIL_H +/**
+ * @file llsdutil.h
+ * @author Phoenix
+ * @date 2006-05-24
+ * @brief Utility classes, functions, etc, for using structured data.
+ *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLSDUTIL_H
+#define LL_LLSDUTIL_H
+
+#include "apply.h" // LL::invoke()
+#include "function_types.h" // LL::function_arity
+#include "llsd.h"
+#include <boost/functional/hash.hpp>
+#include <cassert>
+#include <memory> // std::shared_ptr
+#include <type_traits>
+#include <vector>
+
+// U32
+LL_COMMON_API LLSD ll_sd_from_U32(const U32);
+LL_COMMON_API U32 ll_U32_from_sd(const LLSD& sd);
+
+// U64
+LL_COMMON_API LLSD ll_sd_from_U64(const U64);
+LL_COMMON_API U64 ll_U64_from_sd(const LLSD& sd);
+
+// IP Address
+LL_COMMON_API LLSD ll_sd_from_ipaddr(const U32);
+LL_COMMON_API U32 ll_ipaddr_from_sd(const LLSD& sd);
+
+// Binary to string
+LL_COMMON_API LLSD ll_string_from_binary(const LLSD& sd);
+
+//String to binary
+LL_COMMON_API LLSD ll_binary_from_string(const LLSD& sd);
+
+// Serializes sd to static buffer and returns pointer, useful for gdb debugging.
+LL_COMMON_API char* ll_print_sd(const LLSD& sd);
+
+// Serializes sd to static buffer and returns pointer, using "pretty printing" mode.
+LL_COMMON_API char* ll_pretty_print_sd_ptr(const LLSD* sd);
+LL_COMMON_API char* ll_pretty_print_sd(const LLSD& sd);
+
+LL_COMMON_API std::string ll_stream_notation_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
+//are pulled from the template. Extra keys/values in the test
+//are ignored in the resultant LLSD. Ordering of arrays matters
+//Returns false if the test is of same type but values differ in type
+//Otherwise, returns true
+
+LL_COMMON_API bool compare_llsd_with_template(
+ const LLSD& llsd_to_test,
+ const LLSD& template_llsd,
+ LLSD& resultant_llsd);
+
+// filter_llsd_with_template() is a direct clone (copy-n-paste) of
+// compare_llsd_with_template with the following differences:
+// (1) bool vs BOOL return types
+// (2) A map with the key value "*" is a special value and maps any key in the
+// test llsd that doesn't have an explicitly matching key in the template.
+// (3) The element of an array with exactly one element is taken as a template
+// for *all* the elements of the test array. If the template array is of
+// different size, compare_llsd_with_template() semantics apply.
+bool filter_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. If you want to compare LLSD::Real values for approximate
+/// equality rather than bitwise equality, pass @a bits as for
+/// is_approx_equal_fraction().
+LL_COMMON_API bool llsd_equals(const LLSD& lhs, const LLSD& rhs, int bits=-1);
+/// If you don't care about LLSD::Real equality
+inline bool operator==(const LLSD& lhs, const LLSD& rhs)
+{
+ return llsd_equals(lhs, rhs);
+}
+inline bool operator!=(const LLSD& lhs, const LLSD& rhs)
+{
+ // operator!=() should always be the negation of operator==()
+ return ! (lhs == 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)
+{
+ LLSD dest;
+ for (; iter != end; ++iter)
+ {
+ dest.append(*iter);
+ }
+ return dest;
+}
+
+namespace llsd
+{
+
+/**
+ * Drill down to locate an element in 'blob' according to 'path', where 'path'
+ * is one of the following:
+ *
+ * - LLSD::String: 'blob' is an LLSD::Map. Find the entry with key 'path'.
+ * - LLSD::Integer: 'blob' is an LLSD::Array. Find the entry with index 'path'.
+ * - Any other 'path' type will be interpreted as LLSD::Array, and 'blob' is a
+ * nested structure. For each element of 'path':
+ * - If it's an LLSD::Integer, select the entry with that index from an
+ * LLSD::Array at that level.
+ * - If it's an LLSD::String, select the entry with that key from an
+ * LLSD::Map at that level.
+ * - Anything else is an error.
+ *
+ * By implication, if path.isUndefined() or otherwise equivalent to an empty
+ * LLSD::Array, drill[_ref]() returns 'blob' as is.
+ */
+LLSD drill(const LLSD& blob, const LLSD& path);
+LLSD& drill_ref( LLSD& blob, const LLSD& path);
+
+}
+
+namespace llsd
+{
+
+/**
+ * Construct an LLSD::Array inline, using modern C++ variadic arguments.
+ */
+
+// recursion tail
+inline
+void array_(LLSD&) {}
+
+// recursive call
+template <typename T0, typename... Ts>
+void array_(LLSD& data, T0&& v0, Ts&&... vs)
+{
+ data.append(std::forward<T0>(v0));
+ array_(data, std::forward<Ts>(vs)...);
+}
+
+// public interface
+template <typename... Ts>
+LLSD array(Ts&&... vs)
+{
+ LLSD data;
+ array_(data, std::forward<Ts>(vs)...);
+ return data;
+}
+
+} // namespace llsd
+
+/*****************************************************************************
+* LLSDMap
+*****************************************************************************/
+/**
+ * Construct an LLSD::Map inline, with implicit conversion to LLSD. Usage:
+ *
+ * @code
+ * void somefunc(const LLSD&);
+ * ...
+ * somefunc(LLSDMap("alpha", "abc")("number", 17)("pi", 3.14));
+ * @endcode
+ *
+ * For completeness, LLSDMap() with no args constructs an empty map, so
+ * <tt>LLSDMap()("alpha", "abc")("number", 17)("pi", 3.14)</tt> produces a map
+ * equivalent to the above. But for most purposes, LLSD() is already
+ * equivalent to an empty map, and if you explicitly want an empty isMap(),
+ * there's LLSD::emptyMap(). However, supporting a no-args LLSDMap()
+ * constructor follows the principle of least astonishment.
+ */
+class LLSDMap
+{
+public:
+ LLSDMap():
+ _data(LLSD::emptyMap())
+ {}
+ LLSDMap(const LLSD::String& key, const LLSD& value):
+ _data(LLSD::emptyMap())
+ {
+ _data[key] = value;
+ }
+
+ LLSDMap& operator()(const LLSD::String& key, const LLSD& value)
+ {
+ _data[key] = value;
+ return *this;
+ }
+
+ operator LLSD() const { return _data; }
+ LLSD get() const { return _data; }
+
+private:
+ LLSD _data;
+};
+
+namespace llsd
+{
+
+/**
+ * Construct an LLSD::Map inline, using modern C++ variadic arguments.
+ */
+
+// recursion tail
+inline
+void map_(LLSD&) {}
+
+// recursive call
+template <typename T0, typename... Ts>
+void map_(LLSD& data, const LLSD::String& k0, T0&& v0, Ts&&... vs)
+{
+ data[k0] = v0;
+ map_(data, std::forward<Ts>(vs)...);
+}
+
+// public interface
+template <typename... Ts>
+LLSD map(Ts&&... vs)
+{
+ LLSD data;
+ map_(data, std::forward<Ts>(vs)...);
+ return data;
+}
+
+} // namespace llsd
+
+/*****************************************************************************
+* LLSDParam
+*****************************************************************************/
+struct LLSDParamBase
+{
+ virtual ~LLSDParamBase() {}
+};
+
+/**
+ * LLSDParam is a customization point for passing LLSD values to function
+ * parameters of more or less arbitrary type. LLSD provides a small set of
+ * native conversions; but if a generic algorithm explicitly constructs an
+ * LLSDParam object in the function's argument list, a consumer can provide
+ * LLSDParam specializations to support more different parameter types than
+ * LLSD's native conversions.
+ *
+ * Usage:
+ *
+ * @code
+ * void somefunc(const paramtype&);
+ * ...
+ * somefunc(..., LLSDParam<paramtype>(someLLSD), ...);
+ * @endcode
+ */
+template <typename T>
+class LLSDParam: public LLSDParamBase
+{
+public:
+ /**
+ * Default implementation converts to T on construction, saves converted
+ * value for later retrieval
+ */
+ LLSDParam(const LLSD& value):
+ value_(value)
+ {}
+
+ operator T() const { return value_; }
+
+private:
+ T value_;
+};
+
+/**
+ * LLSDParam<LLSD> is for when you don't already have the target parameter
+ * type in hand. Instantiate LLSDParam<LLSD>(your LLSD object), and the
+ * templated conversion operator will try to select a more specific LLSDParam
+ * specialization.
+ */
+template <>
+class LLSDParam<LLSD>: public LLSDParamBase
+{
+private:
+ LLSD value_;
+ // LLSDParam<LLSD>::operator T() works by instantiating an LLSDParam<T> on
+ // demand. Returning that engages LLSDParam<T>::operator T(), producing
+ // the desired result. But LLSDParam<const char*> owns a std::string whose
+ // c_str() is returned by its operator const char*(). If we return a temp
+ // LLSDParam<const char*>, the compiler can destroy it right away, as soon
+ // as we've called operator const char*(). That's a problem! That
+ // invalidates the const char* we've just passed to the subject function.
+ // This LLSDParam<LLSD> is presumably guaranteed to survive until the
+ // subject function has returned, so we must ensure that any constructed
+ // LLSDParam<T> lives just as long as this LLSDParam<LLSD> does. Putting
+ // each LLSDParam<T> on the heap and capturing a smart pointer in a vector
+ // works. We would have liked to use std::unique_ptr, but vector entries
+ // must be copyable.
+ // (Alternatively we could assume that every instance of LLSDParam<LLSD>
+ // will be asked for at most ONE conversion. We could store a scalar
+ // std::unique_ptr and, when constructing an new LLSDParam<T>, assert that
+ // the unique_ptr is empty. But some future change in usage patterns, and
+ // consequent failure of that assertion, would be very mysterious. Instead
+ // of explaining how to fix it, just fix it now.)
+ mutable std::vector<std::shared_ptr<LLSDParamBase>> converters_;
+
+public:
+ LLSDParam(const LLSD& value): value_(value) {}
+
+ /// if we're literally being asked for an LLSD parameter, avoid infinite
+ /// recursion
+ operator LLSD() const { return value_; }
+
+ /// otherwise, instantiate a more specific LLSDParam<T> to convert; that
+ /// preserves the existing customization mechanism
+ template <typename T>
+ operator T() const
+ {
+ // capture 'ptr' with the specific subclass type because converters_
+ // only stores LLSDParamBase pointers
+ auto ptr{ std::make_shared<LLSDParam<std::decay_t<T>>>(value_) };
+ // keep the new converter alive until we ourselves are destroyed
+ converters_.push_back(ptr);
+ return *ptr;
+ }
+};
+
+/**
+ * Turns out that several target types could accept an LLSD param using any of
+ * a few different conversions, e.g. LLUUID's constructor can accept LLUUID or
+ * std::string. Therefore, the compiler can't decide which LLSD conversion
+ * operator to choose, even though to us it seems obvious. But that's okay, we
+ * can specialize LLSDParam for such target types, explicitly specifying the
+ * desired conversion -- that's part of what LLSDParam is all about. Turns out
+ * we have to do that enough to make it worthwhile generalizing. Use a macro
+ * because I need to specify one of the asReal, etc., explicit conversion
+ * methods as well as a type. If I'm overlooking a clever way to implement
+ * that using a template instead, feel free to reimplement.
+ */
+#define LLSDParam_for(T, AS) \
+template <> \
+class LLSDParam<T>: public LLSDParamBase \
+{ \
+public: \
+ LLSDParam(const LLSD& value): \
+ value_((T)value.AS()) \
+ {} \
+ \
+ operator T() const { return value_; } \
+ \
+private: \
+ T value_; \
+}
+
+LLSDParam_for(float, asReal);
+LLSDParam_for(LLUUID, asUUID);
+LLSDParam_for(LLDate, asDate);
+LLSDParam_for(LLURI, asURI);
+LLSDParam_for(LLSD::Binary, asBinary);
+
+/**
+ * LLSDParam<const char*> is an example of the kind of conversion you can
+ * support with LLSDParam beyond native LLSD conversions. Normally you can't
+ * pass an LLSD object to a function accepting const char* -- but you can
+ * safely pass an LLSDParam<const char*>(yourLLSD).
+ */
+template <>
+class LLSDParam<const char*>: public LLSDParamBase
+{
+private:
+ // The difference here is that we store a std::string rather than a const
+ // char*. It's important that the LLSDParam object own the std::string.
+ std::string value_;
+ // We don't bother storing the incoming LLSD object, but we do have to
+ // distinguish whether value_ is an empty string because the LLSD object
+ // contains an empty string or because it's isUndefined().
+ bool undefined_;
+
+public:
+ LLSDParam(const LLSD& value):
+ value_(value),
+ undefined_(value.isUndefined())
+ {}
+
+ // The const char* we retrieve is for storage owned by our value_ member.
+ // That's how we guarantee that the const char* is valid for the lifetime
+ // of this LLSDParam object. Constructing your LLSDParam in the argument
+ // list should ensure that the LLSDParam object will persist for the
+ // duration of the function call.
+ operator const char*() const
+ {
+ if (undefined_)
+ {
+ // By default, an isUndefined() LLSD object's asString() method
+ // will produce an empty string. But for a function accepting
+ // const char*, it's often important to be able to pass NULL, and
+ // isUndefined() seems like the best way. If you want to pass an
+ // empty string, you can still pass LLSD(""). Without this special
+ // case, though, no LLSD value could pass NULL.
+ return NULL;
+ }
+ return value_.c_str();
+ }
+};
+
+namespace llsd
+{
+
+/*****************************************************************************
+* range-based for-loop helpers for LLSD
+*****************************************************************************/
+/// Usage: for (LLSD item : inArray(someLLSDarray)) { ... }
+class inArray
+{
+public:
+ inArray(const LLSD& array):
+ _array(array)
+ {}
+
+ typedef LLSD::array_const_iterator const_iterator;
+ typedef LLSD::array_iterator iterator;
+
+ iterator begin() { return _array.beginArray(); }
+ iterator end() { return _array.endArray(); }
+ const_iterator begin() const { return _array.beginArray(); }
+ const_iterator end() const { return _array.endArray(); }
+
+private:
+ LLSD _array;
+};
+
+/// MapEntry is what you get from dereferencing an LLSD::map_[const_]iterator.
+typedef std::map<LLSD::String, LLSD>::value_type MapEntry;
+
+/// Usage: for([const] MapEntry& e : inMap(someLLSDmap)) { ... }
+class inMap
+{
+public:
+ inMap(const LLSD& map):
+ _map(map)
+ {}
+
+ typedef LLSD::map_const_iterator const_iterator;
+ typedef LLSD::map_iterator iterator;
+
+ iterator begin() { return _map.beginMap(); }
+ iterator end() { return _map.endMap(); }
+ const_iterator begin() const { return _map.beginMap(); }
+ const_iterator end() const { return _map.endMap(); }
+
+private:
+ LLSD _map;
+};
+
+} // namespace llsd
+
+
+// Creates a deep clone of an LLSD object. Maps, Arrays and binary objects
+// are duplicated, atomic primitives (Boolean, Integer, Real, etc) simply
+// use a shared reference.
+// Optionally a filter may be specified to control what is duplicated. The
+// map takes the form "keyname/boolean".
+// If the value is true the value will be duplicated otherwise it will be skipped
+// when encountered in a map. A key name of "*" can be specified as a wild card
+// and will specify the default behavior. If no wild card is given and the clone
+// encounters a name not in the filter, that value will be skipped.
+LLSD llsd_clone(LLSD value, LLSD filter = LLSD());
+
+// Creates a shallow copy of a map or array. If passed any other type of LLSD
+// object it simply returns that value. See llsd_clone for a description of
+// the filter parameter.
+LLSD llsd_shallow(LLSD value, LLSD filter = LLSD());
+
+namespace llsd
+{
+
+// llsd namespace aliases
+inline
+LLSD clone (LLSD value, LLSD filter=LLSD()) { return llsd_clone (value, filter); }
+inline
+LLSD shallow(LLSD value, LLSD filter=LLSD()) { return llsd_shallow(value, filter); }
+
+} // namespace llsd
+
+// Specialization for generating a hash value from an LLSD block.
+namespace boost
+{
+template <>
+struct hash<LLSD>
+{
+ typedef LLSD argument_type;
+ typedef std::size_t result_type;
+ result_type operator()(argument_type const& s) const
+ {
+ result_type seed(0);
+
+ LLSD::Type stype = s.type();
+ boost::hash_combine(seed, (S32)stype);
+
+ switch (stype)
+ {
+ case LLSD::TypeBoolean:
+ boost::hash_combine(seed, s.asBoolean());
+ break;
+ case LLSD::TypeInteger:
+ boost::hash_combine(seed, s.asInteger());
+ break;
+ case LLSD::TypeReal:
+ boost::hash_combine(seed, s.asReal());
+ break;
+ case LLSD::TypeURI:
+ case LLSD::TypeString:
+ boost::hash_combine(seed, s.asString());
+ break;
+ case LLSD::TypeUUID:
+ boost::hash_combine(seed, s.asUUID());
+ break;
+ case LLSD::TypeDate:
+ boost::hash_combine(seed, s.asDate().secondsSinceEpoch());
+ break;
+ case LLSD::TypeBinary:
+ {
+ const LLSD::Binary &b(s.asBinary());
+ boost::hash_range(seed, b.begin(), b.end());
+ break;
+ }
+ case LLSD::TypeMap:
+ {
+ for (LLSD::map_const_iterator itm = s.beginMap(); itm != s.endMap(); ++itm)
+ {
+ boost::hash_combine(seed, (*itm).first);
+ boost::hash_combine(seed, (*itm).second);
+ }
+ break;
+ }
+ case LLSD::TypeArray:
+ for (LLSD::array_const_iterator ita = s.beginArray(); ita != s.endArray(); ++ita)
+ {
+ boost::hash_combine(seed, (*ita));
+ }
+ break;
+ case LLSD::TypeUndefined:
+ default:
+ break;
+ }
+
+ return seed;
+ }
+};
+}
+
+namespace LL
+{
+
+/*****************************************************************************
+* apply(function, LLSD array)
+*****************************************************************************/
+// validate incoming LLSD blob, and return an LLSD array suitable to pass to
+// the function of interest
+LLSD apply_llsd_fix(size_t arity, const LLSD& args);
+
+// Derived from https://stackoverflow.com/a/20441189
+// and https://en.cppreference.com/w/cpp/utility/apply .
+// We can't simply make a tuple from the LLSD array and then apply() that
+// tuple to the function -- how would make_tuple() deduce the correct
+// parameter type for each entry? We must go directly to the target function.
+template <typename CALLABLE, std::size_t... I>
+auto apply_impl(CALLABLE&& func, const LLSD& array, std::index_sequence<I...>)
+{
+ // call func(unpacked args), using generic LLSDParam<LLSD> to convert each
+ // entry in 'array' to the target parameter type
+ return std::forward<CALLABLE>(func)(LLSDParam<LLSD>(array[I])...);
+}
+
+// use apply_n<ARITY>(function, LLSD) to call a specific arity of a variadic
+// function with (that many) items from the passed LLSD array
+template <size_t ARITY, typename CALLABLE>
+auto apply_n(CALLABLE&& func, const LLSD& args)
+{
+ return apply_impl(std::forward<CALLABLE>(func),
+ apply_llsd_fix(ARITY, args),
+ std::make_index_sequence<ARITY>());
+}
+
+/**
+ * apply(function, LLSD) goes beyond C++17 std::apply(). For this case
+ * @a function @emph cannot be variadic: the compiler must know at compile
+ * time how many arguments to pass. This isn't Python. (But see apply_n() to
+ * pass a specific number of args to a variadic function.)
+ */
+template <typename CALLABLE>
+auto apply(CALLABLE&& func, const LLSD& args)
+{
+ // infer arity from the definition of func
+ constexpr auto arity = function_arity<
+ typename std::remove_reference<CALLABLE>::type>::value;
+ // now that we have a compile-time arity, apply_n() works
+ return apply_n<arity>(std::forward<CALLABLE>(func), args);
+}
+
+} // namespace LL
+
+#endif // LL_LLSDUTIL_H
diff --git a/indra/llcommon/llsimplehash.h b/indra/llcommon/llsimplehash.h index 727b4568d8..530d566466 100644 --- a/indra/llcommon/llsimplehash.h +++ b/indra/llcommon/llsimplehash.h @@ -1,24 +1,24 @@ -/** +/** * @file llsimplehash.h * * $LicenseInfo:firstyear=2003&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -32,124 +32,124 @@ template <typename HASH_KEY_TYPE> class LLSimpleHashEntry { protected: - HASH_KEY_TYPE mHashKey; - LLSimpleHashEntry<HASH_KEY_TYPE>* mNextEntry; + HASH_KEY_TYPE mHashKey; + LLSimpleHashEntry<HASH_KEY_TYPE>* mNextEntry; public: - LLSimpleHashEntry(HASH_KEY_TYPE key) : - mHashKey(key), - mNextEntry(0) - { - } - virtual ~LLSimpleHashEntry() - { - } - HASH_KEY_TYPE getHashKey() const - { - return mHashKey; - } - LLSimpleHashEntry<HASH_KEY_TYPE>* getNextEntry() const - { - return mNextEntry; - } - void setNextEntry(LLSimpleHashEntry<HASH_KEY_TYPE>* next) - { - mNextEntry = next; - } + LLSimpleHashEntry(HASH_KEY_TYPE key) : + mHashKey(key), + mNextEntry(0) + { + } + virtual ~LLSimpleHashEntry() + { + } + HASH_KEY_TYPE getHashKey() const + { + return mHashKey; + } + LLSimpleHashEntry<HASH_KEY_TYPE>* getNextEntry() const + { + return mNextEntry; + } + void setNextEntry(LLSimpleHashEntry<HASH_KEY_TYPE>* next) + { + mNextEntry = next; + } }; template <typename HASH_KEY_TYPE, int TABLE_SIZE> class LL_COMMON_API LLSimpleHash { public: - LLSimpleHash() - { - llassert(TABLE_SIZE); - llassert((TABLE_SIZE ^ (TABLE_SIZE-1)) == (TABLE_SIZE | (TABLE_SIZE-1))); // power of 2 - memset(mEntryTable, 0, sizeof(mEntryTable)); - } - virtual ~LLSimpleHash() - { - } + LLSimpleHash() + { + llassert(TABLE_SIZE); + llassert((TABLE_SIZE ^ (TABLE_SIZE-1)) == (TABLE_SIZE | (TABLE_SIZE-1))); // power of 2 + memset(mEntryTable, 0, sizeof(mEntryTable)); + } + virtual ~LLSimpleHash() + { + } + + virtual int getIndex(HASH_KEY_TYPE key) + { + return key & (TABLE_SIZE-1); + } + + bool insert(LLSimpleHashEntry<HASH_KEY_TYPE>* entry) + { + llassert(entry->getNextEntry() == 0); + int index = getIndex(entry->getHashKey()); + entry->setNextEntry(mEntryTable[index]); + mEntryTable[index] = entry; + return true; + } + LLSimpleHashEntry<HASH_KEY_TYPE>* find(HASH_KEY_TYPE key) + { + int index = getIndex(key); + LLSimpleHashEntry<HASH_KEY_TYPE>* res = mEntryTable[index]; + while(res && (res->getHashKey() != key)) + { + res = res->getNextEntry(); + } + return res; + } + bool erase(LLSimpleHashEntry<HASH_KEY_TYPE>* entry) + { + return erase(entry->getHashKey()); + } + bool erase(HASH_KEY_TYPE key) + { + int index = getIndex(key); + LLSimpleHashEntry<HASH_KEY_TYPE>* prev = 0; + LLSimpleHashEntry<HASH_KEY_TYPE>* res = mEntryTable[index]; + while(res && (res->getHashKey() != key)) + { + prev = res; + res = res->getNextEntry(); + } + if (res) + { + LLSimpleHashEntry<HASH_KEY_TYPE>* next = res->getNextEntry(); + if (prev) + { + prev->setNextEntry(next); + } + else + { + mEntryTable[index] = next; + } + return true; + } + else + { + return false; + } + } + // Removes and returns an arbitrary ("first") element from the table + // Used for deleting the entire table. + LLSimpleHashEntry<HASH_KEY_TYPE>* pop_element() + { + for (int i=0; i<TABLE_SIZE; i++) + { + LLSimpleHashEntry<HASH_KEY_TYPE>* entry = mEntryTable[i]; + if (entry) + { + mEntryTable[i] = entry->getNextEntry(); + return entry; + } + } + return 0; + } + // debugging + LLSimpleHashEntry<HASH_KEY_TYPE>* get_element_at_index(S32 index) const + { + return mEntryTable[index]; + } + - virtual int getIndex(HASH_KEY_TYPE key) - { - return key & (TABLE_SIZE-1); - } - - bool insert(LLSimpleHashEntry<HASH_KEY_TYPE>* entry) - { - llassert(entry->getNextEntry() == 0); - int index = getIndex(entry->getHashKey()); - entry->setNextEntry(mEntryTable[index]); - mEntryTable[index] = entry; - return true; - } - LLSimpleHashEntry<HASH_KEY_TYPE>* find(HASH_KEY_TYPE key) - { - int index = getIndex(key); - LLSimpleHashEntry<HASH_KEY_TYPE>* res = mEntryTable[index]; - while(res && (res->getHashKey() != key)) - { - res = res->getNextEntry(); - } - return res; - } - bool erase(LLSimpleHashEntry<HASH_KEY_TYPE>* entry) - { - return erase(entry->getHashKey()); - } - bool erase(HASH_KEY_TYPE key) - { - int index = getIndex(key); - LLSimpleHashEntry<HASH_KEY_TYPE>* prev = 0; - LLSimpleHashEntry<HASH_KEY_TYPE>* res = mEntryTable[index]; - while(res && (res->getHashKey() != key)) - { - prev = res; - res = res->getNextEntry(); - } - if (res) - { - LLSimpleHashEntry<HASH_KEY_TYPE>* next = res->getNextEntry(); - if (prev) - { - prev->setNextEntry(next); - } - else - { - mEntryTable[index] = next; - } - return true; - } - else - { - return false; - } - } - // Removes and returns an arbitrary ("first") element from the table - // Used for deleting the entire table. - LLSimpleHashEntry<HASH_KEY_TYPE>* pop_element() - { - for (int i=0; i<TABLE_SIZE; i++) - { - LLSimpleHashEntry<HASH_KEY_TYPE>* entry = mEntryTable[i]; - if (entry) - { - mEntryTable[i] = entry->getNextEntry(); - return entry; - } - } - return 0; - } - // debugging - LLSimpleHashEntry<HASH_KEY_TYPE>* get_element_at_index(S32 index) const - { - return mEntryTable[index]; - } - - private: - LLSimpleHashEntry<HASH_KEY_TYPE>* mEntryTable[TABLE_SIZE]; + LLSimpleHashEntry<HASH_KEY_TYPE>* mEntryTable[TABLE_SIZE]; }; #endif // LL_LLSIMPLEHASH_H diff --git a/indra/llcommon/llsingleton.cpp b/indra/llcommon/llsingleton.cpp index 5f1a89670e..d00e703a10 100644 --- a/indra/llcommon/llsingleton.cpp +++ b/indra/llcommon/llsingleton.cpp @@ -1,25 +1,25 @@ -/** +/** * @file llsingleton.cpp * @author Brad Kittenbrink * * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -363,7 +363,7 @@ LLSingletonBase::vec_t LLSingletonBase::dep_sort() // should happen basically once: for deleteAll(). typedef LLDependencies<LLSingletonBase*> SingletonDeps; SingletonDeps sdeps; - // Lock while traversing the master list + // Lock while traversing the master list MasterList::LockedMaster master; for (LLSingletonBase* sp : master.get()) { @@ -455,7 +455,7 @@ std::ostream& operator<<(std::ostream& out, const LLSingletonBase::string_params return out; } -} // anonymous namespace +} // anonymous namespace //static void LLSingletonBase::logwarns(const string_params& args) diff --git a/indra/llcommon/llsmoothstep.h b/indra/llcommon/llsmoothstep.h index 1f97a3ec89..70686c60e2 100644 --- a/indra/llcommon/llsmoothstep.h +++ b/indra/llcommon/llsmoothstep.h @@ -1,25 +1,25 @@ -/** +/** * @file llsmoothstep.h * @brief Smoothstep - transition from 0 to 1 - function, first and second derivatives all continuous (smooth) * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -31,15 +31,15 @@ template <class LLDATATYPE> inline LLDATATYPE llsmoothstep(const LLDATATYPE& edge0, const LLDATATYPE& edge1, const LLDATATYPE& value) { if (value < edge0) - return (LLDATATYPE)0; + return (LLDATATYPE)0; - if (value >= edge1) - return (LLDATATYPE)1; + if (value >= edge1) + return (LLDATATYPE)1; - // Scale/bias into [0..1] range - LLDATATYPE scaled_value = (value - edge0) / (edge1 - edge0); + // Scale/bias into [0..1] range + LLDATATYPE scaled_value = (value - edge0) / (edge1 - edge0); - return scaled_value * scaled_value * (3 - 2 * scaled_value); + return scaled_value * scaled_value * (3 - 2 * scaled_value); } #endif // LL_LLSMOOTHSTEP_H diff --git a/indra/llcommon/llstacktrace.cpp b/indra/llcommon/llstacktrace.cpp index 285bdb83ec..05e71b8203 100644 --- a/indra/llcommon/llstacktrace.cpp +++ b/indra/llcommon/llstacktrace.cpp @@ -1,168 +1,168 @@ -/** - * @file llstacktrace.cpp - * @brief stack tracing functionality - * - * $LicenseInfo:firstyear=2001&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" -#include "llstacktrace.h" - -#ifdef LL_WINDOWS - -#include <iostream> -#include <sstream> - -#include "llwin32headerslean.h" -#pragma warning (push) -#pragma warning (disable:4091) // a microsoft header has warnings. Very nice. -#include <dbghelp.h> -#pragma warning (pop) - -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; -} - -void ll_get_stack_trace_internal(std::vector<std::string>& lines) -{ - const S32 MAX_STACK_DEPTH = 100; - const S32 STRING_NAME_LENGTH = 256; - - HANDLE process = GetCurrentProcess(); - SymInitialize( process, NULL, true ); - - void *stack[MAX_STACK_DEPTH]; - - unsigned short frames = RtlCaptureStackBackTrace_fn( 0, MAX_STACK_DEPTH, stack, NULL ); - SYMBOL_INFO *symbol = (SYMBOL_INFO*)calloc(sizeof(SYMBOL_INFO) + STRING_NAME_LENGTH * sizeof(char), 1); - symbol->MaxNameLen = STRING_NAME_LENGTH-1; - symbol->SizeOfStruct = sizeof(SYMBOL_INFO); - - for(unsigned int i = 0; i < frames; i++) - { - SymFromAddr(process, (DWORD64)(stack[i]), 0, symbol); - lines.push_back(symbol->Name); - } - - free( symbol ); -} - -#else - -bool ll_get_stack_trace(std::vector<std::string>& lines) -{ - return false; -} - -void ll_get_stack_trace_internal(std::vector<std::string>& lines) -{ - -} - -#endif - +/**
+ * @file llstacktrace.cpp
+ * @brief stack tracing functionality
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+#include "llstacktrace.h"
+
+#ifdef LL_WINDOWS
+
+#include <iostream>
+#include <sstream>
+
+#include "llwin32headerslean.h"
+#pragma warning (push)
+#pragma warning (disable:4091) // a microsoft header has warnings. Very nice.
+#include <dbghelp.h>
+#pragma warning (pop)
+
+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;
+}
+
+void ll_get_stack_trace_internal(std::vector<std::string>& lines)
+{
+ const S32 MAX_STACK_DEPTH = 100;
+ const S32 STRING_NAME_LENGTH = 256;
+
+ HANDLE process = GetCurrentProcess();
+ SymInitialize( process, NULL, true );
+
+ void *stack[MAX_STACK_DEPTH];
+
+ unsigned short frames = RtlCaptureStackBackTrace_fn( 0, MAX_STACK_DEPTH, stack, NULL );
+ SYMBOL_INFO *symbol = (SYMBOL_INFO*)calloc(sizeof(SYMBOL_INFO) + STRING_NAME_LENGTH * sizeof(char), 1);
+ symbol->MaxNameLen = STRING_NAME_LENGTH-1;
+ symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
+
+ for(unsigned int i = 0; i < frames; i++)
+ {
+ SymFromAddr(process, (DWORD64)(stack[i]), 0, symbol);
+ lines.push_back(symbol->Name);
+ }
+
+ free( symbol );
+}
+
+#else
+
+bool ll_get_stack_trace(std::vector<std::string>& lines)
+{
+ return false;
+}
+
+void ll_get_stack_trace_internal(std::vector<std::string>& lines)
+{
+
+}
+
+#endif
+
diff --git a/indra/llcommon/llstacktrace.h b/indra/llcommon/llstacktrace.h index 335765386a..693eeced63 100644 --- a/indra/llcommon/llstacktrace.h +++ b/indra/llcommon/llstacktrace.h @@ -1,25 +1,25 @@ -/** +/** * @file llstacktrace.h * @brief stack trace functions * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llcommon/llstatenums.h b/indra/llcommon/llstatenums.h index 6515a3e3cb..99f3d3c870 100644 --- a/indra/llcommon/llstatenums.h +++ b/indra/llcommon/llstatenums.h @@ -1,24 +1,24 @@ -/** +/** * @file llstatenums.h * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llcommon/llstaticstringtable.h b/indra/llcommon/llstaticstringtable.h index d7e0e8a08d..66ba3487c4 100644 --- a/indra/llcommon/llstaticstringtable.h +++ b/indra/llcommon/llstaticstringtable.h @@ -1,4 +1,4 @@ -/** +/** * @file llstringtable.h * @brief The LLStringTable class provides a _fast_ method for finding * unique copies of strings. @@ -6,21 +6,21 @@ * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -36,45 +36,45 @@ class LLStaticHashedString { public: - LLStaticHashedString(const std::string& s) - { - string_hash = makehash(s); - string = s; - } + LLStaticHashedString(const std::string& s) + { + string_hash = makehash(s); + string = s; + } - const std::string& String() const { return string; } - size_t Hash() const { return string_hash; } + const std::string& String() const { return string; } + size_t Hash() const { return string_hash; } - bool operator==(const LLStaticHashedString& b) const { return Hash() == b.Hash(); } + bool operator==(const LLStaticHashedString& b) const { return Hash() == b.Hash(); } protected: - size_t makehash(const std::string& s) - { - size_t len = s.size(); - const char* c = s.c_str(); - size_t hashval = 0; - for (size_t i=0; i<len; i++) - { - hashval = ((hashval<<5) + hashval) + *c++; - } - return hashval; - } + size_t makehash(const std::string& s) + { + size_t len = s.size(); + const char* c = s.c_str(); + size_t hashval = 0; + for (size_t i=0; i<len; i++) + { + hashval = ((hashval<<5) + hashval) + *c++; + } + return hashval; + } - std::string string; - size_t string_hash; + std::string string; + size_t string_hash; }; struct LLStaticStringHasher { - enum { bucket_size = 8 }; - size_t operator()(const LLStaticHashedString& key_value) const { return key_value.Hash(); } - bool operator()(const LLStaticHashedString& left, const LLStaticHashedString& right) const { return left.Hash() < right.Hash(); } + enum { bucket_size = 8 }; + size_t operator()(const LLStaticHashedString& key_value) const { return key_value.Hash(); } + bool operator()(const LLStaticHashedString& left, const LLStaticHashedString& right) const { return left.Hash() < right.Hash(); } }; template< typename MappedObject > class LL_COMMON_API LLStaticStringTable - : public boost::unordered_map< LLStaticHashedString, MappedObject, LLStaticStringHasher > + : public boost::unordered_map< LLStaticHashedString, MappedObject, LLStaticStringHasher > { }; diff --git a/indra/llcommon/llstl.h b/indra/llcommon/llstl.h index e39769fe9d..f2d268bb9a 100644 --- a/indra/llcommon/llstl.h +++ b/indra/llcommon/llstl.h @@ -1,713 +1,713 @@ -/** - * @file llstl.h - * @brief helper object & functions for use with the stl. - * - * $LicenseInfo:firstyear=2003&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLSTL_H -#define LL_LLSTL_H - -#include "stdtypes.h" -#include <functional> -#include <algorithm> -#include <map> -#include <vector> -#include <list> -#include <set> -#include <typeinfo> - -#ifdef LL_LINUX -// <ND> For strcmp -#include <string.h> -#endif -// Use to compare the first element only of a pair -// e.g. typedef std::set<std::pair<int, Data*>, compare_pair<int, Data*> > some_pair_set_t; -template <typename T1, typename T2> -struct compare_pair_first -{ - bool operator()(const std::pair<T1, T2>& a, const std::pair<T1, T2>& b) const - { - return a.first < b.first; - } -}; - -template <typename T1, typename T2> -struct compare_pair_greater -{ - bool operator()(const std::pair<T1, T2>& a, const std::pair<T1, T2>& b) const - { - if (!(a.first < b.first)) - return true; - else if (!(b.first < a.first)) - return false; - else - return !(a.second < b.second); - } -}; - -// Use to compare the contents of two pointers (e.g. std::string*) -template <typename T> -struct compare_pointer_contents -{ - typedef const T* Tptr; - bool operator()(const Tptr& a, const Tptr& b) const - { - return *a < *b; - } -}; - -// DeletePointer is a simple helper for deleting all pointers in a container. -// The general form is: -// -// std::for_each(cont.begin(), cont.end(), DeletePointer()); -// somemap.clear(); -// -// Don't forget to clear()! - -struct DeletePointer -{ - template<typename T> void operator()(T* ptr) const - { - delete ptr; - } -}; -struct DeletePointerArray -{ - template<typename T> void operator()(T* ptr) const - { - delete[] ptr; - } -}; - -// DeletePairedPointer is a simple helper for deleting all pointers in a map. -// The general form is: -// -// std::for_each(somemap.begin(), somemap.end(), DeletePairedPointer()); -// somemap.clear(); // Don't leave dangling pointers around - -struct DeletePairedPointer -{ - template<typename T> void operator()(T &ptr) const - { - delete ptr.second; - ptr.second = NULL; - } -}; -struct DeletePairedPointerArray -{ - template<typename T> void operator()(T &ptr) const - { - delete[] ptr.second; - ptr.second = NULL; - } -}; - - -// Alternate version of the above so that has a more cumbersome -// syntax, but it can be used with compositional functors. -// NOTE: The functor retuns a bool because msdev bombs during the -// composition if you return void. Once we upgrade to a newer -// compiler, the second unary_function template parameter can be set -// to void. -// -// Here's a snippet showing how you use this object: -// -// typedef std::map<int, widget*> map_type; -// map_type widget_map; -// ... // add elements -// // delete them all -// for_each(widget_map.begin(), -// widget_map.end(), -// llcompose1(DeletePointerFunctor<widget>(), -// llselect2nd<map_type::value_type>())); - -template<typename T> -struct DeletePointerFunctor -{ - bool operator()(T* ptr) const - { - delete ptr; - return true; - } -}; - -// See notes about DeleteArray for why you should consider avoiding this. -template<typename T> -struct DeleteArrayFunctor -{ - bool operator()(T* ptr) const - { - delete[] ptr; - return true; - } -}; - -// CopyNewPointer is a simple helper which accepts a pointer, and -// returns a new pointer built with the copy constructor. Example: -// -// transform(in.begin(), in.end(), out.end(), CopyNewPointer()); - -struct CopyNewPointer -{ - template<typename T> T* operator()(const T* ptr) const - { - return new T(*ptr); - } -}; - -template<typename T, typename ALLOC> -void delete_and_clear(std::list<T*, ALLOC>& list) -{ - std::for_each(list.begin(), list.end(), DeletePointer()); - list.clear(); -} - -template<typename T, typename ALLOC> -void delete_and_clear(std::vector<T*, ALLOC>& vector) -{ - std::for_each(vector.begin(), vector.end(), DeletePointer()); - vector.clear(); -} - -template<typename T, typename COMPARE, typename ALLOC> -void delete_and_clear(std::set<T*, COMPARE, ALLOC>& set) -{ - std::for_each(set.begin(), set.end(), DeletePointer()); - set.clear(); -} - -template<typename K, typename V, typename COMPARE, typename ALLOC> -void delete_and_clear(std::map<K, V*, COMPARE, ALLOC>& map) -{ - std::for_each(map.begin(), map.end(), DeletePairedPointer()); - map.clear(); -} - -template<typename T> -void delete_and_clear(T*& ptr) -{ - delete ptr; - ptr = NULL; -} - - -template<typename T> -void delete_and_clear_array(T*& ptr) -{ - delete[] ptr; - ptr = NULL; -} - -// Simple function to help with finding pointers in maps. -// For example: -// typedef map_t; -// std::map<int, const char*> foo; -// foo[18] = "there"; -// foo[2] = "hello"; -// const char* bar = get_ptr_in_map(foo, 2); // bar -> "hello" -// const char* baz = get_ptr_in_map(foo, 3); // baz == NULL -template <typename K, typename T> -inline T* get_ptr_in_map(const std::map<K,T*>& inmap, const K& key) -{ - // Typedef here avoids warnings because of new c++ naming rules. - typedef typename std::map<K,T*>::const_iterator map_iter; - map_iter iter = inmap.find(key); - if(iter == inmap.end()) - { - return NULL; - } - else - { - return iter->second; - } -}; - -// helper function which returns true if key is in inmap. -template <typename K, typename T> -inline bool is_in_map(const std::map<K,T>& inmap, const K& key) -{ - if(inmap.find(key) == inmap.end()) - { - return false; - } - else - { - return true; - } -} - -// Similar to get_ptr_in_map, but for any type with a valid T(0) constructor. -// To replace LLSkipMap getIfThere, use: -// get_if_there(map, key, 0) -// WARNING: Make sure default_value (generally 0) is not a valid map entry! -template <typename K, typename T> -inline T get_if_there(const std::map<K,T>& inmap, const K& key, T default_value) -{ - // Typedef here avoids warnings because of new c++ naming rules. - typedef typename std::map<K,T>::const_iterator map_iter; - map_iter iter = inmap.find(key); - if(iter == inmap.end()) - { - return default_value; - } - else - { - return iter->second; - } -}; - -// Useful for replacing the removeObj() functionality of LLDynamicArray -// Example: -// for (std::vector<T>::iterator iter = mList.begin(); iter != mList.end(); ) -// { -// if ((*iter)->isMarkedForRemoval()) -// iter = vector_replace_with_last(mList, iter); -// else -// ++iter; -// } -template <typename T> -inline typename std::vector<T>::iterator vector_replace_with_last(std::vector<T>& invec, typename std::vector<T>::iterator iter) -{ - typename std::vector<T>::iterator last = invec.end(); --last; - if (iter == invec.end()) - { - return iter; - } - else if (iter == last) - { - invec.pop_back(); - return invec.end(); - } - else - { - *iter = *last; - invec.pop_back(); - return iter; - } -}; - -// Example: -// vector_replace_with_last(mList, x); -template <typename T> -inline bool vector_replace_with_last(std::vector<T>& invec, const T& val) -{ - typename std::vector<T>::iterator iter = std::find(invec.begin(), invec.end(), val); - if (iter != invec.end()) - { - typename std::vector<T>::iterator last = invec.end(); --last; - *iter = *last; - invec.pop_back(); - return true; - } - return false; -} - -// Append N elements to the vector and return a pointer to the first new element. -template <typename T> -inline T* vector_append(std::vector<T>& invec, S32 N) -{ - U32 sz = invec.size(); - invec.resize(sz+N); - return &(invec[sz]); -} - -// call function f to n members starting at first. similar to std::for_each -template <class InputIter, class Size, class Function> -Function ll_for_n(InputIter first, Size n, Function f) -{ - for ( ; n > 0; --n, ++first) - f(*first); - return f; -} - -// copy first to result n times, incrementing each as we go -template <class InputIter, class Size, class OutputIter> -OutputIter ll_copy_n(InputIter first, Size n, OutputIter result) -{ - for ( ; n > 0; --n, ++result, ++first) - *result = *first; - return result; -} - -// set *result = op(*f) for n elements of f -template <class InputIter, class OutputIter, class Size, class UnaryOp> -OutputIter ll_transform_n( - InputIter first, - Size n, - OutputIter result, - UnaryOp op) -{ - for ( ; n > 0; --n, ++result, ++first) - *result = op(*first); - return result; -} - - - -/* - * - * Copyright (c) 1994 - * Hewlett-Packard Company - * - * Permission to use, copy, modify, distribute and sell this software - * and its documentation for any purpose is hereby granted without fee, - * provided that the above copyright notice appear in all copies and - * that both that copyright notice and this permission notice appear - * in supporting documentation. Hewlett-Packard Company makes no - * representations about the suitability of this software for any - * purpose. It is provided "as is" without express or implied warranty. - * - * - * Copyright (c) 1996-1998 - * Silicon Graphics Computer Systems, Inc. - * - * Permission to use, copy, modify, distribute and sell this software - * and its documentation for any purpose is hereby granted without fee, - * provided that the above copyright notice appear in all copies and - * that both that copyright notice and this permission notice appear - * in supporting documentation. Silicon Graphics makes no - * representations about the suitability of this software for any - * purpose. It is provided "as is" without express or implied warranty. - */ - - -// helper to deal with the fact that MSDev does not package -// select... with the stl. Look up usage on the sgi website. - -template <class _Pair> -struct _LLSelect1st -{ - const auto& operator()(const _Pair& __x) const { - return __x.first; - } -}; - -template <class _Pair> -struct _LLSelect2nd -{ - const auto& operator()(const _Pair& __x) const { - return __x.second; - } -}; - -template <class _Pair> struct llselect1st : public _LLSelect1st<_Pair> {}; -template <class _Pair> struct llselect2nd : public _LLSelect2nd<_Pair> {}; - -// helper to deal with the fact that MSDev does not package -// compose... with the stl. Look up usage on the sgi website. - -template <class _Operation1, class _Operation2> -class ll_unary_compose -{ -protected: - _Operation1 __op1; - _Operation2 __op2; -public: - ll_unary_compose(const _Operation1& __x, const _Operation2& __y) - : __op1(__x), __op2(__y) {} - template <typename _Op2Arg> - auto - operator()(const _Op2Arg& __x) const { - return __op1(__op2(__x)); - } -}; - -template <class _Operation1, class _Operation2> -inline ll_unary_compose<_Operation1,_Operation2> -llcompose1(const _Operation1& __op1, const _Operation2& __op2) -{ - return ll_unary_compose<_Operation1,_Operation2>(__op1, __op2); -} - -template <class _Operation1, class _Operation2, class _Operation3> -class ll_binary_compose -{ -protected: - _Operation1 _M_op1; - _Operation2 _M_op2; - _Operation3 _M_op3; -public: - ll_binary_compose(const _Operation1& __x, const _Operation2& __y, - const _Operation3& __z) - : _M_op1(__x), _M_op2(__y), _M_op3(__z) { } - template<typename OP2ARG> - auto - operator()(const OP2ARG& __x) const { - return _M_op1(_M_op2(__x), _M_op3(__x)); - } -}; - -template <class _Operation1, class _Operation2, class _Operation3> -inline ll_binary_compose<_Operation1, _Operation2, _Operation3> -llcompose2(const _Operation1& __op1, const _Operation2& __op2, - const _Operation3& __op3) -{ - return ll_binary_compose<_Operation1,_Operation2,_Operation3> - (__op1, __op2, __op3); -} - -// helpers to deal with the fact that MSDev does not package -// bind... with the stl. Again, this is from sgi. -template <class _Operation, typename _Arg1> -class llbinder1st -{ -protected: - _Operation op; - _Arg1 value; -public: - llbinder1st(const _Operation& __x, const _Arg1& __y) - : op(__x), value(__y) {} - template <typename _Arg2> - auto - operator()(const _Arg2& __x) const { - return op(value, __x); - } -}; - -template <class _Operation, class _Tp> -inline auto -llbind1st(const _Operation& __oper, const _Tp& __x) -{ - return llbinder1st<_Operation, _Tp>(__oper, __x); -} - -template <class _Operation, typename _Arg2> -class llbinder2nd -{ -protected: - _Operation op; - _Arg2 value; -public: - llbinder2nd(const _Operation& __x, - const _Arg2& __y) - : op(__x), value(__y) {} - template <typename _Arg1> - auto - operator()(const _Arg1& __x) const { - return op(__x, value); - } -}; - -template <class _Operation, class _Tp> -inline auto -llbind2nd(const _Operation& __oper, const _Tp& __x) -{ - return llbinder2nd<_Operation, _Tp>(__oper, __x); -} - -/** - * Compare std::type_info* pointers a la std::less. We break this out as a - * separate function for use in two different std::less specializations. - */ -inline -bool before(const std::type_info* lhs, const std::type_info* rhs) -{ -#if LL_LINUX && defined(__GNUC__) && ((__GNUC__ < 4) || (__GNUC__ == 4 && __GNUC_MINOR__ < 4)) - // If we're building on Linux with gcc, and it's either gcc 3.x or - // 4.{0,1,2,3}, then we have to use a workaround. Note that we use gcc on - // Mac too, and some people build with gcc on Windows (cygwin or mingw). - // On Linux, different load modules may produce different type_info* - // pointers for the same type. Have to compare name strings to get good - // results. - return strcmp(lhs->name(), rhs->name()) < 0; -#else // not Linux, or gcc 4.4+ - // Just use before(), as we normally would - return lhs->before(*rhs); -#endif -} - -/** - * Specialize std::less<std::type_info*> to use std::type_info::before(). - * See MAINT-1175. It is NEVER a good idea to directly compare std::type_info* - * because, on Linux, you might get different std::type_info* pointers for the - * same type (from different load modules)! - */ -namespace std -{ - template <> - struct less<const std::type_info*> - { - bool operator()(const std::type_info* lhs, const std::type_info* rhs) const - { - return before(lhs, rhs); - } - }; - - template <> - struct less<std::type_info*> - { - bool operator()(std::type_info* lhs, std::type_info* rhs) const - { - return before(lhs, rhs); - } - }; -} // std - - -/** - * Implementation for ll_template_cast() (q.v.). - * - * Default implementation: trying to cast two completely unrelated types - * returns 0. Typically you'd specify T and U as pointer types, but in fact T - * can be any type that can be initialized with 0. - */ -template <typename T, typename U> -struct ll_template_cast_impl -{ - T operator()(U) - { - return 0; - } -}; - -/** - * ll_template_cast<T>(some_value) is for use in a template function when - * some_value might be of arbitrary type, but you want to recognize type T - * specially. - * - * It's designed for use with pointer types. Example: - * @code - * struct SpecialClass - * { - * void someMethod(const std::string&) const; - * }; - * - * template <class REALCLASS> - * void somefunc(const REALCLASS& instance) - * { - * const SpecialClass* ptr = ll_template_cast<const SpecialClass*>(&instance); - * if (ptr) - * { - * ptr->someMethod("Call method only available on SpecialClass"); - * } - * } - * @endcode - * - * Why is this better than dynamic_cast<>? Because unless OtherClass is - * polymorphic, the following won't even compile (gcc 4.0.1): - * @code - * OtherClass other; - * SpecialClass* ptr = dynamic_cast<SpecialClass*>(&other); - * @endcode - * to say nothing of this: - * @code - * void function(int); - * SpecialClass* ptr = dynamic_cast<SpecialClass*>(&function); - * @endcode - * ll_template_cast handles these kinds of cases by returning 0. - */ -template <typename T, typename U> -T ll_template_cast(U value) -{ - return ll_template_cast_impl<T, U>()(value); -} - -/** - * Implementation for ll_template_cast() (q.v.). - * - * Implementation for identical types: return same value. - */ -template <typename T> -struct ll_template_cast_impl<T, T> -{ - T operator()(T value) - { - return value; - } -}; - -/** - * LL_TEMPLATE_CONVERTIBLE(dest, source) asserts that, for a value @c s of - * type @c source, <tt>ll_template_cast<dest>(s)</tt> will return @c s -- - * presuming that @c source can be converted to @c dest by the normal rules of - * C++. - * - * By default, <tt>ll_template_cast<dest>(s)</tt> will return 0 unless @c s's - * type is literally identical to @c dest. (This is because of the - * straightforward application of template specialization rules.) That can - * lead to surprising results, e.g.: - * - * @code - * Foo myFoo; - * const Foo* fooptr = ll_template_cast<const Foo*>(&myFoo); - * @endcode - * - * Here @c fooptr will be 0 because <tt>&myFoo</tt> is of type <tt>Foo*</tt> - * -- @em not <tt>const Foo*</tt>. (Declaring <tt>const Foo myFoo;</tt> would - * force the compiler to do the right thing.) - * - * More disappointingly: - * @code - * struct Base {}; - * struct Subclass: public Base {}; - * Subclass object; - * Base* ptr = ll_template_cast<Base*>(&object); - * @endcode - * - * Here @c ptr will be 0 because <tt>&object</tt> is of type - * <tt>Subclass*</tt> rather than <tt>Base*</tt>. We @em want this cast to - * succeed, but without our help ll_template_cast can't recognize it. - * - * The following would suffice: - * @code - * LL_TEMPLATE_CONVERTIBLE(Base*, Subclass*); - * ... - * Base* ptr = ll_template_cast<Base*>(&object); - * @endcode - * - * However, as noted earlier, this is easily fooled: - * @code - * const Base* ptr = ll_template_cast<const Base*>(&object); - * @endcode - * would still produce 0 because we haven't yet seen: - * @code - * LL_TEMPLATE_CONVERTIBLE(const Base*, Subclass*); - * @endcode - * - * @TODO - * This macro should use Boost type_traits facilities for stripping and - * re-adding @c const and @c volatile qualifiers so that invoking - * LL_TEMPLATE_CONVERTIBLE(dest, source) will automatically generate all - * permitted permutations. It's really not fair to the coder to require - * separate: - * @code - * LL_TEMPLATE_CONVERTIBLE(Base*, Subclass*); - * LL_TEMPLATE_CONVERTIBLE(const Base*, Subclass*); - * LL_TEMPLATE_CONVERTIBLE(const Base*, const Subclass*); - * @endcode - * - * (Naturally we omit <tt>LL_TEMPLATE_CONVERTIBLE(Base*, const Subclass*)</tt> - * because that's not permitted by normal C++ assignment anyway.) - */ -#define LL_TEMPLATE_CONVERTIBLE(DEST, SOURCE) \ -template <> \ -struct ll_template_cast_impl<DEST, SOURCE> \ -{ \ - DEST operator()(SOURCE wrapper) \ - { \ - return wrapper; \ - } \ -} - - -#endif // LL_LLSTL_H +/**
+ * @file llstl.h
+ * @brief helper object & functions for use with the stl.
+ *
+ * $LicenseInfo:firstyear=2003&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLSTL_H
+#define LL_LLSTL_H
+
+#include "stdtypes.h"
+#include <functional>
+#include <algorithm>
+#include <map>
+#include <vector>
+#include <list>
+#include <set>
+#include <typeinfo>
+
+#ifdef LL_LINUX
+// <ND> For strcmp
+#include <string.h>
+#endif
+// Use to compare the first element only of a pair
+// e.g. typedef std::set<std::pair<int, Data*>, compare_pair<int, Data*> > some_pair_set_t;
+template <typename T1, typename T2>
+struct compare_pair_first
+{
+ bool operator()(const std::pair<T1, T2>& a, const std::pair<T1, T2>& b) const
+ {
+ return a.first < b.first;
+ }
+};
+
+template <typename T1, typename T2>
+struct compare_pair_greater
+{
+ bool operator()(const std::pair<T1, T2>& a, const std::pair<T1, T2>& b) const
+ {
+ if (!(a.first < b.first))
+ return true;
+ else if (!(b.first < a.first))
+ return false;
+ else
+ return !(a.second < b.second);
+ }
+};
+
+// Use to compare the contents of two pointers (e.g. std::string*)
+template <typename T>
+struct compare_pointer_contents
+{
+ typedef const T* Tptr;
+ bool operator()(const Tptr& a, const Tptr& b) const
+ {
+ return *a < *b;
+ }
+};
+
+// DeletePointer is a simple helper for deleting all pointers in a container.
+// The general form is:
+//
+// std::for_each(cont.begin(), cont.end(), DeletePointer());
+// somemap.clear();
+//
+// Don't forget to clear()!
+
+struct DeletePointer
+{
+ template<typename T> void operator()(T* ptr) const
+ {
+ delete ptr;
+ }
+};
+struct DeletePointerArray
+{
+ template<typename T> void operator()(T* ptr) const
+ {
+ delete[] ptr;
+ }
+};
+
+// DeletePairedPointer is a simple helper for deleting all pointers in a map.
+// The general form is:
+//
+// std::for_each(somemap.begin(), somemap.end(), DeletePairedPointer());
+// somemap.clear(); // Don't leave dangling pointers around
+
+struct DeletePairedPointer
+{
+ template<typename T> void operator()(T &ptr) const
+ {
+ delete ptr.second;
+ ptr.second = NULL;
+ }
+};
+struct DeletePairedPointerArray
+{
+ template<typename T> void operator()(T &ptr) const
+ {
+ delete[] ptr.second;
+ ptr.second = NULL;
+ }
+};
+
+
+// Alternate version of the above so that has a more cumbersome
+// syntax, but it can be used with compositional functors.
+// NOTE: The functor retuns a bool because msdev bombs during the
+// composition if you return void. Once we upgrade to a newer
+// compiler, the second unary_function template parameter can be set
+// to void.
+//
+// Here's a snippet showing how you use this object:
+//
+// typedef std::map<int, widget*> map_type;
+// map_type widget_map;
+// ... // add elements
+// // delete them all
+// for_each(widget_map.begin(),
+// widget_map.end(),
+// llcompose1(DeletePointerFunctor<widget>(),
+// llselect2nd<map_type::value_type>()));
+
+template<typename T>
+struct DeletePointerFunctor
+{
+ bool operator()(T* ptr) const
+ {
+ delete ptr;
+ return true;
+ }
+};
+
+// See notes about DeleteArray for why you should consider avoiding this.
+template<typename T>
+struct DeleteArrayFunctor
+{
+ bool operator()(T* ptr) const
+ {
+ delete[] ptr;
+ return true;
+ }
+};
+
+// CopyNewPointer is a simple helper which accepts a pointer, and
+// returns a new pointer built with the copy constructor. Example:
+//
+// transform(in.begin(), in.end(), out.end(), CopyNewPointer());
+
+struct CopyNewPointer
+{
+ template<typename T> T* operator()(const T* ptr) const
+ {
+ return new T(*ptr);
+ }
+};
+
+template<typename T, typename ALLOC>
+void delete_and_clear(std::list<T*, ALLOC>& list)
+{
+ std::for_each(list.begin(), list.end(), DeletePointer());
+ list.clear();
+}
+
+template<typename T, typename ALLOC>
+void delete_and_clear(std::vector<T*, ALLOC>& vector)
+{
+ std::for_each(vector.begin(), vector.end(), DeletePointer());
+ vector.clear();
+}
+
+template<typename T, typename COMPARE, typename ALLOC>
+void delete_and_clear(std::set<T*, COMPARE, ALLOC>& set)
+{
+ std::for_each(set.begin(), set.end(), DeletePointer());
+ set.clear();
+}
+
+template<typename K, typename V, typename COMPARE, typename ALLOC>
+void delete_and_clear(std::map<K, V*, COMPARE, ALLOC>& map)
+{
+ std::for_each(map.begin(), map.end(), DeletePairedPointer());
+ map.clear();
+}
+
+template<typename T>
+void delete_and_clear(T*& ptr)
+{
+ delete ptr;
+ ptr = NULL;
+}
+
+
+template<typename T>
+void delete_and_clear_array(T*& ptr)
+{
+ delete[] ptr;
+ ptr = NULL;
+}
+
+// Simple function to help with finding pointers in maps.
+// For example:
+// typedef map_t;
+// std::map<int, const char*> foo;
+// foo[18] = "there";
+// foo[2] = "hello";
+// const char* bar = get_ptr_in_map(foo, 2); // bar -> "hello"
+// const char* baz = get_ptr_in_map(foo, 3); // baz == NULL
+template <typename K, typename T>
+inline T* get_ptr_in_map(const std::map<K,T*>& inmap, const K& key)
+{
+ // Typedef here avoids warnings because of new c++ naming rules.
+ typedef typename std::map<K,T*>::const_iterator map_iter;
+ map_iter iter = inmap.find(key);
+ if(iter == inmap.end())
+ {
+ return NULL;
+ }
+ else
+ {
+ return iter->second;
+ }
+};
+
+// helper function which returns true if key is in inmap.
+template <typename K, typename T>
+inline bool is_in_map(const std::map<K,T>& inmap, const K& key)
+{
+ if(inmap.find(key) == inmap.end())
+ {
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+}
+
+// Similar to get_ptr_in_map, but for any type with a valid T(0) constructor.
+// To replace LLSkipMap getIfThere, use:
+// get_if_there(map, key, 0)
+// WARNING: Make sure default_value (generally 0) is not a valid map entry!
+template <typename K, typename T>
+inline T get_if_there(const std::map<K,T>& inmap, const K& key, T default_value)
+{
+ // Typedef here avoids warnings because of new c++ naming rules.
+ typedef typename std::map<K,T>::const_iterator map_iter;
+ map_iter iter = inmap.find(key);
+ if(iter == inmap.end())
+ {
+ return default_value;
+ }
+ else
+ {
+ return iter->second;
+ }
+};
+
+// Useful for replacing the removeObj() functionality of LLDynamicArray
+// Example:
+// for (std::vector<T>::iterator iter = mList.begin(); iter != mList.end(); )
+// {
+// if ((*iter)->isMarkedForRemoval())
+// iter = vector_replace_with_last(mList, iter);
+// else
+// ++iter;
+// }
+template <typename T>
+inline typename std::vector<T>::iterator vector_replace_with_last(std::vector<T>& invec, typename std::vector<T>::iterator iter)
+{
+ typename std::vector<T>::iterator last = invec.end(); --last;
+ if (iter == invec.end())
+ {
+ return iter;
+ }
+ else if (iter == last)
+ {
+ invec.pop_back();
+ return invec.end();
+ }
+ else
+ {
+ *iter = *last;
+ invec.pop_back();
+ return iter;
+ }
+};
+
+// Example:
+// vector_replace_with_last(mList, x);
+template <typename T>
+inline bool vector_replace_with_last(std::vector<T>& invec, const T& val)
+{
+ typename std::vector<T>::iterator iter = std::find(invec.begin(), invec.end(), val);
+ if (iter != invec.end())
+ {
+ typename std::vector<T>::iterator last = invec.end(); --last;
+ *iter = *last;
+ invec.pop_back();
+ return true;
+ }
+ return false;
+}
+
+// Append N elements to the vector and return a pointer to the first new element.
+template <typename T>
+inline T* vector_append(std::vector<T>& invec, S32 N)
+{
+ U32 sz = invec.size();
+ invec.resize(sz+N);
+ return &(invec[sz]);
+}
+
+// call function f to n members starting at first. similar to std::for_each
+template <class InputIter, class Size, class Function>
+Function ll_for_n(InputIter first, Size n, Function f)
+{
+ for ( ; n > 0; --n, ++first)
+ f(*first);
+ return f;
+}
+
+// copy first to result n times, incrementing each as we go
+template <class InputIter, class Size, class OutputIter>
+OutputIter ll_copy_n(InputIter first, Size n, OutputIter result)
+{
+ for ( ; n > 0; --n, ++result, ++first)
+ *result = *first;
+ return result;
+}
+
+// set *result = op(*f) for n elements of f
+template <class InputIter, class OutputIter, class Size, class UnaryOp>
+OutputIter ll_transform_n(
+ InputIter first,
+ Size n,
+ OutputIter result,
+ UnaryOp op)
+{
+ for ( ; n > 0; --n, ++result, ++first)
+ *result = op(*first);
+ return result;
+}
+
+
+
+/*
+ *
+ * Copyright (c) 1994
+ * Hewlett-Packard Company
+ *
+ * Permission to use, copy, modify, distribute and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appear in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation. Hewlett-Packard Company makes no
+ * representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ *
+ *
+ * Copyright (c) 1996-1998
+ * Silicon Graphics Computer Systems, Inc.
+ *
+ * Permission to use, copy, modify, distribute and sell this software
+ * and its documentation for any purpose is hereby granted without fee,
+ * provided that the above copyright notice appear in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation. Silicon Graphics makes no
+ * representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ */
+
+
+// helper to deal with the fact that MSDev does not package
+// select... with the stl. Look up usage on the sgi website.
+
+template <class _Pair>
+struct _LLSelect1st
+{
+ const auto& operator()(const _Pair& __x) const {
+ return __x.first;
+ }
+};
+
+template <class _Pair>
+struct _LLSelect2nd
+{
+ const auto& operator()(const _Pair& __x) const {
+ return __x.second;
+ }
+};
+
+template <class _Pair> struct llselect1st : public _LLSelect1st<_Pair> {};
+template <class _Pair> struct llselect2nd : public _LLSelect2nd<_Pair> {};
+
+// helper to deal with the fact that MSDev does not package
+// compose... with the stl. Look up usage on the sgi website.
+
+template <class _Operation1, class _Operation2>
+class ll_unary_compose
+{
+protected:
+ _Operation1 __op1;
+ _Operation2 __op2;
+public:
+ ll_unary_compose(const _Operation1& __x, const _Operation2& __y)
+ : __op1(__x), __op2(__y) {}
+ template <typename _Op2Arg>
+ auto
+ operator()(const _Op2Arg& __x) const {
+ return __op1(__op2(__x));
+ }
+};
+
+template <class _Operation1, class _Operation2>
+inline ll_unary_compose<_Operation1,_Operation2>
+llcompose1(const _Operation1& __op1, const _Operation2& __op2)
+{
+ return ll_unary_compose<_Operation1,_Operation2>(__op1, __op2);
+}
+
+template <class _Operation1, class _Operation2, class _Operation3>
+class ll_binary_compose
+{
+protected:
+ _Operation1 _M_op1;
+ _Operation2 _M_op2;
+ _Operation3 _M_op3;
+public:
+ ll_binary_compose(const _Operation1& __x, const _Operation2& __y,
+ const _Operation3& __z)
+ : _M_op1(__x), _M_op2(__y), _M_op3(__z) { }
+ template<typename OP2ARG>
+ auto
+ operator()(const OP2ARG& __x) const {
+ return _M_op1(_M_op2(__x), _M_op3(__x));
+ }
+};
+
+template <class _Operation1, class _Operation2, class _Operation3>
+inline ll_binary_compose<_Operation1, _Operation2, _Operation3>
+llcompose2(const _Operation1& __op1, const _Operation2& __op2,
+ const _Operation3& __op3)
+{
+ return ll_binary_compose<_Operation1,_Operation2,_Operation3>
+ (__op1, __op2, __op3);
+}
+
+// helpers to deal with the fact that MSDev does not package
+// bind... with the stl. Again, this is from sgi.
+template <class _Operation, typename _Arg1>
+class llbinder1st
+{
+protected:
+ _Operation op;
+ _Arg1 value;
+public:
+ llbinder1st(const _Operation& __x, const _Arg1& __y)
+ : op(__x), value(__y) {}
+ template <typename _Arg2>
+ auto
+ operator()(const _Arg2& __x) const {
+ return op(value, __x);
+ }
+};
+
+template <class _Operation, class _Tp>
+inline auto
+llbind1st(const _Operation& __oper, const _Tp& __x)
+{
+ return llbinder1st<_Operation, _Tp>(__oper, __x);
+}
+
+template <class _Operation, typename _Arg2>
+class llbinder2nd
+{
+protected:
+ _Operation op;
+ _Arg2 value;
+public:
+ llbinder2nd(const _Operation& __x,
+ const _Arg2& __y)
+ : op(__x), value(__y) {}
+ template <typename _Arg1>
+ auto
+ operator()(const _Arg1& __x) const {
+ return op(__x, value);
+ }
+};
+
+template <class _Operation, class _Tp>
+inline auto
+llbind2nd(const _Operation& __oper, const _Tp& __x)
+{
+ return llbinder2nd<_Operation, _Tp>(__oper, __x);
+}
+
+/**
+ * Compare std::type_info* pointers a la std::less. We break this out as a
+ * separate function for use in two different std::less specializations.
+ */
+inline
+bool before(const std::type_info* lhs, const std::type_info* rhs)
+{
+#if LL_LINUX && defined(__GNUC__) && ((__GNUC__ < 4) || (__GNUC__ == 4 && __GNUC_MINOR__ < 4))
+ // If we're building on Linux with gcc, and it's either gcc 3.x or
+ // 4.{0,1,2,3}, then we have to use a workaround. Note that we use gcc on
+ // Mac too, and some people build with gcc on Windows (cygwin or mingw).
+ // On Linux, different load modules may produce different type_info*
+ // pointers for the same type. Have to compare name strings to get good
+ // results.
+ return strcmp(lhs->name(), rhs->name()) < 0;
+#else // not Linux, or gcc 4.4+
+ // Just use before(), as we normally would
+ return lhs->before(*rhs);
+#endif
+}
+
+/**
+ * Specialize std::less<std::type_info*> to use std::type_info::before().
+ * See MAINT-1175. It is NEVER a good idea to directly compare std::type_info*
+ * because, on Linux, you might get different std::type_info* pointers for the
+ * same type (from different load modules)!
+ */
+namespace std
+{
+ template <>
+ struct less<const std::type_info*>
+ {
+ bool operator()(const std::type_info* lhs, const std::type_info* rhs) const
+ {
+ return before(lhs, rhs);
+ }
+ };
+
+ template <>
+ struct less<std::type_info*>
+ {
+ bool operator()(std::type_info* lhs, std::type_info* rhs) const
+ {
+ return before(lhs, rhs);
+ }
+ };
+} // std
+
+
+/**
+ * Implementation for ll_template_cast() (q.v.).
+ *
+ * Default implementation: trying to cast two completely unrelated types
+ * returns 0. Typically you'd specify T and U as pointer types, but in fact T
+ * can be any type that can be initialized with 0.
+ */
+template <typename T, typename U>
+struct ll_template_cast_impl
+{
+ T operator()(U)
+ {
+ return 0;
+ }
+};
+
+/**
+ * ll_template_cast<T>(some_value) is for use in a template function when
+ * some_value might be of arbitrary type, but you want to recognize type T
+ * specially.
+ *
+ * It's designed for use with pointer types. Example:
+ * @code
+ * struct SpecialClass
+ * {
+ * void someMethod(const std::string&) const;
+ * };
+ *
+ * template <class REALCLASS>
+ * void somefunc(const REALCLASS& instance)
+ * {
+ * const SpecialClass* ptr = ll_template_cast<const SpecialClass*>(&instance);
+ * if (ptr)
+ * {
+ * ptr->someMethod("Call method only available on SpecialClass");
+ * }
+ * }
+ * @endcode
+ *
+ * Why is this better than dynamic_cast<>? Because unless OtherClass is
+ * polymorphic, the following won't even compile (gcc 4.0.1):
+ * @code
+ * OtherClass other;
+ * SpecialClass* ptr = dynamic_cast<SpecialClass*>(&other);
+ * @endcode
+ * to say nothing of this:
+ * @code
+ * void function(int);
+ * SpecialClass* ptr = dynamic_cast<SpecialClass*>(&function);
+ * @endcode
+ * ll_template_cast handles these kinds of cases by returning 0.
+ */
+template <typename T, typename U>
+T ll_template_cast(U value)
+{
+ return ll_template_cast_impl<T, U>()(value);
+}
+
+/**
+ * Implementation for ll_template_cast() (q.v.).
+ *
+ * Implementation for identical types: return same value.
+ */
+template <typename T>
+struct ll_template_cast_impl<T, T>
+{
+ T operator()(T value)
+ {
+ return value;
+ }
+};
+
+/**
+ * LL_TEMPLATE_CONVERTIBLE(dest, source) asserts that, for a value @c s of
+ * type @c source, <tt>ll_template_cast<dest>(s)</tt> will return @c s --
+ * presuming that @c source can be converted to @c dest by the normal rules of
+ * C++.
+ *
+ * By default, <tt>ll_template_cast<dest>(s)</tt> will return 0 unless @c s's
+ * type is literally identical to @c dest. (This is because of the
+ * straightforward application of template specialization rules.) That can
+ * lead to surprising results, e.g.:
+ *
+ * @code
+ * Foo myFoo;
+ * const Foo* fooptr = ll_template_cast<const Foo*>(&myFoo);
+ * @endcode
+ *
+ * Here @c fooptr will be 0 because <tt>&myFoo</tt> is of type <tt>Foo*</tt>
+ * -- @em not <tt>const Foo*</tt>. (Declaring <tt>const Foo myFoo;</tt> would
+ * force the compiler to do the right thing.)
+ *
+ * More disappointingly:
+ * @code
+ * struct Base {};
+ * struct Subclass: public Base {};
+ * Subclass object;
+ * Base* ptr = ll_template_cast<Base*>(&object);
+ * @endcode
+ *
+ * Here @c ptr will be 0 because <tt>&object</tt> is of type
+ * <tt>Subclass*</tt> rather than <tt>Base*</tt>. We @em want this cast to
+ * succeed, but without our help ll_template_cast can't recognize it.
+ *
+ * The following would suffice:
+ * @code
+ * LL_TEMPLATE_CONVERTIBLE(Base*, Subclass*);
+ * ...
+ * Base* ptr = ll_template_cast<Base*>(&object);
+ * @endcode
+ *
+ * However, as noted earlier, this is easily fooled:
+ * @code
+ * const Base* ptr = ll_template_cast<const Base*>(&object);
+ * @endcode
+ * would still produce 0 because we haven't yet seen:
+ * @code
+ * LL_TEMPLATE_CONVERTIBLE(const Base*, Subclass*);
+ * @endcode
+ *
+ * @TODO
+ * This macro should use Boost type_traits facilities for stripping and
+ * re-adding @c const and @c volatile qualifiers so that invoking
+ * LL_TEMPLATE_CONVERTIBLE(dest, source) will automatically generate all
+ * permitted permutations. It's really not fair to the coder to require
+ * separate:
+ * @code
+ * LL_TEMPLATE_CONVERTIBLE(Base*, Subclass*);
+ * LL_TEMPLATE_CONVERTIBLE(const Base*, Subclass*);
+ * LL_TEMPLATE_CONVERTIBLE(const Base*, const Subclass*);
+ * @endcode
+ *
+ * (Naturally we omit <tt>LL_TEMPLATE_CONVERTIBLE(Base*, const Subclass*)</tt>
+ * because that's not permitted by normal C++ assignment anyway.)
+ */
+#define LL_TEMPLATE_CONVERTIBLE(DEST, SOURCE) \
+template <> \
+struct ll_template_cast_impl<DEST, SOURCE> \
+{ \
+ DEST operator()(SOURCE wrapper) \
+ { \
+ return wrapper; \
+ } \
+}
+
+
+#endif // LL_LLSTL_H
diff --git a/indra/llcommon/llstreamqueue.cpp b/indra/llcommon/llstreamqueue.cpp index 1116a2b6a2..981d913749 100644 --- a/indra/llcommon/llstreamqueue.cpp +++ b/indra/llcommon/llstreamqueue.cpp @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2012-01-05 * @brief Implementation for llstreamqueue. - * + * * $LicenseInfo:firstyear=2012&license=viewerlgpl$ * Copyright (c) 2012, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/llstreamqueue.h b/indra/llcommon/llstreamqueue.h index 0726bad175..a09bf4cb4b 100644 --- a/indra/llcommon/llstreamqueue.h +++ b/indra/llcommon/llstreamqueue.h @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2012-01-04 * @brief Definition of LLStreamQueue - * + * * $LicenseInfo:firstyear=2012&license=viewerlgpl$ * Copyright (c) 2012, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/llstreamtools.cpp b/indra/llcommon/llstreamtools.cpp index bc32b6fd9e..1021b08ad1 100644 --- a/indra/llcommon/llstreamtools.cpp +++ b/indra/llcommon/llstreamtools.cpp @@ -1,25 +1,25 @@ -/** +/** * @file llstreamtools.cpp * @brief some helper functions for parsing legacy simstate and asset files. * * $LicenseInfo:firstyear=2005&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -39,275 +39,275 @@ // skips spaces and tabs bool skip_whitespace(std::istream& input_stream) { - int c = input_stream.peek(); - while (('\t' == c || ' ' == c) && input_stream.good()) - { - input_stream.get(); - c = input_stream.peek(); - } - return input_stream.good(); + int c = input_stream.peek(); + while (('\t' == c || ' ' == c) && input_stream.good()) + { + input_stream.get(); + c = input_stream.peek(); + } + return input_stream.good(); } // skips whitespace, newlines, and carriage returns bool skip_emptyspace(std::istream& input_stream) { - int c = input_stream.peek(); - while ( input_stream.good() - && ('\t' == c || ' ' == c || '\n' == c || '\r' == c) ) - { - input_stream.get(); - c = input_stream.peek(); - } - return input_stream.good(); + int c = input_stream.peek(); + while ( input_stream.good() + && ('\t' == c || ' ' == c || '\n' == c || '\r' == c) ) + { + input_stream.get(); + c = input_stream.peek(); + } + return input_stream.good(); } // skips emptyspace and lines that start with a # bool skip_comments_and_emptyspace(std::istream& input_stream) { - while (skip_emptyspace(input_stream)) - { - int c = input_stream.peek(); - if ('#' == c ) - { - while ('\n' != c && input_stream.good()) - { - c = input_stream.get(); - } - } - else - { - break; - } - } - return input_stream.good(); + while (skip_emptyspace(input_stream)) + { + int c = input_stream.peek(); + if ('#' == c ) + { + while ('\n' != c && input_stream.good()) + { + c = input_stream.get(); + } + } + else + { + break; + } + } + return input_stream.good(); } bool skip_line(std::istream& input_stream) { - int c; - do - { - c = input_stream.get(); - } while ('\n' != c && input_stream.good()); - return input_stream.good(); + int c; + do + { + c = input_stream.get(); + } while ('\n' != c && input_stream.good()); + return input_stream.good(); } bool skip_to_next_word(std::istream& input_stream) { - int c = input_stream.peek(); - while ( input_stream.good() - && ( (c >= 'a' && c <= 'z') - || (c >= 'A' && c <= 'Z') - || (c >= '0' && c <= '9') - || '_' == c ) ) - { - input_stream.get(); - c = input_stream.peek(); - } - while ( input_stream.good() - && !( (c >= 'a' && c <= 'z') - || (c >= 'A' && c <= 'Z') - || (c >= '0' && c <= '9') - || '_' == c ) ) - { - input_stream.get(); - c = input_stream.peek(); - } - return input_stream.good(); + int c = input_stream.peek(); + while ( input_stream.good() + && ( (c >= 'a' && c <= 'z') + || (c >= 'A' && c <= 'Z') + || (c >= '0' && c <= '9') + || '_' == c ) ) + { + input_stream.get(); + c = input_stream.peek(); + } + while ( input_stream.good() + && !( (c >= 'a' && c <= 'z') + || (c >= 'A' && c <= 'Z') + || (c >= '0' && c <= '9') + || '_' == c ) ) + { + input_stream.get(); + c = input_stream.peek(); + } + return input_stream.good(); } bool skip_to_end_of_next_keyword(const char* keyword, std::istream& input_stream) { - auto key_length = strlen(keyword); /*Flawfinder: ignore*/ - if (0 == key_length) - { - return false; - } - while (input_stream.good()) - { - skip_emptyspace(input_stream); - int c = input_stream.get(); - if (keyword[0] != c) - { - skip_line(input_stream); - } - else - { - int key_index = 1; - while ( key_index < key_length - && keyword[key_index - 1] == c - && input_stream.good()) - { - key_index++; - c = input_stream.get(); - } - - if (key_index == key_length - && keyword[key_index-1] == c) - { - c = input_stream.peek(); - if (' ' == c || '\t' == c || '\r' == c || '\n' == c) - { - return true; - } - else - { - skip_line(input_stream); - } - } - else - { - skip_line(input_stream); - } - } - } - return false; + auto key_length = strlen(keyword); /*Flawfinder: ignore*/ + if (0 == key_length) + { + return false; + } + while (input_stream.good()) + { + skip_emptyspace(input_stream); + int c = input_stream.get(); + if (keyword[0] != c) + { + skip_line(input_stream); + } + else + { + int key_index = 1; + while ( key_index < key_length + && keyword[key_index - 1] == c + && input_stream.good()) + { + key_index++; + c = input_stream.get(); + } + + if (key_index == key_length + && keyword[key_index-1] == c) + { + c = input_stream.peek(); + if (' ' == c || '\t' == c || '\r' == c || '\n' == c) + { + return true; + } + else + { + skip_line(input_stream); + } + } + else + { + skip_line(input_stream); + } + } + } + return false; } /* skip_to_start_of_next_keyword() is disabled -- might tickle corruption bug in windows iostream bool skip_to_start_of_next_keyword(const char* keyword, std::istream& input_stream) { - int key_length = strlen(keyword); - if (0 == key_length) - { - return false; - } - while (input_stream.good()) - { - skip_emptyspace(input_stream); - int c = input_stream.get(); - if (keyword[0] != c) - { - skip_line(input_stream); - } - else - { - int key_index = 1; - while ( key_index < key_length - && keyword[key_index - 1] == c - && input_stream.good()) - { - key_index++; - c = input_stream.get(); - } - - if (key_index == key_length - && keyword[key_index-1] == c) - { - c = input_stream.peek(); - if (' ' == c || '\t' == c || '\r' == c || '\n' == c) - { - // put the keyword back onto the stream - for (int index = key_length - 1; index >= 0; index--) - { - input_stream.putback(keyword[index]); - } - return true; - } - else - { - skip_line(input_stream); - break; - } - } - else - { - skip_line(input_stream); - } - } - } - return false; + int key_length = strlen(keyword); + if (0 == key_length) + { + return false; + } + while (input_stream.good()) + { + skip_emptyspace(input_stream); + int c = input_stream.get(); + if (keyword[0] != c) + { + skip_line(input_stream); + } + else + { + int key_index = 1; + while ( key_index < key_length + && keyword[key_index - 1] == c + && input_stream.good()) + { + key_index++; + c = input_stream.get(); + } + + if (key_index == key_length + && keyword[key_index-1] == c) + { + c = input_stream.peek(); + if (' ' == c || '\t' == c || '\r' == c || '\n' == c) + { + // put the keyword back onto the stream + for (int index = key_length - 1; index >= 0; index--) + { + input_stream.putback(keyword[index]); + } + return true; + } + else + { + skip_line(input_stream); + break; + } + } + else + { + skip_line(input_stream); + } + } + } + return false; } */ bool get_word(std::string& output_string, std::istream& input_stream) { - skip_emptyspace(input_stream); - int c = input_stream.peek(); - while ( !isspace(c) - && '\n' != c - && '\r' != c - && input_stream.good() ) - { - output_string += c; - input_stream.get(); - c = input_stream.peek(); - } - return input_stream.good(); + skip_emptyspace(input_stream); + int c = input_stream.peek(); + while ( !isspace(c) + && '\n' != c + && '\r' != c + && input_stream.good() ) + { + output_string += c; + input_stream.get(); + c = input_stream.peek(); + } + return input_stream.good(); } bool get_word(std::string& output_string, std::istream& input_stream, int n) { - skip_emptyspace(input_stream); - int char_count = 0; - int c = input_stream.peek(); - while (!isspace(c) - && '\n' != c - && '\r' != c - && input_stream.good() - && char_count < n) - { - char_count++; - output_string += c; - input_stream.get(); - c = input_stream.peek(); - } - return input_stream.good(); + skip_emptyspace(input_stream); + int char_count = 0; + int c = input_stream.peek(); + while (!isspace(c) + && '\n' != c + && '\r' != c + && input_stream.good() + && char_count < n) + { + char_count++; + output_string += c; + input_stream.get(); + c = input_stream.peek(); + } + return input_stream.good(); } // get everything up to and including the next newline bool get_line(std::string& output_string, std::istream& input_stream) { - output_string.clear(); - int c = input_stream.get(); - while (input_stream.good()) - { - output_string += c; - if ('\n' == c) - { - break; - } - c = input_stream.get(); - } - return input_stream.good(); + output_string.clear(); + int c = input_stream.get(); + while (input_stream.good()) + { + output_string += c; + if ('\n' == c) + { + break; + } + c = input_stream.get(); + } + return input_stream.good(); } // get everything up to and including the next newline -// up to the next n characters. +// up to the next n characters. // add a newline on the end if bail before actual line ending bool get_line(std::string& output_string, std::istream& input_stream, int n) { - output_string.clear(); - int char_count = 0; - int c = input_stream.get(); - while (input_stream.good() && char_count < n) - { - char_count++; - output_string += c; - if ('\n' == c) - { - break; - } - if (char_count >= n) - { - output_string.append("\n"); - break; - } - c = input_stream.get(); - } - return input_stream.good(); + output_string.clear(); + int char_count = 0; + int c = input_stream.get(); + while (input_stream.good() && char_count < n) + { + char_count++; + output_string += c; + if ('\n' == c) + { + break; + } + if (char_count >= n) + { + output_string.append("\n"); + break; + } + c = input_stream.get(); + } + return input_stream.good(); } /* disabled -- might tickle bug in windows iostream // backs up the input_stream by line_size + 1 characters bool unget_line(const std::string& line, std::istream& input_stream) { - input_stream.putback('\n'); // unget the newline - for (int line_index = line.size()-1; line_index >= 0; line_index--) - { - input_stream.putback(line[line_index]); - } - return input_stream.good(); + input_stream.putback('\n'); // unget the newline + for (int line_index = line.size()-1; line_index >= 0; line_index--) + { + input_stream.putback(line[line_index]); + } + return input_stream.good(); } */ @@ -315,14 +315,14 @@ bool unget_line(const std::string& line, std::istream& input_stream) // returns true if removed last char bool remove_last_char(char c, std::string& line) { - auto line_size = line.size(); - if (line_size > 1 - && c == line[line_size - 1]) - { - line.replace(line_size - 1, 1, ""); - return true; - } - return false; + auto line_size = line.size(); + if (line_size > 1 + && c == line[line_size - 1]) + { + line.replace(line_size - 1, 1, ""); + return true; + } + return false; } // replaces escaped characters with the correct characters from left to right @@ -330,23 +330,23 @@ bool remove_last_char(char c, std::string& line) // "\\n" ---> '\n' (backslash n becomes carriage return) void unescape_string(std::string& line) { - auto line_size = line.size(); - for (size_t index = 0; line_size >= 1 && index < line_size - 1; ++index) - { - if ('\\' == line[index]) - { - if ('\\' == line[index + 1]) - { - line.replace(index, 2, "\\"); - line_size--; - } - else if ('n' == line[index + 1]) - { - line.replace(index, 2, "\n"); - line_size--; - } - } - } + auto line_size = line.size(); + for (size_t index = 0; line_size >= 1 && index < line_size - 1; ++index) + { + if ('\\' == line[index]) + { + if ('\\' == line[index + 1]) + { + line.replace(index, 2, "\\"); + line_size--; + } + else if ('n' == line[index + 1]) + { + line.replace(index, 2, "\n"); + line_size--; + } + } + } } // replaces unescaped characters with expanded equivalents from left to right @@ -354,164 +354,164 @@ void unescape_string(std::string& line) // '\n' ---> "\\n" (carriage return becomes backslash n) void escape_string(std::string& line) { - auto line_size = line.size(); - for (size_t index = 0; index < line_size; ++index) - { - if ('\\' == line[index]) - { - line.replace(index, 1, "\\\\"); - line_size++; - index++; - } - else if ('\n' == line[index]) - { - line.replace(index, 1, "\\n"); - line_size++; - index++; - } - } + auto line_size = line.size(); + for (size_t index = 0; index < line_size; ++index) + { + if ('\\' == line[index]) + { + line.replace(index, 1, "\\\\"); + line_size++; + index++; + } + else if ('\n' == line[index]) + { + line.replace(index, 1, "\\n"); + line_size++; + index++; + } + } } // removes '\n' characters void replace_newlines_with_whitespace(std::string& line) { - auto line_size = line.size(); - for (size_t index = 0; index < line_size; ++index) - { - if ('\n' == line[index]) - { - line.replace(index, 1, " "); - } - } + auto line_size = line.size(); + for (size_t index = 0; index < line_size; ++index) + { + if ('\n' == line[index]) + { + line.replace(index, 1, " "); + } + } } // erases any double-quote characters in 'line' void remove_double_quotes(std::string& line) { - auto line_size = line.size(); - for (size_t index = 0; index < line_size; ) - { - if ('"' == line[index]) - { - int count = 1; - while (index + count < line_size - && '"' == line[index + count]) - { - count++; - } - line.replace(index, count, ""); - line_size -= count; - } - else - { - index++; - } - } + auto line_size = line.size(); + for (size_t index = 0; index < line_size; ) + { + if ('"' == line[index]) + { + int count = 1; + while (index + count < line_size + && '"' == line[index + count]) + { + count++; + } + line.replace(index, count, ""); + line_size -= count; + } + else + { + index++; + } + } } // 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, - std::string& value, - const std::string& line) +void get_keyword_and_value(std::string& keyword, + std::string& value, + const std::string& line) { - // skip initial whitespace - auto line_size = line.size(); - size_t line_index = 0; - char c; - for ( ; line_index < line_size; ++line_index) - { - c = line[line_index]; - if (!LLStringOps::isSpace(c)) - { - break; - } - } - - // get the keyword - keyword.clear(); - for ( ; line_index < line_size; ++line_index) - { - c = line[line_index]; - if (LLStringOps::isSpace(c) || '\r' == c || '\n' == c) - { - break; - } - keyword += c; - } - - // get the value - value.clear(); - if (keyword.size() > 0 - && '\r' != line[line_index] - && '\n' != line[line_index]) - - { - // discard initial white spaces - while (line_index < line_size - && (' ' == line[line_index] - || '\t' == line[line_index]) ) - { - line_index++; - } - - for ( ; line_index < line_size; ++line_index) - { - c = line[line_index]; - if ('\r' == c || '\n' == c) - { - break; - } - value += c; - } - } + // skip initial whitespace + auto line_size = line.size(); + size_t line_index = 0; + char c; + for ( ; line_index < line_size; ++line_index) + { + c = line[line_index]; + if (!LLStringOps::isSpace(c)) + { + break; + } + } + + // get the keyword + keyword.clear(); + for ( ; line_index < line_size; ++line_index) + { + c = line[line_index]; + if (LLStringOps::isSpace(c) || '\r' == c || '\n' == c) + { + break; + } + keyword += c; + } + + // get the value + value.clear(); + if (keyword.size() > 0 + && '\r' != line[line_index] + && '\n' != line[line_index]) + + { + // discard initial white spaces + while (line_index < line_size + && (' ' == line[line_index] + || '\t' == line[line_index]) ) + { + line_index++; + } + + for ( ; line_index < line_size; ++line_index) + { + c = line[line_index]; + if ('\r' == c || '\n' == c) + { + break; + } + value += c; + } + } } std::streamsize fullread( - std::istream& istr, - char* buf, - std::streamsize requested) + std::istream& istr, + char* buf, + std::streamsize requested) { - std::streamsize got; - std::streamsize total = 0; - - istr.read(buf, requested); /*Flawfinder: ignore*/ - got = istr.gcount(); - total += got; - while(got && total < requested) - { - if(istr.fail()) - { - // If bad is true, not much we can doo -- it implies loss - // of stream integrity. Bail in that case, and otherwise - // clear and attempt to continue. - if(istr.bad()) return total; - istr.clear(); - } - istr.read(buf + total, requested - total); /*Flawfinder: ignore*/ - got = istr.gcount(); - total += got; - } - return total; + std::streamsize got; + std::streamsize total = 0; + + istr.read(buf, requested); /*Flawfinder: ignore*/ + got = istr.gcount(); + total += got; + while(got && total < requested) + { + if(istr.fail()) + { + // If bad is true, not much we can doo -- it implies loss + // of stream integrity. Bail in that case, and otherwise + // clear and attempt to continue. + if(istr.bad()) return total; + istr.clear(); + } + istr.read(buf + total, requested - total); /*Flawfinder: ignore*/ + got = istr.gcount(); + total += got; + } + return total; } std::istream& operator>>(std::istream& str, const char *tocheck) { - char c = '\0'; - const char *p; - p = tocheck; - while (*p && !str.bad()) - { - str.get(c); - if (c != *p) - { - str.setstate(std::ios::failbit); /*Flawfinder: ignore*/ - break; - } - p++; - } - return str; + char c = '\0'; + const char *p; + p = tocheck; + while (*p && !str.bad()) + { + str.get(c); + if (c != *p) + { + str.setstate(std::ios::failbit); /*Flawfinder: ignore*/ + break; + } + p++; + } + return str; } int cat_streambuf::underflow() diff --git a/indra/llcommon/llstreamtools.h b/indra/llcommon/llstreamtools.h index bb7bc20327..599484361b 100644 --- a/indra/llcommon/llstreamtools.h +++ b/indra/llcommon/llstreamtools.h @@ -1,25 +1,25 @@ -/** +/** * @file llstreamtools.h * @brief some helper functions for parsing legacy simstate and asset files. * * $LicenseInfo:firstyear=2005&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -49,11 +49,11 @@ LL_COMMON_API bool skip_line(std::istream& input_stream); // skips to beginning of next non-emptyspace LL_COMMON_API bool skip_to_next_word(std::istream& input_stream); -// skips to character after the end of next keyword +// skips to character after the end of next keyword // a 'keyword' is defined as the first word on a line 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 +// skip_to_start_of_next_keyword() is disabled -- might tickle corruption bug // in windows iostream // skips to beginning of next keyword // a 'keyword' is defined as the first word on a line @@ -65,7 +65,7 @@ LL_COMMON_API bool get_word(std::string& output_string, std::istream& input_stre 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 +// and appended to output_string // returns result of input_stream.good() after characters are pulled 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); @@ -81,13 +81,13 @@ LL_COMMON_API bool get_line(std::string& output_string, std::istream& input_stre 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' +// "\\" ---> '\\' +// "\n" ---> '\n' LL_COMMON_API void unescape_string(std::string& line); // replaces unescaped characters with expanded equivalents from left to right -// '\\' ---> "\\" -// '\n' ---> "\n" +// '\\' ---> "\\" +// '\n' ---> "\n" LL_COMMON_API void escape_string(std::string& line); // replaces each '\n' character with ' ' @@ -99,18 +99,18 @@ 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 -LL_COMMON_API void get_keyword_and_value(std::string& keyword, - std::string& value, - const std::string& line); +LL_COMMON_API void get_keyword_and_value(std::string& keyword, + std::string& value, + const std::string& line); // continue to read from the stream until you really can't // read anymore or until we hit the count. Some istream // implimentations have a max that they will read. // Returns the number of bytes read. LL_COMMON_API std::streamsize fullread( - std::istream& istr, - char* buf, - std::streamsize requested); + std::istream& istr, + char* buf, + std::streamsize requested); LL_COMMON_API std::istream& operator>>(std::istream& str, const char *tocheck); diff --git a/indra/llcommon/llstrider.h b/indra/llcommon/llstrider.h index ed9284d2c5..6c76ab8104 100644 --- a/indra/llcommon/llstrider.h +++ b/indra/llcommon/llstrider.h @@ -1,24 +1,24 @@ -/** +/** * @file llstrider.h * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -30,38 +30,38 @@ template <class Object> class LLStrider { - union - { - Object* mObjectp; - U8* mBytep; - }; - U32 mSkip; + union + { + Object* mObjectp; + U8* mBytep; + }; + U32 mSkip; public: - LLStrider() { mObjectp = NULL; mSkip = sizeof(Object); } - ~LLStrider() { } + LLStrider() { mObjectp = NULL; mSkip = sizeof(Object); } + ~LLStrider() { } - const LLStrider<Object>& operator = (Object *first) { mObjectp = first; return *this;} - void setStride (S32 skipBytes) { mSkip = (skipBytes ? skipBytes : sizeof(Object));} + const LLStrider<Object>& operator = (Object *first) { mObjectp = first; return *this;} + void setStride (S32 skipBytes) { mSkip = (skipBytes ? skipBytes : sizeof(Object));} - LLStrider<Object> operator+(const S32& index) - { - LLStrider<Object> ret; - ret.mBytep = mBytep + mSkip*index; - ret.mSkip = mSkip; + LLStrider<Object> operator+(const S32& index) + { + LLStrider<Object> ret; + ret.mBytep = mBytep + mSkip*index; + ret.mSkip = mSkip; - return ret; - } + return ret; + } - void skip(const U32 index) { mBytep += mSkip*index;} - U32 getSkip() const { return mSkip; } - Object* get() { return mObjectp; } - Object* operator->() { return mObjectp; } - Object& operator *() { return *mObjectp; } - Object* operator ++(int) { Object* old = mObjectp; mBytep += mSkip; return old; } - Object* operator +=(int i) { mBytep += mSkip*i; return mObjectp; } + void skip(const U32 index) { mBytep += mSkip*index;} + U32 getSkip() const { return mSkip; } + Object* get() { return mObjectp; } + Object* operator->() { return mObjectp; } + Object& operator *() { return *mObjectp; } + Object* operator ++(int) { Object* old = mObjectp; mBytep += mSkip; return old; } + Object* operator +=(int i) { mBytep += mSkip*i; return mObjectp; } - Object& operator[](U32 index) { return *(Object*)(mBytep + (mSkip * index)); } + Object& operator[](U32 index) { return *(Object*)(mBytep + (mSkip * index)); } }; #endif // LL_LLSTRIDER_H diff --git a/indra/llcommon/llstring.cpp b/indra/llcommon/llstring.cpp index ca72dda8ed..b3fc9b484a 100644 --- a/indra/llcommon/llstring.cpp +++ b/indra/llcommon/llstring.cpp @@ -1,1742 +1,1756 @@ -/** - * @file llstring.cpp - * @brief String utility functions and the std::string class. - * - * $LicenseInfo:firstyear=2001&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" - -#include "llstring.h" -#include "llerror.h" -#include "llfasttimer.h" -#include "llsd.h" -#include <unicode/uchar.h> -#include <vector> - -#if LL_WINDOWS -#include "llwin32headerslean.h" -#include <winnls.h> // for WideCharToMultiByte -#endif - -std::string ll_safe_string(const char* in) -{ - if(in) return std::string(in); - return std::string(); -} - -std::string ll_safe_string(const char* in, S32 maxlen) -{ - if(in && maxlen > 0 ) return std::string(in, maxlen); - - return std::string(); -} - -bool is_char_hex(char hex) -{ - if((hex >= '0') && (hex <= '9')) - { - return true; - } - else if((hex >= 'a') && (hex <='f')) - { - return true; - } - else if((hex >= 'A') && (hex <='F')) - { - return true; - } - return false; // uh - oh, not hex any more... -} - -U8 hex_as_nybble(char hex) -{ - if((hex >= '0') && (hex <= '9')) - { - return (U8)(hex - '0'); - } - else if((hex >= 'a') && (hex <='f')) - { - return (U8)(10 + hex - 'a'); - } - else if((hex >= 'A') && (hex <='F')) - { - return (U8)(10 + hex - 'A'); - } - return 0; // uh - oh, not hex any more... -} - -bool iswindividual(llwchar elem) -{ - U32 cur_char = (U32)elem; - bool result = false; - if (0x2E80<= cur_char && cur_char <= 0x9FFF) - { - result = true; - } - else if (0xAC00<= cur_char && cur_char <= 0xD7A0 ) - { - result = true; - } - else if (0xF900<= cur_char && cur_char <= 0xFA60 ) - { - result = true; - } - return result; -} - -bool _read_file_into_string(std::string& str, const std::string& filename) -{ - llifstream ifs(filename.c_str(), llifstream::binary); - if (!ifs.is_open()) - { - LL_INFOS() << "Unable to open file " << filename << LL_ENDL; - return false; - } - - std::ostringstream oss; - - oss << ifs.rdbuf(); - str = oss.str(); - ifs.close(); - return true; -} - - - - -// See http://www.unicode.org/Public/BETA/CVTUTF-1-2/ConvertUTF.c -// for the Unicode implementation - this doesn't match because it was written before finding -// it. - - -std::ostream& operator<<(std::ostream &s, const LLWString &wstr) -{ - std::string utf8_str = wstring_to_utf8str(wstr); - s << utf8_str; - return s; -} - -std::string rawstr_to_utf8(const std::string& raw) -{ - LLWString wstr(utf8str_to_wstring(raw)); - return wstring_to_utf8str(wstr); -} - -std::ptrdiff_t wchar_to_utf8chars(llwchar in_char, char* outchars) -{ - U32 cur_char = (U32)in_char; - char* base = outchars; - if (cur_char < 0x80) - { - *outchars++ = (U8)cur_char; - } - else if (cur_char < 0x800) - { - *outchars++ = 0xC0 | (cur_char >> 6); - *outchars++ = 0x80 | (cur_char & 0x3F); - } - else if (cur_char < 0x10000) - { - *outchars++ = 0xE0 | (cur_char >> 12); - *outchars++ = 0x80 | ((cur_char >> 6) & 0x3F); - *outchars++ = 0x80 | (cur_char & 0x3F); - } - else if (cur_char < 0x200000) - { - *outchars++ = 0xF0 | (cur_char >> 18); - *outchars++ = 0x80 | ((cur_char >> 12) & 0x3F); - *outchars++ = 0x80 | ((cur_char >> 6) & 0x3F); - *outchars++ = 0x80 | (cur_char & 0x3F); - } - else if (cur_char < 0x4000000) - { - *outchars++ = 0xF8 | (cur_char >> 24); - *outchars++ = 0x80 | ((cur_char >> 18) & 0x3F); - *outchars++ = 0x80 | ((cur_char >> 12) & 0x3F); - *outchars++ = 0x80 | ((cur_char >> 6) & 0x3F); - *outchars++ = 0x80 | (cur_char & 0x3F); - } - else if (cur_char < 0x80000000) - { - *outchars++ = 0xFC | (cur_char >> 30); - *outchars++ = 0x80 | ((cur_char >> 24) & 0x3F); - *outchars++ = 0x80 | ((cur_char >> 18) & 0x3F); - *outchars++ = 0x80 | ((cur_char >> 12) & 0x3F); - *outchars++ = 0x80 | ((cur_char >> 6) & 0x3F); - *outchars++ = 0x80 | (cur_char & 0x3F); - } - else - { - LL_WARNS() << "Invalid Unicode character " << cur_char << "!" << LL_ENDL; - *outchars++ = LL_UNKNOWN_CHAR; - } - return outchars - base; -} - -auto utf16chars_to_wchar(const U16* inchars, llwchar* outchar) -{ - const U16* base = inchars; - U16 cur_char = *inchars++; - llwchar char32 = cur_char; - if ((cur_char >= 0xD800) && (cur_char <= 0xDFFF)) - { - // Surrogates - char32 = ((llwchar)(cur_char - 0xD800)) << 10; - cur_char = *inchars++; - char32 += (llwchar)(cur_char - 0xDC00) + 0x0010000UL; - } - else - { - char32 = (llwchar)cur_char; - } - *outchar = char32; - return inchars - base; -} - -llutf16string wstring_to_utf16str(const llwchar* utf32str, size_t len) -{ - llutf16string out; - - S32 i = 0; - while (i < len) - { - U32 cur_char = utf32str[i]; - if (cur_char > 0xFFFF) - { - out += (0xD7C0 + (cur_char >> 10)); - out += (0xDC00 | (cur_char & 0x3FF)); - } - else - { - out += cur_char; - } - i++; - } - return out; -} - -llutf16string utf8str_to_utf16str( const char* utf8str, size_t len ) -{ - LLWString wstr = utf8str_to_wstring ( utf8str, len ); - return wstring_to_utf16str ( wstr ); -} - -LLWString utf16str_to_wstring(const U16* utf16str, size_t len) -{ - LLWString wout; - if (len == 0) return wout; - - S32 i = 0; - const U16* chars16 = utf16str; - while (i < len) - { - llwchar cur_char; - i += utf16chars_to_wchar(chars16+i, &cur_char); - wout += cur_char; - } - return wout; -} - -// 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, const S32 utf16_len) -{ - S32 surrogate_pairs = 0; - // ... craziness to make gcc happy (llutf16string.c_str() is tweaked on linux): - const U16 *const utf16_chars = &(*(utf16str.begin())); - S32 i = 0; - while (i < utf16_len) - { - const U16 c = utf16_chars[i++]; - if (c >= 0xD800 && c <= 0xDBFF) // See http://en.wikipedia.org/wiki/UTF-16 - { // Have first byte of a surrogate pair - if (i >= utf16_len) - { - break; - } - const U16 d = utf16_chars[i]; - if (d >= 0xDC00 && d <= 0xDFFF) - { // Have valid second byte of a surrogate pair - surrogate_pairs++; - i++; - } - } - } - return utf16_len - surrogate_pairs; -} - -// Length in utf16string (UTF-16) of wlen wchars beginning at woffset. -S32 wstring_utf16_length(const LLWString &wstr, const S32 woffset, const S32 wlen) -{ - const S32 end = llmin((S32)wstr.length(), woffset + wlen); - if (end < woffset) - { - return 0; - } - else - { - S32 length = end - woffset; - for (S32 i = woffset; i < end; i++) - { - if (wstr[i] >= 0x10000) - { - length++; - } - } - return length; - } -} - -// Given a wstring and an offset in it, returns the length as wstring (i.e., -// number of llwchars) of the longest substring that starts at the offset -// and whose equivalent utf-16 string does not exceeds the given utf16_length. -S32 wstring_wstring_length_from_utf16_length(const LLWString & wstr, const S32 woffset, const S32 utf16_length, bool *unaligned) -{ - const auto end = wstr.length(); - bool u{ false }; - S32 n = woffset + utf16_length; - S32 i = woffset; - while (i < end) - { - if (wstr[i] >= 0x10000) - { - --n; - } - if (i >= n) - { - u = (i > n); - break; - } - i++; - } - if (unaligned) - { - *unaligned = u; - } - return i - woffset; -} - -S32 wchar_utf8_length(const llwchar wc) -{ - if (wc < 0x80) - { - return 1; - } - else if (wc < 0x800) - { - return 2; - } - else if (wc < 0x10000) - { - return 3; - } - else if (wc < 0x200000) - { - return 4; - } - else if (wc < 0x4000000) - { - return 5; - } - else - { - return 6; - } -} - -std::string wchar_utf8_preview(const llwchar wc) -{ - std::ostringstream oss; - oss << std::hex << std::uppercase << (U32)wc; - - U8 out_bytes[8]; - U32 size = (U32)wchar_to_utf8chars(wc, (char*)out_bytes); - - if (size > 1) - { - oss << " ["; - for (U32 i = 0; i < size; ++i) - { - if (i) - { - oss << ", "; - } - oss << (int)out_bytes[i]; - } - oss << "]"; - } - - return oss.str(); -} - -S32 wstring_utf8_length(const LLWString& wstr) -{ - S32 len = 0; - for (S32 i = 0; i < (S32)wstr.length(); i++) - { - len += wchar_utf8_length(wstr[i]); - } - return len; -} - -LLWString utf8str_to_wstring(const char* utf8str, size_t len) -{ - LLWString wout; - - S32 i = 0; - while (i < len) - { - llwchar unichar; - U8 cur_char = utf8str[i]; - - if (cur_char < 0x80) - { - // Ascii character, just add it - unichar = cur_char; - } - else - { - S32 cont_bytes = 0; - if ((cur_char >> 5) == 0x6) // Two byte UTF8 -> 1 UTF32 - { - unichar = (0x1F&cur_char); - cont_bytes = 1; - } - else if ((cur_char >> 4) == 0xe) // Three byte UTF8 -> 1 UTF32 - { - unichar = (0x0F&cur_char); - cont_bytes = 2; - } - else if ((cur_char >> 3) == 0x1e) // Four byte UTF8 -> 1 UTF32 - { - unichar = (0x07&cur_char); - cont_bytes = 3; - } - else if ((cur_char >> 2) == 0x3e) // Five byte UTF8 -> 1 UTF32 - { - unichar = (0x03&cur_char); - cont_bytes = 4; - } - else if ((cur_char >> 1) == 0x7e) // Six byte UTF8 -> 1 UTF32 - { - unichar = (0x01&cur_char); - cont_bytes = 5; - } - else - { - wout += LL_UNKNOWN_CHAR; - ++i; - continue; - } - - // Check that this character doesn't go past the end of the string - auto end = (len < (i + cont_bytes)) ? len : (i + cont_bytes); - do - { - ++i; - - cur_char = utf8str[i]; - if ( (cur_char >> 6) == 0x2 ) - { - unichar <<= 6; - unichar += (0x3F&cur_char); - } - else - { - // Malformed sequence - roll back to look at this as a new char - unichar = LL_UNKNOWN_CHAR; - --i; - break; - } - } while(i < end); - - // Handle overlong characters and NULL characters - if ( ((cont_bytes == 1) && (unichar < 0x80)) - || ((cont_bytes == 2) && (unichar < 0x800)) - || ((cont_bytes == 3) && (unichar < 0x10000)) - || ((cont_bytes == 4) && (unichar < 0x200000)) - || ((cont_bytes == 5) && (unichar < 0x4000000)) ) - { - unichar = LL_UNKNOWN_CHAR; - } - } - - wout += unichar; - ++i; - } - return wout; -} - -std::string wstring_to_utf8str(const llwchar* utf32str, size_t len) -{ - std::string out; - - S32 i = 0; - while (i < len) - { - char tchars[8]; /* Flawfinder: ignore */ - auto n = wchar_to_utf8chars(utf32str[i], tchars); - tchars[n] = 0; - out += tchars; - i++; - } - return out; -} - -std::string utf16str_to_utf8str(const U16* utf16str, size_t len) -{ - return wstring_to_utf8str(utf16str_to_wstring(utf16str, len)); -} - -std::string utf8str_trim(const std::string& utf8str) -{ - LLWString wstr = utf8str_to_wstring(utf8str); - LLWStringUtil::trim(wstr); - return wstring_to_utf8str(wstr); -} - - -std::string utf8str_tolower(const std::string& utf8str) -{ - LLWString out_str = utf8str_to_wstring(utf8str); - LLWStringUtil::toLower(out_str); - return wstring_to_utf8str(out_str); -} - - -S32 utf8str_compare_insensitive(const std::string& lhs, const std::string& rhs) -{ - LLWString wlhs = utf8str_to_wstring(lhs); - LLWString wrhs = utf8str_to_wstring(rhs); - return LLWStringUtil::compareInsensitive(wlhs, wrhs); -} - -std::string utf8str_truncate(const std::string& utf8str, const S32 max_len) -{ - if (0 == max_len) - { - return std::string(); - } - if ((S32)utf8str.length() <= max_len) - { - return utf8str; - } - else - { - S32 cur_char = max_len; - - // If we're ASCII, we don't need to do anything - if ((U8)utf8str[cur_char] > 0x7f) - { - // If first two bits are (10), it's the tail end of a multibyte char. We need to shift back - // to the first character - while (0x80 == (0xc0 & utf8str[cur_char])) - { - cur_char--; - // Keep moving forward until we hit the first char; - if (cur_char == 0) - { - // Make sure we don't trash memory if we've got a bogus string. - break; - } - } - } - // The byte index we're on is one we want to get rid of, so we only want to copy up to (cur_char-1) chars - return utf8str.substr(0, cur_char); - } -} - -std::string utf8str_symbol_truncate(const std::string& utf8str, const S32 symbol_len) -{ - if (0 == symbol_len) - { - return std::string(); - } - if ((S32)utf8str.length() <= symbol_len) - { - return utf8str; - } - else - { - int len = 0, byteIndex = 0; - const char* aStr = utf8str.c_str(); - size_t origSize = utf8str.size(); - - for (byteIndex = 0; len < symbol_len && byteIndex < origSize; byteIndex++) - { - if ((aStr[byteIndex] & 0xc0) != 0x80) - { - len += 1; - } - } - return utf8str.substr(0, byteIndex); - } -} - -std::string utf8str_substChar( - const std::string& utf8str, - const llwchar target_char, - const llwchar replace_char) -{ - LLWString wstr = utf8str_to_wstring(utf8str); - LLWStringUtil::replaceChar(wstr, target_char, replace_char); - //wstr = wstring_substChar(wstr, target_char, replace_char); - return wstring_to_utf8str(wstr); -} - -std::string utf8str_makeASCII(const std::string& utf8str) -{ - LLWString wstr = utf8str_to_wstring(utf8str); - LLWStringUtil::_makeASCII(wstr); - return wstring_to_utf8str(wstr); -} - -std::string mbcsstring_makeASCII(const std::string& wstr) -{ - // Replace non-ASCII chars with replace_char - std::string out_str = wstr; - for (S32 i = 0; i < (S32)out_str.length(); i++) - { - if ((U8)out_str[i] > 0x7f) - { - out_str[i] = LL_UNKNOWN_CHAR; - } - } - return out_str; -} - -std::string utf8str_removeCRLF(const std::string& utf8str) -{ - if (0 == utf8str.length()) - { - return std::string(); - } - const char CR = 13; - - std::string out; - out.reserve(utf8str.length()); - const S32 len = (S32)utf8str.length(); - for( S32 i = 0; i < len; i++ ) - { - if( utf8str[i] != CR ) - { - out.push_back(utf8str[i]); - } - } - return out; -} - -llwchar utf8str_to_wchar(const std::string& utf8str, size_t offset, size_t length) -{ - switch (length) - { - case 2: - return ((utf8str[offset] & 0x1F) << 6) + - (utf8str[offset + 1] & 0x3F); - case 3: - return ((utf8str[offset] & 0x0F) << 12) + - ((utf8str[offset + 1] & 0x3F) << 6) + - (utf8str[offset + 2] & 0x3F); - case 4: - return ((utf8str[offset] & 0x07) << 18) + - ((utf8str[offset + 1] & 0x3F) << 12) + - ((utf8str[offset + 2] & 0x3F) << 6) + - (utf8str[offset + 3] & 0x3F); - case 5: - return ((utf8str[offset] & 0x03) << 24) + - ((utf8str[offset + 1] & 0x3F) << 18) + - ((utf8str[offset + 2] & 0x3F) << 12) + - ((utf8str[offset + 3] & 0x3F) << 6) + - (utf8str[offset + 4] & 0x3F); - case 6: - return ((utf8str[offset] & 0x01) << 30) + - ((utf8str[offset + 1] & 0x3F) << 24) + - ((utf8str[offset + 2] & 0x3F) << 18) + - ((utf8str[offset + 3] & 0x3F) << 12) + - ((utf8str[offset + 4] & 0x3F) << 6) + - (utf8str[offset + 5] & 0x3F); - case 7: - return ((utf8str[offset + 1] & 0x03) << 30) + - ((utf8str[offset + 2] & 0x3F) << 24) + - ((utf8str[offset + 3] & 0x3F) << 18) + - ((utf8str[offset + 4] & 0x3F) << 12) + - ((utf8str[offset + 5] & 0x3F) << 6) + - (utf8str[offset + 6] & 0x3F); - } - return LL_UNKNOWN_CHAR; -} - -std::string utf8str_showBytesUTF8(const std::string& utf8str) -{ - std::string result; - - bool in_sequence = false; - size_t sequence_size = 0; - size_t byte_index = 0; - size_t source_length = utf8str.size(); - - auto open_sequence = [&]() - { - if (!result.empty() && result.back() != '\n') - result += '\n'; // Use LF as a separator before new UTF-8 sequence - result += '['; - in_sequence = true; - }; - - auto close_sequence = [&]() - { - llwchar unicode = utf8str_to_wchar(utf8str, byte_index - sequence_size, sequence_size); - if (unicode != LL_UNKNOWN_CHAR) - { - result += llformat("+%04X", unicode); - } - result += ']'; - in_sequence = false; - sequence_size = 0; - }; - - while (byte_index < source_length) - { - U8 byte = utf8str[byte_index]; - if (byte >= 0x80) // Part of an UTF-8 sequence - { - if (!in_sequence) // Start new UTF-8 sequence - { - open_sequence(); - } - else if (byte >= 0xC0) // Start another UTF-8 sequence - { - close_sequence(); - open_sequence(); - } - else // Continue the same UTF-8 sequence - { - result += '.'; - } - result += llformat("%02X", byte); // The byte is represented in hexadecimal form - ++sequence_size; - } - else // ASCII symbol is represented as a character - { - if (in_sequence) // End of UTF-8 sequence - { - close_sequence(); - if (byte != '\n') - { - result += '\n'; // Use LF as a separator between UTF-8 and ASCII - } - } - result += byte; - } - ++byte_index; - } - - if (in_sequence) // End of UTF-8 sequence - { - close_sequence(); - } - - return result; -} - -#if LL_WINDOWS -unsigned int ll_wstring_default_code_page() -{ - return CP_UTF8; -} - -std::string ll_convert_wide_to_string(const wchar_t* in, size_t len_in, unsigned int code_page) -{ - std::string out; - if(in) - { - int len_out = WideCharToMultiByte( - code_page, - 0, - in, - len_in, - NULL, - 0, - 0, - 0); - // We will need two more bytes for the double NULL ending - // created in WideCharToMultiByte(). - char* pout = new char [len_out + 2]; - memset(pout, 0, len_out + 2); - if(pout) - { - WideCharToMultiByte( - code_page, - 0, - in, - len_in, - pout, - len_out, - 0, - 0); - out.assign(pout); - delete[] pout; - } - } - return out; -} - -std::wstring ll_convert_string_to_wide(const char* in, size_t len, unsigned int code_page) -{ - // From review: - // We can preallocate a wide char buffer that is the same length (in wchar_t elements) as the utf8 input, - // plus one for a null terminator, and be guaranteed to not overflow. - - // Normally, I'd call that sort of thing premature optimization, - // but we *are* seeing string operations taking a bunch of time, especially when constructing widgets. -// int output_str_len = MultiByteToWideChar(code_page, 0, in.c_str(), in.length(), NULL, 0); - - // reserve an output buffer that will be destroyed on exit, with a place - // to put NULL terminator - std::vector<wchar_t> w_out(len + 1); - - memset(&w_out[0], 0, w_out.size()); - int real_output_str_len = MultiByteToWideChar(code_page, 0, in, len, - &w_out[0], w_out.size() - 1); - - //looks like MultiByteToWideChar didn't add null terminator to converted string, see EXT-4858. - w_out[real_output_str_len] = 0; - - // construct string<wchar_t> from our temporary output buffer - return {&w_out[0]}; -} - -LLWString ll_convert_wide_to_wstring(const wchar_t* in, size_t len) -{ - // Whether or not std::wstring and llutf16string are distinct types, they - // both hold UTF-16LE characters. (See header file comments.) Pretend this - // wchar_t* sequence is really a U16* sequence and use the conversion we - // define above. - return utf16str_to_wstring(reinterpret_cast<const U16*>(in), len); -} - -std::wstring ll_convert_wstring_to_wide(const llwchar* in, size_t len) -{ - // first, convert to llutf16string, for which we have a real implementation - auto utf16str{ wstring_to_utf16str(in, len) }; - // then, because each U16 char must be UTF-16LE encoded, pretend the U16* - // string pointer is a wchar_t* and instantiate a std::wstring of the same - // length. - return { reinterpret_cast<const wchar_t*>(utf16str.c_str()), utf16str.length() }; -} - -std::string ll_convert_string_to_utf8_string(const std::string& in) -{ - // If you pass code_page, you must also pass length, otherwise the code - // page parameter will be mistaken for length. - auto w_mesg = ll_convert_string_to_wide(in, in.length(), CP_ACP); - // CP_UTF8 is default -- see ll_wstring_default_code_page() above. - return ll_convert_wide_to_string(w_mesg); -} - -namespace -{ - -void HeapFree_deleter(void* ptr) -{ - // instead of LocalFree(), per https://stackoverflow.com/a/31541205 - HeapFree(GetProcessHeap(), NULL, ptr); -} - -} // anonymous namespace - -template<> -std::wstring windows_message<std::wstring>(DWORD error) -{ - // derived from https://stackoverflow.com/a/455533 - wchar_t* rawptr = nullptr; - auto okay = FormatMessageW( - // use system message tables for GetLastError() codes - FORMAT_MESSAGE_FROM_SYSTEM | - // internally allocate buffer and return its pointer - FORMAT_MESSAGE_ALLOCATE_BUFFER | - // you cannot pass insertion parameters (thanks Gandalf) - FORMAT_MESSAGE_IGNORE_INSERTS | - // ignore line breaks in message definition text - FORMAT_MESSAGE_MAX_WIDTH_MASK, - NULL, // lpSource, unused with FORMAT_MESSAGE_FROM_SYSTEM - error, // dwMessageId - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // dwLanguageId - (LPWSTR)&rawptr, // lpBuffer: force-cast wchar_t** to wchar_t* - 0, // nSize, unused with FORMAT_MESSAGE_ALLOCATE_BUFFER - NULL); // Arguments, unused - - // make a unique_ptr from rawptr so it gets cleaned up properly - std::unique_ptr<wchar_t, void(*)(void*)> bufferptr(rawptr, HeapFree_deleter); - - if (okay && bufferptr) - { - // got the message, return it ('okay' is length in characters) - return { bufferptr.get(), okay }; - } - - // did not get the message, synthesize one - auto format_message_error = GetLastError(); - std::wostringstream out; - out << L"GetLastError() " << error << L" (FormatMessageW() failed with " - << format_message_error << L")"; - return out.str(); -} - -std::optional<std::wstring> llstring_getoptenv(const std::string& key) -{ - auto wkey = ll_convert_string_to_wide(key); - // Take a wild guess as to how big the buffer should be. - std::vector<wchar_t> buffer(1024); - auto n = GetEnvironmentVariableW(wkey.c_str(), &buffer[0], buffer.size()); - // If our initial guess was too short, n will indicate the size (in - // wchar_t's) that buffer should have been, including the terminating nul. - if (n > (buffer.size() - 1)) - { - // make it big enough - buffer.resize(n); - // and try again - n = GetEnvironmentVariableW(wkey.c_str(), &buffer[0], buffer.size()); - } - // did that (ultimately) succeed? - if (n) - { - // great, return populated std::optional - return std::make_optional<std::wstring>(&buffer[0]); - } - - // not successful - auto last_error = GetLastError(); - // Don't bother warning for NOT_FOUND; that's an expected case - if (last_error != ERROR_ENVVAR_NOT_FOUND) - { - LL_WARNS() << "GetEnvironmentVariableW('" << key << "') failed: " - << windows_message<std::string>(last_error) << LL_ENDL; - } - // return empty std::optional - return {}; -} - -#else // ! LL_WINDOWS - -std::optional<std::string> llstring_getoptenv(const std::string& key) -{ - auto found = getenv(key.c_str()); - if (found) - { - // return populated std::optional - return std::make_optional<std::string>(found); - } - else - { - // return empty std::optional - return {}; - } -} - -#endif // ! LL_WINDOWS - -long LLStringOps::sPacificTimeOffset = 0; -long LLStringOps::sLocalTimeOffset = 0; -bool LLStringOps::sPacificDaylightTime = 0; -std::map<std::string, std::string> LLStringOps::datetimeToCodes; - -std::vector<std::string> LLStringOps::sWeekDayList; -std::vector<std::string> LLStringOps::sWeekDayShortList; -std::vector<std::string> LLStringOps::sMonthList; -std::vector<std::string> LLStringOps::sMonthShortList; - - -std::string LLStringOps::sDayFormat; -std::string LLStringOps::sAM; -std::string LLStringOps::sPM; - -// static -bool LLStringOps::isEmoji(llwchar wch) -{ - int ublock = ublock_getCode(wch); - switch (ublock) - { - case UBLOCK_GENERAL_PUNCTUATION: - case UBLOCK_LETTERLIKE_SYMBOLS: - case UBLOCK_ARROWS: - case UBLOCK_MISCELLANEOUS_TECHNICAL: - case UBLOCK_ENCLOSED_ALPHANUMERICS: - case UBLOCK_GEOMETRIC_SHAPES: - case UBLOCK_MISCELLANEOUS_SYMBOLS: - case UBLOCK_DINGBATS: - case UBLOCK_CJK_SYMBOLS_AND_PUNCTUATION: - case UBLOCK_ENCLOSED_CJK_LETTERS_AND_MONTHS: - case UBLOCK_MISCELLANEOUS_SYMBOLS_AND_PICTOGRAPHS: - case UBLOCK_EMOTICONS: - case UBLOCK_TRANSPORT_AND_MAP_SYMBOLS: -#if U_ICU_VERSION_MAJOR_NUM > 56 - // Boost uses ICU so we can't update it independently - case UBLOCK_SUPPLEMENTAL_SYMBOLS_AND_PICTOGRAPHS: -#endif // U_ICU_VERSION_MAJOR_NUM > 56 - return true; - default: -#if U_ICU_VERSION_MAJOR_NUM > 56 - return false; -#else - // See https://en.wikipedia.org/wiki/Supplemental_Symbols_and_Pictographs - return wch >= 0x1F900 && wch <= 0x1F9FF; -#endif // U_ICU_VERSION_MAJOR_NUM > 56 - } -} - - -S32 LLStringOps::collate(const llwchar* a, const llwchar* b) -{ - #if LL_WINDOWS - // in Windows, wide string functions operator on 16-bit strings, - // not the proper 32 bit wide string - return strcmp(wstring_to_utf8str(LLWString(a)).c_str(), wstring_to_utf8str(LLWString(b)).c_str()); - #else - return wcscoll(a, b); - #endif -} - -void LLStringOps::setupDatetimeInfo (bool daylight) -{ - time_t nowT, localT, gmtT; - struct tm * tmpT; - - nowT = time (NULL); - - tmpT = gmtime (&nowT); - gmtT = mktime (tmpT); - - tmpT = localtime (&nowT); - localT = mktime (tmpT); - - sLocalTimeOffset = (long) (gmtT - localT); - if (tmpT->tm_isdst) - { - sLocalTimeOffset -= 60 * 60; // 1 hour - } - - sPacificDaylightTime = daylight; - sPacificTimeOffset = (sPacificDaylightTime? 7 : 8 ) * 60 * 60; - - datetimeToCodes["wkday"] = "%a"; // Thu - datetimeToCodes["weekday"] = "%A"; // Thursday - datetimeToCodes["year4"] = "%Y"; // 2009 - datetimeToCodes["year"] = "%Y"; // 2009 - datetimeToCodes["year2"] = "%y"; // 09 - datetimeToCodes["mth"] = "%b"; // Aug - datetimeToCodes["month"] = "%B"; // August - datetimeToCodes["mthnum"] = "%m"; // 08 - datetimeToCodes["day"] = "%d"; // 31 - datetimeToCodes["sday"] = "%-d"; // 9 - datetimeToCodes["hour24"] = "%H"; // 14 - datetimeToCodes["hour"] = "%H"; // 14 - datetimeToCodes["hour12"] = "%I"; // 02 - datetimeToCodes["min"] = "%M"; // 59 - datetimeToCodes["ampm"] = "%p"; // AM - datetimeToCodes["second"] = "%S"; // 59 - datetimeToCodes["timezone"] = "%Z"; // PST -} - -void tokenizeStringToArray(const std::string& data, std::vector<std::string>& output) -{ - output.clear(); - size_t length = data.size(); - - // tokenize it and put it in the array - std::string cur_word; - for(size_t i = 0; i < length; ++i) - { - if(data[i] == ':') - { - output.push_back(cur_word); - cur_word.clear(); - } - else - { - cur_word.append(1, data[i]); - } - } - output.push_back(cur_word); -} - -void LLStringOps::setupWeekDaysNames(const std::string& data) -{ - tokenizeStringToArray(data,sWeekDayList); -} -void LLStringOps::setupWeekDaysShortNames(const std::string& data) -{ - tokenizeStringToArray(data,sWeekDayShortList); -} -void LLStringOps::setupMonthNames(const std::string& data) -{ - tokenizeStringToArray(data,sMonthList); -} -void LLStringOps::setupMonthShortNames(const std::string& data) -{ - tokenizeStringToArray(data,sMonthShortList); -} -void LLStringOps::setupDayFormat(const std::string& data) -{ - sDayFormat = data; -} - - -std::string LLStringOps::getDatetimeCode (std::string key) -{ - std::map<std::string, std::string>::iterator iter; - - iter = datetimeToCodes.find (key); - if (iter != datetimeToCodes.end()) - { - return iter->second; - } - else - { - return std::string(""); - } -} - -std::string LLStringOps::getReadableNumber(F64 num) -{ - if (fabs(num)>=1e9) - { - return llformat("%.2lfB", num / 1e9); - } - else if (fabs(num)>=1e6) - { - return llformat("%.2lfM", num / 1e6); - } - else if (fabs(num)>=1e3) - { - return llformat("%.2lfK", num / 1e3); - } - else - { - return llformat("%.2lf", num); - } -} - -namespace LLStringFn -{ - // NOTE - this restricts output to ascii - void replace_nonprintable_in_ascii(std::basic_string<char>& string, char replacement) - { - const char MIN = 0x20; - std::basic_string<char>::size_type len = string.size(); - for(std::basic_string<char>::size_type ii = 0; ii < len; ++ii) - { - if(string[ii] < MIN) - { - string[ii] = replacement; - } - } - } - - - // NOTE - this restricts output to ascii - void replace_nonprintable_and_pipe_in_ascii(std::basic_string<char>& str, - char replacement) - { - const char MIN = 0x20; - const char PIPE = 0x7c; - std::basic_string<char>::size_type len = str.size(); - for(std::basic_string<char>::size_type ii = 0; ii < len; ++ii) - { - if( (str[ii] < MIN) || (str[ii] == PIPE) ) - { - str[ii] = replacement; - } - } - } - - // https://wiki.lindenlab.com/wiki/Unicode_Guidelines has details on - // allowable code points for XML. Specifically, they are: - // 0x09, 0x0a, 0x0d, and 0x20 on up. JC - std::string strip_invalid_xml(const std::string& instr) - { - std::string output; - output.reserve( instr.size() ); - std::string::const_iterator it = instr.begin(); - while (it != instr.end()) - { - // Must compare as unsigned for >= - // Test most likely match first - const unsigned char c = (unsigned char)*it; - if ( c >= (unsigned char)0x20 // SPACE - || c == (unsigned char)0x09 // TAB - || c == (unsigned char)0x0a // LINE_FEED - || c == (unsigned char)0x0d ) // CARRIAGE_RETURN - { - output.push_back(c); - } - ++it; - } - return output; - } - - /** - * @brief Replace all control characters (c < 0x20) with replacement in - * string. - */ - void replace_ascii_controlchars(std::basic_string<char>& string, char replacement) - { - const unsigned char MIN = 0x20; - std::basic_string<char>::size_type len = string.size(); - for(std::basic_string<char>::size_type ii = 0; ii < len; ++ii) - { - const unsigned char c = (unsigned char) string[ii]; - if(c < MIN) - { - string[ii] = replacement; - } - } - } -} - -//////////////////////////////////////////////////////////// - -// Forward specialization of LLStringUtil::format before use in LLStringUtil::formatDatetime. -template<> -S32 LLStringUtil::format(std::string& s, const format_map_t& substitutions); - -//static -template<> -void LLStringUtil::getTokens(const std::string& instr, std::vector<std::string >& tokens, const std::string& delims) -{ - // Starting at offset 0, scan forward for the next non-delimiter. We're - // done when the only characters left in 'instr' are delimiters. - for (std::string::size_type begIdx, endIdx = 0; - (begIdx = instr.find_first_not_of (delims, endIdx)) != std::string::npos; ) - { - // Found a non-delimiter. After that, find the next delimiter. - endIdx = instr.find_first_of (delims, begIdx); - if (endIdx == std::string::npos) - { - // No more delimiters: this token extends to the end of the string. - endIdx = instr.length(); - } - - // extract the token between begIdx and endIdx; substr() needs length - std::string currToken(instr.substr(begIdx, endIdx - begIdx)); - LLStringUtil::trim (currToken); - tokens.push_back(currToken); - // next scan past delimiters starts at endIdx - } -} - -template<> -LLStringUtil::size_type LLStringUtil::getSubstitution(const std::string& instr, size_type& start, std::vector<std::string>& tokens) -{ - const std::string delims (","); - - // Find the first [ - size_type pos1 = instr.find('[', start); - if (pos1 == std::string::npos) - return std::string::npos; - - //Find the first ] after the initial [ - size_type pos2 = instr.find(']', pos1); - if (pos2 == std::string::npos) - return std::string::npos; - - // Find the last [ before ] in case of nested [[]] - pos1 = instr.find_last_of('[', pos2-1); - if (pos1 == std::string::npos || pos1 < start) - return std::string::npos; - - getTokens(std::string(instr,pos1+1,pos2-pos1-1), tokens, delims); - start = pos2+1; - - return pos1; -} - -// static -template<> -bool LLStringUtil::simpleReplacement(std::string &replacement, std::string token, const format_map_t& substitutions) -{ - // see if we have a replacement for the bracketed string (without the brackets) - // test first using has() because if we just look up with operator[] we get back an - // empty string even if the value is missing. We want to distinguish between - // missing replacements and deliberately empty replacement strings. - format_map_t::const_iterator iter = substitutions.find(token); - if (iter != substitutions.end()) - { - replacement = iter->second; - return true; - } - // if not, see if there's one WITH brackets - iter = substitutions.find(std::string("[" + token + "]")); - if (iter != substitutions.end()) - { - replacement = iter->second; - return true; - } - - return false; -} - -// static -template<> -bool LLStringUtil::simpleReplacement(std::string &replacement, std::string token, const LLSD& substitutions) -{ - // see if we have a replacement for the bracketed string (without the brackets) - // test first using has() because if we just look up with operator[] we get back an - // empty string even if the value is missing. We want to distinguish between - // missing replacements and deliberately empty replacement strings. - if (substitutions.has(token)) - { - replacement = substitutions[token].asString(); - return true; - } - // if not, see if there's one WITH brackets - else if (substitutions.has(std::string("[" + token + "]"))) - { - replacement = substitutions[std::string("[" + token + "]")].asString(); - return true; - } - - return false; -} - -//static -template<> -void LLStringUtil::setLocale(std::string inLocale) -{ - sLocale = inLocale; -}; - -//static -template<> -std::string LLStringUtil::getLocale(void) -{ - return sLocale; -}; - -// static -template<> -void LLStringUtil::formatNumber(std::string& numStr, std::string decimals) -{ - std::stringstream strStream; - S32 intDecimals = 0; - - convertToS32 (decimals, intDecimals); - if (!sLocale.empty()) - { - // std::locale() throws if the locale is unknown! (EXT-7926) - try - { - strStream.imbue(std::locale(sLocale.c_str())); - } catch (const std::exception &) - { - LL_WARNS_ONCE("Locale") << "Cannot set locale to " << sLocale << LL_ENDL; - } - } - - if (!intDecimals) - { - S32 intStr; - - if (convertToS32(numStr, intStr)) - { - strStream << intStr; - numStr = strStream.str(); - } - } - else - { - F32 floatStr; - - if (convertToF32(numStr, floatStr)) - { - strStream << std::fixed << std::showpoint << std::setprecision(intDecimals) << floatStr; - numStr = strStream.str(); - } - } -} - -// static -template<> -bool LLStringUtil::formatDatetime(std::string& replacement, std::string token, - std::string param, S32 secFromEpoch) -{ - if (param == "local") // local - { - secFromEpoch -= LLStringOps::getLocalTimeOffset(); - } - else if (param != "utc") // slt - { - secFromEpoch -= LLStringOps::getPacificTimeOffset(); - } - - // if never fell into those two ifs above, param must be utc - if (secFromEpoch < 0) secFromEpoch = 0; - - 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 == "local") - { - replacement = ""; // user knows their own timezone - } - else - { -#if 0 - // EXT-1565 : Zai Lynch, James Linden : 15/Oct/09 - // [BSI] Feedback: Viewer clock mentions SLT, but would prefer it to show PST/PDT - // "slt" = Second Life Time, which is deprecated. - // If not utc or user local time, fallback to Pacific time - replacement = LLStringOps::getPacificDaylightTime() ? "PDT" : "PST"; -#else - // SL-20370 : Steeltoe Linden : 29/Sep/23 - // Change "PDT" to "SLT" on menu bar - replacement = "SLT"; -#endif - } - return true; - } - - //EXT-7013 - //few codes are not suppotred by strtime function (example - weekdays for Japanise) - //so use predefined ones - - //if sWeekDayList is not empty than current locale doesn't support - //weekday name. - time_t loc_seconds = (time_t) secFromEpoch; - if(LLStringOps::sWeekDayList.size() == 7 && code == "%A") - { - struct tm * gmt = gmtime (&loc_seconds); - replacement = LLStringOps::sWeekDayList[gmt->tm_wday]; - } - else if(LLStringOps::sWeekDayShortList.size() == 7 && code == "%a") - { - struct tm * gmt = gmtime (&loc_seconds); - replacement = LLStringOps::sWeekDayShortList[gmt->tm_wday]; - } - else if(LLStringOps::sMonthList.size() == 12 && code == "%B") - { - struct tm * gmt = gmtime (&loc_seconds); - replacement = LLStringOps::sMonthList[gmt->tm_mon]; - } - else if( !LLStringOps::sDayFormat.empty() && code == "%d" ) - { - struct tm * gmt = gmtime (&loc_seconds); - LLStringUtil::format_map_t args; - args["[MDAY]"] = llformat ("%d", gmt->tm_mday); - replacement = LLStringOps::sDayFormat; - LLStringUtil::format(replacement, args); - } - else if (code == "%-d") - { - struct tm * gmt = gmtime (&loc_seconds); - replacement = llformat ("%d", gmt->tm_mday); // day of the month without leading zero - } - else if( !LLStringOps::sAM.empty() && !LLStringOps::sPM.empty() && code == "%p" ) - { - struct tm * gmt = gmtime (&loc_seconds); - if(gmt->tm_hour<12) - { - replacement = LLStringOps::sAM; - } - else - { - replacement = LLStringOps::sPM; - } - } - else - { - replacement = datetime.toHTTPDateString(code); - } - - // *HACK: delete leading zero from hour string in case 'hour12' (code = %I) time format - // to show time without leading zero, e.g. 08:16 -> 8:16 (EXT-2738). - // We could have used '%l' format instead, but it's not supported by Windows. - if(code == "%I" && token == "hour12" && replacement.at(0) == '0') - { - replacement = replacement.at(1); - } - - return !code.empty(); -} - -// LLStringUtil::format recogizes the following patterns. -// All substitutions *must* be encased in []'s in the input string. -// The []'s are optional in the substitution map. -// [FOO_123] -// [FOO,number,precision] -// [FOO,datetime,format] - - -// static -template<> -S32 LLStringUtil::format(std::string& s, const format_map_t& substitutions) -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_STRING; - S32 res = 0; - - std::string output; - std::vector<std::string> tokens; - - std::string::size_type start = 0; - std::string::size_type prev_start = 0; - std::string::size_type key_start = 0; - while ((key_start = getSubstitution(s, start, tokens)) != std::string::npos) - { - output += std::string(s, prev_start, key_start-prev_start); - prev_start = start; - - bool found_replacement = false; - std::string replacement; - - if (tokens.size() == 0) - { - found_replacement = false; - } - else if (tokens.size() == 1) - { - found_replacement = simpleReplacement (replacement, tokens[0], substitutions); - } - else if (tokens[1] == "number") - { - std::string param = "0"; - - if (tokens.size() > 2) param = tokens[2]; - found_replacement = simpleReplacement (replacement, tokens[0], substitutions); - if (found_replacement) formatNumber (replacement, param); - } - else if (tokens[1] == "datetime") - { - std::string param; - if (tokens.size() > 2) param = tokens[2]; - - format_map_t::const_iterator iter = substitutions.find("datetime"); - if (iter != substitutions.end()) - { - S32 secFromEpoch = 0; - bool r = LLStringUtil::convertToS32(iter->second, secFromEpoch); - if (r) - { - found_replacement = formatDatetime(replacement, tokens[0], param, secFromEpoch); - } - } - } - - if (found_replacement) - { - output += replacement; - res++; - } - else - { - // we had no replacement, use the string as is - // e.g. "hello [MISSING_REPLACEMENT]" or "-=[Stylized Name]=-" - output += std::string(s, key_start, start-key_start); - } - tokens.clear(); - } - // send the remainder of the string (with no further matches for bracketed names) - output += std::string(s, start); - s = output; - return res; -} - -//static -template<> -S32 LLStringUtil::format(std::string& s, const LLSD& substitutions) -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_STRING; - S32 res = 0; - - if (!substitutions.isMap()) - { - return res; - } - - std::string output; - std::vector<std::string> tokens; - - std::string::size_type start = 0; - std::string::size_type prev_start = 0; - std::string::size_type key_start = 0; - while ((key_start = getSubstitution(s, start, tokens)) != std::string::npos) - { - output += std::string(s, prev_start, key_start-prev_start); - prev_start = start; - - bool found_replacement = false; - std::string replacement; - - if (tokens.size() == 0) - { - found_replacement = false; - } - else if (tokens.size() == 1) - { - found_replacement = simpleReplacement (replacement, tokens[0], substitutions); - } - else if (tokens[1] == "number") - { - std::string param = "0"; - - if (tokens.size() > 2) param = tokens[2]; - found_replacement = simpleReplacement (replacement, tokens[0], substitutions); - if (found_replacement) formatNumber (replacement, param); - } - else if (tokens[1] == "datetime") - { - std::string param; - if (tokens.size() > 2) param = tokens[2]; - - S32 secFromEpoch = (S32) substitutions["datetime"].asInteger(); - found_replacement = formatDatetime (replacement, tokens[0], param, secFromEpoch); - } - - if (found_replacement) - { - output += replacement; - res++; - } - else - { - // we had no replacement, use the string as is - // e.g. "hello [MISSING_REPLACEMENT]" or "-=[Stylized Name]=-" - output += std::string(s, key_start, start-key_start); - } - tokens.clear(); - } - // send the remainder of the string (with no further matches for bracketed names) - output += std::string(s, start); - s = output; - return res; -} - -//////////////////////////////////////////////////////////// -// Testing - -#ifdef _DEBUG - -template<class T> -void LLStringUtilBase<T>::testHarness() -{ - std::string s1; - - llassert( s1.c_str() == NULL ); - llassert( s1.size() == 0 ); - llassert( s1.empty() ); - - std::string s2( "hello"); - llassert( !strcmp( s2.c_str(), "hello" ) ); - llassert( s2.size() == 5 ); - llassert( !s2.empty() ); - std::string s3( s2 ); - - llassert( "hello" == s2 ); - llassert( s2 == "hello" ); - llassert( s2 > "gello" ); - llassert( "gello" < s2 ); - llassert( "gello" != s2 ); - llassert( s2 != "gello" ); - - std::string s4 = s2; - llassert( !s4.empty() ); - s4.empty(); - llassert( s4.empty() ); - - std::string s5(""); - llassert( s5.empty() ); - - llassert( isValidIndex(s5, 0) ); - llassert( !isValidIndex(s5, 1) ); - - s3 = s2; - s4 = "hello again"; - - s4 += "!"; - s4 += s4; - llassert( s4 == "hello again!hello again!" ); - - - std::string s6 = s2 + " " + s2; - std::string s7 = s6; - llassert( s6 == s7 ); - llassert( !( s6 != s7) ); - llassert( !(s6 < s7) ); - llassert( !(s6 > s7) ); - - llassert( !(s6 == "hi")); - llassert( s6 == "hello hello"); - llassert( s6 < "hi"); - - llassert( s6[1] == 'e' ); - s6[1] = 'f'; - llassert( s6[1] == 'f' ); - - s2.erase( 4, 1 ); - llassert( s2 == "hell"); - s2.insert( 0, "y" ); - llassert( s2 == "yhell"); - s2.erase( 1, 3 ); - llassert( s2 == "yl"); - s2.insert( 1, "awn, don't yel"); - llassert( s2 == "yawn, don't yell"); - - std::string s8 = s2.substr( 6, 5 ); - llassert( s8 == "don't" ); - - std::string s9 = " \t\ntest \t\t\n "; - trim(s9); - llassert( s9 == "test" ); - - s8 = "abc123&*(ABC"; - - s9 = s8; - toUpper(s9); - llassert( s9 == "ABC123&*(ABC" ); - - s9 = s8; - toLower(s9); - llassert( s9 == "abc123&*(abc" ); - - - std::string s10( 10, 'x' ); - llassert( s10 == "xxxxxxxxxx" ); - - std::string s11( "monkey in the middle", 7, 2 ); - llassert( s11 == "in" ); - - std::string s12; //empty - s12 += "foo"; - llassert( s12 == "foo" ); - - std::string s13; //empty - s13 += 'f'; - llassert( s13 == "f" ); -} - - -#endif // _DEBUG +/**
+ * @file llstring.cpp
+ * @brief String utility functions and the std::string class.
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llstring.h"
+#include "llerror.h"
+#include "llfasttimer.h"
+#include "llsd.h"
+#include <vector>
+
+#if LL_WINDOWS
+#include "llwin32headerslean.h"
+#include <winnls.h> // for WideCharToMultiByte
+#endif
+
+std::string ll_safe_string(const char* in)
+{
+ if(in) return std::string(in);
+ return std::string();
+}
+
+std::string ll_safe_string(const char* in, S32 maxlen)
+{
+ if(in && maxlen > 0 ) return std::string(in, maxlen);
+
+ return std::string();
+}
+
+bool is_char_hex(char hex)
+{
+ if((hex >= '0') && (hex <= '9'))
+ {
+ return true;
+ }
+ else if((hex >= 'a') && (hex <='f'))
+ {
+ return true;
+ }
+ else if((hex >= 'A') && (hex <='F'))
+ {
+ return true;
+ }
+ return false; // uh - oh, not hex any more...
+}
+
+U8 hex_as_nybble(char hex)
+{
+ if((hex >= '0') && (hex <= '9'))
+ {
+ return (U8)(hex - '0');
+ }
+ else if((hex >= 'a') && (hex <='f'))
+ {
+ return (U8)(10 + hex - 'a');
+ }
+ else if((hex >= 'A') && (hex <='F'))
+ {
+ return (U8)(10 + hex - 'A');
+ }
+ return 0; // uh - oh, not hex any more...
+}
+
+bool iswindividual(llwchar elem)
+{
+ U32 cur_char = (U32)elem;
+ bool result = false;
+ if (0x2E80<= cur_char && cur_char <= 0x9FFF)
+ {
+ result = true;
+ }
+ else if (0xAC00<= cur_char && cur_char <= 0xD7A0 )
+ {
+ result = true;
+ }
+ else if (0xF900<= cur_char && cur_char <= 0xFA60 )
+ {
+ result = true;
+ }
+ return result;
+}
+
+bool _read_file_into_string(std::string& str, const std::string& filename)
+{
+ llifstream ifs(filename.c_str(), llifstream::binary);
+ if (!ifs.is_open())
+ {
+ LL_INFOS() << "Unable to open file " << filename << LL_ENDL;
+ return false;
+ }
+
+ std::ostringstream oss;
+
+ oss << ifs.rdbuf();
+ str = oss.str();
+ ifs.close();
+ return true;
+}
+
+
+
+
+// See http://www.unicode.org/Public/BETA/CVTUTF-1-2/ConvertUTF.c
+// for the Unicode implementation - this doesn't match because it was written before finding
+// it.
+
+
+std::ostream& operator<<(std::ostream &s, const LLWString &wstr)
+{
+ std::string utf8_str = wstring_to_utf8str(wstr);
+ s << utf8_str;
+ return s;
+}
+
+std::string rawstr_to_utf8(const std::string& raw)
+{
+ LLWString wstr(utf8str_to_wstring(raw));
+ return wstring_to_utf8str(wstr);
+}
+
+std::ptrdiff_t wchar_to_utf8chars(llwchar in_char, char* outchars)
+{
+ U32 cur_char = (U32)in_char;
+ char* base = outchars;
+ if (cur_char < 0x80)
+ {
+ *outchars++ = (U8)cur_char;
+ }
+ else if (cur_char < 0x800)
+ {
+ *outchars++ = 0xC0 | (cur_char >> 6);
+ *outchars++ = 0x80 | (cur_char & 0x3F);
+ }
+ else if (cur_char < 0x10000)
+ {
+ *outchars++ = 0xE0 | (cur_char >> 12);
+ *outchars++ = 0x80 | ((cur_char >> 6) & 0x3F);
+ *outchars++ = 0x80 | (cur_char & 0x3F);
+ }
+ else if (cur_char < 0x200000)
+ {
+ *outchars++ = 0xF0 | (cur_char >> 18);
+ *outchars++ = 0x80 | ((cur_char >> 12) & 0x3F);
+ *outchars++ = 0x80 | ((cur_char >> 6) & 0x3F);
+ *outchars++ = 0x80 | (cur_char & 0x3F);
+ }
+ else if (cur_char < 0x4000000)
+ {
+ *outchars++ = 0xF8 | (cur_char >> 24);
+ *outchars++ = 0x80 | ((cur_char >> 18) & 0x3F);
+ *outchars++ = 0x80 | ((cur_char >> 12) & 0x3F);
+ *outchars++ = 0x80 | ((cur_char >> 6) & 0x3F);
+ *outchars++ = 0x80 | (cur_char & 0x3F);
+ }
+ else if (cur_char < 0x80000000)
+ {
+ *outchars++ = 0xFC | (cur_char >> 30);
+ *outchars++ = 0x80 | ((cur_char >> 24) & 0x3F);
+ *outchars++ = 0x80 | ((cur_char >> 18) & 0x3F);
+ *outchars++ = 0x80 | ((cur_char >> 12) & 0x3F);
+ *outchars++ = 0x80 | ((cur_char >> 6) & 0x3F);
+ *outchars++ = 0x80 | (cur_char & 0x3F);
+ }
+ else
+ {
+ LL_WARNS() << "Invalid Unicode character " << cur_char << "!" << LL_ENDL;
+ *outchars++ = LL_UNKNOWN_CHAR;
+ }
+ return outchars - base;
+}
+
+auto utf16chars_to_wchar(const U16* inchars, llwchar* outchar)
+{
+ const U16* base = inchars;
+ U16 cur_char = *inchars++;
+ llwchar char32 = cur_char;
+ if ((cur_char >= 0xD800) && (cur_char <= 0xDFFF))
+ {
+ // Surrogates
+ char32 = ((llwchar)(cur_char - 0xD800)) << 10;
+ cur_char = *inchars++;
+ char32 += (llwchar)(cur_char - 0xDC00) + 0x0010000UL;
+ }
+ else
+ {
+ char32 = (llwchar)cur_char;
+ }
+ *outchar = char32;
+ return inchars - base;
+}
+
+llutf16string wstring_to_utf16str(const llwchar* utf32str, size_t len)
+{
+ llutf16string out;
+
+ S32 i = 0;
+ while (i < len)
+ {
+ U32 cur_char = utf32str[i];
+ if (cur_char > 0xFFFF)
+ {
+ out += (0xD7C0 + (cur_char >> 10));
+ out += (0xDC00 | (cur_char & 0x3FF));
+ }
+ else
+ {
+ out += cur_char;
+ }
+ i++;
+ }
+ return out;
+}
+
+llutf16string utf8str_to_utf16str( const char* utf8str, size_t len )
+{
+ LLWString wstr = utf8str_to_wstring ( utf8str, len );
+ return wstring_to_utf16str ( wstr );
+}
+
+LLWString utf16str_to_wstring(const U16* utf16str, size_t len)
+{
+ LLWString wout;
+ if (len == 0) return wout;
+
+ S32 i = 0;
+ const U16* chars16 = utf16str;
+ while (i < len)
+ {
+ llwchar cur_char;
+ i += utf16chars_to_wchar(chars16+i, &cur_char);
+ wout += cur_char;
+ }
+ return wout;
+}
+
+// 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, const S32 utf16_len)
+{
+ S32 surrogate_pairs = 0;
+ // ... craziness to make gcc happy (llutf16string.c_str() is tweaked on linux):
+ const U16 *const utf16_chars = &(*(utf16str.begin()));
+ S32 i = 0;
+ while (i < utf16_len)
+ {
+ const U16 c = utf16_chars[i++];
+ if (c >= 0xD800 && c <= 0xDBFF) // See http://en.wikipedia.org/wiki/UTF-16
+ { // Have first byte of a surrogate pair
+ if (i >= utf16_len)
+ {
+ break;
+ }
+ const U16 d = utf16_chars[i];
+ if (d >= 0xDC00 && d <= 0xDFFF)
+ { // Have valid second byte of a surrogate pair
+ surrogate_pairs++;
+ i++;
+ }
+ }
+ }
+ return utf16_len - surrogate_pairs;
+}
+
+// Length in utf16string (UTF-16) of wlen wchars beginning at woffset.
+S32 wstring_utf16_length(const LLWString &wstr, const S32 woffset, const S32 wlen)
+{
+ const S32 end = llmin((S32)wstr.length(), woffset + wlen);
+ if (end < woffset)
+ {
+ return 0;
+ }
+ else
+ {
+ S32 length = end - woffset;
+ for (S32 i = woffset; i < end; i++)
+ {
+ if (wstr[i] >= 0x10000)
+ {
+ length++;
+ }
+ }
+ return length;
+ }
+}
+
+// Given a wstring and an offset in it, returns the length as wstring (i.e.,
+// number of llwchars) of the longest substring that starts at the offset
+// and whose equivalent utf-16 string does not exceeds the given utf16_length.
+S32 wstring_wstring_length_from_utf16_length(const LLWString & wstr, const S32 woffset, const S32 utf16_length, bool *unaligned)
+{
+ const auto end = wstr.length();
+ bool u{ false };
+ S32 n = woffset + utf16_length;
+ S32 i = woffset;
+ while (i < end)
+ {
+ if (wstr[i] >= 0x10000)
+ {
+ --n;
+ }
+ if (i >= n)
+ {
+ u = (i > n);
+ break;
+ }
+ i++;
+ }
+ if (unaligned)
+ {
+ *unaligned = u;
+ }
+ return i - woffset;
+}
+
+S32 wchar_utf8_length(const llwchar wc)
+{
+ if (wc < 0x80)
+ {
+ return 1;
+ }
+ else if (wc < 0x800)
+ {
+ return 2;
+ }
+ else if (wc < 0x10000)
+ {
+ return 3;
+ }
+ else if (wc < 0x200000)
+ {
+ return 4;
+ }
+ else if (wc < 0x4000000)
+ {
+ return 5;
+ }
+ else
+ {
+ return 6;
+ }
+}
+
+std::string wchar_utf8_preview(const llwchar wc)
+{
+ std::ostringstream oss;
+ oss << std::hex << std::uppercase << (U32)wc;
+
+ U8 out_bytes[8];
+ U32 size = (U32)wchar_to_utf8chars(wc, (char*)out_bytes);
+
+ if (size > 1)
+ {
+ oss << " [";
+ for (U32 i = 0; i < size; ++i)
+ {
+ if (i)
+ {
+ oss << ", ";
+ }
+ oss << (int)out_bytes[i];
+ }
+ oss << "]";
+ }
+
+ return oss.str();
+}
+
+S32 wstring_utf8_length(const LLWString& wstr)
+{
+ S32 len = 0;
+ for (S32 i = 0; i < (S32)wstr.length(); i++)
+ {
+ len += wchar_utf8_length(wstr[i]);
+ }
+ return len;
+}
+
+LLWString utf8str_to_wstring(const char* utf8str, size_t len)
+{
+ LLWString wout;
+
+ S32 i = 0;
+ while (i < len)
+ {
+ llwchar unichar;
+ U8 cur_char = utf8str[i];
+
+ if (cur_char < 0x80)
+ {
+ // Ascii character, just add it
+ unichar = cur_char;
+ }
+ else
+ {
+ S32 cont_bytes = 0;
+ if ((cur_char >> 5) == 0x6) // Two byte UTF8 -> 1 UTF32
+ {
+ unichar = (0x1F&cur_char);
+ cont_bytes = 1;
+ }
+ else if ((cur_char >> 4) == 0xe) // Three byte UTF8 -> 1 UTF32
+ {
+ unichar = (0x0F&cur_char);
+ cont_bytes = 2;
+ }
+ else if ((cur_char >> 3) == 0x1e) // Four byte UTF8 -> 1 UTF32
+ {
+ unichar = (0x07&cur_char);
+ cont_bytes = 3;
+ }
+ else if ((cur_char >> 2) == 0x3e) // Five byte UTF8 -> 1 UTF32
+ {
+ unichar = (0x03&cur_char);
+ cont_bytes = 4;
+ }
+ else if ((cur_char >> 1) == 0x7e) // Six byte UTF8 -> 1 UTF32
+ {
+ unichar = (0x01&cur_char);
+ cont_bytes = 5;
+ }
+ else
+ {
+ wout += LL_UNKNOWN_CHAR;
+ ++i;
+ continue;
+ }
+
+ // Check that this character doesn't go past the end of the string
+ auto end = (len < (i + cont_bytes)) ? len : (i + cont_bytes);
+ do
+ {
+ ++i;
+
+ cur_char = utf8str[i];
+ if ( (cur_char >> 6) == 0x2 )
+ {
+ unichar <<= 6;
+ unichar += (0x3F&cur_char);
+ }
+ else
+ {
+ // Malformed sequence - roll back to look at this as a new char
+ unichar = LL_UNKNOWN_CHAR;
+ --i;
+ break;
+ }
+ } while(i < end);
+
+ // Handle overlong characters and NULL characters
+ if ( ((cont_bytes == 1) && (unichar < 0x80))
+ || ((cont_bytes == 2) && (unichar < 0x800))
+ || ((cont_bytes == 3) && (unichar < 0x10000))
+ || ((cont_bytes == 4) && (unichar < 0x200000))
+ || ((cont_bytes == 5) && (unichar < 0x4000000)) )
+ {
+ unichar = LL_UNKNOWN_CHAR;
+ }
+ }
+
+ wout += unichar;
+ ++i;
+ }
+ return wout;
+}
+
+std::string wstring_to_utf8str(const llwchar* utf32str, size_t len)
+{
+ std::string out;
+
+ S32 i = 0;
+ while (i < len)
+ {
+ char tchars[8]; /* Flawfinder: ignore */
+ auto n = wchar_to_utf8chars(utf32str[i], tchars);
+ tchars[n] = 0;
+ out += tchars;
+ i++;
+ }
+ return out;
+}
+
+std::string utf16str_to_utf8str(const U16* utf16str, size_t len)
+{
+ return wstring_to_utf8str(utf16str_to_wstring(utf16str, len));
+}
+
+std::string utf8str_trim(const std::string& utf8str)
+{
+ LLWString wstr = utf8str_to_wstring(utf8str);
+ LLWStringUtil::trim(wstr);
+ return wstring_to_utf8str(wstr);
+}
+
+
+std::string utf8str_tolower(const std::string& utf8str)
+{
+ LLWString out_str = utf8str_to_wstring(utf8str);
+ LLWStringUtil::toLower(out_str);
+ return wstring_to_utf8str(out_str);
+}
+
+
+S32 utf8str_compare_insensitive(const std::string& lhs, const std::string& rhs)
+{
+ LLWString wlhs = utf8str_to_wstring(lhs);
+ LLWString wrhs = utf8str_to_wstring(rhs);
+ return LLWStringUtil::compareInsensitive(wlhs, wrhs);
+}
+
+std::string utf8str_truncate(const std::string& utf8str, const S32 max_len)
+{
+ if (0 == max_len)
+ {
+ return std::string();
+ }
+ if ((S32)utf8str.length() <= max_len)
+ {
+ return utf8str;
+ }
+ else
+ {
+ S32 cur_char = max_len;
+
+ // If we're ASCII, we don't need to do anything
+ if ((U8)utf8str[cur_char] > 0x7f)
+ {
+ // If first two bits are (10), it's the tail end of a multibyte char. We need to shift back
+ // to the first character
+ while (0x80 == (0xc0 & utf8str[cur_char]))
+ {
+ cur_char--;
+ // Keep moving forward until we hit the first char;
+ if (cur_char == 0)
+ {
+ // Make sure we don't trash memory if we've got a bogus string.
+ break;
+ }
+ }
+ }
+ // The byte index we're on is one we want to get rid of, so we only want to copy up to (cur_char-1) chars
+ return utf8str.substr(0, cur_char);
+ }
+}
+
+std::string utf8str_symbol_truncate(const std::string& utf8str, const S32 symbol_len)
+{
+ if (0 == symbol_len)
+ {
+ return std::string();
+ }
+ if ((S32)utf8str.length() <= symbol_len)
+ {
+ return utf8str;
+ }
+ else
+ {
+ int len = 0, byteIndex = 0;
+ const char* aStr = utf8str.c_str();
+ size_t origSize = utf8str.size();
+
+ for (byteIndex = 0; len < symbol_len && byteIndex < origSize; byteIndex++)
+ {
+ if ((aStr[byteIndex] & 0xc0) != 0x80)
+ {
+ len += 1;
+ }
+ }
+ return utf8str.substr(0, byteIndex);
+ }
+}
+
+std::string utf8str_substChar(
+ const std::string& utf8str,
+ const llwchar target_char,
+ const llwchar replace_char)
+{
+ LLWString wstr = utf8str_to_wstring(utf8str);
+ LLWStringUtil::replaceChar(wstr, target_char, replace_char);
+ //wstr = wstring_substChar(wstr, target_char, replace_char);
+ return wstring_to_utf8str(wstr);
+}
+
+std::string utf8str_makeASCII(const std::string& utf8str)
+{
+ LLWString wstr = utf8str_to_wstring(utf8str);
+ LLWStringUtil::_makeASCII(wstr);
+ return wstring_to_utf8str(wstr);
+}
+
+std::string mbcsstring_makeASCII(const std::string& wstr)
+{
+ // Replace non-ASCII chars with replace_char
+ std::string out_str = wstr;
+ for (S32 i = 0; i < (S32)out_str.length(); i++)
+ {
+ if ((U8)out_str[i] > 0x7f)
+ {
+ out_str[i] = LL_UNKNOWN_CHAR;
+ }
+ }
+ return out_str;
+}
+
+std::string utf8str_removeCRLF(const std::string& utf8str)
+{
+ if (0 == utf8str.length())
+ {
+ return std::string();
+ }
+ const char CR = 13;
+
+ std::string out;
+ out.reserve(utf8str.length());
+ const S32 len = (S32)utf8str.length();
+ for( S32 i = 0; i < len; i++ )
+ {
+ if( utf8str[i] != CR )
+ {
+ out.push_back(utf8str[i]);
+ }
+ }
+ return out;
+}
+
+llwchar utf8str_to_wchar(const std::string& utf8str, size_t offset, size_t length)
+{
+ switch (length)
+ {
+ case 2:
+ return ((utf8str[offset] & 0x1F) << 6) +
+ (utf8str[offset + 1] & 0x3F);
+ case 3:
+ return ((utf8str[offset] & 0x0F) << 12) +
+ ((utf8str[offset + 1] & 0x3F) << 6) +
+ (utf8str[offset + 2] & 0x3F);
+ case 4:
+ return ((utf8str[offset] & 0x07) << 18) +
+ ((utf8str[offset + 1] & 0x3F) << 12) +
+ ((utf8str[offset + 2] & 0x3F) << 6) +
+ (utf8str[offset + 3] & 0x3F);
+ case 5:
+ return ((utf8str[offset] & 0x03) << 24) +
+ ((utf8str[offset + 1] & 0x3F) << 18) +
+ ((utf8str[offset + 2] & 0x3F) << 12) +
+ ((utf8str[offset + 3] & 0x3F) << 6) +
+ (utf8str[offset + 4] & 0x3F);
+ case 6:
+ return ((utf8str[offset] & 0x01) << 30) +
+ ((utf8str[offset + 1] & 0x3F) << 24) +
+ ((utf8str[offset + 2] & 0x3F) << 18) +
+ ((utf8str[offset + 3] & 0x3F) << 12) +
+ ((utf8str[offset + 4] & 0x3F) << 6) +
+ (utf8str[offset + 5] & 0x3F);
+ case 7:
+ return ((utf8str[offset + 1] & 0x03) << 30) +
+ ((utf8str[offset + 2] & 0x3F) << 24) +
+ ((utf8str[offset + 3] & 0x3F) << 18) +
+ ((utf8str[offset + 4] & 0x3F) << 12) +
+ ((utf8str[offset + 5] & 0x3F) << 6) +
+ (utf8str[offset + 6] & 0x3F);
+ }
+ return LL_UNKNOWN_CHAR;
+}
+
+std::string utf8str_showBytesUTF8(const std::string& utf8str)
+{
+ std::string result;
+
+ bool in_sequence = false;
+ size_t sequence_size = 0;
+ size_t byte_index = 0;
+ size_t source_length = utf8str.size();
+
+ auto open_sequence = [&]()
+ {
+ if (!result.empty() && result.back() != '\n')
+ result += '\n'; // Use LF as a separator before new UTF-8 sequence
+ result += '[';
+ in_sequence = true;
+ };
+
+ auto close_sequence = [&]()
+ {
+ llwchar unicode = utf8str_to_wchar(utf8str, byte_index - sequence_size, sequence_size);
+ if (unicode != LL_UNKNOWN_CHAR)
+ {
+ result += llformat("+%04X", unicode);
+ }
+ result += ']';
+ in_sequence = false;
+ sequence_size = 0;
+ };
+
+ while (byte_index < source_length)
+ {
+ U8 byte = utf8str[byte_index];
+ if (byte >= 0x80) // Part of an UTF-8 sequence
+ {
+ if (!in_sequence) // Start new UTF-8 sequence
+ {
+ open_sequence();
+ }
+ else if (byte >= 0xC0) // Start another UTF-8 sequence
+ {
+ close_sequence();
+ open_sequence();
+ }
+ else // Continue the same UTF-8 sequence
+ {
+ result += '.';
+ }
+ result += llformat("%02X", byte); // The byte is represented in hexadecimal form
+ ++sequence_size;
+ }
+ else // ASCII symbol is represented as a character
+ {
+ if (in_sequence) // End of UTF-8 sequence
+ {
+ close_sequence();
+ if (byte != '\n')
+ {
+ result += '\n'; // Use LF as a separator between UTF-8 and ASCII
+ }
+ }
+ result += byte;
+ }
+ ++byte_index;
+ }
+
+ if (in_sequence) // End of UTF-8 sequence
+ {
+ close_sequence();
+ }
+
+ return result;
+}
+
+// Search for any emoji symbol, return true if found
+bool wstring_has_emoji(const LLWString& wstr)
+{
+ for (const llwchar& wch : wstr)
+ {
+ if (LLStringOps::isEmoji(wch))
+ return true;
+ }
+
+ return false;
+}
+
+// Cut emoji symbols if exist
+bool wstring_remove_emojis(LLWString& wstr)
+{
+ bool found = false;
+ for (size_t i = 0; i < wstr.size(); ++i)
+ {
+ if (LLStringOps::isEmoji(wstr[i]))
+ {
+ wstr.erase(i--, 1);
+ found = true;
+ }
+ }
+ return found;
+}
+
+// Cut emoji symbols if exist
+bool utf8str_remove_emojis(std::string& utf8str)
+{
+ LLWString wstr = utf8str_to_wstring(utf8str);
+ if (!wstring_remove_emojis(wstr))
+ return false;
+ utf8str = wstring_to_utf8str(wstr);
+ return true;
+}
+
+#if LL_WINDOWS
+unsigned int ll_wstring_default_code_page()
+{
+ return CP_UTF8;
+}
+
+std::string ll_convert_wide_to_string(const wchar_t* in, size_t len_in, unsigned int code_page)
+{
+ std::string out;
+ if(in)
+ {
+ int len_out = WideCharToMultiByte(
+ code_page,
+ 0,
+ in,
+ len_in,
+ NULL,
+ 0,
+ 0,
+ 0);
+ // We will need two more bytes for the double NULL ending
+ // created in WideCharToMultiByte().
+ char* pout = new char [len_out + 2];
+ memset(pout, 0, len_out + 2);
+ if(pout)
+ {
+ WideCharToMultiByte(
+ code_page,
+ 0,
+ in,
+ len_in,
+ pout,
+ len_out,
+ 0,
+ 0);
+ out.assign(pout);
+ delete[] pout;
+ }
+ }
+ return out;
+}
+
+std::wstring ll_convert_string_to_wide(const char* in, size_t len, unsigned int code_page)
+{
+ // From review:
+ // We can preallocate a wide char buffer that is the same length (in wchar_t elements) as the utf8 input,
+ // plus one for a null terminator, and be guaranteed to not overflow.
+
+ // Normally, I'd call that sort of thing premature optimization,
+ // but we *are* seeing string operations taking a bunch of time, especially when constructing widgets.
+// int output_str_len = MultiByteToWideChar(code_page, 0, in.c_str(), in.length(), NULL, 0);
+
+ // reserve an output buffer that will be destroyed on exit, with a place
+ // to put NULL terminator
+ std::vector<wchar_t> w_out(len + 1);
+
+ memset(&w_out[0], 0, w_out.size());
+ int real_output_str_len = MultiByteToWideChar(code_page, 0, in, len,
+ &w_out[0], w_out.size() - 1);
+
+ //looks like MultiByteToWideChar didn't add null terminator to converted string, see EXT-4858.
+ w_out[real_output_str_len] = 0;
+
+ // construct string<wchar_t> from our temporary output buffer
+ return {&w_out[0]};
+}
+
+LLWString ll_convert_wide_to_wstring(const wchar_t* in, size_t len)
+{
+ // Whether or not std::wstring and llutf16string are distinct types, they
+ // both hold UTF-16LE characters. (See header file comments.) Pretend this
+ // wchar_t* sequence is really a U16* sequence and use the conversion we
+ // define above.
+ return utf16str_to_wstring(reinterpret_cast<const U16*>(in), len);
+}
+
+std::wstring ll_convert_wstring_to_wide(const llwchar* in, size_t len)
+{
+ // first, convert to llutf16string, for which we have a real implementation
+ auto utf16str{ wstring_to_utf16str(in, len) };
+ // then, because each U16 char must be UTF-16LE encoded, pretend the U16*
+ // string pointer is a wchar_t* and instantiate a std::wstring of the same
+ // length.
+ return { reinterpret_cast<const wchar_t*>(utf16str.c_str()), utf16str.length() };
+}
+
+std::string ll_convert_string_to_utf8_string(const std::string& in)
+{
+ // If you pass code_page, you must also pass length, otherwise the code
+ // page parameter will be mistaken for length.
+ auto w_mesg = ll_convert_string_to_wide(in, in.length(), CP_ACP);
+ // CP_UTF8 is default -- see ll_wstring_default_code_page() above.
+ return ll_convert_wide_to_string(w_mesg);
+}
+
+namespace
+{
+
+void HeapFree_deleter(void* ptr)
+{
+ // instead of LocalFree(), per https://stackoverflow.com/a/31541205
+ HeapFree(GetProcessHeap(), NULL, ptr);
+}
+
+} // anonymous namespace
+
+template<>
+std::wstring windows_message<std::wstring>(DWORD error)
+{
+ // derived from https://stackoverflow.com/a/455533
+ wchar_t* rawptr = nullptr;
+ auto okay = FormatMessageW(
+ // use system message tables for GetLastError() codes
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ // internally allocate buffer and return its pointer
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ // you cannot pass insertion parameters (thanks Gandalf)
+ FORMAT_MESSAGE_IGNORE_INSERTS |
+ // ignore line breaks in message definition text
+ FORMAT_MESSAGE_MAX_WIDTH_MASK,
+ NULL, // lpSource, unused with FORMAT_MESSAGE_FROM_SYSTEM
+ error, // dwMessageId
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // dwLanguageId
+ (LPWSTR)&rawptr, // lpBuffer: force-cast wchar_t** to wchar_t*
+ 0, // nSize, unused with FORMAT_MESSAGE_ALLOCATE_BUFFER
+ NULL); // Arguments, unused
+
+ // make a unique_ptr from rawptr so it gets cleaned up properly
+ std::unique_ptr<wchar_t, void(*)(void*)> bufferptr(rawptr, HeapFree_deleter);
+
+ if (okay && bufferptr)
+ {
+ // got the message, return it ('okay' is length in characters)
+ return { bufferptr.get(), okay };
+ }
+
+ // did not get the message, synthesize one
+ auto format_message_error = GetLastError();
+ std::wostringstream out;
+ out << L"GetLastError() " << error << L" (FormatMessageW() failed with "
+ << format_message_error << L")";
+ return out.str();
+}
+
+std::optional<std::wstring> llstring_getoptenv(const std::string& key)
+{
+ auto wkey = ll_convert_string_to_wide(key);
+ // Take a wild guess as to how big the buffer should be.
+ std::vector<wchar_t> buffer(1024);
+ auto n = GetEnvironmentVariableW(wkey.c_str(), &buffer[0], buffer.size());
+ // If our initial guess was too short, n will indicate the size (in
+ // wchar_t's) that buffer should have been, including the terminating nul.
+ if (n > (buffer.size() - 1))
+ {
+ // make it big enough
+ buffer.resize(n);
+ // and try again
+ n = GetEnvironmentVariableW(wkey.c_str(), &buffer[0], buffer.size());
+ }
+ // did that (ultimately) succeed?
+ if (n)
+ {
+ // great, return populated std::optional
+ return std::make_optional<std::wstring>(&buffer[0]);
+ }
+
+ // not successful
+ auto last_error = GetLastError();
+ // Don't bother warning for NOT_FOUND; that's an expected case
+ if (last_error != ERROR_ENVVAR_NOT_FOUND)
+ {
+ LL_WARNS() << "GetEnvironmentVariableW('" << key << "') failed: "
+ << windows_message<std::string>(last_error) << LL_ENDL;
+ }
+ // return empty std::optional
+ return {};
+}
+
+#else // ! LL_WINDOWS
+
+std::optional<std::string> llstring_getoptenv(const std::string& key)
+{
+ auto found = getenv(key.c_str());
+ if (found)
+ {
+ // return populated std::optional
+ return std::make_optional<std::string>(found);
+ }
+ else
+ {
+ // return empty std::optional
+ return {};
+ }
+}
+
+#endif // ! LL_WINDOWS
+
+long LLStringOps::sPacificTimeOffset = 0;
+long LLStringOps::sLocalTimeOffset = 0;
+bool LLStringOps::sPacificDaylightTime = 0;
+std::map<std::string, std::string> LLStringOps::datetimeToCodes;
+
+std::vector<std::string> LLStringOps::sWeekDayList;
+std::vector<std::string> LLStringOps::sWeekDayShortList;
+std::vector<std::string> LLStringOps::sMonthList;
+std::vector<std::string> LLStringOps::sMonthShortList;
+
+
+std::string LLStringOps::sDayFormat;
+std::string LLStringOps::sAM;
+std::string LLStringOps::sPM;
+
+// static
+bool LLStringOps::isEmoji(llwchar a)
+{
+#if 0 // Do not consider special characters that might have a corresponding
+ // glyph in the monochorme fallback fonts as a "genuine" emoji. HB
+ return a == 0xa9 || a == 0xae || (a >= 0x2000 && a < 0x3300) ||
+ (a >= 0x1f000 && a < 0x20000);
+#else
+ // These are indeed "genuine" emojis, we *do want* rendered as such. HB
+ return a >= 0x1f000 && a < 0x20000;
+#endif
+ }
+
+S32 LLStringOps::collate(const llwchar* a, const llwchar* b)
+{
+ #if LL_WINDOWS
+ // in Windows, wide string functions operator on 16-bit strings,
+ // not the proper 32 bit wide string
+ return strcmp(wstring_to_utf8str(LLWString(a)).c_str(), wstring_to_utf8str(LLWString(b)).c_str());
+ #else
+ return wcscoll(a, b);
+ #endif
+}
+
+void LLStringOps::setupDatetimeInfo (bool daylight)
+{
+ time_t nowT, localT, gmtT;
+ struct tm * tmpT;
+
+ nowT = time (NULL);
+
+ tmpT = gmtime (&nowT);
+ gmtT = mktime (tmpT);
+
+ tmpT = localtime (&nowT);
+ localT = mktime (tmpT);
+
+ sLocalTimeOffset = (long) (gmtT - localT);
+ if (tmpT->tm_isdst)
+ {
+ sLocalTimeOffset -= 60 * 60; // 1 hour
+ }
+
+ sPacificDaylightTime = daylight;
+ sPacificTimeOffset = (sPacificDaylightTime? 7 : 8 ) * 60 * 60;
+
+ datetimeToCodes["wkday"] = "%a"; // Thu
+ datetimeToCodes["weekday"] = "%A"; // Thursday
+ datetimeToCodes["year4"] = "%Y"; // 2009
+ datetimeToCodes["year"] = "%Y"; // 2009
+ datetimeToCodes["year2"] = "%y"; // 09
+ datetimeToCodes["mth"] = "%b"; // Aug
+ datetimeToCodes["month"] = "%B"; // August
+ datetimeToCodes["mthnum"] = "%m"; // 08
+ datetimeToCodes["day"] = "%d"; // 31
+ datetimeToCodes["sday"] = "%-d"; // 9
+ datetimeToCodes["hour24"] = "%H"; // 14
+ datetimeToCodes["hour"] = "%H"; // 14
+ datetimeToCodes["hour12"] = "%I"; // 02
+ datetimeToCodes["min"] = "%M"; // 59
+ datetimeToCodes["ampm"] = "%p"; // AM
+ datetimeToCodes["second"] = "%S"; // 59
+ datetimeToCodes["timezone"] = "%Z"; // PST
+}
+
+void tokenizeStringToArray(const std::string& data, std::vector<std::string>& output)
+{
+ output.clear();
+ size_t length = data.size();
+
+ // tokenize it and put it in the array
+ std::string cur_word;
+ for(size_t i = 0; i < length; ++i)
+ {
+ if(data[i] == ':')
+ {
+ output.push_back(cur_word);
+ cur_word.clear();
+ }
+ else
+ {
+ cur_word.append(1, data[i]);
+ }
+ }
+ output.push_back(cur_word);
+}
+
+void LLStringOps::setupWeekDaysNames(const std::string& data)
+{
+ tokenizeStringToArray(data,sWeekDayList);
+}
+void LLStringOps::setupWeekDaysShortNames(const std::string& data)
+{
+ tokenizeStringToArray(data,sWeekDayShortList);
+}
+void LLStringOps::setupMonthNames(const std::string& data)
+{
+ tokenizeStringToArray(data,sMonthList);
+}
+void LLStringOps::setupMonthShortNames(const std::string& data)
+{
+ tokenizeStringToArray(data,sMonthShortList);
+}
+void LLStringOps::setupDayFormat(const std::string& data)
+{
+ sDayFormat = data;
+}
+
+
+std::string LLStringOps::getDatetimeCode (std::string key)
+{
+ std::map<std::string, std::string>::iterator iter;
+
+ iter = datetimeToCodes.find (key);
+ if (iter != datetimeToCodes.end())
+ {
+ return iter->second;
+ }
+ else
+ {
+ return std::string("");
+ }
+}
+
+std::string LLStringOps::getReadableNumber(F64 num)
+{
+ if (fabs(num)>=1e9)
+ {
+ return llformat("%.2lfB", num / 1e9);
+ }
+ else if (fabs(num)>=1e6)
+ {
+ return llformat("%.2lfM", num / 1e6);
+ }
+ else if (fabs(num)>=1e3)
+ {
+ return llformat("%.2lfK", num / 1e3);
+ }
+ else
+ {
+ return llformat("%.2lf", num);
+ }
+}
+
+namespace LLStringFn
+{
+ // NOTE - this restricts output to ascii
+ void replace_nonprintable_in_ascii(std::basic_string<char>& string, char replacement)
+ {
+ const char MIN = 0x20;
+ std::basic_string<char>::size_type len = string.size();
+ for(std::basic_string<char>::size_type ii = 0; ii < len; ++ii)
+ {
+ if(string[ii] < MIN)
+ {
+ string[ii] = replacement;
+ }
+ }
+ }
+
+
+ // NOTE - this restricts output to ascii
+ void replace_nonprintable_and_pipe_in_ascii(std::basic_string<char>& str,
+ char replacement)
+ {
+ const char MIN = 0x20;
+ const char PIPE = 0x7c;
+ std::basic_string<char>::size_type len = str.size();
+ for(std::basic_string<char>::size_type ii = 0; ii < len; ++ii)
+ {
+ if( (str[ii] < MIN) || (str[ii] == PIPE) )
+ {
+ str[ii] = replacement;
+ }
+ }
+ }
+
+ // https://wiki.lindenlab.com/wiki/Unicode_Guidelines has details on
+ // allowable code points for XML. Specifically, they are:
+ // 0x09, 0x0a, 0x0d, and 0x20 on up. JC
+ std::string strip_invalid_xml(const std::string& instr)
+ {
+ std::string output;
+ output.reserve( instr.size() );
+ std::string::const_iterator it = instr.begin();
+ while (it != instr.end())
+ {
+ // Must compare as unsigned for >=
+ // Test most likely match first
+ const unsigned char c = (unsigned char)*it;
+ if ( c >= (unsigned char)0x20 // SPACE
+ || c == (unsigned char)0x09 // TAB
+ || c == (unsigned char)0x0a // LINE_FEED
+ || c == (unsigned char)0x0d ) // CARRIAGE_RETURN
+ {
+ output.push_back(c);
+ }
+ ++it;
+ }
+ return output;
+ }
+
+ /**
+ * @brief Replace all control characters (c < 0x20) with replacement in
+ * string.
+ */
+ void replace_ascii_controlchars(std::basic_string<char>& string, char replacement)
+ {
+ const unsigned char MIN = 0x20;
+ std::basic_string<char>::size_type len = string.size();
+ for(std::basic_string<char>::size_type ii = 0; ii < len; ++ii)
+ {
+ const unsigned char c = (unsigned char) string[ii];
+ if(c < MIN)
+ {
+ string[ii] = replacement;
+ }
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////
+
+// Forward specialization of LLStringUtil::format before use in LLStringUtil::formatDatetime.
+template<>
+S32 LLStringUtil::format(std::string& s, const format_map_t& substitutions);
+
+//static
+template<>
+void LLStringUtil::getTokens(const std::string& instr, std::vector<std::string >& tokens, const std::string& delims)
+{
+ // Starting at offset 0, scan forward for the next non-delimiter. We're
+ // done when the only characters left in 'instr' are delimiters.
+ for (std::string::size_type begIdx, endIdx = 0;
+ (begIdx = instr.find_first_not_of (delims, endIdx)) != std::string::npos; )
+ {
+ // Found a non-delimiter. After that, find the next delimiter.
+ endIdx = instr.find_first_of (delims, begIdx);
+ if (endIdx == std::string::npos)
+ {
+ // No more delimiters: this token extends to the end of the string.
+ endIdx = instr.length();
+ }
+
+ // extract the token between begIdx and endIdx; substr() needs length
+ std::string currToken(instr.substr(begIdx, endIdx - begIdx));
+ LLStringUtil::trim (currToken);
+ tokens.push_back(currToken);
+ // next scan past delimiters starts at endIdx
+ }
+}
+
+template<>
+LLStringUtil::size_type LLStringUtil::getSubstitution(const std::string& instr, size_type& start, std::vector<std::string>& tokens)
+{
+ const std::string delims (",");
+
+ // Find the first [
+ size_type pos1 = instr.find('[', start);
+ if (pos1 == std::string::npos)
+ return std::string::npos;
+
+ //Find the first ] after the initial [
+ size_type pos2 = instr.find(']', pos1);
+ if (pos2 == std::string::npos)
+ return std::string::npos;
+
+ // Find the last [ before ] in case of nested [[]]
+ pos1 = instr.find_last_of('[', pos2-1);
+ if (pos1 == std::string::npos || pos1 < start)
+ return std::string::npos;
+
+ getTokens(std::string(instr,pos1+1,pos2-pos1-1), tokens, delims);
+ start = pos2+1;
+
+ return pos1;
+}
+
+// static
+template<>
+bool LLStringUtil::simpleReplacement(std::string &replacement, std::string token, const format_map_t& substitutions)
+{
+ // see if we have a replacement for the bracketed string (without the brackets)
+ // test first using has() because if we just look up with operator[] we get back an
+ // empty string even if the value is missing. We want to distinguish between
+ // missing replacements and deliberately empty replacement strings.
+ format_map_t::const_iterator iter = substitutions.find(token);
+ if (iter != substitutions.end())
+ {
+ replacement = iter->second;
+ return true;
+ }
+ // if not, see if there's one WITH brackets
+ iter = substitutions.find(std::string("[" + token + "]"));
+ if (iter != substitutions.end())
+ {
+ replacement = iter->second;
+ return true;
+ }
+
+ return false;
+}
+
+// static
+template<>
+bool LLStringUtil::simpleReplacement(std::string &replacement, std::string token, const LLSD& substitutions)
+{
+ // see if we have a replacement for the bracketed string (without the brackets)
+ // test first using has() because if we just look up with operator[] we get back an
+ // empty string even if the value is missing. We want to distinguish between
+ // missing replacements and deliberately empty replacement strings.
+ if (substitutions.has(token))
+ {
+ replacement = substitutions[token].asString();
+ return true;
+ }
+ // if not, see if there's one WITH brackets
+ else if (substitutions.has(std::string("[" + token + "]")))
+ {
+ replacement = substitutions[std::string("[" + token + "]")].asString();
+ return true;
+ }
+
+ return false;
+}
+
+//static
+template<>
+void LLStringUtil::setLocale(std::string inLocale)
+{
+ sLocale = inLocale;
+};
+
+//static
+template<>
+std::string LLStringUtil::getLocale(void)
+{
+ return sLocale;
+};
+
+// static
+template<>
+void LLStringUtil::formatNumber(std::string& numStr, std::string decimals)
+{
+ std::stringstream strStream;
+ S32 intDecimals = 0;
+
+ convertToS32 (decimals, intDecimals);
+ if (!sLocale.empty())
+ {
+ // std::locale() throws if the locale is unknown! (EXT-7926)
+ try
+ {
+ strStream.imbue(std::locale(sLocale.c_str()));
+ } catch (const std::exception &)
+ {
+ LL_WARNS_ONCE("Locale") << "Cannot set locale to " << sLocale << LL_ENDL;
+ }
+ }
+
+ if (!intDecimals)
+ {
+ S32 intStr;
+
+ if (convertToS32(numStr, intStr))
+ {
+ strStream << intStr;
+ numStr = strStream.str();
+ }
+ }
+ else
+ {
+ F32 floatStr;
+
+ if (convertToF32(numStr, floatStr))
+ {
+ strStream << std::fixed << std::showpoint << std::setprecision(intDecimals) << floatStr;
+ numStr = strStream.str();
+ }
+ }
+}
+
+// static
+template<>
+bool LLStringUtil::formatDatetime(std::string& replacement, std::string token,
+ std::string param, S32 secFromEpoch)
+{
+ if (param == "local") // local
+ {
+ secFromEpoch -= LLStringOps::getLocalTimeOffset();
+ }
+ else if (param != "utc") // slt
+ {
+ secFromEpoch -= LLStringOps::getPacificTimeOffset();
+ }
+
+ // if never fell into those two ifs above, param must be utc
+ if (secFromEpoch < 0) secFromEpoch = 0;
+
+ 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 == "local")
+ {
+ replacement = ""; // user knows their own timezone
+ }
+ else
+ {
+#if 0
+ // EXT-1565 : Zai Lynch, James Linden : 15/Oct/09
+ // [BSI] Feedback: Viewer clock mentions SLT, but would prefer it to show PST/PDT
+ // "slt" = Second Life Time, which is deprecated.
+ // If not utc or user local time, fallback to Pacific time
+ replacement = LLStringOps::getPacificDaylightTime() ? "PDT" : "PST";
+#else
+ // SL-20370 : Steeltoe Linden : 29/Sep/23
+ // Change "PDT" to "SLT" on menu bar
+ replacement = "SLT";
+#endif
+ }
+ return true;
+ }
+
+ //EXT-7013
+ //few codes are not suppotred by strtime function (example - weekdays for Japanise)
+ //so use predefined ones
+
+ //if sWeekDayList is not empty than current locale doesn't support
+ //weekday name.
+ time_t loc_seconds = (time_t) secFromEpoch;
+ if(LLStringOps::sWeekDayList.size() == 7 && code == "%A")
+ {
+ struct tm * gmt = gmtime (&loc_seconds);
+ replacement = LLStringOps::sWeekDayList[gmt->tm_wday];
+ }
+ else if(LLStringOps::sWeekDayShortList.size() == 7 && code == "%a")
+ {
+ struct tm * gmt = gmtime (&loc_seconds);
+ replacement = LLStringOps::sWeekDayShortList[gmt->tm_wday];
+ }
+ else if(LLStringOps::sMonthList.size() == 12 && code == "%B")
+ {
+ struct tm * gmt = gmtime (&loc_seconds);
+ replacement = LLStringOps::sMonthList[gmt->tm_mon];
+ }
+ else if( !LLStringOps::sDayFormat.empty() && code == "%d" )
+ {
+ struct tm * gmt = gmtime (&loc_seconds);
+ LLStringUtil::format_map_t args;
+ args["[MDAY]"] = llformat ("%d", gmt->tm_mday);
+ replacement = LLStringOps::sDayFormat;
+ LLStringUtil::format(replacement, args);
+ }
+ else if (code == "%-d")
+ {
+ struct tm * gmt = gmtime (&loc_seconds);
+ replacement = llformat ("%d", gmt->tm_mday); // day of the month without leading zero
+ }
+ else if( !LLStringOps::sAM.empty() && !LLStringOps::sPM.empty() && code == "%p" )
+ {
+ struct tm * gmt = gmtime (&loc_seconds);
+ if(gmt->tm_hour<12)
+ {
+ replacement = LLStringOps::sAM;
+ }
+ else
+ {
+ replacement = LLStringOps::sPM;
+ }
+ }
+ else
+ {
+ replacement = datetime.toHTTPDateString(code);
+ }
+
+ // *HACK: delete leading zero from hour string in case 'hour12' (code = %I) time format
+ // to show time without leading zero, e.g. 08:16 -> 8:16 (EXT-2738).
+ // We could have used '%l' format instead, but it's not supported by Windows.
+ if(code == "%I" && token == "hour12" && replacement.at(0) == '0')
+ {
+ replacement = replacement.at(1);
+ }
+
+ return !code.empty();
+}
+
+// LLStringUtil::format recogizes the following patterns.
+// All substitutions *must* be encased in []'s in the input string.
+// The []'s are optional in the substitution map.
+// [FOO_123]
+// [FOO,number,precision]
+// [FOO,datetime,format]
+
+
+// static
+template<>
+S32 LLStringUtil::format(std::string& s, const format_map_t& substitutions)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_STRING;
+ S32 res = 0;
+
+ std::string output;
+ std::vector<std::string> tokens;
+
+ std::string::size_type start = 0;
+ std::string::size_type prev_start = 0;
+ std::string::size_type key_start = 0;
+ while ((key_start = getSubstitution(s, start, tokens)) != std::string::npos)
+ {
+ output += std::string(s, prev_start, key_start-prev_start);
+ prev_start = start;
+
+ bool found_replacement = false;
+ std::string replacement;
+
+ if (tokens.size() == 0)
+ {
+ found_replacement = false;
+ }
+ else if (tokens.size() == 1)
+ {
+ found_replacement = simpleReplacement (replacement, tokens[0], substitutions);
+ }
+ else if (tokens[1] == "number")
+ {
+ std::string param = "0";
+
+ if (tokens.size() > 2) param = tokens[2];
+ found_replacement = simpleReplacement (replacement, tokens[0], substitutions);
+ if (found_replacement) formatNumber (replacement, param);
+ }
+ else if (tokens[1] == "datetime")
+ {
+ std::string param;
+ if (tokens.size() > 2) param = tokens[2];
+
+ format_map_t::const_iterator iter = substitutions.find("datetime");
+ if (iter != substitutions.end())
+ {
+ S32 secFromEpoch = 0;
+ bool r = LLStringUtil::convertToS32(iter->second, secFromEpoch);
+ if (r)
+ {
+ found_replacement = formatDatetime(replacement, tokens[0], param, secFromEpoch);
+ }
+ }
+ }
+
+ if (found_replacement)
+ {
+ output += replacement;
+ res++;
+ }
+ else
+ {
+ // we had no replacement, use the string as is
+ // e.g. "hello [MISSING_REPLACEMENT]" or "-=[Stylized Name]=-"
+ output += std::string(s, key_start, start-key_start);
+ }
+ tokens.clear();
+ }
+ // send the remainder of the string (with no further matches for bracketed names)
+ output += std::string(s, start);
+ s = output;
+ return res;
+}
+
+//static
+template<>
+S32 LLStringUtil::format(std::string& s, const LLSD& substitutions)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_STRING;
+ S32 res = 0;
+
+ if (!substitutions.isMap())
+ {
+ return res;
+ }
+
+ std::string output;
+ std::vector<std::string> tokens;
+
+ std::string::size_type start = 0;
+ std::string::size_type prev_start = 0;
+ std::string::size_type key_start = 0;
+ while ((key_start = getSubstitution(s, start, tokens)) != std::string::npos)
+ {
+ output += std::string(s, prev_start, key_start-prev_start);
+ prev_start = start;
+
+ bool found_replacement = false;
+ std::string replacement;
+
+ if (tokens.size() == 0)
+ {
+ found_replacement = false;
+ }
+ else if (tokens.size() == 1)
+ {
+ found_replacement = simpleReplacement (replacement, tokens[0], substitutions);
+ }
+ else if (tokens[1] == "number")
+ {
+ std::string param = "0";
+
+ if (tokens.size() > 2) param = tokens[2];
+ found_replacement = simpleReplacement (replacement, tokens[0], substitutions);
+ if (found_replacement) formatNumber (replacement, param);
+ }
+ else if (tokens[1] == "datetime")
+ {
+ std::string param;
+ if (tokens.size() > 2) param = tokens[2];
+
+ S32 secFromEpoch = (S32) substitutions["datetime"].asInteger();
+ found_replacement = formatDatetime (replacement, tokens[0], param, secFromEpoch);
+ }
+
+ if (found_replacement)
+ {
+ output += replacement;
+ res++;
+ }
+ else
+ {
+ // we had no replacement, use the string as is
+ // e.g. "hello [MISSING_REPLACEMENT]" or "-=[Stylized Name]=-"
+ output += std::string(s, key_start, start-key_start);
+ }
+ tokens.clear();
+ }
+ // send the remainder of the string (with no further matches for bracketed names)
+ output += std::string(s, start);
+ s = output;
+ return res;
+}
+
+////////////////////////////////////////////////////////////
+// Testing
+
+#ifdef _DEBUG
+
+template<class T>
+void LLStringUtilBase<T>::testHarness()
+{
+ std::string s1;
+
+ llassert( s1.c_str() == NULL );
+ llassert( s1.size() == 0 );
+ llassert( s1.empty() );
+
+ std::string s2( "hello");
+ llassert( !strcmp( s2.c_str(), "hello" ) );
+ llassert( s2.size() == 5 );
+ llassert( !s2.empty() );
+ std::string s3( s2 );
+
+ llassert( "hello" == s2 );
+ llassert( s2 == "hello" );
+ llassert( s2 > "gello" );
+ llassert( "gello" < s2 );
+ llassert( "gello" != s2 );
+ llassert( s2 != "gello" );
+
+ std::string s4 = s2;
+ llassert( !s4.empty() );
+ s4.empty();
+ llassert( s4.empty() );
+
+ std::string s5("");
+ llassert( s5.empty() );
+
+ llassert( isValidIndex(s5, 0) );
+ llassert( !isValidIndex(s5, 1) );
+
+ s3 = s2;
+ s4 = "hello again";
+
+ s4 += "!";
+ s4 += s4;
+ llassert( s4 == "hello again!hello again!" );
+
+
+ std::string s6 = s2 + " " + s2;
+ std::string s7 = s6;
+ llassert( s6 == s7 );
+ llassert( !( s6 != s7) );
+ llassert( !(s6 < s7) );
+ llassert( !(s6 > s7) );
+
+ llassert( !(s6 == "hi"));
+ llassert( s6 == "hello hello");
+ llassert( s6 < "hi");
+
+ llassert( s6[1] == 'e' );
+ s6[1] = 'f';
+ llassert( s6[1] == 'f' );
+
+ s2.erase( 4, 1 );
+ llassert( s2 == "hell");
+ s2.insert( 0, "y" );
+ llassert( s2 == "yhell");
+ s2.erase( 1, 3 );
+ llassert( s2 == "yl");
+ s2.insert( 1, "awn, don't yel");
+ llassert( s2 == "yawn, don't yell");
+
+ std::string s8 = s2.substr( 6, 5 );
+ llassert( s8 == "don't" );
+
+ std::string s9 = " \t\ntest \t\t\n ";
+ trim(s9);
+ llassert( s9 == "test" );
+
+ s8 = "abc123&*(ABC";
+
+ s9 = s8;
+ toUpper(s9);
+ llassert( s9 == "ABC123&*(ABC" );
+
+ s9 = s8;
+ toLower(s9);
+ llassert( s9 == "abc123&*(abc" );
+
+
+ std::string s10( 10, 'x' );
+ llassert( s10 == "xxxxxxxxxx" );
+
+ std::string s11( "monkey in the middle", 7, 2 );
+ llassert( s11 == "in" );
+
+ std::string s12; //empty
+ s12 += "foo";
+ llassert( s12 == "foo" );
+
+ std::string s13; //empty
+ s13 += 'f';
+ llassert( s13 == "f" );
+}
+
+
+#endif // _DEBUG
diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h index a20a40e205..ac05dc3cd0 100644 --- a/indra/llcommon/llstring.h +++ b/indra/llcommon/llstring.h @@ -1,2032 +1,2039 @@ -/** - * @file llstring.h - * @brief String utility functions and std::string class. - * - * $LicenseInfo:firstyear=2001&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLSTRING_H -#define LL_LLSTRING_H - -#include <boost/call_traits.hpp> -#include <optional> -#include <string> -#include <string_view> -#include <cstdio> -#include <cwchar> // std::wcslen() -//#include <locale> -#include <iomanip> -#include <algorithm> -#include <vector> -#include <map> -#include "llformat.h" - -#if LL_LINUX -#include <wctype.h> -#include <wchar.h> -#endif - -#include <string.h> -#include <boost/scoped_ptr.hpp> - -const char LL_UNKNOWN_CHAR = '?'; -class LLSD; - -#if LL_DARWIN || LL_LINUX -// 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 std::vector<std::string> sWeekDayList; - static std::vector<std::string> sWeekDayShortList; - static std::vector<std::string> sMonthList; - static std::vector<std::string> sMonthShortList; - static std::string sDayFormat; - - static std::string sAM; - static std::string sPM; - - 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 isAlpha(char a) { return isalpha((unsigned char)a) != 0; } - static bool isAlpha(llwchar a) { return iswalpha(a) != 0; } - - static bool isAlnum(char a) { return isalnum((unsigned char)a) != 0; } - static bool isAlnum(llwchar a) { return iswalnum(a) != 0; } - - static bool isEmoji(llwchar wch); - - 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 void setupWeekDaysNames(const std::string& data); - static void setupWeekDaysShortNames(const std::string& data); - static void setupMonthNames(const std::string& data); - static void setupMonthShortNames(const std::string& data); - static void setupDayFormat(const std::string& data); - - - 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); - - // Express a value like 1234567 as "1.23M" - static std::string getReadableNumber(F64 num); -}; - -/** - * @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 std::basic_string<T> string_type; - typedef typename string_type::size_type size_type; - -public: - ///////////////////////////////////////////////////////////////////////////////////////// - // Static Utility functions that operate on std::strings - - static const string_type null; - - typedef std::map<LLFormatMapString, LLFormatMapString> format_map_t; - /// considers any sequence of delims as a single field separator - LL_COMMON_API static void getTokens(const string_type& instr, - std::vector<string_type >& tokens, - const string_type& delims); - /// like simple scan overload, but returns scanned vector - static std::vector<string_type> getTokens(const string_type& instr, - const string_type& delims); - /// add support for keep_delims and quotes (either could be empty string) - static void getTokens(const string_type& instr, - std::vector<string_type>& tokens, - const string_type& drop_delims, - const string_type& keep_delims, - const string_type& quotes=string_type()); - /// like keep_delims-and-quotes overload, but returns scanned vector - static std::vector<string_type> getTokens(const string_type& instr, - const string_type& drop_delims, - const string_type& keep_delims, - const string_type& quotes=string_type()); - /// add support for escapes (could be empty string) - static void getTokens(const string_type& instr, - std::vector<string_type>& tokens, - const string_type& drop_delims, - const string_type& keep_delims, - const string_type& quotes, - const string_type& escapes); - /// like escapes overload, but returns scanned vector - static std::vector<string_type> getTokens(const string_type& instr, - const string_type& drop_delims, - const string_type& keep_delims, - const string_type& quotes, - const string_type& escapes); - - LL_COMMON_API static void formatNumber(string_type& numStr, string_type decimals); - LL_COMMON_API static bool formatDatetime(string_type& replacement, string_type token, string_type param, S32 secFromEpoch); - LL_COMMON_API static S32 format(string_type& s, const format_map_t& substitutions); - LL_COMMON_API static S32 format(string_type& s, const LLSD& substitutions); - LL_COMMON_API static bool simpleReplacement(string_type& replacement, string_type token, const format_map_t& substitutions); - LL_COMMON_API static bool simpleReplacement(string_type& replacement, string_type token, const LLSD& substitutions); - LL_COMMON_API static void setLocale (std::string inLocale); - LL_COMMON_API static std::string getLocale (void); - - static bool isValidIndex(const string_type& string, size_type i) - { - return !string.empty() && (0 <= i) && (i <= string.size()); - } - - static bool contains(const string_type& string, T c, size_type i=0) - { - return string.find(c, i) != string_type::npos; - } - - static void trimHead(string_type& string); - static void trimTail(string_type& string); - static void trim(string_type& string) { trimHead(string); trimTail(string); } - static void truncate(string_type& string, size_type count); - - static void toUpper(string_type& string); - static void toLower(string_type& string); - - // True if this is the head of s. - static bool isHead( const string_type& 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 string_type& string, - const string_type& substr); - - /** - * @brief Returns true if string ends in substr - * - * If etither string or substr are empty, this method returns false. - */ - static bool endsWith( - const string_type& string, - const string_type& substr); - - /** - * get environment string value with proper Unicode handling - * (key is always UTF-8) - * detect absence by return value == dflt - */ - static string_type getenv(const std::string& key, const string_type& dflt=""); - /** - * get optional environment string value with proper Unicode handling - * (key is always UTF-8) - * detect absence by (! return value) - */ - static std::optional<string_type> getoptenv(const std::string& key); - - static void addCRLF(string_type& string); - static void removeCRLF(string_type& string); - static void removeWindowsCR(string_type& string); - - static void replaceTabsWithSpaces( string_type& string, size_type spaces_per_tab ); - static void replaceNonstandardASCII( string_type& string, T replacement ); - static void replaceChar( string_type& string, T target, T replacement ); - static void replaceString( string_type& string, string_type target, string_type replacement ); - static string_type capitalize(const string_type& str); - static void capitalize(string_type& str); - - static bool containsNonprintable(const string_type& string); - static void stripNonprintable(string_type& string); - - /** - * Double-quote an argument string if needed, unless it's already - * double-quoted. Decide whether it's needed based on the presence of any - * character in @a triggers (default space or double-quote). If we quote - * it, escape any embedded double-quote with the @a escape string (default - * backslash). - * - * Passing triggers="" means always quote, unless it's already double-quoted. - */ - static string_type quote(const string_type& str, - const string_type& triggers=" \"", - const string_type& escape="\\"); - - /** - * @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(string_type& string); - - // Conversion to other data types - static bool convertToBOOL(const string_type& string, bool& value); - static bool convertToU8(const string_type& string, U8& value); - static bool convertToS8(const string_type& string, S8& value); - static bool convertToS16(const string_type& string, S16& value); - static bool convertToU16(const string_type& string, U16& value); - static bool convertToU32(const string_type& string, U32& value); - static bool convertToS32(const string_type& string, S32& value); - static bool convertToF32(const string_type& string, F32& value); - static bool convertToF64(const string_type& 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 string_type& lhs, const string_type& 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 string_type& lhs, const string_type& rhs); - - // Case sensitive comparison with good handling of numbers. Does not use current locale. - // a.k.a. strdictcmp() - static S32 compareDict(const string_type& a, const string_type& b); - - // Case *in*sensitive comparison with good handling of numbers. Does not use current locale. - // a.k.a. strdictcmp() - static S32 compareDictInsensitive(const string_type& a, const string_type& b); - - // Puts compareDict() in a form appropriate for LL container classes to use for sorting. - static bool precedesDict( const string_type& a, const string_type& 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(string_type& dst, const string_type& 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 string_type& instr, size_type& start, std::vector<string_type >& tokens); -}; - -template<class T> const 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) const - { - return (LLStringUtil::precedesDict(a, b)); - } -}; - - -/** - * 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 bool is_char_hex(char hex); -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 - */ - -/// generic conversion aliases -template<typename TO, typename FROM, typename Enable=void> -struct ll_convert_impl -{ - // Don't even provide a generic implementation. We specialize for every - // combination we do support. - TO operator()(const FROM& in) const; -}; - -// Use a function template to get the nice ll_convert<TO>(from_value) API. -template<typename TO, typename FROM> -TO ll_convert(const FROM& in) -{ - return ll_convert_impl<TO, FROM>()(in); -} - -// degenerate case -template<typename T> -struct ll_convert_impl<T, T> -{ - T operator()(const T& in) const { return in; } -}; - -// simple construction from char* -template<typename T> -struct ll_convert_impl<T, const typename T::value_type*> -{ - T operator()(const typename T::value_type* in) const { return { in }; } -}; - -// specialize ll_convert_impl<TO, FROM> to return EXPR -#define ll_convert_alias(TO, FROM, EXPR) \ -template<> \ -struct ll_convert_impl<TO, FROM> \ -{ \ - /* param_type optimally passes both char* and string */ \ - TO operator()(typename boost::call_traits<FROM>::param_type in) const { return EXPR; } \ -} - -// If all we're doing is copying characters, pass this to ll_convert_alias as -// EXPR. Since it expands into the 'return EXPR' slot in the ll_convert_impl -// specialization above, it implies TO{ in.begin(), in.end() }. -#define LL_CONVERT_COPY_CHARS { in.begin(), in.end() } - -// Generic name for strlen() / wcslen() - the default implementation should -// (!) work with U16 and llwchar, but we don't intend to engage it. -template <typename CHARTYPE> -size_t ll_convert_length(const CHARTYPE* zstr) -{ - const CHARTYPE* zp; - // classic C string scan - for (zp = zstr; *zp; ++zp) - ; - return (zp - zstr); -} - -// specialize where we have a library function; may use intrinsic operations -template <> -inline size_t ll_convert_length<wchar_t>(const wchar_t* zstr) { return std::wcslen(zstr); } -template <> -inline size_t ll_convert_length<char> (const char* zstr) { return std::strlen(zstr); } - -// ll_convert_forms() is short for a bunch of boilerplate. It defines -// longname(const char*, len), longname(const char*), longname(const string&) -// and longname(const string&, len) so calls written pre-ll_convert() will -// work. Most of these overloads will be unified once we turn on C++17 and can -// use std::string_view. -// It also uses aliasmacro to ensure that both ll_convert<OUTSTR>(const char*) -// and ll_convert<OUTSTR>(const string&) will work. -#define ll_convert_forms(aliasmacro, OUTSTR, INSTR, longname) \ -LL_COMMON_API OUTSTR longname(const INSTR::value_type* in, size_t len); \ -inline auto longname(const INSTR& in, size_t len) \ -{ \ - return longname(in.c_str(), len); \ -} \ -inline auto longname(const INSTR::value_type* in) \ -{ \ - return longname(in, ll_convert_length(in)); \ -} \ -inline auto longname(const INSTR& in) \ -{ \ - return longname(in.c_str(), in.length()); \ -} \ -/* string param */ \ -aliasmacro(OUTSTR, INSTR, longname(in)); \ -/* char* param */ \ -aliasmacro(OUTSTR, const INSTR::value_type*, longname(in)) - -// Make the incoming string a utf8 string. Replaces any unknown glyph -// with the UNKNOWN_CHARACTER. Once any unknown glyph 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! -// https://docs.microsoft.com/en-us/cpp/cpp/char-wchar-t-char16-t-char32-t -// nat 2018-12-14: I consider the whole llutf16string thing a mistake, because -// the Windows APIs we want to call are all defined in terms of wchar_t* -// (or worse, LPCTSTR). -// https://docs.microsoft.com/en-us/windows/desktop/winprog/windows-data-types - -// While there is no point coding for an ASCII-only world (! defined(UNICODE)), -// use of U16 and llutf16string for Windows APIs locks in /Zc:wchar_t-. Going -// forward, we should code in terms of wchar_t and std::wstring so as to -// support either setting of /Zc:wchar_t. - -// The first link above states that char can be used to hold ASCII or any -// multi-byte character set, and distinguishes wchar_t (UTF-16LE), char16_t -// (UTF-16) and char32_t (UTF-32). Nonetheless, within this code base: -// * char and std::string always hold UTF-8 (of which ASCII is a subset). It -// is a BUG if they are used to pass strings in any other multi-byte -// encoding. -// * wchar_t and std::wstring should be our interface to Windows wide-string -// APIs, and therefore hold UTF-16LE. -// * U16 and llutf16string are the previous but DEPRECATED UTF-16LE type. Do -// not introduce new uses of U16 or llutf16string for string data. -// * llwchar and LLWString hold UTF-32 strings. -// * Do not introduce char16_t or std::u16string. -// * Do not introduce char32_t or std::u32string. -// -// This typedef may or may not be identical to std::wstring, depending on -// LL_WCHAR_T_NATIVE. -typedef std::basic_string<U16> llutf16string; - -// Considering wchar_t, llwchar and U16, there are three relevant cases: -#if LLWCHAR_IS_WCHAR_T // every which way but Windows -// llwchar is identical to wchar_t, LLWString is identical to std::wstring. -// U16 is distinct, llutf16string is distinct (though pretty useless). -// Given conversions to/from LLWString and to/from llutf16string, conversions -// involving std::wstring would collide. -#define ll_convert_wstr_alias(TO, FROM, EXPR) // nothing -// but we can define conversions involving llutf16string without collisions -#define ll_convert_u16_alias(TO, FROM, EXPR) ll_convert_alias(TO, FROM, EXPR) - -#elif defined(LL_WCHAR_T_NATIVE) // Windows, either clang or MS /Zc:wchar_t -// llwchar (32-bit), wchar_t (16-bit) and U16 are all different types. -// Conversions to/from LLWString, to/from std::wstring and to/from llutf16string -// can all be defined. -#define ll_convert_wstr_alias(TO, FROM, EXPR) ll_convert_alias(TO, FROM, EXPR) -#define ll_convert_u16_alias(TO, FROM, EXPR) ll_convert_alias(TO, FROM, EXPR) - -#else // ! LL_WCHAR_T_NATIVE: Windows with MS /Zc:wchar_t- -// wchar_t is identical to U16, std::wstring is identical to llutf16string. -// Given conversions to/from LLWString and to/from std::wstring, conversions -// involving llutf16string would collide. -#define ll_convert_u16_alias(TO, FROM, EXPR) // nothing -// but we can define conversions involving std::wstring without collisions -#define ll_convert_wstr_alias(TO, FROM, EXPR) ll_convert_alias(TO, FROM, EXPR) -#endif - -ll_convert_forms(ll_convert_u16_alias, LLWString, llutf16string, utf16str_to_wstring); -ll_convert_forms(ll_convert_u16_alias, llutf16string, LLWString, wstring_to_utf16str); -ll_convert_forms(ll_convert_u16_alias, llutf16string, std::string, utf8str_to_utf16str); -ll_convert_forms(ll_convert_alias, LLWString, std::string, utf8str_to_wstring); - -// Same function, better name. JC -inline LLWString utf8string_to_wstring(const std::string& utf8_string) { return utf8str_to_wstring(utf8_string); } - -LL_COMMON_API std::ptrdiff_t wchar_to_utf8chars(llwchar inchar, char* outchars); - -ll_convert_forms(ll_convert_alias, std::string, LLWString, wstring_to_utf8str); -ll_convert_forms(ll_convert_u16_alias, std::string, llutf16string, utf16str_to_utf8str); - -// an older alias for utf16str_to_utf8str(llutf16string) -inline std::string wstring_to_utf8str(const llutf16string &utf16str) { return utf16str_to_utf8str(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 wchar_utf8_preview(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 = nullptr); - -/** - * @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 Properly truncate a utf8 string to a maximum character count. -* -* If symbol_len is longer than the string passed in, the return -* value == utf8str. -* @param utf8str A valid utf8 string to truncate. -* @param symbol_len The maximum number of symbols in the return value. -* @return Returns a valid utf8 string with symbol count <= max_len. -*/ -LL_COMMON_API std::string utf8str_symbol_truncate(const std::string& utf8str, const S32 symbol_len); - -/** - * @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); - -LL_COMMON_API llwchar utf8str_to_wchar(const std::string& utf8str, size_t offset, size_t length); - -LL_COMMON_API std::string utf8str_showBytesUTF8(const std::string& utf8str); - -#if LL_WINDOWS -/* @name Windows string helpers - */ -//@{ - -/** - * @brief Convert a wide string to/from std::string - * Convert a Windows wide string to/from our LLWString - * - * This replaces the unsafe W2A macro from ATL. - */ -// Avoid requiring this header to #include the Windows header file declaring -// our actual default code_page by delegating this function to our .cpp file. -LL_COMMON_API unsigned int ll_wstring_default_code_page(); - -// This is like ll_convert_forms(), with the added complexity of a code page -// parameter that may or may not be passed. -#define ll_convert_cp_forms(aliasmacro, OUTSTR, INSTR, longname) \ -/* declare the only nontrivial implementation (in .cpp file) */ \ -LL_COMMON_API OUTSTR longname( \ - const INSTR::value_type* in, \ - size_t len, \ - unsigned int code_page=ll_wstring_default_code_page()); \ -/* if passed only a char pointer, scan for nul terminator */ \ -inline auto longname(const INSTR::value_type* in) \ -{ \ - return longname(in, ll_convert_length(in)); \ -} \ -/* if passed string and length, extract its char pointer */ \ -inline auto longname( \ - const INSTR& in, \ - size_t len, \ - unsigned int code_page=ll_wstring_default_code_page()) \ -{ \ - return longname(in.c_str(), len, code_page); \ -} \ -/* if passed only a string object, no scan, pass known length */ \ -inline auto longname(const INSTR& in) \ -{ \ - return longname(in.c_str(), in.length()); \ -} \ -aliasmacro(OUTSTR, INSTR, longname(in)); \ -aliasmacro(OUTSTR, const INSTR::value_type*, longname(in)) - -ll_convert_cp_forms(ll_convert_wstr_alias, std::string, std::wstring, ll_convert_wide_to_string); -ll_convert_cp_forms(ll_convert_wstr_alias, std::wstring, std::string, ll_convert_string_to_wide); - ll_convert_forms(ll_convert_wstr_alias, LLWString, std::wstring, ll_convert_wide_to_wstring); - ll_convert_forms(ll_convert_wstr_alias, std::wstring, LLWString, ll_convert_wstring_to_wide); - -/** - * Converts incoming string into utf8 string - * - */ -LL_COMMON_API std::string ll_convert_string_to_utf8_string(const std::string& in); - -/// Get Windows message string for passed GetLastError() code -// VS 2013 doesn't let us forward-declare this template, which is what we -// started with, so the implementation could reference the specialization we -// haven't yet declared. Somewhat weirdly, just stating the generic -// implementation in terms of the specialization works, even in this order... - -// the general case is just a conversion from the sole implementation -// Microsoft says DWORD is a typedef for unsigned long -// https://docs.microsoft.com/en-us/windows/desktop/winprog/windows-data-types -// so rather than drag windows.h into everybody's include space... -template<typename STRING> -STRING windows_message(unsigned long error) -{ - return ll_convert<STRING>(windows_message<std::wstring>(error)); -} - -/// There's only one real implementation -template<> -LL_COMMON_API std::wstring windows_message<std::wstring>(unsigned long error); - -/// Get Windows message string, implicitly calling GetLastError() -template<typename STRING> -STRING windows_message() { return windows_message<STRING>(GetLastError()); } - -//@} - -LL_COMMON_API std::optional<std::wstring> llstring_getoptenv(const std::string& key); - -#else // ! LL_WINDOWS - -LL_COMMON_API std::optional<std::string> llstring_getoptenv(const std::string& key); - -#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 these for anything other than LLStringUtil will produce link errors. - -//////////////////////////////////////////////////////////// - -// static -template <class T> -std::vector<typename LLStringUtilBase<T>::string_type> -LLStringUtilBase<T>::getTokens(const string_type& instr, const string_type& delims) -{ - std::vector<string_type> tokens; - getTokens(instr, tokens, delims); - return tokens; -} - -// static -template <class T> -std::vector<typename LLStringUtilBase<T>::string_type> -LLStringUtilBase<T>::getTokens(const string_type& instr, - const string_type& drop_delims, - const string_type& keep_delims, - const string_type& quotes) -{ - std::vector<string_type> tokens; - getTokens(instr, tokens, drop_delims, keep_delims, quotes); - return tokens; -} - -// static -template <class T> -std::vector<typename LLStringUtilBase<T>::string_type> -LLStringUtilBase<T>::getTokens(const string_type& instr, - const string_type& drop_delims, - const string_type& keep_delims, - const string_type& quotes, - const string_type& escapes) -{ - std::vector<string_type> tokens; - getTokens(instr, tokens, drop_delims, keep_delims, quotes, escapes); - return tokens; -} - -namespace LLStringUtilBaseImpl -{ - -/** - * Input string scanner helper for getTokens(), or really any other - * character-parsing routine that may have to deal with escape characters. - * This implementation defines the concept (also an interface, should you - * choose to implement the concept by subclassing) and provides trivial - * implementations for a string @em without escape processing. - */ -template <class T> -struct InString -{ - typedef std::basic_string<T> string_type; - typedef typename string_type::const_iterator const_iterator; - - InString(const_iterator b, const_iterator e): - mIter(b), - mEnd(e) - {} - virtual ~InString() {} - - bool done() const { return mIter == mEnd; } - /// Is the current character (*mIter) escaped? This implementation can - /// answer trivially because it doesn't support escapes. - virtual bool escaped() const { return false; } - /// Obtain the current character and advance @c mIter. - virtual T next() { return *mIter++; } - /// Does the current character match specified character? - virtual bool is(T ch) const { return (! done()) && *mIter == ch; } - /// Is the current character any one of the specified characters? - virtual bool oneof(const string_type& delims) const - { - return (! done()) && LLStringUtilBase<T>::contains(delims, *mIter); - } - - /** - * Scan forward from @from until either @a delim or end. This is primarily - * useful for processing quoted substrings. - * - * If we do see @a delim, append everything from @from until (excluding) - * @a delim to @a into, advance @c mIter to skip @a delim, and return @c - * true. - * - * If we do not see @a delim, do not alter @a into or @c mIter and return - * @c false. Do not pass GO, do not collect $200. - * - * @note The @c false case described above implements normal getTokens() - * treatment of an unmatched open quote: treat the quote character as if - * escaped, that is, simply collect it as part of the current token. Other - * plausible behaviors directly affect the way getTokens() deals with an - * unmatched quote: e.g. throwing an exception to treat it as an error, or - * assuming a close quote beyond end of string (in which case return @c - * true). - */ - virtual bool collect_until(string_type& into, const_iterator from, T delim) - { - const_iterator found = std::find(from, mEnd, delim); - // If we didn't find delim, change nothing, just tell caller. - if (found == mEnd) - return false; - // Found delim! Append everything between from and found. - into.append(from, found); - // advance past delim in input - mIter = found + 1; - return true; - } - - const_iterator mIter, mEnd; -}; - -/// InString subclass that handles escape characters -template <class T> -class InEscString: public InString<T> -{ -public: - typedef InString<T> super; - typedef typename super::string_type string_type; - typedef typename super::const_iterator const_iterator; - using super::done; - using super::mIter; - using super::mEnd; - - InEscString(const_iterator b, const_iterator e, const string_type& escapes): - super(b, e), - mEscapes(escapes) - { - // Even though we've already initialized 'mIter' via our base-class - // constructor, set it again to check for initial escape char. - setiter(b); - } - - /// This implementation uses the answer cached by setiter(). - virtual bool escaped() const { return mIsEsc; } - virtual T next() - { - // If we're looking at the escape character of an escape sequence, - // skip that character. This is the one time we can modify 'mIter' - // without using setiter: for this one case we DO NOT CARE if the - // escaped character is itself an escape. - if (mIsEsc) - ++mIter; - // If we were looking at an escape character, this is the escaped - // character; otherwise it's just the next character. - T result(*mIter); - // Advance mIter, checking for escape sequence. - setiter(mIter + 1); - return result; - } - - virtual bool is(T ch) const - { - // Like base-class is(), except that an escaped character matches - // nothing. - return (! done()) && (! mIsEsc) && *mIter == ch; - } - - virtual bool oneof(const string_type& delims) const - { - // Like base-class oneof(), except that an escaped character matches - // nothing. - return (! done()) && (! mIsEsc) && LLStringUtilBase<T>::contains(delims, *mIter); - } - - virtual bool collect_until(string_type& into, const_iterator from, T delim) - { - // Deal with escapes in the characters we collect; that is, an escaped - // character must become just that character without the preceding - // escape. Collect characters in a separate string rather than - // directly appending to 'into' in case we do not find delim, in which - // case we're supposed to leave 'into' unmodified. - string_type collected; - // For scanning purposes, we're going to work directly with 'mIter'. - // Save its current value in case we fail to see delim. - const_iterator save_iter(mIter); - // Okay, set 'mIter', checking for escape. - setiter(from); - while (! done()) - { - // If we see an unescaped delim, stop and report success. - if ((! mIsEsc) && *mIter == delim) - { - // Append collected chars to 'into'. - into.append(collected); - // Don't forget to advance 'mIter' past delim. - setiter(mIter + 1); - return true; - } - // We're not at end, and either we're not looking at delim or it's - // escaped. Collect this character and keep going. - collected.push_back(next()); - } - // Here we hit 'mEnd' without ever seeing delim. Restore mIter and tell - // caller. - setiter(save_iter); - return false; - } - -private: - void setiter(const_iterator i) - { - mIter = i; - - // Every time we change 'mIter', set 'mIsEsc' to be able to repetitively - // answer escaped() without having to rescan 'mEscapes'. mIsEsc caches - // contains(mEscapes, *mIter). - - // We're looking at an escaped char if we're not already at end (that - // is, *mIter is even meaningful); if *mIter is in fact one of the - // specified escape characters; and if there's one more character - // following it. That is, if an escape character is the very last - // character of the input string, it loses its special meaning. - mIsEsc = (! done()) && - LLStringUtilBase<T>::contains(mEscapes, *mIter) && - (mIter+1) != mEnd; - } - - const string_type mEscapes; - bool mIsEsc; -}; - -/// getTokens() implementation based on InString concept -template <typename INSTRING, typename string_type> -void getTokens(INSTRING& instr, std::vector<string_type>& tokens, - const string_type& drop_delims, const string_type& keep_delims, - const string_type& quotes) -{ - // There are times when we want to match either drop_delims or - // keep_delims. Concatenate them up front to speed things up. - string_type all_delims(drop_delims + keep_delims); - // no tokens yet - tokens.clear(); - - // try for another token - while (! instr.done()) - { - // scan past any drop_delims - while (instr.oneof(drop_delims)) - { - // skip this drop_delim - instr.next(); - // but if that was the end of the string, done - if (instr.done()) - return; - } - // found the start of another token: make a slot for it - tokens.push_back(string_type()); - if (instr.oneof(keep_delims)) - { - // *iter is a keep_delim, a token of exactly 1 character. Append - // that character to the new token and proceed. - tokens.back().push_back(instr.next()); - continue; - } - // Here we have a non-delimiter token, which might consist of a mix of - // quoted and unquoted parts. Use bash rules for quoting: you can - // embed a quoted substring in the midst of an unquoted token (e.g. - // ~/"sub dir"/myfile.txt); you can ram two quoted substrings together - // to make a single token (e.g. 'He said, "'"Don't."'"'). We diverge - // from bash in that bash considers an unmatched quote an error. Our - // param signature doesn't allow for errors, so just pretend it's not - // a quote and embed it. - // At this level, keep scanning until we hit the next delimiter of - // either type (drop_delims or keep_delims). - while (! instr.oneof(all_delims)) - { - // If we're looking at an open quote, search forward for - // a close quote, collecting characters along the way. - if (instr.oneof(quotes) && - instr.collect_until(tokens.back(), instr.mIter+1, *instr.mIter)) - { - // collect_until is cleverly designed to do exactly what we - // need here. No further action needed if it returns true. - } - else - { - // Either *iter isn't a quote, or there's no matching close - // quote: in other words, just an ordinary char. Append it to - // current token. - tokens.back().push_back(instr.next()); - } - // having scanned that segment of this token, if we've reached the - // end of the string, we're done - if (instr.done()) - return; - } - } -} - -} // namespace LLStringUtilBaseImpl - -// static -template <class T> -void LLStringUtilBase<T>::getTokens(const string_type& string, std::vector<string_type>& tokens, - const string_type& drop_delims, const string_type& keep_delims, - const string_type& quotes) -{ - // Because this overload doesn't support escapes, use simple InString to - // manage input range. - LLStringUtilBaseImpl::InString<T> instring(string.begin(), string.end()); - LLStringUtilBaseImpl::getTokens(instring, tokens, drop_delims, keep_delims, quotes); -} - -// static -template <class T> -void LLStringUtilBase<T>::getTokens(const string_type& string, std::vector<string_type>& tokens, - const string_type& drop_delims, const string_type& keep_delims, - const string_type& quotes, const string_type& escapes) -{ - // This overload must deal with escapes. Delegate that to InEscString - // (unless there ARE no escapes). - std::unique_ptr< LLStringUtilBaseImpl::InString<T> > instrp; - if (escapes.empty()) - instrp.reset(new LLStringUtilBaseImpl::InString<T>(string.begin(), string.end())); - else - instrp.reset(new LLStringUtilBaseImpl::InEscString<T>(string.begin(), string.end(), escapes)); - LLStringUtilBaseImpl::getTokens(*instrp, tokens, drop_delims, keep_delims, quotes); -} - -// 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 string_type& lhs, const string_type& 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 - { - string_type lhs_string(lhs); - string_type 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 string_type& lhs, const string_type& rhs) -{ - string_type lhs_string(lhs); - string_type 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 string_type& astr, const string_type& 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 string_type& astr, const string_type& 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 string_type& a, const string_type& 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(string_type& string) -{ - if( !string.empty() ) - { - std::transform( - string.begin(), - string.end(), - string.begin(), - (T(*)(T)) &LLStringOps::toUpper); - } -} - -//static -template<class T> -void LLStringUtilBase<T>::toLower(string_type& string) -{ - if( !string.empty() ) - { - std::transform( - string.begin(), - string.end(), - string.begin(), - (T(*)(T)) &LLStringOps::toLower); - } -} - -//static -template<class T> -void LLStringUtilBase<T>::trimHead(string_type& 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(string_type& 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(string_type& 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); - delete[] t; - } -} - -// Remove all carriage returns -//static -template<class T> -void LLStringUtilBase<T>::removeCRLF(string_type& 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>::removeWindowsCR(string_type& string) -{ - if (string.empty()) - { - return; - } - const T LF = 10; - const T CR = 13; - - size_type cr_count = 0; - size_type len = string.size(); - size_type i; - for( i = 0; i < len - cr_count - 1; i++ ) - { - if( string[i+cr_count] == CR && string[i+cr_count+1] == LF) - { - cr_count++; - } - - string[i] = string[i+cr_count]; - } - string.erase(i, cr_count); -} - -//static -template<class T> -void LLStringUtilBase<T>::replaceChar( string_type& string, T target, T replacement ) -{ - size_type found_pos = 0; - while( (found_pos = string.find(target, found_pos)) != string_type::npos ) - { - string[found_pos] = replacement; - found_pos++; // avoid infinite defeat if target == replacement - } -} - -//static -template<class T> -void LLStringUtilBase<T>::replaceString( string_type& string, string_type target, string_type replacement ) -{ - size_type found_pos = 0; - while( (found_pos = string.find(target, found_pos)) != string_type::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( string_type& 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( string_type& str, size_type spaces_per_tab ) -{ - const T TAB = '\t'; - const T SPACE = ' '; - - string_type 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> -std::basic_string<T> LLStringUtilBase<T>::capitalize(const string_type& str) -{ - string_type result(str); - capitalize(result); - return result; -} - -//static -template<class T> -void LLStringUtilBase<T>::capitalize(string_type& str) -{ - if (str.size()) - { - auto last = str[0] = toupper(str[0]); - for (U32 i = 1; i < str.size(); ++i) - { - last = (last == ' ' || last == '-' || last == '_') ? str[i] = toupper(str[i]) : str[i]; - } - } -} - -//static -template<class T> -bool LLStringUtilBase<T>::containsNonprintable(const string_type& 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; -} - -// *TODO: reimplement in terms of algorithm -//static -template<class T> -void LLStringUtilBase<T>::stripNonprintable(string_type& 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; -} - -// *TODO: reimplement in terms of algorithm -template<class T> -std::basic_string<T> LLStringUtilBase<T>::quote(const string_type& str, - const string_type& triggers, - const string_type& escape) -{ - size_type len(str.length()); - // If the string is already quoted, assume user knows what s/he's doing. - if (len >= 2 && str[0] == '"' && str[len-1] == '"') - { - return str; - } - - // Not already quoted: do we need to? triggers.empty() is a special case - // meaning "always quote." - if ((! triggers.empty()) && str.find_first_of(triggers) == string_type::npos) - { - // no trigger characters, don't bother quoting - return str; - } - - // For whatever reason, we must quote this string. - string_type result; - result.push_back('"'); - for (typename string_type::const_iterator ci(str.begin()), cend(str.end()); ci != cend; ++ci) - { - if (*ci == '"') - { - result.append(escape); - } - result.push_back(*ci); - } - result.push_back('"'); - return result; -} - -template<class T> -void LLStringUtilBase<T>::_makeASCII(string_type& 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(string_type& dst, const string_type& 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 - { - string_type 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 string_type& 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 string_type& string, - const string_type& substr) -{ - if(string.empty() || (substr.empty())) return false; - if (substr.length() > string.length()) return false; - if (0 == string.compare(0, substr.length(), substr)) return true; - return false; -} - -// static -template<class T> -bool LLStringUtilBase<T>::endsWith( - const string_type& string, - const string_type& substr) -{ - if(string.empty() || (substr.empty())) return false; - size_t sub_len = substr.length(); - size_t str_len = string.length(); - if (sub_len > str_len) return false; - if (0 == string.compare(str_len - sub_len, sub_len, substr)) return true; - return false; -} - -// static -template<class T> -auto LLStringUtilBase<T>::getoptenv(const std::string& key) -> std::optional<string_type> -{ - auto found(llstring_getoptenv(key)); - if (found) - { - // return populated std::optional - return { ll_convert<string_type>(*found) }; - } - else - { - // empty std::optional - return {}; - } -} - -// static -template<class T> -auto LLStringUtilBase<T>::getenv(const std::string& key, const string_type& dflt) -> string_type -{ - auto found(getoptenv(key)); - if (found) - { - return *found; - } - else - { - return dflt; - } -} - -template<class T> -bool LLStringUtilBase<T>::convertToBOOL(const string_type& string, bool& value) -{ - if( string.empty() ) - { - return false; - } - - string_type 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 string_type& 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 string_type& 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 string_type& 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 string_type& 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 string_type& string, U32& value) -{ - if( string.empty() ) - { - return false; - } - - string_type temp( string ); - trim(temp); - U32 v; - std::basic_istringstream<T> i_stream((string_type)temp); - if(i_stream >> v) - { - value = v; - return true; - } - return false; -} - -template<class T> -bool LLStringUtilBase<T>::convertToS32(const string_type& string, S32& value) -{ - if( string.empty() ) - { - return false; - } - - string_type temp( string ); - trim(temp); - S32 v; - std::basic_istringstream<T> i_stream((string_type)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 string_type& 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 string_type& string, F64& value) -{ - if( string.empty() ) - { - return false; - } - - string_type temp( string ); - trim(temp); - F64 v; - std::basic_istringstream<T> i_stream((string_type)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(string_type& string, size_type count) -{ - size_type cur_size = string.size(); - string.resize(count < cur_size ? count : cur_size); -} - -// The good thing about *declaration* macros, vs. usage macros, is that now -// we're done with them: we don't need them to bleed into the consuming source -// file. -#undef ll_convert_alias -#undef ll_convert_u16_alias -#undef ll_convert_wstr_alias -#undef LL_CONVERT_COPY_CHARS -#undef ll_convert_forms -#undef ll_convert_cp_forms - -#endif // LL_STRING_H +/**
+ * @file llstring.h
+ * @brief String utility functions and std::string class.
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLSTRING_H
+#define LL_LLSTRING_H
+
+#include <boost/call_traits.hpp>
+#include <optional>
+#include <string>
+#include <string_view>
+#include <cstdio>
+#include <cwchar> // std::wcslen()
+//#include <locale>
+#include <iomanip>
+#include <algorithm>
+#include <vector>
+#include <map>
+#include "llformat.h"
+
+#if LL_LINUX
+#include <wctype.h>
+#include <wchar.h>
+#endif
+
+#include <string.h>
+#include <boost/scoped_ptr.hpp>
+
+const char LL_UNKNOWN_CHAR = '?';
+class LLSD;
+
+#if LL_DARWIN || LL_LINUX
+// 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 std::vector<std::string> sWeekDayList;
+ static std::vector<std::string> sWeekDayShortList;
+ static std::vector<std::string> sMonthList;
+ static std::vector<std::string> sMonthShortList;
+ static std::string sDayFormat;
+
+ static std::string sAM;
+ static std::string sPM;
+
+ 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 isAlpha(char a) { return isalpha((unsigned char)a) != 0; }
+ static bool isAlpha(llwchar a) { return iswalpha(a) != 0; }
+
+ static bool isAlnum(char a) { return isalnum((unsigned char)a) != 0; }
+ static bool isAlnum(llwchar a) { return iswalnum(a) != 0; }
+
+ // Returns true when 'a' corresponds to a "genuine" emoji. HB
+ static bool isEmoji(llwchar a);
+
+ 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 void setupWeekDaysNames(const std::string& data);
+ static void setupWeekDaysShortNames(const std::string& data);
+ static void setupMonthNames(const std::string& data);
+ static void setupMonthShortNames(const std::string& data);
+ static void setupDayFormat(const std::string& data);
+
+
+ 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);
+
+ // Express a value like 1234567 as "1.23M"
+ static std::string getReadableNumber(F64 num);
+};
+
+/**
+ * @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 std::basic_string<T> string_type;
+ typedef typename string_type::size_type size_type;
+
+public:
+ /////////////////////////////////////////////////////////////////////////////////////////
+ // Static Utility functions that operate on std::strings
+
+ static const string_type null;
+
+ typedef std::map<LLFormatMapString, LLFormatMapString> format_map_t;
+ /// considers any sequence of delims as a single field separator
+ LL_COMMON_API static void getTokens(const string_type& instr,
+ std::vector<string_type >& tokens,
+ const string_type& delims);
+ /// like simple scan overload, but returns scanned vector
+ static std::vector<string_type> getTokens(const string_type& instr,
+ const string_type& delims);
+ /// add support for keep_delims and quotes (either could be empty string)
+ static void getTokens(const string_type& instr,
+ std::vector<string_type>& tokens,
+ const string_type& drop_delims,
+ const string_type& keep_delims,
+ const string_type& quotes=string_type());
+ /// like keep_delims-and-quotes overload, but returns scanned vector
+ static std::vector<string_type> getTokens(const string_type& instr,
+ const string_type& drop_delims,
+ const string_type& keep_delims,
+ const string_type& quotes=string_type());
+ /// add support for escapes (could be empty string)
+ static void getTokens(const string_type& instr,
+ std::vector<string_type>& tokens,
+ const string_type& drop_delims,
+ const string_type& keep_delims,
+ const string_type& quotes,
+ const string_type& escapes);
+ /// like escapes overload, but returns scanned vector
+ static std::vector<string_type> getTokens(const string_type& instr,
+ const string_type& drop_delims,
+ const string_type& keep_delims,
+ const string_type& quotes,
+ const string_type& escapes);
+
+ LL_COMMON_API static void formatNumber(string_type& numStr, string_type decimals);
+ LL_COMMON_API static bool formatDatetime(string_type& replacement, string_type token, string_type param, S32 secFromEpoch);
+ LL_COMMON_API static S32 format(string_type& s, const format_map_t& substitutions);
+ LL_COMMON_API static S32 format(string_type& s, const LLSD& substitutions);
+ LL_COMMON_API static bool simpleReplacement(string_type& replacement, string_type token, const format_map_t& substitutions);
+ LL_COMMON_API static bool simpleReplacement(string_type& replacement, string_type token, const LLSD& substitutions);
+ LL_COMMON_API static void setLocale (std::string inLocale);
+ LL_COMMON_API static std::string getLocale (void);
+
+ static bool isValidIndex(const string_type& string, size_type i)
+ {
+ return !string.empty() && (0 <= i) && (i <= string.size());
+ }
+
+ static bool contains(const string_type& string, T c, size_type i=0)
+ {
+ return string.find(c, i) != string_type::npos;
+ }
+
+ static void trimHead(string_type& string);
+ static void trimTail(string_type& string);
+ static void trim(string_type& string) { trimHead(string); trimTail(string); }
+ static void truncate(string_type& string, size_type count);
+
+ static void toUpper(string_type& string);
+ static void toLower(string_type& string);
+
+ // True if this is the head of s.
+ static bool isHead( const string_type& 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 string_type& string,
+ const string_type& substr);
+
+ /**
+ * @brief Returns true if string ends in substr
+ *
+ * If etither string or substr are empty, this method returns false.
+ */
+ static bool endsWith(
+ const string_type& string,
+ const string_type& substr);
+
+ /**
+ * get environment string value with proper Unicode handling
+ * (key is always UTF-8)
+ * detect absence by return value == dflt
+ */
+ static string_type getenv(const std::string& key, const string_type& dflt="");
+ /**
+ * get optional environment string value with proper Unicode handling
+ * (key is always UTF-8)
+ * detect absence by (! return value)
+ */
+ static std::optional<string_type> getoptenv(const std::string& key);
+
+ static void addCRLF(string_type& string);
+ static void removeCRLF(string_type& string);
+ static void removeWindowsCR(string_type& string);
+
+ static void replaceTabsWithSpaces( string_type& string, size_type spaces_per_tab );
+ static void replaceNonstandardASCII( string_type& string, T replacement );
+ static void replaceChar( string_type& string, T target, T replacement );
+ static void replaceString( string_type& string, string_type target, string_type replacement );
+ static string_type capitalize(const string_type& str);
+ static void capitalize(string_type& str);
+
+ static bool containsNonprintable(const string_type& string);
+ static void stripNonprintable(string_type& string);
+
+ /**
+ * Double-quote an argument string if needed, unless it's already
+ * double-quoted. Decide whether it's needed based on the presence of any
+ * character in @a triggers (default space or double-quote). If we quote
+ * it, escape any embedded double-quote with the @a escape string (default
+ * backslash).
+ *
+ * Passing triggers="" means always quote, unless it's already double-quoted.
+ */
+ static string_type quote(const string_type& str,
+ const string_type& triggers=" \"",
+ const string_type& escape="\\");
+
+ /**
+ * @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(string_type& string);
+
+ // Conversion to other data types
+ static bool convertToBOOL(const string_type& string, bool& value);
+ static bool convertToU8(const string_type& string, U8& value);
+ static bool convertToS8(const string_type& string, S8& value);
+ static bool convertToS16(const string_type& string, S16& value);
+ static bool convertToU16(const string_type& string, U16& value);
+ static bool convertToU32(const string_type& string, U32& value);
+ static bool convertToS32(const string_type& string, S32& value);
+ static bool convertToF32(const string_type& string, F32& value);
+ static bool convertToF64(const string_type& 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 string_type& lhs, const string_type& 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 string_type& lhs, const string_type& rhs);
+
+ // Case sensitive comparison with good handling of numbers. Does not use current locale.
+ // a.k.a. strdictcmp()
+ static S32 compareDict(const string_type& a, const string_type& b);
+
+ // Case *in*sensitive comparison with good handling of numbers. Does not use current locale.
+ // a.k.a. strdictcmp()
+ static S32 compareDictInsensitive(const string_type& a, const string_type& b);
+
+ // Puts compareDict() in a form appropriate for LL container classes to use for sorting.
+ static bool precedesDict( const string_type& a, const string_type& 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(string_type& dst, const string_type& 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 string_type& instr, size_type& start, std::vector<string_type >& tokens);
+};
+
+template<class T> const 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) const
+ {
+ return (LLStringUtil::precedesDict(a, b));
+ }
+};
+
+
+/**
+ * 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 bool is_char_hex(char hex);
+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
+ */
+
+/// generic conversion aliases
+template<typename TO, typename FROM, typename Enable=void>
+struct ll_convert_impl
+{
+ // Don't even provide a generic implementation. We specialize for every
+ // combination we do support.
+ TO operator()(const FROM& in) const;
+};
+
+// Use a function template to get the nice ll_convert<TO>(from_value) API.
+template<typename TO, typename FROM>
+TO ll_convert(const FROM& in)
+{
+ return ll_convert_impl<TO, FROM>()(in);
+}
+
+// degenerate case
+template<typename T>
+struct ll_convert_impl<T, T>
+{
+ T operator()(const T& in) const { return in; }
+};
+
+// simple construction from char*
+template<typename T>
+struct ll_convert_impl<T, const typename T::value_type*>
+{
+ T operator()(const typename T::value_type* in) const { return { in }; }
+};
+
+// specialize ll_convert_impl<TO, FROM> to return EXPR
+#define ll_convert_alias(TO, FROM, EXPR) \
+template<> \
+struct ll_convert_impl<TO, FROM> \
+{ \
+ /* param_type optimally passes both char* and string */ \
+ TO operator()(typename boost::call_traits<FROM>::param_type in) const { return EXPR; } \
+}
+
+// If all we're doing is copying characters, pass this to ll_convert_alias as
+// EXPR. Since it expands into the 'return EXPR' slot in the ll_convert_impl
+// specialization above, it implies TO{ in.begin(), in.end() }.
+#define LL_CONVERT_COPY_CHARS { in.begin(), in.end() }
+
+// Generic name for strlen() / wcslen() - the default implementation should
+// (!) work with U16 and llwchar, but we don't intend to engage it.
+template <typename CHARTYPE>
+size_t ll_convert_length(const CHARTYPE* zstr)
+{
+ const CHARTYPE* zp;
+ // classic C string scan
+ for (zp = zstr; *zp; ++zp)
+ ;
+ return (zp - zstr);
+}
+
+// specialize where we have a library function; may use intrinsic operations
+template <>
+inline size_t ll_convert_length<wchar_t>(const wchar_t* zstr) { return std::wcslen(zstr); }
+template <>
+inline size_t ll_convert_length<char> (const char* zstr) { return std::strlen(zstr); }
+
+// ll_convert_forms() is short for a bunch of boilerplate. It defines
+// longname(const char*, len), longname(const char*), longname(const string&)
+// and longname(const string&, len) so calls written pre-ll_convert() will
+// work. Most of these overloads will be unified once we turn on C++17 and can
+// use std::string_view.
+// It also uses aliasmacro to ensure that both ll_convert<OUTSTR>(const char*)
+// and ll_convert<OUTSTR>(const string&) will work.
+#define ll_convert_forms(aliasmacro, OUTSTR, INSTR, longname) \
+LL_COMMON_API OUTSTR longname(const INSTR::value_type* in, size_t len); \
+inline auto longname(const INSTR& in, size_t len) \
+{ \
+ return longname(in.c_str(), len); \
+} \
+inline auto longname(const INSTR::value_type* in) \
+{ \
+ return longname(in, ll_convert_length(in)); \
+} \
+inline auto longname(const INSTR& in) \
+{ \
+ return longname(in.c_str(), in.length()); \
+} \
+/* string param */ \
+aliasmacro(OUTSTR, INSTR, longname(in)); \
+/* char* param */ \
+aliasmacro(OUTSTR, const INSTR::value_type*, longname(in))
+
+// Make the incoming string a utf8 string. Replaces any unknown glyph
+// with the UNKNOWN_CHARACTER. Once any unknown glyph 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!
+// https://docs.microsoft.com/en-us/cpp/cpp/char-wchar-t-char16-t-char32-t
+// nat 2018-12-14: I consider the whole llutf16string thing a mistake, because
+// the Windows APIs we want to call are all defined in terms of wchar_t*
+// (or worse, LPCTSTR).
+// https://docs.microsoft.com/en-us/windows/desktop/winprog/windows-data-types
+
+// While there is no point coding for an ASCII-only world (! defined(UNICODE)),
+// use of U16 and llutf16string for Windows APIs locks in /Zc:wchar_t-. Going
+// forward, we should code in terms of wchar_t and std::wstring so as to
+// support either setting of /Zc:wchar_t.
+
+// The first link above states that char can be used to hold ASCII or any
+// multi-byte character set, and distinguishes wchar_t (UTF-16LE), char16_t
+// (UTF-16) and char32_t (UTF-32). Nonetheless, within this code base:
+// * char and std::string always hold UTF-8 (of which ASCII is a subset). It
+// is a BUG if they are used to pass strings in any other multi-byte
+// encoding.
+// * wchar_t and std::wstring should be our interface to Windows wide-string
+// APIs, and therefore hold UTF-16LE.
+// * U16 and llutf16string are the previous but DEPRECATED UTF-16LE type. Do
+// not introduce new uses of U16 or llutf16string for string data.
+// * llwchar and LLWString hold UTF-32 strings.
+// * Do not introduce char16_t or std::u16string.
+// * Do not introduce char32_t or std::u32string.
+//
+// This typedef may or may not be identical to std::wstring, depending on
+// LL_WCHAR_T_NATIVE.
+typedef std::basic_string<U16> llutf16string;
+
+// Considering wchar_t, llwchar and U16, there are three relevant cases:
+#if LLWCHAR_IS_WCHAR_T // every which way but Windows
+// llwchar is identical to wchar_t, LLWString is identical to std::wstring.
+// U16 is distinct, llutf16string is distinct (though pretty useless).
+// Given conversions to/from LLWString and to/from llutf16string, conversions
+// involving std::wstring would collide.
+#define ll_convert_wstr_alias(TO, FROM, EXPR) // nothing
+// but we can define conversions involving llutf16string without collisions
+#define ll_convert_u16_alias(TO, FROM, EXPR) ll_convert_alias(TO, FROM, EXPR)
+
+#elif defined(LL_WCHAR_T_NATIVE) // Windows, either clang or MS /Zc:wchar_t
+// llwchar (32-bit), wchar_t (16-bit) and U16 are all different types.
+// Conversions to/from LLWString, to/from std::wstring and to/from llutf16string
+// can all be defined.
+#define ll_convert_wstr_alias(TO, FROM, EXPR) ll_convert_alias(TO, FROM, EXPR)
+#define ll_convert_u16_alias(TO, FROM, EXPR) ll_convert_alias(TO, FROM, EXPR)
+
+#else // ! LL_WCHAR_T_NATIVE: Windows with MS /Zc:wchar_t-
+// wchar_t is identical to U16, std::wstring is identical to llutf16string.
+// Given conversions to/from LLWString and to/from std::wstring, conversions
+// involving llutf16string would collide.
+#define ll_convert_u16_alias(TO, FROM, EXPR) // nothing
+// but we can define conversions involving std::wstring without collisions
+#define ll_convert_wstr_alias(TO, FROM, EXPR) ll_convert_alias(TO, FROM, EXPR)
+#endif
+
+ll_convert_forms(ll_convert_u16_alias, LLWString, llutf16string, utf16str_to_wstring);
+ll_convert_forms(ll_convert_u16_alias, llutf16string, LLWString, wstring_to_utf16str);
+ll_convert_forms(ll_convert_u16_alias, llutf16string, std::string, utf8str_to_utf16str);
+ll_convert_forms(ll_convert_alias, LLWString, std::string, utf8str_to_wstring);
+
+// Same function, better name. JC
+inline LLWString utf8string_to_wstring(const std::string& utf8_string) { return utf8str_to_wstring(utf8_string); }
+
+LL_COMMON_API std::ptrdiff_t wchar_to_utf8chars(llwchar inchar, char* outchars);
+
+ll_convert_forms(ll_convert_alias, std::string, LLWString, wstring_to_utf8str);
+ll_convert_forms(ll_convert_u16_alias, std::string, llutf16string, utf16str_to_utf8str);
+
+// an older alias for utf16str_to_utf8str(llutf16string)
+inline std::string wstring_to_utf8str(const llutf16string &utf16str) { return utf16str_to_utf8str(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 wchar_utf8_preview(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 = nullptr);
+
+/**
+ * @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 Properly truncate a utf8 string to a maximum character count.
+*
+* If symbol_len is longer than the string passed in, the return
+* value == utf8str.
+* @param utf8str A valid utf8 string to truncate.
+* @param symbol_len The maximum number of symbols in the return value.
+* @return Returns a valid utf8 string with symbol count <= max_len.
+*/
+LL_COMMON_API std::string utf8str_symbol_truncate(const std::string& utf8str, const S32 symbol_len);
+
+/**
+ * @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);
+
+LL_COMMON_API llwchar utf8str_to_wchar(const std::string& utf8str, size_t offset, size_t length);
+
+LL_COMMON_API std::string utf8str_showBytesUTF8(const std::string& utf8str);
+
+LL_COMMON_API bool wstring_has_emoji(const LLWString& wstr);
+
+LL_COMMON_API bool wstring_remove_emojis(LLWString& wstr);
+
+LL_COMMON_API bool utf8str_remove_emojis(std::string& utf8str);
+
+#if LL_WINDOWS
+/* @name Windows string helpers
+ */
+//@{
+
+/**
+ * @brief Convert a wide string to/from std::string
+ * Convert a Windows wide string to/from our LLWString
+ *
+ * This replaces the unsafe W2A macro from ATL.
+ */
+// Avoid requiring this header to #include the Windows header file declaring
+// our actual default code_page by delegating this function to our .cpp file.
+LL_COMMON_API unsigned int ll_wstring_default_code_page();
+
+// This is like ll_convert_forms(), with the added complexity of a code page
+// parameter that may or may not be passed.
+#define ll_convert_cp_forms(aliasmacro, OUTSTR, INSTR, longname) \
+/* declare the only nontrivial implementation (in .cpp file) */ \
+LL_COMMON_API OUTSTR longname( \
+ const INSTR::value_type* in, \
+ size_t len, \
+ unsigned int code_page=ll_wstring_default_code_page()); \
+/* if passed only a char pointer, scan for nul terminator */ \
+inline auto longname(const INSTR::value_type* in) \
+{ \
+ return longname(in, ll_convert_length(in)); \
+} \
+/* if passed string and length, extract its char pointer */ \
+inline auto longname( \
+ const INSTR& in, \
+ size_t len, \
+ unsigned int code_page=ll_wstring_default_code_page()) \
+{ \
+ return longname(in.c_str(), len, code_page); \
+} \
+/* if passed only a string object, no scan, pass known length */ \
+inline auto longname(const INSTR& in) \
+{ \
+ return longname(in.c_str(), in.length()); \
+} \
+aliasmacro(OUTSTR, INSTR, longname(in)); \
+aliasmacro(OUTSTR, const INSTR::value_type*, longname(in))
+
+ll_convert_cp_forms(ll_convert_wstr_alias, std::string, std::wstring, ll_convert_wide_to_string);
+ll_convert_cp_forms(ll_convert_wstr_alias, std::wstring, std::string, ll_convert_string_to_wide);
+ ll_convert_forms(ll_convert_wstr_alias, LLWString, std::wstring, ll_convert_wide_to_wstring);
+ ll_convert_forms(ll_convert_wstr_alias, std::wstring, LLWString, ll_convert_wstring_to_wide);
+
+/**
+ * Converts incoming string into utf8 string
+ *
+ */
+LL_COMMON_API std::string ll_convert_string_to_utf8_string(const std::string& in);
+
+/// Get Windows message string for passed GetLastError() code
+// VS 2013 doesn't let us forward-declare this template, which is what we
+// started with, so the implementation could reference the specialization we
+// haven't yet declared. Somewhat weirdly, just stating the generic
+// implementation in terms of the specialization works, even in this order...
+
+// the general case is just a conversion from the sole implementation
+// Microsoft says DWORD is a typedef for unsigned long
+// https://docs.microsoft.com/en-us/windows/desktop/winprog/windows-data-types
+// so rather than drag windows.h into everybody's include space...
+template<typename STRING>
+STRING windows_message(unsigned long error)
+{
+ return ll_convert<STRING>(windows_message<std::wstring>(error));
+}
+
+/// There's only one real implementation
+template<>
+LL_COMMON_API std::wstring windows_message<std::wstring>(unsigned long error);
+
+/// Get Windows message string, implicitly calling GetLastError()
+template<typename STRING>
+STRING windows_message() { return windows_message<STRING>(GetLastError()); }
+
+//@}
+
+LL_COMMON_API std::optional<std::wstring> llstring_getoptenv(const std::string& key);
+
+#else // ! LL_WINDOWS
+
+LL_COMMON_API std::optional<std::string> llstring_getoptenv(const std::string& key);
+
+#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 these for anything other than LLStringUtil will produce link errors.
+
+////////////////////////////////////////////////////////////
+
+// static
+template <class T>
+std::vector<typename LLStringUtilBase<T>::string_type>
+LLStringUtilBase<T>::getTokens(const string_type& instr, const string_type& delims)
+{
+ std::vector<string_type> tokens;
+ getTokens(instr, tokens, delims);
+ return tokens;
+}
+
+// static
+template <class T>
+std::vector<typename LLStringUtilBase<T>::string_type>
+LLStringUtilBase<T>::getTokens(const string_type& instr,
+ const string_type& drop_delims,
+ const string_type& keep_delims,
+ const string_type& quotes)
+{
+ std::vector<string_type> tokens;
+ getTokens(instr, tokens, drop_delims, keep_delims, quotes);
+ return tokens;
+}
+
+// static
+template <class T>
+std::vector<typename LLStringUtilBase<T>::string_type>
+LLStringUtilBase<T>::getTokens(const string_type& instr,
+ const string_type& drop_delims,
+ const string_type& keep_delims,
+ const string_type& quotes,
+ const string_type& escapes)
+{
+ std::vector<string_type> tokens;
+ getTokens(instr, tokens, drop_delims, keep_delims, quotes, escapes);
+ return tokens;
+}
+
+namespace LLStringUtilBaseImpl
+{
+
+/**
+ * Input string scanner helper for getTokens(), or really any other
+ * character-parsing routine that may have to deal with escape characters.
+ * This implementation defines the concept (also an interface, should you
+ * choose to implement the concept by subclassing) and provides trivial
+ * implementations for a string @em without escape processing.
+ */
+template <class T>
+struct InString
+{
+ typedef std::basic_string<T> string_type;
+ typedef typename string_type::const_iterator const_iterator;
+
+ InString(const_iterator b, const_iterator e):
+ mIter(b),
+ mEnd(e)
+ {}
+ virtual ~InString() {}
+
+ bool done() const { return mIter == mEnd; }
+ /// Is the current character (*mIter) escaped? This implementation can
+ /// answer trivially because it doesn't support escapes.
+ virtual bool escaped() const { return false; }
+ /// Obtain the current character and advance @c mIter.
+ virtual T next() { return *mIter++; }
+ /// Does the current character match specified character?
+ virtual bool is(T ch) const { return (! done()) && *mIter == ch; }
+ /// Is the current character any one of the specified characters?
+ virtual bool oneof(const string_type& delims) const
+ {
+ return (! done()) && LLStringUtilBase<T>::contains(delims, *mIter);
+ }
+
+ /**
+ * Scan forward from @from until either @a delim or end. This is primarily
+ * useful for processing quoted substrings.
+ *
+ * If we do see @a delim, append everything from @from until (excluding)
+ * @a delim to @a into, advance @c mIter to skip @a delim, and return @c
+ * true.
+ *
+ * If we do not see @a delim, do not alter @a into or @c mIter and return
+ * @c false. Do not pass GO, do not collect $200.
+ *
+ * @note The @c false case described above implements normal getTokens()
+ * treatment of an unmatched open quote: treat the quote character as if
+ * escaped, that is, simply collect it as part of the current token. Other
+ * plausible behaviors directly affect the way getTokens() deals with an
+ * unmatched quote: e.g. throwing an exception to treat it as an error, or
+ * assuming a close quote beyond end of string (in which case return @c
+ * true).
+ */
+ virtual bool collect_until(string_type& into, const_iterator from, T delim)
+ {
+ const_iterator found = std::find(from, mEnd, delim);
+ // If we didn't find delim, change nothing, just tell caller.
+ if (found == mEnd)
+ return false;
+ // Found delim! Append everything between from and found.
+ into.append(from, found);
+ // advance past delim in input
+ mIter = found + 1;
+ return true;
+ }
+
+ const_iterator mIter, mEnd;
+};
+
+/// InString subclass that handles escape characters
+template <class T>
+class InEscString: public InString<T>
+{
+public:
+ typedef InString<T> super;
+ typedef typename super::string_type string_type;
+ typedef typename super::const_iterator const_iterator;
+ using super::done;
+ using super::mIter;
+ using super::mEnd;
+
+ InEscString(const_iterator b, const_iterator e, const string_type& escapes):
+ super(b, e),
+ mEscapes(escapes)
+ {
+ // Even though we've already initialized 'mIter' via our base-class
+ // constructor, set it again to check for initial escape char.
+ setiter(b);
+ }
+
+ /// This implementation uses the answer cached by setiter().
+ virtual bool escaped() const { return mIsEsc; }
+ virtual T next()
+ {
+ // If we're looking at the escape character of an escape sequence,
+ // skip that character. This is the one time we can modify 'mIter'
+ // without using setiter: for this one case we DO NOT CARE if the
+ // escaped character is itself an escape.
+ if (mIsEsc)
+ ++mIter;
+ // If we were looking at an escape character, this is the escaped
+ // character; otherwise it's just the next character.
+ T result(*mIter);
+ // Advance mIter, checking for escape sequence.
+ setiter(mIter + 1);
+ return result;
+ }
+
+ virtual bool is(T ch) const
+ {
+ // Like base-class is(), except that an escaped character matches
+ // nothing.
+ return (! done()) && (! mIsEsc) && *mIter == ch;
+ }
+
+ virtual bool oneof(const string_type& delims) const
+ {
+ // Like base-class oneof(), except that an escaped character matches
+ // nothing.
+ return (! done()) && (! mIsEsc) && LLStringUtilBase<T>::contains(delims, *mIter);
+ }
+
+ virtual bool collect_until(string_type& into, const_iterator from, T delim)
+ {
+ // Deal with escapes in the characters we collect; that is, an escaped
+ // character must become just that character without the preceding
+ // escape. Collect characters in a separate string rather than
+ // directly appending to 'into' in case we do not find delim, in which
+ // case we're supposed to leave 'into' unmodified.
+ string_type collected;
+ // For scanning purposes, we're going to work directly with 'mIter'.
+ // Save its current value in case we fail to see delim.
+ const_iterator save_iter(mIter);
+ // Okay, set 'mIter', checking for escape.
+ setiter(from);
+ while (! done())
+ {
+ // If we see an unescaped delim, stop and report success.
+ if ((! mIsEsc) && *mIter == delim)
+ {
+ // Append collected chars to 'into'.
+ into.append(collected);
+ // Don't forget to advance 'mIter' past delim.
+ setiter(mIter + 1);
+ return true;
+ }
+ // We're not at end, and either we're not looking at delim or it's
+ // escaped. Collect this character and keep going.
+ collected.push_back(next());
+ }
+ // Here we hit 'mEnd' without ever seeing delim. Restore mIter and tell
+ // caller.
+ setiter(save_iter);
+ return false;
+ }
+
+private:
+ void setiter(const_iterator i)
+ {
+ mIter = i;
+
+ // Every time we change 'mIter', set 'mIsEsc' to be able to repetitively
+ // answer escaped() without having to rescan 'mEscapes'. mIsEsc caches
+ // contains(mEscapes, *mIter).
+
+ // We're looking at an escaped char if we're not already at end (that
+ // is, *mIter is even meaningful); if *mIter is in fact one of the
+ // specified escape characters; and if there's one more character
+ // following it. That is, if an escape character is the very last
+ // character of the input string, it loses its special meaning.
+ mIsEsc = (! done()) &&
+ LLStringUtilBase<T>::contains(mEscapes, *mIter) &&
+ (mIter+1) != mEnd;
+ }
+
+ const string_type mEscapes;
+ bool mIsEsc;
+};
+
+/// getTokens() implementation based on InString concept
+template <typename INSTRING, typename string_type>
+void getTokens(INSTRING& instr, std::vector<string_type>& tokens,
+ const string_type& drop_delims, const string_type& keep_delims,
+ const string_type& quotes)
+{
+ // There are times when we want to match either drop_delims or
+ // keep_delims. Concatenate them up front to speed things up.
+ string_type all_delims(drop_delims + keep_delims);
+ // no tokens yet
+ tokens.clear();
+
+ // try for another token
+ while (! instr.done())
+ {
+ // scan past any drop_delims
+ while (instr.oneof(drop_delims))
+ {
+ // skip this drop_delim
+ instr.next();
+ // but if that was the end of the string, done
+ if (instr.done())
+ return;
+ }
+ // found the start of another token: make a slot for it
+ tokens.push_back(string_type());
+ if (instr.oneof(keep_delims))
+ {
+ // *iter is a keep_delim, a token of exactly 1 character. Append
+ // that character to the new token and proceed.
+ tokens.back().push_back(instr.next());
+ continue;
+ }
+ // Here we have a non-delimiter token, which might consist of a mix of
+ // quoted and unquoted parts. Use bash rules for quoting: you can
+ // embed a quoted substring in the midst of an unquoted token (e.g.
+ // ~/"sub dir"/myfile.txt); you can ram two quoted substrings together
+ // to make a single token (e.g. 'He said, "'"Don't."'"'). We diverge
+ // from bash in that bash considers an unmatched quote an error. Our
+ // param signature doesn't allow for errors, so just pretend it's not
+ // a quote and embed it.
+ // At this level, keep scanning until we hit the next delimiter of
+ // either type (drop_delims or keep_delims).
+ while (! instr.oneof(all_delims))
+ {
+ // If we're looking at an open quote, search forward for
+ // a close quote, collecting characters along the way.
+ if (instr.oneof(quotes) &&
+ instr.collect_until(tokens.back(), instr.mIter+1, *instr.mIter))
+ {
+ // collect_until is cleverly designed to do exactly what we
+ // need here. No further action needed if it returns true.
+ }
+ else
+ {
+ // Either *iter isn't a quote, or there's no matching close
+ // quote: in other words, just an ordinary char. Append it to
+ // current token.
+ tokens.back().push_back(instr.next());
+ }
+ // having scanned that segment of this token, if we've reached the
+ // end of the string, we're done
+ if (instr.done())
+ return;
+ }
+ }
+}
+
+} // namespace LLStringUtilBaseImpl
+
+// static
+template <class T>
+void LLStringUtilBase<T>::getTokens(const string_type& string, std::vector<string_type>& tokens,
+ const string_type& drop_delims, const string_type& keep_delims,
+ const string_type& quotes)
+{
+ // Because this overload doesn't support escapes, use simple InString to
+ // manage input range.
+ LLStringUtilBaseImpl::InString<T> instring(string.begin(), string.end());
+ LLStringUtilBaseImpl::getTokens(instring, tokens, drop_delims, keep_delims, quotes);
+}
+
+// static
+template <class T>
+void LLStringUtilBase<T>::getTokens(const string_type& string, std::vector<string_type>& tokens,
+ const string_type& drop_delims, const string_type& keep_delims,
+ const string_type& quotes, const string_type& escapes)
+{
+ // This overload must deal with escapes. Delegate that to InEscString
+ // (unless there ARE no escapes).
+ std::unique_ptr< LLStringUtilBaseImpl::InString<T> > instrp;
+ if (escapes.empty())
+ instrp.reset(new LLStringUtilBaseImpl::InString<T>(string.begin(), string.end()));
+ else
+ instrp.reset(new LLStringUtilBaseImpl::InEscString<T>(string.begin(), string.end(), escapes));
+ LLStringUtilBaseImpl::getTokens(*instrp, tokens, drop_delims, keep_delims, quotes);
+}
+
+// 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 string_type& lhs, const string_type& 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
+ {
+ string_type lhs_string(lhs);
+ string_type 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 string_type& lhs, const string_type& rhs)
+{
+ string_type lhs_string(lhs);
+ string_type 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 string_type& astr, const string_type& 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 string_type& astr, const string_type& 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 string_type& a, const string_type& 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(string_type& string)
+{
+ if( !string.empty() )
+ {
+ std::transform(
+ string.begin(),
+ string.end(),
+ string.begin(),
+ (T(*)(T)) &LLStringOps::toUpper);
+ }
+}
+
+//static
+template<class T>
+void LLStringUtilBase<T>::toLower(string_type& string)
+{
+ if( !string.empty() )
+ {
+ std::transform(
+ string.begin(),
+ string.end(),
+ string.begin(),
+ (T(*)(T)) &LLStringOps::toLower);
+ }
+}
+
+//static
+template<class T>
+void LLStringUtilBase<T>::trimHead(string_type& 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(string_type& 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(string_type& 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);
+ delete[] t;
+ }
+}
+
+// Remove all carriage returns
+//static
+template<class T>
+void LLStringUtilBase<T>::removeCRLF(string_type& 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>::removeWindowsCR(string_type& string)
+{
+ if (string.empty())
+ {
+ return;
+ }
+ const T LF = 10;
+ const T CR = 13;
+
+ size_type cr_count = 0;
+ size_type len = string.size();
+ size_type i;
+ for( i = 0; i < len - cr_count - 1; i++ )
+ {
+ if( string[i+cr_count] == CR && string[i+cr_count+1] == LF)
+ {
+ cr_count++;
+ }
+
+ string[i] = string[i+cr_count];
+ }
+ string.erase(i, cr_count);
+}
+
+//static
+template<class T>
+void LLStringUtilBase<T>::replaceChar( string_type& string, T target, T replacement )
+{
+ size_type found_pos = 0;
+ while( (found_pos = string.find(target, found_pos)) != string_type::npos )
+ {
+ string[found_pos] = replacement;
+ found_pos++; // avoid infinite defeat if target == replacement
+ }
+}
+
+//static
+template<class T>
+void LLStringUtilBase<T>::replaceString( string_type& string, string_type target, string_type replacement )
+{
+ size_type found_pos = 0;
+ while( (found_pos = string.find(target, found_pos)) != string_type::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( string_type& 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( string_type& str, size_type spaces_per_tab )
+{
+ const T TAB = '\t';
+ const T SPACE = ' ';
+
+ string_type 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>
+std::basic_string<T> LLStringUtilBase<T>::capitalize(const string_type& str)
+{
+ string_type result(str);
+ capitalize(result);
+ return result;
+}
+
+//static
+template<class T>
+void LLStringUtilBase<T>::capitalize(string_type& str)
+{
+ if (str.size())
+ {
+ auto last = str[0] = toupper(str[0]);
+ for (U32 i = 1; i < str.size(); ++i)
+ {
+ last = (last == ' ' || last == '-' || last == '_') ? str[i] = toupper(str[i]) : str[i];
+ }
+ }
+}
+
+//static
+template<class T>
+bool LLStringUtilBase<T>::containsNonprintable(const string_type& 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;
+}
+
+// *TODO: reimplement in terms of algorithm
+//static
+template<class T>
+void LLStringUtilBase<T>::stripNonprintable(string_type& 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;
+}
+
+// *TODO: reimplement in terms of algorithm
+template<class T>
+std::basic_string<T> LLStringUtilBase<T>::quote(const string_type& str,
+ const string_type& triggers,
+ const string_type& escape)
+{
+ size_type len(str.length());
+ // If the string is already quoted, assume user knows what s/he's doing.
+ if (len >= 2 && str[0] == '"' && str[len-1] == '"')
+ {
+ return str;
+ }
+
+ // Not already quoted: do we need to? triggers.empty() is a special case
+ // meaning "always quote."
+ if ((! triggers.empty()) && str.find_first_of(triggers) == string_type::npos)
+ {
+ // no trigger characters, don't bother quoting
+ return str;
+ }
+
+ // For whatever reason, we must quote this string.
+ string_type result;
+ result.push_back('"');
+ for (typename string_type::const_iterator ci(str.begin()), cend(str.end()); ci != cend; ++ci)
+ {
+ if (*ci == '"')
+ {
+ result.append(escape);
+ }
+ result.push_back(*ci);
+ }
+ result.push_back('"');
+ return result;
+}
+
+template<class T>
+void LLStringUtilBase<T>::_makeASCII(string_type& 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(string_type& dst, const string_type& 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
+ {
+ string_type 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 string_type& 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 string_type& string,
+ const string_type& substr)
+{
+ if(string.empty() || (substr.empty())) return false;
+ if (substr.length() > string.length()) return false;
+ if (0 == string.compare(0, substr.length(), substr)) return true;
+ return false;
+}
+
+// static
+template<class T>
+bool LLStringUtilBase<T>::endsWith(
+ const string_type& string,
+ const string_type& substr)
+{
+ if(string.empty() || (substr.empty())) return false;
+ size_t sub_len = substr.length();
+ size_t str_len = string.length();
+ if (sub_len > str_len) return false;
+ if (0 == string.compare(str_len - sub_len, sub_len, substr)) return true;
+ return false;
+}
+
+// static
+template<class T>
+auto LLStringUtilBase<T>::getoptenv(const std::string& key) -> std::optional<string_type>
+{
+ auto found(llstring_getoptenv(key));
+ if (found)
+ {
+ // return populated std::optional
+ return { ll_convert<string_type>(*found) };
+ }
+ else
+ {
+ // empty std::optional
+ return {};
+ }
+}
+
+// static
+template<class T>
+auto LLStringUtilBase<T>::getenv(const std::string& key, const string_type& dflt) -> string_type
+{
+ auto found(getoptenv(key));
+ if (found)
+ {
+ return *found;
+ }
+ else
+ {
+ return dflt;
+ }
+}
+
+template<class T>
+bool LLStringUtilBase<T>::convertToBOOL(const string_type& string, bool& value)
+{
+ if( string.empty() )
+ {
+ return false;
+ }
+
+ string_type 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 string_type& 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 string_type& 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 string_type& 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 string_type& 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 string_type& string, U32& value)
+{
+ if( string.empty() )
+ {
+ return false;
+ }
+
+ string_type temp( string );
+ trim(temp);
+ U32 v;
+ std::basic_istringstream<T> i_stream((string_type)temp);
+ if(i_stream >> v)
+ {
+ value = v;
+ return true;
+ }
+ return false;
+}
+
+template<class T>
+bool LLStringUtilBase<T>::convertToS32(const string_type& string, S32& value)
+{
+ if( string.empty() )
+ {
+ return false;
+ }
+
+ string_type temp( string );
+ trim(temp);
+ S32 v;
+ std::basic_istringstream<T> i_stream((string_type)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 string_type& 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 string_type& string, F64& value)
+{
+ if( string.empty() )
+ {
+ return false;
+ }
+
+ string_type temp( string );
+ trim(temp);
+ F64 v;
+ std::basic_istringstream<T> i_stream((string_type)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(string_type& string, size_type count)
+{
+ size_type cur_size = string.size();
+ string.resize(count < cur_size ? count : cur_size);
+}
+
+// The good thing about *declaration* macros, vs. usage macros, is that now
+// we're done with them: we don't need them to bleed into the consuming source
+// file.
+#undef ll_convert_alias
+#undef ll_convert_u16_alias
+#undef ll_convert_wstr_alias
+#undef LL_CONVERT_COPY_CHARS
+#undef ll_convert_forms
+#undef ll_convert_cp_forms
+
+#endif // LL_STRING_H
diff --git a/indra/llcommon/llstringtable.cpp b/indra/llcommon/llstringtable.cpp index 92a5e777a6..d28e1e2219 100644 --- a/indra/llcommon/llstringtable.cpp +++ b/indra/llcommon/llstringtable.cpp @@ -1,4 +1,4 @@ -/** +/** * @file llstringtable.cpp * @brief The LLStringTable class provides a _fast_ method for finding * unique copies of strings. @@ -6,21 +6,21 @@ * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -35,101 +35,101 @@ LLStringTable gStringTable(32768); LLStringTableEntry::LLStringTableEntry(const char *str) : mString(NULL), mCount(1) { - // Copy string - U32 length = (U32)strlen(str) + 1; /*Flawfinder: ignore*/ - length = llmin(length, MAX_STRINGS_LENGTH); - mString = new char[length]; - strncpy(mString, str, length); /*Flawfinder: ignore*/ - mString[length - 1] = 0; + // Copy string + U32 length = (U32)strlen(str) + 1; /*Flawfinder: ignore*/ + length = llmin(length, MAX_STRINGS_LENGTH); + mString = new char[length]; + strncpy(mString, str, length); /*Flawfinder: ignore*/ + mString[length - 1] = 0; } LLStringTableEntry::~LLStringTableEntry() { - delete [] mString; - mCount = 0; + delete [] mString; + mCount = 0; } LLStringTable::LLStringTable(int tablesize) : mUniqueEntries(0) { - S32 i; - if (!tablesize) - tablesize = 4096; // some arbitrary default - // Make sure tablesize is power of 2 - for (i = 31; i>0; i--) - { - if (tablesize & (1<<i)) - { - if (tablesize >= (3<<(i-1))) - tablesize = (1<<(i+1)); - else - tablesize = (1<<i); - break; - } - } - mMaxEntries = tablesize; + S32 i; + if (!tablesize) + tablesize = 4096; // some arbitrary default + // Make sure tablesize is power of 2 + for (i = 31; i>0; i--) + { + if (tablesize & (1<<i)) + { + if (tablesize >= (3<<(i-1))) + tablesize = (1<<(i+1)); + else + tablesize = (1<<i); + break; + } + } + mMaxEntries = tablesize; #if !STRING_TABLE_HASH_MAP - // ALlocate strings - mStringList = new string_list_ptr_t[mMaxEntries]; - // Clear strings - for (i = 0; i < mMaxEntries; i++) - { - mStringList[i] = NULL; - } + // ALlocate strings + mStringList = new string_list_ptr_t[mMaxEntries]; + // Clear strings + for (i = 0; i < mMaxEntries; i++) + { + mStringList[i] = NULL; + } #endif } LLStringTable::~LLStringTable() { #if !STRING_TABLE_HASH_MAP - if (mStringList) - { - for (S32 i = 0; i < mMaxEntries; i++) - { - if (mStringList[i]) - { - for (LLStringTableEntry* entry : *mStringList[i]) - delete entry; - } - delete mStringList[i]; - } - delete [] mStringList; - mStringList = NULL; - } + if (mStringList) + { + for (S32 i = 0; i < mMaxEntries; i++) + { + if (mStringList[i]) + { + for (LLStringTableEntry* entry : *mStringList[i]) + delete entry; + } + delete mStringList[i]; + } + delete [] mStringList; + mStringList = NULL; + } #else - // Need to clean up the string hash - for_each(mStringHash.begin(), mStringHash.end(), DeletePairedPointer()); - mStringHash.clear(); + // Need to clean up the string hash + for_each(mStringHash.begin(), mStringHash.end(), DeletePairedPointer()); + mStringHash.clear(); #endif } static U32 hash_my_string(const char *str, int max_entries) { - U32 retval = 0; + U32 retval = 0; #if 0 - while (*str) - { - retval <<= 1; - retval += *str++; - } + while (*str) + { + retval <<= 1; + retval += *str++; + } #else - while (*str) - { - retval = (retval<<4) + *str; - U32 x = (retval & 0xf0000000); - if (x) retval = retval ^ (x>>24); - retval = retval & (~x); - str++; - } + while (*str) + { + retval = (retval<<4) + *str; + U32 x = (retval & 0xf0000000); + if (x) retval = retval ^ (x>>24); + retval = retval & (~x); + str++; + } #endif - return (retval & (max_entries-1)); // max_entries is gauranteed to be power of 2 + return (retval & (max_entries-1)); // max_entries is gauranteed to be power of 2 } char* LLStringTable::checkString(const std::string& str) { - return checkString(str.c_str()); + return checkString(str.c_str()); } char* LLStringTable::checkString(const char *str) @@ -137,11 +137,11 @@ char* LLStringTable::checkString(const char *str) LLStringTableEntry* entry = checkStringEntry(str); if (entry) { - return entry->mString; + return entry->mString; } else { - return NULL; + return NULL; } } @@ -152,51 +152,51 @@ LLStringTableEntry* LLStringTable::checkStringEntry(const std::string& str) LLStringTableEntry* LLStringTable::checkStringEntry(const char *str) { - if (str) - { - char *ret_val; - U32 hash_value = hash_my_string(str, mMaxEntries); + if (str) + { + char *ret_val; + U32 hash_value = hash_my_string(str, mMaxEntries); #if STRING_TABLE_HASH_MAP - LLStringTableEntry *entry; + LLStringTableEntry *entry; #if 1 // Microsoft - string_hash_t::iterator lower = mStringHash.lower_bound(hash_value); - string_hash_t::iterator upper = mStringHash.upper_bound(hash_value); + string_hash_t::iterator lower = mStringHash.lower_bound(hash_value); + string_hash_t::iterator upper = mStringHash.upper_bound(hash_value); #else // stlport - std::pair<string_hash_t::iterator, string_hash_t::iterator> P = mStringHash.equal_range(hash_value); - string_hash_t::iterator lower = P.first; - string_hash_t::iterator upper = P.second; + std::pair<string_hash_t::iterator, string_hash_t::iterator> P = mStringHash.equal_range(hash_value); + string_hash_t::iterator lower = P.first; + string_hash_t::iterator upper = P.second; #endif - for (string_hash_t::iterator iter = lower; iter != upper; iter++) - { - entry = iter->second; - ret_val = entry->mString; - if (!strncmp(ret_val, str, MAX_STRINGS_LENGTH)) - { - return entry; - } - } + for (string_hash_t::iterator iter = lower; iter != upper; iter++) + { + entry = iter->second; + ret_val = entry->mString; + if (!strncmp(ret_val, str, MAX_STRINGS_LENGTH)) + { + return entry; + } + } #else - string_list_t *strlist = mStringList[hash_value]; - if (strlist) - { - for (LLStringTableEntry* entry : *strlist) - { - ret_val = entry->mString; - if (!strncmp(ret_val, str, MAX_STRINGS_LENGTH)) - { - return entry; - } - } - } + string_list_t *strlist = mStringList[hash_value]; + if (strlist) + { + for (LLStringTableEntry* entry : *strlist) + { + ret_val = entry->mString; + if (!strncmp(ret_val, str, MAX_STRINGS_LENGTH)) + { + return entry; + } + } + } #endif - } - return NULL; + } + return NULL; } char* LLStringTable::addString(const std::string& str) { - //RN: safe to use temporary c_str since string is copied - return addString(str.c_str()); + //RN: safe to use temporary c_str since string is copied + return addString(str.c_str()); } char* LLStringTable::addString(const char *str) @@ -205,11 +205,11 @@ char* LLStringTable::addString(const char *str) LLStringTableEntry* entry = addStringEntry(str); if (entry) { - return entry->mString; + return entry->mString; } else { - return NULL; + return NULL; } } @@ -220,132 +220,132 @@ LLStringTableEntry* LLStringTable::addStringEntry(const std::string& str) LLStringTableEntry* LLStringTable::addStringEntry(const char *str) { - if (str) - { - char *ret_val = NULL; - U32 hash_value = hash_my_string(str, mMaxEntries); + if (str) + { + char *ret_val = NULL; + U32 hash_value = hash_my_string(str, mMaxEntries); #if STRING_TABLE_HASH_MAP - LLStringTableEntry *entry; + LLStringTableEntry *entry; #if 1 // Microsoft - string_hash_t::iterator lower = mStringHash.lower_bound(hash_value); - string_hash_t::iterator upper = mStringHash.upper_bound(hash_value); + string_hash_t::iterator lower = mStringHash.lower_bound(hash_value); + string_hash_t::iterator upper = mStringHash.upper_bound(hash_value); #else // stlport - std::pair<string_hash_t::iterator, string_hash_t::iterator> P = mStringHash.equal_range(hash_value); - string_hash_t::iterator lower = P.first; - string_hash_t::iterator upper = P.second; + std::pair<string_hash_t::iterator, string_hash_t::iterator> P = mStringHash.equal_range(hash_value); + string_hash_t::iterator lower = P.first; + string_hash_t::iterator upper = P.second; #endif - for (string_hash_t::iterator iter = lower; iter != upper; iter++) - { - entry = iter->second; - ret_val = entry->mString; - if (!strncmp(ret_val, str, MAX_STRINGS_LENGTH)) - { - entry->incCount(); - return entry; - } - } + for (string_hash_t::iterator iter = lower; iter != upper; iter++) + { + entry = iter->second; + ret_val = entry->mString; + if (!strncmp(ret_val, str, MAX_STRINGS_LENGTH)) + { + entry->incCount(); + return entry; + } + } - // not found, so add! - LLStringTableEntry* newentry = new LLStringTableEntry(str); - ret_val = newentry->mString; - mStringHash.insert(string_hash_t::value_type(hash_value, newentry)); + // not found, so add! + LLStringTableEntry* newentry = new LLStringTableEntry(str); + ret_val = newentry->mString; + mStringHash.insert(string_hash_t::value_type(hash_value, newentry)); #else - string_list_t *strlist = mStringList[hash_value]; + string_list_t *strlist = mStringList[hash_value]; - if (strlist) - { - for (LLStringTableEntry* entry : *strlist) - { - ret_val = entry->mString; - if (!strncmp(ret_val, str, MAX_STRINGS_LENGTH)) - { - entry->incCount(); - return entry; - } - } - } - else - { - mStringList[hash_value] = new string_list_t; - strlist = mStringList[hash_value]; - } + if (strlist) + { + for (LLStringTableEntry* entry : *strlist) + { + ret_val = entry->mString; + if (!strncmp(ret_val, str, MAX_STRINGS_LENGTH)) + { + entry->incCount(); + return entry; + } + } + } + else + { + mStringList[hash_value] = new string_list_t; + strlist = mStringList[hash_value]; + } - // not found, so add! - LLStringTableEntry *newentry = new LLStringTableEntry(str); - //ret_val = newentry->mString; - strlist->push_front(newentry); + // not found, so add! + LLStringTableEntry *newentry = new LLStringTableEntry(str); + //ret_val = newentry->mString; + strlist->push_front(newentry); #endif - mUniqueEntries++; - return newentry; - } - else - { - return NULL; - } + mUniqueEntries++; + return newentry; + } + else + { + return NULL; + } } void LLStringTable::removeString(const char *str) { - if (str) - { - char *ret_val; - U32 hash_value = hash_my_string(str, mMaxEntries); + if (str) + { + char *ret_val; + U32 hash_value = hash_my_string(str, mMaxEntries); #if STRING_TABLE_HASH_MAP - { - LLStringTableEntry *entry; + { + LLStringTableEntry *entry; #if 1 // Microsoft - string_hash_t::iterator lower = mStringHash.lower_bound(hash_value); - string_hash_t::iterator upper = mStringHash.upper_bound(hash_value); + string_hash_t::iterator lower = mStringHash.lower_bound(hash_value); + string_hash_t::iterator upper = mStringHash.upper_bound(hash_value); #else // stlport - std::pair<string_hash_t::iterator, string_hash_t::iterator> P = mStringHash.equal_range(hash_value); - string_hash_t::iterator lower = P.first; - string_hash_t::iterator upper = P.second; + std::pair<string_hash_t::iterator, string_hash_t::iterator> P = mStringHash.equal_range(hash_value); + string_hash_t::iterator lower = P.first; + string_hash_t::iterator upper = P.second; #endif - for (string_hash_t::iterator iter = lower; iter != upper; iter++) - { - entry = iter->second; - ret_val = entry->mString; - if (!strncmp(ret_val, str, MAX_STRINGS_LENGTH)) - { - if (!entry->decCount()) - { - mUniqueEntries--; - if (mUniqueEntries < 0) - { - LL_ERRS() << "LLStringTable:removeString trying to remove too many strings!" << LL_ENDL; - } - delete iter->second; - mStringHash.erase(iter); - } - return; - } - } - } + for (string_hash_t::iterator iter = lower; iter != upper; iter++) + { + entry = iter->second; + ret_val = entry->mString; + if (!strncmp(ret_val, str, MAX_STRINGS_LENGTH)) + { + if (!entry->decCount()) + { + mUniqueEntries--; + if (mUniqueEntries < 0) + { + LL_ERRS() << "LLStringTable:removeString trying to remove too many strings!" << LL_ENDL; + } + delete iter->second; + mStringHash.erase(iter); + } + return; + } + } + } #else - string_list_t *strlist = mStringList[hash_value]; + string_list_t *strlist = mStringList[hash_value]; - if (strlist) - { - for (LLStringTableEntry* entry : *strlist) - { - ret_val = entry->mString; - if (!strncmp(ret_val, str, MAX_STRINGS_LENGTH)) - { - if (!entry->decCount()) - { - mUniqueEntries--; - if (mUniqueEntries < 0) - { - LL_ERRS() << "LLStringTable:removeString trying to remove too many strings!" << LL_ENDL; - } - strlist->remove(entry); - delete entry; - } - return; - } - } - } + if (strlist) + { + for (LLStringTableEntry* entry : *strlist) + { + ret_val = entry->mString; + if (!strncmp(ret_val, str, MAX_STRINGS_LENGTH)) + { + if (!entry->decCount()) + { + mUniqueEntries--; + if (mUniqueEntries < 0) + { + LL_ERRS() << "LLStringTable:removeString trying to remove too many strings!" << LL_ENDL; + } + strlist->remove(entry); + delete entry; + } + return; + } + } + } #endif - } + } } diff --git a/indra/llcommon/llstringtable.h b/indra/llcommon/llstringtable.h index bbf61bb8ac..a9b38fb14b 100644 --- a/indra/llcommon/llstringtable.h +++ b/indra/llcommon/llstringtable.h @@ -1,209 +1,209 @@ -/** - * @file llstringtable.h - * @brief The LLStringTable class provides a _fast_ method for finding - * unique copies of strings. - * - * $LicenseInfo:firstyear=2001&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_STRING_TABLE_H -#define LL_STRING_TABLE_H - -#include "lldefs.h" -#include "llformat.h" -#include "llstl.h" -#include <list> -#include <set> - -#if LL_WINDOWS -# if (_MSC_VER >= 1300 && _MSC_VER < 1400) -# define STRING_TABLE_HASH_MAP 1 -# endif -#else -//# define STRING_TABLE_HASH_MAP 1 -#endif - -const U32 MAX_STRINGS_LENGTH = 256; - -class LL_COMMON_API LLStringTableEntry -{ -public: - LLStringTableEntry(const char *str); - ~LLStringTableEntry(); - - void incCount() { mCount++; } - bool decCount() { return --mCount != 0; } - - char *mString; - S32 mCount; -}; - -class LL_COMMON_API LLStringTable -{ -public: - LLStringTable(int tablesize); - ~LLStringTable(); - - char *checkString(const char *str); - char *checkString(const std::string& str); - LLStringTableEntry *checkStringEntry(const char *str); - LLStringTableEntry *checkStringEntry(const std::string& str); - - char *addString(const char *str); - char *addString(const std::string& str); - LLStringTableEntry *addStringEntry(const char *str); - LLStringTableEntry *addStringEntry(const std::string& str); - void removeString(const char *str); - - S32 mMaxEntries; - S32 mUniqueEntries; - -#if STRING_TABLE_HASH_MAP -#if LL_WINDOWS - typedef std::hash_multimap<U32, LLStringTableEntry *> string_hash_t; -#else - typedef __gnu_cxx::hash_multimap<U32, LLStringTableEntry *> string_hash_t; -#endif - string_hash_t mStringHash; -#else - typedef std::list<LLStringTableEntry *> string_list_t; - typedef string_list_t * string_list_ptr_t; - string_list_ptr_t *mStringList; -#endif -}; - -extern LL_COMMON_API LLStringTable gStringTable; - -//============================================================================ - -// This class is designed to be used locally, -// e.g. as a member of an LLXmlTree -// Strings can be inserted only, then quickly looked up - -typedef const std::string* LLStdStringHandle; - -class LL_COMMON_API LLStdStringTable -{ -public: - LLStdStringTable(S32 tablesize = 0) - { - if (tablesize == 0) - { - tablesize = 256; // default - } - // Make sure tablesize is power of 2 - for (S32 i = 31; i>0; i--) - { - if (tablesize & (1<<i)) - { - if (tablesize >= (3<<(i-1))) - tablesize = (1<<(i+1)); - else - tablesize = (1<<i); - break; - } - } - mTableSize = tablesize; - mStringList = new string_set_t[tablesize]; - } - ~LLStdStringTable() - { - cleanup(); - delete[] mStringList; - } - void cleanup() - { - // remove strings - for (S32 i = 0; i<mTableSize; i++) - { - string_set_t& stringset = mStringList[i]; - for (LLStdStringHandle str : stringset) - { - delete str; - } - stringset.clear(); - } - } - - LLStdStringHandle lookup(const std::string& s) - { - U32 hashval = makehash(s); - return lookup(hashval, s); - } - - LLStdStringHandle checkString(const std::string& s) - { - U32 hashval = makehash(s); - return lookup(hashval, s); - } - - LLStdStringHandle insert(const std::string& s) - { - U32 hashval = makehash(s); - LLStdStringHandle result = lookup(hashval, s); - if (result == NULL) - { - result = new std::string(s); - mStringList[hashval].insert(result); - } - return result; - } - LLStdStringHandle addString(const std::string& s) - { - return insert(s); - } - -private: - U32 makehash(const std::string& s) - { - S32 len = (S32)s.size(); - const char* c = s.c_str(); - U32 hashval = 0; - for (S32 i=0; i<len; i++) - { - hashval = ((hashval<<5) + hashval) + *c++; - } - return hashval & (mTableSize-1); - } - LLStdStringHandle lookup(U32 hashval, const std::string& s) - { - string_set_t& stringset = mStringList[hashval]; - LLStdStringHandle handle = &s; - string_set_t::iterator iter = stringset.find(handle); // compares actual strings - if (iter != stringset.end()) - { - return *iter; - } - else - { - return NULL; - } - } - -private: - S32 mTableSize; - typedef std::set<LLStdStringHandle, compare_pointer_contents<std::string> > string_set_t; - string_set_t* mStringList; // [mTableSize] -}; - - -#endif +/**
+ * @file llstringtable.h
+ * @brief The LLStringTable class provides a _fast_ method for finding
+ * unique copies of strings.
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_STRING_TABLE_H
+#define LL_STRING_TABLE_H
+
+#include "lldefs.h"
+#include "llformat.h"
+#include "llstl.h"
+#include <list>
+#include <set>
+
+#if LL_WINDOWS
+# if (_MSC_VER >= 1300 && _MSC_VER < 1400)
+# define STRING_TABLE_HASH_MAP 1
+# endif
+#else
+//# define STRING_TABLE_HASH_MAP 1
+#endif
+
+const U32 MAX_STRINGS_LENGTH = 256;
+
+class LL_COMMON_API LLStringTableEntry
+{
+public:
+ LLStringTableEntry(const char *str);
+ ~LLStringTableEntry();
+
+ void incCount() { mCount++; }
+ bool decCount() { return --mCount != 0; }
+
+ char *mString;
+ S32 mCount;
+};
+
+class LL_COMMON_API LLStringTable
+{
+public:
+ LLStringTable(int tablesize);
+ ~LLStringTable();
+
+ char *checkString(const char *str);
+ char *checkString(const std::string& str);
+ LLStringTableEntry *checkStringEntry(const char *str);
+ LLStringTableEntry *checkStringEntry(const std::string& str);
+
+ char *addString(const char *str);
+ char *addString(const std::string& str);
+ LLStringTableEntry *addStringEntry(const char *str);
+ LLStringTableEntry *addStringEntry(const std::string& str);
+ void removeString(const char *str);
+
+ S32 mMaxEntries;
+ S32 mUniqueEntries;
+
+#if STRING_TABLE_HASH_MAP
+#if LL_WINDOWS
+ typedef std::hash_multimap<U32, LLStringTableEntry *> string_hash_t;
+#else
+ typedef __gnu_cxx::hash_multimap<U32, LLStringTableEntry *> string_hash_t;
+#endif
+ string_hash_t mStringHash;
+#else
+ typedef std::list<LLStringTableEntry *> string_list_t;
+ typedef string_list_t * string_list_ptr_t;
+ string_list_ptr_t *mStringList;
+#endif
+};
+
+extern LL_COMMON_API LLStringTable gStringTable;
+
+//============================================================================
+
+// This class is designed to be used locally,
+// e.g. as a member of an LLXmlTree
+// Strings can be inserted only, then quickly looked up
+
+typedef const std::string* LLStdStringHandle;
+
+class LL_COMMON_API LLStdStringTable
+{
+public:
+ LLStdStringTable(S32 tablesize = 0)
+ {
+ if (tablesize == 0)
+ {
+ tablesize = 256; // default
+ }
+ // Make sure tablesize is power of 2
+ for (S32 i = 31; i>0; i--)
+ {
+ if (tablesize & (1<<i))
+ {
+ if (tablesize >= (3<<(i-1)))
+ tablesize = (1<<(i+1));
+ else
+ tablesize = (1<<i);
+ break;
+ }
+ }
+ mTableSize = tablesize;
+ mStringList = new string_set_t[tablesize];
+ }
+ ~LLStdStringTable()
+ {
+ cleanup();
+ delete[] mStringList;
+ }
+ void cleanup()
+ {
+ // remove strings
+ for (S32 i = 0; i<mTableSize; i++)
+ {
+ string_set_t& stringset = mStringList[i];
+ for (LLStdStringHandle str : stringset)
+ {
+ delete str;
+ }
+ stringset.clear();
+ }
+ }
+
+ LLStdStringHandle lookup(const std::string& s)
+ {
+ U32 hashval = makehash(s);
+ return lookup(hashval, s);
+ }
+
+ LLStdStringHandle checkString(const std::string& s)
+ {
+ U32 hashval = makehash(s);
+ return lookup(hashval, s);
+ }
+
+ LLStdStringHandle insert(const std::string& s)
+ {
+ U32 hashval = makehash(s);
+ LLStdStringHandle result = lookup(hashval, s);
+ if (result == NULL)
+ {
+ result = new std::string(s);
+ mStringList[hashval].insert(result);
+ }
+ return result;
+ }
+ LLStdStringHandle addString(const std::string& s)
+ {
+ return insert(s);
+ }
+
+private:
+ U32 makehash(const std::string& s)
+ {
+ S32 len = (S32)s.size();
+ const char* c = s.c_str();
+ U32 hashval = 0;
+ for (S32 i=0; i<len; i++)
+ {
+ hashval = ((hashval<<5) + hashval) + *c++;
+ }
+ return hashval & (mTableSize-1);
+ }
+ LLStdStringHandle lookup(U32 hashval, const std::string& s)
+ {
+ string_set_t& stringset = mStringList[hashval];
+ LLStdStringHandle handle = &s;
+ string_set_t::iterator iter = stringset.find(handle); // compares actual strings
+ if (iter != stringset.end())
+ {
+ return *iter;
+ }
+ else
+ {
+ return NULL;
+ }
+ }
+
+private:
+ S32 mTableSize;
+ typedef std::set<LLStdStringHandle, compare_pointer_contents<std::string> > string_set_t;
+ string_set_t* mStringList; // [mTableSize]
+};
+
+
+#endif
diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp index efa7ea5666..02fd0733e5 100644 --- a/indra/llcommon/llsys.cpp +++ b/indra/llcommon/llsys.cpp @@ -1,1410 +1,1414 @@ -/** - * @file llsys.cpp - * @brief Implementation of the basic system query functions. - * - * $LicenseInfo:firstyear=2002&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#if LL_WINDOWS -#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally -#endif - -#include "linden_common.h" - -#include "llsys.h" - -#include <iostream> -#ifdef LL_USESYSTEMLIBS -# include <zlib.h> -#else -# include "zlib-ng/zlib.h" -#endif - -#include "llprocessor.h" -#include "llerrorcontrol.h" -#include "llevents.h" -#include "llformat.h" -#include "llregex.h" -#include "lltimer.h" -#include "llsdserialize.h" -#include "llsdutil.h" -#include <boost/bind.hpp> -#include <boost/circular_buffer.hpp> -#include <boost/lexical_cast.hpp> -#include <boost/range.hpp> -#include <boost/utility/enable_if.hpp> -#include <boost/type_traits/is_integral.hpp> -#include <boost/type_traits/is_float.hpp> -#include "llfasttimer.h" - -using namespace llsd; - -#if LL_WINDOWS -# include "llwin32headerslean.h" -# include <psapi.h> // GetPerformanceInfo() et al. -# include <VersionHelpers.h> -#elif LL_DARWIN -# include "llsys_objc.h" -# include <errno.h> -# include <sys/sysctl.h> -# include <sys/utsname.h> -# include <stdint.h> -# include <CoreServices/CoreServices.h> -# include <stdexcept> -# include <mach/host_info.h> -# include <mach/mach_host.h> -# include <mach/task.h> -# include <mach/task_info.h> -#elif LL_LINUX -# include <errno.h> -# include <sys/utsname.h> -# include <unistd.h> -# include <sys/sysinfo.h> -# include <stdexcept> -const char MEMINFO_FILE[] = "/proc/meminfo"; -# include <gnu/libc-version.h> -#endif - -LLCPUInfo gSysCPU; - -// Don't log memory info any more often than this. It also serves as our -// framerate sample size. -static const F32 MEM_INFO_THROTTLE = 20; -// Sliding window of samples. We intentionally limit the length of time we -// remember "the slowest" framerate because framerate is very slow at login. -// If we only triggered FrameWatcher logging when the session framerate -// dropped below the login framerate, we'd have very little additional data. -static const F32 MEM_INFO_WINDOW = 10*60; - -LLOSInfo::LLOSInfo() : - mMajorVer(0), mMinorVer(0), mBuild(0), mOSVersionString("") -{ - -#if LL_WINDOWS - - if (IsWindows10OrGreater()) - { - mMajorVer = 10; - mMinorVer = 0; - mOSStringSimple = "Microsoft Windows 10 "; - } - else if (IsWindows8Point1OrGreater()) - { - mMajorVer = 6; - mMinorVer = 3; - if (IsWindowsServer()) - { - mOSStringSimple = "Windows Server 2012 R2 "; - } - else - { - mOSStringSimple = "Microsoft Windows 8.1 "; - } - } - else if (IsWindows8OrGreater()) - { - mMajorVer = 6; - mMinorVer = 2; - if (IsWindowsServer()) - { - mOSStringSimple = "Windows Server 2012 "; - } - else - { - mOSStringSimple = "Microsoft Windows 8 "; - } - } - else if (IsWindows7SP1OrGreater()) - { - mMajorVer = 6; - mMinorVer = 1; - if (IsWindowsServer()) - { - mOSStringSimple = "Windows Server 2008 R2 SP1 "; - } - else - { - mOSStringSimple = "Microsoft Windows 7 SP1 "; - } - } - else if (IsWindows7OrGreater()) - { - mMajorVer = 6; - mMinorVer = 1; - if (IsWindowsServer()) - { - mOSStringSimple = "Windows Server 2008 R2 "; - } - else - { - mOSStringSimple = "Microsoft Windows 7 "; - } - } - else if (IsWindowsVistaSP2OrGreater()) - { - mMajorVer = 6; - mMinorVer = 0; - if (IsWindowsServer()) - { - mOSStringSimple = "Windows Server 2008 SP2 "; - } - else - { - mOSStringSimple = "Microsoft Windows Vista SP2 "; - } - } - else - { - mOSStringSimple = "Unsupported Windows version "; - } - - ///get native system info if available.. - typedef void (WINAPI *PGNSI)(LPSYSTEM_INFO); ///function pointer for loading GetNativeSystemInfo - SYSTEM_INFO si; //System Info object file contains architecture info - PGNSI pGNSI; //pointer object - ZeroMemory(&si, sizeof(SYSTEM_INFO)); //zero out the memory in information - pGNSI = (PGNSI)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetNativeSystemInfo"); //load kernel32 get function - if (NULL != pGNSI) //check if it has failed - pGNSI(&si); //success - else - GetSystemInfo(&si); //if it fails get regular system info - //(Warning: If GetSystemInfo it may result in incorrect information in a WOW64 machine, if the kernel fails to load) - - // Try calling GetVersionEx using the OSVERSIONINFOEX structure. - OSVERSIONINFOEX osvi; - ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); - osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); - if (GetVersionEx((OSVERSIONINFO *)&osvi)) - { - mBuild = osvi.dwBuildNumber & 0xffff; - } - else - { - // If OSVERSIONINFOEX doesn't work, try OSVERSIONINFO. - osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); - if (GetVersionEx((OSVERSIONINFO *)&osvi)) - { - mBuild = osvi.dwBuildNumber & 0xffff; - } - } - - S32 ubr = 0; // Windows 10 Update Build Revision, can be retrieved from a registry - if (mMajorVer == 10) - { - DWORD cbData(sizeof(DWORD)); - DWORD data(0); - HKEY key; - LSTATUS ret_code = RegOpenKeyExW(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"), 0, KEY_READ, &key); - if (ERROR_SUCCESS == ret_code) - { - ret_code = RegQueryValueExW(key, L"UBR", 0, NULL, reinterpret_cast<LPBYTE>(&data), &cbData); - if (ERROR_SUCCESS == ret_code) - { - ubr = data; - } - } - - if (mBuild >= 22000) - { - // At release Windows 11 version was 10.0.22000.194 - // Windows 10 version was 10.0.19043.1266 - // There is no warranty that Win10 build won't increase, - // so until better solution is found or Microsoft updates - // SDK with IsWindows11OrGreater(), indicate "10/11" - // - // Current alternatives: - // Query WMI's Win32_OperatingSystem for OS string. Slow - // and likely to return 'compatibility' string. - // Check presence of dlls/libs or may be their version. - mOSStringSimple = "Microsoft Windows 10/11 "; - } - } - - //msdn microsoft finds 32 bit and 64 bit flavors this way.. - //http://msdn.microsoft.com/en-us/library/ms724429(VS.85).aspx (example code that contains quite a few more flavors - //of windows than this code does (in case it is needed for the future) - if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) //check for 64 bit - { - mOSStringSimple += "64-bit "; - } - else if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL) - { - mOSStringSimple += "32-bit "; - } - - mOSString = mOSStringSimple; - if (mBuild > 0) - { - mOSString += llformat("(Build %d", mBuild); - if (ubr > 0) - { - mOSString += llformat(".%d", ubr); - } - mOSString += ")"; - } - - LLStringUtil::trim(mOSStringSimple); - LLStringUtil::trim(mOSString); - -#elif LL_DARWIN - - // Initialize mOSStringSimple to something like: - // "macOS 10.13.1" - { - const char * DARWIN_PRODUCT_NAME = "macOS"; - - int64_t major_version, minor_version, bugfix_version = 0; - - if (LLGetDarwinOSInfo(major_version, minor_version, bugfix_version)) - { - mMajorVer = major_version; - mMinorVer = minor_version; - mBuild = bugfix_version; - - std::stringstream os_version_string; - os_version_string << DARWIN_PRODUCT_NAME << " " << mMajorVer << "." << mMinorVer << "." << mBuild; - - // Put it in the OS string we are compiling - mOSStringSimple.append(os_version_string.str()); - } - else - { - mOSStringSimple.append("Unable to collect OS info"); - } - } - - // Initialize mOSString to something like: - // "macOS 10.13.1 Darwin Kernel Version 10.7.0: Sat Jan 29 15:17:16 PST 2011; root:xnu-1504.9.37~1/RELEASE_I386 i386" - struct utsname un; - if(uname(&un) != -1) - { - mOSString = mOSStringSimple; - mOSString.append(" "); - mOSString.append(un.sysname); - mOSString.append(" "); - mOSString.append(un.release); - mOSString.append(" "); - mOSString.append(un.version); - mOSString.append(" "); - mOSString.append(un.machine); - } - else - { - mOSString = mOSStringSimple; - } - -#elif LL_LINUX - - struct utsname un; - if(uname(&un) != -1) - { - mOSStringSimple.append(un.sysname); - mOSStringSimple.append(" "); - mOSStringSimple.append(un.release); - - mOSString = mOSStringSimple; - mOSString.append(" "); - mOSString.append(un.version); - mOSString.append(" "); - mOSString.append(un.machine); - - // Simplify 'Simple' - std::string ostype = mOSStringSimple.substr(0, mOSStringSimple.find_first_of(" ", 0)); - if (ostype == "Linux") - { - // Only care about major and minor Linux versions, truncate at second '.' - std::string::size_type idx1 = mOSStringSimple.find_first_of(".", 0); - std::string::size_type idx2 = (idx1 != std::string::npos) ? mOSStringSimple.find_first_of(".", idx1+1) : std::string::npos; - std::string simple = mOSStringSimple.substr(0, idx2); - if (simple.length() > 0) - mOSStringSimple = simple; - } - } - else - { - mOSStringSimple.append("Unable to collect OS info"); - mOSString = mOSStringSimple; - } - - const char OS_VERSION_MATCH_EXPRESSION[] = "([0-9]+)\\.([0-9]+)(\\.([0-9]+))?"; - boost::regex os_version_parse(OS_VERSION_MATCH_EXPRESSION); - boost::smatch matched; - - std::string glibc_version(gnu_get_libc_version()); - if ( ll_regex_match(glibc_version, matched, os_version_parse) ) - { - LL_INFOS("AppInit") << "Using glibc version '" << glibc_version << "' as OS version" << LL_ENDL; - - std::string version_value; - - if ( matched[1].matched ) // Major version - { - version_value.assign(matched[1].first, matched[1].second); - if (sscanf(version_value.c_str(), "%d", &mMajorVer) != 1) - { - LL_WARNS("AppInit") << "failed to parse major version '" << version_value << "' as a number" << LL_ENDL; - } - } - else - { - LL_ERRS("AppInit") - << "OS version regex '" << OS_VERSION_MATCH_EXPRESSION - << "' returned true, but major version [1] did not match" - << LL_ENDL; - } - - if ( matched[2].matched ) // Minor version - { - version_value.assign(matched[2].first, matched[2].second); - if (sscanf(version_value.c_str(), "%d", &mMinorVer) != 1) - { - LL_ERRS("AppInit") << "failed to parse minor version '" << version_value << "' as a number" << LL_ENDL; - } - } - else - { - LL_ERRS("AppInit") - << "OS version regex '" << OS_VERSION_MATCH_EXPRESSION - << "' returned true, but minor version [1] did not match" - << LL_ENDL; - } - - if ( matched[4].matched ) // Build version (optional) - note that [3] includes the '.' - { - version_value.assign(matched[4].first, matched[4].second); - if (sscanf(version_value.c_str(), "%d", &mBuild) != 1) - { - LL_ERRS("AppInit") << "failed to parse build version '" << version_value << "' as a number" << LL_ENDL; - } - } - else - { - LL_INFOS("AppInit") - << "OS build version not provided; using zero" - << LL_ENDL; - } - } - else - { - LL_WARNS("AppInit") << "glibc version '" << glibc_version << "' cannot be parsed to three numbers; using all zeros" << LL_ENDL; - } - -#else - - struct utsname un; - if(uname(&un) != -1) - { - mOSStringSimple.append(un.sysname); - mOSStringSimple.append(" "); - mOSStringSimple.append(un.release); - - mOSString = mOSStringSimple; - mOSString.append(" "); - mOSString.append(un.version); - mOSString.append(" "); - mOSString.append(un.machine); - - // Simplify 'Simple' - std::string ostype = mOSStringSimple.substr(0, mOSStringSimple.find_first_of(" ", 0)); - if (ostype == "Linux") - { - // Only care about major and minor Linux versions, truncate at second '.' - std::string::size_type idx1 = mOSStringSimple.find_first_of(".", 0); - std::string::size_type idx2 = (idx1 != std::string::npos) ? mOSStringSimple.find_first_of(".", idx1+1) : std::string::npos; - std::string simple = mOSStringSimple.substr(0, idx2); - if (simple.length() > 0) - mOSStringSimple = simple; - } - } - else - { - mOSStringSimple.append("Unable to collect OS info"); - mOSString = mOSStringSimple; - } - -#endif - - std::stringstream dotted_version_string; - dotted_version_string << mMajorVer << "." << mMinorVer << "." << mBuild; - mOSVersionString.append(dotted_version_string.str()); - - mOSBitness = is64Bit() ? 64 : 32; - LL_INFOS("LLOSInfo") << "OS bitness: " << mOSBitness << LL_ENDL; -} - -#ifndef LL_WINDOWS -// static -long LLOSInfo::getMaxOpenFiles() -{ - const long OPEN_MAX_GUESS = 256; - -#ifdef OPEN_MAX - static long open_max = OPEN_MAX; -#else - static long open_max = 0; -#endif - - if (0 == open_max) - { - // First time through. - errno = 0; - if ( (open_max = sysconf(_SC_OPEN_MAX)) < 0) - { - if (0 == errno) - { - // Indeterminate. - open_max = OPEN_MAX_GUESS; - } - else - { - LL_ERRS() << "LLOSInfo::getMaxOpenFiles: sysconf error for _SC_OPEN_MAX" << LL_ENDL; - } - } - } - return open_max; -} -#endif - -void LLOSInfo::stream(std::ostream& s) const -{ - s << mOSString; -} - -const std::string& LLOSInfo::getOSString() const -{ - return mOSString; -} - -const std::string& LLOSInfo::getOSStringSimple() const -{ - return mOSStringSimple; -} - -const std::string& LLOSInfo::getOSVersionString() const -{ - return mOSVersionString; -} - -const S32 LLOSInfo::getOSBitness() const -{ - return mOSBitness; -} - -//static -U32 LLOSInfo::getProcessVirtualSizeKB() -{ - U32 virtual_size = 0; -#if LL_LINUX -# define STATUS_SIZE 2048 - LLFILE* status_filep = LLFile::fopen("/proc/self/status", "rb"); - if (status_filep) - { - S32 numRead = 0; - char buff[STATUS_SIZE]; /* Flawfinder: ignore */ - - size_t nbytes = fread(buff, 1, STATUS_SIZE-1, status_filep); - buff[nbytes] = '\0'; - - // All these guys return numbers in KB - char *memp = strstr(buff, "VmSize:"); - if (memp) - { - numRead += sscanf(memp, "%*s %u", &virtual_size); - } - fclose(status_filep); - } -#endif - return virtual_size; -} - -//static -U32 LLOSInfo::getProcessResidentSizeKB() -{ - U32 resident_size = 0; -#if LL_LINUX - LLFILE* status_filep = LLFile::fopen("/proc/self/status", "rb"); - if (status_filep != NULL) - { - S32 numRead = 0; - char buff[STATUS_SIZE]; /* Flawfinder: ignore */ - - size_t nbytes = fread(buff, 1, STATUS_SIZE-1, status_filep); - buff[nbytes] = '\0'; - - // All these guys return numbers in KB - char *memp = strstr(buff, "VmRSS:"); - if (memp) - { - numRead += sscanf(memp, "%*s %u", &resident_size); - } - fclose(status_filep); - } -#endif - return resident_size; -} - -//static -bool LLOSInfo::is64Bit() -{ -#if LL_WINDOWS -#if defined(_WIN64) - return true; -#elif defined(_WIN32) - // 32-bit viewer may be run on both 32-bit and 64-bit Windows, need to elaborate - bool f64 = false; - return IsWow64Process(GetCurrentProcess(), &f64) && f64; -#else - return false; -#endif -#else // ! LL_WINDOWS - // we only build a 64-bit mac viewer and currently we don't build for linux at all - return true; -#endif -} - -LLCPUInfo::LLCPUInfo() -{ - std::ostringstream out; - LLProcessorInfo proc; - // proc.WriteInfoTextFile("procInfo.txt"); - mHasSSE = proc.hasSSE(); - mHasSSE2 = proc.hasSSE2(); - mHasSSE3 = proc.hasSSE3(); - mHasSSE3S = proc.hasSSE3S(); - mHasSSE41 = proc.hasSSE41(); - mHasSSE42 = proc.hasSSE42(); - mHasSSE4a = proc.hasSSE4a(); - mHasAltivec = proc.hasAltivec(); - mCPUMHz = (F64)proc.getCPUFrequency(); - mFamily = proc.getCPUFamilyName(); - mCPUString = "Unknown"; - - out << proc.getCPUBrandName(); - if (200 < mCPUMHz && mCPUMHz < 10000) // *NOTE: cpu speed is often way wrong, do a sanity check - { - out << " (" << mCPUMHz << " MHz)"; - } - mCPUString = out.str(); - LLStringUtil::trim(mCPUString); - - if (mHasSSE) - { - mSSEVersions.append("1"); - } - if (mHasSSE2) - { - mSSEVersions.append("2"); - } - if (mHasSSE3) - { - mSSEVersions.append("3"); - } - if (mHasSSE3S) - { - mSSEVersions.append("3S"); - } - if (mHasSSE41) - { - mSSEVersions.append("4.1"); - } - if (mHasSSE42) - { - mSSEVersions.append("4.2"); - } - if (mHasSSE4a) - { - mSSEVersions.append("4a"); - } -} - -bool LLCPUInfo::hasAltivec() const -{ - return mHasAltivec; -} - -bool LLCPUInfo::hasSSE() const -{ - return mHasSSE; -} - -bool LLCPUInfo::hasSSE2() const -{ - return mHasSSE2; -} - -bool LLCPUInfo::hasSSE3() const -{ - return mHasSSE3; -} - -bool LLCPUInfo::hasSSE3S() const -{ - return mHasSSE3S; -} - -bool LLCPUInfo::hasSSE41() const -{ - return mHasSSE41; -} - -bool LLCPUInfo::hasSSE42() const -{ - return mHasSSE42; -} - -bool LLCPUInfo::hasSSE4a() const -{ - return mHasSSE4a; -} - -F64 LLCPUInfo::getMHz() const -{ - return mCPUMHz; -} - -std::string LLCPUInfo::getCPUString() const -{ - return mCPUString; -} - -const LLSD& LLCPUInfo::getSSEVersions() const -{ - return mSSEVersions; -} - -void LLCPUInfo::stream(std::ostream& s) const -{ - // gather machine information. - s << LLProcessorInfo().getCPUFeatureDescription(); - - // These are interesting as they reflect our internal view of the - // CPU's attributes regardless of platform - s << "->mHasSSE: " << (U32)mHasSSE << std::endl; - s << "->mHasSSE2: " << (U32)mHasSSE2 << std::endl; - s << "->mHasSSE3: " << (U32)mHasSSE3 << std::endl; - s << "->mHasSSE3S: " << (U32)mHasSSE3S << std::endl; - s << "->mHasSSE41: " << (U32)mHasSSE41 << std::endl; - s << "->mHasSSE42: " << (U32)mHasSSE42 << std::endl; - s << "->mHasSSE4a: " << (U32)mHasSSE4a << std::endl; - s << "->mHasAltivec: " << (U32)mHasAltivec << std::endl; - s << "->mCPUMHz: " << mCPUMHz << std::endl; - s << "->mCPUString: " << mCPUString << std::endl; -} - -// Helper class for LLMemoryInfo: accumulate stats in the form we store for -// LLMemoryInfo::getStatsMap(). -class Stats -{ -public: - Stats(): - mStats(LLSD::emptyMap()) - {} - - // Store every integer type as LLSD::Integer. - template <class T> - void add(const LLSD::String& name, const T& value, - typename boost::enable_if<boost::is_integral<T> >::type* = 0) - { - mStats[name] = LLSD::Integer(value); - } - - // Store every floating-point type as LLSD::Real. - template <class T> - void add(const LLSD::String& name, const T& value, - typename boost::enable_if<boost::is_float<T> >::type* = 0) - { - mStats[name] = LLSD::Real(value); - } - - // Hope that LLSD::Date values are sufficiently unambiguous. - void add(const LLSD::String& name, const LLSD::Date& value) - { - mStats[name] = value; - } - - LLSD get() const { return mStats; } - -private: - LLSD mStats; -}; - -LLMemoryInfo::LLMemoryInfo() -{ - refresh(); -} - -#if LL_WINDOWS -static U32Kilobytes LLMemoryAdjustKBResult(U32Kilobytes inKB) -{ - // Moved this here from llfloaterabout.cpp - - //! \bug - // For some reason, the reported amount of memory is always wrong. - // The original adjustment assumes it's always off by one meg, however - // errors of as much as 2520 KB have been observed in the value - // returned from the GetMemoryStatusEx function. Here we keep the - // original adjustment from llfoaterabout.cpp until this can be - // fixed somehow. - inKB += U32Megabytes(1); - - return inKB; -} -#endif - -#if LL_DARWIN -// static -U32Kilobytes LLMemoryInfo::getHardwareMemSize() -{ - // This might work on Linux as well. Someone check... - uint64_t phys = 0; - int mib[2] = { CTL_HW, HW_MEMSIZE }; - - size_t len = sizeof(phys); - sysctl(mib, 2, &phys, &len, NULL, 0); - - return U64Bytes(phys); -} -#endif - -U32Kilobytes LLMemoryInfo::getPhysicalMemoryKB() const -{ -#if LL_WINDOWS - return LLMemoryAdjustKBResult(U32Kilobytes(mStatsMap["Total Physical KB"].asInteger())); - -#elif LL_DARWIN - return getHardwareMemSize(); - -#elif LL_LINUX - U64 phys = 0; - phys = (U64)(getpagesize()) * (U64)(get_phys_pages()); - return U64Bytes(phys); - -#else - return 0; - -#endif -} - -//static -void LLMemoryInfo::getAvailableMemoryKB(U32Kilobytes& avail_physical_mem_kb, U32Kilobytes& avail_virtual_mem_kb) -{ -#if LL_WINDOWS - // Sigh, this shouldn't be a static method, then we wouldn't have to - // reload this data separately from refresh() - LLSD statsMap(loadStatsMap()); - - avail_physical_mem_kb = (U32Kilobytes)statsMap["Avail Physical KB"].asInteger(); - avail_virtual_mem_kb = (U32Kilobytes)statsMap["Avail Virtual KB"].asInteger(); - -#elif LL_DARWIN - // mStatsMap is derived from vm_stat, look for (e.g.) "kb free": - // $ vm_stat - // Mach Virtual Memory Statistics: (page size of 4096 bytes) - // Pages free: 462078. - // Pages active: 142010. - // Pages inactive: 220007. - // Pages wired down: 159552. - // "Translation faults": 220825184. - // Pages copy-on-write: 2104153. - // Pages zero filled: 167034876. - // Pages reactivated: 65153. - // Pageins: 2097212. - // Pageouts: 41759. - // Object cache: 841598 hits of 7629869 lookups (11% hit rate) - avail_physical_mem_kb = (U32Kilobytes)-1 ; - avail_virtual_mem_kb = (U32Kilobytes)-1 ; - -#elif LL_LINUX - // mStatsMap is derived from MEMINFO_FILE: - // $ cat /proc/meminfo - // MemTotal: 4108424 kB - // MemFree: 1244064 kB - // Buffers: 85164 kB - // Cached: 1990264 kB - // SwapCached: 0 kB - // Active: 1176648 kB - // Inactive: 1427532 kB - // Active(anon): 529152 kB - // Inactive(anon): 15924 kB - // Active(file): 647496 kB - // Inactive(file): 1411608 kB - // Unevictable: 16 kB - // Mlocked: 16 kB - // HighTotal: 3266316 kB - // HighFree: 721308 kB - // LowTotal: 842108 kB - // LowFree: 522756 kB - // SwapTotal: 6384632 kB - // SwapFree: 6384632 kB - // Dirty: 28 kB - // Writeback: 0 kB - // AnonPages: 528820 kB - // Mapped: 89472 kB - // Shmem: 16324 kB - // Slab: 159624 kB - // SReclaimable: 145168 kB - // SUnreclaim: 14456 kB - // KernelStack: 2560 kB - // PageTables: 5560 kB - // NFS_Unstable: 0 kB - // Bounce: 0 kB - // WritebackTmp: 0 kB - // CommitLimit: 8438844 kB - // Committed_AS: 1271596 kB - // VmallocTotal: 122880 kB - // VmallocUsed: 65252 kB - // VmallocChunk: 52356 kB - // HardwareCorrupted: 0 kB - // HugePages_Total: 0 - // HugePages_Free: 0 - // HugePages_Rsvd: 0 - // HugePages_Surp: 0 - // Hugepagesize: 2048 kB - // DirectMap4k: 434168 kB - // DirectMap2M: 477184 kB - // (could also run 'free', but easier to read a file than run a program) - avail_physical_mem_kb = (U32Kilobytes)-1 ; - avail_virtual_mem_kb = (U32Kilobytes)-1 ; - -#else - //do not know how to collect available memory info for other systems. - //leave it blank here for now. - - avail_physical_mem_kb = (U32Kilobytes)-1 ; - avail_virtual_mem_kb = (U32Kilobytes)-1 ; -#endif -} - -void LLMemoryInfo::stream(std::ostream& s) const -{ - // We want these memory stats to be easy to grep from the log, along with - // the timestamp. So preface each line with the timestamp and a - // distinctive marker. Without that, we'd have to search the log for the - // introducer line, then read subsequent lines, etc... - std::string pfx(LLError::utcTime() + " <mem> "); - - // Max key length - size_t key_width(0); - for (const auto& [key, value] : inMap(mStatsMap)) - { - size_t len(key.length()); - if (len > key_width) - { - key_width = len; - } - } - - // Now stream stats - for (const auto& [key, value] : inMap(mStatsMap)) - { - s << pfx << std::setw(narrow<size_t>(key_width+1)) << (key + ':') << ' '; - if (value.isInteger()) - s << std::setw(12) << value.asInteger(); - else if (value.isReal()) - s << std::fixed << std::setprecision(1) << value.asReal(); - else if (value.isDate()) - value.asDate().toStream(s); - else - s << value; // just use default LLSD formatting - s << std::endl; - } -} - -LLSD LLMemoryInfo::getStatsMap() const -{ - return mStatsMap; -} - -LLMemoryInfo& LLMemoryInfo::refresh() -{ - LL_PROFILE_ZONE_SCOPED - mStatsMap = loadStatsMap(); - - LL_DEBUGS("LLMemoryInfo") << "Populated mStatsMap:\n"; - LLSDSerialize::toPrettyXML(mStatsMap, LL_CONT); - LL_ENDL; - - return *this; -} - -LLSD LLMemoryInfo::loadStatsMap() -{ - LL_PROFILE_ZONE_SCOPED; - - // This implementation is derived from stream() code (as of 2011-06-29). - Stats stats; - - // associate timestamp for analysis over time - stats.add("timestamp", LLDate::now()); - -#if LL_WINDOWS - MEMORYSTATUSEX state; - state.dwLength = sizeof(state); - GlobalMemoryStatusEx(&state); - - DWORDLONG div = 1024; - - stats.add("Percent Memory use", state.dwMemoryLoad/div); - stats.add("Total Physical KB", state.ullTotalPhys/div); - stats.add("Avail Physical KB", state.ullAvailPhys/div); - stats.add("Total page KB", state.ullTotalPageFile/div); - stats.add("Avail page KB", state.ullAvailPageFile/div); - stats.add("Total Virtual KB", state.ullTotalVirtual/div); - stats.add("Avail Virtual KB", state.ullAvailVirtual/div); - - // SL-12122 - Call to GetPerformanceInfo() was removed here. Took - // on order of 10 ms, causing unacceptable frame time spike every - // second, and results were never used. If this is needed in the - // future, must find a way to avoid frame time impact (e.g. move - // to another thread, call much less often). - - PROCESS_MEMORY_COUNTERS_EX pmem; - pmem.cb = sizeof(pmem); - // GetProcessMemoryInfo() is documented to accept either - // PROCESS_MEMORY_COUNTERS* or PROCESS_MEMORY_COUNTERS_EX*, presumably - // using the redundant size info to distinguish. But its prototype - // specifically accepts PROCESS_MEMORY_COUNTERS*, and since this is a - // classic-C API, PROCESS_MEMORY_COUNTERS_EX isn't a subclass. Cast the - // pointer. - GetProcessMemoryInfo(GetCurrentProcess(), PPROCESS_MEMORY_COUNTERS(&pmem), sizeof(pmem)); - - stats.add("Page Fault Count", pmem.PageFaultCount); - stats.add("PeakWorkingSetSize KB", pmem.PeakWorkingSetSize/div); - stats.add("WorkingSetSize KB", pmem.WorkingSetSize/div); - stats.add("QutaPeakPagedPoolUsage KB", pmem.QuotaPeakPagedPoolUsage/div); - stats.add("QuotaPagedPoolUsage KB", pmem.QuotaPagedPoolUsage/div); - stats.add("QuotaPeakNonPagedPoolUsage KB", pmem.QuotaPeakNonPagedPoolUsage/div); - stats.add("QuotaNonPagedPoolUsage KB", pmem.QuotaNonPagedPoolUsage/div); - stats.add("PagefileUsage KB", pmem.PagefileUsage/div); - stats.add("PeakPagefileUsage KB", pmem.PeakPagefileUsage/div); - stats.add("PrivateUsage KB", pmem.PrivateUsage/div); - -#elif LL_DARWIN - - const vm_size_t pagekb(vm_page_size / 1024); - - // - // Collect the vm_stat's - // - - { - vm_statistics64_data_t vmstat; - mach_msg_type_number_t vmstatCount = HOST_VM_INFO64_COUNT; - - if (host_statistics64(mach_host_self(), HOST_VM_INFO64, (host_info64_t) &vmstat, &vmstatCount) != KERN_SUCCESS) - { - LL_WARNS("LLMemoryInfo") << "Unable to collect memory information" << LL_ENDL; - } - else - { - stats.add("Pages free KB", pagekb * vmstat.free_count); - stats.add("Pages active KB", pagekb * vmstat.active_count); - stats.add("Pages inactive KB", pagekb * vmstat.inactive_count); - stats.add("Pages wired KB", pagekb * vmstat.wire_count); - - stats.add("Pages zero fill", vmstat.zero_fill_count); - stats.add("Page reactivations", vmstat.reactivations); - stats.add("Page-ins", vmstat.pageins); - stats.add("Page-outs", vmstat.pageouts); - - stats.add("Faults", vmstat.faults); - stats.add("Faults copy-on-write", vmstat.cow_faults); - - stats.add("Cache lookups", vmstat.lookups); - stats.add("Cache hits", vmstat.hits); - - stats.add("Page purgeable count", vmstat.purgeable_count); - stats.add("Page purges", vmstat.purges); - - stats.add("Page speculative reads", vmstat.speculative_count); - } - } - - // - // Collect the misc task info - // - - { - task_events_info_data_t taskinfo; - unsigned taskinfoSize = sizeof(taskinfo); - - if (task_info(mach_task_self(), TASK_EVENTS_INFO, (task_info_t) &taskinfo, &taskinfoSize) != KERN_SUCCESS) - { - LL_WARNS("LLMemoryInfo") << "Unable to collect task information" << LL_ENDL; - } - else - { - stats.add("Task page-ins", taskinfo.pageins); - stats.add("Task copy-on-write faults", taskinfo.cow_faults); - stats.add("Task messages sent", taskinfo.messages_sent); - stats.add("Task messages received", taskinfo.messages_received); - stats.add("Task mach system call count", taskinfo.syscalls_mach); - stats.add("Task unix system call count", taskinfo.syscalls_unix); - stats.add("Task context switch count", taskinfo.csw); - } - } - - // - // Collect the basic task info - // - - { - mach_task_basic_info_data_t taskinfo; - mach_msg_type_number_t task_count = MACH_TASK_BASIC_INFO_COUNT; - if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t) &taskinfo, &task_count) != KERN_SUCCESS) - { - LL_WARNS("LLMemoryInfo") << "Unable to collect task information" << LL_ENDL; - } - else - { - stats.add("Basic virtual memory KB", taskinfo.virtual_size / 1024); - stats.add("Basic resident memory KB", taskinfo.resident_size / 1024); - stats.add("Basic max resident memory KB", taskinfo.resident_size_max / 1024); - stats.add("Basic new thread policy", taskinfo.policy); - stats.add("Basic suspend count", taskinfo.suspend_count); - } - } - -#elif LL_LINUX - std::ifstream meminfo(MEMINFO_FILE); - if (meminfo.is_open()) - { - // MemTotal: 4108424 kB - // MemFree: 1244064 kB - // Buffers: 85164 kB - // Cached: 1990264 kB - // SwapCached: 0 kB - // Active: 1176648 kB - // Inactive: 1427532 kB - // ... - // VmallocTotal: 122880 kB - // VmallocUsed: 65252 kB - // VmallocChunk: 52356 kB - // HardwareCorrupted: 0 kB - // HugePages_Total: 0 - // HugePages_Free: 0 - // HugePages_Rsvd: 0 - // HugePages_Surp: 0 - // Hugepagesize: 2048 kB - // DirectMap4k: 434168 kB - // DirectMap2M: 477184 kB - - // Intentionally don't pass the boost::no_except flag. This - // boost::regex object is constructed with a string literal, so it - // should be valid every time. If it becomes invalid, we WANT an - // exception, hopefully even before the dev checks in. - boost::regex stat_rx("(.+): +([0-9]+)( kB)?"); - boost::smatch matched; - - std::string line; - while (std::getline(meminfo, line)) - { - LL_DEBUGS("LLMemoryInfo") << line << LL_ENDL; - if (ll_regex_match(line, matched, stat_rx)) - { - // e.g. "MemTotal: 4108424 kB" - LLSD::String key(matched[1].first, matched[1].second); - LLSD::String value_str(matched[2].first, matched[2].second); - LLSD::Integer value(0); - try - { - value = boost::lexical_cast<LLSD::Integer>(value_str); - } - catch (const boost::bad_lexical_cast&) - { - LL_WARNS("LLMemoryInfo") << "couldn't parse '" << value_str - << "' in " << MEMINFO_FILE << " line: " - << line << LL_ENDL; - continue; - } - // Store this statistic. - stats.add(key, value); - } - else - { - LL_WARNS("LLMemoryInfo") << "unrecognized " << MEMINFO_FILE << " line: " - << line << LL_ENDL; - } - } - } - else - { - LL_WARNS("LLMemoryInfo") << "Unable to collect memory information" << LL_ENDL; - } - -#else - LL_WARNS("LLMemoryInfo") << "Unknown system; unable to collect memory information" << LL_ENDL; - -#endif - - return stats.get(); -} - -std::ostream& operator<<(std::ostream& s, const LLOSInfo& info) -{ - info.stream(s); - return s; -} - -std::ostream& operator<<(std::ostream& s, const LLCPUInfo& info) -{ - info.stream(s); - return s; -} - -std::ostream& operator<<(std::ostream& s, const LLMemoryInfo& info) -{ - info.stream(s); - return s; -} - -class FrameWatcher -{ -public: - FrameWatcher(): - // Hooking onto the "mainloop" event pump gets us one call per frame. - mConnection(LLEventPumps::instance() - .obtain("mainloop") - .listen("FrameWatcher", boost::bind(&FrameWatcher::tick, this, _1))), - // Initializing mSampleStart to an invalid timestamp alerts us to skip - // trying to compute framerate on the first call. - mSampleStart(-1), - // Initializing mSampleEnd to 0 ensures that we treat the first call - // as the completion of a sample window. - mSampleEnd(0), - mFrames(0), - // Both MEM_INFO_WINDOW and MEM_INFO_THROTTLE are in seconds. We need - // the number of integer MEM_INFO_THROTTLE sample slots that will fit - // in MEM_INFO_WINDOW. Round up. - mSamples(int((MEM_INFO_WINDOW / MEM_INFO_THROTTLE) + 0.7)), - // Initializing to F32_MAX means that the first real frame will become - // the slowest ever, which sounds like a good idea. - mSlowest(F32_MAX) - {} - - bool tick(const LLSD&) - { - F32 timestamp(mTimer.getElapsedTimeF32()); - - // Count this frame in the interval just completed. - ++mFrames; - - // Have we finished a sample window yet? - if (timestamp < mSampleEnd) - { - // no, just keep waiting - return false; - } - - // Set up for next sample window. Capture values for previous frame in - // local variables and reset data members. - U32 frames(mFrames); - F32 sampleStart(mSampleStart); - // No frames yet in next window - mFrames = 0; - // which starts right now - mSampleStart = timestamp; - // and ends MEM_INFO_THROTTLE seconds in the future - mSampleEnd = mSampleStart + MEM_INFO_THROTTLE; - - // On the very first call, that's all we can do, no framerate - // computation is possible. - if (sampleStart < 0) - { - return false; - } - - // How long did this actually take? As framerate slows, the duration - // of the frame we just finished could push us WELL beyond our desired - // sample window size. - F32 elapsed(timestamp - sampleStart); - F32 framerate(frames/elapsed); - - // Remember previous slowest framerate because we're just about to - // update it. - F32 slowest(mSlowest); - // Remember previous number of samples. - boost::circular_buffer<F32>::size_type prevSize(mSamples.size()); - - // Capture new framerate in our samples buffer. Once the buffer is - // full (after MEM_INFO_WINDOW seconds), this will displace the oldest - // sample. ("So they all rolled over, and one fell out...") - mSamples.push_back(framerate); - - // Calculate the new minimum framerate. I know of no way to update a - // rolling minimum without ever rescanning the buffer. But since there - // are only a few tens of items in this buffer, rescanning it is - // probably cheaper (and certainly easier to reason about) than - // attempting to optimize away some of the scans. - mSlowest = framerate; // pick an arbitrary entry to start - for (boost::circular_buffer<F32>::const_iterator si(mSamples.begin()), send(mSamples.end()); - si != send; ++si) - { - if (*si < mSlowest) - { - mSlowest = *si; - } - } - - // We're especially interested in memory as framerate drops. Only log - // when framerate drops below the slowest framerate we remember. - // (Should always be true for the end of the very first sample - // window.) - if (framerate >= slowest) - { - return false; - } - // Congratulations, we've hit a new low. :-P - - LL_INFOS("FrameWatcher") << ' '; - if (! prevSize) - { - LL_CONT << "initial framerate "; - } - else - { - LL_CONT << "slowest framerate for last " << int(prevSize * MEM_INFO_THROTTLE) - << " seconds "; - } - - auto precision = LL_CONT.precision(); - - LL_CONT << std::fixed << std::setprecision(1) << framerate << '\n' - << LLMemoryInfo(); - - LL_CONT.precision(precision); - LL_CONT << LL_ENDL; - return false; - } - -private: - // Storing the connection in an LLTempBoundListener ensures it will be - // disconnected when we're destroyed. - LLTempBoundListener mConnection; - // Track elapsed time - LLTimer mTimer; - // Some of what you see here is in fact redundant with functionality you - // can get from LLTimer. Unfortunately the LLTimer API is missing the - // feature we need: has at least the stated interval elapsed, and if so, - // exactly how long has passed? So we have to do it by hand, sigh. - // Time at start, end of sample window - F32 mSampleStart, mSampleEnd; - // Frames this sample window - U32 mFrames; - // Sliding window of framerate samples - boost::circular_buffer<F32> mSamples; - // Slowest framerate in mSamples - F32 mSlowest; -}; - -// Need an instance of FrameWatcher before it does any good -static FrameWatcher sFrameWatcher; - -bool gunzip_file(const std::string& srcfile, const std::string& dstfile) -{ - std::string tmpfile; - const S32 UNCOMPRESS_BUFFER_SIZE = 32768; - bool retval = false; - gzFile src = NULL; - U8 buffer[UNCOMPRESS_BUFFER_SIZE]; - LLFILE *dst = NULL; - S32 bytes = 0; - tmpfile = dstfile + ".t"; -#ifdef LL_WINDOWS - llutf16string utf16filename = utf8str_to_utf16str(srcfile); - src = gzopen_w(utf16filename.c_str(), "rb"); -#else - src = gzopen(srcfile.c_str(), "rb"); -#endif - if (! src) goto err; - dst = LLFile::fopen(tmpfile, "wb"); /* Flawfinder: ignore */ - if (! dst) goto err; - do - { - bytes = gzread(src, buffer, UNCOMPRESS_BUFFER_SIZE); - size_t nwrit = fwrite(buffer, sizeof(U8), bytes, dst); - if (nwrit < (size_t) bytes) - { - LL_WARNS() << "Short write on " << tmpfile << ": Wrote " << nwrit << " of " << bytes << " bytes." << LL_ENDL; - goto err; - } - } while(gzeof(src) == 0); - fclose(dst); - dst = NULL; - if (LLFile::rename(tmpfile, dstfile) == -1) goto err; /* Flawfinder: ignore */ - retval = true; -err: - if (src != NULL) gzclose(src); - if (dst != NULL) fclose(dst); - return retval; -} - -bool gzip_file(const std::string& srcfile, const std::string& dstfile) -{ - const S32 COMPRESS_BUFFER_SIZE = 32768; - std::string tmpfile; - bool retval = false; - U8 buffer[COMPRESS_BUFFER_SIZE]; - gzFile dst = NULL; - LLFILE *src = NULL; - S32 bytes = 0; - tmpfile = dstfile + ".t"; - -#ifdef LL_WINDOWS - llutf16string utf16filename = utf8str_to_utf16str(tmpfile); - dst = gzopen_w(utf16filename.c_str(), "wb"); -#else - dst = gzopen(tmpfile.c_str(), "wb"); -#endif - - if (! dst) goto err; - src = LLFile::fopen(srcfile, "rb"); /* Flawfinder: ignore */ - if (! src) goto err; - - while ((bytes = (S32)fread(buffer, sizeof(U8), COMPRESS_BUFFER_SIZE, src)) > 0) - { - if (gzwrite(dst, buffer, bytes) <= 0) - { - LL_WARNS() << "gzwrite failed: " << gzerror(dst, NULL) << LL_ENDL; - goto err; - } - } - - if (ferror(src)) - { - LL_WARNS() << "Error reading " << srcfile << LL_ENDL; - goto err; - } - - gzclose(dst); - dst = NULL; -#if LL_WINDOWS - // Rename in windows needs the dstfile to not exist. - LLFile::remove(dstfile); -#endif - if (LLFile::rename(tmpfile, dstfile) == -1) goto err; /* Flawfinder: ignore */ - retval = true; - err: - if (src != NULL) fclose(src); - if (dst != NULL) gzclose(dst); - return retval; -} +/**
+ * @file llsys.cpp
+ * @brief Implementation of the basic system query functions.
+ *
+ * $LicenseInfo:firstyear=2002&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#if LL_WINDOWS
+#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally
+#endif
+
+#include "linden_common.h"
+
+#include "llsys.h"
+
+#include <iostream>
+#ifdef LL_USESYSTEMLIBS
+# include <zlib.h>
+#else
+# include "zlib-ng/zlib.h"
+#endif
+
+#include "llprocessor.h"
+#include "llerrorcontrol.h"
+#include "llevents.h"
+#include "llformat.h"
+#include "llregex.h"
+#include "lltimer.h"
+#include "llsdserialize.h"
+#include "llsdutil.h"
+#include <boost/bind.hpp>
+#include <boost/circular_buffer.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/range.hpp>
+#include <boost/utility/enable_if.hpp>
+#include <boost/type_traits/is_integral.hpp>
+#include <boost/type_traits/is_float.hpp>
+#include "llfasttimer.h"
+
+using namespace llsd;
+
+#if LL_WINDOWS
+# include "llwin32headerslean.h"
+# include <psapi.h> // GetPerformanceInfo() et al.
+# include <VersionHelpers.h>
+#elif LL_DARWIN
+# include "llsys_objc.h"
+# include <errno.h>
+# include <sys/sysctl.h>
+# include <sys/utsname.h>
+# include <stdint.h>
+# include <CoreServices/CoreServices.h>
+# include <stdexcept>
+# include <mach/host_info.h>
+# include <mach/mach_host.h>
+# include <mach/task.h>
+# include <mach/task_info.h>
+#elif LL_LINUX
+# include <errno.h>
+# include <sys/utsname.h>
+# include <unistd.h>
+# include <sys/sysinfo.h>
+# include <stdexcept>
+const char MEMINFO_FILE[] = "/proc/meminfo";
+# include <gnu/libc-version.h>
+#endif
+
+LLCPUInfo gSysCPU;
+
+// Don't log memory info any more often than this. It also serves as our
+// framerate sample size.
+static const F32 MEM_INFO_THROTTLE = 20;
+// Sliding window of samples. We intentionally limit the length of time we
+// remember "the slowest" framerate because framerate is very slow at login.
+// If we only triggered FrameWatcher logging when the session framerate
+// dropped below the login framerate, we'd have very little additional data.
+static const F32 MEM_INFO_WINDOW = 10*60;
+
+LLOSInfo::LLOSInfo() :
+ mMajorVer(0), mMinorVer(0), mBuild(0), mOSVersionString("")
+{
+
+#if LL_WINDOWS
+
+ if (IsWindows10OrGreater())
+ {
+ mMajorVer = 10;
+ mMinorVer = 0;
+ mOSStringSimple = "Microsoft Windows 10 ";
+ }
+ else if (IsWindows8Point1OrGreater())
+ {
+ mMajorVer = 6;
+ mMinorVer = 3;
+ if (IsWindowsServer())
+ {
+ mOSStringSimple = "Windows Server 2012 R2 ";
+ }
+ else
+ {
+ mOSStringSimple = "Microsoft Windows 8.1 ";
+ }
+ }
+ else if (IsWindows8OrGreater())
+ {
+ mMajorVer = 6;
+ mMinorVer = 2;
+ if (IsWindowsServer())
+ {
+ mOSStringSimple = "Windows Server 2012 ";
+ }
+ else
+ {
+ mOSStringSimple = "Microsoft Windows 8 ";
+ }
+ }
+ else if (IsWindows7SP1OrGreater())
+ {
+ mMajorVer = 6;
+ mMinorVer = 1;
+ if (IsWindowsServer())
+ {
+ mOSStringSimple = "Windows Server 2008 R2 SP1 ";
+ }
+ else
+ {
+ mOSStringSimple = "Microsoft Windows 7 SP1 ";
+ }
+ }
+ else if (IsWindows7OrGreater())
+ {
+ mMajorVer = 6;
+ mMinorVer = 1;
+ if (IsWindowsServer())
+ {
+ mOSStringSimple = "Windows Server 2008 R2 ";
+ }
+ else
+ {
+ mOSStringSimple = "Microsoft Windows 7 ";
+ }
+ }
+ else if (IsWindowsVistaSP2OrGreater())
+ {
+ mMajorVer = 6;
+ mMinorVer = 0;
+ if (IsWindowsServer())
+ {
+ mOSStringSimple = "Windows Server 2008 SP2 ";
+ }
+ else
+ {
+ mOSStringSimple = "Microsoft Windows Vista SP2 ";
+ }
+ }
+ else
+ {
+ mOSStringSimple = "Unsupported Windows version ";
+ }
+
+ ///get native system info if available..
+ typedef void (WINAPI *PGNSI)(LPSYSTEM_INFO); ///function pointer for loading GetNativeSystemInfo
+ SYSTEM_INFO si; //System Info object file contains architecture info
+ PGNSI pGNSI; //pointer object
+ ZeroMemory(&si, sizeof(SYSTEM_INFO)); //zero out the memory in information
+ pGNSI = (PGNSI)GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetNativeSystemInfo"); //load kernel32 get function
+ if (NULL != pGNSI) //check if it has failed
+ pGNSI(&si); //success
+ else
+ GetSystemInfo(&si); //if it fails get regular system info
+ //(Warning: If GetSystemInfo it may result in incorrect information in a WOW64 machine, if the kernel fails to load)
+
+ // Try calling GetVersionEx using the OSVERSIONINFOEX structure.
+ OSVERSIONINFOEX osvi;
+ ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
+ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+ if (GetVersionEx((OSVERSIONINFO *)&osvi))
+ {
+ mBuild = osvi.dwBuildNumber & 0xffff;
+ }
+ else
+ {
+ // If OSVERSIONINFOEX doesn't work, try OSVERSIONINFO.
+ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ if (GetVersionEx((OSVERSIONINFO *)&osvi))
+ {
+ mBuild = osvi.dwBuildNumber & 0xffff;
+ }
+ }
+
+ S32 ubr = 0; // Windows 10 Update Build Revision, can be retrieved from a registry
+ if (mMajorVer == 10)
+ {
+ DWORD cbData(sizeof(DWORD));
+ DWORD data(0);
+ HKEY key;
+ LSTATUS ret_code = RegOpenKeyExW(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"), 0, KEY_READ, &key);
+ if (ERROR_SUCCESS == ret_code)
+ {
+ ret_code = RegQueryValueExW(key, L"UBR", 0, NULL, reinterpret_cast<LPBYTE>(&data), &cbData);
+ if (ERROR_SUCCESS == ret_code)
+ {
+ ubr = data;
+ }
+ }
+
+ if (mBuild >= 22000)
+ {
+ // At release Windows 11 version was 10.0.22000.194
+ // Windows 10 version was 10.0.19043.1266
+ // There is no warranty that Win10 build won't increase,
+ // so until better solution is found or Microsoft updates
+ // SDK with IsWindows11OrGreater(), indicate "10/11"
+ //
+ // Current alternatives:
+ // Query WMI's Win32_OperatingSystem for OS string. Slow
+ // and likely to return 'compatibility' string.
+ // Check presence of dlls/libs or may be their version.
+ mOSStringSimple = "Microsoft Windows 10/11 ";
+ }
+ }
+
+ //msdn microsoft finds 32 bit and 64 bit flavors this way..
+ //http://msdn.microsoft.com/en-us/library/ms724429(VS.85).aspx (example code that contains quite a few more flavors
+ //of windows than this code does (in case it is needed for the future)
+ if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) //check for 64 bit
+ {
+ mOSStringSimple += "64-bit ";
+ }
+ else if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL)
+ {
+ mOSStringSimple += "32-bit ";
+ }
+
+ mOSString = mOSStringSimple;
+ if (mBuild > 0)
+ {
+ mOSString += llformat("(Build %d", mBuild);
+ if (ubr > 0)
+ {
+ mOSString += llformat(".%d", ubr);
+ }
+ mOSString += ")";
+ }
+
+ LLStringUtil::trim(mOSStringSimple);
+ LLStringUtil::trim(mOSString);
+
+#elif LL_DARWIN
+
+ // Initialize mOSStringSimple to something like:
+ // "macOS 10.13.1"
+ {
+ const char * DARWIN_PRODUCT_NAME = "macOS";
+
+ int64_t major_version, minor_version, bugfix_version = 0;
+
+ if (LLGetDarwinOSInfo(major_version, minor_version, bugfix_version))
+ {
+ mMajorVer = major_version;
+ mMinorVer = minor_version;
+ mBuild = bugfix_version;
+
+ std::stringstream os_version_string;
+ os_version_string << DARWIN_PRODUCT_NAME << " " << mMajorVer << "." << mMinorVer << "." << mBuild;
+
+ // Put it in the OS string we are compiling
+ mOSStringSimple.append(os_version_string.str());
+ }
+ else
+ {
+ mOSStringSimple.append("Unable to collect OS info");
+ }
+ }
+
+ // Initialize mOSString to something like:
+ // "macOS 10.13.1 Darwin Kernel Version 10.7.0: Sat Jan 29 15:17:16 PST 2011; root:xnu-1504.9.37~1/RELEASE_I386 i386"
+ struct utsname un;
+ if(uname(&un) != -1)
+ {
+ mOSString = mOSStringSimple;
+ mOSString.append(" ");
+ mOSString.append(un.sysname);
+ mOSString.append(" ");
+ mOSString.append(un.release);
+ mOSString.append(" ");
+ mOSString.append(un.version);
+ mOSString.append(" ");
+ mOSString.append(un.machine);
+ }
+ else
+ {
+ mOSString = mOSStringSimple;
+ }
+
+#elif LL_LINUX
+
+ struct utsname un;
+ if(uname(&un) != -1)
+ {
+ mOSStringSimple.append(un.sysname);
+ mOSStringSimple.append(" ");
+ mOSStringSimple.append(un.release);
+
+ mOSString = mOSStringSimple;
+ mOSString.append(" ");
+ mOSString.append(un.version);
+ mOSString.append(" ");
+ mOSString.append(un.machine);
+
+ // Simplify 'Simple'
+ std::string ostype = mOSStringSimple.substr(0, mOSStringSimple.find_first_of(" ", 0));
+ if (ostype == "Linux")
+ {
+ // Only care about major and minor Linux versions, truncate at second '.'
+ std::string::size_type idx1 = mOSStringSimple.find_first_of(".", 0);
+ std::string::size_type idx2 = (idx1 != std::string::npos) ? mOSStringSimple.find_first_of(".", idx1+1) : std::string::npos;
+ std::string simple = mOSStringSimple.substr(0, idx2);
+ if (simple.length() > 0)
+ mOSStringSimple = simple;
+ }
+ }
+ else
+ {
+ mOSStringSimple.append("Unable to collect OS info");
+ mOSString = mOSStringSimple;
+ }
+
+ const char OS_VERSION_MATCH_EXPRESSION[] = "([0-9]+)\\.([0-9]+)(\\.([0-9]+))?";
+ boost::regex os_version_parse(OS_VERSION_MATCH_EXPRESSION);
+ boost::smatch matched;
+
+ std::string glibc_version(gnu_get_libc_version());
+ if ( ll_regex_match(glibc_version, matched, os_version_parse) )
+ {
+ LL_INFOS("AppInit") << "Using glibc version '" << glibc_version << "' as OS version" << LL_ENDL;
+
+ std::string version_value;
+
+ if ( matched[1].matched ) // Major version
+ {
+ version_value.assign(matched[1].first, matched[1].second);
+ if (sscanf(version_value.c_str(), "%d", &mMajorVer) != 1)
+ {
+ LL_WARNS("AppInit") << "failed to parse major version '" << version_value << "' as a number" << LL_ENDL;
+ }
+ }
+ else
+ {
+ LL_ERRS("AppInit")
+ << "OS version regex '" << OS_VERSION_MATCH_EXPRESSION
+ << "' returned true, but major version [1] did not match"
+ << LL_ENDL;
+ }
+
+ if ( matched[2].matched ) // Minor version
+ {
+ version_value.assign(matched[2].first, matched[2].second);
+ if (sscanf(version_value.c_str(), "%d", &mMinorVer) != 1)
+ {
+ LL_ERRS("AppInit") << "failed to parse minor version '" << version_value << "' as a number" << LL_ENDL;
+ }
+ }
+ else
+ {
+ LL_ERRS("AppInit")
+ << "OS version regex '" << OS_VERSION_MATCH_EXPRESSION
+ << "' returned true, but minor version [1] did not match"
+ << LL_ENDL;
+ }
+
+ if ( matched[4].matched ) // Build version (optional) - note that [3] includes the '.'
+ {
+ version_value.assign(matched[4].first, matched[4].second);
+ if (sscanf(version_value.c_str(), "%d", &mBuild) != 1)
+ {
+ LL_ERRS("AppInit") << "failed to parse build version '" << version_value << "' as a number" << LL_ENDL;
+ }
+ }
+ else
+ {
+ LL_INFOS("AppInit")
+ << "OS build version not provided; using zero"
+ << LL_ENDL;
+ }
+ }
+ else
+ {
+ LL_WARNS("AppInit") << "glibc version '" << glibc_version << "' cannot be parsed to three numbers; using all zeros" << LL_ENDL;
+ }
+
+#else
+
+ struct utsname un;
+ if(uname(&un) != -1)
+ {
+ mOSStringSimple.append(un.sysname);
+ mOSStringSimple.append(" ");
+ mOSStringSimple.append(un.release);
+
+ mOSString = mOSStringSimple;
+ mOSString.append(" ");
+ mOSString.append(un.version);
+ mOSString.append(" ");
+ mOSString.append(un.machine);
+
+ // Simplify 'Simple'
+ std::string ostype = mOSStringSimple.substr(0, mOSStringSimple.find_first_of(" ", 0));
+ if (ostype == "Linux")
+ {
+ // Only care about major and minor Linux versions, truncate at second '.'
+ std::string::size_type idx1 = mOSStringSimple.find_first_of(".", 0);
+ std::string::size_type idx2 = (idx1 != std::string::npos) ? mOSStringSimple.find_first_of(".", idx1+1) : std::string::npos;
+ std::string simple = mOSStringSimple.substr(0, idx2);
+ if (simple.length() > 0)
+ mOSStringSimple = simple;
+ }
+ }
+ else
+ {
+ mOSStringSimple.append("Unable to collect OS info");
+ mOSString = mOSStringSimple;
+ }
+
+#endif
+
+ std::stringstream dotted_version_string;
+ dotted_version_string << mMajorVer << "." << mMinorVer << "." << mBuild;
+ mOSVersionString.append(dotted_version_string.str());
+
+ mOSBitness = is64Bit() ? 64 : 32;
+ LL_INFOS("LLOSInfo") << "OS bitness: " << mOSBitness << LL_ENDL;
+}
+
+#ifndef LL_WINDOWS
+// static
+long LLOSInfo::getMaxOpenFiles()
+{
+ const long OPEN_MAX_GUESS = 256;
+
+#ifdef OPEN_MAX
+ static long open_max = OPEN_MAX;
+#else
+ static long open_max = 0;
+#endif
+
+ if (0 == open_max)
+ {
+ // First time through.
+ errno = 0;
+ if ( (open_max = sysconf(_SC_OPEN_MAX)) < 0)
+ {
+ if (0 == errno)
+ {
+ // Indeterminate.
+ open_max = OPEN_MAX_GUESS;
+ }
+ else
+ {
+ LL_ERRS() << "LLOSInfo::getMaxOpenFiles: sysconf error for _SC_OPEN_MAX" << LL_ENDL;
+ }
+ }
+ }
+ return open_max;
+}
+#endif
+
+void LLOSInfo::stream(std::ostream& s) const
+{
+ s << mOSString;
+}
+
+const std::string& LLOSInfo::getOSString() const
+{
+ return mOSString;
+}
+
+const std::string& LLOSInfo::getOSStringSimple() const
+{
+ return mOSStringSimple;
+}
+
+const std::string& LLOSInfo::getOSVersionString() const
+{
+ return mOSVersionString;
+}
+
+const S32 LLOSInfo::getOSBitness() const
+{
+ return mOSBitness;
+}
+
+//static
+U32 LLOSInfo::getProcessVirtualSizeKB()
+{
+ U32 virtual_size = 0;
+#if LL_LINUX
+# define STATUS_SIZE 2048
+ LLFILE* status_filep = LLFile::fopen("/proc/self/status", "rb");
+ if (status_filep)
+ {
+ S32 numRead = 0;
+ char buff[STATUS_SIZE]; /* Flawfinder: ignore */
+
+ size_t nbytes = fread(buff, 1, STATUS_SIZE-1, status_filep);
+ buff[nbytes] = '\0';
+
+ // All these guys return numbers in KB
+ char *memp = strstr(buff, "VmSize:");
+ if (memp)
+ {
+ numRead += sscanf(memp, "%*s %u", &virtual_size);
+ }
+ fclose(status_filep);
+ }
+#endif
+ return virtual_size;
+}
+
+//static
+U32 LLOSInfo::getProcessResidentSizeKB()
+{
+ U32 resident_size = 0;
+#if LL_LINUX
+ LLFILE* status_filep = LLFile::fopen("/proc/self/status", "rb");
+ if (status_filep != NULL)
+ {
+ S32 numRead = 0;
+ char buff[STATUS_SIZE]; /* Flawfinder: ignore */
+
+ size_t nbytes = fread(buff, 1, STATUS_SIZE-1, status_filep);
+ buff[nbytes] = '\0';
+
+ // All these guys return numbers in KB
+ char *memp = strstr(buff, "VmRSS:");
+ if (memp)
+ {
+ numRead += sscanf(memp, "%*s %u", &resident_size);
+ }
+ fclose(status_filep);
+ }
+#endif
+ return resident_size;
+}
+
+//static
+bool LLOSInfo::is64Bit()
+{
+#if LL_WINDOWS
+#if defined(_WIN64)
+ return true;
+#elif defined(_WIN32)
+ // 32-bit viewer may be run on both 32-bit and 64-bit Windows, need to elaborate
+ bool f64 = false;
+ return IsWow64Process(GetCurrentProcess(), &f64) && f64;
+#else
+ return false;
+#endif
+#else // ! LL_WINDOWS
+ // we only build a 64-bit mac viewer and currently we don't build for linux at all
+ return true;
+#endif
+}
+
+LLCPUInfo::LLCPUInfo()
+{
+ std::ostringstream out;
+ LLProcessorInfo proc;
+ // proc.WriteInfoTextFile("procInfo.txt");
+ mHasSSE = proc.hasSSE();
+ mHasSSE2 = proc.hasSSE2();
+ mHasSSE3 = proc.hasSSE3();
+ mHasSSE3S = proc.hasSSE3S();
+ mHasSSE41 = proc.hasSSE41();
+ mHasSSE42 = proc.hasSSE42();
+ mHasSSE4a = proc.hasSSE4a();
+ mHasAltivec = proc.hasAltivec();
+ mCPUMHz = (F64)proc.getCPUFrequency();
+ mFamily = proc.getCPUFamilyName();
+ mCPUString = "Unknown";
+
+ out << proc.getCPUBrandName();
+ if (200 < mCPUMHz && mCPUMHz < 10000) // *NOTE: cpu speed is often way wrong, do a sanity check
+ {
+ out << " (" << mCPUMHz << " MHz)";
+ }
+ mCPUString = out.str();
+ LLStringUtil::trim(mCPUString);
+
+ if (mHasSSE)
+ {
+ mSSEVersions.append("1");
+ }
+ if (mHasSSE2)
+ {
+ mSSEVersions.append("2");
+ }
+ if (mHasSSE3)
+ {
+ mSSEVersions.append("3");
+ }
+ if (mHasSSE3S)
+ {
+ mSSEVersions.append("3S");
+ }
+ if (mHasSSE41)
+ {
+ mSSEVersions.append("4.1");
+ }
+ if (mHasSSE42)
+ {
+ mSSEVersions.append("4.2");
+ }
+ if (mHasSSE4a)
+ {
+ mSSEVersions.append("4a");
+ }
+}
+
+bool LLCPUInfo::hasAltivec() const
+{
+ return mHasAltivec;
+}
+
+bool LLCPUInfo::hasSSE() const
+{
+ return mHasSSE;
+}
+
+bool LLCPUInfo::hasSSE2() const
+{
+ return mHasSSE2;
+}
+
+bool LLCPUInfo::hasSSE3() const
+{
+ return mHasSSE3;
+}
+
+bool LLCPUInfo::hasSSE3S() const
+{
+ return mHasSSE3S;
+}
+
+bool LLCPUInfo::hasSSE41() const
+{
+ return mHasSSE41;
+}
+
+bool LLCPUInfo::hasSSE42() const
+{
+ return mHasSSE42;
+}
+
+bool LLCPUInfo::hasSSE4a() const
+{
+ return mHasSSE4a;
+}
+
+F64 LLCPUInfo::getMHz() const
+{
+ return mCPUMHz;
+}
+
+std::string LLCPUInfo::getCPUString() const
+{
+ return mCPUString;
+}
+
+const LLSD& LLCPUInfo::getSSEVersions() const
+{
+ return mSSEVersions;
+}
+
+void LLCPUInfo::stream(std::ostream& s) const
+{
+ // gather machine information.
+ s << LLProcessorInfo().getCPUFeatureDescription();
+
+ // These are interesting as they reflect our internal view of the
+ // CPU's attributes regardless of platform
+ s << "->mHasSSE: " << (U32)mHasSSE << std::endl;
+ s << "->mHasSSE2: " << (U32)mHasSSE2 << std::endl;
+ s << "->mHasSSE3: " << (U32)mHasSSE3 << std::endl;
+ s << "->mHasSSE3S: " << (U32)mHasSSE3S << std::endl;
+ s << "->mHasSSE41: " << (U32)mHasSSE41 << std::endl;
+ s << "->mHasSSE42: " << (U32)mHasSSE42 << std::endl;
+ s << "->mHasSSE4a: " << (U32)mHasSSE4a << std::endl;
+ s << "->mHasAltivec: " << (U32)mHasAltivec << std::endl;
+ s << "->mCPUMHz: " << mCPUMHz << std::endl;
+ s << "->mCPUString: " << mCPUString << std::endl;
+}
+
+// Helper class for LLMemoryInfo: accumulate stats in the form we store for
+// LLMemoryInfo::getStatsMap().
+class Stats
+{
+public:
+ Stats():
+ mStats(LLSD::emptyMap())
+ {}
+
+ // Store every integer type as LLSD::Integer.
+ template <class T>
+ void add(const LLSD::String& name, const T& value,
+ typename boost::enable_if<boost::is_integral<T> >::type* = 0)
+ {
+ mStats[name] = LLSD::Integer(value);
+ }
+
+ // Store every floating-point type as LLSD::Real.
+ template <class T>
+ void add(const LLSD::String& name, const T& value,
+ typename boost::enable_if<boost::is_float<T> >::type* = 0)
+ {
+ mStats[name] = LLSD::Real(value);
+ }
+
+ // Hope that LLSD::Date values are sufficiently unambiguous.
+ void add(const LLSD::String& name, const LLSD::Date& value)
+ {
+ mStats[name] = value;
+ }
+
+ LLSD get() const { return mStats; }
+
+private:
+ LLSD mStats;
+};
+
+LLMemoryInfo::LLMemoryInfo()
+{
+ refresh();
+}
+
+#if LL_WINDOWS
+static U32Kilobytes LLMemoryAdjustKBResult(U32Kilobytes inKB)
+{
+ // Moved this here from llfloaterabout.cpp
+
+ //! \bug
+ // For some reason, the reported amount of memory is always wrong.
+ // The original adjustment assumes it's always off by one meg, however
+ // errors of as much as 2520 KB have been observed in the value
+ // returned from the GetMemoryStatusEx function. Here we keep the
+ // original adjustment from llfoaterabout.cpp until this can be
+ // fixed somehow.
+ inKB += U32Megabytes(1);
+
+ return inKB;
+}
+#endif
+
+#if LL_DARWIN
+// static
+U32Kilobytes LLMemoryInfo::getHardwareMemSize()
+{
+ // This might work on Linux as well. Someone check...
+ uint64_t phys = 0;
+ int mib[2] = { CTL_HW, HW_MEMSIZE };
+
+ size_t len = sizeof(phys);
+ sysctl(mib, 2, &phys, &len, NULL, 0);
+
+ return U64Bytes(phys);
+}
+#endif
+
+U32Kilobytes LLMemoryInfo::getPhysicalMemoryKB() const
+{
+#if LL_WINDOWS
+ return LLMemoryAdjustKBResult(U32Kilobytes(mStatsMap["Total Physical KB"].asInteger()));
+
+#elif LL_DARWIN
+ return getHardwareMemSize();
+
+#elif LL_LINUX
+ U64 phys = 0;
+ phys = (U64)(getpagesize()) * (U64)(get_phys_pages());
+ return U64Bytes(phys);
+
+#else
+ return 0;
+
+#endif
+}
+
+//static
+void LLMemoryInfo::getAvailableMemoryKB(U32Kilobytes& avail_physical_mem_kb, U32Kilobytes& avail_virtual_mem_kb)
+{
+#if LL_WINDOWS
+ // Sigh, this shouldn't be a static method, then we wouldn't have to
+ // reload this data separately from refresh()
+ LLSD statsMap(loadStatsMap());
+
+ avail_physical_mem_kb = (U32Kilobytes)statsMap["Avail Physical KB"].asInteger();
+ avail_virtual_mem_kb = (U32Kilobytes)statsMap["Avail Virtual KB"].asInteger();
+
+#elif LL_DARWIN
+ // mStatsMap is derived from vm_stat, look for (e.g.) "kb free":
+ // $ vm_stat
+ // Mach Virtual Memory Statistics: (page size of 4096 bytes)
+ // Pages free: 462078.
+ // Pages active: 142010.
+ // Pages inactive: 220007.
+ // Pages wired down: 159552.
+ // "Translation faults": 220825184.
+ // Pages copy-on-write: 2104153.
+ // Pages zero filled: 167034876.
+ // Pages reactivated: 65153.
+ // Pageins: 2097212.
+ // Pageouts: 41759.
+ // Object cache: 841598 hits of 7629869 lookups (11% hit rate)
+ avail_physical_mem_kb = (U32Kilobytes)-1 ;
+ avail_virtual_mem_kb = (U32Kilobytes)-1 ;
+
+#elif LL_LINUX
+ // mStatsMap is derived from MEMINFO_FILE:
+ // $ cat /proc/meminfo
+ // MemTotal: 4108424 kB
+ // MemFree: 1244064 kB
+ // Buffers: 85164 kB
+ // Cached: 1990264 kB
+ // SwapCached: 0 kB
+ // Active: 1176648 kB
+ // Inactive: 1427532 kB
+ // Active(anon): 529152 kB
+ // Inactive(anon): 15924 kB
+ // Active(file): 647496 kB
+ // Inactive(file): 1411608 kB
+ // Unevictable: 16 kB
+ // Mlocked: 16 kB
+ // HighTotal: 3266316 kB
+ // HighFree: 721308 kB
+ // LowTotal: 842108 kB
+ // LowFree: 522756 kB
+ // SwapTotal: 6384632 kB
+ // SwapFree: 6384632 kB
+ // Dirty: 28 kB
+ // Writeback: 0 kB
+ // AnonPages: 528820 kB
+ // Mapped: 89472 kB
+ // Shmem: 16324 kB
+ // Slab: 159624 kB
+ // SReclaimable: 145168 kB
+ // SUnreclaim: 14456 kB
+ // KernelStack: 2560 kB
+ // PageTables: 5560 kB
+ // NFS_Unstable: 0 kB
+ // Bounce: 0 kB
+ // WritebackTmp: 0 kB
+ // CommitLimit: 8438844 kB
+ // Committed_AS: 1271596 kB
+ // VmallocTotal: 122880 kB
+ // VmallocUsed: 65252 kB
+ // VmallocChunk: 52356 kB
+ // HardwareCorrupted: 0 kB
+ // HugePages_Total: 0
+ // HugePages_Free: 0
+ // HugePages_Rsvd: 0
+ // HugePages_Surp: 0
+ // Hugepagesize: 2048 kB
+ // DirectMap4k: 434168 kB
+ // DirectMap2M: 477184 kB
+ // (could also run 'free', but easier to read a file than run a program)
+ avail_physical_mem_kb = (U32Kilobytes)-1 ;
+ avail_virtual_mem_kb = (U32Kilobytes)-1 ;
+
+#else
+ //do not know how to collect available memory info for other systems.
+ //leave it blank here for now.
+
+ avail_physical_mem_kb = (U32Kilobytes)-1 ;
+ avail_virtual_mem_kb = (U32Kilobytes)-1 ;
+#endif
+}
+
+void LLMemoryInfo::stream(std::ostream& s) const
+{
+ // We want these memory stats to be easy to grep from the log, along with
+ // the timestamp. So preface each line with the timestamp and a
+ // distinctive marker. Without that, we'd have to search the log for the
+ // introducer line, then read subsequent lines, etc...
+ std::string pfx(LLError::utcTime() + " <mem> ");
+
+ // Max key length
+ size_t key_width(0);
+ for (const auto& [key, value] : inMap(mStatsMap))
+ {
+ size_t len(key.length());
+ if (len > key_width)
+ {
+ key_width = len;
+ }
+ }
+
+ // Now stream stats
+ for (const auto& [key, value] : inMap(mStatsMap))
+ {
+ s << pfx << std::setw(narrow<size_t>(key_width+1)) << (key + ':') << ' ';
+ if (value.isInteger())
+ s << std::setw(12) << value.asInteger();
+ else if (value.isReal())
+ s << std::fixed << std::setprecision(1) << value.asReal();
+ else if (value.isDate())
+ value.asDate().toStream(s);
+ else
+ s << value; // just use default LLSD formatting
+ s << std::endl;
+ }
+}
+
+LLSD LLMemoryInfo::getStatsMap() const
+{
+ return mStatsMap;
+}
+
+LLMemoryInfo& LLMemoryInfo::refresh()
+{
+ LL_PROFILE_ZONE_SCOPED
+ mStatsMap = loadStatsMap();
+
+ LL_DEBUGS("LLMemoryInfo") << "Populated mStatsMap:\n";
+ LLSDSerialize::toPrettyXML(mStatsMap, LL_CONT);
+ LL_ENDL;
+
+ return *this;
+}
+
+LLSD LLMemoryInfo::loadStatsMap()
+{
+ LL_PROFILE_ZONE_SCOPED;
+
+ // This implementation is derived from stream() code (as of 2011-06-29).
+ Stats stats;
+
+ // associate timestamp for analysis over time
+ stats.add("timestamp", LLDate::now());
+
+#if LL_WINDOWS
+ MEMORYSTATUSEX state;
+ state.dwLength = sizeof(state);
+ GlobalMemoryStatusEx(&state);
+
+ DWORDLONG div = 1024;
+
+ stats.add("Percent Memory use", state.dwMemoryLoad/div);
+ stats.add("Total Physical KB", state.ullTotalPhys/div);
+ stats.add("Avail Physical KB", state.ullAvailPhys/div);
+ stats.add("Total page KB", state.ullTotalPageFile/div);
+ stats.add("Avail page KB", state.ullAvailPageFile/div);
+ stats.add("Total Virtual KB", state.ullTotalVirtual/div);
+ stats.add("Avail Virtual KB", state.ullAvailVirtual/div);
+
+ // SL-12122 - Call to GetPerformanceInfo() was removed here. Took
+ // on order of 10 ms, causing unacceptable frame time spike every
+ // second, and results were never used. If this is needed in the
+ // future, must find a way to avoid frame time impact (e.g. move
+ // to another thread, call much less often).
+
+ PROCESS_MEMORY_COUNTERS_EX pmem;
+ pmem.cb = sizeof(pmem);
+ // GetProcessMemoryInfo() is documented to accept either
+ // PROCESS_MEMORY_COUNTERS* or PROCESS_MEMORY_COUNTERS_EX*, presumably
+ // using the redundant size info to distinguish. But its prototype
+ // specifically accepts PROCESS_MEMORY_COUNTERS*, and since this is a
+ // classic-C API, PROCESS_MEMORY_COUNTERS_EX isn't a subclass. Cast the
+ // pointer.
+ GetProcessMemoryInfo(GetCurrentProcess(), PPROCESS_MEMORY_COUNTERS(&pmem), sizeof(pmem));
+
+ stats.add("Page Fault Count", pmem.PageFaultCount);
+ stats.add("PeakWorkingSetSize KB", pmem.PeakWorkingSetSize/div);
+ stats.add("WorkingSetSize KB", pmem.WorkingSetSize/div);
+ stats.add("QutaPeakPagedPoolUsage KB", pmem.QuotaPeakPagedPoolUsage/div);
+ stats.add("QuotaPagedPoolUsage KB", pmem.QuotaPagedPoolUsage/div);
+ stats.add("QuotaPeakNonPagedPoolUsage KB", pmem.QuotaPeakNonPagedPoolUsage/div);
+ stats.add("QuotaNonPagedPoolUsage KB", pmem.QuotaNonPagedPoolUsage/div);
+ stats.add("PagefileUsage KB", pmem.PagefileUsage/div);
+ stats.add("PeakPagefileUsage KB", pmem.PeakPagefileUsage/div);
+ stats.add("PrivateUsage KB", pmem.PrivateUsage/div);
+
+#elif LL_DARWIN
+
+ const vm_size_t pagekb(vm_page_size / 1024);
+
+ //
+ // Collect the vm_stat's
+ //
+
+ {
+ vm_statistics64_data_t vmstat;
+ mach_msg_type_number_t vmstatCount = HOST_VM_INFO64_COUNT;
+
+ if (host_statistics64(mach_host_self(), HOST_VM_INFO64, (host_info64_t) &vmstat, &vmstatCount) != KERN_SUCCESS)
+ {
+ LL_WARNS("LLMemoryInfo") << "Unable to collect memory information" << LL_ENDL;
+ }
+ else
+ {
+ stats.add("Pages free KB", pagekb * vmstat.free_count);
+ stats.add("Pages active KB", pagekb * vmstat.active_count);
+ stats.add("Pages inactive KB", pagekb * vmstat.inactive_count);
+ stats.add("Pages wired KB", pagekb * vmstat.wire_count);
+
+ stats.add("Pages zero fill", vmstat.zero_fill_count);
+ stats.add("Page reactivations", vmstat.reactivations);
+ stats.add("Page-ins", vmstat.pageins);
+ stats.add("Page-outs", vmstat.pageouts);
+
+ stats.add("Faults", vmstat.faults);
+ stats.add("Faults copy-on-write", vmstat.cow_faults);
+
+ stats.add("Cache lookups", vmstat.lookups);
+ stats.add("Cache hits", vmstat.hits);
+
+ stats.add("Page purgeable count", vmstat.purgeable_count);
+ stats.add("Page purges", vmstat.purges);
+
+ stats.add("Page speculative reads", vmstat.speculative_count);
+ }
+ }
+
+ //
+ // Collect the misc task info
+ //
+
+ {
+ task_events_info_data_t taskinfo;
+ unsigned taskinfoSize = sizeof(taskinfo);
+
+ if (task_info(mach_task_self(), TASK_EVENTS_INFO, (task_info_t) &taskinfo, &taskinfoSize) != KERN_SUCCESS)
+ {
+ LL_WARNS("LLMemoryInfo") << "Unable to collect task information" << LL_ENDL;
+ }
+ else
+ {
+ stats.add("Task page-ins", taskinfo.pageins);
+ stats.add("Task copy-on-write faults", taskinfo.cow_faults);
+ stats.add("Task messages sent", taskinfo.messages_sent);
+ stats.add("Task messages received", taskinfo.messages_received);
+ stats.add("Task mach system call count", taskinfo.syscalls_mach);
+ stats.add("Task unix system call count", taskinfo.syscalls_unix);
+ stats.add("Task context switch count", taskinfo.csw);
+ }
+ }
+
+ //
+ // Collect the basic task info
+ //
+
+ {
+ mach_task_basic_info_data_t taskinfo;
+ mach_msg_type_number_t task_count = MACH_TASK_BASIC_INFO_COUNT;
+ if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t) &taskinfo, &task_count) != KERN_SUCCESS)
+ {
+ LL_WARNS("LLMemoryInfo") << "Unable to collect task information" << LL_ENDL;
+ }
+ else
+ {
+ stats.add("Basic virtual memory KB", taskinfo.virtual_size / 1024);
+ stats.add("Basic resident memory KB", taskinfo.resident_size / 1024);
+ stats.add("Basic max resident memory KB", taskinfo.resident_size_max / 1024);
+ stats.add("Basic new thread policy", taskinfo.policy);
+ stats.add("Basic suspend count", taskinfo.suspend_count);
+ }
+ }
+
+#elif LL_LINUX
+ std::ifstream meminfo(MEMINFO_FILE);
+ if (meminfo.is_open())
+ {
+ // MemTotal: 4108424 kB
+ // MemFree: 1244064 kB
+ // Buffers: 85164 kB
+ // Cached: 1990264 kB
+ // SwapCached: 0 kB
+ // Active: 1176648 kB
+ // Inactive: 1427532 kB
+ // ...
+ // VmallocTotal: 122880 kB
+ // VmallocUsed: 65252 kB
+ // VmallocChunk: 52356 kB
+ // HardwareCorrupted: 0 kB
+ // HugePages_Total: 0
+ // HugePages_Free: 0
+ // HugePages_Rsvd: 0
+ // HugePages_Surp: 0
+ // Hugepagesize: 2048 kB
+ // DirectMap4k: 434168 kB
+ // DirectMap2M: 477184 kB
+
+ // Intentionally don't pass the boost::no_except flag. This
+ // boost::regex object is constructed with a string literal, so it
+ // should be valid every time. If it becomes invalid, we WANT an
+ // exception, hopefully even before the dev checks in.
+ boost::regex stat_rx("(.+): +([0-9]+)( kB)?");
+ boost::smatch matched;
+
+ std::string line;
+ while (std::getline(meminfo, line))
+ {
+ LL_DEBUGS("LLMemoryInfo") << line << LL_ENDL;
+ if (ll_regex_match(line, matched, stat_rx))
+ {
+ // e.g. "MemTotal: 4108424 kB"
+ LLSD::String key(matched[1].first, matched[1].second);
+ LLSD::String value_str(matched[2].first, matched[2].second);
+ LLSD::Integer value(0);
+ try
+ {
+ value = boost::lexical_cast<LLSD::Integer>(value_str);
+ }
+ catch (const boost::bad_lexical_cast&)
+ {
+ LL_WARNS("LLMemoryInfo") << "couldn't parse '" << value_str
+ << "' in " << MEMINFO_FILE << " line: "
+ << line << LL_ENDL;
+ continue;
+ }
+ // Store this statistic.
+ stats.add(key, value);
+ }
+ else
+ {
+ LL_WARNS("LLMemoryInfo") << "unrecognized " << MEMINFO_FILE << " line: "
+ << line << LL_ENDL;
+ }
+ }
+ }
+ else
+ {
+ LL_WARNS("LLMemoryInfo") << "Unable to collect memory information" << LL_ENDL;
+ }
+
+#else
+ LL_WARNS("LLMemoryInfo") << "Unknown system; unable to collect memory information" << LL_ENDL;
+
+#endif
+
+ return stats.get();
+}
+
+std::ostream& operator<<(std::ostream& s, const LLOSInfo& info)
+{
+ info.stream(s);
+ return s;
+}
+
+std::ostream& operator<<(std::ostream& s, const LLCPUInfo& info)
+{
+ info.stream(s);
+ return s;
+}
+
+std::ostream& operator<<(std::ostream& s, const LLMemoryInfo& info)
+{
+ info.stream(s);
+ return s;
+}
+
+class FrameWatcher
+{
+public:
+ FrameWatcher():
+ // Hooking onto the "mainloop" event pump gets us one call per frame.
+ mConnection(LLEventPumps::instance()
+ .obtain("mainloop")
+ .listen("FrameWatcher", boost::bind(&FrameWatcher::tick, this, _1))),
+ // Initializing mSampleStart to an invalid timestamp alerts us to skip
+ // trying to compute framerate on the first call.
+ mSampleStart(-1),
+ // Initializing mSampleEnd to 0 ensures that we treat the first call
+ // as the completion of a sample window.
+ mSampleEnd(0),
+ mFrames(0),
+ // Both MEM_INFO_WINDOW and MEM_INFO_THROTTLE are in seconds. We need
+ // the number of integer MEM_INFO_THROTTLE sample slots that will fit
+ // in MEM_INFO_WINDOW. Round up.
+ mSamples(int((MEM_INFO_WINDOW / MEM_INFO_THROTTLE) + 0.7)),
+ // Initializing to F32_MAX means that the first real frame will become
+ // the slowest ever, which sounds like a good idea.
+ mSlowest(F32_MAX)
+ {}
+
+ bool tick(const LLSD&)
+ {
+ F32 timestamp(mTimer.getElapsedTimeF32());
+
+ // Count this frame in the interval just completed.
+ ++mFrames;
+
+ // Have we finished a sample window yet?
+ if (timestamp < mSampleEnd)
+ {
+ // no, just keep waiting
+ return false;
+ }
+
+ // Set up for next sample window. Capture values for previous frame in
+ // local variables and reset data members.
+ U32 frames(mFrames);
+ F32 sampleStart(mSampleStart);
+ // No frames yet in next window
+ mFrames = 0;
+ // which starts right now
+ mSampleStart = timestamp;
+ // and ends MEM_INFO_THROTTLE seconds in the future
+ mSampleEnd = mSampleStart + MEM_INFO_THROTTLE;
+
+ // On the very first call, that's all we can do, no framerate
+ // computation is possible.
+ if (sampleStart < 0)
+ {
+ return false;
+ }
+
+ // How long did this actually take? As framerate slows, the duration
+ // of the frame we just finished could push us WELL beyond our desired
+ // sample window size.
+ F32 elapsed(timestamp - sampleStart);
+ F32 framerate(frames/elapsed);
+
+ // Remember previous slowest framerate because we're just about to
+ // update it.
+ F32 slowest(mSlowest);
+ // Remember previous number of samples.
+ boost::circular_buffer<F32>::size_type prevSize(mSamples.size());
+
+ // Capture new framerate in our samples buffer. Once the buffer is
+ // full (after MEM_INFO_WINDOW seconds), this will displace the oldest
+ // sample. ("So they all rolled over, and one fell out...")
+ mSamples.push_back(framerate);
+
+ // Calculate the new minimum framerate. I know of no way to update a
+ // rolling minimum without ever rescanning the buffer. But since there
+ // are only a few tens of items in this buffer, rescanning it is
+ // probably cheaper (and certainly easier to reason about) than
+ // attempting to optimize away some of the scans.
+ mSlowest = framerate; // pick an arbitrary entry to start
+ for (boost::circular_buffer<F32>::const_iterator si(mSamples.begin()), send(mSamples.end());
+ si != send; ++si)
+ {
+ if (*si < mSlowest)
+ {
+ mSlowest = *si;
+ }
+ }
+
+ // We're especially interested in memory as framerate drops. Only log
+ // when framerate drops below the slowest framerate we remember.
+ // (Should always be true for the end of the very first sample
+ // window.)
+ if (framerate >= slowest)
+ {
+ return false;
+ }
+ // Congratulations, we've hit a new low. :-P
+
+ LL_INFOS("FrameWatcher") << ' ';
+ if (! prevSize)
+ {
+ LL_CONT << "initial framerate ";
+ }
+ else
+ {
+ LL_CONT << "slowest framerate for last " << int(prevSize * MEM_INFO_THROTTLE)
+ << " seconds ";
+ }
+
+ auto precision = LL_CONT.precision();
+
+ LL_CONT << std::fixed << std::setprecision(1) << framerate << '\n'
+ << LLMemoryInfo();
+
+ LL_CONT.precision(precision);
+ LL_CONT << LL_ENDL;
+ return false;
+ }
+
+private:
+ // Storing the connection in an LLTempBoundListener ensures it will be
+ // disconnected when we're destroyed.
+ LLTempBoundListener mConnection;
+ // Track elapsed time
+ LLTimer mTimer;
+ // Some of what you see here is in fact redundant with functionality you
+ // can get from LLTimer. Unfortunately the LLTimer API is missing the
+ // feature we need: has at least the stated interval elapsed, and if so,
+ // exactly how long has passed? So we have to do it by hand, sigh.
+ // Time at start, end of sample window
+ F32 mSampleStart, mSampleEnd;
+ // Frames this sample window
+ U32 mFrames;
+ // Sliding window of framerate samples
+ boost::circular_buffer<F32> mSamples;
+ // Slowest framerate in mSamples
+ F32 mSlowest;
+};
+
+// Need an instance of FrameWatcher before it does any good
+static FrameWatcher sFrameWatcher;
+
+bool gunzip_file(const std::string& srcfile, const std::string& dstfile)
+{
+ std::string tmpfile;
+ const S32 UNCOMPRESS_BUFFER_SIZE = 32768;
+ bool retval = false;
+ gzFile src = NULL;
+ U8 buffer[UNCOMPRESS_BUFFER_SIZE];
+ LLFILE *dst = NULL;
+ S32 bytes = 0;
+ tmpfile = dstfile + ".t";
+#ifdef LL_WINDOWS
+ llutf16string utf16filename = utf8str_to_utf16str(srcfile);
+ src = gzopen_w(utf16filename.c_str(), "rb");
+#else
+ src = gzopen(srcfile.c_str(), "rb");
+#endif
+ if (! src) goto err;
+ dst = LLFile::fopen(tmpfile, "wb"); /* Flawfinder: ignore */
+ if (! dst) goto err;
+ do
+ {
+ bytes = gzread(src, buffer, UNCOMPRESS_BUFFER_SIZE);
+ size_t nwrit = fwrite(buffer, sizeof(U8), bytes, dst);
+ if (nwrit < (size_t) bytes)
+ {
+ LL_WARNS() << "Short write on " << tmpfile << ": Wrote " << nwrit << " of " << bytes << " bytes." << LL_ENDL;
+ goto err;
+ }
+ } while(gzeof(src) == 0);
+ fclose(dst);
+ dst = NULL;
+#if LL_WINDOWS
+ // Rename in windows needs the dstfile to not exist.
+ LLFile::remove(dstfile, ENOENT);
+#endif
+ if (LLFile::rename(tmpfile, dstfile) == -1) goto err; /* Flawfinder: ignore */
+ retval = true;
+err:
+ if (src != NULL) gzclose(src);
+ if (dst != NULL) fclose(dst);
+ return retval;
+}
+
+bool gzip_file(const std::string& srcfile, const std::string& dstfile)
+{
+ const S32 COMPRESS_BUFFER_SIZE = 32768;
+ std::string tmpfile;
+ bool retval = false;
+ U8 buffer[COMPRESS_BUFFER_SIZE];
+ gzFile dst = NULL;
+ LLFILE *src = NULL;
+ S32 bytes = 0;
+ tmpfile = dstfile + ".t";
+
+#ifdef LL_WINDOWS
+ llutf16string utf16filename = utf8str_to_utf16str(tmpfile);
+ dst = gzopen_w(utf16filename.c_str(), "wb");
+#else
+ dst = gzopen(tmpfile.c_str(), "wb");
+#endif
+
+ if (! dst) goto err;
+ src = LLFile::fopen(srcfile, "rb"); /* Flawfinder: ignore */
+ if (! src) goto err;
+
+ while ((bytes = (S32)fread(buffer, sizeof(U8), COMPRESS_BUFFER_SIZE, src)) > 0)
+ {
+ if (gzwrite(dst, buffer, bytes) <= 0)
+ {
+ LL_WARNS() << "gzwrite failed: " << gzerror(dst, NULL) << LL_ENDL;
+ goto err;
+ }
+ }
+
+ if (ferror(src))
+ {
+ LL_WARNS() << "Error reading " << srcfile << LL_ENDL;
+ goto err;
+ }
+
+ gzclose(dst);
+ dst = NULL;
+#if LL_WINDOWS
+ // Rename in windows needs the dstfile to not exist.
+ LLFile::remove(dstfile);
+#endif
+ if (LLFile::rename(tmpfile, dstfile) == -1) goto err; /* Flawfinder: ignore */
+ retval = true;
+ err:
+ if (src != NULL) fclose(src);
+ if (dst != NULL) gzclose(dst);
+ return retval;
+}
diff --git a/indra/llcommon/llsys.h b/indra/llcommon/llsys.h index 42f0c2a7f3..3ef1e2b528 100644 --- a/indra/llcommon/llsys.h +++ b/indra/llcommon/llsys.h @@ -1,174 +1,174 @@ -/** - * @file llsys.h - * @brief System information debugging classes. - * - * $LicenseInfo:firstyear=2001&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_SYS_H -#define LL_SYS_H - -// -// The LLOSInfo, LLCPUInfo, and LLMemoryInfo classes are essentially -// the same, but query different machine subsystems. Here's how you -// use an LLCPUInfo object: -// -// LLCPUInfo info; -// LL_INFOS() << info << LL_ENDL; -// - -#include "llsd.h" -#include "llsingleton.h" -#include <iosfwd> -#include <string> - -class LL_COMMON_API LLOSInfo : public LLSingleton<LLOSInfo> -{ - LLSINGLETON(LLOSInfo); -public: - void stream(std::ostream& s) const; - - const std::string& getOSString() const; - const std::string& getOSStringSimple() const; - - const std::string& getOSVersionString() const; - - const S32 getOSBitness() const; - - S32 mMajorVer; - S32 mMinorVer; - S32 mBuild; - -#ifndef LL_WINDOWS - static long getMaxOpenFiles(); -#endif - static bool is64Bit(); - - static U32 getProcessVirtualSizeKB(); - static U32 getProcessResidentSizeKB(); -private: - std::string mOSString; - std::string mOSStringSimple; - std::string mOSVersionString; - S32 mOSBitness; -}; - - -class LL_COMMON_API LLCPUInfo -{ -public: - LLCPUInfo(); - void stream(std::ostream& s) const; - - std::string getCPUString() const; - const LLSD& getSSEVersions() const; - - bool hasAltivec() const; - bool hasSSE() const; - bool hasSSE2() const; - bool hasSSE3() const; - bool hasSSE3S() const; - bool hasSSE41() const; - bool hasSSE42() const; - bool hasSSE4a() const; - F64 getMHz() const; - - // Family is "AMD Duron" or "Intel Pentium Pro" - const std::string& getFamily() const { return mFamily; } - -private: - bool mHasSSE; - bool mHasSSE2; - bool mHasSSE3; - bool mHasSSE3S; - bool mHasSSE41; - bool mHasSSE42; - bool mHasSSE4a; - bool mHasAltivec; - F64 mCPUMHz; - std::string mFamily; - std::string mCPUString; - LLSD mSSEVersions; -}; - -//============================================================================= -// -// CLASS LLMemoryInfo - -class LL_COMMON_API LLMemoryInfo - -/*! @brief Class to query the memory subsystem - - @details - Here's how you use an LLMemoryInfo: - - LLMemoryInfo info; -<br> LL_INFOS() << info << LL_ENDL; -*/ -{ -public: - LLMemoryInfo(); ///< Default constructor - void stream(std::ostream& s) const; ///< output text info to s - - U32Kilobytes getPhysicalMemoryKB() const; -#if LL_DARWIN - static U32Kilobytes getHardwareMemSize(); // Because some Mac linkers won't let us reference extern gSysMemory from a different lib. -#endif - - //get the available memory infomation in KiloBytes. - static void getAvailableMemoryKB(U32Kilobytes& avail_physical_mem_kb, U32Kilobytes& avail_virtual_mem_kb); - - // Retrieve a map of memory statistics. The keys of the map are platform- - // dependent. The values are in kilobytes to try to avoid integer overflow. - LLSD getStatsMap() const; - - // Re-fetch memory data (as reported by stream() and getStatsMap()) from the - // system. Normally this is fetched at construction time. Return (*this) - // to permit usage of the form: - // @code - // LLMemoryInfo info; - // ... - // info.refresh().getStatsMap(); - // @endcode - LLMemoryInfo& refresh(); - -private: - // set mStatsMap - static LLSD loadStatsMap(); - - // Memory stats for getStatsMap(). - LLSD mStatsMap; -}; - - -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 LL_COMMON_API gunzip_file(const std::string& srcfile, const std::string& dstfile); -// gzip srcfile into dstfile. Returns false on error. -bool LL_COMMON_API gzip_file(const std::string& srcfile, const std::string& dstfile); - -extern LL_COMMON_API LLCPUInfo gSysCPU; - -#endif // LL_LLSYS_H +/**
+ * @file llsys.h
+ * @brief System information debugging classes.
+ *
+ * $LicenseInfo:firstyear=2001&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_SYS_H
+#define LL_SYS_H
+
+//
+// The LLOSInfo, LLCPUInfo, and LLMemoryInfo classes are essentially
+// the same, but query different machine subsystems. Here's how you
+// use an LLCPUInfo object:
+//
+// LLCPUInfo info;
+// LL_INFOS() << info << LL_ENDL;
+//
+
+#include "llsd.h"
+#include "llsingleton.h"
+#include <iosfwd>
+#include <string>
+
+class LL_COMMON_API LLOSInfo : public LLSingleton<LLOSInfo>
+{
+ LLSINGLETON(LLOSInfo);
+public:
+ void stream(std::ostream& s) const;
+
+ const std::string& getOSString() const;
+ const std::string& getOSStringSimple() const;
+
+ const std::string& getOSVersionString() const;
+
+ const S32 getOSBitness() const;
+
+ S32 mMajorVer;
+ S32 mMinorVer;
+ S32 mBuild;
+
+#ifndef LL_WINDOWS
+ static long getMaxOpenFiles();
+#endif
+ static bool is64Bit();
+
+ static U32 getProcessVirtualSizeKB();
+ static U32 getProcessResidentSizeKB();
+private:
+ std::string mOSString;
+ std::string mOSStringSimple;
+ std::string mOSVersionString;
+ S32 mOSBitness;
+};
+
+
+class LL_COMMON_API LLCPUInfo
+{
+public:
+ LLCPUInfo();
+ void stream(std::ostream& s) const;
+
+ std::string getCPUString() const;
+ const LLSD& getSSEVersions() const;
+
+ bool hasAltivec() const;
+ bool hasSSE() const;
+ bool hasSSE2() const;
+ bool hasSSE3() const;
+ bool hasSSE3S() const;
+ bool hasSSE41() const;
+ bool hasSSE42() const;
+ bool hasSSE4a() const;
+ F64 getMHz() const;
+
+ // Family is "AMD Duron" or "Intel Pentium Pro"
+ const std::string& getFamily() const { return mFamily; }
+
+private:
+ bool mHasSSE;
+ bool mHasSSE2;
+ bool mHasSSE3;
+ bool mHasSSE3S;
+ bool mHasSSE41;
+ bool mHasSSE42;
+ bool mHasSSE4a;
+ bool mHasAltivec;
+ F64 mCPUMHz;
+ std::string mFamily;
+ std::string mCPUString;
+ LLSD mSSEVersions;
+};
+
+//=============================================================================
+//
+// CLASS LLMemoryInfo
+
+class LL_COMMON_API LLMemoryInfo
+
+/*! @brief Class to query the memory subsystem
+
+ @details
+ Here's how you use an LLMemoryInfo:
+
+ LLMemoryInfo info;
+<br> LL_INFOS() << info << LL_ENDL;
+*/
+{
+public:
+ LLMemoryInfo(); ///< Default constructor
+ void stream(std::ostream& s) const; ///< output text info to s
+
+ U32Kilobytes getPhysicalMemoryKB() const;
+#if LL_DARWIN
+ static U32Kilobytes getHardwareMemSize(); // Because some Mac linkers won't let us reference extern gSysMemory from a different lib.
+#endif
+
+ //get the available memory infomation in KiloBytes.
+ static void getAvailableMemoryKB(U32Kilobytes& avail_physical_mem_kb, U32Kilobytes& avail_virtual_mem_kb);
+
+ // Retrieve a map of memory statistics. The keys of the map are platform-
+ // dependent. The values are in kilobytes to try to avoid integer overflow.
+ LLSD getStatsMap() const;
+
+ // Re-fetch memory data (as reported by stream() and getStatsMap()) from the
+ // system. Normally this is fetched at construction time. Return (*this)
+ // to permit usage of the form:
+ // @code
+ // LLMemoryInfo info;
+ // ...
+ // info.refresh().getStatsMap();
+ // @endcode
+ LLMemoryInfo& refresh();
+
+private:
+ // set mStatsMap
+ static LLSD loadStatsMap();
+
+ // Memory stats for getStatsMap().
+ LLSD mStatsMap;
+};
+
+
+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 LL_COMMON_API gunzip_file(const std::string& srcfile, const std::string& dstfile);
+// gzip srcfile into dstfile. Returns false on error.
+bool LL_COMMON_API gzip_file(const std::string& srcfile, const std::string& dstfile);
+
+extern LL_COMMON_API LLCPUInfo gSysCPU;
+
+#endif // LL_LLSYS_H
diff --git a/indra/llcommon/lltempredirect.cpp b/indra/llcommon/lltempredirect.cpp index ec194c1d29..ee7923e8ca 100644 --- a/indra/llcommon/lltempredirect.cpp +++ b/indra/llcommon/lltempredirect.cpp @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2019-10-31 * @brief Implementation for lltempredirect. - * + * * $LicenseInfo:firstyear=2019&license=viewerlgpl$ * Copyright (c) 2019, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/lltempredirect.h b/indra/llcommon/lltempredirect.h index 33e05dc06b..0ab3575d30 100644 --- a/indra/llcommon/lltempredirect.h +++ b/indra/llcommon/lltempredirect.h @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2019-10-31 * @brief RAII low-level file-descriptor redirection - * + * * $LicenseInfo:firstyear=2019&license=viewerlgpl$ * Copyright (c) 2019, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp index 1db97ad3e3..deb1df640c 100644 --- a/indra/llcommon/llthread.cpp +++ b/indra/llcommon/llthread.cpp @@ -1,472 +1,472 @@ -/** - * @file llthread.cpp - * - * $LicenseInfo:firstyear=2004&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010-2013, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" -#include "llapr.h" - -#include "apr_portable.h" - -#include "llthread.h" -#include "llmutex.h" - -#include "lltimer.h" -#include "lltrace.h" -#include "lltracethreadrecorder.h" -#include "llexception.h" - -#if LL_LINUX -#include <sched.h> -#endif - - -#ifdef LL_WINDOWS - -const DWORD MS_VC_EXCEPTION=0x406D1388; - -#pragma pack(push,8) -typedef struct tagTHREADNAME_INFO -{ - DWORD dwType; // Must be 0x1000. - LPCSTR szName; // Pointer to name (in user addr space). - DWORD dwThreadID; // Thread ID (-1=caller thread). - DWORD dwFlags; // Reserved for future use, must be zero. -} THREADNAME_INFO; -#pragma pack(pop) - -void set_thread_name( DWORD dwThreadID, const char* threadName) -{ - THREADNAME_INFO info; - info.dwType = 0x1000; - info.szName = threadName; - info.dwThreadID = dwThreadID; - info.dwFlags = 0; - - __try - { - ::RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(DWORD), (ULONG_PTR*)&info ); - } - __except(EXCEPTION_CONTINUE_EXECUTION) - { - } -} -#endif - - -//---------------------------------------------------------------------------- -// Usage: -// void run_func(LLThread* thread) -// { -// } -// LLThread* thread = new LLThread(); -// thread->run(run_func); -// ... -// thread->setQuitting(); -// while(!timeout) -// { -// if (thread->isStopped()) -// { -// delete thread; -// break; -// } -// } -// -//---------------------------------------------------------------------------- -namespace -{ - - LLThread::id_t main_thread() - { - // Using a function-static variable to identify the main thread - // requires that control reach here from the main thread before it - // reaches here from any other thread. We simply trust that whichever - // thread gets here first is the main thread. - static LLThread::id_t s_thread_id = LLThread::currentID(); - return s_thread_id; - } - -} // anonymous namespace - -LL_COMMON_API bool on_main_thread() -{ - return (LLThread::currentID() == main_thread()); -} - -LL_COMMON_API bool assert_main_thread() -{ - auto curr = LLThread::currentID(); - auto main = main_thread(); - if (curr == main) - return true; - - LL_WARNS() << "Illegal execution from thread id " << curr - << " outside main thread " << main << LL_ENDL; - return false; -} - -// this function has become moot -void LLThread::registerThreadID() {} - -// -// Handed to the APR thread creation function -// -void LLThread::threadRun() -{ -#ifdef LL_WINDOWS - set_thread_name(-1, mName.c_str()); - -#if 0 // probably a bad idea, see usage of SetThreadIdealProcessor in LLWindowWin32) - HANDLE hThread = GetCurrentThread(); - if (hThread) - { - SetThreadAffinityMask(hThread, (DWORD_PTR) 0xFFFFFFFFFFFFFFFE); - } -#endif - -#endif - - LL_PROFILER_SET_THREAD_NAME( mName.c_str() ); - - // this is the first point at which we're actually running in the new thread - mID = currentID(); - - // for now, hard code all LLThreads to report to single master thread recorder, which is known to be running on main thread - mRecorder = new LLTrace::ThreadRecorder(*LLTrace::get_master_thread_recorder()); - - // Run the user supplied function - do - { - try - { - run(); - } - catch (const LLContinueError &e) - { - LL_WARNS("THREAD") << "ContinueException on thread '" << mName << - "' reentering run(). Error what is: '" << e.what() << "'" << LL_ENDL; - //output possible call stacks to log file. - LLError::LLCallStacks::print(); - - LOG_UNHANDLED_EXCEPTION("LLThread"); - continue; - } - break; - - } while (true); - - //LL_INFOS() << "LLThread::staticRun() Exiting: " << threadp->mName << LL_ENDL; - - - delete mRecorder; - mRecorder = NULL; - - // We're done with the run function, this thread is done executing now. - //NB: we are using this flag to sync across threads...we really need memory barriers here - // Todo: add LLMutex per thread instead of flag? - // We are using "while (mStatus != STOPPED) {ms_sleep();}" everywhere. - mStatus = STOPPED; -} - -LLThread::LLThread(const std::string& name, apr_pool_t *poolp) : - mPaused(false), - mName(name), - mThreadp(NULL), - mStatus(STOPPED), - mRecorder(NULL) -{ - mRunCondition = new LLCondition(); - mDataLock = new LLMutex(); - mLocalAPRFilePoolp = NULL ; -} - - -LLThread::~LLThread() -{ - shutdown(); - - if (isCrashed()) - { - LL_WARNS("THREAD") << "Destroying crashed thread named '" << mName << "'" << LL_ENDL; - } - - if(mLocalAPRFilePoolp) - { - delete mLocalAPRFilePoolp ; - mLocalAPRFilePoolp = NULL ; - } -} - -void LLThread::shutdown() -{ - if (isCrashed()) - { - LL_WARNS("THREAD") << "Shutting down crashed thread named '" << mName << "'" << LL_ENDL; - } - - // Warning! If you somehow call the thread destructor from itself, - // the thread will die in an unclean fashion! - if (mThreadp) - { - if (!isStopped()) - { - // The thread isn't already stopped - // First, set the flag that indicates that we're ready to die - setQuitting(); - - //LL_INFOS() << "LLThread::~LLThread() Killing thread " << mName << " Status: " << mStatus << LL_ENDL; - // Now wait a bit for the thread to exit - // It's unclear whether I should even bother doing this - this destructor - // should never get called unless we're already stopped, really... - S32 counter = 0; - const S32 MAX_WAIT = 600; - while (counter < MAX_WAIT) - { - if (isStopped()) - { - break; - } - // Sleep for a tenth of a second - ms_sleep(100); - yield(); - counter++; - } - } - - if (!isStopped()) - { - // This thread just wouldn't stop, even though we gave it time - //LL_WARNS() << "LLThread::~LLThread() exiting thread before clean exit!" << LL_ENDL; - // Put a stake in its heart. (A very hostile method to force a thread to quit) -#if LL_WINDOWS - TerminateThread(mNativeHandle, 0); -#else - pthread_cancel(mNativeHandle); -#endif - - delete mRecorder; - mRecorder = NULL; - mStatus = STOPPED; - return; - } - mThreadp = NULL; - } - - delete mRunCondition; - mRunCondition = NULL; - - delete mDataLock; - mDataLock = NULL; - - if (mRecorder) - { - // missed chance to properly shut down recorder (needs to be done in thread context) - // probably due to abnormal thread termination - // so just leak it and remove it from parent - LLTrace::get_master_thread_recorder()->removeChildRecorder(mRecorder); - } -} - - -void LLThread::start() -{ - llassert(isStopped()); - - // Set thread state to running - mStatus = RUNNING; - - try - { - mThreadp = new std::thread(std::bind(&LLThread::threadRun, this)); - mNativeHandle = mThreadp->native_handle(); - } - catch (std::system_error& ex) - { - mStatus = STOPPED; - LL_WARNS() << "failed to start thread " << mName << " " << ex.what() << LL_ENDL; - } - -} - -//============================================================================ -// Called from MAIN THREAD. - -// Request that the thread pause/resume. -// The thread will pause when (and if) it calls checkPause() -void LLThread::pause() -{ - if (!mPaused) - { - // this will cause the thread to stop execution as soon as checkPause() is called - mPaused = 1; // Does not need to be atomic since this is only set/unset from the main thread - } -} - -void LLThread::unpause() -{ - if (mPaused) - { - mPaused = 0; - } - - wake(); // wake up the thread if necessary -} - -// virtual predicate function -- returns true if the thread should wake up, false if it should sleep. -bool LLThread::runCondition(void) -{ - // by default, always run. Handling of pause/unpause is done regardless of this function's result. - return true; -} - -//============================================================================ -// Called from run() (CHILD THREAD). -// Stop thread execution if requested until unpaused. -void LLThread::checkPause() -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD - mDataLock->lock(); - - // This is in a while loop because the pthread API allows for spurious wakeups. - while(shouldSleep()) - { - mDataLock->unlock(); - mRunCondition->wait(); // unlocks mRunCondition - mDataLock->lock(); - // mRunCondition is locked when the thread wakes up - } - - mDataLock->unlock(); -} - -//============================================================================ - -void LLThread::setQuitting() -{ - mDataLock->lock(); - if (mStatus == RUNNING) - { - mStatus = QUITTING; - } - // It's only safe to remove mRunCondition if all locked threads were notified - mRunCondition->broadcast(); - mDataLock->unlock(); -} - -// static -LLThread::id_t LLThread::currentID() -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD - return std::this_thread::get_id(); -} - -// static -void LLThread::yield() -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD - std::this_thread::yield(); -} - -void LLThread::wake() -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD - mDataLock->lock(); - if(!shouldSleep()) - { - mRunCondition->signal(); - } - mDataLock->unlock(); -} - -void LLThread::wakeLocked() -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD - if(!shouldSleep()) - { - mRunCondition->signal(); - } -} - -void LLThread::lockData() -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD - mDataLock->lock(); -} - -void LLThread::unlockData() -{ - LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD - mDataLock->unlock(); -} - -//============================================================================ - -//---------------------------------------------------------------------------- - -//static -LLMutex* LLThreadSafeRefCount::sMutex = 0; - -//static -void LLThreadSafeRefCount::initThreadSafeRefCount() -{ - if (!sMutex) - { - sMutex = new LLMutex(); - } -} - -//static -void LLThreadSafeRefCount::cleanupThreadSafeRefCount() -{ - delete sMutex; - sMutex = NULL; -} - - -//---------------------------------------------------------------------------- - -LLThreadSafeRefCount::LLThreadSafeRefCount() : - mRef(0) -{ -} - -LLThreadSafeRefCount::LLThreadSafeRefCount(const LLThreadSafeRefCount& src) -{ - mRef = 0; -} - -LLThreadSafeRefCount::~LLThreadSafeRefCount() -{ - if (mRef != 0) - { - LL_ERRS() << "deleting referenced object mRef = " << mRef << LL_ENDL; - } -} - -//============================================================================ - -LLResponder::~LLResponder() -{ -} - -//============================================================================ +/**
+ * @file llthread.cpp
+ *
+ * $LicenseInfo:firstyear=2004&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010-2013, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+#include "llapr.h"
+
+#include "apr_portable.h"
+
+#include "llthread.h"
+#include "llmutex.h"
+
+#include "lltimer.h"
+#include "lltrace.h"
+#include "lltracethreadrecorder.h"
+#include "llexception.h"
+
+#if LL_LINUX
+#include <sched.h>
+#endif
+
+
+#ifdef LL_WINDOWS
+
+const DWORD MS_VC_EXCEPTION=0x406D1388;
+
+#pragma pack(push,8)
+typedef struct tagTHREADNAME_INFO
+{
+ DWORD dwType; // Must be 0x1000.
+ LPCSTR szName; // Pointer to name (in user addr space).
+ DWORD dwThreadID; // Thread ID (-1=caller thread).
+ DWORD dwFlags; // Reserved for future use, must be zero.
+} THREADNAME_INFO;
+#pragma pack(pop)
+
+void set_thread_name( DWORD dwThreadID, const char* threadName)
+{
+ THREADNAME_INFO info;
+ info.dwType = 0x1000;
+ info.szName = threadName;
+ info.dwThreadID = dwThreadID;
+ info.dwFlags = 0;
+
+ __try
+ {
+ ::RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(DWORD), (ULONG_PTR*)&info );
+ }
+ __except(EXCEPTION_CONTINUE_EXECUTION)
+ {
+ }
+}
+#endif
+
+
+//----------------------------------------------------------------------------
+// Usage:
+// void run_func(LLThread* thread)
+// {
+// }
+// LLThread* thread = new LLThread();
+// thread->run(run_func);
+// ...
+// thread->setQuitting();
+// while(!timeout)
+// {
+// if (thread->isStopped())
+// {
+// delete thread;
+// break;
+// }
+// }
+//
+//----------------------------------------------------------------------------
+namespace
+{
+
+ LLThread::id_t main_thread()
+ {
+ // Using a function-static variable to identify the main thread
+ // requires that control reach here from the main thread before it
+ // reaches here from any other thread. We simply trust that whichever
+ // thread gets here first is the main thread.
+ static LLThread::id_t s_thread_id = LLThread::currentID();
+ return s_thread_id;
+ }
+
+} // anonymous namespace
+
+LL_COMMON_API bool on_main_thread()
+{
+ return (LLThread::currentID() == main_thread());
+}
+
+LL_COMMON_API bool assert_main_thread()
+{
+ auto curr = LLThread::currentID();
+ auto main = main_thread();
+ if (curr == main)
+ return true;
+
+ LL_WARNS() << "Illegal execution from thread id " << curr
+ << " outside main thread " << main << LL_ENDL;
+ return false;
+}
+
+// this function has become moot
+void LLThread::registerThreadID() {}
+
+//
+// Handed to the APR thread creation function
+//
+void LLThread::threadRun()
+{
+#ifdef LL_WINDOWS
+ set_thread_name(-1, mName.c_str());
+
+#if 0 // probably a bad idea, see usage of SetThreadIdealProcessor in LLWindowWin32)
+ HANDLE hThread = GetCurrentThread();
+ if (hThread)
+ {
+ SetThreadAffinityMask(hThread, (DWORD_PTR) 0xFFFFFFFFFFFFFFFE);
+ }
+#endif
+
+#endif
+
+ LL_PROFILER_SET_THREAD_NAME( mName.c_str() );
+
+ // this is the first point at which we're actually running in the new thread
+ mID = currentID();
+
+ // for now, hard code all LLThreads to report to single master thread recorder, which is known to be running on main thread
+ mRecorder = new LLTrace::ThreadRecorder(*LLTrace::get_master_thread_recorder());
+
+ // Run the user supplied function
+ do
+ {
+ try
+ {
+ run();
+ }
+ catch (const LLContinueError &e)
+ {
+ LL_WARNS("THREAD") << "ContinueException on thread '" << mName <<
+ "' reentering run(). Error what is: '" << e.what() << "'" << LL_ENDL;
+ //output possible call stacks to log file.
+ LLError::LLCallStacks::print();
+
+ LOG_UNHANDLED_EXCEPTION("LLThread");
+ continue;
+ }
+ break;
+
+ } while (true);
+
+ //LL_INFOS() << "LLThread::staticRun() Exiting: " << threadp->mName << LL_ENDL;
+
+
+ delete mRecorder;
+ mRecorder = NULL;
+
+ // We're done with the run function, this thread is done executing now.
+ //NB: we are using this flag to sync across threads...we really need memory barriers here
+ // Todo: add LLMutex per thread instead of flag?
+ // We are using "while (mStatus != STOPPED) {ms_sleep();}" everywhere.
+ mStatus = STOPPED;
+}
+
+LLThread::LLThread(const std::string& name, apr_pool_t *poolp) :
+ mPaused(false),
+ mName(name),
+ mThreadp(NULL),
+ mStatus(STOPPED),
+ mRecorder(NULL)
+{
+ mRunCondition = new LLCondition();
+ mDataLock = new LLMutex();
+ mLocalAPRFilePoolp = NULL ;
+}
+
+
+LLThread::~LLThread()
+{
+ shutdown();
+
+ if (isCrashed())
+ {
+ LL_WARNS("THREAD") << "Destroying crashed thread named '" << mName << "'" << LL_ENDL;
+ }
+
+ if(mLocalAPRFilePoolp)
+ {
+ delete mLocalAPRFilePoolp ;
+ mLocalAPRFilePoolp = NULL ;
+ }
+}
+
+void LLThread::shutdown()
+{
+ if (isCrashed())
+ {
+ LL_WARNS("THREAD") << "Shutting down crashed thread named '" << mName << "'" << LL_ENDL;
+ }
+
+ // Warning! If you somehow call the thread destructor from itself,
+ // the thread will die in an unclean fashion!
+ if (mThreadp)
+ {
+ if (!isStopped())
+ {
+ // The thread isn't already stopped
+ // First, set the flag that indicates that we're ready to die
+ setQuitting();
+
+ //LL_INFOS() << "LLThread::~LLThread() Killing thread " << mName << " Status: " << mStatus << LL_ENDL;
+ // Now wait a bit for the thread to exit
+ // It's unclear whether I should even bother doing this - this destructor
+ // should never get called unless we're already stopped, really...
+ S32 counter = 0;
+ const S32 MAX_WAIT = 600;
+ while (counter < MAX_WAIT)
+ {
+ if (isStopped())
+ {
+ break;
+ }
+ // Sleep for a tenth of a second
+ ms_sleep(100);
+ yield();
+ counter++;
+ }
+ }
+
+ if (!isStopped())
+ {
+ // This thread just wouldn't stop, even though we gave it time
+ //LL_WARNS() << "LLThread::~LLThread() exiting thread before clean exit!" << LL_ENDL;
+ // Put a stake in its heart. (A very hostile method to force a thread to quit)
+#if LL_WINDOWS
+ TerminateThread(mNativeHandle, 0);
+#else
+ pthread_cancel(mNativeHandle);
+#endif
+
+ delete mRecorder;
+ mRecorder = NULL;
+ mStatus = STOPPED;
+ return;
+ }
+ mThreadp = NULL;
+ }
+
+ delete mRunCondition;
+ mRunCondition = NULL;
+
+ delete mDataLock;
+ mDataLock = NULL;
+
+ if (mRecorder)
+ {
+ // missed chance to properly shut down recorder (needs to be done in thread context)
+ // probably due to abnormal thread termination
+ // so just leak it and remove it from parent
+ LLTrace::get_master_thread_recorder()->removeChildRecorder(mRecorder);
+ }
+}
+
+
+void LLThread::start()
+{
+ llassert(isStopped());
+
+ // Set thread state to running
+ mStatus = RUNNING;
+
+ try
+ {
+ mThreadp = new std::thread(std::bind(&LLThread::threadRun, this));
+ mNativeHandle = mThreadp->native_handle();
+ }
+ catch (std::system_error& ex)
+ {
+ mStatus = STOPPED;
+ LL_WARNS() << "failed to start thread " << mName << " " << ex.what() << LL_ENDL;
+ }
+
+}
+
+//============================================================================
+// Called from MAIN THREAD.
+
+// Request that the thread pause/resume.
+// The thread will pause when (and if) it calls checkPause()
+void LLThread::pause()
+{
+ if (!mPaused)
+ {
+ // this will cause the thread to stop execution as soon as checkPause() is called
+ mPaused = 1; // Does not need to be atomic since this is only set/unset from the main thread
+ }
+}
+
+void LLThread::unpause()
+{
+ if (mPaused)
+ {
+ mPaused = 0;
+ }
+
+ wake(); // wake up the thread if necessary
+}
+
+// virtual predicate function -- returns true if the thread should wake up, false if it should sleep.
+bool LLThread::runCondition(void)
+{
+ // by default, always run. Handling of pause/unpause is done regardless of this function's result.
+ return true;
+}
+
+//============================================================================
+// Called from run() (CHILD THREAD).
+// Stop thread execution if requested until unpaused.
+void LLThread::checkPause()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
+ mDataLock->lock();
+
+ // This is in a while loop because the pthread API allows for spurious wakeups.
+ while(shouldSleep())
+ {
+ mDataLock->unlock();
+ mRunCondition->wait(); // unlocks mRunCondition
+ mDataLock->lock();
+ // mRunCondition is locked when the thread wakes up
+ }
+
+ mDataLock->unlock();
+}
+
+//============================================================================
+
+void LLThread::setQuitting()
+{
+ mDataLock->lock();
+ if (mStatus == RUNNING)
+ {
+ mStatus = QUITTING;
+ }
+ // It's only safe to remove mRunCondition if all locked threads were notified
+ mRunCondition->broadcast();
+ mDataLock->unlock();
+}
+
+// static
+LLThread::id_t LLThread::currentID()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
+ return std::this_thread::get_id();
+}
+
+// static
+void LLThread::yield()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
+ std::this_thread::yield();
+}
+
+void LLThread::wake()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
+ mDataLock->lock();
+ if(!shouldSleep())
+ {
+ mRunCondition->signal();
+ }
+ mDataLock->unlock();
+}
+
+void LLThread::wakeLocked()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
+ if(!shouldSleep())
+ {
+ mRunCondition->signal();
+ }
+}
+
+void LLThread::lockData()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
+ mDataLock->lock();
+}
+
+void LLThread::unlockData()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD
+ mDataLock->unlock();
+}
+
+//============================================================================
+
+//----------------------------------------------------------------------------
+
+//static
+LLMutex* LLThreadSafeRefCount::sMutex = 0;
+
+//static
+void LLThreadSafeRefCount::initThreadSafeRefCount()
+{
+ if (!sMutex)
+ {
+ sMutex = new LLMutex();
+ }
+}
+
+//static
+void LLThreadSafeRefCount::cleanupThreadSafeRefCount()
+{
+ delete sMutex;
+ sMutex = NULL;
+}
+
+
+//----------------------------------------------------------------------------
+
+LLThreadSafeRefCount::LLThreadSafeRefCount() :
+ mRef(0)
+{
+}
+
+LLThreadSafeRefCount::LLThreadSafeRefCount(const LLThreadSafeRefCount& src)
+{
+ mRef = 0;
+}
+
+LLThreadSafeRefCount::~LLThreadSafeRefCount()
+{
+ if (mRef != 0)
+ {
+ LL_ERRS() << "deleting referenced object mRef = " << mRef << LL_ENDL;
+ }
+}
+
+//============================================================================
+
+LLResponder::~LLResponder()
+{
+}
+
+//============================================================================
diff --git a/indra/llcommon/llthreadlocalstorage.h b/indra/llcommon/llthreadlocalstorage.h index bdd28ec865..151d8a666b 100644 --- a/indra/llcommon/llthreadlocalstorage.h +++ b/indra/llcommon/llthreadlocalstorage.h @@ -1,4 +1,4 @@ -/** +/** * @file llthreadlocalstorage.h * @author Richard * @brief Class wrappers for thread local storage @@ -6,21 +6,21 @@ * $LicenseInfo:firstyear=2004&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -34,18 +34,18 @@ template<typename DERIVED_TYPE> class LLThreadLocalSingletonPointer { public: - LL_FORCE_INLINE static DERIVED_TYPE* getInstance() - { - return sInstance; - } + LL_FORCE_INLINE static DERIVED_TYPE* getInstance() + { + return sInstance; + } - static void setInstance(DERIVED_TYPE* instance) - { - sInstance = instance; - } + static void setInstance(DERIVED_TYPE* instance) + { + sInstance = instance; + } private: - static thread_local DERIVED_TYPE* sInstance; + static thread_local DERIVED_TYPE* sInstance; }; template<typename DERIVED_TYPE> diff --git a/indra/llcommon/llthreadsafequeue.cpp b/indra/llcommon/llthreadsafequeue.cpp index bde36999ba..03baca2f0a 100644 --- a/indra/llcommon/llthreadsafequeue.cpp +++ b/indra/llcommon/llthreadsafequeue.cpp @@ -1,24 +1,24 @@ -/** +/** * @file llthread.cpp * * $LicenseInfo:firstyear=2004&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llcommon/llthreadsafequeue.h b/indra/llcommon/llthreadsafequeue.h index f396a71e6f..034e3f7897 100644 --- a/indra/llcommon/llthreadsafequeue.h +++ b/indra/llcommon/llthreadsafequeue.h @@ -1,25 +1,25 @@ -/** +/** * @file llthreadsafequeue.h * @brief Queue protected with mutexes for cross-thread use * * $LicenseInfo:firstyear=2004&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -44,14 +44,14 @@ // A general queue exception. // class LL_COMMON_API LLThreadSafeQueueError: - public LLException + public LLException { public: - LLThreadSafeQueueError(std::string const & message): - LLException(message) - { - ; // No op. - } + LLThreadSafeQueueError(std::string const & message): + LLException(message) + { + ; // No op. + } }; @@ -59,14 +59,14 @@ public: // An exception raised when blocking operations are interrupted. // class LL_COMMON_API LLThreadSafeQueueInterrupt: - public LLThreadSafeQueueError + public LLThreadSafeQueueError { public: - LLThreadSafeQueueInterrupt(void): - LLThreadSafeQueueError("queue operation interrupted") - { - ; // No op. - } + LLThreadSafeQueueInterrupt(void): + LLThreadSafeQueueError("queue operation interrupted") + { + ; // No op. + } }; /** @@ -78,138 +78,138 @@ template<typename ElementT, typename QueueT=std::queue<ElementT>> class LLThreadSafeQueue { public: - typedef ElementT value_type; - - // Limiting the number of pending items prevents unbounded growth of the - // underlying queue. - LLThreadSafeQueue(size_t capacity = 1024); - virtual ~LLThreadSafeQueue() {} - - // Add an element to the queue (will block if the queue has reached - // capacity). - // - // This call will raise an interrupt error if the queue is closed while - // the caller is blocked. - template <typename T> - void push(T&& element); - // legacy name - void pushFront(ElementT const & element) { return push(element); } - - // Add an element to the queue (will block if the queue has reached - // capacity). Return false if the queue is closed before push is possible. - template <typename T> - bool pushIfOpen(T&& element); - - // Try to add an element to the queue without blocking. Returns - // true only if the element was actually added. - template <typename T> - bool tryPush(T&& element); - // legacy name - bool tryPushFront(ElementT const & element) { return tryPush(element); } - - // Try to add an element to the queue, blocking if full but with timeout - // after specified duration. Returns true if the element was added. - // There are potentially two different timeouts involved: how long to try - // to lock the mutex, versus how long to wait for the queue to stop being - // full. Careful settings for each timeout might be orders of magnitude - // apart. However, this method conflates them. - template <typename Rep, typename Period, typename T> - bool tryPushFor(const std::chrono::duration<Rep, Period>& timeout, - T&& element); - // legacy name - template <typename Rep, typename Period> - bool tryPushFrontFor(const std::chrono::duration<Rep, Period>& timeout, - ElementT const & element) { return tryPushFor(timeout, element); } - - // Try to add an element to the queue, blocking if full but with - // timeout at specified time_point. Returns true if the element was added. - template <typename Clock, typename Duration, typename T> - bool tryPushUntil(const std::chrono::time_point<Clock, Duration>& until, - T&& element); - // no legacy name because this is a newer method - - // Pop the element at the head of the queue (will block if the queue is - // empty). - // - // This call will raise an interrupt error if the queue is closed while - // the caller is blocked. - ElementT pop(void); - // legacy name - ElementT popBack(void) { return pop(); } - - // Pop an element from the head of the queue if there is one available. - // Returns true only if an element was popped. - bool tryPop(ElementT & element); - // legacy name - bool tryPopBack(ElementT & element) { return tryPop(element); } - - // Pop the element at the head of the queue, blocking if empty, with - // timeout after specified duration. Returns true if an element was popped. - template <typename Rep, typename Period> - bool tryPopFor(const std::chrono::duration<Rep, Period>& timeout, ElementT& element); - // no legacy name because this is a newer method - - // Pop the element at the head of the queue, blocking if empty, with - // timeout at specified time_point. Returns true if an element was popped. - template <typename Clock, typename Duration> - bool tryPopUntil(const std::chrono::time_point<Clock, Duration>& until, - ElementT& element); - // no legacy name because this is a newer method - - // Returns the size of the queue. - size_t size(); + typedef ElementT value_type; + + // Limiting the number of pending items prevents unbounded growth of the + // underlying queue. + LLThreadSafeQueue(size_t capacity = 1024); + virtual ~LLThreadSafeQueue() {} + + // Add an element to the queue (will block if the queue has reached + // capacity). + // + // This call will raise an interrupt error if the queue is closed while + // the caller is blocked. + template <typename T> + void push(T&& element); + // legacy name + void pushFront(ElementT const & element) { return push(element); } + + // Add an element to the queue (will block if the queue has reached + // capacity). Return false if the queue is closed before push is possible. + template <typename T> + bool pushIfOpen(T&& element); + + // Try to add an element to the queue without blocking. Returns + // true only if the element was actually added. + template <typename T> + bool tryPush(T&& element); + // legacy name + bool tryPushFront(ElementT const & element) { return tryPush(element); } + + // Try to add an element to the queue, blocking if full but with timeout + // after specified duration. Returns true if the element was added. + // There are potentially two different timeouts involved: how long to try + // to lock the mutex, versus how long to wait for the queue to stop being + // full. Careful settings for each timeout might be orders of magnitude + // apart. However, this method conflates them. + template <typename Rep, typename Period, typename T> + bool tryPushFor(const std::chrono::duration<Rep, Period>& timeout, + T&& element); + // legacy name + template <typename Rep, typename Period> + bool tryPushFrontFor(const std::chrono::duration<Rep, Period>& timeout, + ElementT const & element) { return tryPushFor(timeout, element); } + + // Try to add an element to the queue, blocking if full but with + // timeout at specified time_point. Returns true if the element was added. + template <typename Clock, typename Duration, typename T> + bool tryPushUntil(const std::chrono::time_point<Clock, Duration>& until, + T&& element); + // no legacy name because this is a newer method + + // Pop the element at the head of the queue (will block if the queue is + // empty). + // + // This call will raise an interrupt error if the queue is closed while + // the caller is blocked. + ElementT pop(void); + // legacy name + ElementT popBack(void) { return pop(); } + + // Pop an element from the head of the queue if there is one available. + // Returns true only if an element was popped. + bool tryPop(ElementT & element); + // legacy name + bool tryPopBack(ElementT & element) { return tryPop(element); } + + // Pop the element at the head of the queue, blocking if empty, with + // timeout after specified duration. Returns true if an element was popped. + template <typename Rep, typename Period> + bool tryPopFor(const std::chrono::duration<Rep, Period>& timeout, ElementT& element); + // no legacy name because this is a newer method + + // Pop the element at the head of the queue, blocking if empty, with + // timeout at specified time_point. Returns true if an element was popped. + template <typename Clock, typename Duration> + bool tryPopUntil(const std::chrono::time_point<Clock, Duration>& until, + ElementT& element); + // no legacy name because this is a newer method + + // Returns the size of the queue. + size_t size(); //Returns the capacity of the queue. U32 capacity() { return mCapacity; } - // closes the queue: - // - every subsequent push() call will throw LLThreadSafeQueueInterrupt - // - every subsequent tryPush() call will return false - // - pop() calls will return normally until the queue is drained, then - // every subsequent pop() will throw LLThreadSafeQueueInterrupt - // - tryPop() calls will return normally until the queue is drained, - // then every subsequent tryPop() call will return false - void close(); + // closes the queue: + // - every subsequent push() call will throw LLThreadSafeQueueInterrupt + // - every subsequent tryPush() call will return false + // - pop() calls will return normally until the queue is drained, then + // every subsequent pop() will throw LLThreadSafeQueueInterrupt + // - tryPop() calls will return normally until the queue is drained, + // then every subsequent tryPop() call will return false + void close(); - // producer end: are we prevented from pushing any additional items? - bool isClosed(); - // consumer end: are we done, is the queue entirely drained? - bool done(); + // producer end: are we prevented from pushing any additional items? + bool isClosed(); + // consumer end: are we done, is the queue entirely drained? + bool done(); protected: - typedef QueueT queue_type; - QueueT mStorage; - size_t mCapacity; - bool mClosed; - - boost::fibers::timed_mutex mLock; - typedef std::unique_lock<decltype(mLock)> lock_t; - boost::fibers::condition_variable_any mCapacityCond; - boost::fibers::condition_variable_any mEmptyCond; - - enum pop_result { EMPTY, DONE, WAITING, POPPED }; - // implementation logic, suitable for passing to tryLockUntil() - template <typename Clock, typename Duration> - pop_result tryPopUntil_(lock_t& lock, - const std::chrono::time_point<Clock, Duration>& until, - ElementT& element); - // if we're able to lock immediately, do so and run the passed callable, - // which must accept lock_t& and return bool - template <typename CALLABLE> - bool tryLock(CALLABLE&& callable); - // if we're able to lock before the passed time_point, do so and run the - // passed callable, which must accept lock_t& and return bool - template <typename Clock, typename Duration, typename CALLABLE> - bool tryLockUntil(const std::chrono::time_point<Clock, Duration>& until, - CALLABLE&& callable); - // while lock is locked, really push the passed element, if we can - template <typename T> - bool push_(lock_t& lock, T&& element); - // while lock is locked, really pop the head element, if we can - pop_result pop_(lock_t& lock, ElementT& element); - // Is the current head element ready to pop? We say yes; subclass can - // override as needed. - virtual bool canPop(const ElementT& head) const { return true; } + typedef QueueT queue_type; + QueueT mStorage; + size_t mCapacity; + bool mClosed; + + boost::fibers::timed_mutex mLock; + typedef std::unique_lock<decltype(mLock)> lock_t; + boost::fibers::condition_variable_any mCapacityCond; + boost::fibers::condition_variable_any mEmptyCond; + + enum pop_result { EMPTY, DONE, WAITING, POPPED }; + // implementation logic, suitable for passing to tryLockUntil() + template <typename Clock, typename Duration> + pop_result tryPopUntil_(lock_t& lock, + const std::chrono::time_point<Clock, Duration>& until, + ElementT& element); + // if we're able to lock immediately, do so and run the passed callable, + // which must accept lock_t& and return bool + template <typename CALLABLE> + bool tryLock(CALLABLE&& callable); + // if we're able to lock before the passed time_point, do so and run the + // passed callable, which must accept lock_t& and return bool + template <typename Clock, typename Duration, typename CALLABLE> + bool tryLockUntil(const std::chrono::time_point<Clock, Duration>& until, + CALLABLE&& callable); + // while lock is locked, really push the passed element, if we can + template <typename T> + bool push_(lock_t& lock, T&& element); + // while lock is locked, really pop the head element, if we can + pop_result pop_(lock_t& lock, ElementT& element); + // Is the current head element ready to pop? We say yes; subclass can + // override as needed. + virtual bool canPop(const ElementT& head) const { return true; } }; /***************************************************************************** @@ -426,7 +426,7 @@ LLThreadSafeQueue<ElementT, QueueT>::pop_(lock_t& lock, ElementT& element) if (mStorage.empty()) return mClosed? DONE : EMPTY; - // If there's a head element, pass it to canPop() to see if it's ready to pop. + // If there's a head element, pass it to canPop() to see if it's ready to pop. if (! canPop(mStorage.front())) return WAITING; diff --git a/indra/llcommon/lltimer.cpp b/indra/llcommon/lltimer.cpp index b1ddfc952c..8a8451b927 100644 --- a/indra/llcommon/lltimer.cpp +++ b/indra/llcommon/lltimer.cpp @@ -1,608 +1,608 @@ -/** - * @file lltimer.cpp - * @brief Cross-platform objects for doing timing - * - * $LicenseInfo:firstyear=2000&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" - -#include "lltimer.h" - -#include "u64.h" - -#include <chrono> -#include <thread> - -#if LL_WINDOWS -# include "llwin32headerslean.h" -#elif LL_LINUX || LL_DARWIN -# include <errno.h> -# include <sys/time.h> -#else -# error "architecture not supported" -#endif - -// -// Locally used constants -// -const U64 SEC_TO_MICROSEC_U64 = 1000000; - -//--------------------------------------------------------------------------- -// Globals and statics -//--------------------------------------------------------------------------- - -S32 gUTCOffset = 0; // viewer's offset from server UTC, in seconds -LLTimer* LLTimer::sTimer = NULL; - - -// -// Forward declarations -// - - -//--------------------------------------------------------------------------- -// Implementation -//--------------------------------------------------------------------------- - -#if LL_WINDOWS - - -#if 0 -void ms_sleep(U32 ms) -{ - LL_PROFILE_ZONE_SCOPED; - using TimePoint = std::chrono::steady_clock::time_point; - auto resume_time = TimePoint::clock::now() + std::chrono::milliseconds(ms); - while (TimePoint::clock::now() < resume_time) - { - std::this_thread::yield(); //note: don't use LLThread::yield here to avoid yielding for too long - } -} - -U32 micro_sleep(U64 us, U32 max_yields) -{ - // max_yields is unused; just fiddle with it to avoid warnings. - max_yields = 0; - ms_sleep((U32)(us / 1000)); - return 0; -} - -#else - -U32 micro_sleep(U64 us, U32 max_yields) -{ - LL_PROFILE_ZONE_SCOPED -#if 0 - LARGE_INTEGER ft; - ft.QuadPart = -static_cast<S64>(us * 10); // '-' using relative time - - HANDLE timer = CreateWaitableTimer(NULL, true, NULL); - SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0); - WaitForSingleObject(timer, INFINITE); - CloseHandle(timer); -#else - Sleep(us / 1000); -#endif - - return 0; -} - -void ms_sleep(U32 ms) -{ - LL_PROFILE_ZONE_SCOPED - micro_sleep(ms * 1000, 0); -} - -#endif - -#elif LL_LINUX || LL_DARWIN -static void _sleep_loop(struct timespec& thiswait) -{ - struct timespec nextwait; - bool sleep_more = false; - - do { - int result = nanosleep(&thiswait, &nextwait); - - // check if sleep was interrupted by a signal; unslept - // remainder was written back into 't' and we just nanosleep - // again. - sleep_more = (result == -1 && EINTR == errno); - - if (sleep_more) - { - if ( nextwait.tv_sec > thiswait.tv_sec || - (nextwait.tv_sec == thiswait.tv_sec && - nextwait.tv_nsec >= thiswait.tv_nsec) ) - { - // if the remaining time isn't actually going - // down then we're being shafted by low clock - // resolution - manually massage the sleep time - // downward. - if (nextwait.tv_nsec > 1000000) { - // lose 1ms - nextwait.tv_nsec -= 1000000; - } else { - if (nextwait.tv_sec == 0) { - // already so close to finished - sleep_more = false; - } else { - // lose up to 1ms - nextwait.tv_nsec = 0; - } - } - } - thiswait = nextwait; - } - } while (sleep_more); -} - -U32 micro_sleep(U64 us, U32 max_yields) -{ - U64 start = get_clock_count(); - // This is kernel dependent. Currently, our kernel generates software clock - // interrupts at 250 Hz (every 4,000 microseconds). - const S64 KERNEL_SLEEP_INTERVAL_US = 4000; - - // Use signed arithmetic to discover whether a sleep is even necessary. If - // either 'us' or KERNEL_SLEEP_INTERVAL_US is unsigned, the compiler - // promotes the difference to unsigned. If 'us' is less than half - // KERNEL_SLEEP_INTERVAL_US, the unsigned difference will be hugely - // positive, resulting in a crazy long wait. - auto num_sleep_intervals = (S64(us) - (KERNEL_SLEEP_INTERVAL_US >> 1)) / KERNEL_SLEEP_INTERVAL_US; - if (num_sleep_intervals > 0) - { - U64 sleep_time = (num_sleep_intervals * KERNEL_SLEEP_INTERVAL_US) - (KERNEL_SLEEP_INTERVAL_US >> 1); - struct timespec thiswait; - thiswait.tv_sec = sleep_time / 1000000; - thiswait.tv_nsec = (sleep_time % 1000000) * 1000l; - _sleep_loop(thiswait); - } - - U64 current_clock = get_clock_count(); - U32 yields = 0; - while ( (yields < max_yields) - && (current_clock - start < us) ) - { - sched_yield(); - ++yields; - current_clock = get_clock_count(); - } - return yields; -} - -void ms_sleep(U32 ms) -{ - long mslong = ms; // tv_nsec is a long - struct timespec thiswait; - thiswait.tv_sec = ms / 1000; - thiswait.tv_nsec = (mslong % 1000) * 1000000l; - _sleep_loop(thiswait); -} -#else -# error "architecture not supported" -#endif - -// -// CPU clock/other clock frequency and count functions -// - -#if LL_WINDOWS -U64 get_clock_count() -{ - static bool firstTime = true; - static U64 offset; - // ensures that callers to this function never have to deal with wrap - - // QueryPerformanceCounter implementation - LARGE_INTEGER clock_count; - QueryPerformanceCounter(&clock_count); - if (firstTime) { - offset = clock_count.QuadPart; - firstTime = false; - } - return clock_count.QuadPart - offset; -} - -F64 calc_clock_frequency() -{ - __int64 freq; - QueryPerformanceFrequency((LARGE_INTEGER *) &freq); - return (F64)freq; -} -#endif // LL_WINDOWS - - -#if LL_LINUX || LL_DARWIN -// Both Linux and Mac use gettimeofday for accurate time -F64 calc_clock_frequency() -{ - return 1000000.0; // microseconds, so 1 MHz. -} - -U64 get_clock_count() -{ - // Linux clocks are in microseconds - struct timeval tv; - gettimeofday(&tv, NULL); - return tv.tv_sec*SEC_TO_MICROSEC_U64 + tv.tv_usec; -} -#endif - - -TimerInfo::TimerInfo() -: mClockFrequency(0.0), - mTotalTimeClockCount(0), - mLastTotalTimeClockCount(0) -{} - -void TimerInfo::update() -{ - mClockFrequency = calc_clock_frequency(); - mClockFrequencyInv = 1.0/mClockFrequency; - mClocksToMicroseconds = mClockFrequencyInv; -} - -TimerInfo& get_timer_info() -{ - static TimerInfo sTimerInfo; - return sTimerInfo; -} - -/////////////////////////////////////////////////////////////////////////////// - -// returns a U64 number that represents the number of -// microseconds since the Unix epoch - Jan 1, 1970 -U64MicrosecondsImplicit totalTime() -{ - U64 current_clock_count = get_clock_count(); - if (!get_timer_info().mTotalTimeClockCount || get_timer_info().mClocksToMicroseconds.value() == 0) - { - get_timer_info().update(); - get_timer_info().mTotalTimeClockCount = current_clock_count; - -#if LL_WINDOWS - // Sync us up with local time (even though we PROBABLY don't need to, this is how it was implemented) - // Unix platforms use gettimeofday so they are synced, although this probably isn't a good assumption to - // make in the future. - - get_timer_info().mTotalTimeClockCount = (U64)(time(NULL) * get_timer_info().mClockFrequency); -#endif - - // Update the last clock count - get_timer_info().mLastTotalTimeClockCount = current_clock_count; - } - else - { - if (current_clock_count >= get_timer_info().mLastTotalTimeClockCount) - { - // No wrapping, we're all okay. - get_timer_info().mTotalTimeClockCount += current_clock_count - get_timer_info().mLastTotalTimeClockCount; - } - else - { - // We've wrapped. Compensate correctly - get_timer_info().mTotalTimeClockCount += (0xFFFFFFFFFFFFFFFFULL - get_timer_info().mLastTotalTimeClockCount) + current_clock_count; - } - - // Update the last clock count - get_timer_info().mLastTotalTimeClockCount = current_clock_count; - } - - // Return the total clock tick count in microseconds. - U64Microseconds time(get_timer_info().mTotalTimeClockCount*get_timer_info().mClocksToMicroseconds); - return time; -} - - -/////////////////////////////////////////////////////////////////////////////// - -LLTimer::LLTimer() -{ - if (!get_timer_info().mClockFrequency) - { - get_timer_info().update(); - } - - mStarted = true; - reset(); -} - -LLTimer::~LLTimer() -{} - -// static -void LLTimer::initClass() -{ - if (!sTimer) sTimer = new LLTimer; -} - -// static -void LLTimer::cleanupClass() -{ - delete sTimer; sTimer = NULL; -} - -// static -U64MicrosecondsImplicit LLTimer::getTotalTime() -{ - // simply call into the implementation function. - U64MicrosecondsImplicit total_time = totalTime(); - return total_time; -} - -// static -F64SecondsImplicit LLTimer::getTotalSeconds() -{ - return F64Microseconds(U64_to_F64(getTotalTime())); -} - -void LLTimer::reset() -{ - mLastClockCount = get_clock_count(); - mExpirationTicks = 0; -} - -/////////////////////////////////////////////////////////////////////////////// - -U64 LLTimer::getCurrentClockCount() -{ - return get_clock_count(); -} - -/////////////////////////////////////////////////////////////////////////////// - -void LLTimer::setLastClockCount(U64 current_count) -{ - mLastClockCount = current_count; -} - -/////////////////////////////////////////////////////////////////////////////// - -static -U64 getElapsedTimeAndUpdate(U64& lastClockCount) -{ - U64 current_clock_count = get_clock_count(); - U64 result; - - if (current_clock_count >= lastClockCount) - { - result = current_clock_count - lastClockCount; - } - else - { - // time has gone backward - result = 0; - } - - lastClockCount = current_clock_count; - - return result; -} - - -F64SecondsImplicit LLTimer::getElapsedTimeF64() const -{ - U64 last = mLastClockCount; - return (F64)getElapsedTimeAndUpdate(last) * get_timer_info().mClockFrequencyInv; -} - -F32SecondsImplicit LLTimer::getElapsedTimeF32() const -{ - return (F32)getElapsedTimeF64(); -} - -F64SecondsImplicit LLTimer::getElapsedTimeAndResetF64() -{ - return (F64)getElapsedTimeAndUpdate(mLastClockCount) * get_timer_info().mClockFrequencyInv; -} - -F32SecondsImplicit LLTimer::getElapsedTimeAndResetF32() -{ - return (F32)getElapsedTimeAndResetF64(); -} - -/////////////////////////////////////////////////////////////////////////////// - -void LLTimer::setTimerExpirySec(F32SecondsImplicit expiration) -{ - mExpirationTicks = get_clock_count() - + (U64)((F32)(expiration * get_timer_info().mClockFrequency.value())); -} - -F32SecondsImplicit LLTimer::getRemainingTimeF32() const -{ - U64 cur_ticks = get_clock_count(); - if (cur_ticks > mExpirationTicks) - { - return 0.0f; - } - return F32((mExpirationTicks - cur_ticks) * get_timer_info().mClockFrequencyInv); -} - - -bool LLTimer::checkExpirationAndReset(F32 expiration) -{ - U64 cur_ticks = get_clock_count(); - if (cur_ticks < mExpirationTicks) - { - return false; - } - - mExpirationTicks = cur_ticks - + (U64)((F32)(expiration * get_timer_info().mClockFrequency)); - return true; -} - - -bool LLTimer::hasExpired() const -{ - return get_clock_count() >= mExpirationTicks; -} - -/////////////////////////////////////////////////////////////////////////////// - -bool LLTimer::knownBadTimer() -{ - bool failed = false; - -#if LL_WINDOWS - WCHAR bad_pci_list[][10] = {L"1039:0530", - L"1039:0620", - L"10B9:0533", - L"10B9:1533", - L"1106:0596", - L"1106:0686", - L"1166:004F", - L"1166:0050", - L"8086:7110", - L"\0" - }; - - HKEY hKey = NULL; - LONG nResult = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE,L"SYSTEM\\CurrentControlSet\\Enum\\PCI", 0, - KEY_EXECUTE | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &hKey); - - WCHAR name[1024]; - DWORD name_len = 1024; - FILETIME scrap; - - S32 key_num = 0; - WCHAR pci_id[10]; - - wcscpy(pci_id, L"0000:0000"); /*Flawfinder: ignore*/ - - while (nResult == ERROR_SUCCESS) - { - nResult = ::RegEnumKeyEx(hKey, key_num++, name, &name_len, NULL, NULL, NULL, &scrap); - - if (nResult == ERROR_SUCCESS) - { - memcpy(&pci_id[0],&name[4],4); /* Flawfinder: ignore */ - memcpy(&pci_id[5],&name[13],4); /* Flawfinder: ignore */ - - for (S32 check = 0; bad_pci_list[check][0]; check++) - { - if (!wcscmp(pci_id, bad_pci_list[check])) - { -// LL_WARNS() << "unreliable PCI chipset found!! " << pci_id << endl; - failed = true; - break; - } - } -// llinfo << "PCI chipset found: " << pci_id << endl; - name_len = 1024; - } - } -#endif - return(failed); -} - -/////////////////////////////////////////////////////////////////////////////// -// -// NON-MEMBER FUNCTIONS -// -/////////////////////////////////////////////////////////////////////////////// - -time_t time_corrected() -{ - return time(NULL) + gUTCOffset; -} - - -// Is the current computer (in its current time zone) -// observing daylight savings time? -bool is_daylight_savings() -{ - time_t now = time(NULL); - - // Internal buffer to local server time - struct tm* internal_time = localtime(&now); - - // tm_isdst > 0 => daylight savings - // tm_isdst = 0 => not daylight savings - // tm_isdst < 0 => can't tell - return (internal_time->tm_isdst > 0); -} - - -struct tm* utc_to_pacific_time(time_t utc_time, bool pacific_daylight_time) -{ - S32Hours pacific_offset_hours; - if (pacific_daylight_time) - { - pacific_offset_hours = S32Hours(7); - } - else - { - pacific_offset_hours = S32Hours(8); - } - - // We subtract off the PST/PDT offset _before_ getting - // "UTC" time, because this will handle wrapping around - // for 5 AM UTC -> 10 PM PDT of the previous day. - utc_time -= S32SecondsImplicit(pacific_offset_hours); - - // Internal buffer to PST/PDT (see above) - struct tm* internal_time = gmtime(&utc_time); - - /* - // Don't do this, this won't correctly tell you if daylight savings is active in CA or not. - if (pacific_daylight_time) - { - internal_time->tm_isdst = 1; - } - */ - - return internal_time; -} - - -void microsecondsToTimecodeString(U64MicrosecondsImplicit current_time, std::string& tcstring) -{ - U64 hours; - U64 minutes; - U64 seconds; - U64 frames; - U64 subframes; - - hours = current_time / (U64)3600000000ul; - minutes = current_time / (U64)60000000; - minutes %= 60; - seconds = current_time / (U64)1000000; - seconds %= 60; - frames = current_time / (U64)41667; - frames %= 24; - subframes = current_time / (U64)42; - subframes %= 100; - - tcstring = llformat("%3.3d:%2.2d:%2.2d:%2.2d.%2.2d",(int)hours,(int)minutes,(int)seconds,(int)frames,(int)subframes); -} - - -void secondsToTimecodeString(F32SecondsImplicit current_time, std::string& tcstring) -{ - microsecondsToTimecodeString(current_time, tcstring); -} - - +/**
+ * @file lltimer.cpp
+ * @brief Cross-platform objects for doing timing
+ *
+ * $LicenseInfo:firstyear=2000&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "lltimer.h"
+
+#include "u64.h"
+
+#include <chrono>
+#include <thread>
+
+#if LL_WINDOWS
+# include "llwin32headerslean.h"
+#elif LL_LINUX || LL_DARWIN
+# include <errno.h>
+# include <sys/time.h>
+#else
+# error "architecture not supported"
+#endif
+
+//
+// Locally used constants
+//
+const U64 SEC_TO_MICROSEC_U64 = 1000000;
+
+//---------------------------------------------------------------------------
+// Globals and statics
+//---------------------------------------------------------------------------
+
+S32 gUTCOffset = 0; // viewer's offset from server UTC, in seconds
+LLTimer* LLTimer::sTimer = NULL;
+
+
+//
+// Forward declarations
+//
+
+
+//---------------------------------------------------------------------------
+// Implementation
+//---------------------------------------------------------------------------
+
+#if LL_WINDOWS
+
+
+#if 0
+void ms_sleep(U32 ms)
+{
+ LL_PROFILE_ZONE_SCOPED;
+ using TimePoint = std::chrono::steady_clock::time_point;
+ auto resume_time = TimePoint::clock::now() + std::chrono::milliseconds(ms);
+ while (TimePoint::clock::now() < resume_time)
+ {
+ std::this_thread::yield(); //note: don't use LLThread::yield here to avoid yielding for too long
+ }
+}
+
+U32 micro_sleep(U64 us, U32 max_yields)
+{
+ // max_yields is unused; just fiddle with it to avoid warnings.
+ max_yields = 0;
+ ms_sleep((U32)(us / 1000));
+ return 0;
+}
+
+#else
+
+U32 micro_sleep(U64 us, U32 max_yields)
+{
+ LL_PROFILE_ZONE_SCOPED
+#if 0
+ LARGE_INTEGER ft;
+ ft.QuadPart = -static_cast<S64>(us * 10); // '-' using relative time
+
+ HANDLE timer = CreateWaitableTimer(NULL, true, NULL);
+ SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0);
+ WaitForSingleObject(timer, INFINITE);
+ CloseHandle(timer);
+#else
+ Sleep(us / 1000);
+#endif
+
+ return 0;
+}
+
+void ms_sleep(U32 ms)
+{
+ LL_PROFILE_ZONE_SCOPED
+ micro_sleep(ms * 1000, 0);
+}
+
+#endif
+
+#elif LL_LINUX || LL_DARWIN
+static void _sleep_loop(struct timespec& thiswait)
+{
+ struct timespec nextwait;
+ bool sleep_more = false;
+
+ do {
+ int result = nanosleep(&thiswait, &nextwait);
+
+ // check if sleep was interrupted by a signal; unslept
+ // remainder was written back into 't' and we just nanosleep
+ // again.
+ sleep_more = (result == -1 && EINTR == errno);
+
+ if (sleep_more)
+ {
+ if ( nextwait.tv_sec > thiswait.tv_sec ||
+ (nextwait.tv_sec == thiswait.tv_sec &&
+ nextwait.tv_nsec >= thiswait.tv_nsec) )
+ {
+ // if the remaining time isn't actually going
+ // down then we're being shafted by low clock
+ // resolution - manually massage the sleep time
+ // downward.
+ if (nextwait.tv_nsec > 1000000) {
+ // lose 1ms
+ nextwait.tv_nsec -= 1000000;
+ } else {
+ if (nextwait.tv_sec == 0) {
+ // already so close to finished
+ sleep_more = false;
+ } else {
+ // lose up to 1ms
+ nextwait.tv_nsec = 0;
+ }
+ }
+ }
+ thiswait = nextwait;
+ }
+ } while (sleep_more);
+}
+
+U32 micro_sleep(U64 us, U32 max_yields)
+{
+ U64 start = get_clock_count();
+ // This is kernel dependent. Currently, our kernel generates software clock
+ // interrupts at 250 Hz (every 4,000 microseconds).
+ const S64 KERNEL_SLEEP_INTERVAL_US = 4000;
+
+ // Use signed arithmetic to discover whether a sleep is even necessary. If
+ // either 'us' or KERNEL_SLEEP_INTERVAL_US is unsigned, the compiler
+ // promotes the difference to unsigned. If 'us' is less than half
+ // KERNEL_SLEEP_INTERVAL_US, the unsigned difference will be hugely
+ // positive, resulting in a crazy long wait.
+ auto num_sleep_intervals = (S64(us) - (KERNEL_SLEEP_INTERVAL_US >> 1)) / KERNEL_SLEEP_INTERVAL_US;
+ if (num_sleep_intervals > 0)
+ {
+ U64 sleep_time = (num_sleep_intervals * KERNEL_SLEEP_INTERVAL_US) - (KERNEL_SLEEP_INTERVAL_US >> 1);
+ struct timespec thiswait;
+ thiswait.tv_sec = sleep_time / 1000000;
+ thiswait.tv_nsec = (sleep_time % 1000000) * 1000l;
+ _sleep_loop(thiswait);
+ }
+
+ U64 current_clock = get_clock_count();
+ U32 yields = 0;
+ while ( (yields < max_yields)
+ && (current_clock - start < us) )
+ {
+ sched_yield();
+ ++yields;
+ current_clock = get_clock_count();
+ }
+ return yields;
+}
+
+void ms_sleep(U32 ms)
+{
+ long mslong = ms; // tv_nsec is a long
+ struct timespec thiswait;
+ thiswait.tv_sec = ms / 1000;
+ thiswait.tv_nsec = (mslong % 1000) * 1000000l;
+ _sleep_loop(thiswait);
+}
+#else
+# error "architecture not supported"
+#endif
+
+//
+// CPU clock/other clock frequency and count functions
+//
+
+#if LL_WINDOWS
+U64 get_clock_count()
+{
+ static bool firstTime = true;
+ static U64 offset;
+ // ensures that callers to this function never have to deal with wrap
+
+ // QueryPerformanceCounter implementation
+ LARGE_INTEGER clock_count;
+ QueryPerformanceCounter(&clock_count);
+ if (firstTime) {
+ offset = clock_count.QuadPart;
+ firstTime = false;
+ }
+ return clock_count.QuadPart - offset;
+}
+
+F64 calc_clock_frequency()
+{
+ __int64 freq;
+ QueryPerformanceFrequency((LARGE_INTEGER *) &freq);
+ return (F64)freq;
+}
+#endif // LL_WINDOWS
+
+
+#if LL_LINUX || LL_DARWIN
+// Both Linux and Mac use gettimeofday for accurate time
+F64 calc_clock_frequency()
+{
+ return 1000000.0; // microseconds, so 1 MHz.
+}
+
+U64 get_clock_count()
+{
+ // Linux clocks are in microseconds
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return tv.tv_sec*SEC_TO_MICROSEC_U64 + tv.tv_usec;
+}
+#endif
+
+
+TimerInfo::TimerInfo()
+: mClockFrequency(0.0),
+ mTotalTimeClockCount(0),
+ mLastTotalTimeClockCount(0)
+{}
+
+void TimerInfo::update()
+{
+ mClockFrequency = calc_clock_frequency();
+ mClockFrequencyInv = 1.0/mClockFrequency;
+ mClocksToMicroseconds = mClockFrequencyInv;
+}
+
+TimerInfo& get_timer_info()
+{
+ static TimerInfo sTimerInfo;
+ return sTimerInfo;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+// returns a U64 number that represents the number of
+// microseconds since the Unix epoch - Jan 1, 1970
+U64MicrosecondsImplicit totalTime()
+{
+ U64 current_clock_count = get_clock_count();
+ if (!get_timer_info().mTotalTimeClockCount || get_timer_info().mClocksToMicroseconds.value() == 0)
+ {
+ get_timer_info().update();
+ get_timer_info().mTotalTimeClockCount = current_clock_count;
+
+#if LL_WINDOWS
+ // Sync us up with local time (even though we PROBABLY don't need to, this is how it was implemented)
+ // Unix platforms use gettimeofday so they are synced, although this probably isn't a good assumption to
+ // make in the future.
+
+ get_timer_info().mTotalTimeClockCount = (U64)(time(NULL) * get_timer_info().mClockFrequency);
+#endif
+
+ // Update the last clock count
+ get_timer_info().mLastTotalTimeClockCount = current_clock_count;
+ }
+ else
+ {
+ if (current_clock_count >= get_timer_info().mLastTotalTimeClockCount)
+ {
+ // No wrapping, we're all okay.
+ get_timer_info().mTotalTimeClockCount += current_clock_count - get_timer_info().mLastTotalTimeClockCount;
+ }
+ else
+ {
+ // We've wrapped. Compensate correctly
+ get_timer_info().mTotalTimeClockCount += (0xFFFFFFFFFFFFFFFFULL - get_timer_info().mLastTotalTimeClockCount) + current_clock_count;
+ }
+
+ // Update the last clock count
+ get_timer_info().mLastTotalTimeClockCount = current_clock_count;
+ }
+
+ // Return the total clock tick count in microseconds.
+ U64Microseconds time(get_timer_info().mTotalTimeClockCount*get_timer_info().mClocksToMicroseconds);
+ return time;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+LLTimer::LLTimer()
+{
+ if (!get_timer_info().mClockFrequency)
+ {
+ get_timer_info().update();
+ }
+
+ mStarted = true;
+ reset();
+}
+
+LLTimer::~LLTimer()
+{}
+
+// static
+void LLTimer::initClass()
+{
+ if (!sTimer) sTimer = new LLTimer;
+}
+
+// static
+void LLTimer::cleanupClass()
+{
+ delete sTimer; sTimer = NULL;
+}
+
+// static
+U64MicrosecondsImplicit LLTimer::getTotalTime()
+{
+ // simply call into the implementation function.
+ U64MicrosecondsImplicit total_time = totalTime();
+ return total_time;
+}
+
+// static
+F64SecondsImplicit LLTimer::getTotalSeconds()
+{
+ return F64Microseconds(U64_to_F64(getTotalTime()));
+}
+
+void LLTimer::reset()
+{
+ mLastClockCount = get_clock_count();
+ mExpirationTicks = 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+U64 LLTimer::getCurrentClockCount()
+{
+ return get_clock_count();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void LLTimer::setLastClockCount(U64 current_count)
+{
+ mLastClockCount = current_count;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static
+U64 getElapsedTimeAndUpdate(U64& lastClockCount)
+{
+ U64 current_clock_count = get_clock_count();
+ U64 result;
+
+ if (current_clock_count >= lastClockCount)
+ {
+ result = current_clock_count - lastClockCount;
+ }
+ else
+ {
+ // time has gone backward
+ result = 0;
+ }
+
+ lastClockCount = current_clock_count;
+
+ return result;
+}
+
+
+F64SecondsImplicit LLTimer::getElapsedTimeF64() const
+{
+ U64 last = mLastClockCount;
+ return (F64)getElapsedTimeAndUpdate(last) * get_timer_info().mClockFrequencyInv;
+}
+
+F32SecondsImplicit LLTimer::getElapsedTimeF32() const
+{
+ return (F32)getElapsedTimeF64();
+}
+
+F64SecondsImplicit LLTimer::getElapsedTimeAndResetF64()
+{
+ return (F64)getElapsedTimeAndUpdate(mLastClockCount) * get_timer_info().mClockFrequencyInv;
+}
+
+F32SecondsImplicit LLTimer::getElapsedTimeAndResetF32()
+{
+ return (F32)getElapsedTimeAndResetF64();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void LLTimer::setTimerExpirySec(F32SecondsImplicit expiration)
+{
+ mExpirationTicks = get_clock_count()
+ + (U64)((F32)(expiration * get_timer_info().mClockFrequency.value()));
+}
+
+F32SecondsImplicit LLTimer::getRemainingTimeF32() const
+{
+ U64 cur_ticks = get_clock_count();
+ if (cur_ticks > mExpirationTicks)
+ {
+ return 0.0f;
+ }
+ return F32((mExpirationTicks - cur_ticks) * get_timer_info().mClockFrequencyInv);
+}
+
+
+bool LLTimer::checkExpirationAndReset(F32 expiration)
+{
+ U64 cur_ticks = get_clock_count();
+ if (cur_ticks < mExpirationTicks)
+ {
+ return false;
+ }
+
+ mExpirationTicks = cur_ticks
+ + (U64)((F32)(expiration * get_timer_info().mClockFrequency));
+ return true;
+}
+
+
+bool LLTimer::hasExpired() const
+{
+ return get_clock_count() >= mExpirationTicks;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool LLTimer::knownBadTimer()
+{
+ bool failed = false;
+
+#if LL_WINDOWS
+ WCHAR bad_pci_list[][10] = {L"1039:0530",
+ L"1039:0620",
+ L"10B9:0533",
+ L"10B9:1533",
+ L"1106:0596",
+ L"1106:0686",
+ L"1166:004F",
+ L"1166:0050",
+ L"8086:7110",
+ L"\0"
+ };
+
+ HKEY hKey = NULL;
+ LONG nResult = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE,L"SYSTEM\\CurrentControlSet\\Enum\\PCI", 0,
+ KEY_EXECUTE | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &hKey);
+
+ WCHAR name[1024];
+ DWORD name_len = 1024;
+ FILETIME scrap;
+
+ S32 key_num = 0;
+ WCHAR pci_id[10];
+
+ wcscpy(pci_id, L"0000:0000"); /*Flawfinder: ignore*/
+
+ while (nResult == ERROR_SUCCESS)
+ {
+ nResult = ::RegEnumKeyEx(hKey, key_num++, name, &name_len, NULL, NULL, NULL, &scrap);
+
+ if (nResult == ERROR_SUCCESS)
+ {
+ memcpy(&pci_id[0],&name[4],4); /* Flawfinder: ignore */
+ memcpy(&pci_id[5],&name[13],4); /* Flawfinder: ignore */
+
+ for (S32 check = 0; bad_pci_list[check][0]; check++)
+ {
+ if (!wcscmp(pci_id, bad_pci_list[check]))
+ {
+// LL_WARNS() << "unreliable PCI chipset found!! " << pci_id << endl;
+ failed = true;
+ break;
+ }
+ }
+// llinfo << "PCI chipset found: " << pci_id << endl;
+ name_len = 1024;
+ }
+ }
+#endif
+ return(failed);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// NON-MEMBER FUNCTIONS
+//
+///////////////////////////////////////////////////////////////////////////////
+
+time_t time_corrected()
+{
+ return time(NULL) + gUTCOffset;
+}
+
+
+// Is the current computer (in its current time zone)
+// observing daylight savings time?
+bool is_daylight_savings()
+{
+ time_t now = time(NULL);
+
+ // Internal buffer to local server time
+ struct tm* internal_time = localtime(&now);
+
+ // tm_isdst > 0 => daylight savings
+ // tm_isdst = 0 => not daylight savings
+ // tm_isdst < 0 => can't tell
+ return (internal_time->tm_isdst > 0);
+}
+
+
+struct tm* utc_to_pacific_time(time_t utc_time, bool pacific_daylight_time)
+{
+ S32Hours pacific_offset_hours;
+ if (pacific_daylight_time)
+ {
+ pacific_offset_hours = S32Hours(7);
+ }
+ else
+ {
+ pacific_offset_hours = S32Hours(8);
+ }
+
+ // We subtract off the PST/PDT offset _before_ getting
+ // "UTC" time, because this will handle wrapping around
+ // for 5 AM UTC -> 10 PM PDT of the previous day.
+ utc_time -= S32SecondsImplicit(pacific_offset_hours);
+
+ // Internal buffer to PST/PDT (see above)
+ struct tm* internal_time = gmtime(&utc_time);
+
+ /*
+ // Don't do this, this won't correctly tell you if daylight savings is active in CA or not.
+ if (pacific_daylight_time)
+ {
+ internal_time->tm_isdst = 1;
+ }
+ */
+
+ return internal_time;
+}
+
+
+void microsecondsToTimecodeString(U64MicrosecondsImplicit current_time, std::string& tcstring)
+{
+ U64 hours;
+ U64 minutes;
+ U64 seconds;
+ U64 frames;
+ U64 subframes;
+
+ hours = current_time / (U64)3600000000ul;
+ minutes = current_time / (U64)60000000;
+ minutes %= 60;
+ seconds = current_time / (U64)1000000;
+ seconds %= 60;
+ frames = current_time / (U64)41667;
+ frames %= 24;
+ subframes = current_time / (U64)42;
+ subframes %= 100;
+
+ tcstring = llformat("%3.3d:%2.2d:%2.2d:%2.2d.%2.2d",(int)hours,(int)minutes,(int)seconds,(int)frames,(int)subframes);
+}
+
+
+void secondsToTimecodeString(F32SecondsImplicit current_time, std::string& tcstring)
+{
+ microsecondsToTimecodeString(current_time, tcstring);
+}
+
+
diff --git a/indra/llcommon/lltimer.h b/indra/llcommon/lltimer.h index 8fea02eb92..60eb007595 100644 --- a/indra/llcommon/lltimer.h +++ b/indra/llcommon/lltimer.h @@ -1,188 +1,188 @@ -/** - * @file lltimer.h - * @brief Cross-platform objects for doing timing - * - * $LicenseInfo:firstyear=2000&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_TIMER_H -#define LL_TIMER_H - -#if LL_LINUX || LL_DARWIN -#include <sys/time.h> -#endif -#include <limits.h> - -#include "stdtypes.h" - -#include <string> -#include <list> -// units conversions -#include "llunits.h" -#ifndef USEC_PER_SEC - const U32 USEC_PER_SEC = 1000000; -#endif -const U32 SEC_PER_MIN = 60; -const U32 MIN_PER_HOUR = 60; -const U32 USEC_PER_MIN = USEC_PER_SEC * SEC_PER_MIN; -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 LL_COMMON_API LLTimer -{ -public: - static LLTimer *sTimer; // global timer - -protected: - U64 mLastClockCount; - U64 mExpirationTicks; - bool mStarted; - -public: - LLTimer(); - ~LLTimer(); - - static void initClass(); - static void cleanupClass(); - - // Return a high precision number of seconds since the start of - // this application instance. - static F64SecondsImplicit getElapsedSeconds() - { - if (sTimer) - { - return sTimer->getElapsedTimeF64(); - } - else - { - return 0; - } - } - - // Return a high precision usec since epoch - static U64MicrosecondsImplicit getTotalTime(); - - // Return a high precision seconds since epoch - static F64SecondsImplicit getTotalSeconds(); - - - // MANIPULATORS - void start() { reset(); mStarted = true; } - void stop() { mStarted = false; } - void reset(); // Resets the timer - void setLastClockCount(U64 current_count); // Sets the timer so that the next elapsed call will be relative to this time - void setTimerExpirySec(F32SecondsImplicit expiration); - bool checkExpirationAndReset(F32 expiration); - bool hasExpired() const; - F32SecondsImplicit getElapsedTimeAndResetF32(); // Returns elapsed time in seconds with reset - F64SecondsImplicit getElapsedTimeAndResetF64(); - - F32SecondsImplicit getRemainingTimeF32() const; - - static bool knownBadTimer(); - - // ACCESSORS - F32SecondsImplicit getElapsedTimeF32() const; // Returns elapsed time in seconds - F64SecondsImplicit getElapsedTimeF64() const; // Returns elapsed time in seconds - - bool getStarted() const { return mStarted; } - - - static U64 getCurrentClockCount(); // Returns the raw clockticks -}; - -// -// Various functions for initializing/accessing clock and timing stuff. Don't use these without REALLY knowing how they work. -// -struct TimerInfo -{ - TimerInfo(); - void update(); - - F64HertzImplicit mClockFrequency; - F64SecondsImplicit mClockFrequencyInv; - F64MicrosecondsImplicit mClocksToMicroseconds; - U64 mTotalTimeClockCount; - U64 mLastTotalTimeClockCount; -}; - -TimerInfo& get_timer_info(); - -LL_COMMON_API U64 get_clock_count(); - -// Sleep for milliseconds -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. -LL_COMMON_API time_t time_corrected(); - -static inline time_t time_min() -{ - if (sizeof(time_t) == 4) - { - return (time_t) INT_MIN; - } else { -#ifdef LLONG_MIN - return (time_t) LLONG_MIN; -#else - return (time_t) LONG_MIN; -#endif - } -} - -static inline time_t time_max() -{ - if (sizeof(time_t) == 4) - { - return (time_t) INT_MAX; - } else { -#ifdef LLONG_MAX - return (time_t) LLONG_MAX; -#else - return (time_t) LONG_MAX; -#endif - } -} - -// Correction factor used by time_corrected() above. -extern LL_COMMON_API S32 gUTCOffset; - -// Is the current computer (in its current time zone) -// observing daylight savings time? -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); -LL_COMMON_API struct tm* utc_to_pacific_time(time_t utc_time, bool pacific_daylight_time); - -LL_COMMON_API void microsecondsToTimecodeString(U64MicrosecondsImplicit current_time, std::string& tcstring); -LL_COMMON_API void secondsToTimecodeString(F32SecondsImplicit current_time, std::string& tcstring); - -U64MicrosecondsImplicit LL_COMMON_API totalTime(); // Returns current system time in microseconds - -#endif +/**
+ * @file lltimer.h
+ * @brief Cross-platform objects for doing timing
+ *
+ * $LicenseInfo:firstyear=2000&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_TIMER_H
+#define LL_TIMER_H
+
+#if LL_LINUX || LL_DARWIN
+#include <sys/time.h>
+#endif
+#include <limits.h>
+
+#include "stdtypes.h"
+
+#include <string>
+#include <list>
+// units conversions
+#include "llunits.h"
+#ifndef USEC_PER_SEC
+ const U32 USEC_PER_SEC = 1000000;
+#endif
+const U32 SEC_PER_MIN = 60;
+const U32 MIN_PER_HOUR = 60;
+const U32 USEC_PER_MIN = USEC_PER_SEC * SEC_PER_MIN;
+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 LL_COMMON_API LLTimer
+{
+public:
+ static LLTimer *sTimer; // global timer
+
+protected:
+ U64 mLastClockCount;
+ U64 mExpirationTicks;
+ bool mStarted;
+
+public:
+ LLTimer();
+ ~LLTimer();
+
+ static void initClass();
+ static void cleanupClass();
+
+ // Return a high precision number of seconds since the start of
+ // this application instance.
+ static F64SecondsImplicit getElapsedSeconds()
+ {
+ if (sTimer)
+ {
+ return sTimer->getElapsedTimeF64();
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ // Return a high precision usec since epoch
+ static U64MicrosecondsImplicit getTotalTime();
+
+ // Return a high precision seconds since epoch
+ static F64SecondsImplicit getTotalSeconds();
+
+
+ // MANIPULATORS
+ void start() { reset(); mStarted = true; }
+ void stop() { mStarted = false; }
+ void reset(); // Resets the timer
+ void setLastClockCount(U64 current_count); // Sets the timer so that the next elapsed call will be relative to this time
+ void setTimerExpirySec(F32SecondsImplicit expiration);
+ bool checkExpirationAndReset(F32 expiration);
+ bool hasExpired() const;
+ F32SecondsImplicit getElapsedTimeAndResetF32(); // Returns elapsed time in seconds with reset
+ F64SecondsImplicit getElapsedTimeAndResetF64();
+
+ F32SecondsImplicit getRemainingTimeF32() const;
+
+ static bool knownBadTimer();
+
+ // ACCESSORS
+ F32SecondsImplicit getElapsedTimeF32() const; // Returns elapsed time in seconds
+ F64SecondsImplicit getElapsedTimeF64() const; // Returns elapsed time in seconds
+
+ bool getStarted() const { return mStarted; }
+
+
+ static U64 getCurrentClockCount(); // Returns the raw clockticks
+};
+
+//
+// Various functions for initializing/accessing clock and timing stuff. Don't use these without REALLY knowing how they work.
+//
+struct TimerInfo
+{
+ TimerInfo();
+ void update();
+
+ F64HertzImplicit mClockFrequency;
+ F64SecondsImplicit mClockFrequencyInv;
+ F64MicrosecondsImplicit mClocksToMicroseconds;
+ U64 mTotalTimeClockCount;
+ U64 mLastTotalTimeClockCount;
+};
+
+TimerInfo& get_timer_info();
+
+LL_COMMON_API U64 get_clock_count();
+
+// Sleep for milliseconds
+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.
+LL_COMMON_API time_t time_corrected();
+
+static inline time_t time_min()
+{
+ if (sizeof(time_t) == 4)
+ {
+ return (time_t) INT_MIN;
+ } else {
+#ifdef LLONG_MIN
+ return (time_t) LLONG_MIN;
+#else
+ return (time_t) LONG_MIN;
+#endif
+ }
+}
+
+static inline time_t time_max()
+{
+ if (sizeof(time_t) == 4)
+ {
+ return (time_t) INT_MAX;
+ } else {
+#ifdef LLONG_MAX
+ return (time_t) LLONG_MAX;
+#else
+ return (time_t) LONG_MAX;
+#endif
+ }
+}
+
+// Correction factor used by time_corrected() above.
+extern LL_COMMON_API S32 gUTCOffset;
+
+// Is the current computer (in its current time zone)
+// observing daylight savings time?
+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);
+LL_COMMON_API struct tm* utc_to_pacific_time(time_t utc_time, bool pacific_daylight_time);
+
+LL_COMMON_API void microsecondsToTimecodeString(U64MicrosecondsImplicit current_time, std::string& tcstring);
+LL_COMMON_API void secondsToTimecodeString(F32SecondsImplicit current_time, std::string& tcstring);
+
+U64MicrosecondsImplicit LL_COMMON_API totalTime(); // Returns current system time in microseconds
+
+#endif
diff --git a/indra/llcommon/lltrace.cpp b/indra/llcommon/lltrace.cpp index 87457ad907..440529b8e8 100644 --- a/indra/llcommon/lltrace.cpp +++ b/indra/llcommon/lltrace.cpp @@ -1,24 +1,24 @@ -/** +/** * @file lltrace.cpp * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2012, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -33,53 +33,53 @@ namespace LLTrace { -StatBase::StatBase( const char* name, const char* description ) -: mName(name), - mDescription(description ? description : "") +StatBase::StatBase( const char* name, const char* description ) +: mName(name), + mDescription(description ? description : "") { #ifndef LL_RELEASE_FOR_DOWNLOAD - if (LLTrace::get_thread_recorder() != NULL) - { - LL_ERRS() << "Attempting to declare trace object after program initialization. Trace objects should be statically initialized." << LL_ENDL; - } + if (LLTrace::get_thread_recorder() != NULL) + { + LL_ERRS() << "Attempting to declare trace object after program initialization. Trace objects should be statically initialized." << LL_ENDL; + } #endif } const char* StatBase::getUnitLabel() const { - return ""; + return ""; } -TimeBlockTreeNode::TimeBlockTreeNode() -: mBlock(NULL), - mParent(NULL), - mNeedsSorting(false), - mCollapsed(true) +TimeBlockTreeNode::TimeBlockTreeNode() +: mBlock(NULL), + mParent(NULL), + mNeedsSorting(false), + mCollapsed(true) {} void TimeBlockTreeNode::setParent( BlockTimerStatHandle* parent ) { LL_PROFILE_ZONE_SCOPED; - llassert_always(parent != mBlock); - llassert_always(parent != NULL); + llassert_always(parent != mBlock); + llassert_always(parent != NULL); - TimeBlockTreeNode* parent_tree_node = get_thread_recorder()->getTimeBlockTreeNode(narrow<size_t>(parent->getIndex())); - if (!parent_tree_node) return; + TimeBlockTreeNode* parent_tree_node = get_thread_recorder()->getTimeBlockTreeNode(narrow<size_t>(parent->getIndex())); + if (!parent_tree_node) return; - if (mParent) - { - std::vector<BlockTimerStatHandle*>& children = mParent->getChildren(); - std::vector<BlockTimerStatHandle*>::iterator found_it = std::find(children.begin(), children.end(), mBlock); - if (found_it != children.end()) - { - children.erase(found_it); - } - } + if (mParent) + { + std::vector<BlockTimerStatHandle*>& children = mParent->getChildren(); + std::vector<BlockTimerStatHandle*>::iterator found_it = std::find(children.begin(), children.end(), mBlock); + if (found_it != children.end()) + { + children.erase(found_it); + } + } - mParent = parent; - mBlock->getCurrentAccumulator().mParent = parent; - parent_tree_node->mChildren.push_back(mBlock); - parent_tree_node->mNeedsSorting = true; + mParent = parent; + mBlock->getCurrentAccumulator().mParent = parent; + parent_tree_node->mChildren.push_back(mBlock); + parent_tree_node->mNeedsSorting = true; } } diff --git a/indra/llcommon/lltrace.h b/indra/llcommon/lltrace.h index 21a5803a76..edb010b0a4 100644 --- a/indra/llcommon/lltrace.h +++ b/indra/llcommon/lltrace.h @@ -1,25 +1,25 @@ -/** +/** * @file lltrace.h * @brief Runtime statistics accumulation. * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2012, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -47,90 +47,90 @@ class Recording; template<typename T> T storage_value(T val) { return val; } -template<typename UNIT_TYPE, typename STORAGE_TYPE> +template<typename UNIT_TYPE, typename STORAGE_TYPE> STORAGE_TYPE storage_value(LLUnit<STORAGE_TYPE, UNIT_TYPE> val) { return val.value(); } -template<typename UNIT_TYPE, typename STORAGE_TYPE> +template<typename UNIT_TYPE, typename STORAGE_TYPE> STORAGE_TYPE storage_value(LLUnitImplicit<STORAGE_TYPE, UNIT_TYPE> val) { return val.value(); } class StatBase { public: - StatBase(const char* name, const char* description); - virtual ~StatBase() {} - virtual const char* getUnitLabel() const; + StatBase(const char* name, const char* description); + virtual ~StatBase() {} + virtual const char* getUnitLabel() const; - const std::string& getName() const { return mName; } - const std::string& getDescription() const { return mDescription; } + const std::string& getName() const { return mName; } + const std::string& getDescription() const { return mDescription; } protected: - std::string mName; - std::string mDescription; + std::string mName; + std::string mDescription; }; template<typename ACCUMULATOR> -class StatType -: public StatBase, - public LLInstanceTracker<StatType<ACCUMULATOR>, std::string> +class StatType +: public StatBase, + public LLInstanceTracker<StatType<ACCUMULATOR>, std::string> { public: - typedef LLInstanceTracker<StatType<ACCUMULATOR>, std::string> instance_tracker_t; - StatType(const char* name, const char* description) - : instance_tracker_t(name), - StatBase(name, description), - mAccumulatorIndex(AccumulatorBuffer<ACCUMULATOR>::getDefaultBuffer()->reserveSlot()) - {} - - LL_FORCE_INLINE ACCUMULATOR& getCurrentAccumulator() const - { - ACCUMULATOR* accumulator_storage = LLThreadLocalSingletonPointer<ACCUMULATOR>::getInstance(); - return accumulator_storage ? accumulator_storage[mAccumulatorIndex] : (*AccumulatorBuffer<ACCUMULATOR>::getDefaultBuffer())[mAccumulatorIndex]; - } - - size_t getIndex() const { return mAccumulatorIndex; } - static size_t getNumIndices() { return AccumulatorBuffer<ACCUMULATOR>::getNumIndices(); } + typedef LLInstanceTracker<StatType<ACCUMULATOR>, std::string> instance_tracker_t; + StatType(const char* name, const char* description) + : instance_tracker_t(name), + StatBase(name, description), + mAccumulatorIndex(AccumulatorBuffer<ACCUMULATOR>::getDefaultBuffer()->reserveSlot()) + {} + + LL_FORCE_INLINE ACCUMULATOR& getCurrentAccumulator() const + { + ACCUMULATOR* accumulator_storage = LLThreadLocalSingletonPointer<ACCUMULATOR>::getInstance(); + return accumulator_storage ? accumulator_storage[mAccumulatorIndex] : (*AccumulatorBuffer<ACCUMULATOR>::getDefaultBuffer())[mAccumulatorIndex]; + } + + size_t getIndex() const { return mAccumulatorIndex; } + static size_t getNumIndices() { return AccumulatorBuffer<ACCUMULATOR>::getNumIndices(); } protected: - const size_t mAccumulatorIndex; + const size_t mAccumulatorIndex; }; template<> class StatType<TimeBlockAccumulator::CallCountFacet> -: public StatType<TimeBlockAccumulator> +: public StatType<TimeBlockAccumulator> { public: - StatType(const char* name, const char* description = "") - : StatType<TimeBlockAccumulator>(name, description) - {} + StatType(const char* name, const char* description = "") + : StatType<TimeBlockAccumulator>(name, description) + {} }; template<> class StatType<TimeBlockAccumulator::SelfTimeFacet> - : public StatType<TimeBlockAccumulator> + : public StatType<TimeBlockAccumulator> { public: - StatType(const char* name, const char* description = "") - : StatType<TimeBlockAccumulator>(name, description) - {} + StatType(const char* name, const char* description = "") + : StatType<TimeBlockAccumulator>(name, description) + {} }; template <typename T = F64> class EventStatHandle -: public StatType<EventAccumulator> +: public StatType<EventAccumulator> { public: - typedef F64 storage_t; - typedef StatType<EventAccumulator> stat_t; - typedef EventStatHandle<T> self_t; + typedef F64 storage_t; + typedef StatType<EventAccumulator> stat_t; + typedef EventStatHandle<T> self_t; - EventStatHandle(const char* name, const char* description = NULL) - : stat_t(name, description) - {} + EventStatHandle(const char* name, const char* description = NULL) + : stat_t(name, description) + {} - /*virtual*/ const char* getUnitLabel() const { return LLGetUnitLabel<T>::getUnitLabel(); } + /*virtual*/ const char* getUnitLabel() const { return LLGetUnitLabel<T>::getUnitLabel(); } }; @@ -138,58 +138,58 @@ template<typename T, typename VALUE_T> void record(EventStatHandle<T>& measurement, VALUE_T value) { #if LL_TRACE_ENABLED - T converted_value(value); - measurement.getCurrentAccumulator().record(storage_value(converted_value)); + T converted_value(value); + measurement.getCurrentAccumulator().record(storage_value(converted_value)); #endif } template <typename T = F64> class SampleStatHandle -: public StatType<SampleAccumulator> +: public StatType<SampleAccumulator> { public: - typedef F64 storage_t; - typedef StatType<SampleAccumulator> stat_t; - typedef SampleStatHandle<T> self_t; + typedef F64 storage_t; + typedef StatType<SampleAccumulator> stat_t; + typedef SampleStatHandle<T> self_t; - SampleStatHandle(const char* name, const char* description = NULL) - : stat_t(name, description) - {} + SampleStatHandle(const char* name, const char* description = NULL) + : stat_t(name, description) + {} - /*virtual*/ const char* getUnitLabel() const { return LLGetUnitLabel<T>::getUnitLabel(); } + /*virtual*/ const char* getUnitLabel() const { return LLGetUnitLabel<T>::getUnitLabel(); } }; template<typename T, typename VALUE_T> void sample(SampleStatHandle<T>& measurement, VALUE_T value) { #if LL_TRACE_ENABLED - T converted_value(value); - measurement.getCurrentAccumulator().sample(storage_value(converted_value)); + T converted_value(value); + measurement.getCurrentAccumulator().sample(storage_value(converted_value)); #endif } template <typename T = F64> class CountStatHandle -: public StatType<CountAccumulator> +: public StatType<CountAccumulator> { public: - typedef F64 storage_t; - typedef StatType<CountAccumulator> stat_t; - typedef CountStatHandle<T> self_t; + typedef F64 storage_t; + typedef StatType<CountAccumulator> stat_t; + typedef CountStatHandle<T> self_t; - CountStatHandle(const char* name, const char* description = NULL) - : stat_t(name, description) - {} + CountStatHandle(const char* name, const char* description = NULL) + : stat_t(name, description) + {} - /*virtual*/ const char* getUnitLabel() const { return LLGetUnitLabel<T>::getUnitLabel(); } + /*virtual*/ const char* getUnitLabel() const { return LLGetUnitLabel<T>::getUnitLabel(); } }; template<typename T, typename VALUE_T> void add(CountStatHandle<T>& count, VALUE_T value) { #if LL_TRACE_ENABLED - T converted_value(value); - count.getCurrentAccumulator().add(storage_value(converted_value)); + T converted_value(value); + count.getCurrentAccumulator().add(storage_value(converted_value)); #endif } @@ -198,85 +198,85 @@ void add(CountStatHandle<T>& count, VALUE_T value) template<typename T, typename IS_MEM_TRACKABLE = void, typename IS_UNITS = void> struct MeasureMem { - static size_t measureFootprint(const T& value) - { - return sizeof(T); - } + static size_t measureFootprint(const T& value) + { + return sizeof(T); + } }; template<typename T, typename IS_BYTES> struct MeasureMem<T, typename T::mem_trackable_tag_t, IS_BYTES> { - static size_t measureFootprint(const T& value) - { + static size_t measureFootprint(const T& value) + { LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - return sizeof(T) + value.getMemFootprint(); - } + return sizeof(T) + value.getMemFootprint(); + } }; template<typename T, typename IS_MEM_TRACKABLE> struct MeasureMem<T, IS_MEM_TRACKABLE, typename T::is_unit_t> { - static size_t measureFootprint(const T& value) - { + static size_t measureFootprint(const T& value) + { LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - return U32Bytes(value).value(); - } + return U32Bytes(value).value(); + } }; template<typename T, typename IS_MEM_TRACKABLE, typename IS_BYTES> struct MeasureMem<T*, IS_MEM_TRACKABLE, IS_BYTES> { - static size_t measureFootprint(const T* value) - { + static size_t measureFootprint(const T* value) + { LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - if (!value) - { - return 0; - } - return MeasureMem<T>::measureFootprint(*value); - } + if (!value) + { + return 0; + } + return MeasureMem<T>::measureFootprint(*value); + } }; template<typename T, typename IS_MEM_TRACKABLE, typename IS_BYTES> struct MeasureMem<LLPointer<T>, IS_MEM_TRACKABLE, IS_BYTES> { - static size_t measureFootprint(const LLPointer<T> value) - { - if (value.isNull()) - { - return 0; - } - return MeasureMem<T>::measureFootprint(*value); - } + static size_t measureFootprint(const LLPointer<T> value) + { + if (value.isNull()) + { + return 0; + } + return MeasureMem<T>::measureFootprint(*value); + } }; template<typename IS_MEM_TRACKABLE, typename IS_BYTES> struct MeasureMem<S32, IS_MEM_TRACKABLE, IS_BYTES> { - static size_t measureFootprint(S32 value) - { - return value; - } + static size_t measureFootprint(S32 value) + { + return value; + } }; template<typename IS_MEM_TRACKABLE, typename IS_BYTES> struct MeasureMem<U32, IS_MEM_TRACKABLE, IS_BYTES> { - static size_t measureFootprint(U32 value) - { - return value; - } + static size_t measureFootprint(U32 value) + { + return value; + } }; template<typename T, typename IS_MEM_TRACKABLE, typename IS_BYTES> struct MeasureMem<std::basic_string<T>, IS_MEM_TRACKABLE, IS_BYTES> { - static size_t measureFootprint(const std::basic_string<T>& value) - { + static size_t measureFootprint(const std::basic_string<T>& value) + { LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - return value.capacity() * sizeof(T); - } + return value.capacity() * sizeof(T); + } }; } diff --git a/indra/llcommon/lltraceaccumulators.cpp b/indra/llcommon/lltraceaccumulators.cpp index b5b32cba38..8741087f3a 100644 --- a/indra/llcommon/lltraceaccumulators.cpp +++ b/indra/llcommon/lltraceaccumulators.cpp @@ -38,246 +38,246 @@ namespace LLTrace AccumulatorBufferGroup::AccumulatorBufferGroup() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; } AccumulatorBufferGroup::AccumulatorBufferGroup(const AccumulatorBufferGroup& other) -: mCounts(other.mCounts), - mSamples(other.mSamples), - mEvents(other.mEvents), - mStackTimers(other.mStackTimers) +: mCounts(other.mCounts), + mSamples(other.mSamples), + mEvents(other.mEvents), + mStackTimers(other.mStackTimers) { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; } AccumulatorBufferGroup::~AccumulatorBufferGroup() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; } void AccumulatorBufferGroup::handOffTo(AccumulatorBufferGroup& other) { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - other.mCounts.reset(&mCounts); - other.mSamples.reset(&mSamples); - other.mEvents.reset(&mEvents); - other.mStackTimers.reset(&mStackTimers); + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + other.mCounts.reset(&mCounts); + other.mSamples.reset(&mSamples); + other.mEvents.reset(&mEvents); + other.mStackTimers.reset(&mStackTimers); } void AccumulatorBufferGroup::makeCurrent() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - mCounts.makeCurrent(); - mSamples.makeCurrent(); - mEvents.makeCurrent(); - mStackTimers.makeCurrent(); - - ThreadRecorder* thread_recorder = get_thread_recorder(); - AccumulatorBuffer<TimeBlockAccumulator>& timer_accumulator_buffer = mStackTimers; - // update stacktimer parent pointers - for (size_t i = 0, end_i = mStackTimers.size(); i < end_i; i++) - { - TimeBlockTreeNode* tree_node = thread_recorder->getTimeBlockTreeNode(narrow<size_t>(i)); - if (tree_node) - { - timer_accumulator_buffer[i].mParent = tree_node->mParent; - } - } + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + mCounts.makeCurrent(); + mSamples.makeCurrent(); + mEvents.makeCurrent(); + mStackTimers.makeCurrent(); + + ThreadRecorder* thread_recorder = get_thread_recorder(); + AccumulatorBuffer<TimeBlockAccumulator>& timer_accumulator_buffer = mStackTimers; + // update stacktimer parent pointers + for (size_t i = 0, end_i = mStackTimers.size(); i < end_i; i++) + { + TimeBlockTreeNode* tree_node = thread_recorder->getTimeBlockTreeNode(narrow<size_t>(i)); + if (tree_node) + { + timer_accumulator_buffer[i].mParent = tree_node->mParent; + } + } } //static void AccumulatorBufferGroup::clearCurrent() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - AccumulatorBuffer<CountAccumulator>::clearCurrent(); - AccumulatorBuffer<SampleAccumulator>::clearCurrent(); - AccumulatorBuffer<EventAccumulator>::clearCurrent(); - AccumulatorBuffer<TimeBlockAccumulator>::clearCurrent(); + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + AccumulatorBuffer<CountAccumulator>::clearCurrent(); + AccumulatorBuffer<SampleAccumulator>::clearCurrent(); + AccumulatorBuffer<EventAccumulator>::clearCurrent(); + AccumulatorBuffer<TimeBlockAccumulator>::clearCurrent(); } bool AccumulatorBufferGroup::isCurrent() const { - return mCounts.isCurrent(); + return mCounts.isCurrent(); } void AccumulatorBufferGroup::append( const AccumulatorBufferGroup& other ) { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - mCounts.addSamples(other.mCounts, SEQUENTIAL); - mSamples.addSamples(other.mSamples, SEQUENTIAL); - mEvents.addSamples(other.mEvents, SEQUENTIAL); - mStackTimers.addSamples(other.mStackTimers, SEQUENTIAL); + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + mCounts.addSamples(other.mCounts, SEQUENTIAL); + mSamples.addSamples(other.mSamples, SEQUENTIAL); + mEvents.addSamples(other.mEvents, SEQUENTIAL); + mStackTimers.addSamples(other.mStackTimers, SEQUENTIAL); } void AccumulatorBufferGroup::merge( const AccumulatorBufferGroup& other) { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - mCounts.addSamples(other.mCounts, NON_SEQUENTIAL); - mSamples.addSamples(other.mSamples, NON_SEQUENTIAL); - mEvents.addSamples(other.mEvents, NON_SEQUENTIAL); - // for now, hold out timers from merge, need to be displayed per thread - //mStackTimers.addSamples(other.mStackTimers, NON_SEQUENTIAL); + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + mCounts.addSamples(other.mCounts, NON_SEQUENTIAL); + mSamples.addSamples(other.mSamples, NON_SEQUENTIAL); + mEvents.addSamples(other.mEvents, NON_SEQUENTIAL); + // for now, hold out timers from merge, need to be displayed per thread + //mStackTimers.addSamples(other.mStackTimers, NON_SEQUENTIAL); } void AccumulatorBufferGroup::reset(AccumulatorBufferGroup* other) { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - mCounts.reset(other ? &other->mCounts : NULL); - mSamples.reset(other ? &other->mSamples : NULL); - mEvents.reset(other ? &other->mEvents : NULL); - mStackTimers.reset(other ? &other->mStackTimers : NULL); + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + mCounts.reset(other ? &other->mCounts : NULL); + mSamples.reset(other ? &other->mSamples : NULL); + mEvents.reset(other ? &other->mEvents : NULL); + mStackTimers.reset(other ? &other->mStackTimers : NULL); } void AccumulatorBufferGroup::sync() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - if (isCurrent()) - { - F64SecondsImplicit time_stamp = LLTimer::getTotalSeconds(); - mSamples.sync(time_stamp); - } + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + if (isCurrent()) + { + F64SecondsImplicit time_stamp = LLTimer::getTotalSeconds(); + mSamples.sync(time_stamp); + } } F64 SampleAccumulator::mergeSumsOfSquares(const SampleAccumulator& a, const SampleAccumulator& b) { - const F64 epsilon = 0.0000001; - - if (a.getSamplingTime() > epsilon && b.getSamplingTime() > epsilon) - { - // combine variance (and hence standard deviation) of 2 different sized sample groups using - // the following formula: http://www.mrc-bsu.cam.ac.uk/cochrane/handbook/chapter_7/7_7_3_8_combining_groups.htm - F64 n_1 = a.getSamplingTime(), - n_2 = b.getSamplingTime(); - F64 m_1 = a.getMean(), - m_2 = b.getMean(); - F64 v_1 = a.getSumOfSquares() / a.getSamplingTime(), - v_2 = b.getSumOfSquares() / b.getSamplingTime(); - if (n_1 < epsilon) - { - return b.getSumOfSquares(); - } - else - { - return a.getSamplingTime() - * ((((n_1 - epsilon) * v_1) - + ((n_2 - epsilon) * v_2) - + (((n_1 * n_2) / (n_1 + n_2)) - * ((m_1 * m_1) + (m_2 * m_2) - (2.f * m_1 * m_2)))) - / (n_1 + n_2 - epsilon)); - } - } - - return a.getSumOfSquares(); + const F64 epsilon = 0.0000001; + + if (a.getSamplingTime() > epsilon && b.getSamplingTime() > epsilon) + { + // combine variance (and hence standard deviation) of 2 different sized sample groups using + // the following formula: http://www.mrc-bsu.cam.ac.uk/cochrane/handbook/chapter_7/7_7_3_8_combining_groups.htm + F64 n_1 = a.getSamplingTime(), + n_2 = b.getSamplingTime(); + F64 m_1 = a.getMean(), + m_2 = b.getMean(); + F64 v_1 = a.getSumOfSquares() / a.getSamplingTime(), + v_2 = b.getSumOfSquares() / b.getSamplingTime(); + if (n_1 < epsilon) + { + return b.getSumOfSquares(); + } + else + { + return a.getSamplingTime() + * ((((n_1 - epsilon) * v_1) + + ((n_2 - epsilon) * v_2) + + (((n_1 * n_2) / (n_1 + n_2)) + * ((m_1 * m_1) + (m_2 * m_2) - (2.f * m_1 * m_2)))) + / (n_1 + n_2 - epsilon)); + } + } + + return a.getSumOfSquares(); } void SampleAccumulator::addSamples( const SampleAccumulator& other, EBufferAppendType append_type ) { - if (append_type == NON_SEQUENTIAL) - { - return; - } - - if (!mHasValue) - { - *this = other; - - if (append_type == NON_SEQUENTIAL) - { - // restore own last value state - mLastValue = NaN; - mHasValue = false; - } - } - else if (other.mHasValue) - { - mSum += other.mSum; - - if (other.mMin < mMin) { mMin = other.mMin; } - if (other.mMax > mMax) { mMax = other.mMax; } - - mSumOfSquares = mergeSumsOfSquares(*this, other); - - if (append_type == SEQUENTIAL) - { - mLastValue = other.mLastValue; - mLastSampleTimeStamp = other.mLastSampleTimeStamp; - } - } + if (append_type == NON_SEQUENTIAL) + { + return; + } + + if (!mHasValue) + { + *this = other; + + if (append_type == NON_SEQUENTIAL) + { + // restore own last value state + mLastValue = NaN; + mHasValue = false; + } + } + else if (other.mHasValue) + { + mSum += other.mSum; + + if (other.mMin < mMin) { mMin = other.mMin; } + if (other.mMax > mMax) { mMax = other.mMax; } + + mSumOfSquares = mergeSumsOfSquares(*this, other); + + if (append_type == SEQUENTIAL) + { + mLastValue = other.mLastValue; + mLastSampleTimeStamp = other.mLastSampleTimeStamp; + } + } } void SampleAccumulator::reset( const SampleAccumulator* other ) { - mLastValue = other ? other->mLastValue : NaN; - mHasValue = other ? other->mHasValue : false; - mNumSamples = 0; - mSum = 0; - mMin = mLastValue; - mMax = mLastValue; - mMean = mLastValue; - llassert(!mHasValue || mMean < 0 || mMean >= 0); - mSumOfSquares = 0; - mLastSampleTimeStamp = LLTimer::getTotalSeconds(); - mTotalSamplingTime = 0; + mLastValue = other ? other->mLastValue : NaN; + mHasValue = other ? other->mHasValue : false; + mNumSamples = 0; + mSum = 0; + mMin = mLastValue; + mMax = mLastValue; + mMean = mLastValue; + llassert(!mHasValue || mMean < 0 || mMean >= 0); + mSumOfSquares = 0; + mLastSampleTimeStamp = LLTimer::getTotalSeconds(); + mTotalSamplingTime = 0; } F64 EventAccumulator::mergeSumsOfSquares(const EventAccumulator& a, const EventAccumulator& b) { - if (a.mNumSamples && b.mNumSamples) - { - // combine variance (and hence standard deviation) of 2 different sized sample groups using - // the following formula: http://www.mrc-bsu.cam.ac.uk/cochrane/handbook/chapter_7/7_7_3_8_combining_groups.htm - F64 n_1 = a.mNumSamples, - n_2 = b.mNumSamples; - F64 m_1 = a.mMean, - m_2 = b.mMean; - F64 v_1 = a.mSumOfSquares / a.mNumSamples, - v_2 = b.mSumOfSquares / b.mNumSamples; - return (F64)a.mNumSamples - * ((((n_1 - 1.f) * v_1) - + ((n_2 - 1.f) * v_2) - + (((n_1 * n_2) / (n_1 + n_2)) - * ((m_1 * m_1) + (m_2 * m_2) - (2.f * m_1 * m_2)))) - / (n_1 + n_2 - 1.f)); - } - - return a.mSumOfSquares; + if (a.mNumSamples && b.mNumSamples) + { + // combine variance (and hence standard deviation) of 2 different sized sample groups using + // the following formula: http://www.mrc-bsu.cam.ac.uk/cochrane/handbook/chapter_7/7_7_3_8_combining_groups.htm + F64 n_1 = a.mNumSamples, + n_2 = b.mNumSamples; + F64 m_1 = a.mMean, + m_2 = b.mMean; + F64 v_1 = a.mSumOfSquares / a.mNumSamples, + v_2 = b.mSumOfSquares / b.mNumSamples; + return (F64)a.mNumSamples + * ((((n_1 - 1.f) * v_1) + + ((n_2 - 1.f) * v_2) + + (((n_1 * n_2) / (n_1 + n_2)) + * ((m_1 * m_1) + (m_2 * m_2) - (2.f * m_1 * m_2)))) + / (n_1 + n_2 - 1.f)); + } + + return a.mSumOfSquares; } void EventAccumulator::addSamples( const EventAccumulator& other, EBufferAppendType append_type ) { - if (other.mNumSamples) - { - if (!mNumSamples) - { - *this = other; - } - else - { - mSum += other.mSum; - - // NOTE: both conditions will hold first time through - if (other.mMin < mMin) { mMin = other.mMin; } - if (other.mMax > mMax) { mMax = other.mMax; } - - mSumOfSquares = mergeSumsOfSquares(*this, other); - - F64 weight = (F64)mNumSamples / (F64)(mNumSamples + other.mNumSamples); - mNumSamples += other.mNumSamples; - mMean = mMean * weight + other.mMean * (1.f - weight); - if (append_type == SEQUENTIAL) mLastValue = other.mLastValue; - } - } + if (other.mNumSamples) + { + if (!mNumSamples) + { + *this = other; + } + else + { + mSum += other.mSum; + + // NOTE: both conditions will hold first time through + if (other.mMin < mMin) { mMin = other.mMin; } + if (other.mMax > mMax) { mMax = other.mMax; } + + mSumOfSquares = mergeSumsOfSquares(*this, other); + + F64 weight = (F64)mNumSamples / (F64)(mNumSamples + other.mNumSamples); + mNumSamples += other.mNumSamples; + mMean = mMean * weight + other.mMean * (1.f - weight); + if (append_type == SEQUENTIAL) mLastValue = other.mLastValue; + } + } } void EventAccumulator::reset( const EventAccumulator* other ) { - mNumSamples = 0; - mSum = 0; - mMin = F32(NaN); - mMax = F32(NaN); - mMean = NaN; - mSumOfSquares = 0; - mLastValue = other ? other->mLastValue : NaN; + mNumSamples = 0; + mSum = 0; + mMin = F32(NaN); + mMax = F32(NaN); + mMean = NaN; + mSumOfSquares = 0; + mLastValue = other ? other->mLastValue : NaN; } } diff --git a/indra/llcommon/lltraceaccumulators.h b/indra/llcommon/lltraceaccumulators.h index b9d577be9e..692bbe5acf 100644 --- a/indra/llcommon/lltraceaccumulators.h +++ b/indra/llcommon/lltraceaccumulators.h @@ -39,515 +39,515 @@ namespace LLTrace { - const F64 NaN = std::numeric_limits<double>::quiet_NaN(); - - enum EBufferAppendType - { - SEQUENTIAL, - NON_SEQUENTIAL - }; - - template<typename ACCUMULATOR> - class AccumulatorBuffer : public LLRefCount - { - typedef AccumulatorBuffer<ACCUMULATOR> self_t; - static const S32 DEFAULT_ACCUMULATOR_BUFFER_SIZE = 32; - private: - struct StaticAllocationMarker { }; - - AccumulatorBuffer(StaticAllocationMarker m) - : mStorageSize(0), - mStorage(NULL) - {} - - public: - AccumulatorBuffer() - : mStorageSize(0), - mStorage(NULL) - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - const AccumulatorBuffer& other = *getDefaultBuffer(); - resize(sNextStorageSlot); - for (S32 i = 0; i < sNextStorageSlot; i++) - { - mStorage[i] = other.mStorage[i]; - } - } - - ~AccumulatorBuffer() - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - if (isCurrent()) - { - LLThreadLocalSingletonPointer<ACCUMULATOR>::setInstance(NULL); - } - delete[] mStorage; - } - - LL_FORCE_INLINE ACCUMULATOR& operator[](size_t index) - { - return mStorage[index]; - } - - LL_FORCE_INLINE const ACCUMULATOR& operator[](size_t index) const - { - return mStorage[index]; - } - - - AccumulatorBuffer(const AccumulatorBuffer& other) - : mStorageSize(0), - mStorage(NULL) - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - resize(sNextStorageSlot); - for (S32 i = 0; i < sNextStorageSlot; i++) - { - mStorage[i] = other.mStorage[i]; - } - } - - void addSamples(const AccumulatorBuffer<ACCUMULATOR>& other, EBufferAppendType append_type) - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - llassert(mStorageSize >= sNextStorageSlot && other.mStorageSize >= sNextStorageSlot); - for (size_t i = 0; i < sNextStorageSlot; i++) - { - mStorage[i].addSamples(other.mStorage[i], append_type); - } - } - - void copyFrom(const AccumulatorBuffer<ACCUMULATOR>& other) - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - llassert(mStorageSize >= sNextStorageSlot && other.mStorageSize >= sNextStorageSlot); - for (size_t i = 0; i < sNextStorageSlot; i++) - { - mStorage[i] = other.mStorage[i]; - } - } - - void reset(const AccumulatorBuffer<ACCUMULATOR>* other = NULL) - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - llassert(mStorageSize >= sNextStorageSlot); - for (size_t i = 0; i < sNextStorageSlot; i++) - { - mStorage[i].reset(other ? &other->mStorage[i] : NULL); - } - } - - void sync(F64SecondsImplicit time_stamp) - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - llassert(mStorageSize >= sNextStorageSlot); - for (size_t i = 0; i < sNextStorageSlot; i++) - { - mStorage[i].sync(time_stamp); - } - } - - void makeCurrent() - { - LLThreadLocalSingletonPointer<ACCUMULATOR>::setInstance(mStorage); - } - - bool isCurrent() const - { - return LLThreadLocalSingletonPointer<ACCUMULATOR>::getInstance() == mStorage; - } - - static void clearCurrent() - { - LLThreadLocalSingletonPointer<ACCUMULATOR>::setInstance(NULL); - } - - // NOTE: this is not thread-safe. We assume that slots are reserved in the main thread before any child threads are spawned - size_t reserveSlot() - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - size_t next_slot = sNextStorageSlot++; - if (next_slot >= mStorageSize) - { - // don't perform doubling, as this should only happen during startup - // want to keep a tight bounds as we will have a lot of these buffers - resize(mStorageSize + mStorageSize / 2); - } - llassert(mStorage && next_slot < mStorageSize); - return next_slot; - } - - void resize(size_t new_size) - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - if (new_size <= mStorageSize) return; - - ACCUMULATOR* old_storage = mStorage; - mStorage = new ACCUMULATOR[new_size]; - if (old_storage) - { - for (S32 i = 0; i < mStorageSize; i++) - { - mStorage[i] = old_storage[i]; - } - } - mStorageSize = new_size; - delete[] old_storage; - - self_t* default_buffer = getDefaultBuffer(); - if (this != default_buffer - && new_size > default_buffer->size()) - { - //NB: this is not thread safe, but we assume that all resizing occurs during static initialization - default_buffer->resize(new_size); - } - } - - size_t size() const - { - return getNumIndices(); - } - - size_t capacity() const - { - return mStorageSize; - } - - static size_t getNumIndices() - { - return sNextStorageSlot; - } - - static self_t* getDefaultBuffer() - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - static bool sInitialized = false; - if (!sInitialized) - { - // this buffer is allowed to leak so that trace calls from global destructors have somewhere to put their data - // so as not to trigger an access violation - sDefaultBuffer = new AccumulatorBuffer(StaticAllocationMarker()); - sInitialized = true; - sDefaultBuffer->resize(DEFAULT_ACCUMULATOR_BUFFER_SIZE); - } - return sDefaultBuffer; - } - - private: - ACCUMULATOR* mStorage; - size_t mStorageSize; - static size_t sNextStorageSlot; - static self_t* sDefaultBuffer; - }; - - template<typename ACCUMULATOR> size_t AccumulatorBuffer<ACCUMULATOR>::sNextStorageSlot = 0; - template<typename ACCUMULATOR> AccumulatorBuffer<ACCUMULATOR>* AccumulatorBuffer<ACCUMULATOR>::sDefaultBuffer = NULL; - - class EventAccumulator - { - public: - typedef F64 value_t; - static F64 getDefaultValue() { return NaN; } - - EventAccumulator() - : mSum(0), - mMin(F32(NaN)), - mMax(F32(NaN)), - mMean(NaN), - mSumOfSquares(0), - mNumSamples(0), - mLastValue(NaN) - {} - - void record(F64 value) - { - if (mNumSamples == 0) - { - mSum = value; - mMean = value; - mMin = value; - mMax = value; - } - else - { - mSum += value; - F64 old_mean = mMean; - mMean += (value - old_mean) / (F64)mNumSamples; - mSumOfSquares += (value - old_mean) * (value - mMean); - - if (value < mMin) { mMin = value; } - else if (value > mMax) { mMax = value; } - } - - mNumSamples++; - mLastValue = value; - } - - void addSamples(const EventAccumulator& other, EBufferAppendType append_type); - void reset(const EventAccumulator* other); - void sync(F64SecondsImplicit) {} - - F64 getSum() const { return mSum; } - F32 getMin() const { return mMin; } - F32 getMax() const { return mMax; } - F64 getLastValue() const { return mLastValue; } - F64 getMean() const { return mMean; } - F64 getStandardDeviation() const { return sqrtf(mSumOfSquares / mNumSamples); } - F64 getSumOfSquares() const { return mSumOfSquares; } - S32 getSampleCount() const { return mNumSamples; } - bool hasValue() const { return mNumSamples > 0; } - - // helper utility to calculate combined sumofsquares total - static F64 mergeSumsOfSquares(const EventAccumulator& a, const EventAccumulator& b); - - private: - F64 mSum, - mLastValue; - - F64 mMean, - mSumOfSquares; - - F32 mMin, - mMax; - - S32 mNumSamples; - }; - - - class SampleAccumulator - { - public: - typedef F64 value_t; - static F64 getDefaultValue() { return NaN; } - - SampleAccumulator() - : mSum(0), - mMin(F32(NaN)), - mMax(F32(NaN)), - mMean(NaN), - mSumOfSquares(0), - mLastSampleTimeStamp(0), - mTotalSamplingTime(0), - mNumSamples(0), - mLastValue(NaN), - mHasValue(false) - {} - - void sample(F64 value) - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - F64SecondsImplicit time_stamp = LLTimer::getTotalSeconds(); - - // store effect of last value - sync(time_stamp); - - if (!mHasValue) - { - mHasValue = true; - - mMin = value; - mMax = value; - mMean = value; - mLastSampleTimeStamp = time_stamp; - } - else - { - if (value < mMin) { mMin = value; } - else if (value > mMax) { mMax = value; } - } - - mLastValue = value; - mNumSamples++; - } - - void addSamples(const SampleAccumulator& other, EBufferAppendType append_type); - void reset(const SampleAccumulator* other); - - void sync(F64SecondsImplicit time_stamp) - { - if (mHasValue && time_stamp != mLastSampleTimeStamp) - { - F64SecondsImplicit delta_time = time_stamp - mLastSampleTimeStamp; - mSum += mLastValue * delta_time; - mTotalSamplingTime += delta_time; - F64 old_mean = mMean; - mMean += (delta_time / mTotalSamplingTime) * (mLastValue - old_mean); - mSumOfSquares += delta_time * (mLastValue - old_mean) * (mLastValue - mMean); - } - mLastSampleTimeStamp = time_stamp; - } - - F64 getSum() const { return mSum; } - F32 getMin() const { return mMin; } - F32 getMax() const { return mMax; } - F64 getLastValue() const { return mLastValue; } - F64 getMean() const { return mMean; } - F64 getStandardDeviation() const { return sqrtf(mSumOfSquares / mTotalSamplingTime); } - F64 getSumOfSquares() const { return mSumOfSquares; } - F64SecondsImplicit getSamplingTime() const { return mTotalSamplingTime; } - S32 getSampleCount() const { return mNumSamples; } - bool hasValue() const { return mHasValue; } - - // helper utility to calculate combined sumofsquares total - static F64 mergeSumsOfSquares(const SampleAccumulator& a, const SampleAccumulator& b); - - private: - F64 mSum, - mLastValue; - - F64 mMean, - mSumOfSquares; - - F64SecondsImplicit - mLastSampleTimeStamp, - mTotalSamplingTime; - - F32 mMin, - mMax; - - S32 mNumSamples; - // distinct from mNumSamples, since we might have inherited a last value from - // a previous sampling period - bool mHasValue; - }; - - class CountAccumulator - { - public: - typedef F64 value_t; - static F64 getDefaultValue() { return 0; } - - CountAccumulator() - : mSum(0), - mNumSamples(0) - {} - - void add(F64 value) - { - mNumSamples++; - mSum += value; - } - - void addSamples(const CountAccumulator& other, EBufferAppendType /*type*/) - { - mSum += other.mSum; - mNumSamples += other.mNumSamples; - } - - void reset(const CountAccumulator* other) - { - mNumSamples = 0; - mSum = 0; - } - - void sync(F64SecondsImplicit) {} - - F64 getSum() const { return mSum; } - - S32 getSampleCount() const { return mNumSamples; } - - bool hasValue() const { return true; } - - private: - F64 mSum; - - S32 mNumSamples; - }; - - class alignas(32) TimeBlockAccumulator - { - public: - typedef F64Seconds value_t; - static F64Seconds getDefaultValue() { return F64Seconds(0); } - - typedef TimeBlockAccumulator self_t; - - // fake classes that allows us to view different facets of underlying statistic - struct CallCountFacet - { - typedef S32 value_t; - }; - - struct SelfTimeFacet - { - typedef F64Seconds value_t; - }; - - // arrays are allocated with 32 byte alignment - void *operator new [](size_t size) - { - return ll_aligned_malloc<32>(size); - } - - void operator delete[](void* ptr, size_t size) - { - ll_aligned_free<32>(ptr); - } - - TimeBlockAccumulator(); - void addSamples(const self_t& other, EBufferAppendType append_type); - void reset(const self_t* other); - void sync(F64SecondsImplicit) {} - bool hasValue() const { return true; } - - // - // members - // - U64 mTotalTimeCounter, - mSelfTimeCounter; - S32 mCalls; - class BlockTimerStatHandle* mParent; // last acknowledged parent of this time block - class BlockTimerStatHandle* mLastCaller; // used to bootstrap tree construction - 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 - - }; - - class BlockTimerStatHandle; - - class TimeBlockTreeNode - { - public: - TimeBlockTreeNode(); - - void setParent(BlockTimerStatHandle* parent); - BlockTimerStatHandle* getParent() { return mParent; } - - BlockTimerStatHandle* mBlock; - BlockTimerStatHandle* mParent; - std::vector<BlockTimerStatHandle*> mChildren; - bool mCollapsed; - bool mNeedsSorting; - }; - - struct BlockTimerStackRecord - { - class BlockTimer* mActiveTimer; - class BlockTimerStatHandle* mTimeBlock; - U64 mChildTime; - }; - - struct AccumulatorBufferGroup : public LLRefCount - { - AccumulatorBufferGroup(); - AccumulatorBufferGroup(const AccumulatorBufferGroup&); - ~AccumulatorBufferGroup(); - - void handOffTo(AccumulatorBufferGroup& other); - void makeCurrent(); - bool isCurrent() const; - static void clearCurrent(); - - void append(const AccumulatorBufferGroup& other); - void merge(const AccumulatorBufferGroup& other); - void reset(AccumulatorBufferGroup* other = NULL); - void sync(); - - AccumulatorBuffer<CountAccumulator> mCounts; - AccumulatorBuffer<SampleAccumulator> mSamples; - AccumulatorBuffer<EventAccumulator> mEvents; - AccumulatorBuffer<TimeBlockAccumulator> mStackTimers; - }; + const F64 NaN = std::numeric_limits<double>::quiet_NaN(); + + enum EBufferAppendType + { + SEQUENTIAL, + NON_SEQUENTIAL + }; + + template<typename ACCUMULATOR> + class AccumulatorBuffer : public LLRefCount + { + typedef AccumulatorBuffer<ACCUMULATOR> self_t; + static const S32 DEFAULT_ACCUMULATOR_BUFFER_SIZE = 32; + private: + struct StaticAllocationMarker { }; + + AccumulatorBuffer(StaticAllocationMarker m) + : mStorageSize(0), + mStorage(NULL) + {} + + public: + AccumulatorBuffer() + : mStorageSize(0), + mStorage(NULL) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + const AccumulatorBuffer& other = *getDefaultBuffer(); + resize(sNextStorageSlot); + for (S32 i = 0; i < sNextStorageSlot; i++) + { + mStorage[i] = other.mStorage[i]; + } + } + + ~AccumulatorBuffer() + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + if (isCurrent()) + { + LLThreadLocalSingletonPointer<ACCUMULATOR>::setInstance(NULL); + } + delete[] mStorage; + } + + LL_FORCE_INLINE ACCUMULATOR& operator[](size_t index) + { + return mStorage[index]; + } + + LL_FORCE_INLINE const ACCUMULATOR& operator[](size_t index) const + { + return mStorage[index]; + } + + + AccumulatorBuffer(const AccumulatorBuffer& other) + : mStorageSize(0), + mStorage(NULL) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + resize(sNextStorageSlot); + for (S32 i = 0; i < sNextStorageSlot; i++) + { + mStorage[i] = other.mStorage[i]; + } + } + + void addSamples(const AccumulatorBuffer<ACCUMULATOR>& other, EBufferAppendType append_type) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + llassert(mStorageSize >= sNextStorageSlot && other.mStorageSize >= sNextStorageSlot); + for (size_t i = 0; i < sNextStorageSlot; i++) + { + mStorage[i].addSamples(other.mStorage[i], append_type); + } + } + + void copyFrom(const AccumulatorBuffer<ACCUMULATOR>& other) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + llassert(mStorageSize >= sNextStorageSlot && other.mStorageSize >= sNextStorageSlot); + for (size_t i = 0; i < sNextStorageSlot; i++) + { + mStorage[i] = other.mStorage[i]; + } + } + + void reset(const AccumulatorBuffer<ACCUMULATOR>* other = NULL) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + llassert(mStorageSize >= sNextStorageSlot); + for (size_t i = 0; i < sNextStorageSlot; i++) + { + mStorage[i].reset(other ? &other->mStorage[i] : NULL); + } + } + + void sync(F64SecondsImplicit time_stamp) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + llassert(mStorageSize >= sNextStorageSlot); + for (size_t i = 0; i < sNextStorageSlot; i++) + { + mStorage[i].sync(time_stamp); + } + } + + void makeCurrent() + { + LLThreadLocalSingletonPointer<ACCUMULATOR>::setInstance(mStorage); + } + + bool isCurrent() const + { + return LLThreadLocalSingletonPointer<ACCUMULATOR>::getInstance() == mStorage; + } + + static void clearCurrent() + { + LLThreadLocalSingletonPointer<ACCUMULATOR>::setInstance(NULL); + } + + // NOTE: this is not thread-safe. We assume that slots are reserved in the main thread before any child threads are spawned + size_t reserveSlot() + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + size_t next_slot = sNextStorageSlot++; + if (next_slot >= mStorageSize) + { + // don't perform doubling, as this should only happen during startup + // want to keep a tight bounds as we will have a lot of these buffers + resize(mStorageSize + mStorageSize / 2); + } + llassert(mStorage && next_slot < mStorageSize); + return next_slot; + } + + void resize(size_t new_size) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + if (new_size <= mStorageSize) return; + + ACCUMULATOR* old_storage = mStorage; + mStorage = new ACCUMULATOR[new_size]; + if (old_storage) + { + for (S32 i = 0; i < mStorageSize; i++) + { + mStorage[i] = old_storage[i]; + } + } + mStorageSize = new_size; + delete[] old_storage; + + self_t* default_buffer = getDefaultBuffer(); + if (this != default_buffer + && new_size > default_buffer->size()) + { + //NB: this is not thread safe, but we assume that all resizing occurs during static initialization + default_buffer->resize(new_size); + } + } + + size_t size() const + { + return getNumIndices(); + } + + size_t capacity() const + { + return mStorageSize; + } + + static size_t getNumIndices() + { + return sNextStorageSlot; + } + + static self_t* getDefaultBuffer() + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + static bool sInitialized = false; + if (!sInitialized) + { + // this buffer is allowed to leak so that trace calls from global destructors have somewhere to put their data + // so as not to trigger an access violation + sDefaultBuffer = new AccumulatorBuffer(StaticAllocationMarker()); + sInitialized = true; + sDefaultBuffer->resize(DEFAULT_ACCUMULATOR_BUFFER_SIZE); + } + return sDefaultBuffer; + } + + private: + ACCUMULATOR* mStorage; + size_t mStorageSize; + static size_t sNextStorageSlot; + static self_t* sDefaultBuffer; + }; + + template<typename ACCUMULATOR> size_t AccumulatorBuffer<ACCUMULATOR>::sNextStorageSlot = 0; + template<typename ACCUMULATOR> AccumulatorBuffer<ACCUMULATOR>* AccumulatorBuffer<ACCUMULATOR>::sDefaultBuffer = NULL; + + class EventAccumulator + { + public: + typedef F64 value_t; + static F64 getDefaultValue() { return NaN; } + + EventAccumulator() + : mSum(0), + mMin(F32(NaN)), + mMax(F32(NaN)), + mMean(NaN), + mSumOfSquares(0), + mNumSamples(0), + mLastValue(NaN) + {} + + void record(F64 value) + { + if (mNumSamples == 0) + { + mSum = value; + mMean = value; + mMin = value; + mMax = value; + } + else + { + mSum += value; + F64 old_mean = mMean; + mMean += (value - old_mean) / (F64)mNumSamples; + mSumOfSquares += (value - old_mean) * (value - mMean); + + if (value < mMin) { mMin = value; } + else if (value > mMax) { mMax = value; } + } + + mNumSamples++; + mLastValue = value; + } + + void addSamples(const EventAccumulator& other, EBufferAppendType append_type); + void reset(const EventAccumulator* other); + void sync(F64SecondsImplicit) {} + + F64 getSum() const { return mSum; } + F32 getMin() const { return mMin; } + F32 getMax() const { return mMax; } + F64 getLastValue() const { return mLastValue; } + F64 getMean() const { return mMean; } + F64 getStandardDeviation() const { return sqrtf(mSumOfSquares / mNumSamples); } + F64 getSumOfSquares() const { return mSumOfSquares; } + S32 getSampleCount() const { return mNumSamples; } + bool hasValue() const { return mNumSamples > 0; } + + // helper utility to calculate combined sumofsquares total + static F64 mergeSumsOfSquares(const EventAccumulator& a, const EventAccumulator& b); + + private: + F64 mSum, + mLastValue; + + F64 mMean, + mSumOfSquares; + + F32 mMin, + mMax; + + S32 mNumSamples; + }; + + + class SampleAccumulator + { + public: + typedef F64 value_t; + static F64 getDefaultValue() { return NaN; } + + SampleAccumulator() + : mSum(0), + mMin(F32(NaN)), + mMax(F32(NaN)), + mMean(NaN), + mSumOfSquares(0), + mLastSampleTimeStamp(0), + mTotalSamplingTime(0), + mNumSamples(0), + mLastValue(NaN), + mHasValue(false) + {} + + void sample(F64 value) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + F64SecondsImplicit time_stamp = LLTimer::getTotalSeconds(); + + // store effect of last value + sync(time_stamp); + + if (!mHasValue) + { + mHasValue = true; + + mMin = value; + mMax = value; + mMean = value; + mLastSampleTimeStamp = time_stamp; + } + else + { + if (value < mMin) { mMin = value; } + else if (value > mMax) { mMax = value; } + } + + mLastValue = value; + mNumSamples++; + } + + void addSamples(const SampleAccumulator& other, EBufferAppendType append_type); + void reset(const SampleAccumulator* other); + + void sync(F64SecondsImplicit time_stamp) + { + if (mHasValue && time_stamp != mLastSampleTimeStamp) + { + F64SecondsImplicit delta_time = time_stamp - mLastSampleTimeStamp; + mSum += mLastValue * delta_time; + mTotalSamplingTime += delta_time; + F64 old_mean = mMean; + mMean += (delta_time / mTotalSamplingTime) * (mLastValue - old_mean); + mSumOfSquares += delta_time * (mLastValue - old_mean) * (mLastValue - mMean); + } + mLastSampleTimeStamp = time_stamp; + } + + F64 getSum() const { return mSum; } + F32 getMin() const { return mMin; } + F32 getMax() const { return mMax; } + F64 getLastValue() const { return mLastValue; } + F64 getMean() const { return mMean; } + F64 getStandardDeviation() const { return sqrtf(mSumOfSquares / mTotalSamplingTime); } + F64 getSumOfSquares() const { return mSumOfSquares; } + F64SecondsImplicit getSamplingTime() const { return mTotalSamplingTime; } + S32 getSampleCount() const { return mNumSamples; } + bool hasValue() const { return mHasValue; } + + // helper utility to calculate combined sumofsquares total + static F64 mergeSumsOfSquares(const SampleAccumulator& a, const SampleAccumulator& b); + + private: + F64 mSum, + mLastValue; + + F64 mMean, + mSumOfSquares; + + F64SecondsImplicit + mLastSampleTimeStamp, + mTotalSamplingTime; + + F32 mMin, + mMax; + + S32 mNumSamples; + // distinct from mNumSamples, since we might have inherited a last value from + // a previous sampling period + bool mHasValue; + }; + + class CountAccumulator + { + public: + typedef F64 value_t; + static F64 getDefaultValue() { return 0; } + + CountAccumulator() + : mSum(0), + mNumSamples(0) + {} + + void add(F64 value) + { + mNumSamples++; + mSum += value; + } + + void addSamples(const CountAccumulator& other, EBufferAppendType /*type*/) + { + mSum += other.mSum; + mNumSamples += other.mNumSamples; + } + + void reset(const CountAccumulator* other) + { + mNumSamples = 0; + mSum = 0; + } + + void sync(F64SecondsImplicit) {} + + F64 getSum() const { return mSum; } + + S32 getSampleCount() const { return mNumSamples; } + + bool hasValue() const { return true; } + + private: + F64 mSum; + + S32 mNumSamples; + }; + + class alignas(32) TimeBlockAccumulator + { + public: + typedef F64Seconds value_t; + static F64Seconds getDefaultValue() { return F64Seconds(0); } + + typedef TimeBlockAccumulator self_t; + + // fake classes that allows us to view different facets of underlying statistic + struct CallCountFacet + { + typedef S32 value_t; + }; + + struct SelfTimeFacet + { + typedef F64Seconds value_t; + }; + + // arrays are allocated with 32 byte alignment + void *operator new [](size_t size) + { + return ll_aligned_malloc<32>(size); + } + + void operator delete[](void* ptr, size_t size) + { + ll_aligned_free<32>(ptr); + } + + TimeBlockAccumulator(); + void addSamples(const self_t& other, EBufferAppendType append_type); + void reset(const self_t* other); + void sync(F64SecondsImplicit) {} + bool hasValue() const { return true; } + + // + // members + // + U64 mTotalTimeCounter, + mSelfTimeCounter; + S32 mCalls; + class BlockTimerStatHandle* mParent; // last acknowledged parent of this time block + class BlockTimerStatHandle* mLastCaller; // used to bootstrap tree construction + 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 + + }; + + class BlockTimerStatHandle; + + class TimeBlockTreeNode + { + public: + TimeBlockTreeNode(); + + void setParent(BlockTimerStatHandle* parent); + BlockTimerStatHandle* getParent() { return mParent; } + + BlockTimerStatHandle* mBlock; + BlockTimerStatHandle* mParent; + std::vector<BlockTimerStatHandle*> mChildren; + bool mCollapsed; + bool mNeedsSorting; + }; + + struct BlockTimerStackRecord + { + class BlockTimer* mActiveTimer; + class BlockTimerStatHandle* mTimeBlock; + U64 mChildTime; + }; + + struct AccumulatorBufferGroup : public LLRefCount + { + AccumulatorBufferGroup(); + AccumulatorBufferGroup(const AccumulatorBufferGroup&); + ~AccumulatorBufferGroup(); + + void handOffTo(AccumulatorBufferGroup& other); + void makeCurrent(); + bool isCurrent() const; + static void clearCurrent(); + + void append(const AccumulatorBufferGroup& other); + void merge(const AccumulatorBufferGroup& other); + void reset(AccumulatorBufferGroup* other = NULL); + void sync(); + + AccumulatorBuffer<CountAccumulator> mCounts; + AccumulatorBuffer<SampleAccumulator> mSamples; + AccumulatorBuffer<EventAccumulator> mEvents; + AccumulatorBuffer<TimeBlockAccumulator> mStackTimers; + }; } #endif // LL_LLTRACEACCUMULATORS_H diff --git a/indra/llcommon/lltracerecording.cpp b/indra/llcommon/lltracerecording.cpp index 075e7c1d28..1ec83be7cb 100644 --- a/indra/llcommon/lltracerecording.cpp +++ b/indra/llcommon/lltracerecording.cpp @@ -34,7 +34,7 @@ inline F64 lerp(F64 a, F64 b, F64 u) { - return a + ((b - a) * u); + return a + ((b - a) * u); } namespace LLTrace @@ -45,388 +45,388 @@ namespace LLTrace /////////////////////////////////////////////////////////////////////// Recording::Recording(EPlayState state) -: mElapsedSeconds(0), - mActiveBuffers(NULL) +: mElapsedSeconds(0), + mActiveBuffers(NULL) { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - mBuffers = new AccumulatorBufferGroup(); - setPlayState(state); + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + mBuffers = new AccumulatorBufferGroup(); + setPlayState(state); } Recording::Recording( const Recording& other ) -: mActiveBuffers(NULL) +: mActiveBuffers(NULL) { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - *this = other; + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + *this = other; } Recording& Recording::operator = (const Recording& other) { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - // this will allow us to seamlessly start without affecting any data we've acquired from other - setPlayState(PAUSED); + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + // this will allow us to seamlessly start without affecting any data we've acquired from other + setPlayState(PAUSED); - const_cast<Recording&>(other).update(); - EPlayState other_play_state = other.getPlayState(); + const_cast<Recording&>(other).update(); + EPlayState other_play_state = other.getPlayState(); - mBuffers = other.mBuffers; + mBuffers = other.mBuffers; - // above call will clear mElapsedSeconds as a side effect, so copy it here - mElapsedSeconds = other.mElapsedSeconds; - mSamplingTimer = other.mSamplingTimer; + // above call will clear mElapsedSeconds as a side effect, so copy it here + mElapsedSeconds = other.mElapsedSeconds; + mSamplingTimer = other.mSamplingTimer; - setPlayState(other_play_state); + setPlayState(other_play_state); - return *this; + return *this; } Recording::~Recording() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - // allow recording destruction without thread recorder running, - // otherwise thread shutdown could crash if a recording outlives the thread recorder - // besides, recording construction and destruction is fine without a recorder...just don't attempt to start one - if (isStarted() && LLTrace::get_thread_recorder() != NULL) - { - LLTrace::get_thread_recorder()->deactivate(mBuffers.write()); - } + // allow recording destruction without thread recorder running, + // otherwise thread shutdown could crash if a recording outlives the thread recorder + // besides, recording construction and destruction is fine without a recorder...just don't attempt to start one + if (isStarted() && LLTrace::get_thread_recorder() != NULL) + { + LLTrace::get_thread_recorder()->deactivate(mBuffers.write()); + } } // brings recording to front of recorder stack, with up to date info void Recording::update() { #if LL_TRACE_ENABLED - if (isStarted()) - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - mElapsedSeconds += mSamplingTimer.getElapsedTimeF64(); - - // must have - llassert(mActiveBuffers != NULL - && LLTrace::get_thread_recorder() != NULL); - - if (!mActiveBuffers->isCurrent() && LLTrace::get_thread_recorder() != NULL) - { - AccumulatorBufferGroup* buffers = mBuffers.write(); - LLTrace::get_thread_recorder()->deactivate(buffers); - mActiveBuffers = LLTrace::get_thread_recorder()->activate(buffers); - } - - mSamplingTimer.reset(); - } + if (isStarted()) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + mElapsedSeconds += mSamplingTimer.getElapsedTimeF64(); + + // must have + llassert(mActiveBuffers != NULL + && LLTrace::get_thread_recorder() != NULL); + + if (!mActiveBuffers->isCurrent() && LLTrace::get_thread_recorder() != NULL) + { + AccumulatorBufferGroup* buffers = mBuffers.write(); + LLTrace::get_thread_recorder()->deactivate(buffers); + mActiveBuffers = LLTrace::get_thread_recorder()->activate(buffers); + } + + mSamplingTimer.reset(); + } #endif } void Recording::handleReset() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; #if LL_TRACE_ENABLED - mBuffers.write()->reset(); + mBuffers.write()->reset(); - mElapsedSeconds = F64Seconds(0.0); - mSamplingTimer.reset(); + mElapsedSeconds = F64Seconds(0.0); + mSamplingTimer.reset(); #endif } void Recording::handleStart() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; #if LL_TRACE_ENABLED - mSamplingTimer.reset(); - mBuffers.setStayUnique(true); - // must have thread recorder running on this thread - llassert(LLTrace::get_thread_recorder() != NULL); - mActiveBuffers = LLTrace::get_thread_recorder()->activate(mBuffers.write()); + mSamplingTimer.reset(); + mBuffers.setStayUnique(true); + // must have thread recorder running on this thread + llassert(LLTrace::get_thread_recorder() != NULL); + mActiveBuffers = LLTrace::get_thread_recorder()->activate(mBuffers.write()); #endif } void Recording::handleStop() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; #if LL_TRACE_ENABLED - mElapsedSeconds += mSamplingTimer.getElapsedTimeF64(); - // must have thread recorder running on this thread - llassert(LLTrace::get_thread_recorder() != NULL); - LLTrace::get_thread_recorder()->deactivate(mBuffers.write()); - mActiveBuffers = NULL; - mBuffers.setStayUnique(false); + mElapsedSeconds += mSamplingTimer.getElapsedTimeF64(); + // must have thread recorder running on this thread + llassert(LLTrace::get_thread_recorder() != NULL); + LLTrace::get_thread_recorder()->deactivate(mBuffers.write()); + mActiveBuffers = NULL; + mBuffers.setStayUnique(false); #endif } void Recording::handleSplitTo(Recording& other) { #if LL_TRACE_ENABLED - mBuffers.write()->handOffTo(*other.mBuffers.write()); + mBuffers.write()->handOffTo(*other.mBuffers.write()); #endif } void Recording::appendRecording( Recording& other ) { #if LL_TRACE_ENABLED - update(); - other.update(); - mBuffers.write()->append(*other.mBuffers); - mElapsedSeconds += other.mElapsedSeconds; + update(); + other.update(); + mBuffers.write()->append(*other.mBuffers); + mElapsedSeconds += other.mElapsedSeconds; #endif } bool Recording::hasValue(const StatType<TimeBlockAccumulator>& stat) { - update(); - const TimeBlockAccumulator& accumulator = mBuffers->mStackTimers[stat.getIndex()]; - const TimeBlockAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mStackTimers[stat.getIndex()] : NULL; - return accumulator.hasValue() || (active_accumulator && active_accumulator->hasValue()); + update(); + const TimeBlockAccumulator& accumulator = mBuffers->mStackTimers[stat.getIndex()]; + const TimeBlockAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mStackTimers[stat.getIndex()] : NULL; + return accumulator.hasValue() || (active_accumulator && active_accumulator->hasValue()); } F64Seconds Recording::getSum(const StatType<TimeBlockAccumulator>& stat) { - update(); - const TimeBlockAccumulator& accumulator = mBuffers->mStackTimers[stat.getIndex()]; - const TimeBlockAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mStackTimers[stat.getIndex()] : NULL; - return F64Seconds((F64)(accumulator.mTotalTimeCounter) + (F64)(active_accumulator ? active_accumulator->mTotalTimeCounter : 0)) - / (F64)LLTrace::BlockTimer::countsPerSecond(); + update(); + const TimeBlockAccumulator& accumulator = mBuffers->mStackTimers[stat.getIndex()]; + const TimeBlockAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mStackTimers[stat.getIndex()] : NULL; + return F64Seconds((F64)(accumulator.mTotalTimeCounter) + (F64)(active_accumulator ? active_accumulator->mTotalTimeCounter : 0)) + / (F64)LLTrace::BlockTimer::countsPerSecond(); } F64Seconds Recording::getSum(const StatType<TimeBlockAccumulator::SelfTimeFacet>& stat) { - update(); - const TimeBlockAccumulator& accumulator = mBuffers->mStackTimers[stat.getIndex()]; - const TimeBlockAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mStackTimers[stat.getIndex()] : NULL; - return F64Seconds(((F64)(accumulator.mSelfTimeCounter) + (F64)(active_accumulator ? active_accumulator->mSelfTimeCounter : 0)) / (F64)LLTrace::BlockTimer::countsPerSecond()); + update(); + const TimeBlockAccumulator& accumulator = mBuffers->mStackTimers[stat.getIndex()]; + const TimeBlockAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mStackTimers[stat.getIndex()] : NULL; + return F64Seconds(((F64)(accumulator.mSelfTimeCounter) + (F64)(active_accumulator ? active_accumulator->mSelfTimeCounter : 0)) / (F64)LLTrace::BlockTimer::countsPerSecond()); } S32 Recording::getSum(const StatType<TimeBlockAccumulator::CallCountFacet>& stat) { - update(); - const TimeBlockAccumulator& accumulator = mBuffers->mStackTimers[stat.getIndex()]; - const TimeBlockAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mStackTimers[stat.getIndex()] : NULL; - return accumulator.mCalls + (active_accumulator ? active_accumulator->mCalls : 0); + update(); + const TimeBlockAccumulator& accumulator = mBuffers->mStackTimers[stat.getIndex()]; + const TimeBlockAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mStackTimers[stat.getIndex()] : NULL; + return accumulator.mCalls + (active_accumulator ? active_accumulator->mCalls : 0); } F64Seconds Recording::getPerSec(const StatType<TimeBlockAccumulator>& stat) { - update(); - const TimeBlockAccumulator& accumulator = mBuffers->mStackTimers[stat.getIndex()]; - const TimeBlockAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mStackTimers[stat.getIndex()] : NULL; + update(); + const TimeBlockAccumulator& accumulator = mBuffers->mStackTimers[stat.getIndex()]; + const TimeBlockAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mStackTimers[stat.getIndex()] : NULL; - return F64Seconds((F64)(accumulator.mTotalTimeCounter + (active_accumulator ? active_accumulator->mTotalTimeCounter : 0)) - / ((F64)LLTrace::BlockTimer::countsPerSecond() * mElapsedSeconds.value())); + return F64Seconds((F64)(accumulator.mTotalTimeCounter + (active_accumulator ? active_accumulator->mTotalTimeCounter : 0)) + / ((F64)LLTrace::BlockTimer::countsPerSecond() * mElapsedSeconds.value())); } F64Seconds Recording::getPerSec(const StatType<TimeBlockAccumulator::SelfTimeFacet>& stat) { - update(); - const TimeBlockAccumulator& accumulator = mBuffers->mStackTimers[stat.getIndex()]; - const TimeBlockAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mStackTimers[stat.getIndex()] : NULL; + update(); + const TimeBlockAccumulator& accumulator = mBuffers->mStackTimers[stat.getIndex()]; + const TimeBlockAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mStackTimers[stat.getIndex()] : NULL; - return F64Seconds((F64)(accumulator.mSelfTimeCounter + (active_accumulator ? active_accumulator->mSelfTimeCounter : 0)) - / ((F64)LLTrace::BlockTimer::countsPerSecond() * mElapsedSeconds.value())); + return F64Seconds((F64)(accumulator.mSelfTimeCounter + (active_accumulator ? active_accumulator->mSelfTimeCounter : 0)) + / ((F64)LLTrace::BlockTimer::countsPerSecond() * mElapsedSeconds.value())); } F32 Recording::getPerSec(const StatType<TimeBlockAccumulator::CallCountFacet>& stat) { - update(); - const TimeBlockAccumulator& accumulator = mBuffers->mStackTimers[stat.getIndex()]; - const TimeBlockAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mStackTimers[stat.getIndex()] : NULL; - return (F32)(accumulator.mCalls + (active_accumulator ? active_accumulator->mCalls : 0)) / mElapsedSeconds.value(); + update(); + const TimeBlockAccumulator& accumulator = mBuffers->mStackTimers[stat.getIndex()]; + const TimeBlockAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mStackTimers[stat.getIndex()] : NULL; + return (F32)(accumulator.mCalls + (active_accumulator ? active_accumulator->mCalls : 0)) / mElapsedSeconds.value(); } bool Recording::hasValue(const StatType<CountAccumulator>& stat) { - update(); - const CountAccumulator& accumulator = mBuffers->mCounts[stat.getIndex()]; - const CountAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mCounts[stat.getIndex()] : NULL; - return accumulator.hasValue() || (active_accumulator ? active_accumulator->hasValue() : false); + update(); + const CountAccumulator& accumulator = mBuffers->mCounts[stat.getIndex()]; + const CountAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mCounts[stat.getIndex()] : NULL; + return accumulator.hasValue() || (active_accumulator ? active_accumulator->hasValue() : false); } F64 Recording::getSum(const StatType<CountAccumulator>& stat) { - update(); - const CountAccumulator& accumulator = mBuffers->mCounts[stat.getIndex()]; - const CountAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mCounts[stat.getIndex()] : NULL; - return accumulator.getSum() + (active_accumulator ? active_accumulator->getSum() : 0); + update(); + const CountAccumulator& accumulator = mBuffers->mCounts[stat.getIndex()]; + const CountAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mCounts[stat.getIndex()] : NULL; + return accumulator.getSum() + (active_accumulator ? active_accumulator->getSum() : 0); } F64 Recording::getPerSec( const StatType<CountAccumulator>& stat ) { - update(); - const CountAccumulator& accumulator = mBuffers->mCounts[stat.getIndex()]; - const CountAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mCounts[stat.getIndex()] : NULL; - F64 sum = accumulator.getSum() + (active_accumulator ? active_accumulator->getSum() : 0); - return sum / mElapsedSeconds.value(); + update(); + const CountAccumulator& accumulator = mBuffers->mCounts[stat.getIndex()]; + const CountAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mCounts[stat.getIndex()] : NULL; + F64 sum = accumulator.getSum() + (active_accumulator ? active_accumulator->getSum() : 0); + return sum / mElapsedSeconds.value(); } S32 Recording::getSampleCount( const StatType<CountAccumulator>& stat ) { - update(); - const CountAccumulator& accumulator = mBuffers->mCounts[stat.getIndex()]; - const CountAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mCounts[stat.getIndex()] : NULL; - return accumulator.getSampleCount() + (active_accumulator ? active_accumulator->getSampleCount() : 0); + update(); + const CountAccumulator& accumulator = mBuffers->mCounts[stat.getIndex()]; + const CountAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mCounts[stat.getIndex()] : NULL; + return accumulator.getSampleCount() + (active_accumulator ? active_accumulator->getSampleCount() : 0); } bool Recording::hasValue(const StatType<SampleAccumulator>& stat) { - update(); - const SampleAccumulator& accumulator = mBuffers->mSamples[stat.getIndex()]; - const SampleAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mSamples[stat.getIndex()] : NULL; - return accumulator.hasValue() || (active_accumulator && active_accumulator->hasValue()); + update(); + const SampleAccumulator& accumulator = mBuffers->mSamples[stat.getIndex()]; + const SampleAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mSamples[stat.getIndex()] : NULL; + return accumulator.hasValue() || (active_accumulator && active_accumulator->hasValue()); } F64 Recording::getMin( const StatType<SampleAccumulator>& stat ) { - update(); - const SampleAccumulator& accumulator = mBuffers->mSamples[stat.getIndex()]; - const SampleAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mSamples[stat.getIndex()] : NULL; - return llmin(accumulator.getMin(), active_accumulator && active_accumulator->hasValue() ? active_accumulator->getMin() : F32_MAX); + update(); + const SampleAccumulator& accumulator = mBuffers->mSamples[stat.getIndex()]; + const SampleAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mSamples[stat.getIndex()] : NULL; + return llmin(accumulator.getMin(), active_accumulator && active_accumulator->hasValue() ? active_accumulator->getMin() : F32_MAX); } F64 Recording::getMax( const StatType<SampleAccumulator>& stat ) { - update(); - const SampleAccumulator& accumulator = mBuffers->mSamples[stat.getIndex()]; - const SampleAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mSamples[stat.getIndex()] : NULL; - return llmax(accumulator.getMax(), active_accumulator && active_accumulator->hasValue() ? active_accumulator->getMax() : F32_MIN); + update(); + const SampleAccumulator& accumulator = mBuffers->mSamples[stat.getIndex()]; + const SampleAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mSamples[stat.getIndex()] : NULL; + return llmax(accumulator.getMax(), active_accumulator && active_accumulator->hasValue() ? active_accumulator->getMax() : F32_MIN); } F64 Recording::getMean( const StatType<SampleAccumulator>& stat ) { - update(); - const SampleAccumulator& accumulator = mBuffers->mSamples[stat.getIndex()]; - const SampleAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mSamples[stat.getIndex()] : NULL; - if (active_accumulator && active_accumulator->hasValue()) - { - F32 t = 0.0f; - S32 div = accumulator.getSampleCount() + active_accumulator->getSampleCount(); - if (div > 0) - { - t = active_accumulator->getSampleCount() / div; - } - return lerp(accumulator.getMean(), active_accumulator->getMean(), t); - } - else - { - return accumulator.getMean(); - } + update(); + const SampleAccumulator& accumulator = mBuffers->mSamples[stat.getIndex()]; + const SampleAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mSamples[stat.getIndex()] : NULL; + if (active_accumulator && active_accumulator->hasValue()) + { + F32 t = 0.0f; + S32 div = accumulator.getSampleCount() + active_accumulator->getSampleCount(); + if (div > 0) + { + t = active_accumulator->getSampleCount() / div; + } + return lerp(accumulator.getMean(), active_accumulator->getMean(), t); + } + else + { + return accumulator.getMean(); + } } F64 Recording::getStandardDeviation( const StatType<SampleAccumulator>& stat ) { - update(); - const SampleAccumulator& accumulator = mBuffers->mSamples[stat.getIndex()]; - const SampleAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mSamples[stat.getIndex()] : NULL; + update(); + const SampleAccumulator& accumulator = mBuffers->mSamples[stat.getIndex()]; + const SampleAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mSamples[stat.getIndex()] : NULL; - if (active_accumulator && active_accumulator->hasValue()) - { - F64 sum_of_squares = SampleAccumulator::mergeSumsOfSquares(accumulator, *active_accumulator); - return sqrtf(sum_of_squares / (accumulator.getSamplingTime() + active_accumulator->getSamplingTime())); - } - else - { - return accumulator.getStandardDeviation(); - } + if (active_accumulator && active_accumulator->hasValue()) + { + F64 sum_of_squares = SampleAccumulator::mergeSumsOfSquares(accumulator, *active_accumulator); + return sqrtf(sum_of_squares / (accumulator.getSamplingTime() + active_accumulator->getSamplingTime())); + } + else + { + return accumulator.getStandardDeviation(); + } } F64 Recording::getLastValue( const StatType<SampleAccumulator>& stat ) { - update(); - const SampleAccumulator& accumulator = mBuffers->mSamples[stat.getIndex()]; - const SampleAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mSamples[stat.getIndex()] : NULL; - return (active_accumulator && active_accumulator->hasValue() ? active_accumulator->getLastValue() : accumulator.getLastValue()); + update(); + const SampleAccumulator& accumulator = mBuffers->mSamples[stat.getIndex()]; + const SampleAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mSamples[stat.getIndex()] : NULL; + return (active_accumulator && active_accumulator->hasValue() ? active_accumulator->getLastValue() : accumulator.getLastValue()); } S32 Recording::getSampleCount( const StatType<SampleAccumulator>& stat ) { - update(); - const SampleAccumulator& accumulator = mBuffers->mSamples[stat.getIndex()]; - const SampleAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mSamples[stat.getIndex()] : NULL; - return accumulator.getSampleCount() + (active_accumulator && active_accumulator->hasValue() ? active_accumulator->getSampleCount() : 0); + update(); + const SampleAccumulator& accumulator = mBuffers->mSamples[stat.getIndex()]; + const SampleAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mSamples[stat.getIndex()] : NULL; + return accumulator.getSampleCount() + (active_accumulator && active_accumulator->hasValue() ? active_accumulator->getSampleCount() : 0); } bool Recording::hasValue(const StatType<EventAccumulator>& stat) { - update(); - const EventAccumulator& accumulator = mBuffers->mEvents[stat.getIndex()]; - const EventAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mEvents[stat.getIndex()] : NULL; - return accumulator.hasValue() || (active_accumulator && active_accumulator->hasValue()); + update(); + const EventAccumulator& accumulator = mBuffers->mEvents[stat.getIndex()]; + const EventAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mEvents[stat.getIndex()] : NULL; + return accumulator.hasValue() || (active_accumulator && active_accumulator->hasValue()); } F64 Recording::getSum( const StatType<EventAccumulator>& stat) { - update(); - const EventAccumulator& accumulator = mBuffers->mEvents[stat.getIndex()]; - const EventAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mEvents[stat.getIndex()] : NULL; - return (F64)(accumulator.getSum() + (active_accumulator && active_accumulator->hasValue() ? active_accumulator->getSum() : 0)); + update(); + const EventAccumulator& accumulator = mBuffers->mEvents[stat.getIndex()]; + const EventAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mEvents[stat.getIndex()] : NULL; + return (F64)(accumulator.getSum() + (active_accumulator && active_accumulator->hasValue() ? active_accumulator->getSum() : 0)); } F64 Recording::getMin( const StatType<EventAccumulator>& stat ) { - update(); - const EventAccumulator& accumulator = mBuffers->mEvents[stat.getIndex()]; - const EventAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mEvents[stat.getIndex()] : NULL; - return llmin(accumulator.getMin(), active_accumulator && active_accumulator->hasValue() ? active_accumulator->getMin() : F32_MAX); + update(); + const EventAccumulator& accumulator = mBuffers->mEvents[stat.getIndex()]; + const EventAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mEvents[stat.getIndex()] : NULL; + return llmin(accumulator.getMin(), active_accumulator && active_accumulator->hasValue() ? active_accumulator->getMin() : F32_MAX); } F64 Recording::getMax( const StatType<EventAccumulator>& stat ) { - update(); - const EventAccumulator& accumulator = mBuffers->mEvents[stat.getIndex()]; - const EventAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mEvents[stat.getIndex()] : NULL; - return llmax(accumulator.getMax(), active_accumulator && active_accumulator->hasValue() ? active_accumulator->getMax() : F32_MIN); + update(); + const EventAccumulator& accumulator = mBuffers->mEvents[stat.getIndex()]; + const EventAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mEvents[stat.getIndex()] : NULL; + return llmax(accumulator.getMax(), active_accumulator && active_accumulator->hasValue() ? active_accumulator->getMax() : F32_MIN); } F64 Recording::getMean( const StatType<EventAccumulator>& stat ) { - update(); - const EventAccumulator& accumulator = mBuffers->mEvents[stat.getIndex()]; - const EventAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mEvents[stat.getIndex()] : NULL; - if (active_accumulator && active_accumulator->hasValue()) - { - F32 t = 0.0f; - S32 div = accumulator.getSampleCount() + active_accumulator->getSampleCount(); - if (div > 0) - { - t = active_accumulator->getSampleCount() / div; - } - return lerp(accumulator.getMean(), active_accumulator->getMean(), t); - } - else - { - return accumulator.getMean(); - } + update(); + const EventAccumulator& accumulator = mBuffers->mEvents[stat.getIndex()]; + const EventAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mEvents[stat.getIndex()] : NULL; + if (active_accumulator && active_accumulator->hasValue()) + { + F32 t = 0.0f; + S32 div = accumulator.getSampleCount() + active_accumulator->getSampleCount(); + if (div > 0) + { + t = active_accumulator->getSampleCount() / div; + } + return lerp(accumulator.getMean(), active_accumulator->getMean(), t); + } + else + { + return accumulator.getMean(); + } } F64 Recording::getStandardDeviation( const StatType<EventAccumulator>& stat ) { - update(); - const EventAccumulator& accumulator = mBuffers->mEvents[stat.getIndex()]; - const EventAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mEvents[stat.getIndex()] : NULL; + update(); + const EventAccumulator& accumulator = mBuffers->mEvents[stat.getIndex()]; + const EventAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mEvents[stat.getIndex()] : NULL; - if (active_accumulator && active_accumulator->hasValue()) - { - F64 sum_of_squares = EventAccumulator::mergeSumsOfSquares(accumulator, *active_accumulator); - return sqrtf(sum_of_squares / (accumulator.getSampleCount() + active_accumulator->getSampleCount())); - } - else - { - return accumulator.getStandardDeviation(); - } + if (active_accumulator && active_accumulator->hasValue()) + { + F64 sum_of_squares = EventAccumulator::mergeSumsOfSquares(accumulator, *active_accumulator); + return sqrtf(sum_of_squares / (accumulator.getSampleCount() + active_accumulator->getSampleCount())); + } + else + { + return accumulator.getStandardDeviation(); + } } F64 Recording::getLastValue( const StatType<EventAccumulator>& stat ) { - update(); - const EventAccumulator& accumulator = mBuffers->mEvents[stat.getIndex()]; - const EventAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mEvents[stat.getIndex()] : NULL; - return active_accumulator ? active_accumulator->getLastValue() : accumulator.getLastValue(); + update(); + const EventAccumulator& accumulator = mBuffers->mEvents[stat.getIndex()]; + const EventAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mEvents[stat.getIndex()] : NULL; + return active_accumulator ? active_accumulator->getLastValue() : accumulator.getLastValue(); } S32 Recording::getSampleCount( const StatType<EventAccumulator>& stat ) { - update(); - const EventAccumulator& accumulator = mBuffers->mEvents[stat.getIndex()]; - const EventAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mEvents[stat.getIndex()] : NULL; - return accumulator.getSampleCount() + (active_accumulator ? active_accumulator->getSampleCount() : 0); + update(); + const EventAccumulator& accumulator = mBuffers->mEvents[stat.getIndex()]; + const EventAccumulator* active_accumulator = mActiveBuffers ? &mActiveBuffers->mEvents[stat.getIndex()] : NULL; + return accumulator.getSampleCount() + (active_accumulator ? active_accumulator->getSampleCount() : 0); } /////////////////////////////////////////////////////////////////////// @@ -434,408 +434,408 @@ S32 Recording::getSampleCount( const StatType<EventAccumulator>& stat ) /////////////////////////////////////////////////////////////////////// PeriodicRecording::PeriodicRecording( size_t num_periods, EPlayState state) -: mAutoResize(num_periods == 0), - mCurPeriod(0), - mNumRecordedPeriods(0), - // This guarantee that mRecordingPeriods cannot be empty is essential for - // code in several methods. - mRecordingPeriods(num_periods ? num_periods : 1) +: mAutoResize(num_periods == 0), + mCurPeriod(0), + mNumRecordedPeriods(0), + // This guarantee that mRecordingPeriods cannot be empty is essential for + // code in several methods. + mRecordingPeriods(num_periods ? num_periods : 1) { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - setPlayState(state); + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + setPlayState(state); } PeriodicRecording::~PeriodicRecording() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; } void PeriodicRecording::nextPeriod() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - if (mAutoResize) - { - mRecordingPeriods.push_back(Recording()); - } + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + if (mAutoResize) + { + mRecordingPeriods.push_back(Recording()); + } - Recording& old_recording = getCurRecording(); - inci(mCurPeriod); - old_recording.splitTo(getCurRecording()); + Recording& old_recording = getCurRecording(); + inci(mCurPeriod); + old_recording.splitTo(getCurRecording()); - // Since mRecordingPeriods always has at least one entry, we can always - // safely subtract 1 from its size(). - mNumRecordedPeriods = llmin(mRecordingPeriods.size() - 1, mNumRecordedPeriods + 1); + // Since mRecordingPeriods always has at least one entry, we can always + // safely subtract 1 from its size(). + mNumRecordedPeriods = llmin(mRecordingPeriods.size() - 1, mNumRecordedPeriods + 1); } void PeriodicRecording::appendRecording(Recording& recording) { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - getCurRecording().appendRecording(recording); - nextPeriod(); + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + getCurRecording().appendRecording(recording); + nextPeriod(); } void PeriodicRecording::appendPeriodicRecording( PeriodicRecording& other ) { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - if (other.mRecordingPeriods.empty()) return; - - getCurRecording().update(); - other.getCurRecording().update(); - - const auto other_num_recordings = other.getNumRecordedPeriods(); - const auto other_current_recording_index = other.mCurPeriod; - const auto other_oldest_recording_index = other.previ(other_current_recording_index, other_num_recordings); - - // append first recording into our current slot - getCurRecording().appendRecording(other.mRecordingPeriods[other_oldest_recording_index]); - - // from now on, add new recordings for everything after the first - auto other_index = other.nexti(other_oldest_recording_index); - - if (mAutoResize) - { - // push back recordings for everything in the middle - while (other_index != other_current_recording_index) - { - mRecordingPeriods.push_back(other.mRecordingPeriods[other_index]); - other.inci(other_index); - } - - // add final recording, if it wasn't already added as the first - if (other_num_recordings > 1) - { - mRecordingPeriods.push_back(other.mRecordingPeriods[other_current_recording_index]); - } - - // mRecordingPeriods is never empty() - mCurPeriod = mRecordingPeriods.size() - 1; - mNumRecordedPeriods = mCurPeriod; - } - else - { - auto num_to_copy = llmin(mRecordingPeriods.size(), other_num_recordings); - // already consumed the first recording from other, so start counting at 1 - for (size_t n = 1, srci = other_index, dsti = mCurPeriod; - n < num_to_copy; - ++n, other.inci(srci), inci(dsti)) - { - mRecordingPeriods[dsti] = other.mRecordingPeriods[srci]; - } - - // want argument to % to be positive, otherwise result could be negative and thus out of bounds - llassert(num_to_copy >= 1); - // advance to last recording period copied, and make that our current period - inci(mCurPeriod, num_to_copy - 1); - mNumRecordedPeriods = llmin(mRecordingPeriods.size() - 1, mNumRecordedPeriods + num_to_copy - 1); - } - - // end with fresh period, otherwise next appendPeriodicRecording() will merge the first - // recording period with the last one appended here - nextPeriod(); - getCurRecording().setPlayState(getPlayState()); + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + if (other.mRecordingPeriods.empty()) return; + + getCurRecording().update(); + other.getCurRecording().update(); + + const auto other_num_recordings = other.getNumRecordedPeriods(); + const auto other_current_recording_index = other.mCurPeriod; + const auto other_oldest_recording_index = other.previ(other_current_recording_index, other_num_recordings); + + // append first recording into our current slot + getCurRecording().appendRecording(other.mRecordingPeriods[other_oldest_recording_index]); + + // from now on, add new recordings for everything after the first + auto other_index = other.nexti(other_oldest_recording_index); + + if (mAutoResize) + { + // push back recordings for everything in the middle + while (other_index != other_current_recording_index) + { + mRecordingPeriods.push_back(other.mRecordingPeriods[other_index]); + other.inci(other_index); + } + + // add final recording, if it wasn't already added as the first + if (other_num_recordings > 1) + { + mRecordingPeriods.push_back(other.mRecordingPeriods[other_current_recording_index]); + } + + // mRecordingPeriods is never empty() + mCurPeriod = mRecordingPeriods.size() - 1; + mNumRecordedPeriods = mCurPeriod; + } + else + { + auto num_to_copy = llmin(mRecordingPeriods.size(), other_num_recordings); + // already consumed the first recording from other, so start counting at 1 + for (size_t n = 1, srci = other_index, dsti = mCurPeriod; + n < num_to_copy; + ++n, other.inci(srci), inci(dsti)) + { + mRecordingPeriods[dsti] = other.mRecordingPeriods[srci]; + } + + // want argument to % to be positive, otherwise result could be negative and thus out of bounds + llassert(num_to_copy >= 1); + // advance to last recording period copied, and make that our current period + inci(mCurPeriod, num_to_copy - 1); + mNumRecordedPeriods = llmin(mRecordingPeriods.size() - 1, mNumRecordedPeriods + num_to_copy - 1); + } + + // end with fresh period, otherwise next appendPeriodicRecording() will merge the first + // recording period with the last one appended here + nextPeriod(); + getCurRecording().setPlayState(getPlayState()); } F64Seconds PeriodicRecording::getDuration() const { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - F64Seconds duration; - for (size_t n = 0; n < mRecordingPeriods.size(); ++n) - { - duration += mRecordingPeriods[nexti(mCurPeriod, n)].getDuration(); - } - return duration; + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + F64Seconds duration; + for (size_t n = 0; n < mRecordingPeriods.size(); ++n) + { + duration += mRecordingPeriods[nexti(mCurPeriod, n)].getDuration(); + } + return duration; } LLTrace::Recording PeriodicRecording::snapshotCurRecording() const { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - Recording recording_copy(getCurRecording()); - recording_copy.stop(); - return recording_copy; + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + Recording recording_copy(getCurRecording()); + recording_copy.stop(); + return recording_copy; } Recording& PeriodicRecording::getLastRecording() { - return getPrevRecording(1); + return getPrevRecording(1); } const Recording& PeriodicRecording::getLastRecording() const { - return getPrevRecording(1); + return getPrevRecording(1); } Recording& PeriodicRecording::getCurRecording() { - return mRecordingPeriods[mCurPeriod]; + return mRecordingPeriods[mCurPeriod]; } const Recording& PeriodicRecording::getCurRecording() const { - return mRecordingPeriods[mCurPeriod]; + return mRecordingPeriods[mCurPeriod]; } Recording& PeriodicRecording::getPrevRecording( size_t offset ) { - // reuse const implementation, but return non-const reference - return const_cast<Recording&>( - const_cast<const PeriodicRecording*>(this)->getPrevRecording(offset)); + // reuse const implementation, but return non-const reference + return const_cast<Recording&>( + const_cast<const PeriodicRecording*>(this)->getPrevRecording(offset)); } const Recording& PeriodicRecording::getPrevRecording( size_t offset ) const { - return mRecordingPeriods[previ(mCurPeriod, offset)]; + return mRecordingPeriods[previ(mCurPeriod, offset)]; } void PeriodicRecording::handleStart() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - getCurRecording().start(); + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + getCurRecording().start(); } void PeriodicRecording::handleStop() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - getCurRecording().pause(); + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + getCurRecording().pause(); } void PeriodicRecording::handleReset() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - getCurRecording().stop(); - - if (mAutoResize) - { - mRecordingPeriods.clear(); - mRecordingPeriods.push_back(Recording()); - } - else - { - for (Recording& rec : mRecordingPeriods) - { - rec.reset(); - } - } - mCurPeriod = 0; - mNumRecordedPeriods = 0; - getCurRecording().setPlayState(getPlayState()); + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + getCurRecording().stop(); + + if (mAutoResize) + { + mRecordingPeriods.clear(); + mRecordingPeriods.push_back(Recording()); + } + else + { + for (Recording& rec : mRecordingPeriods) + { + rec.reset(); + } + } + mCurPeriod = 0; + mNumRecordedPeriods = 0; + getCurRecording().setPlayState(getPlayState()); } void PeriodicRecording::handleSplitTo(PeriodicRecording& other) { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - getCurRecording().splitTo(other.getCurRecording()); + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + getCurRecording().splitTo(other.getCurRecording()); } F64 PeriodicRecording::getPeriodMin( const StatType<EventAccumulator>& stat, size_t num_periods /*= std::numeric_limits<size_t>::max()*/ ) { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - num_periods = llmin(num_periods, getNumRecordedPeriods()); + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + num_periods = llmin(num_periods, getNumRecordedPeriods()); - bool has_value = false; - F64 min_val = std::numeric_limits<F64>::max(); - for (size_t i = 1; i <= num_periods; i++) - { - Recording& recording = getPrevRecording(i); - if (recording.hasValue(stat)) - { - min_val = llmin(min_val, recording.getMin(stat)); - has_value = true; - } - } + bool has_value = false; + F64 min_val = std::numeric_limits<F64>::max(); + for (size_t i = 1; i <= num_periods; i++) + { + Recording& recording = getPrevRecording(i); + if (recording.hasValue(stat)) + { + min_val = llmin(min_val, recording.getMin(stat)); + has_value = true; + } + } - return has_value - ? min_val - : NaN; + return has_value + ? min_val + : NaN; } F64 PeriodicRecording::getPeriodMax( const StatType<EventAccumulator>& stat, size_t num_periods /*= std::numeric_limits<size_t>::max()*/ ) { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - num_periods = llmin(num_periods, getNumRecordedPeriods()); + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + num_periods = llmin(num_periods, getNumRecordedPeriods()); - bool has_value = false; - F64 max_val = std::numeric_limits<F64>::min(); - for (size_t i = 1; i <= num_periods; i++) - { - Recording& recording = getPrevRecording(i); - if (recording.hasValue(stat)) - { - max_val = llmax(max_val, recording.getMax(stat)); - has_value = true; - } - } + bool has_value = false; + F64 max_val = std::numeric_limits<F64>::min(); + for (size_t i = 1; i <= num_periods; i++) + { + Recording& recording = getPrevRecording(i); + if (recording.hasValue(stat)) + { + max_val = llmax(max_val, recording.getMax(stat)); + has_value = true; + } + } - return has_value - ? max_val - : NaN; + return has_value + ? max_val + : NaN; } // calculates means using aggregates per period F64 PeriodicRecording::getPeriodMean( const StatType<EventAccumulator>& stat, size_t num_periods /*= std::numeric_limits<size_t>::max()*/ ) { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - num_periods = llmin(num_periods, getNumRecordedPeriods()); + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + num_periods = llmin(num_periods, getNumRecordedPeriods()); - F64 mean = 0; - S32 valid_period_count = 0; + F64 mean = 0; + S32 valid_period_count = 0; - for (size_t i = 1; i <= num_periods; i++) - { - Recording& recording = getPrevRecording(i); - if (recording.hasValue(stat)) - { - mean += recording.getMean(stat); - valid_period_count++; - } - } + for (size_t i = 1; i <= num_periods; i++) + { + Recording& recording = getPrevRecording(i); + if (recording.hasValue(stat)) + { + mean += recording.getMean(stat); + valid_period_count++; + } + } - return valid_period_count - ? mean / (F64)valid_period_count - : NaN; + return valid_period_count + ? mean / (F64)valid_period_count + : NaN; } F64 PeriodicRecording::getPeriodStandardDeviation( const StatType<EventAccumulator>& stat, size_t num_periods /*= std::numeric_limits<size_t>::max()*/ ) { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - num_periods = llmin(num_periods, getNumRecordedPeriods()); + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + num_periods = llmin(num_periods, getNumRecordedPeriods()); - F64 period_mean = getPeriodMean(stat, num_periods); - F64 sum_of_squares = 0; - S32 valid_period_count = 0; + F64 period_mean = getPeriodMean(stat, num_periods); + F64 sum_of_squares = 0; + S32 valid_period_count = 0; - for (size_t i = 1; i <= num_periods; i++) - { - Recording& recording = getPrevRecording(i); - if (recording.hasValue(stat)) - { - F64 delta = recording.getMean(stat) - period_mean; - sum_of_squares += delta * delta; - valid_period_count++; - } - } + for (size_t i = 1; i <= num_periods; i++) + { + Recording& recording = getPrevRecording(i); + if (recording.hasValue(stat)) + { + F64 delta = recording.getMean(stat) - period_mean; + sum_of_squares += delta * delta; + valid_period_count++; + } + } - return valid_period_count - ? sqrt((F64)sum_of_squares / (F64)valid_period_count) - : NaN; + return valid_period_count + ? sqrt((F64)sum_of_squares / (F64)valid_period_count) + : NaN; } F64 PeriodicRecording::getPeriodMin( const StatType<SampleAccumulator>& stat, size_t num_periods /*= std::numeric_limits<size_t>::max()*/ ) { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - num_periods = llmin(num_periods, getNumRecordedPeriods()); + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + num_periods = llmin(num_periods, getNumRecordedPeriods()); - bool has_value = false; - F64 min_val = std::numeric_limits<F64>::max(); - for (size_t i = 1; i <= num_periods; i++) - { - Recording& recording = getPrevRecording(i); - if (recording.hasValue(stat)) - { - min_val = llmin(min_val, recording.getMin(stat)); - has_value = true; - } - } + bool has_value = false; + F64 min_val = std::numeric_limits<F64>::max(); + for (size_t i = 1; i <= num_periods; i++) + { + Recording& recording = getPrevRecording(i); + if (recording.hasValue(stat)) + { + min_val = llmin(min_val, recording.getMin(stat)); + has_value = true; + } + } - return has_value - ? min_val - : NaN; + return has_value + ? min_val + : NaN; } F64 PeriodicRecording::getPeriodMax(const StatType<SampleAccumulator>& stat, size_t num_periods /*= std::numeric_limits<size_t>::max()*/) { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - num_periods = llmin(num_periods, getNumRecordedPeriods()); + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + num_periods = llmin(num_periods, getNumRecordedPeriods()); - bool has_value = false; - F64 max_val = std::numeric_limits<F64>::min(); - for (size_t i = 1; i <= num_periods; i++) - { - Recording& recording = getPrevRecording(i); - if (recording.hasValue(stat)) - { - max_val = llmax(max_val, recording.getMax(stat)); - has_value = true; - } - } + bool has_value = false; + F64 max_val = std::numeric_limits<F64>::min(); + for (size_t i = 1; i <= num_periods; i++) + { + Recording& recording = getPrevRecording(i); + if (recording.hasValue(stat)) + { + max_val = llmax(max_val, recording.getMax(stat)); + has_value = true; + } + } - return has_value - ? max_val - : NaN; + return has_value + ? max_val + : NaN; } F64 PeriodicRecording::getPeriodMean( const StatType<SampleAccumulator>& stat, size_t num_periods /*= std::numeric_limits<size_t>::max()*/ ) { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - num_periods = llmin(num_periods, getNumRecordedPeriods()); + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + num_periods = llmin(num_periods, getNumRecordedPeriods()); - S32 valid_period_count = 0; - F64 mean = 0; + S32 valid_period_count = 0; + F64 mean = 0; - for (size_t i = 1; i <= num_periods; i++) - { - Recording& recording = getPrevRecording(i); - if (recording.hasValue(stat)) - { - mean += recording.getMean(stat); - valid_period_count++; - } - } + for (size_t i = 1; i <= num_periods; i++) + { + Recording& recording = getPrevRecording(i); + if (recording.hasValue(stat)) + { + mean += recording.getMean(stat); + valid_period_count++; + } + } - return valid_period_count - ? mean / F64(valid_period_count) - : NaN; + return valid_period_count + ? mean / F64(valid_period_count) + : NaN; } F64 PeriodicRecording::getPeriodMedian( const StatType<SampleAccumulator>& stat, size_t num_periods /*= std::numeric_limits<size_t>::max()*/ ) { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - num_periods = llmin(num_periods, getNumRecordedPeriods()); - - std::vector<F64> buf; - for (size_t i = 1; i <= num_periods; i++) - { - Recording& recording = getPrevRecording(i); - if (recording.getDuration() > (F32Seconds)0.f) - { - if (recording.hasValue(stat)) - { - buf.push_back(recording.getMean(stat)); - } - } - } - if (buf.size()==0) - { - return 0.0f; - } - std::sort(buf.begin(), buf.end()); - - return F64((buf.size() % 2 == 0) ? (buf[buf.size() / 2 - 1] + buf[buf.size() / 2]) / 2 : buf[buf.size() / 2]); + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + num_periods = llmin(num_periods, getNumRecordedPeriods()); + + std::vector<F64> buf; + for (size_t i = 1; i <= num_periods; i++) + { + Recording& recording = getPrevRecording(i); + if (recording.getDuration() > (F32Seconds)0.f) + { + if (recording.hasValue(stat)) + { + buf.push_back(recording.getMean(stat)); + } + } + } + if (buf.size()==0) + { + return 0.0f; + } + std::sort(buf.begin(), buf.end()); + + return F64((buf.size() % 2 == 0) ? (buf[buf.size() / 2 - 1] + buf[buf.size() / 2]) / 2 : buf[buf.size() / 2]); } F64 PeriodicRecording::getPeriodStandardDeviation( const StatType<SampleAccumulator>& stat, size_t num_periods /*= std::numeric_limits<size_t>::max()*/ ) { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - num_periods = llmin(num_periods, getNumRecordedPeriods()); + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + num_periods = llmin(num_periods, getNumRecordedPeriods()); - F64 period_mean = getPeriodMean(stat, num_periods); - S32 valid_period_count = 0; - F64 sum_of_squares = 0; + F64 period_mean = getPeriodMean(stat, num_periods); + S32 valid_period_count = 0; + F64 sum_of_squares = 0; - for (size_t i = 1; i <= num_periods; i++) - { - Recording& recording = getPrevRecording(i); - if (recording.hasValue(stat)) - { - F64 delta = recording.getMean(stat) - period_mean; - sum_of_squares += delta * delta; - valid_period_count++; - } - } + for (size_t i = 1; i <= num_periods; i++) + { + Recording& recording = getPrevRecording(i); + if (recording.hasValue(stat)) + { + F64 delta = recording.getMean(stat) - period_mean; + sum_of_squares += delta * delta; + valid_period_count++; + } + } - return valid_period_count - ? sqrt(sum_of_squares / (F64)valid_period_count) - : NaN; + return valid_period_count + ? sqrt(sum_of_squares / (F64)valid_period_count) + : NaN; } /////////////////////////////////////////////////////////////////////// @@ -844,36 +844,36 @@ F64 PeriodicRecording::getPeriodStandardDeviation( const StatType<SampleAccumula void ExtendableRecording::extend() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - // push the data back to accepted recording - mAcceptedRecording.appendRecording(mPotentialRecording); - // flush data, so we can start from scratch - mPotentialRecording.reset(); + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + // push the data back to accepted recording + mAcceptedRecording.appendRecording(mPotentialRecording); + // flush data, so we can start from scratch + mPotentialRecording.reset(); } void ExtendableRecording::handleStart() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - mPotentialRecording.start(); + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + mPotentialRecording.start(); } void ExtendableRecording::handleStop() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - mPotentialRecording.pause(); + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + mPotentialRecording.pause(); } void ExtendableRecording::handleReset() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - mAcceptedRecording.reset(); - mPotentialRecording.reset(); + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + mAcceptedRecording.reset(); + mPotentialRecording.reset(); } void ExtendableRecording::handleSplitTo(ExtendableRecording& other) { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - mPotentialRecording.splitTo(other.mPotentialRecording); + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + mPotentialRecording.splitTo(other.mPotentialRecording); } /////////////////////////////////////////////////////////////////////// @@ -881,203 +881,203 @@ void ExtendableRecording::handleSplitTo(ExtendableRecording& other) /////////////////////////////////////////////////////////////////////// ExtendablePeriodicRecording::ExtendablePeriodicRecording() -: mAcceptedRecording(0), - mPotentialRecording(0) +: mAcceptedRecording(0), + mPotentialRecording(0) {} void ExtendablePeriodicRecording::extend() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - // push the data back to accepted recording - mAcceptedRecording.appendPeriodicRecording(mPotentialRecording); - // flush data, so we can start from scratch - mPotentialRecording.reset(); + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + // push the data back to accepted recording + mAcceptedRecording.appendPeriodicRecording(mPotentialRecording); + // flush data, so we can start from scratch + mPotentialRecording.reset(); } void ExtendablePeriodicRecording::handleStart() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - mPotentialRecording.start(); + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + mPotentialRecording.start(); } void ExtendablePeriodicRecording::handleStop() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - mPotentialRecording.pause(); + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + mPotentialRecording.pause(); } void ExtendablePeriodicRecording::handleReset() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - mAcceptedRecording.reset(); - mPotentialRecording.reset(); + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + mAcceptedRecording.reset(); + mPotentialRecording.reset(); } void ExtendablePeriodicRecording::handleSplitTo(ExtendablePeriodicRecording& other) { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - mPotentialRecording.splitTo(other.mPotentialRecording); + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + mPotentialRecording.splitTo(other.mPotentialRecording); } PeriodicRecording& get_frame_recording() { - static thread_local PeriodicRecording sRecording(200, PeriodicRecording::STARTED); - return sRecording; + static thread_local PeriodicRecording sRecording(200, PeriodicRecording::STARTED); + return sRecording; } } void LLStopWatchControlsMixinCommon::start() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - switch (mPlayState) - { - case STOPPED: - handleReset(); - handleStart(); - mPlayState = STARTED; - break; - case PAUSED: - handleStart(); - mPlayState = STARTED; - break; - case STARTED: - break; - default: - llassert(false); - break; - } + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + switch (mPlayState) + { + case STOPPED: + handleReset(); + handleStart(); + mPlayState = STARTED; + break; + case PAUSED: + handleStart(); + mPlayState = STARTED; + break; + case STARTED: + break; + default: + llassert(false); + break; + } } void LLStopWatchControlsMixinCommon::stop() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - switch (mPlayState) - { - case STOPPED: - break; - case PAUSED: - mPlayState = STOPPED; - break; - case STARTED: - handleStop(); - mPlayState = STOPPED; - break; - default: - llassert(false); - break; - } + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + switch (mPlayState) + { + case STOPPED: + break; + case PAUSED: + mPlayState = STOPPED; + break; + case STARTED: + handleStop(); + mPlayState = STOPPED; + break; + default: + llassert(false); + break; + } } void LLStopWatchControlsMixinCommon::pause() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - switch (mPlayState) - { - case STOPPED: - // stay stopped, don't go to pause - break; - case PAUSED: - break; - case STARTED: - handleStop(); - mPlayState = PAUSED; - break; - default: - llassert(false); - break; - } + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + switch (mPlayState) + { + case STOPPED: + // stay stopped, don't go to pause + break; + case PAUSED: + break; + case STARTED: + handleStop(); + mPlayState = PAUSED; + break; + default: + llassert(false); + break; + } } void LLStopWatchControlsMixinCommon::unpause() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - switch (mPlayState) - { - case STOPPED: - // stay stopped, don't start - break; - case PAUSED: - handleStart(); - mPlayState = STARTED; - break; - case STARTED: - break; - default: - llassert(false); - break; - } + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + switch (mPlayState) + { + case STOPPED: + // stay stopped, don't start + break; + case PAUSED: + handleStart(); + mPlayState = STARTED; + break; + case STARTED: + break; + default: + llassert(false); + break; + } } void LLStopWatchControlsMixinCommon::resume() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - switch (mPlayState) - { - case STOPPED: - handleStart(); - mPlayState = STARTED; - break; - case PAUSED: - handleStart(); - mPlayState = STARTED; - break; - case STARTED: - break; - default: - llassert(false); - break; - } + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + switch (mPlayState) + { + case STOPPED: + handleStart(); + mPlayState = STARTED; + break; + case PAUSED: + handleStart(); + mPlayState = STARTED; + break; + case STARTED: + break; + default: + llassert(false); + break; + } } void LLStopWatchControlsMixinCommon::restart() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - switch (mPlayState) - { - case STOPPED: - handleReset(); - handleStart(); - mPlayState = STARTED; - break; - case PAUSED: - handleReset(); - handleStart(); - mPlayState = STARTED; - break; - case STARTED: - handleReset(); - break; - default: - llassert(false); - break; - } + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + switch (mPlayState) + { + case STOPPED: + handleReset(); + handleStart(); + mPlayState = STARTED; + break; + case PAUSED: + handleReset(); + handleStart(); + mPlayState = STARTED; + break; + case STARTED: + handleReset(); + break; + default: + llassert(false); + break; + } } void LLStopWatchControlsMixinCommon::reset() { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - handleReset(); + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + handleReset(); } void LLStopWatchControlsMixinCommon::setPlayState( EPlayState state ) { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - switch(state) - { - case STOPPED: - stop(); - break; - case PAUSED: - pause(); - break; - case STARTED: - start(); - break; - default: - llassert(false); - break; - } - - mPlayState = state; + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + switch(state) + { + case STOPPED: + stop(); + break; + case PAUSED: + pause(); + break; + case STARTED: + start(); + break; + default: + llassert(false); + break; + } + + mPlayState = state; } diff --git a/indra/llcommon/lltracerecording.h b/indra/llcommon/lltracerecording.h index 61b9096ae2..985f06cd59 100644 --- a/indra/llcommon/lltracerecording.h +++ b/indra/llcommon/lltracerecording.h @@ -38,680 +38,680 @@ class LLStopWatchControlsMixinCommon { public: - virtual ~LLStopWatchControlsMixinCommon() {} - - enum EPlayState - { - STOPPED, - PAUSED, - STARTED - }; - - void start(); // moves to started state, resetting if stopped - void stop(); // moves to stopped state - void pause(); // moves to paused state, unless stopped - void unpause(); // moves to started state if paused - void resume(); // moves to started state, without resetting - void restart(); // moves to started state, always resetting - void reset(); // resets - - bool isStarted() const { return mPlayState == STARTED; } - bool isPaused() const { return mPlayState == PAUSED; } - bool isStopped() const { return mPlayState == STOPPED; } - - EPlayState getPlayState() const { return mPlayState; } - // force play state to specific value by calling appropriate handle* methods - void setPlayState(EPlayState state); + virtual ~LLStopWatchControlsMixinCommon() {} + + enum EPlayState + { + STOPPED, + PAUSED, + STARTED + }; + + void start(); // moves to started state, resetting if stopped + void stop(); // moves to stopped state + void pause(); // moves to paused state, unless stopped + void unpause(); // moves to started state if paused + void resume(); // moves to started state, without resetting + void restart(); // moves to started state, always resetting + void reset(); // resets + + bool isStarted() const { return mPlayState == STARTED; } + bool isPaused() const { return mPlayState == PAUSED; } + bool isStopped() const { return mPlayState == STOPPED; } + + EPlayState getPlayState() const { return mPlayState; } + // force play state to specific value by calling appropriate handle* methods + void setPlayState(EPlayState state); protected: - LLStopWatchControlsMixinCommon() - : mPlayState(STOPPED) - {} + LLStopWatchControlsMixinCommon() + : mPlayState(STOPPED) + {} private: - // override these methods to provide started/stopped semantics + // override these methods to provide started/stopped semantics - // activate behavior (without reset) - virtual void handleStart() = 0; - // deactivate behavior - virtual void handleStop() = 0; - // clear accumulated state, may be called while started - virtual void handleReset() = 0; + // activate behavior (without reset) + virtual void handleStart() = 0; + // deactivate behavior + virtual void handleStop() = 0; + // clear accumulated state, may be called while started + virtual void handleReset() = 0; - EPlayState mPlayState; + EPlayState mPlayState; }; template<typename DERIVED> class LLStopWatchControlsMixin -: public LLStopWatchControlsMixinCommon +: public LLStopWatchControlsMixinCommon { public: - typedef LLStopWatchControlsMixin<DERIVED> self_t; - virtual void splitTo(DERIVED& other) - { - EPlayState play_state = getPlayState(); - stop(); - other.reset(); + typedef LLStopWatchControlsMixin<DERIVED> self_t; + virtual void splitTo(DERIVED& other) + { + EPlayState play_state = getPlayState(); + stop(); + other.reset(); - handleSplitTo(other); + handleSplitTo(other); - other.setPlayState(play_state); - } + other.setPlayState(play_state); + } - virtual void splitFrom(DERIVED& other) - { - static_cast<self_t&>(other).handleSplitTo(*static_cast<DERIVED*>(this)); - } + virtual void splitFrom(DERIVED& other) + { + static_cast<self_t&>(other).handleSplitTo(*static_cast<DERIVED*>(this)); + } private: - self_t& operator = (const self_t& other) - { - // don't do anything, derived class must implement logic - } - - // atomically stop this object while starting the other - // no data can be missed in between stop and start - virtual void handleSplitTo(DERIVED& other) {}; + self_t& operator = (const self_t& other) + { + // don't do anything, derived class must implement logic + } + + // atomically stop this object while starting the other + // no data can be missed in between stop and start + virtual void handleSplitTo(DERIVED& other) {}; }; namespace LLTrace { - template<typename T> - class StatType; - - template<typename T> - class CountStatHandle; - - template<typename T> - class SampleStatHandle; - - template<typename T> - class EventStatHandle; - - template<typename T> - struct RelatedTypes - { - typedef F64 fractional_t; - typedef T sum_t; - }; - - template<typename T, typename UNIT_T> - struct RelatedTypes<LLUnit<T, UNIT_T> > - { - typedef LLUnit<typename RelatedTypes<T>::fractional_t, UNIT_T> fractional_t; - typedef LLUnit<typename RelatedTypes<T>::sum_t, UNIT_T> sum_t; - }; - - template<> - struct RelatedTypes<bool> - { - typedef F64 fractional_t; - typedef S32 sum_t; - }; - - class Recording - : public LLStopWatchControlsMixin<Recording> - { - public: - Recording(EPlayState state = LLStopWatchControlsMixinCommon::STOPPED); - - Recording(const Recording& other); - ~Recording(); - - Recording& operator = (const Recording& other); - - // accumulate data from subsequent, non-overlapping recording - void appendRecording(Recording& other); - - // grab latest recorded data - void update(); - - // ensure that buffers are exclusively owned by this recording - void makeUnique() { mBuffers.makeUnique(); } - - // Timer accessors - bool hasValue(const StatType<TimeBlockAccumulator>& stat); - F64Seconds getSum(const StatType<TimeBlockAccumulator>& stat); - F64Seconds getSum(const StatType<TimeBlockAccumulator::SelfTimeFacet>& stat); - S32 getSum(const StatType<TimeBlockAccumulator::CallCountFacet>& stat); - - F64Seconds getPerSec(const StatType<TimeBlockAccumulator>& stat); - F64Seconds getPerSec(const StatType<TimeBlockAccumulator::SelfTimeFacet>& stat); - F32 getPerSec(const StatType<TimeBlockAccumulator::CallCountFacet>& stat); - - // CountStatHandle accessors - bool hasValue(const StatType<CountAccumulator>& stat); - F64 getSum(const StatType<CountAccumulator>& stat); - template <typename T> - typename RelatedTypes<T>::sum_t getSum(const CountStatHandle<T>& stat) - { - return (typename RelatedTypes<T>::sum_t)getSum(static_cast<const StatType<CountAccumulator>&> (stat)); - } - - F64 getPerSec(const StatType<CountAccumulator>& stat); - template <typename T> - typename RelatedTypes<T>::fractional_t getPerSec(const CountStatHandle<T>& stat) - { - return (typename RelatedTypes<T>::fractional_t)getPerSec(static_cast<const StatType<CountAccumulator>&> (stat)); - } - - S32 getSampleCount(const StatType<CountAccumulator>& stat); - - - // SampleStatHandle accessors - bool hasValue(const StatType<SampleAccumulator>& stat); - - F64 getMin(const StatType<SampleAccumulator>& stat); - template <typename T> - T getMin(const SampleStatHandle<T>& stat) - { - return (T)getMin(static_cast<const StatType<SampleAccumulator>&> (stat)); - } - - F64 getMax(const StatType<SampleAccumulator>& stat); - template <typename T> - T getMax(const SampleStatHandle<T>& stat) - { - return (T)getMax(static_cast<const StatType<SampleAccumulator>&> (stat)); - } - - F64 getMean(const StatType<SampleAccumulator>& stat); - template <typename T> - typename RelatedTypes<T>::fractional_t getMean(SampleStatHandle<T>& stat) - { - return (typename RelatedTypes<T>::fractional_t)getMean(static_cast<const StatType<SampleAccumulator>&> (stat)); - } - - F64 getStandardDeviation(const StatType<SampleAccumulator>& stat); - template <typename T> - typename RelatedTypes<T>::fractional_t getStandardDeviation(const SampleStatHandle<T>& stat) - { - return (typename RelatedTypes<T>::fractional_t)getStandardDeviation(static_cast<const StatType<SampleAccumulator>&> (stat)); - } - - F64 getLastValue(const StatType<SampleAccumulator>& stat); - template <typename T> - T getLastValue(const SampleStatHandle<T>& stat) - { - return (T)getLastValue(static_cast<const StatType<SampleAccumulator>&> (stat)); - } - - S32 getSampleCount(const StatType<SampleAccumulator>& stat); - - // EventStatHandle accessors - bool hasValue(const StatType<EventAccumulator>& stat); - - F64 getSum(const StatType<EventAccumulator>& stat); - template <typename T> - typename RelatedTypes<T>::sum_t getSum(const EventStatHandle<T>& stat) - { - return (typename RelatedTypes<T>::sum_t)getSum(static_cast<const StatType<EventAccumulator>&> (stat)); - } - - F64 getMin(const StatType<EventAccumulator>& stat); - template <typename T> - T getMin(const EventStatHandle<T>& stat) - { - return (T)getMin(static_cast<const StatType<EventAccumulator>&> (stat)); - } - - F64 getMax(const StatType<EventAccumulator>& stat); - template <typename T> - T getMax(const EventStatHandle<T>& stat) - { - return (T)getMax(static_cast<const StatType<EventAccumulator>&> (stat)); - } - - F64 getMean(const StatType<EventAccumulator>& stat); - template <typename T> - typename RelatedTypes<T>::fractional_t getMean(EventStatHandle<T>& stat) - { - return (typename RelatedTypes<T>::fractional_t)getMean(static_cast<const StatType<EventAccumulator>&> (stat)); - } - - F64 getStandardDeviation(const StatType<EventAccumulator>& stat); - template <typename T> - typename RelatedTypes<T>::fractional_t getStandardDeviation(const EventStatHandle<T>& stat) - { - return (typename RelatedTypes<T>::fractional_t)getStandardDeviation(static_cast<const StatType<EventAccumulator>&> (stat)); - } - - F64 getLastValue(const StatType<EventAccumulator>& stat); - template <typename T> - T getLastValue(const EventStatHandle<T>& stat) - { - return (T)getLastValue(static_cast<const StatType<EventAccumulator>&> (stat)); - } - - S32 getSampleCount(const StatType<EventAccumulator>& stat); - - F64Seconds getDuration() const { return mElapsedSeconds; } - - protected: - friend class ThreadRecorder; - - // implementation for LLStopWatchControlsMixin - /*virtual*/ void handleStart(); - /*virtual*/ void handleStop(); - /*virtual*/ void handleReset(); - /*virtual*/ void handleSplitTo(Recording& other); - - // returns data for current thread - class ThreadRecorder* getThreadRecorder(); - - LLTimer mSamplingTimer; - F64Seconds mElapsedSeconds; - LLCopyOnWritePointer<AccumulatorBufferGroup> mBuffers; - AccumulatorBufferGroup* mActiveBuffers; - - }; - - class LL_COMMON_API PeriodicRecording - : public LLStopWatchControlsMixin<PeriodicRecording> - { - public: - PeriodicRecording(size_t num_periods, EPlayState state = STOPPED); - ~PeriodicRecording(); - - void nextPeriod(); - auto getNumRecordedPeriods() - { - // current period counts if not active - return mNumRecordedPeriods + (isStarted() ? 0 : 1); - } - - F64Seconds getDuration() const; - - void appendPeriodicRecording(PeriodicRecording& other); - void appendRecording(Recording& recording); - Recording& getLastRecording(); - const Recording& getLastRecording() const; - Recording& getCurRecording(); - const Recording& getCurRecording() const; - Recording& getPrevRecording(size_t offset); - const Recording& getPrevRecording(size_t offset) const; - Recording snapshotCurRecording() const; - - template <typename T> - auto getSampleCount(const StatType<T>& stat, size_t num_periods = std::numeric_limits<size_t>::max()) - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - num_periods = llmin(num_periods, getNumRecordedPeriods()); - - size_t num_samples = 0; - for (size_t i = 1; i <= num_periods; i++) - { - Recording& recording = getPrevRecording(i); - num_samples += recording.getSampleCount(stat); - } - return num_samples; - } - - // - // PERIODIC MIN - // - - // catch all for stats that have a defined sum - template <typename T> - typename T::value_t getPeriodMin(const StatType<T>& stat, size_t num_periods = std::numeric_limits<size_t>::max()) - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - num_periods = llmin(num_periods, getNumRecordedPeriods()); - - bool has_value = false; - typename T::value_t min_val(std::numeric_limits<typename T::value_t>::max()); - for (size_t i = 1; i <= num_periods; i++) - { - Recording& recording = getPrevRecording(i); - if (recording.hasValue(stat)) - { - min_val = llmin(min_val, recording.getSum(stat)); - has_value = true; - } - } - - return has_value - ? min_val - : T::getDefaultValue(); - } - - template<typename T> - T getPeriodMin(const CountStatHandle<T>& stat, size_t num_periods = std::numeric_limits<size_t>::max()) - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - return T(getPeriodMin(static_cast<const StatType<CountAccumulator>&>(stat), num_periods)); - } - - F64 getPeriodMin(const StatType<SampleAccumulator>& stat, size_t num_periods = std::numeric_limits<size_t>::max()); - template<typename T> - T getPeriodMin(const SampleStatHandle<T>& stat, size_t num_periods = std::numeric_limits<size_t>::max()) - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - return T(getPeriodMin(static_cast<const StatType<SampleAccumulator>&>(stat), num_periods)); - } - - F64 getPeriodMin(const StatType<EventAccumulator>& stat, size_t num_periods = std::numeric_limits<size_t>::max()); - template<typename T> - T getPeriodMin(const EventStatHandle<T>& stat, size_t num_periods = std::numeric_limits<size_t>::max()) - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - return T(getPeriodMin(static_cast<const StatType<EventAccumulator>&>(stat), num_periods)); - } - - template <typename T> - typename RelatedTypes<typename T::value_t>::fractional_t getPeriodMinPerSec(const StatType<T>& stat, size_t num_periods = std::numeric_limits<size_t>::max()) - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - num_periods = llmin(num_periods, getNumRecordedPeriods()); - - typename RelatedTypes<typename T::value_t>::fractional_t min_val(std::numeric_limits<F64>::max()); - for (size_t i = 1; i <= num_periods; i++) - { - Recording& recording = getPrevRecording(i); - min_val = llmin(min_val, recording.getPerSec(stat)); - } - return (typename RelatedTypes<typename T::value_t>::fractional_t) min_val; - } - - template<typename T> - typename RelatedTypes<T>::fractional_t getPeriodMinPerSec(const CountStatHandle<T>& stat, size_t num_periods = std::numeric_limits<size_t>::max()) - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - return typename RelatedTypes<T>::fractional_t(getPeriodMinPerSec(static_cast<const StatType<CountAccumulator>&>(stat), num_periods)); - } - - // - // PERIODIC MAX - // - - // catch all for stats that have a defined sum - template <typename T> - typename T::value_t getPeriodMax(const StatType<T>& stat, size_t num_periods = std::numeric_limits<size_t>::max()) - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - num_periods = llmin(num_periods, getNumRecordedPeriods()); - - bool has_value = false; - typename T::value_t max_val(std::numeric_limits<typename T::value_t>::min()); - for (size_t i = 1; i <= num_periods; i++) - { - Recording& recording = getPrevRecording(i); - if (recording.hasValue(stat)) - { - max_val = llmax(max_val, recording.getSum(stat)); - has_value = true; - } - } - - return has_value - ? max_val - : T::getDefaultValue(); - } - - template<typename T> - T getPeriodMax(const CountStatHandle<T>& stat, size_t num_periods = std::numeric_limits<size_t>::max()) - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - return T(getPeriodMax(static_cast<const StatType<CountAccumulator>&>(stat), num_periods)); - } - - F64 getPeriodMax(const StatType<SampleAccumulator>& stat, size_t num_periods = std::numeric_limits<size_t>::max()); - template<typename T> - T getPeriodMax(const SampleStatHandle<T>& stat, size_t num_periods = std::numeric_limits<size_t>::max()) - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - return T(getPeriodMax(static_cast<const StatType<SampleAccumulator>&>(stat), num_periods)); - } - - F64 getPeriodMax(const StatType<EventAccumulator>& stat, size_t num_periods = std::numeric_limits<size_t>::max()); - template<typename T> - T getPeriodMax(const EventStatHandle<T>& stat, size_t num_periods = std::numeric_limits<size_t>::max()) - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - return T(getPeriodMax(static_cast<const StatType<EventAccumulator>&>(stat), num_periods)); - } - - template <typename T> - typename RelatedTypes<typename T::value_t>::fractional_t getPeriodMaxPerSec(const StatType<T>& stat, size_t num_periods = std::numeric_limits<size_t>::max()) - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - num_periods = llmin(num_periods, getNumRecordedPeriods()); - - F64 max_val = std::numeric_limits<F64>::min(); - for (size_t i = 1; i <= num_periods; i++) - { - Recording& recording = getPrevRecording(i); - max_val = llmax(max_val, recording.getPerSec(stat)); - } - return (typename RelatedTypes<typename T::value_t>::fractional_t)max_val; - } - - template<typename T> - typename RelatedTypes<T>::fractional_t getPeriodMaxPerSec(const CountStatHandle<T>& stat, size_t num_periods = std::numeric_limits<size_t>::max()) - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - return typename RelatedTypes<T>::fractional_t(getPeriodMaxPerSec(static_cast<const StatType<CountAccumulator>&>(stat), num_periods)); - } - - // - // PERIODIC MEAN - // - - // catch all for stats that have a defined sum - template <typename T> - typename RelatedTypes<typename T::value_t>::fractional_t getPeriodMean(const StatType<T >& stat, size_t num_periods = std::numeric_limits<size_t>::max()) - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - num_periods = llmin(num_periods, getNumRecordedPeriods()); - - typename RelatedTypes<typename T::value_t>::fractional_t mean(0); - - for (size_t i = 1; i <= num_periods; i++) - { - Recording& recording = getPrevRecording(i); - if (recording.getDuration() > (F32Seconds)0.f) - { - mean += recording.getSum(stat); - } - } - return (num_periods - ? typename RelatedTypes<typename T::value_t>::fractional_t(mean / num_periods) - : typename RelatedTypes<typename T::value_t>::fractional_t(NaN)); - } - - template<typename T> - typename RelatedTypes<T>::fractional_t getPeriodMean(const CountStatHandle<T>& stat, size_t num_periods = std::numeric_limits<size_t>::max()) - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - return typename RelatedTypes<T>::fractional_t(getPeriodMean(static_cast<const StatType<CountAccumulator>&>(stat), num_periods)); - } - F64 getPeriodMean(const StatType<SampleAccumulator>& stat, size_t num_periods = std::numeric_limits<size_t>::max()); - template<typename T> - typename RelatedTypes<T>::fractional_t getPeriodMean(const SampleStatHandle<T>& stat, size_t num_periods = std::numeric_limits<size_t>::max()) - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - return typename RelatedTypes<T>::fractional_t(getPeriodMean(static_cast<const StatType<SampleAccumulator>&>(stat), num_periods)); - } - - F64 getPeriodMean(const StatType<EventAccumulator>& stat, size_t num_periods = std::numeric_limits<size_t>::max()); - template<typename T> - typename RelatedTypes<T>::fractional_t getPeriodMean(const EventStatHandle<T>& stat, size_t num_periods = std::numeric_limits<size_t>::max()) - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - return typename RelatedTypes<T>::fractional_t(getPeriodMean(static_cast<const StatType<EventAccumulator>&>(stat), num_periods)); - } - - template <typename T> - typename RelatedTypes<typename T::value_t>::fractional_t getPeriodMeanPerSec(const StatType<T>& stat, size_t num_periods = std::numeric_limits<size_t>::max()) - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - num_periods = llmin(num_periods, getNumRecordedPeriods()); - - typename RelatedTypes<typename T::value_t>::fractional_t mean = 0; - - for (size_t i = 1; i <= num_periods; i++) - { - Recording& recording = getPrevRecording(i); - if (recording.getDuration() > (F32Seconds)0.f) - { - mean += recording.getPerSec(stat); - } - } - - return (num_periods - ? typename RelatedTypes<typename T::value_t>::fractional_t(mean / num_periods) - : typename RelatedTypes<typename T::value_t>::fractional_t(NaN)); - } - - template<typename T> - typename RelatedTypes<T>::fractional_t getPeriodMeanPerSec(const CountStatHandle<T>& stat, size_t num_periods = std::numeric_limits<size_t>::max()) - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - return typename RelatedTypes<T>::fractional_t(getPeriodMeanPerSec(static_cast<const StatType<CountAccumulator>&>(stat), num_periods)); - } - - F64 getPeriodMedian( const StatType<SampleAccumulator>& stat, size_t num_periods = std::numeric_limits<size_t>::max()); - - template <typename T> - typename RelatedTypes<typename T::value_t>::fractional_t getPeriodMedianPerSec(const StatType<T>& stat, size_t num_periods = std::numeric_limits<size_t>::max()) - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - num_periods = llmin(num_periods, getNumRecordedPeriods()); - - std::vector <typename RelatedTypes<typename T::value_t>::fractional_t> buf; - for (size_t i = 1; i <= num_periods; i++) - { - Recording& recording = getPrevRecording(i); - if (recording.getDuration() > (F32Seconds)0.f) - { - buf.push_back(recording.getPerSec(stat)); - } - } - std::sort(buf.begin(), buf.end()); - - return typename RelatedTypes<T>::fractional_t((buf.size() % 2 == 0) ? (buf[buf.size() / 2 - 1] + buf[buf.size() / 2]) / 2 : buf[buf.size() / 2]); - } - - template<typename T> - typename RelatedTypes<T>::fractional_t getPeriodMedianPerSec(const CountStatHandle<T>& stat, size_t num_periods = std::numeric_limits<size_t>::max()) - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - return typename RelatedTypes<T>::fractional_t(getPeriodMedianPerSec(static_cast<const StatType<CountAccumulator>&>(stat), num_periods)); - } - - // - // PERIODIC STANDARD DEVIATION - // - - F64 getPeriodStandardDeviation(const StatType<SampleAccumulator>& stat, size_t num_periods = std::numeric_limits<size_t>::max()); - - template<typename T> - typename RelatedTypes<T>::fractional_t getPeriodStandardDeviation(const SampleStatHandle<T>& stat, size_t num_periods = std::numeric_limits<size_t>::max()) - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - return typename RelatedTypes<T>::fractional_t(getPeriodStandardDeviation(static_cast<const StatType<SampleAccumulator>&>(stat), num_periods)); - } - - F64 getPeriodStandardDeviation(const StatType<EventAccumulator>& stat, size_t num_periods = std::numeric_limits<size_t>::max()); - template<typename T> - typename RelatedTypes<T>::fractional_t getPeriodStandardDeviation(const EventStatHandle<T>& stat, size_t num_periods = std::numeric_limits<size_t>::max()) - { - LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - return typename RelatedTypes<T>::fractional_t(getPeriodStandardDeviation(static_cast<const StatType<EventAccumulator>&>(stat), num_periods)); - } - - private: - // implementation for LLStopWatchControlsMixin - /*virtual*/ void handleStart(); - /*virtual*/ void handleStop(); - /*virtual*/ void handleReset(); - /*virtual*/ void handleSplitTo(PeriodicRecording& other); - - // helper methods for wraparound ring-buffer arithmetic - inline - size_t wrapi(size_t i) const - { - return i % mRecordingPeriods.size(); - } - - inline - size_t nexti(size_t i, size_t offset=1) const - { - return wrapi(i + offset); - } - - inline - size_t previ(size_t i, size_t offset=1) const - { - auto num_periods = mRecordingPeriods.size(); - // constrain offset - offset = llclamp(offset, 0, num_periods - 1); - // add size() so expression can't go (unsigned) "negative" - return wrapi(i + num_periods - offset); - } - - inline - void inci(size_t& i, size_t offset=1) const - { - i = nexti(i, offset); - } - - private: - std::vector<Recording> mRecordingPeriods; - const bool mAutoResize; - size_t mCurPeriod; - size_t mNumRecordedPeriods; - }; - - PeriodicRecording& get_frame_recording(); - - class ExtendableRecording - : public LLStopWatchControlsMixin<ExtendableRecording> - { - public: - void extend(); - - Recording& getAcceptedRecording() { return mAcceptedRecording; } - const Recording& getAcceptedRecording() const {return mAcceptedRecording;} - - Recording& getPotentialRecording() { return mPotentialRecording; } - const Recording& getPotentialRecording() const { return mPotentialRecording;} - - private: - // implementation for LLStopWatchControlsMixin - /*virtual*/ void handleStart(); - /*virtual*/ void handleStop(); - /*virtual*/ void handleReset(); - /*virtual*/ void handleSplitTo(ExtendableRecording& other); - - private: - Recording mAcceptedRecording; - Recording mPotentialRecording; - }; - - class ExtendablePeriodicRecording - : public LLStopWatchControlsMixin<ExtendablePeriodicRecording> - { - public: - ExtendablePeriodicRecording(); - void extend(); - - PeriodicRecording& getResults() { return mAcceptedRecording; } - const PeriodicRecording& getResults() const {return mAcceptedRecording;} - - void nextPeriod() { mPotentialRecording.nextPeriod(); } - - private: - // implementation for LLStopWatchControlsMixin - /*virtual*/ void handleStart(); - /*virtual*/ void handleStop(); - /*virtual*/ void handleReset(); - /*virtual*/ void handleSplitTo(ExtendablePeriodicRecording& other); - - private: - PeriodicRecording mAcceptedRecording; - PeriodicRecording mPotentialRecording; - }; + template<typename T> + class StatType; + + template<typename T> + class CountStatHandle; + + template<typename T> + class SampleStatHandle; + + template<typename T> + class EventStatHandle; + + template<typename T> + struct RelatedTypes + { + typedef F64 fractional_t; + typedef T sum_t; + }; + + template<typename T, typename UNIT_T> + struct RelatedTypes<LLUnit<T, UNIT_T> > + { + typedef LLUnit<typename RelatedTypes<T>::fractional_t, UNIT_T> fractional_t; + typedef LLUnit<typename RelatedTypes<T>::sum_t, UNIT_T> sum_t; + }; + + template<> + struct RelatedTypes<bool> + { + typedef F64 fractional_t; + typedef S32 sum_t; + }; + + class Recording + : public LLStopWatchControlsMixin<Recording> + { + public: + Recording(EPlayState state = LLStopWatchControlsMixinCommon::STOPPED); + + Recording(const Recording& other); + ~Recording(); + + Recording& operator = (const Recording& other); + + // accumulate data from subsequent, non-overlapping recording + void appendRecording(Recording& other); + + // grab latest recorded data + void update(); + + // ensure that buffers are exclusively owned by this recording + void makeUnique() { mBuffers.makeUnique(); } + + // Timer accessors + bool hasValue(const StatType<TimeBlockAccumulator>& stat); + F64Seconds getSum(const StatType<TimeBlockAccumulator>& stat); + F64Seconds getSum(const StatType<TimeBlockAccumulator::SelfTimeFacet>& stat); + S32 getSum(const StatType<TimeBlockAccumulator::CallCountFacet>& stat); + + F64Seconds getPerSec(const StatType<TimeBlockAccumulator>& stat); + F64Seconds getPerSec(const StatType<TimeBlockAccumulator::SelfTimeFacet>& stat); + F32 getPerSec(const StatType<TimeBlockAccumulator::CallCountFacet>& stat); + + // CountStatHandle accessors + bool hasValue(const StatType<CountAccumulator>& stat); + F64 getSum(const StatType<CountAccumulator>& stat); + template <typename T> + typename RelatedTypes<T>::sum_t getSum(const CountStatHandle<T>& stat) + { + return (typename RelatedTypes<T>::sum_t)getSum(static_cast<const StatType<CountAccumulator>&> (stat)); + } + + F64 getPerSec(const StatType<CountAccumulator>& stat); + template <typename T> + typename RelatedTypes<T>::fractional_t getPerSec(const CountStatHandle<T>& stat) + { + return (typename RelatedTypes<T>::fractional_t)getPerSec(static_cast<const StatType<CountAccumulator>&> (stat)); + } + + S32 getSampleCount(const StatType<CountAccumulator>& stat); + + + // SampleStatHandle accessors + bool hasValue(const StatType<SampleAccumulator>& stat); + + F64 getMin(const StatType<SampleAccumulator>& stat); + template <typename T> + T getMin(const SampleStatHandle<T>& stat) + { + return (T)getMin(static_cast<const StatType<SampleAccumulator>&> (stat)); + } + + F64 getMax(const StatType<SampleAccumulator>& stat); + template <typename T> + T getMax(const SampleStatHandle<T>& stat) + { + return (T)getMax(static_cast<const StatType<SampleAccumulator>&> (stat)); + } + + F64 getMean(const StatType<SampleAccumulator>& stat); + template <typename T> + typename RelatedTypes<T>::fractional_t getMean(SampleStatHandle<T>& stat) + { + return (typename RelatedTypes<T>::fractional_t)getMean(static_cast<const StatType<SampleAccumulator>&> (stat)); + } + + F64 getStandardDeviation(const StatType<SampleAccumulator>& stat); + template <typename T> + typename RelatedTypes<T>::fractional_t getStandardDeviation(const SampleStatHandle<T>& stat) + { + return (typename RelatedTypes<T>::fractional_t)getStandardDeviation(static_cast<const StatType<SampleAccumulator>&> (stat)); + } + + F64 getLastValue(const StatType<SampleAccumulator>& stat); + template <typename T> + T getLastValue(const SampleStatHandle<T>& stat) + { + return (T)getLastValue(static_cast<const StatType<SampleAccumulator>&> (stat)); + } + + S32 getSampleCount(const StatType<SampleAccumulator>& stat); + + // EventStatHandle accessors + bool hasValue(const StatType<EventAccumulator>& stat); + + F64 getSum(const StatType<EventAccumulator>& stat); + template <typename T> + typename RelatedTypes<T>::sum_t getSum(const EventStatHandle<T>& stat) + { + return (typename RelatedTypes<T>::sum_t)getSum(static_cast<const StatType<EventAccumulator>&> (stat)); + } + + F64 getMin(const StatType<EventAccumulator>& stat); + template <typename T> + T getMin(const EventStatHandle<T>& stat) + { + return (T)getMin(static_cast<const StatType<EventAccumulator>&> (stat)); + } + + F64 getMax(const StatType<EventAccumulator>& stat); + template <typename T> + T getMax(const EventStatHandle<T>& stat) + { + return (T)getMax(static_cast<const StatType<EventAccumulator>&> (stat)); + } + + F64 getMean(const StatType<EventAccumulator>& stat); + template <typename T> + typename RelatedTypes<T>::fractional_t getMean(EventStatHandle<T>& stat) + { + return (typename RelatedTypes<T>::fractional_t)getMean(static_cast<const StatType<EventAccumulator>&> (stat)); + } + + F64 getStandardDeviation(const StatType<EventAccumulator>& stat); + template <typename T> + typename RelatedTypes<T>::fractional_t getStandardDeviation(const EventStatHandle<T>& stat) + { + return (typename RelatedTypes<T>::fractional_t)getStandardDeviation(static_cast<const StatType<EventAccumulator>&> (stat)); + } + + F64 getLastValue(const StatType<EventAccumulator>& stat); + template <typename T> + T getLastValue(const EventStatHandle<T>& stat) + { + return (T)getLastValue(static_cast<const StatType<EventAccumulator>&> (stat)); + } + + S32 getSampleCount(const StatType<EventAccumulator>& stat); + + F64Seconds getDuration() const { return mElapsedSeconds; } + + protected: + friend class ThreadRecorder; + + // implementation for LLStopWatchControlsMixin + /*virtual*/ void handleStart(); + /*virtual*/ void handleStop(); + /*virtual*/ void handleReset(); + /*virtual*/ void handleSplitTo(Recording& other); + + // returns data for current thread + class ThreadRecorder* getThreadRecorder(); + + LLTimer mSamplingTimer; + F64Seconds mElapsedSeconds; + LLCopyOnWritePointer<AccumulatorBufferGroup> mBuffers; + AccumulatorBufferGroup* mActiveBuffers; + + }; + + class LL_COMMON_API PeriodicRecording + : public LLStopWatchControlsMixin<PeriodicRecording> + { + public: + PeriodicRecording(size_t num_periods, EPlayState state = STOPPED); + ~PeriodicRecording(); + + void nextPeriod(); + auto getNumRecordedPeriods() + { + // current period counts if not active + return mNumRecordedPeriods + (isStarted() ? 0 : 1); + } + + F64Seconds getDuration() const; + + void appendPeriodicRecording(PeriodicRecording& other); + void appendRecording(Recording& recording); + Recording& getLastRecording(); + const Recording& getLastRecording() const; + Recording& getCurRecording(); + const Recording& getCurRecording() const; + Recording& getPrevRecording(size_t offset); + const Recording& getPrevRecording(size_t offset) const; + Recording snapshotCurRecording() const; + + template <typename T> + auto getSampleCount(const StatType<T>& stat, size_t num_periods = std::numeric_limits<size_t>::max()) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + num_periods = llmin(num_periods, getNumRecordedPeriods()); + + size_t num_samples = 0; + for (size_t i = 1; i <= num_periods; i++) + { + Recording& recording = getPrevRecording(i); + num_samples += recording.getSampleCount(stat); + } + return num_samples; + } + + // + // PERIODIC MIN + // + + // catch all for stats that have a defined sum + template <typename T> + typename T::value_t getPeriodMin(const StatType<T>& stat, size_t num_periods = std::numeric_limits<size_t>::max()) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + num_periods = llmin(num_periods, getNumRecordedPeriods()); + + bool has_value = false; + typename T::value_t min_val(std::numeric_limits<typename T::value_t>::max()); + for (size_t i = 1; i <= num_periods; i++) + { + Recording& recording = getPrevRecording(i); + if (recording.hasValue(stat)) + { + min_val = llmin(min_val, recording.getSum(stat)); + has_value = true; + } + } + + return has_value + ? min_val + : T::getDefaultValue(); + } + + template<typename T> + T getPeriodMin(const CountStatHandle<T>& stat, size_t num_periods = std::numeric_limits<size_t>::max()) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + return T(getPeriodMin(static_cast<const StatType<CountAccumulator>&>(stat), num_periods)); + } + + F64 getPeriodMin(const StatType<SampleAccumulator>& stat, size_t num_periods = std::numeric_limits<size_t>::max()); + template<typename T> + T getPeriodMin(const SampleStatHandle<T>& stat, size_t num_periods = std::numeric_limits<size_t>::max()) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + return T(getPeriodMin(static_cast<const StatType<SampleAccumulator>&>(stat), num_periods)); + } + + F64 getPeriodMin(const StatType<EventAccumulator>& stat, size_t num_periods = std::numeric_limits<size_t>::max()); + template<typename T> + T getPeriodMin(const EventStatHandle<T>& stat, size_t num_periods = std::numeric_limits<size_t>::max()) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + return T(getPeriodMin(static_cast<const StatType<EventAccumulator>&>(stat), num_periods)); + } + + template <typename T> + typename RelatedTypes<typename T::value_t>::fractional_t getPeriodMinPerSec(const StatType<T>& stat, size_t num_periods = std::numeric_limits<size_t>::max()) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + num_periods = llmin(num_periods, getNumRecordedPeriods()); + + typename RelatedTypes<typename T::value_t>::fractional_t min_val(std::numeric_limits<F64>::max()); + for (size_t i = 1; i <= num_periods; i++) + { + Recording& recording = getPrevRecording(i); + min_val = llmin(min_val, recording.getPerSec(stat)); + } + return (typename RelatedTypes<typename T::value_t>::fractional_t) min_val; + } + + template<typename T> + typename RelatedTypes<T>::fractional_t getPeriodMinPerSec(const CountStatHandle<T>& stat, size_t num_periods = std::numeric_limits<size_t>::max()) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + return typename RelatedTypes<T>::fractional_t(getPeriodMinPerSec(static_cast<const StatType<CountAccumulator>&>(stat), num_periods)); + } + + // + // PERIODIC MAX + // + + // catch all for stats that have a defined sum + template <typename T> + typename T::value_t getPeriodMax(const StatType<T>& stat, size_t num_periods = std::numeric_limits<size_t>::max()) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + num_periods = llmin(num_periods, getNumRecordedPeriods()); + + bool has_value = false; + typename T::value_t max_val(std::numeric_limits<typename T::value_t>::min()); + for (size_t i = 1; i <= num_periods; i++) + { + Recording& recording = getPrevRecording(i); + if (recording.hasValue(stat)) + { + max_val = llmax(max_val, recording.getSum(stat)); + has_value = true; + } + } + + return has_value + ? max_val + : T::getDefaultValue(); + } + + template<typename T> + T getPeriodMax(const CountStatHandle<T>& stat, size_t num_periods = std::numeric_limits<size_t>::max()) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + return T(getPeriodMax(static_cast<const StatType<CountAccumulator>&>(stat), num_periods)); + } + + F64 getPeriodMax(const StatType<SampleAccumulator>& stat, size_t num_periods = std::numeric_limits<size_t>::max()); + template<typename T> + T getPeriodMax(const SampleStatHandle<T>& stat, size_t num_periods = std::numeric_limits<size_t>::max()) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + return T(getPeriodMax(static_cast<const StatType<SampleAccumulator>&>(stat), num_periods)); + } + + F64 getPeriodMax(const StatType<EventAccumulator>& stat, size_t num_periods = std::numeric_limits<size_t>::max()); + template<typename T> + T getPeriodMax(const EventStatHandle<T>& stat, size_t num_periods = std::numeric_limits<size_t>::max()) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + return T(getPeriodMax(static_cast<const StatType<EventAccumulator>&>(stat), num_periods)); + } + + template <typename T> + typename RelatedTypes<typename T::value_t>::fractional_t getPeriodMaxPerSec(const StatType<T>& stat, size_t num_periods = std::numeric_limits<size_t>::max()) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + num_periods = llmin(num_periods, getNumRecordedPeriods()); + + F64 max_val = std::numeric_limits<F64>::min(); + for (size_t i = 1; i <= num_periods; i++) + { + Recording& recording = getPrevRecording(i); + max_val = llmax(max_val, recording.getPerSec(stat)); + } + return (typename RelatedTypes<typename T::value_t>::fractional_t)max_val; + } + + template<typename T> + typename RelatedTypes<T>::fractional_t getPeriodMaxPerSec(const CountStatHandle<T>& stat, size_t num_periods = std::numeric_limits<size_t>::max()) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + return typename RelatedTypes<T>::fractional_t(getPeriodMaxPerSec(static_cast<const StatType<CountAccumulator>&>(stat), num_periods)); + } + + // + // PERIODIC MEAN + // + + // catch all for stats that have a defined sum + template <typename T> + typename RelatedTypes<typename T::value_t>::fractional_t getPeriodMean(const StatType<T >& stat, size_t num_periods = std::numeric_limits<size_t>::max()) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + num_periods = llmin(num_periods, getNumRecordedPeriods()); + + typename RelatedTypes<typename T::value_t>::fractional_t mean(0); + + for (size_t i = 1; i <= num_periods; i++) + { + Recording& recording = getPrevRecording(i); + if (recording.getDuration() > (F32Seconds)0.f) + { + mean += recording.getSum(stat); + } + } + return (num_periods + ? typename RelatedTypes<typename T::value_t>::fractional_t(mean / num_periods) + : typename RelatedTypes<typename T::value_t>::fractional_t(NaN)); + } + + template<typename T> + typename RelatedTypes<T>::fractional_t getPeriodMean(const CountStatHandle<T>& stat, size_t num_periods = std::numeric_limits<size_t>::max()) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + return typename RelatedTypes<T>::fractional_t(getPeriodMean(static_cast<const StatType<CountAccumulator>&>(stat), num_periods)); + } + F64 getPeriodMean(const StatType<SampleAccumulator>& stat, size_t num_periods = std::numeric_limits<size_t>::max()); + template<typename T> + typename RelatedTypes<T>::fractional_t getPeriodMean(const SampleStatHandle<T>& stat, size_t num_periods = std::numeric_limits<size_t>::max()) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + return typename RelatedTypes<T>::fractional_t(getPeriodMean(static_cast<const StatType<SampleAccumulator>&>(stat), num_periods)); + } + + F64 getPeriodMean(const StatType<EventAccumulator>& stat, size_t num_periods = std::numeric_limits<size_t>::max()); + template<typename T> + typename RelatedTypes<T>::fractional_t getPeriodMean(const EventStatHandle<T>& stat, size_t num_periods = std::numeric_limits<size_t>::max()) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + return typename RelatedTypes<T>::fractional_t(getPeriodMean(static_cast<const StatType<EventAccumulator>&>(stat), num_periods)); + } + + template <typename T> + typename RelatedTypes<typename T::value_t>::fractional_t getPeriodMeanPerSec(const StatType<T>& stat, size_t num_periods = std::numeric_limits<size_t>::max()) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + num_periods = llmin(num_periods, getNumRecordedPeriods()); + + typename RelatedTypes<typename T::value_t>::fractional_t mean = 0; + + for (size_t i = 1; i <= num_periods; i++) + { + Recording& recording = getPrevRecording(i); + if (recording.getDuration() > (F32Seconds)0.f) + { + mean += recording.getPerSec(stat); + } + } + + return (num_periods + ? typename RelatedTypes<typename T::value_t>::fractional_t(mean / num_periods) + : typename RelatedTypes<typename T::value_t>::fractional_t(NaN)); + } + + template<typename T> + typename RelatedTypes<T>::fractional_t getPeriodMeanPerSec(const CountStatHandle<T>& stat, size_t num_periods = std::numeric_limits<size_t>::max()) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + return typename RelatedTypes<T>::fractional_t(getPeriodMeanPerSec(static_cast<const StatType<CountAccumulator>&>(stat), num_periods)); + } + + F64 getPeriodMedian( const StatType<SampleAccumulator>& stat, size_t num_periods = std::numeric_limits<size_t>::max()); + + template <typename T> + typename RelatedTypes<typename T::value_t>::fractional_t getPeriodMedianPerSec(const StatType<T>& stat, size_t num_periods = std::numeric_limits<size_t>::max()) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + num_periods = llmin(num_periods, getNumRecordedPeriods()); + + std::vector <typename RelatedTypes<typename T::value_t>::fractional_t> buf; + for (size_t i = 1; i <= num_periods; i++) + { + Recording& recording = getPrevRecording(i); + if (recording.getDuration() > (F32Seconds)0.f) + { + buf.push_back(recording.getPerSec(stat)); + } + } + std::sort(buf.begin(), buf.end()); + + return typename RelatedTypes<T>::fractional_t((buf.size() % 2 == 0) ? (buf[buf.size() / 2 - 1] + buf[buf.size() / 2]) / 2 : buf[buf.size() / 2]); + } + + template<typename T> + typename RelatedTypes<T>::fractional_t getPeriodMedianPerSec(const CountStatHandle<T>& stat, size_t num_periods = std::numeric_limits<size_t>::max()) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + return typename RelatedTypes<T>::fractional_t(getPeriodMedianPerSec(static_cast<const StatType<CountAccumulator>&>(stat), num_periods)); + } + + // + // PERIODIC STANDARD DEVIATION + // + + F64 getPeriodStandardDeviation(const StatType<SampleAccumulator>& stat, size_t num_periods = std::numeric_limits<size_t>::max()); + + template<typename T> + typename RelatedTypes<T>::fractional_t getPeriodStandardDeviation(const SampleStatHandle<T>& stat, size_t num_periods = std::numeric_limits<size_t>::max()) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + return typename RelatedTypes<T>::fractional_t(getPeriodStandardDeviation(static_cast<const StatType<SampleAccumulator>&>(stat), num_periods)); + } + + F64 getPeriodStandardDeviation(const StatType<EventAccumulator>& stat, size_t num_periods = std::numeric_limits<size_t>::max()); + template<typename T> + typename RelatedTypes<T>::fractional_t getPeriodStandardDeviation(const EventStatHandle<T>& stat, size_t num_periods = std::numeric_limits<size_t>::max()) + { + LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; + return typename RelatedTypes<T>::fractional_t(getPeriodStandardDeviation(static_cast<const StatType<EventAccumulator>&>(stat), num_periods)); + } + + private: + // implementation for LLStopWatchControlsMixin + /*virtual*/ void handleStart(); + /*virtual*/ void handleStop(); + /*virtual*/ void handleReset(); + /*virtual*/ void handleSplitTo(PeriodicRecording& other); + + // helper methods for wraparound ring-buffer arithmetic + inline + size_t wrapi(size_t i) const + { + return i % mRecordingPeriods.size(); + } + + inline + size_t nexti(size_t i, size_t offset=1) const + { + return wrapi(i + offset); + } + + inline + size_t previ(size_t i, size_t offset=1) const + { + auto num_periods = mRecordingPeriods.size(); + // constrain offset + offset = llclamp(offset, 0, num_periods - 1); + // add size() so expression can't go (unsigned) "negative" + return wrapi(i + num_periods - offset); + } + + inline + void inci(size_t& i, size_t offset=1) const + { + i = nexti(i, offset); + } + + private: + std::vector<Recording> mRecordingPeriods; + const bool mAutoResize; + size_t mCurPeriod; + size_t mNumRecordedPeriods; + }; + + PeriodicRecording& get_frame_recording(); + + class ExtendableRecording + : public LLStopWatchControlsMixin<ExtendableRecording> + { + public: + void extend(); + + Recording& getAcceptedRecording() { return mAcceptedRecording; } + const Recording& getAcceptedRecording() const {return mAcceptedRecording;} + + Recording& getPotentialRecording() { return mPotentialRecording; } + const Recording& getPotentialRecording() const { return mPotentialRecording;} + + private: + // implementation for LLStopWatchControlsMixin + /*virtual*/ void handleStart(); + /*virtual*/ void handleStop(); + /*virtual*/ void handleReset(); + /*virtual*/ void handleSplitTo(ExtendableRecording& other); + + private: + Recording mAcceptedRecording; + Recording mPotentialRecording; + }; + + class ExtendablePeriodicRecording + : public LLStopWatchControlsMixin<ExtendablePeriodicRecording> + { + public: + ExtendablePeriodicRecording(); + void extend(); + + PeriodicRecording& getResults() { return mAcceptedRecording; } + const PeriodicRecording& getResults() const {return mAcceptedRecording;} + + void nextPeriod() { mPotentialRecording.nextPeriod(); } + + private: + // implementation for LLStopWatchControlsMixin + /*virtual*/ void handleStart(); + /*virtual*/ void handleStop(); + /*virtual*/ void handleReset(); + /*virtual*/ void handleSplitTo(ExtendablePeriodicRecording& other); + + private: + PeriodicRecording mAcceptedRecording; + PeriodicRecording mPotentialRecording; + }; } #endif // LL_LLTRACERECORDING_H diff --git a/indra/llcommon/lltracethreadrecorder.cpp b/indra/llcommon/lltracethreadrecorder.cpp index 914bfb55dc..be3e585ef8 100644 --- a/indra/llcommon/lltracethreadrecorder.cpp +++ b/indra/llcommon/lltracethreadrecorder.cpp @@ -1,24 +1,24 @@ -/** +/** * @file lltracethreadrecorder.cpp * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2012, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -41,203 +41,203 @@ static ThreadRecorder* sMasterThreadRecorder = NULL; /////////////////////////////////////////////////////////////////////// ThreadRecorder::ThreadRecorder() -: mParentRecorder(NULL) +: mParentRecorder(NULL) { - init(); + init(); } void ThreadRecorder::init() { #if LL_TRACE_ENABLED - LLThreadLocalSingletonPointer<BlockTimerStackRecord>::setInstance(&mBlockTimerStackRecord); - //NB: the ordering of initialization in this function is very fragile due to a large number of implicit dependencies - set_thread_recorder(this); - BlockTimerStatHandle& root_time_block = BlockTimer::getRootTimeBlock(); + LLThreadLocalSingletonPointer<BlockTimerStackRecord>::setInstance(&mBlockTimerStackRecord); + //NB: the ordering of initialization in this function is very fragile due to a large number of implicit dependencies + set_thread_recorder(this); + BlockTimerStatHandle& root_time_block = BlockTimer::getRootTimeBlock(); - BlockTimerStackRecord* timer_stack = LLThreadLocalSingletonPointer<BlockTimerStackRecord>::getInstance(); - timer_stack->mTimeBlock = &root_time_block; - timer_stack->mActiveTimer = NULL; + BlockTimerStackRecord* timer_stack = LLThreadLocalSingletonPointer<BlockTimerStackRecord>::getInstance(); + timer_stack->mTimeBlock = &root_time_block; + timer_stack->mActiveTimer = NULL; - mNumTimeBlockTreeNodes = AccumulatorBuffer<TimeBlockAccumulator>::getDefaultBuffer()->size(); + mNumTimeBlockTreeNodes = AccumulatorBuffer<TimeBlockAccumulator>::getDefaultBuffer()->size(); - mTimeBlockTreeNodes = new TimeBlockTreeNode[mNumTimeBlockTreeNodes]; + mTimeBlockTreeNodes = new TimeBlockTreeNode[mNumTimeBlockTreeNodes]; - activate(&mThreadRecordingBuffers); + activate(&mThreadRecordingBuffers); - // initialize time block parent pointers - for (auto& base : BlockTimerStatHandle::instance_snapshot()) - { - // because of indirect derivation from LLInstanceTracker, have to downcast - BlockTimerStatHandle& time_block = static_cast<BlockTimerStatHandle&>(base); - TimeBlockTreeNode& tree_node = mTimeBlockTreeNodes[time_block.getIndex()]; - tree_node.mBlock = &time_block; - tree_node.mParent = &root_time_block; + // initialize time block parent pointers + for (auto& base : BlockTimerStatHandle::instance_snapshot()) + { + // because of indirect derivation from LLInstanceTracker, have to downcast + BlockTimerStatHandle& time_block = static_cast<BlockTimerStatHandle&>(base); + TimeBlockTreeNode& tree_node = mTimeBlockTreeNodes[time_block.getIndex()]; + tree_node.mBlock = &time_block; + tree_node.mParent = &root_time_block; - time_block.getCurrentAccumulator().mParent = &root_time_block; - } + time_block.getCurrentAccumulator().mParent = &root_time_block; + } - mRootTimer = new BlockTimer(root_time_block); - timer_stack->mActiveTimer = mRootTimer; + mRootTimer = new BlockTimer(root_time_block); + timer_stack->mActiveTimer = mRootTimer; - BlockTimer::getRootTimeBlock().getCurrentAccumulator().mActiveCount = 1; + BlockTimer::getRootTimeBlock().getCurrentAccumulator().mActiveCount = 1; - //claim_alloc(gTraceMemStat, this); - //claim_alloc(gTraceMemStat, mRootTimer); - //claim_alloc(gTraceMemStat, sizeof(TimeBlockTreeNode) * mNumTimeBlockTreeNodes); + //claim_alloc(gTraceMemStat, this); + //claim_alloc(gTraceMemStat, mRootTimer); + //claim_alloc(gTraceMemStat, sizeof(TimeBlockTreeNode) * mNumTimeBlockTreeNodes); #endif } ThreadRecorder::ThreadRecorder( ThreadRecorder& parent ) -: mParentRecorder(&parent) +: mParentRecorder(&parent) { - init(); - mParentRecorder->addChildRecorder(this); + init(); + mParentRecorder->addChildRecorder(this); } ThreadRecorder::~ThreadRecorder() { #if LL_TRACE_ENABLED - LLThreadLocalSingletonPointer<BlockTimerStackRecord>::setInstance(NULL); + LLThreadLocalSingletonPointer<BlockTimerStackRecord>::setInstance(NULL); - //disclaim_alloc(gTraceMemStat, this); - //disclaim_alloc(gTraceMemStat, sizeof(BlockTimer)); - //disclaim_alloc(gTraceMemStat, sizeof(TimeBlockTreeNode) * mNumTimeBlockTreeNodes); + //disclaim_alloc(gTraceMemStat, this); + //disclaim_alloc(gTraceMemStat, sizeof(BlockTimer)); + //disclaim_alloc(gTraceMemStat, sizeof(TimeBlockTreeNode) * mNumTimeBlockTreeNodes); - deactivate(&mThreadRecordingBuffers); + deactivate(&mThreadRecordingBuffers); - delete mRootTimer; + delete mRootTimer; - if (!mActiveRecordings.empty()) - { - std::for_each(mActiveRecordings.begin(), mActiveRecordings.end(), DeletePointer()); - mActiveRecordings.clear(); - } + if (!mActiveRecordings.empty()) + { + std::for_each(mActiveRecordings.begin(), mActiveRecordings.end(), DeletePointer()); + mActiveRecordings.clear(); + } - set_thread_recorder(NULL); - delete[] mTimeBlockTreeNodes; + set_thread_recorder(NULL); + delete[] mTimeBlockTreeNodes; - if (mParentRecorder) - { - mParentRecorder->removeChildRecorder(this); - } + if (mParentRecorder) + { + mParentRecorder->removeChildRecorder(this); + } #endif } TimeBlockTreeNode* ThreadRecorder::getTimeBlockTreeNode( size_t index ) { #if LL_TRACE_ENABLED - if (0 <= index && index < mNumTimeBlockTreeNodes) - { - return &mTimeBlockTreeNodes[index]; - } + if (0 <= index && index < mNumTimeBlockTreeNodes) + { + return &mTimeBlockTreeNodes[index]; + } #endif - return NULL; + return NULL; } AccumulatorBufferGroup* ThreadRecorder::activate( AccumulatorBufferGroup* recording) { #if LL_TRACE_ENABLED - ActiveRecording* active_recording = new ActiveRecording(recording); - if (!mActiveRecordings.empty()) - { - AccumulatorBufferGroup& prev_active_recording = mActiveRecordings.back()->mPartialRecording; - prev_active_recording.sync(); - BlockTimer::updateTimes(); - prev_active_recording.handOffTo(active_recording->mPartialRecording); - } - mActiveRecordings.push_back(active_recording); - - mActiveRecordings.back()->mPartialRecording.makeCurrent(); - return &active_recording->mPartialRecording; + ActiveRecording* active_recording = new ActiveRecording(recording); + if (!mActiveRecordings.empty()) + { + AccumulatorBufferGroup& prev_active_recording = mActiveRecordings.back()->mPartialRecording; + prev_active_recording.sync(); + BlockTimer::updateTimes(); + prev_active_recording.handOffTo(active_recording->mPartialRecording); + } + mActiveRecordings.push_back(active_recording); + + mActiveRecordings.back()->mPartialRecording.makeCurrent(); + return &active_recording->mPartialRecording; #else - return NULL; + return NULL; #endif } ThreadRecorder::active_recording_list_t::iterator ThreadRecorder::bringUpToDate( AccumulatorBufferGroup* recording ) { #if LL_TRACE_ENABLED - if (mActiveRecordings.empty()) return mActiveRecordings.end(); - - mActiveRecordings.back()->mPartialRecording.sync(); - BlockTimer::updateTimes(); - - active_recording_list_t::reverse_iterator it, end_it; - for (it = mActiveRecordings.rbegin(), end_it = mActiveRecordings.rend(); - it != end_it; - ++it) - { - ActiveRecording* cur_recording = *it; - - active_recording_list_t::reverse_iterator next_it = it; - ++next_it; - - // if we have another recording further down in the stack... - if (next_it != mActiveRecordings.rend()) - { - // ...push our gathered data down to it - (*next_it)->mPartialRecording.append(cur_recording->mPartialRecording); - } - - // copy accumulated measurements into result buffer and clear accumulator (mPartialRecording) - cur_recording->movePartialToTarget(); - - if (cur_recording->mTargetRecording == recording) - { - // found the recording, so return it - break; - } - } - - if (it == end_it) - { - LL_WARNS() << "Recording not active on this thread" << LL_ENDL; - } - - return (++it).base(); + if (mActiveRecordings.empty()) return mActiveRecordings.end(); + + mActiveRecordings.back()->mPartialRecording.sync(); + BlockTimer::updateTimes(); + + active_recording_list_t::reverse_iterator it, end_it; + for (it = mActiveRecordings.rbegin(), end_it = mActiveRecordings.rend(); + it != end_it; + ++it) + { + ActiveRecording* cur_recording = *it; + + active_recording_list_t::reverse_iterator next_it = it; + ++next_it; + + // if we have another recording further down in the stack... + if (next_it != mActiveRecordings.rend()) + { + // ...push our gathered data down to it + (*next_it)->mPartialRecording.append(cur_recording->mPartialRecording); + } + + // copy accumulated measurements into result buffer and clear accumulator (mPartialRecording) + cur_recording->movePartialToTarget(); + + if (cur_recording->mTargetRecording == recording) + { + // found the recording, so return it + break; + } + } + + if (it == end_it) + { + LL_WARNS() << "Recording not active on this thread" << LL_ENDL; + } + + return (++it).base(); #else - return ThreadRecorder::active_recording_list_t::iterator(); + return ThreadRecorder::active_recording_list_t::iterator(); #endif } void ThreadRecorder::deactivate( AccumulatorBufferGroup* recording ) { #if LL_TRACE_ENABLED - active_recording_list_t::iterator recording_it = bringUpToDate(recording); - // this method should only be called on a thread where the recorder is active - llassert_always(recording_it != mActiveRecordings.end()); - - ActiveRecording* recording_to_remove = *recording_it; - bool was_current = recording_to_remove->mPartialRecording.isCurrent(); - llassert(recording_to_remove->mTargetRecording == recording); - mActiveRecordings.erase(recording_it); - if (was_current) - { - if (mActiveRecordings.empty()) - { - AccumulatorBufferGroup::clearCurrent(); - } - else - { - mActiveRecordings.back()->mPartialRecording.makeCurrent(); - } - } - delete recording_to_remove; + active_recording_list_t::iterator recording_it = bringUpToDate(recording); + // this method should only be called on a thread where the recorder is active + llassert_always(recording_it != mActiveRecordings.end()); + + ActiveRecording* recording_to_remove = *recording_it; + bool was_current = recording_to_remove->mPartialRecording.isCurrent(); + llassert(recording_to_remove->mTargetRecording == recording); + mActiveRecordings.erase(recording_it); + if (was_current) + { + if (mActiveRecordings.empty()) + { + AccumulatorBufferGroup::clearCurrent(); + } + else + { + mActiveRecordings.back()->mPartialRecording.makeCurrent(); + } + } + delete recording_to_remove; #endif } -ThreadRecorder::ActiveRecording::ActiveRecording( AccumulatorBufferGroup* target ) -: mTargetRecording(target) +ThreadRecorder::ActiveRecording::ActiveRecording( AccumulatorBufferGroup* target ) +: mTargetRecording(target) {} void ThreadRecorder::ActiveRecording::movePartialToTarget() { #if LL_TRACE_ENABLED - mTargetRecording->append(mPartialRecording); - // reset based on self to keep history - mPartialRecording.reset(&mPartialRecording); + mTargetRecording->append(mPartialRecording); + // reset based on self to keep history + mPartialRecording.reset(&mPartialRecording); #endif } @@ -246,30 +246,30 @@ void ThreadRecorder::ActiveRecording::movePartialToTarget() void ThreadRecorder::addChildRecorder( class ThreadRecorder* child ) { #if LL_TRACE_ENABLED - { LLMutexLock lock(&mChildListMutex); - mChildThreadRecorders.push_back(child); - } + { LLMutexLock lock(&mChildListMutex); + mChildThreadRecorders.push_back(child); + } #endif } // called by child thread void ThreadRecorder::removeChildRecorder( class ThreadRecorder* child ) -{ +{ #if LL_TRACE_ENABLED - { LLMutexLock lock(&mChildListMutex); - mChildThreadRecorders.remove(child); - } + { LLMutexLock lock(&mChildListMutex); + mChildThreadRecorders.remove(child); + } #endif } void ThreadRecorder::pushToParent() { #if LL_TRACE_ENABLED - { LLMutexLock lock(&mSharedRecordingMutex); - LLTrace::get_thread_recorder()->bringUpToDate(&mThreadRecordingBuffers); - mSharedRecordingBuffers.append(mThreadRecordingBuffers); - mThreadRecordingBuffers.reset(); - } + { LLMutexLock lock(&mSharedRecordingMutex); + LLTrace::get_thread_recorder()->bringUpToDate(&mThreadRecordingBuffers); + mSharedRecordingBuffers.append(mThreadRecordingBuffers); + mThreadRecordingBuffers.reset(); + } #endif } @@ -278,48 +278,48 @@ void ThreadRecorder::pullFromChildren() { #if LL_TRACE_ENABLED LL_PROFILE_ZONE_SCOPED_CATEGORY_STATS; - if (mActiveRecordings.empty()) return; + if (mActiveRecordings.empty()) return; - { LLMutexLock lock(&mChildListMutex); + { LLMutexLock lock(&mChildListMutex); - AccumulatorBufferGroup& target_recording_buffers = mActiveRecordings.back()->mPartialRecording; - target_recording_buffers.sync(); - for (LLTrace::ThreadRecorder* rec : mChildThreadRecorders) - { LLMutexLock lock(&(rec->mSharedRecordingMutex)); + AccumulatorBufferGroup& target_recording_buffers = mActiveRecordings.back()->mPartialRecording; + target_recording_buffers.sync(); + for (LLTrace::ThreadRecorder* rec : mChildThreadRecorders) + { LLMutexLock lock(&(rec->mSharedRecordingMutex)); - target_recording_buffers.merge(rec->mSharedRecordingBuffers); - rec->mSharedRecordingBuffers.reset(); - } - } + target_recording_buffers.merge(rec->mSharedRecordingBuffers); + rec->mSharedRecordingBuffers.reset(); + } + } #endif } void set_master_thread_recorder( ThreadRecorder* recorder ) { - sMasterThreadRecorder = recorder; + sMasterThreadRecorder = recorder; } ThreadRecorder* get_master_thread_recorder() { - return sMasterThreadRecorder; + return sMasterThreadRecorder; } ThreadRecorder*& get_thread_recorder_ptr() { - static thread_local ThreadRecorder* s_thread_recorder; - return s_thread_recorder; + static thread_local ThreadRecorder* s_thread_recorder; + return s_thread_recorder; } ThreadRecorder* get_thread_recorder() { - return get_thread_recorder_ptr(); + return get_thread_recorder_ptr(); } void set_thread_recorder( ThreadRecorder* recorder ) { - get_thread_recorder_ptr() = recorder; + get_thread_recorder_ptr() = recorder; } } diff --git a/indra/llcommon/lltracethreadrecorder.h b/indra/llcommon/lltracethreadrecorder.h index 8ee6729ac6..e3eadfb0bd 100644 --- a/indra/llcommon/lltracethreadrecorder.h +++ b/indra/llcommon/lltracethreadrecorder.h @@ -1,25 +1,25 @@ -/** +/** * @file lltrace.h * @brief Runtime statistics accumulation. * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2012, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -35,67 +35,67 @@ namespace LLTrace { - class LL_COMMON_API ThreadRecorder - { - protected: - struct ActiveRecording; - typedef std::vector<ActiveRecording*> active_recording_list_t; - public: - ThreadRecorder(); - explicit ThreadRecorder(ThreadRecorder& parent); + class LL_COMMON_API ThreadRecorder + { + protected: + struct ActiveRecording; + typedef std::vector<ActiveRecording*> active_recording_list_t; + public: + ThreadRecorder(); + explicit ThreadRecorder(ThreadRecorder& parent); - ~ThreadRecorder(); + ~ThreadRecorder(); - AccumulatorBufferGroup* activate(AccumulatorBufferGroup* recording); - void deactivate(AccumulatorBufferGroup* recording); - active_recording_list_t::iterator bringUpToDate(AccumulatorBufferGroup* recording); + AccumulatorBufferGroup* activate(AccumulatorBufferGroup* recording); + void deactivate(AccumulatorBufferGroup* recording); + active_recording_list_t::iterator bringUpToDate(AccumulatorBufferGroup* recording); - void addChildRecorder(class ThreadRecorder* child); - void removeChildRecorder(class ThreadRecorder* child); + void addChildRecorder(class ThreadRecorder* child); + void removeChildRecorder(class ThreadRecorder* child); - // call this periodically to gather stats data from child threads - void pullFromChildren(); - void pushToParent(); + // call this periodically to gather stats data from child threads + void pullFromChildren(); + void pushToParent(); - TimeBlockTreeNode* getTimeBlockTreeNode(size_t index); + TimeBlockTreeNode* getTimeBlockTreeNode(size_t index); - protected: - void init(); + protected: + void init(); - protected: - struct ActiveRecording - { - ActiveRecording(AccumulatorBufferGroup* target); + protected: + struct ActiveRecording + { + ActiveRecording(AccumulatorBufferGroup* target); - AccumulatorBufferGroup* mTargetRecording; - AccumulatorBufferGroup mPartialRecording; + AccumulatorBufferGroup* mTargetRecording; + AccumulatorBufferGroup mPartialRecording; - void movePartialToTarget(); - }; + void movePartialToTarget(); + }; - AccumulatorBufferGroup mThreadRecordingBuffers; + AccumulatorBufferGroup mThreadRecordingBuffers; - BlockTimerStackRecord mBlockTimerStackRecord; - active_recording_list_t mActiveRecordings; + BlockTimerStackRecord mBlockTimerStackRecord; + active_recording_list_t mActiveRecordings; - class BlockTimer* mRootTimer; - TimeBlockTreeNode* mTimeBlockTreeNodes; - size_t mNumTimeBlockTreeNodes; - typedef std::list<class ThreadRecorder*> child_thread_recorder_list_t; + class BlockTimer* mRootTimer; + TimeBlockTreeNode* mTimeBlockTreeNodes; + size_t mNumTimeBlockTreeNodes; + typedef std::list<class ThreadRecorder*> child_thread_recorder_list_t; - child_thread_recorder_list_t mChildThreadRecorders; // list of child thread recorders associated with this master - LLMutex mChildListMutex; // protects access to child list - LLMutex mSharedRecordingMutex; - AccumulatorBufferGroup mSharedRecordingBuffers; - ThreadRecorder* mParentRecorder; + child_thread_recorder_list_t mChildThreadRecorders; // list of child thread recorders associated with this master + LLMutex mChildListMutex; // protects access to child list + LLMutex mSharedRecordingMutex; + AccumulatorBufferGroup mSharedRecordingBuffers; + ThreadRecorder* mParentRecorder; - }; + }; - ThreadRecorder* get_thread_recorder(); - void set_thread_recorder(ThreadRecorder*); + ThreadRecorder* get_thread_recorder(); + void set_thread_recorder(ThreadRecorder*); - void set_master_thread_recorder(ThreadRecorder*); - ThreadRecorder* get_master_thread_recorder(); + void set_master_thread_recorder(ThreadRecorder*); + ThreadRecorder* get_master_thread_recorder(); } #endif // LL_LLTRACETHREADRECORDER_H diff --git a/indra/llcommon/lltreeiterators.h b/indra/llcommon/lltreeiterators.h index ba861ae70b..cef501b987 100644 --- a/indra/llcommon/lltreeiterators.h +++ b/indra/llcommon/lltreeiterators.h @@ -33,25 +33,25 @@ * child of its own. Child nodes with multiple parents will be visited * once for each parent. Cycles in the graph will result in either an * infinite loop or an out-of-memory crash. You Have Been Warned. - * + * * $LicenseInfo:firstyear=2008&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -338,9 +338,9 @@ public: /// functors to extract the 'child begin' and 'child end' iterators from /// each node. LLTreeDFSIter(const ptr_type& node, const func_type& beginfunc, const func_type& endfunc) - : mBeginFunc(beginfunc), - mEndFunc(endfunc), - mSkipChildren(false) + : mBeginFunc(beginfunc), + mEndFunc(endfunc), + mSkipChildren(false) { // Only push back this node if it's non-NULL! if (node) @@ -364,13 +364,13 @@ private: ptr_type current = mPending.back(); // Remove it from mPending so we don't process it again later mPending.pop_back(); - if (!mSkipChildren) - { - // Add all its children to mPending - addChildren(current); - } - // reset flag after each step - mSkipChildren = false; + if (!mSkipChildren) + { + // Add all its children to mPending + addChildren(current); + } + // reset flag after each step + mSkipChildren = false; } /// equality bool equal(const self_type& that) const { return this->mPending == that.mPending; } @@ -400,7 +400,7 @@ private: /// functor to extract end() child iterator func_type mEndFunc; /// flag which controls traversal of children (skip children of current node if true) - bool mSkipChildren; + bool mSkipChildren; }; /** @@ -446,10 +446,10 @@ public: /// functors to extract the 'child begin' and 'child end' iterators from /// each node. LLTreeDFSPostIter(const ptr_type& node, const func_type& beginfunc, const func_type& endfunc) - : mBeginFunc(beginfunc), - mEndFunc(endfunc), - mSkipAncestors(false) - { + : mBeginFunc(beginfunc), + mEndFunc(endfunc), + mSkipAncestors(false) + { if (! node) return; mPending.push_back(typename list_type::value_type(node, false)); @@ -478,24 +478,24 @@ private: /// implement dereference/indirection operators ptr_type& dereference() const { return const_cast<ptr_type&>(mPending.back().first); } - struct isOpen - { - bool operator()(const typename list_type::value_type& item) - { - return item.second; - } - }; + struct isOpen + { + bool operator()(const typename list_type::value_type& item) + { + return item.second; + } + }; /// Call this each time we change mPending.back() -- that is, every time /// we're about to change the value returned by dereference(). If we /// haven't yet pushed the new node's children, do so now. void makeCurrent() { - if (mSkipAncestors) - { - mPending.erase(std::remove_if(mPending.begin(), mPending.end(), isOpen()), mPending.end()); - mSkipAncestors = false; - } + if (mSkipAncestors) + { + mPending.erase(std::remove_if(mPending.begin(), mPending.end(), isOpen()), mPending.end()); + mSkipAncestors = false; + } // Once we've popped the last node, this becomes a no-op. if (mPending.empty()) @@ -536,13 +536,13 @@ private: } /// list of the nodes yet to be processed - list_type mPending; + list_type mPending; /// functor to extract begin() child iterator - func_type mBeginFunc; + func_type mBeginFunc; /// functor to extract end() child iterator - func_type mEndFunc; - /// flags logic to skip traversal of ancestors of current node - bool mSkipAncestors; + func_type mEndFunc; + /// flags logic to skip traversal of ancestors of current node + bool mSkipAncestors; }; /** diff --git a/indra/llcommon/llunits.h b/indra/llcommon/llunits.h index 0fcb8281a0..122d4557bf 100644 --- a/indra/llcommon/llunits.h +++ b/indra/llcommon/llunits.h @@ -1,25 +1,25 @@ -/** +/** * @file llunits.h * @brief Unit definitions * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2012, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -51,10 +51,10 @@ LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Gigabytes); namespace LLUnits { // technically, these are kibibits, mibibits, etc. but we should stick with commonly accepted terminology -LL_DECLARE_DERIVED_UNIT(Bits, "b", Bytes, * 8 ); -LL_DECLARE_DERIVED_UNIT(Kilobits, "Kb", Bits, / 1024); -LL_DECLARE_DERIVED_UNIT(Megabits, "Mb", Kilobits, / 1024); -LL_DECLARE_DERIVED_UNIT(Gigabits, "Gb", Megabits, / 1024); +LL_DECLARE_DERIVED_UNIT(Bits, "b", Bytes, * 8 ); +LL_DECLARE_DERIVED_UNIT(Kilobits, "Kb", Bits, / 1024); +LL_DECLARE_DERIVED_UNIT(Megabits, "Mb", Kilobits, / 1024); +LL_DECLARE_DERIVED_UNIT(Gigabits, "Gb", Megabits, / 1024); } LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Bits); @@ -65,12 +65,12 @@ LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Gigabits); namespace LLUnits { LL_DECLARE_BASE_UNIT(Seconds, "s"); -LL_DECLARE_DERIVED_UNIT(Minutes, "min", Seconds, / 60); -LL_DECLARE_DERIVED_UNIT(Hours, "h", Minutes, / 60); -LL_DECLARE_DERIVED_UNIT(Days, "d", Hours, / 24); -LL_DECLARE_DERIVED_UNIT(Milliseconds, "ms", Seconds, * 1000); -LL_DECLARE_DERIVED_UNIT(Microseconds, "\x09\x3cs", Milliseconds, * 1000); -LL_DECLARE_DERIVED_UNIT(Nanoseconds, "ns", Microseconds, * 1000); +LL_DECLARE_DERIVED_UNIT(Minutes, "min", Seconds, / 60); +LL_DECLARE_DERIVED_UNIT(Hours, "h", Minutes, / 60); +LL_DECLARE_DERIVED_UNIT(Days, "d", Hours, / 24); +LL_DECLARE_DERIVED_UNIT(Milliseconds, "ms", Seconds, * 1000); +LL_DECLARE_DERIVED_UNIT(Microseconds, "\x09\x3cs", Milliseconds, * 1000); +LL_DECLARE_DERIVED_UNIT(Nanoseconds, "ns", Microseconds, * 1000); } LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Seconds); @@ -84,9 +84,9 @@ LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Nanoseconds); namespace LLUnits { LL_DECLARE_BASE_UNIT(Meters, "m"); -LL_DECLARE_DERIVED_UNIT(Kilometers, "km", Meters, / 1000); -LL_DECLARE_DERIVED_UNIT(Centimeters, "cm", Meters, * 100); -LL_DECLARE_DERIVED_UNIT(Millimeters, "mm", Meters, * 1000); +LL_DECLARE_DERIVED_UNIT(Kilometers, "km", Meters, / 1000); +LL_DECLARE_DERIVED_UNIT(Centimeters, "cm", Meters, * 100); +LL_DECLARE_DERIVED_UNIT(Millimeters, "mm", Meters, * 1000); } LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Meters); diff --git a/indra/llcommon/llunittype.h b/indra/llcommon/llunittype.h index 81f244e422..83ce0d05a8 100644 --- a/indra/llcommon/llunittype.h +++ b/indra/llcommon/llunittype.h @@ -1,25 +1,25 @@ -/** +/** * @file llunit.h * @brief Unit conversion classes * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2012, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -32,122 +32,122 @@ #include "llerror.h" //lightweight replacement of type traits for simple type equality check -template<typename S, typename T> +template<typename S, typename T> struct LLIsSameType { - static const bool value = false; + static const bool value = false; }; template<typename T> struct LLIsSameType<T, T> { - static const bool value = true; + static const bool value = true; }; // workaround for decltype() not existing and typeof() not working inline in gcc 4.2 template<typename S, typename T> struct LLResultTypeAdd { - typedef LL_TYPEOF(S() + T()) type_t; + typedef LL_TYPEOF(S() + T()) type_t; }; template<typename S, typename T> struct LLResultTypeSubtract { - typedef LL_TYPEOF(S() - T()) type_t; + typedef LL_TYPEOF(S() - T()) type_t; }; template<typename S, typename T> struct LLResultTypeMultiply { - typedef LL_TYPEOF(S() * T()) type_t; + typedef LL_TYPEOF(S() * T()) type_t; }; template<typename S, typename T> struct LLResultTypeDivide { - typedef LL_TYPEOF(S() / T(1)) type_t; + typedef LL_TYPEOF(S() / T(1)) type_t; }; template<typename S, typename T> struct LLResultTypePromote { - typedef LL_TYPEOF((true) ? S() : T()) type_t; + typedef LL_TYPEOF((true) ? S() : T()) type_t; }; template<typename STORAGE_TYPE, typename UNITS> struct LLUnit { - typedef LLUnit<STORAGE_TYPE, UNITS> self_t; - typedef STORAGE_TYPE storage_t; - typedef void is_unit_t; - - // value initialization - LL_FORCE_INLINE explicit LLUnit(storage_t value = storage_t()) - : mValue(value) - {} - - - LL_FORCE_INLINE static self_t convert(self_t v) - { - return v; - } - - template<typename FROM_STORAGE_TYPE> - LL_FORCE_INLINE static self_t convert(LLUnit<FROM_STORAGE_TYPE, UNITS> v) - { - self_t result; - result.mValue = (STORAGE_TYPE)v.value(); - return result; - } - - template<typename FROM_UNITS> - LL_FORCE_INLINE static self_t convert(LLUnit<STORAGE_TYPE, FROM_UNITS> v) - { - self_t result; - STORAGE_TYPE divisor = ll_convert_units(v, result); - result.mValue /= divisor; - return result; - } - - template<typename FROM_STORAGE_TYPE, typename FROM_UNITS> - LL_FORCE_INLINE static self_t convert(LLUnit<FROM_STORAGE_TYPE, FROM_UNITS> v) - { - typedef typename LLResultTypePromote<FROM_STORAGE_TYPE, STORAGE_TYPE>::type_t result_storage_t; - LLUnit<result_storage_t, UNITS> result; - result_storage_t divisor = ll_convert_units(v, result); - result.value(result.value() / divisor); - return self_t(result.value()); - } - - - // unit initialization and conversion - template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS> - LL_FORCE_INLINE LLUnit(LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) - : mValue(convert(other).mValue) - {} - - LL_FORCE_INLINE storage_t value() const - { - return mValue; - } - - LL_FORCE_INLINE void value(const storage_t& value) - { - mValue = value; - } - - template<typename NEW_UNITS> - storage_t valueInUnits() const - { - return LLUnit<storage_t, NEW_UNITS>(*this).value(); - } - - template<typename NEW_UNITS> - void valueInUnits(const storage_t& value) const - { - *this = LLUnit<storage_t, NEW_UNITS>(value); - } + typedef LLUnit<STORAGE_TYPE, UNITS> self_t; + typedef STORAGE_TYPE storage_t; + typedef void is_unit_t; + + // value initialization + LL_FORCE_INLINE explicit LLUnit(storage_t value = storage_t()) + : mValue(value) + {} + + + LL_FORCE_INLINE static self_t convert(self_t v) + { + return v; + } + + template<typename FROM_STORAGE_TYPE> + LL_FORCE_INLINE static self_t convert(LLUnit<FROM_STORAGE_TYPE, UNITS> v) + { + self_t result; + result.mValue = (STORAGE_TYPE)v.value(); + return result; + } + + template<typename FROM_UNITS> + LL_FORCE_INLINE static self_t convert(LLUnit<STORAGE_TYPE, FROM_UNITS> v) + { + self_t result; + STORAGE_TYPE divisor = ll_convert_units(v, result); + result.mValue /= divisor; + return result; + } + + template<typename FROM_STORAGE_TYPE, typename FROM_UNITS> + LL_FORCE_INLINE static self_t convert(LLUnit<FROM_STORAGE_TYPE, FROM_UNITS> v) + { + typedef typename LLResultTypePromote<FROM_STORAGE_TYPE, STORAGE_TYPE>::type_t result_storage_t; + LLUnit<result_storage_t, UNITS> result; + result_storage_t divisor = ll_convert_units(v, result); + result.value(result.value() / divisor); + return self_t(result.value()); + } + + + // unit initialization and conversion + template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS> + LL_FORCE_INLINE LLUnit(LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) + : mValue(convert(other).mValue) + {} + + LL_FORCE_INLINE storage_t value() const + { + return mValue; + } + + LL_FORCE_INLINE void value(const storage_t& value) + { + mValue = value; + } + + template<typename NEW_UNITS> + storage_t valueInUnits() const + { + return LLUnit<storage_t, NEW_UNITS>(*this).value(); + } + + template<typename NEW_UNITS> + void valueInUnits(const storage_t& value) const + { + *this = LLUnit<storage_t, NEW_UNITS>(value); + } LL_FORCE_INLINE operator storage_t() const { @@ -155,81 +155,81 @@ struct LLUnit } /*LL_FORCE_INLINE self_t& operator= (storage_t v) - { - value(v); + { + value(v); return *this; - }*/ - - LL_FORCE_INLINE void operator += (self_t other) - { - mValue += convert(other).mValue; - } - - LL_FORCE_INLINE void operator -= (self_t other) - { - mValue -= convert(other).mValue; - } - - LL_FORCE_INLINE void operator *= (const storage_t& multiplicand) - { - mValue *= multiplicand; - } - - LL_FORCE_INLINE void operator *= (const self_t& multiplicand) - { - // spurious use of dependent type to stop gcc from triggering the static assertion before instantiating the template - LL_BAD_TEMPLATE_INSTANTIATION(STORAGE_TYPE, "Multiplication of unit types not supported."); - } - - LL_FORCE_INLINE void operator /= (const storage_t& divisor) - { - mValue /= divisor; - } - - void operator /= (const self_t& divisor) - { - // spurious use of dependent type to stop gcc from triggering the static assertion before instantiating the template - LL_BAD_TEMPLATE_INSTANTIATION(STORAGE_TYPE, "Illegal in-place division of unit types."); - } - - template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS> - LL_FORCE_INLINE bool operator == (const LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS>& other) const - { - return mValue == convert(other).value(); - } - - template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS> - LL_FORCE_INLINE bool operator != (const LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS>& other) const - { - return mValue != convert(other).value(); - } - - template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS> - LL_FORCE_INLINE bool operator < (const LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS>& other) const - { - return mValue < convert(other).value(); - } - - template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS> - LL_FORCE_INLINE bool operator <= (const LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS>& other) const - { - return mValue <= convert(other).value(); - } - - template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS> - LL_FORCE_INLINE bool operator > (const LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS>& other) const - { - return mValue > convert(other).value(); - } - - template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS> - LL_FORCE_INLINE bool operator >= (const LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS>& other) const - { - return mValue >= convert(other).value(); - } + }*/ + + LL_FORCE_INLINE void operator += (self_t other) + { + mValue += convert(other).mValue; + } + + LL_FORCE_INLINE void operator -= (self_t other) + { + mValue -= convert(other).mValue; + } + + LL_FORCE_INLINE void operator *= (const storage_t& multiplicand) + { + mValue *= multiplicand; + } + + LL_FORCE_INLINE void operator *= (const self_t& multiplicand) + { + // spurious use of dependent type to stop gcc from triggering the static assertion before instantiating the template + LL_BAD_TEMPLATE_INSTANTIATION(STORAGE_TYPE, "Multiplication of unit types not supported."); + } + + LL_FORCE_INLINE void operator /= (const storage_t& divisor) + { + mValue /= divisor; + } + + void operator /= (const self_t& divisor) + { + // spurious use of dependent type to stop gcc from triggering the static assertion before instantiating the template + LL_BAD_TEMPLATE_INSTANTIATION(STORAGE_TYPE, "Illegal in-place division of unit types."); + } + + template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS> + LL_FORCE_INLINE bool operator == (const LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS>& other) const + { + return mValue == convert(other).value(); + } + + template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS> + LL_FORCE_INLINE bool operator != (const LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS>& other) const + { + return mValue != convert(other).value(); + } + + template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS> + LL_FORCE_INLINE bool operator < (const LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS>& other) const + { + return mValue < convert(other).value(); + } + + template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS> + LL_FORCE_INLINE bool operator <= (const LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS>& other) const + { + return mValue <= convert(other).value(); + } + + template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS> + LL_FORCE_INLINE bool operator > (const LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS>& other) const + { + return mValue > convert(other).value(); + } + + template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS> + LL_FORCE_INLINE bool operator >= (const LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS>& other) const + { + return mValue >= convert(other).value(); + } protected: - storage_t mValue; + storage_t mValue; }; template<typename STORAGE_TYPE, typename UNITS> @@ -251,161 +251,161 @@ std::istream& operator >>(std::istream& s, LLUnit<STORAGE_TYPE, UNITS>& unit) template<typename STORAGE_TYPE, typename UNITS> struct LLUnitImplicit : public LLUnit<STORAGE_TYPE, UNITS> { - typedef LLUnitImplicit<STORAGE_TYPE, UNITS> self_t; - typedef typename LLUnit<STORAGE_TYPE, UNITS>::storage_t storage_t; - typedef LLUnit<STORAGE_TYPE, UNITS> base_t; - - LL_FORCE_INLINE LLUnitImplicit(storage_t value = storage_t()) - : base_t(value) - {} - - template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS> - LL_FORCE_INLINE LLUnitImplicit(LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) - : base_t(other) - {} - - // unlike LLUnit, LLUnitImplicit is *implicitly* convertable to a POD value (F32, S32, etc) - // this allows for interoperability with legacy code - LL_FORCE_INLINE operator storage_t() const - { - return base_t::value(); - } - - using base_t::operator +=; - LL_FORCE_INLINE void operator += (storage_t value) - { + typedef LLUnitImplicit<STORAGE_TYPE, UNITS> self_t; + typedef typename LLUnit<STORAGE_TYPE, UNITS>::storage_t storage_t; + typedef LLUnit<STORAGE_TYPE, UNITS> base_t; + + LL_FORCE_INLINE LLUnitImplicit(storage_t value = storage_t()) + : base_t(value) + {} + + template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS> + LL_FORCE_INLINE LLUnitImplicit(LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) + : base_t(other) + {} + + // unlike LLUnit, LLUnitImplicit is *implicitly* convertable to a POD value (F32, S32, etc) + // this allows for interoperability with legacy code + LL_FORCE_INLINE operator storage_t() const + { + return base_t::value(); + } + + using base_t::operator +=; + LL_FORCE_INLINE void operator += (storage_t value) + { base_t::mValue += value; - } + } - // this overload exists to explicitly catch use of another implicit unit - // without ambiguity between conversion to storage_t vs conversion to base_t - template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS> - LL_FORCE_INLINE void operator += (LLUnitImplicit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) - { + // this overload exists to explicitly catch use of another implicit unit + // without ambiguity between conversion to storage_t vs conversion to base_t + template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS> + LL_FORCE_INLINE void operator += (LLUnitImplicit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) + { base_t::mValue += base_t::convert(other).value(); - } + } - using base_t::operator -=; - LL_FORCE_INLINE void operator -= (storage_t value) - { + using base_t::operator -=; + LL_FORCE_INLINE void operator -= (storage_t value) + { base_t::mValue -= value; - } + } - // this overload exists to explicitly catch use of another implicit unit - // without ambiguity between conversion to storage_t vs conversion to base_t - template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS> - LL_FORCE_INLINE void operator -= (LLUnitImplicit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) - { + // this overload exists to explicitly catch use of another implicit unit + // without ambiguity between conversion to storage_t vs conversion to base_t + template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS> + LL_FORCE_INLINE void operator -= (LLUnitImplicit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) + { base_t::mValue -= base_t::convert(other).value(); - } - - template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS> - LL_FORCE_INLINE bool operator == (LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const - { - return base_t::mValue == base_t::convert(other).value(); - } - - template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS> - LL_FORCE_INLINE bool operator == (LLUnitImplicit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const - { - return base_t::mValue == base_t::convert(other).value(); - } - - template<typename STORAGE_T> - LL_FORCE_INLINE bool operator == (STORAGE_T other) const - { - return base_t::mValue == other; - } - - template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS> - LL_FORCE_INLINE bool operator != (LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const - { - return base_t::mValue != convert(other).value(); - } - - template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS> - LL_FORCE_INLINE bool operator != (LLUnitImplicit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const - { - return base_t::mValue != base_t::convert(other).value(); - } - - template<typename STORAGE_T> - LL_FORCE_INLINE bool operator != (STORAGE_T other) const - { - return base_t::mValue != other; - } - - template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS> - LL_FORCE_INLINE bool operator < (LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const - { - return base_t::mValue < base_t::convert(other).value(); - } - - template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS> - LL_FORCE_INLINE bool operator < (LLUnitImplicit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const - { - return base_t::mValue < base_t::convert(other).value(); - } - - template<typename STORAGE_T> - LL_FORCE_INLINE bool operator < (STORAGE_T other) const - { - return base_t::mValue < other; - } - - template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS> - LL_FORCE_INLINE bool operator <= (LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const - { - return base_t::mValue <= base_t::convert(other).value(); - } - - template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS> - LL_FORCE_INLINE bool operator <= (LLUnitImplicit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const - { - return base_t::mValue <= base_t::convert(other).value(); - } - - template<typename STORAGE_T> - LL_FORCE_INLINE bool operator <= (STORAGE_T other) const - { - return base_t::mValue <= other; - } - - template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS> - LL_FORCE_INLINE bool operator > (LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const - { - return base_t::mValue > base_t::convert(other).value(); - } - - template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS> - LL_FORCE_INLINE bool operator > (LLUnitImplicit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const - { - return base_t::mValue > base_t::convert(other).value(); - } - - template<typename STORAGE_T> - LL_FORCE_INLINE bool operator > (STORAGE_T other) const - { - return base_t::mValue > other; - } - - template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS> - LL_FORCE_INLINE bool operator >= (LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const - { - return base_t::mValue >= base_t::convert(other).value(); - } - - template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS> - LL_FORCE_INLINE bool operator >= (LLUnitImplicit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const - { - return base_t::mValue >= base_t::convert(other).value(); - } - - template<typename STORAGE_T> - LL_FORCE_INLINE bool operator >= (STORAGE_T other) const - { - return base_t::mValue >= other; - } + } + + template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS> + LL_FORCE_INLINE bool operator == (LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const + { + return base_t::mValue == base_t::convert(other).value(); + } + + template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS> + LL_FORCE_INLINE bool operator == (LLUnitImplicit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const + { + return base_t::mValue == base_t::convert(other).value(); + } + + template<typename STORAGE_T> + LL_FORCE_INLINE bool operator == (STORAGE_T other) const + { + return base_t::mValue == other; + } + + template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS> + LL_FORCE_INLINE bool operator != (LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const + { + return base_t::mValue != convert(other).value(); + } + + template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS> + LL_FORCE_INLINE bool operator != (LLUnitImplicit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const + { + return base_t::mValue != base_t::convert(other).value(); + } + + template<typename STORAGE_T> + LL_FORCE_INLINE bool operator != (STORAGE_T other) const + { + return base_t::mValue != other; + } + + template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS> + LL_FORCE_INLINE bool operator < (LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const + { + return base_t::mValue < base_t::convert(other).value(); + } + + template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS> + LL_FORCE_INLINE bool operator < (LLUnitImplicit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const + { + return base_t::mValue < base_t::convert(other).value(); + } + + template<typename STORAGE_T> + LL_FORCE_INLINE bool operator < (STORAGE_T other) const + { + return base_t::mValue < other; + } + + template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS> + LL_FORCE_INLINE bool operator <= (LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const + { + return base_t::mValue <= base_t::convert(other).value(); + } + + template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS> + LL_FORCE_INLINE bool operator <= (LLUnitImplicit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const + { + return base_t::mValue <= base_t::convert(other).value(); + } + + template<typename STORAGE_T> + LL_FORCE_INLINE bool operator <= (STORAGE_T other) const + { + return base_t::mValue <= other; + } + + template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS> + LL_FORCE_INLINE bool operator > (LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const + { + return base_t::mValue > base_t::convert(other).value(); + } + + template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS> + LL_FORCE_INLINE bool operator > (LLUnitImplicit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const + { + return base_t::mValue > base_t::convert(other).value(); + } + + template<typename STORAGE_T> + LL_FORCE_INLINE bool operator > (STORAGE_T other) const + { + return base_t::mValue > other; + } + + template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS> + LL_FORCE_INLINE bool operator >= (LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const + { + return base_t::mValue >= base_t::convert(other).value(); + } + + template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS> + LL_FORCE_INLINE bool operator >= (LLUnitImplicit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const + { + return base_t::mValue >= base_t::convert(other).value(); + } + + template<typename STORAGE_T> + LL_FORCE_INLINE bool operator >= (STORAGE_T other) const + { + return base_t::mValue >= other; + } }; template<typename STORAGE_TYPE, typename UNITS> @@ -427,49 +427,49 @@ std::istream& operator >>(std::istream& s, LLUnitImplicit<STORAGE_TYPE, UNITS>& template<typename S1, typename T1, typename S2, typename T2> LL_FORCE_INLINE S2 ll_convert_units(LLUnit<S1, T1> in, LLUnit<S2, T2>& out) { - S2 divisor(1); - - LL_STATIC_ASSERT((LLIsSameType<T1, T2>::value - || !LLIsSameType<T1, typename T1::base_unit_t>::value - || !LLIsSameType<T2, typename T2::base_unit_t>::value), - "conversion requires compatible units"); - - if (LLIsSameType<T1, T2>::value) - { - // T1 and T2 same type, just assign - out.value((S2)in.value()); - } - else if (T1::sLevel > T2::sLevel) - { - // reduce T1 - LLUnit<S2, typename T1::base_unit_t> new_in; - divisor *= (S2)ll_convert_units(in, new_in); - divisor *= (S2)ll_convert_units(new_in, out); - } - else - { - // reduce T2 - LLUnit<S2, typename T2::base_unit_t> new_out; - divisor *= (S2)ll_convert_units(in, new_out); - divisor *= (S2)ll_convert_units(new_out, out); - } - return divisor; + S2 divisor(1); + + LL_STATIC_ASSERT((LLIsSameType<T1, T2>::value + || !LLIsSameType<T1, typename T1::base_unit_t>::value + || !LLIsSameType<T2, typename T2::base_unit_t>::value), + "conversion requires compatible units"); + + if (LLIsSameType<T1, T2>::value) + { + // T1 and T2 same type, just assign + out.value((S2)in.value()); + } + else if (T1::sLevel > T2::sLevel) + { + // reduce T1 + LLUnit<S2, typename T1::base_unit_t> new_in; + divisor *= (S2)ll_convert_units(in, new_in); + divisor *= (S2)ll_convert_units(new_in, out); + } + else + { + // reduce T2 + LLUnit<S2, typename T2::base_unit_t> new_out; + divisor *= (S2)ll_convert_units(in, new_out); + divisor *= (S2)ll_convert_units(new_out, out); + } + return divisor; } template<typename T> struct LLStorageType { - typedef T type_t; + typedef T type_t; }; template<typename STORAGE_TYPE, typename UNITS> struct LLStorageType<LLUnit<STORAGE_TYPE, UNITS> > { - typedef STORAGE_TYPE type_t; + typedef STORAGE_TYPE type_t; }; -// all of these operators need to perform type promotion on the storage type of the units, so they -// cannot be expressed as operations on identical types with implicit unit conversion +// all of these operators need to perform type promotion on the storage type of the units, so they +// cannot be expressed as operations on identical types with implicit unit conversion // e.g. typeof(S32Bytes(x) + F32Megabytes(y)) <==> F32Bytes // @@ -478,64 +478,64 @@ struct LLStorageType<LLUnit<STORAGE_TYPE, UNITS> > template<typename STORAGE_TYPE1, typename UNITS1, typename STORAGE_TYPE2, typename UNITS2> LL_FORCE_INLINE LLUnit<typename LLResultTypeAdd<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> operator + (LLUnit<STORAGE_TYPE1, UNITS1> first, LLUnit<STORAGE_TYPE2, UNITS2> second) { - LLUnit<typename LLResultTypeAdd<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> result(first); - result += second; - return result; + LLUnit<typename LLResultTypeAdd<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> result(first); + result += second; + return result; } template<typename STORAGE_TYPE, typename UNITS, typename UNITLESS> LLUnit<STORAGE_TYPE, UNITS> operator + (LLUnit<STORAGE_TYPE, UNITS> first, UNITLESS second) { - LL_BAD_TEMPLATE_INSTANTIATION(STORAGE_TYPE, "operator + requires compatible unit types"); - return LLUnit<STORAGE_TYPE, UNITS>(0); + LL_BAD_TEMPLATE_INSTANTIATION(STORAGE_TYPE, "operator + requires compatible unit types"); + return LLUnit<STORAGE_TYPE, UNITS>(0); } template<typename STORAGE_TYPE, typename UNITS, typename UNITLESS> LLUnit<STORAGE_TYPE, UNITS> operator + (UNITLESS first, LLUnit<STORAGE_TYPE, UNITS> second) { - LL_BAD_TEMPLATE_INSTANTIATION(STORAGE_TYPE, "operator + requires compatible unit types"); - return LLUnit<STORAGE_TYPE, UNITS>(0); + LL_BAD_TEMPLATE_INSTANTIATION(STORAGE_TYPE, "operator + requires compatible unit types"); + return LLUnit<STORAGE_TYPE, UNITS>(0); } template<typename STORAGE_TYPE1, typename UNITS1, typename STORAGE_TYPE2, typename UNITS2> LL_FORCE_INLINE LLUnitImplicit<typename LLResultTypeAdd<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> operator + (LLUnitImplicit<STORAGE_TYPE1, UNITS1> first, LLUnitImplicit<STORAGE_TYPE2, UNITS2> second) { - LLUnitImplicit<typename LLResultTypeAdd<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> result(first); - result += second; - return result; + LLUnitImplicit<typename LLResultTypeAdd<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> result(first); + result += second; + return result; } template<typename STORAGE_TYPE1, typename UNITS1, typename STORAGE_TYPE2, typename UNITS2> LL_FORCE_INLINE LLUnitImplicit<typename LLResultTypeAdd<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> operator + (LLUnit<STORAGE_TYPE1, UNITS1> first, LLUnitImplicit<STORAGE_TYPE2, UNITS2> second) { - LLUnitImplicit<typename LLResultTypeAdd<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> result(first); - result += second; - return result; + LLUnitImplicit<typename LLResultTypeAdd<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> result(first); + result += second; + return result; } template<typename STORAGE_TYPE1, typename UNITS1, typename STORAGE_TYPE2, typename UNITS2> LL_FORCE_INLINE LLUnitImplicit<typename LLResultTypeAdd<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> operator + (LLUnitImplicit<STORAGE_TYPE1, UNITS1> first, LLUnit<STORAGE_TYPE2, UNITS2> second) { - LLUnitImplicit<typename LLResultTypeAdd<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> result(first); - result += LLUnitImplicit<STORAGE_TYPE1, UNITS1>(second); - return result; + LLUnitImplicit<typename LLResultTypeAdd<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> result(first); + result += LLUnitImplicit<STORAGE_TYPE1, UNITS1>(second); + return result; } template<typename STORAGE_TYPE, typename UNITS, typename UNITLESS_TYPE> LL_FORCE_INLINE LLUnitImplicit<typename LLResultTypeAdd<STORAGE_TYPE, typename LLStorageType<UNITLESS_TYPE>::type_t>::type_t, UNITS> operator + (LLUnitImplicit<STORAGE_TYPE, UNITS> first, UNITLESS_TYPE second) { - LLUnitImplicit<typename LLResultTypeAdd<STORAGE_TYPE, typename LLStorageType<UNITLESS_TYPE>::type_t>::type_t, UNITS> result(first); - result += second; - return result; + LLUnitImplicit<typename LLResultTypeAdd<STORAGE_TYPE, typename LLStorageType<UNITLESS_TYPE>::type_t>::type_t, UNITS> result(first); + result += second; + return result; } template<typename STORAGE_TYPE, typename UNITS, typename UNITLESS_TYPE> LL_FORCE_INLINE LLUnitImplicit<typename LLResultTypeAdd<typename LLStorageType<UNITLESS_TYPE>::type_t, STORAGE_TYPE>:: - type_t, UNITS> operator + (UNITLESS_TYPE first, LLUnitImplicit<STORAGE_TYPE, UNITS> second) + type_t, UNITS> operator + (UNITLESS_TYPE first, LLUnitImplicit<STORAGE_TYPE, UNITS> second) { - LLUnitImplicit<typename LLResultTypeAdd<typename LLStorageType<UNITLESS_TYPE>::type_t, STORAGE_TYPE>::type_t, UNITS> result(first); - result += second; - return result; + LLUnitImplicit<typename LLResultTypeAdd<typename LLStorageType<UNITLESS_TYPE>::type_t, STORAGE_TYPE>::type_t, UNITS> result(first); + result += second; + return result; } // @@ -544,63 +544,63 @@ LL_FORCE_INLINE LLUnitImplicit<typename LLResultTypeAdd<typename LLStorageType<U template<typename STORAGE_TYPE1, typename UNITS1, typename STORAGE_TYPE2, typename UNITS2> LL_FORCE_INLINE LLUnit<typename LLResultTypeSubtract<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> operator - (LLUnit<STORAGE_TYPE1, UNITS1> first, LLUnit<STORAGE_TYPE2, UNITS2> second) { - LLUnit<typename LLResultTypeSubtract<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> result(first); - result -= second; - return result; + LLUnit<typename LLResultTypeSubtract<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> result(first); + result -= second; + return result; } template<typename STORAGE_TYPE, typename UNITS, typename UNITLESS> LLUnit<STORAGE_TYPE, UNITS> operator - (LLUnit<STORAGE_TYPE, UNITS> first, UNITLESS second) { - LL_BAD_TEMPLATE_INSTANTIATION(STORAGE_TYPE, "operator - requires compatible unit types"); - return LLUnit<STORAGE_TYPE, UNITS>(0); + LL_BAD_TEMPLATE_INSTANTIATION(STORAGE_TYPE, "operator - requires compatible unit types"); + return LLUnit<STORAGE_TYPE, UNITS>(0); } template<typename STORAGE_TYPE, typename UNITS, typename UNITLESS> LLUnit<STORAGE_TYPE, UNITS> operator - (UNITLESS first, LLUnit<STORAGE_TYPE, UNITS> second) { - LL_BAD_TEMPLATE_INSTANTIATION(STORAGE_TYPE, "operator - requires compatible unit types"); - return LLUnit<STORAGE_TYPE, UNITS>(0); + LL_BAD_TEMPLATE_INSTANTIATION(STORAGE_TYPE, "operator - requires compatible unit types"); + return LLUnit<STORAGE_TYPE, UNITS>(0); } template<typename STORAGE_TYPE1, typename UNITS1, typename STORAGE_TYPE2, typename UNITS2> LL_FORCE_INLINE LLUnitImplicit<typename LLResultTypeSubtract<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> operator - (LLUnitImplicit<STORAGE_TYPE1, UNITS1> first, LLUnitImplicit<STORAGE_TYPE2, UNITS2> second) { - LLUnitImplicit<typename LLResultTypeSubtract<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> result(first); - result -= second; - return result; + LLUnitImplicit<typename LLResultTypeSubtract<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> result(first); + result -= second; + return result; } template<typename STORAGE_TYPE1, typename UNITS1, typename STORAGE_TYPE2, typename UNITS2> LL_FORCE_INLINE LLUnitImplicit<typename LLResultTypeSubtract<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> operator - (LLUnit<STORAGE_TYPE1, UNITS1> first, LLUnitImplicit<STORAGE_TYPE2, UNITS2> second) { - LLUnitImplicit<typename LLResultTypeSubtract<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> result(first); - result -= second; - return result; + LLUnitImplicit<typename LLResultTypeSubtract<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> result(first); + result -= second; + return result; } template<typename STORAGE_TYPE1, typename UNITS1, typename STORAGE_TYPE2, typename UNITS2> LL_FORCE_INLINE LLUnitImplicit<typename LLResultTypeSubtract<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> operator - (LLUnitImplicit<STORAGE_TYPE1, UNITS1> first, LLUnit<STORAGE_TYPE2, UNITS2> second) { - LLUnitImplicit<typename LLResultTypeSubtract<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> result(first); - result -= LLUnitImplicit<STORAGE_TYPE1, UNITS1>(second); - return result; + LLUnitImplicit<typename LLResultTypeSubtract<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> result(first); + result -= LLUnitImplicit<STORAGE_TYPE1, UNITS1>(second); + return result; } template<typename STORAGE_TYPE, typename UNITS, typename UNITLESS_TYPE> LL_FORCE_INLINE LLUnitImplicit<typename LLResultTypeSubtract<STORAGE_TYPE, typename LLStorageType<UNITLESS_TYPE>::type_t>::type_t, UNITS> operator - (LLUnitImplicit<STORAGE_TYPE, UNITS> first, UNITLESS_TYPE second) { - LLUnitImplicit<typename LLResultTypeSubtract<STORAGE_TYPE, typename LLStorageType<UNITLESS_TYPE>::type_t>::type_t, UNITS> result(first); - result -= second; - return result; + LLUnitImplicit<typename LLResultTypeSubtract<STORAGE_TYPE, typename LLStorageType<UNITLESS_TYPE>::type_t>::type_t, UNITS> result(first); + result -= second; + return result; } template<typename STORAGE_TYPE, typename UNITS, typename UNITLESS_TYPE> LL_FORCE_INLINE LLUnitImplicit<typename LLResultTypeSubtract<typename LLStorageType<UNITLESS_TYPE>::type_t, STORAGE_TYPE>::type_t, UNITS> operator - (UNITLESS_TYPE first, LLUnitImplicit<STORAGE_TYPE, UNITS> second) { - LLUnitImplicit<typename LLResultTypeSubtract<typename LLStorageType<UNITLESS_TYPE>::type_t, STORAGE_TYPE>::type_t, UNITS> result(first); - result -= second; - return result; + LLUnitImplicit<typename LLResultTypeSubtract<typename LLStorageType<UNITLESS_TYPE>::type_t, STORAGE_TYPE>::type_t, UNITS> result(first); + result -= second; + return result; } // @@ -609,41 +609,41 @@ LL_FORCE_INLINE LLUnitImplicit<typename LLResultTypeSubtract<typename LLStorageT template<typename STORAGE_TYPE1, typename UNITS1, typename STORAGE_TYPE2, typename UNITS2> LLUnit<STORAGE_TYPE1, UNITS1> operator * (LLUnit<STORAGE_TYPE1, UNITS1>, LLUnit<STORAGE_TYPE2, UNITS2>) { - // spurious use of dependent type to stop gcc from triggering the static assertion before instantiating the template - LL_BAD_TEMPLATE_INSTANTIATION(STORAGE_TYPE1, "multiplication of unit types results in new unit type - not supported."); - return LLUnit<STORAGE_TYPE1, UNITS1>(); + // spurious use of dependent type to stop gcc from triggering the static assertion before instantiating the template + LL_BAD_TEMPLATE_INSTANTIATION(STORAGE_TYPE1, "multiplication of unit types results in new unit type - not supported."); + return LLUnit<STORAGE_TYPE1, UNITS1>(); } template<typename STORAGE_TYPE, typename UNITS, typename UNITLESS_TYPE> LL_FORCE_INLINE LLUnit<typename LLResultTypeMultiply<STORAGE_TYPE, typename LLStorageType<UNITLESS_TYPE>::type_t>::type_t, UNITS> operator * (LLUnit<STORAGE_TYPE, UNITS> first, UNITLESS_TYPE second) { - return LLUnit<typename LLResultTypeMultiply<STORAGE_TYPE, typename LLStorageType<UNITLESS_TYPE>::type_t>::type_t, UNITS>(first.value() * second); + return LLUnit<typename LLResultTypeMultiply<STORAGE_TYPE, typename LLStorageType<UNITLESS_TYPE>::type_t>::type_t, UNITS>(first.value() * second); } template<typename STORAGE_TYPE, typename UNITS, typename UNITLESS_TYPE> LL_FORCE_INLINE LLUnit<typename LLResultTypeMultiply<typename LLStorageType<UNITLESS_TYPE>::type_t, STORAGE_TYPE>::type_t, UNITS> operator * (UNITLESS_TYPE first, LLUnit<STORAGE_TYPE, UNITS> second) { - return LLUnit<typename LLResultTypeMultiply<typename LLStorageType<UNITLESS_TYPE>::type_t, STORAGE_TYPE>::type_t, UNITS>(first * second.value()); + return LLUnit<typename LLResultTypeMultiply<typename LLStorageType<UNITLESS_TYPE>::type_t, STORAGE_TYPE>::type_t, UNITS>(first * second.value()); } template<typename STORAGE_TYPE1, typename UNITS1, typename STORAGE_TYPE2, typename UNITS2> LLUnitImplicit<STORAGE_TYPE1, UNITS1> operator * (LLUnitImplicit<STORAGE_TYPE1, UNITS1>, LLUnitImplicit<STORAGE_TYPE2, UNITS2>) { - // spurious use of dependent type to stop gcc from triggering the static assertion before instantiating the template - LL_BAD_TEMPLATE_INSTANTIATION(STORAGE_TYPE1, "multiplication of unit types results in new unit type - not supported."); - return LLUnitImplicit<STORAGE_TYPE1, UNITS1>(); + // spurious use of dependent type to stop gcc from triggering the static assertion before instantiating the template + LL_BAD_TEMPLATE_INSTANTIATION(STORAGE_TYPE1, "multiplication of unit types results in new unit type - not supported."); + return LLUnitImplicit<STORAGE_TYPE1, UNITS1>(); } template<typename STORAGE_TYPE, typename UNITS, typename UNITLESS_TYPE> LL_FORCE_INLINE LLUnitImplicit<typename LLResultTypeMultiply<STORAGE_TYPE, typename LLStorageType<UNITLESS_TYPE>::type_t>::type_t, UNITS> operator * (LLUnitImplicit<STORAGE_TYPE, UNITS> first, UNITLESS_TYPE second) { - return LLUnitImplicit<typename LLResultTypeMultiply<STORAGE_TYPE, UNITLESS_TYPE>::type_t, UNITS>(first.value() * second); + return LLUnitImplicit<typename LLResultTypeMultiply<STORAGE_TYPE, UNITLESS_TYPE>::type_t, UNITS>(first.value() * second); } template<typename STORAGE_TYPE, typename UNITS, typename UNITLESS_TYPE> LL_FORCE_INLINE LLUnitImplicit<typename LLResultTypeMultiply<typename LLStorageType<UNITLESS_TYPE>::type_t, STORAGE_TYPE>::type_t, UNITS> operator * (UNITLESS_TYPE first, LLUnitImplicit<STORAGE_TYPE, UNITS> second) { - return LLUnitImplicit<typename LLResultTypeMultiply<typename LLStorageType<UNITLESS_TYPE>::type_t, STORAGE_TYPE>::type_t, UNITS>(first * second.value()); + return LLUnitImplicit<typename LLResultTypeMultiply<typename LLStorageType<UNITLESS_TYPE>::type_t, STORAGE_TYPE>::type_t, UNITS>(first * second.value()); } @@ -654,196 +654,196 @@ LL_FORCE_INLINE LLUnitImplicit<typename LLResultTypeMultiply<typename LLStorageT template<typename STORAGE_TYPE, typename UNITS, typename UNITLESS_TYPE> LL_FORCE_INLINE LLUnit<typename LLResultTypeDivide<STORAGE_TYPE, typename LLStorageType<UNITLESS_TYPE>::type_t>::type_t, UNITS> operator / (LLUnit<STORAGE_TYPE, UNITS> first, UNITLESS_TYPE second) { - return LLUnit<typename LLResultTypeDivide<STORAGE_TYPE, typename LLStorageType<UNITLESS_TYPE>::type_t>::type_t, UNITS>(first.value() / second); + return LLUnit<typename LLResultTypeDivide<STORAGE_TYPE, typename LLStorageType<UNITLESS_TYPE>::type_t>::type_t, UNITS>(first.value() / second); } template<typename STORAGE_TYPE1, typename UNITS1, typename STORAGE_TYPE2, typename UNITS2> LL_FORCE_INLINE typename LLResultTypeDivide<STORAGE_TYPE1, STORAGE_TYPE2>::type_t operator / (LLUnit<STORAGE_TYPE1, UNITS1> first, LLUnit<STORAGE_TYPE2, UNITS2> second) { - return first.value() / first.convert(second).value(); + return first.value() / first.convert(second).value(); } template<typename STORAGE_TYPE, typename UNITS, typename UNITLESS_TYPE> LL_FORCE_INLINE LLUnitImplicit<typename LLResultTypeDivide<STORAGE_TYPE, typename LLStorageType<UNITLESS_TYPE>::type_t>::type_t, UNITS> operator / (LLUnitImplicit<STORAGE_TYPE, UNITS> first, UNITLESS_TYPE second) { - return LLUnitImplicit<typename LLResultTypeDivide<STORAGE_TYPE, typename LLStorageType<UNITLESS_TYPE>::type_t>::type_t, UNITS>(first.value() / second); + return LLUnitImplicit<typename LLResultTypeDivide<STORAGE_TYPE, typename LLStorageType<UNITLESS_TYPE>::type_t>::type_t, UNITS>(first.value() / second); } template<typename STORAGE_TYPE1, typename UNITS1, typename STORAGE_TYPE2, typename UNITS2> LL_FORCE_INLINE typename LLResultTypeDivide<STORAGE_TYPE1, STORAGE_TYPE2>::type_t operator / (LLUnitImplicit<STORAGE_TYPE1, UNITS1> first, LLUnitImplicit<STORAGE_TYPE2, UNITS2> second) { - return (typename LLResultTypeDivide<STORAGE_TYPE1, STORAGE_TYPE2>::type_t)(first.value() / first.convert(second).value()); + return (typename LLResultTypeDivide<STORAGE_TYPE1, STORAGE_TYPE2>::type_t)(first.value() / first.convert(second).value()); } template<typename STORAGE_TYPE1, typename UNITS1, typename STORAGE_TYPE2, typename UNITS2> LL_FORCE_INLINE typename LLResultTypeDivide<STORAGE_TYPE1, STORAGE_TYPE2>::type_t operator / (LLUnit<STORAGE_TYPE1, UNITS1> first, LLUnitImplicit<STORAGE_TYPE2, UNITS2> second) { - return (typename LLResultTypeDivide<STORAGE_TYPE1, STORAGE_TYPE2>::type_t)(first.value() / first.convert(second).value()); + return (typename LLResultTypeDivide<STORAGE_TYPE1, STORAGE_TYPE2>::type_t)(first.value() / first.convert(second).value()); } template<typename STORAGE_TYPE1, typename UNITS1, typename STORAGE_TYPE2, typename UNITS2> LL_FORCE_INLINE typename LLResultTypeDivide<STORAGE_TYPE1, STORAGE_TYPE2>::type_t operator / (LLUnitImplicit<STORAGE_TYPE1, UNITS1> first, LLUnit<STORAGE_TYPE2, UNITS2> second) { - return (typename LLResultTypeDivide<STORAGE_TYPE1, STORAGE_TYPE2>::type_t)(first.value() / first.convert(second).value()); + return (typename LLResultTypeDivide<STORAGE_TYPE1, STORAGE_TYPE2>::type_t)(first.value() / first.convert(second).value()); } -template<typename T> +template<typename T> struct LLGetUnitLabel { - static const char* getUnitLabel() { return ""; } + static const char* getUnitLabel() { return ""; } }; template<typename T, typename STORAGE_T> struct LLGetUnitLabel<LLUnit<STORAGE_T, T> > { - static const char* getUnitLabel() { return T::getUnitLabel(); } + static const char* getUnitLabel() { return T::getUnitLabel(); } }; template<typename T> struct LLUnitLinearOps { - typedef LLUnitLinearOps<T> self_t; - - LLUnitLinearOps(T val) - : mValue(val), - mDivisor(1) - {} - - template<typename OTHER_T> - self_t operator * (OTHER_T other) - { - return mValue * other; - } - - template<typename OTHER_T> - self_t operator / (OTHER_T other) - { - mDivisor *= other; - return *this; - } - - template<typename OTHER_T> - self_t operator + (OTHER_T other) - { - mValue += other * mDivisor; - return *this; - } - - template<typename OTHER_T> - self_t operator - (OTHER_T other) - { - mValue -= other * mDivisor; - return *this; - } - - T mValue; - T mDivisor; + typedef LLUnitLinearOps<T> self_t; + + LLUnitLinearOps(T val) + : mValue(val), + mDivisor(1) + {} + + template<typename OTHER_T> + self_t operator * (OTHER_T other) + { + return mValue * other; + } + + template<typename OTHER_T> + self_t operator / (OTHER_T other) + { + mDivisor *= other; + return *this; + } + + template<typename OTHER_T> + self_t operator + (OTHER_T other) + { + mValue += other * mDivisor; + return *this; + } + + template<typename OTHER_T> + self_t operator - (OTHER_T other) + { + mValue -= other * mDivisor; + return *this; + } + + T mValue; + T mDivisor; }; template<typename T> struct LLUnitInverseLinearOps { - typedef LLUnitInverseLinearOps<T> self_t; - - LLUnitInverseLinearOps(T val) - : mValue(val), - mDivisor(1), - mMultiplicand(1) - {} - - template<typename OTHER_T> - self_t operator * (OTHER_T other) - { - mDivisor *= other; - return *this; - } - - template<typename OTHER_T> - self_t operator / (OTHER_T other) - { - mValue *= other; - mMultiplicand *= other; - return *this; - } - - template<typename OTHER_T> - self_t operator + (OTHER_T other) - { - mValue -= other * mMultiplicand; - return *this; - } - - template<typename OTHER_T> - self_t operator - (OTHER_T other) - { - mValue += other * mMultiplicand; - return *this; - } - - T mValue; - T mDivisor; - T mMultiplicand; + typedef LLUnitInverseLinearOps<T> self_t; + + LLUnitInverseLinearOps(T val) + : mValue(val), + mDivisor(1), + mMultiplicand(1) + {} + + template<typename OTHER_T> + self_t operator * (OTHER_T other) + { + mDivisor *= other; + return *this; + } + + template<typename OTHER_T> + self_t operator / (OTHER_T other) + { + mValue *= other; + mMultiplicand *= other; + return *this; + } + + template<typename OTHER_T> + self_t operator + (OTHER_T other) + { + mValue -= other * mMultiplicand; + return *this; + } + + template<typename OTHER_T> + self_t operator - (OTHER_T other) + { + mValue += other * mMultiplicand; + return *this; + } + + T mValue; + T mDivisor; + T mMultiplicand; }; #define LL_DECLARE_BASE_UNIT(base_unit_name, unit_label) \ struct base_unit_name \ { \ - static const int sLevel = 0; \ - typedef base_unit_name base_unit_t; \ - static const char* getUnitLabel() { return unit_label; } \ - template<typename T> \ - static LLUnit<T, base_unit_name> fromValue(T value) { return LLUnit<T, base_unit_name>(value); } \ - template<typename STORAGE_T, typename UNIT_T> \ - static LLUnit<STORAGE_T, base_unit_name> fromValue(LLUnit<STORAGE_T, UNIT_T> value) \ - { return LLUnit<STORAGE_T, base_unit_name>(value); } \ + static const int sLevel = 0; \ + typedef base_unit_name base_unit_t; \ + static const char* getUnitLabel() { return unit_label; } \ + template<typename T> \ + static LLUnit<T, base_unit_name> fromValue(T value) { return LLUnit<T, base_unit_name>(value); } \ + template<typename STORAGE_T, typename UNIT_T> \ + static LLUnit<STORAGE_T, base_unit_name> fromValue(LLUnit<STORAGE_T, UNIT_T> value) \ + { return LLUnit<STORAGE_T, base_unit_name>(value); } \ } -#define LL_DECLARE_DERIVED_UNIT(unit_name, unit_label, base_unit_name, conversion_operation) \ +#define LL_DECLARE_DERIVED_UNIT(unit_name, unit_label, base_unit_name, conversion_operation) \ struct unit_name \ { \ - static const int sLevel = base_unit_name::sLevel + 1; \ - typedef base_unit_name base_unit_t; \ - static const char* getUnitLabel() { return unit_label; } \ - template<typename T> \ - static LLUnit<T, unit_name> fromValue(T value) { return LLUnit<T, unit_name>(value); } \ - template<typename STORAGE_T, typename UNIT_T> \ - static LLUnit<STORAGE_T, unit_name> fromValue(LLUnit<STORAGE_T, UNIT_T> value) \ - { return LLUnit<STORAGE_T, unit_name>(value); } \ + static const int sLevel = base_unit_name::sLevel + 1; \ + typedef base_unit_name base_unit_t; \ + static const char* getUnitLabel() { return unit_label; } \ + template<typename T> \ + static LLUnit<T, unit_name> fromValue(T value) { return LLUnit<T, unit_name>(value); } \ + template<typename STORAGE_T, typename UNIT_T> \ + static LLUnit<STORAGE_T, unit_name> fromValue(LLUnit<STORAGE_T, UNIT_T> value) \ + { return LLUnit<STORAGE_T, unit_name>(value); } \ }; \ - \ + \ template<typename S1, typename S2> \ LL_FORCE_INLINE S2 ll_convert_units(LLUnit<S1, unit_name> in, LLUnit<S2, base_unit_name>& out) \ { \ - typedef typename LLResultTypePromote<S1, S2>::type_t result_storage_t; \ - LLUnitInverseLinearOps<result_storage_t> result = \ - LLUnitInverseLinearOps<result_storage_t>(in.value()) conversion_operation; \ - out = LLUnit<S2, base_unit_name>((S2)result.mValue); \ - return result.mDivisor; \ + typedef typename LLResultTypePromote<S1, S2>::type_t result_storage_t; \ + LLUnitInverseLinearOps<result_storage_t> result = \ + LLUnitInverseLinearOps<result_storage_t>(in.value()) conversion_operation; \ + out = LLUnit<S2, base_unit_name>((S2)result.mValue); \ + return result.mDivisor; \ } \ \ template<typename S1, typename S2> \ LL_FORCE_INLINE S2 ll_convert_units(LLUnit<S1, base_unit_name> in, LLUnit<S2, unit_name>& out) \ { \ - typedef typename LLResultTypePromote<S1, S2>::type_t result_storage_t; \ - LLUnitLinearOps<result_storage_t> result = \ - LLUnitLinearOps<result_storage_t>(in.value()) conversion_operation; \ - out = LLUnit<S2, unit_name>((S2)result.mValue); \ - return result.mDivisor; \ -} + typedef typename LLResultTypePromote<S1, S2>::type_t result_storage_t; \ + LLUnitLinearOps<result_storage_t> result = \ + LLUnitLinearOps<result_storage_t>(in.value()) conversion_operation; \ + out = LLUnit<S2, unit_name>((S2)result.mValue); \ + return result.mDivisor; \ +} #define LL_DECLARE_UNIT_TYPEDEFS(ns, unit_name) \ - typedef LLUnit<F32, ns::unit_name> F32##unit_name; \ - typedef LLUnitImplicit<F32, ns::unit_name> F32##unit_name##Implicit;\ - typedef LLUnit<F64, ns::unit_name> F64##unit_name; \ - typedef LLUnitImplicit<F64, ns::unit_name> F64##unit_name##Implicit;\ - typedef LLUnit<S32, ns::unit_name> S32##unit_name; \ - typedef LLUnitImplicit<S32, ns::unit_name> S32##unit_name##Implicit;\ - typedef LLUnit<S64, ns::unit_name> S64##unit_name; \ - typedef LLUnitImplicit<S64, ns::unit_name> S64##unit_name##Implicit;\ - typedef LLUnit<U32, ns::unit_name> U32##unit_name; \ - typedef LLUnitImplicit<U32, ns::unit_name> U32##unit_name##Implicit;\ - typedef LLUnit<U64, ns::unit_name> U64##unit_name; \ - typedef LLUnitImplicit<U64, ns::unit_name> U64##unit_name##Implicit + typedef LLUnit<F32, ns::unit_name> F32##unit_name; \ + typedef LLUnitImplicit<F32, ns::unit_name> F32##unit_name##Implicit;\ + typedef LLUnit<F64, ns::unit_name> F64##unit_name; \ + typedef LLUnitImplicit<F64, ns::unit_name> F64##unit_name##Implicit;\ + typedef LLUnit<S32, ns::unit_name> S32##unit_name; \ + typedef LLUnitImplicit<S32, ns::unit_name> S32##unit_name##Implicit;\ + typedef LLUnit<S64, ns::unit_name> S64##unit_name; \ + typedef LLUnitImplicit<S64, ns::unit_name> S64##unit_name##Implicit;\ + typedef LLUnit<U32, ns::unit_name> U32##unit_name; \ + typedef LLUnitImplicit<U32, ns::unit_name> U32##unit_name##Implicit;\ + typedef LLUnit<U64, ns::unit_name> U64##unit_name; \ + typedef LLUnitImplicit<U64, ns::unit_name> U64##unit_name##Implicit #endif //LL_UNITTYPE_H diff --git a/indra/llcommon/lluri.cpp b/indra/llcommon/lluri.cpp index 50ff0f9de0..b4664318cc 100644 --- a/indra/llcommon/lluri.cpp +++ b/indra/llcommon/lluri.cpp @@ -1,758 +1,758 @@ -/** - * @file lluri.cpp - * @author Phoenix - * @date 2006-02-08 - * @brief Implementation of the LLURI class. - * - * $LicenseInfo:firstyear=2006&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" - -#include "llapp.h" -#include "lluri.h" -#include "llsd.h" -#include <iomanip> - -#include "lluuid.h" - -// system includes -#include <boost/tokenizer.hpp> -#include <boost/algorithm/string/find_iterator.hpp> -#include <boost/algorithm/string/finder.hpp> - -// static -void LLURI::encodeCharacter(std::ostream& ostr, std::string::value_type val) -{ - ostr << "%" - - << std::uppercase - << std::hex - << std::setw(2) - << std::setfill('0') - - // VWR-4010 Cannot cast to U32 because sign-extension on - // chars > 128 will result in FFFFFFC3 instead of F3. - << static_cast<S32>(static_cast<U8>(val)) - - // reset stream state - << std::nouppercase - << std::dec - << std::setfill(' '); -} - -// static -std::string LLURI::escape( - const std::string& str, - const std::string& allowed, - bool is_allowed_sorted) -{ - // *NOTE: This size determination feels like a good value to - // me. If someone wante to come up with a more precise heuristic - // with some data to back up the assertion that 'sort is good' - // then feel free to change this test a bit. - if(!is_allowed_sorted && (str.size() > 2 * allowed.size())) - { - // if it's already sorted, or if the url is quite long, we - // want to optimize this process. - std::string sorted_allowed(allowed); - std::sort(sorted_allowed.begin(), sorted_allowed.end()); - return escape(str, sorted_allowed, true); - } - - std::ostringstream ostr; - std::string::const_iterator it = str.begin(); - std::string::const_iterator end = str.end(); - std::string::value_type c; - if(is_allowed_sorted) - { - std::string::const_iterator allowed_begin(allowed.begin()); - std::string::const_iterator allowed_end(allowed.end()); - for(; it != end; ++it) - { - c = *it; - if(std::binary_search(allowed_begin, allowed_end, c)) - { - ostr << c; - } - else - { - encodeCharacter(ostr, c); - } - } - } - else - { - for(; it != end; ++it) - { - c = *it; - if(allowed.find(c) == std::string::npos) - { - encodeCharacter(ostr, c); - } - else - { - ostr << c; - } - } - } - return ostr.str(); -} - -// static -std::string LLURI::unescape(const std::string& str) -{ - std::ostringstream ostr; - std::string::const_iterator it = str.begin(); - std::string::const_iterator end = str.end(); - for(; it != end; ++it) - { - if((*it) == '%') - { - ++it; - if(it == end) break; - - if(is_char_hex(*it)) - { - U8 c = hex_as_nybble(*it++); - - c = c << 4; - if (it == end) break; - - if(is_char_hex(*it)) - { - c |= hex_as_nybble(*it); - ostr.put((char)c); - } - else - { - ostr.put((char)c); - ostr.put(*it); - } - } - else - { - ostr.put('%'); - ostr.put(*it); - } - } - else - { - ostr.put(*it); - } - } - return ostr.str(); -} - -namespace -{ - const std::string unreserved() - { - static const std::string s = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" - "0123456789" - "-._~"; - return s; - } - const std::string path() - { - static const std::string s = - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789" - "$-_.+" - "!*'()," - "{}|\\^~[]`" - "<>#%" - ";/?:@&="; - return s; - } - const std::string sub_delims() - { - static const std::string s = "!$&'()*+,;="; - return s; - } - - std::string escapeHostAndPort(const std::string& s) - { return LLURI::escape(s, unreserved() + sub_delims() +":"); } - std::string escapePathComponent(const std::string& s) - { return LLURI::escape(s, unreserved() + sub_delims() + ":@"); } - std::string escapeQueryVariable(const std::string& s) - { return LLURI::escape(s, unreserved() + ":@!$'()*+,"); } // sub_delims - "&;=" + ":@" - std::string escapeQueryValue(const std::string& s) - { return LLURI::escape(s, unreserved() + ":@!$'()*+,="); } // sub_delims - "&;" + ":@" - std::string escapeUriQuery(const std::string& s) - { return LLURI::escape(s, unreserved() + ":@?&$;*+=%/"); } - std::string escapeUriData(const std::string& s) - { return LLURI::escape(s, unreserved() + "%"); } - std::string escapeUriPath(const std::string& s) - { return LLURI::escape(s, path()); } -} - -//static -std::string LLURI::escape(const std::string& str) -{ - static std::string default_allowed = unreserved(); - static bool initialized = false; - if(!initialized) - { - std::sort(default_allowed.begin(), default_allowed.end()); - initialized = true; - } - return escape(str, default_allowed, true); -} - -//static -std::string LLURI::escapePathAndData(const std::string &str) -{ - std::string result; - - const std::string data_marker = "data:"; - if (str.compare(0, data_marker.length(), data_marker) == 0) - { - // This is not url, but data, data part needs to be properly escaped - // data part is separated by ',' from header. Minimal data uri is "data:," - // See "data URI scheme" - size_t separator = str.find(','); - if (separator != std::string::npos) - { - size_t header_size = separator + 1; - std::string header = str.substr(0, header_size); - // base64 is url-safe - if (header.find("base64") != std::string::npos) - { - // assume url-safe data - result = str; - } - else - { - std::string data = str.substr(header_size, str.length() - header_size); - - // Notes: File can be partially pre-escaped, that's why escaping ignores '%' - // It somewhat limits user from displaying strings like "%20" in text - // but that's how viewer worked for a while and user can double-escape it - - - // Header doesn't need escaping - result = header + escapeUriData(data); - } - } - } - else - { - // try processing it as path with query separator - // The query component is indicated by the first question - // mark("?") character and terminated by a number sign("#") - size_t delim_pos = str.find('?'); - if (delim_pos == std::string::npos) - { - // alternate separator - delim_pos = str.find(';'); - } - - if (delim_pos != std::string::npos) - { - size_t path_size = delim_pos + 1; - std::string query; - std::string fragment; - - size_t fragment_pos = str.find('#'); - if ((fragment_pos != std::string::npos) && (fragment_pos > delim_pos)) - { - query = str.substr(path_size, fragment_pos - path_size); - fragment = str.substr(fragment_pos); - } - else - { - query = str.substr(path_size); - } - - std::string path = str.substr(0, path_size); - - result = escapeUriPath(path) + escapeUriQuery(query) + escapeUriPath(fragment); - } - } - - if (result.empty()) - { - // Not a known scheme or no data part, try just escaping as Uri path - result = escapeUriPath(str); - } - return result; -} - -LLURI::LLURI() -{ -} - -LLURI::LLURI(const std::string& escaped_str) -{ - std::string::size_type delim_pos; - delim_pos = escaped_str.find(':'); - std::string temp; - if (delim_pos == std::string::npos) - { - mScheme = ""; - mEscapedOpaque = escaped_str; - } - else - { - mScheme = escaped_str.substr(0, delim_pos); - mEscapedOpaque = escaped_str.substr(delim_pos+1); - } - - parseAuthorityAndPathUsingOpaque(); - - delim_pos = mEscapedPath.find('?'); - if (delim_pos != std::string::npos) - { - mEscapedQuery = mEscapedPath.substr(delim_pos+1); - mEscapedPath = mEscapedPath.substr(0,delim_pos); - } -} - -static bool isDefault(const std::string& scheme, U16 port) -{ - if (scheme == "http") - return port == 80; - if (scheme == "https") - return port == 443; - if (scheme == "ftp") - return port == 21; - - return false; -} - -void LLURI::parseAuthorityAndPathUsingOpaque() -{ - if (mScheme == "http" || mScheme == "https" || - mScheme == "ftp" || mScheme == "secondlife" || - mScheme == "x-grid-location-info") - { - if (mEscapedOpaque.substr(0,2) != "//") - { - return; - } - - std::string::size_type delim_pos, delim_pos2; - delim_pos = mEscapedOpaque.find('/', 2); - delim_pos2 = mEscapedOpaque.find('?', 2); - // no path, no query - if (delim_pos == std::string::npos && - delim_pos2 == std::string::npos) - { - mEscapedAuthority = mEscapedOpaque.substr(2); - mEscapedPath = ""; - } - // path exist, no query - else if (delim_pos2 == std::string::npos) - { - mEscapedAuthority = mEscapedOpaque.substr(2,delim_pos-2); - mEscapedPath = mEscapedOpaque.substr(delim_pos); - } - // no path, only query - else if (delim_pos == std::string::npos || - delim_pos2 < delim_pos) - { - mEscapedAuthority = mEscapedOpaque.substr(2,delim_pos2-2); - // query part will be broken out later - mEscapedPath = mEscapedOpaque.substr(delim_pos2); - } - // path and query - else - { - mEscapedAuthority = mEscapedOpaque.substr(2,delim_pos-2); - // query part will be broken out later - mEscapedPath = mEscapedOpaque.substr(delim_pos); - } - } - else if (mScheme == "about") - { - mEscapedPath = mEscapedOpaque; - } -} - -LLURI::LLURI(const std::string& scheme, - const std::string& userName, - const std::string& password, - const std::string& hostName, - U16 port, - const std::string& escapedPath, - const std::string& escapedQuery) - : mScheme(scheme), - mEscapedPath(escapedPath), - mEscapedQuery(escapedQuery) -{ - std::ostringstream auth; - std::ostringstream opaque; - - opaque << "//"; - - if (!userName.empty()) - { - auth << escape(userName); - if (!password.empty()) - { - auth << ':' << escape(password); - } - auth << '@'; - } - auth << hostName; - if (!isDefault(scheme, port)) - { - auth << ':' << port; - } - mEscapedAuthority = auth.str(); - - opaque << mEscapedAuthority << escapedPath << escapedQuery; - - mEscapedOpaque = opaque.str(); -} - -LLURI::~LLURI() -{ -} - -// static -LLURI LLURI::buildHTTP(const std::string& prefix, - const LLSD& path) -{ - LLURI result; - - // TODO: deal with '/' '?' '#' in host_port - if (prefix.find("://") != prefix.npos) - { - // it is a prefix - result = LLURI(prefix); - } - else - { - // it is just a host and optional port - result.mScheme = "http"; - result.mEscapedAuthority = escapeHostAndPort(prefix); - } - - if (path.isArray()) - { - // break out and escape each path component - for (LLSD::array_const_iterator it = path.beginArray(); - it != path.endArray(); - ++it) - { - LL_DEBUGS() << "PATH: inserting " << it->asString() << LL_ENDL; - result.mEscapedPath += "/" + escapePathComponent(it->asString()); - } - } - else if (path.isString()) - { - std::string pathstr(path); - // Trailing slash is significant in HTTP land. If caller specified, - // make a point of preserving. - std::string last_slash; - std::string::size_type len(pathstr.length()); - if (len && pathstr[len-1] == '/') - { - last_slash = "/"; - } - - // Escape every individual path component, recombining with slashes. - for (boost::split_iterator<std::string::const_iterator> - ti(pathstr, boost::first_finder("/")), tend; - ti != tend; ++ti) - { - // Eliminate a leading slash or duplicate slashes anywhere. (Extra - // slashes show up here as empty components.) This test also - // eliminates a trailing slash, hence last_slash above. - if (! ti->empty()) - { - result.mEscapedPath - += "/" + escapePathComponent(std::string(ti->begin(), ti->end())); - } - } - - // Reinstate trailing slash, if any. - result.mEscapedPath += last_slash; - } - else if(path.isUndefined()) - { - // do nothing - } - else - { - LL_WARNS() << "Valid path arguments to buildHTTP are array, string, or undef, you passed type" - << path.type() << LL_ENDL; - } - result.mEscapedOpaque = "//" + result.mEscapedAuthority + - result.mEscapedPath; - return result; -} - -// static -LLURI LLURI::buildHTTP(const std::string& prefix, - const LLSD& path, - const LLSD& query) -{ - LLURI uri = buildHTTP(prefix, path); - // break out and escape each query component - uri.mEscapedQuery = mapToQueryString(query); - uri.mEscapedOpaque += uri.mEscapedQuery ; - uri.mEscapedQuery.erase(0,1); // trim the leading '?' - return uri; -} - -// static -LLURI LLURI::buildHTTP(const std::string& host, - const U32& port, - const LLSD& path) -{ - return LLURI::buildHTTP(llformat("%s:%u", host.c_str(), port), path); -} - -// static -LLURI LLURI::buildHTTP(const std::string& host, - const U32& port, - const LLSD& path, - const LLSD& query) -{ - return LLURI::buildHTTP(llformat("%s:%u", host.c_str(), port), path, query); -} - -std::string LLURI::asString() const -{ - if (mScheme.empty()) - { - return mEscapedOpaque; - } - else - { - return mScheme + ":" + mEscapedOpaque; - } -} - -std::string LLURI::scheme() const -{ - return mScheme; -} - -std::string LLURI::opaque() const -{ - return unescape(mEscapedOpaque); -} - -std::string LLURI::authority() const -{ - return unescape(mEscapedAuthority); -} - - -namespace { - void findAuthorityParts(const std::string& authority, - std::string& user, - std::string& host, - std::string& port) - { - std::string::size_type start_pos = authority.find('@'); - if (start_pos == std::string::npos) - { - user = ""; - start_pos = 0; - } - else - { - user = authority.substr(0, start_pos); - start_pos += 1; - } - - std::string::size_type end_pos = authority.find(':', start_pos); - if (end_pos == std::string::npos) - { - host = authority.substr(start_pos); - port = ""; - } - else - { - host = authority.substr(start_pos, end_pos - start_pos); - port = authority.substr(end_pos + 1); - } - } -} - -std::string LLURI::hostName() const -{ - std::string user, host, port; - findAuthorityParts(mEscapedAuthority, user, host, port); - return unescape(host); -} - -std::string LLURI::userName() const -{ - std::string user, userPass, host, port; - findAuthorityParts(mEscapedAuthority, userPass, host, port); - std::string::size_type pos = userPass.find(':'); - if (pos != std::string::npos) - { - user = userPass.substr(0, pos); - } - return unescape(user); -} - -std::string LLURI::password() const -{ - std::string pass, userPass, host, port; - findAuthorityParts(mEscapedAuthority, userPass, host, port); - std::string::size_type pos = userPass.find(':'); - if (pos != std::string::npos) - { - pass = userPass.substr(pos + 1); - } - return unescape(pass); -} - -bool LLURI::defaultPort() const -{ - return isDefault(mScheme, hostPort()); -} - -U16 LLURI::hostPort() const -{ - std::string user, host, port; - findAuthorityParts(mEscapedAuthority, user, host, port); - if (port.empty()) - { - if (mScheme == "http") - return 80; - if (mScheme == "https") - return 443; - if (mScheme == "ftp") - return 21; - return 0; - } - return atoi(port.c_str()); -} - -std::string LLURI::path() const -{ - return unescape(mEscapedPath); -} - -LLSD LLURI::pathArray() const -{ - typedef boost::tokenizer<boost::char_separator<char> > tokenizer; - boost::char_separator<char> sep("/", "", boost::drop_empty_tokens); - tokenizer tokens(mEscapedPath, sep); - tokenizer::iterator it = tokens.begin(); - tokenizer::iterator end = tokens.end(); - - LLSD params; - for (const std::string& str : tokens) - { - params.append(str); - } - return params; -} - -std::string LLURI::query() const -{ - return unescape(mEscapedQuery); -} - -LLSD LLURI::queryMap() const -{ - return queryMap(mEscapedQuery); -} - -// static -LLSD LLURI::queryMap(std::string escaped_query_string) -{ - LL_DEBUGS() << "LLURI::queryMap query params: " << escaped_query_string << LL_ENDL; - - LLSD result = LLSD::emptyArray(); - while(!escaped_query_string.empty()) - { - // get tuple first - std::string tuple; - std::string::size_type tuple_begin = escaped_query_string.find('&'); - if (tuple_begin != std::string::npos) - { - tuple = escaped_query_string.substr(0, tuple_begin); - escaped_query_string = escaped_query_string.substr(tuple_begin+1); - } - else - { - tuple = escaped_query_string; - escaped_query_string = ""; - } - if (tuple.empty()) continue; - - // parse tuple - std::string::size_type key_end = tuple.find('='); - if (key_end != std::string::npos) - { - std::string key = unescape(tuple.substr(0,key_end)); - std::string value = unescape(tuple.substr(key_end+1)); - LL_DEBUGS() << "inserting key " << key << " value " << value << LL_ENDL; - result[key] = value; - } - else - { - LL_DEBUGS() << "inserting key " << unescape(tuple) << " value true" << LL_ENDL; - result[unescape(tuple)] = true; - } - } - return result; -} - -std::string LLURI::mapToQueryString(const LLSD& queryMap) -{ - std::string query_string; - if (queryMap.isMap()) - { - bool first_element = true; - LLSD::map_const_iterator iter = queryMap.beginMap(); - LLSD::map_const_iterator end = queryMap.endMap(); - std::ostringstream ostr; - for (; iter != end; ++iter) - { - if(first_element) - { - ostr << "?"; - first_element = false; - } - else - { - ostr << "&"; - } - ostr << escapeQueryVariable(iter->first); - if(iter->second.isDefined()) - { - ostr << "=" << escapeQueryValue(iter->second.asString()); - } - } - query_string = ostr.str(); - } - return query_string; -} - -bool operator!=(const LLURI& first, const LLURI& second) -{ - return (first.asString() != second.asString()); -} +/**
+ * @file lluri.cpp
+ * @author Phoenix
+ * @date 2006-02-08
+ * @brief Implementation of the LLURI class.
+ *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include "llapp.h"
+#include "lluri.h"
+#include "llsd.h"
+#include <iomanip>
+
+#include "lluuid.h"
+
+// system includes
+#include <boost/tokenizer.hpp>
+#include <boost/algorithm/string/find_iterator.hpp>
+#include <boost/algorithm/string/finder.hpp>
+
+// static
+void LLURI::encodeCharacter(std::ostream& ostr, std::string::value_type val)
+{
+ ostr << "%"
+
+ << std::uppercase
+ << std::hex
+ << std::setw(2)
+ << std::setfill('0')
+
+ // VWR-4010 Cannot cast to U32 because sign-extension on
+ // chars > 128 will result in FFFFFFC3 instead of F3.
+ << static_cast<S32>(static_cast<U8>(val))
+
+ // reset stream state
+ << std::nouppercase
+ << std::dec
+ << std::setfill(' ');
+}
+
+// static
+std::string LLURI::escape(
+ const std::string& str,
+ const std::string& allowed,
+ bool is_allowed_sorted)
+{
+ // *NOTE: This size determination feels like a good value to
+ // me. If someone wante to come up with a more precise heuristic
+ // with some data to back up the assertion that 'sort is good'
+ // then feel free to change this test a bit.
+ if(!is_allowed_sorted && (str.size() > 2 * allowed.size()))
+ {
+ // if it's already sorted, or if the url is quite long, we
+ // want to optimize this process.
+ std::string sorted_allowed(allowed);
+ std::sort(sorted_allowed.begin(), sorted_allowed.end());
+ return escape(str, sorted_allowed, true);
+ }
+
+ std::ostringstream ostr;
+ std::string::const_iterator it = str.begin();
+ std::string::const_iterator end = str.end();
+ std::string::value_type c;
+ if(is_allowed_sorted)
+ {
+ std::string::const_iterator allowed_begin(allowed.begin());
+ std::string::const_iterator allowed_end(allowed.end());
+ for(; it != end; ++it)
+ {
+ c = *it;
+ if(std::binary_search(allowed_begin, allowed_end, c))
+ {
+ ostr << c;
+ }
+ else
+ {
+ encodeCharacter(ostr, c);
+ }
+ }
+ }
+ else
+ {
+ for(; it != end; ++it)
+ {
+ c = *it;
+ if(allowed.find(c) == std::string::npos)
+ {
+ encodeCharacter(ostr, c);
+ }
+ else
+ {
+ ostr << c;
+ }
+ }
+ }
+ return ostr.str();
+}
+
+// static
+std::string LLURI::unescape(const std::string& str)
+{
+ std::ostringstream ostr;
+ std::string::const_iterator it = str.begin();
+ std::string::const_iterator end = str.end();
+ for(; it != end; ++it)
+ {
+ if((*it) == '%')
+ {
+ ++it;
+ if(it == end) break;
+
+ if(is_char_hex(*it))
+ {
+ U8 c = hex_as_nybble(*it++);
+
+ c = c << 4;
+ if (it == end) break;
+
+ if(is_char_hex(*it))
+ {
+ c |= hex_as_nybble(*it);
+ ostr.put((char)c);
+ }
+ else
+ {
+ ostr.put((char)c);
+ ostr.put(*it);
+ }
+ }
+ else
+ {
+ ostr.put('%');
+ ostr.put(*it);
+ }
+ }
+ else
+ {
+ ostr.put(*it);
+ }
+ }
+ return ostr.str();
+}
+
+namespace
+{
+ const std::string unreserved()
+ {
+ static const std::string s =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+ "0123456789"
+ "-._~";
+ return s;
+ }
+ const std::string path()
+ {
+ static const std::string s =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789"
+ "$-_.+"
+ "!*'(),"
+ "{}|\\^~[]`"
+ "<>#%"
+ ";/?:@&=";
+ return s;
+ }
+ const std::string sub_delims()
+ {
+ static const std::string s = "!$&'()*+,;=";
+ return s;
+ }
+
+ std::string escapeHostAndPort(const std::string& s)
+ { return LLURI::escape(s, unreserved() + sub_delims() +":"); }
+ std::string escapePathComponent(const std::string& s)
+ { return LLURI::escape(s, unreserved() + sub_delims() + ":@"); }
+ std::string escapeQueryVariable(const std::string& s)
+ { return LLURI::escape(s, unreserved() + ":@!$'()*+,"); } // sub_delims - "&;=" + ":@"
+ std::string escapeQueryValue(const std::string& s)
+ { return LLURI::escape(s, unreserved() + ":@!$'()*+,="); } // sub_delims - "&;" + ":@"
+ std::string escapeUriQuery(const std::string& s)
+ { return LLURI::escape(s, unreserved() + ":@?&$;*+=%/"); }
+ std::string escapeUriData(const std::string& s)
+ { return LLURI::escape(s, unreserved() + "%"); }
+ std::string escapeUriPath(const std::string& s)
+ { return LLURI::escape(s, path()); }
+}
+
+//static
+std::string LLURI::escape(const std::string& str)
+{
+ static std::string default_allowed = unreserved();
+ static bool initialized = false;
+ if(!initialized)
+ {
+ std::sort(default_allowed.begin(), default_allowed.end());
+ initialized = true;
+ }
+ return escape(str, default_allowed, true);
+}
+
+//static
+std::string LLURI::escapePathAndData(const std::string &str)
+{
+ std::string result;
+
+ const std::string data_marker = "data:";
+ if (str.compare(0, data_marker.length(), data_marker) == 0)
+ {
+ // This is not url, but data, data part needs to be properly escaped
+ // data part is separated by ',' from header. Minimal data uri is "data:,"
+ // See "data URI scheme"
+ size_t separator = str.find(',');
+ if (separator != std::string::npos)
+ {
+ size_t header_size = separator + 1;
+ std::string header = str.substr(0, header_size);
+ // base64 is url-safe
+ if (header.find("base64") != std::string::npos)
+ {
+ // assume url-safe data
+ result = str;
+ }
+ else
+ {
+ std::string data = str.substr(header_size, str.length() - header_size);
+
+ // Notes: File can be partially pre-escaped, that's why escaping ignores '%'
+ // It somewhat limits user from displaying strings like "%20" in text
+ // but that's how viewer worked for a while and user can double-escape it
+
+
+ // Header doesn't need escaping
+ result = header + escapeUriData(data);
+ }
+ }
+ }
+ else
+ {
+ // try processing it as path with query separator
+ // The query component is indicated by the first question
+ // mark("?") character and terminated by a number sign("#")
+ size_t delim_pos = str.find('?');
+ if (delim_pos == std::string::npos)
+ {
+ // alternate separator
+ delim_pos = str.find(';');
+ }
+
+ if (delim_pos != std::string::npos)
+ {
+ size_t path_size = delim_pos + 1;
+ std::string query;
+ std::string fragment;
+
+ size_t fragment_pos = str.find('#');
+ if ((fragment_pos != std::string::npos) && (fragment_pos > delim_pos))
+ {
+ query = str.substr(path_size, fragment_pos - path_size);
+ fragment = str.substr(fragment_pos);
+ }
+ else
+ {
+ query = str.substr(path_size);
+ }
+
+ std::string path = str.substr(0, path_size);
+
+ result = escapeUriPath(path) + escapeUriQuery(query) + escapeUriPath(fragment);
+ }
+ }
+
+ if (result.empty())
+ {
+ // Not a known scheme or no data part, try just escaping as Uri path
+ result = escapeUriPath(str);
+ }
+ return result;
+}
+
+LLURI::LLURI()
+{
+}
+
+LLURI::LLURI(const std::string& escaped_str)
+{
+ std::string::size_type delim_pos;
+ delim_pos = escaped_str.find(':');
+ std::string temp;
+ if (delim_pos == std::string::npos)
+ {
+ mScheme = "";
+ mEscapedOpaque = escaped_str;
+ }
+ else
+ {
+ mScheme = escaped_str.substr(0, delim_pos);
+ mEscapedOpaque = escaped_str.substr(delim_pos+1);
+ }
+
+ parseAuthorityAndPathUsingOpaque();
+
+ delim_pos = mEscapedPath.find('?');
+ if (delim_pos != std::string::npos)
+ {
+ mEscapedQuery = mEscapedPath.substr(delim_pos+1);
+ mEscapedPath = mEscapedPath.substr(0,delim_pos);
+ }
+}
+
+static bool isDefault(const std::string& scheme, U16 port)
+{
+ if (scheme == "http")
+ return port == 80;
+ if (scheme == "https")
+ return port == 443;
+ if (scheme == "ftp")
+ return port == 21;
+
+ return false;
+}
+
+void LLURI::parseAuthorityAndPathUsingOpaque()
+{
+ if (mScheme == "http" || mScheme == "https" ||
+ mScheme == "ftp" || mScheme == "secondlife" ||
+ mScheme == "x-grid-location-info")
+ {
+ if (mEscapedOpaque.substr(0,2) != "//")
+ {
+ return;
+ }
+
+ std::string::size_type delim_pos, delim_pos2;
+ delim_pos = mEscapedOpaque.find('/', 2);
+ delim_pos2 = mEscapedOpaque.find('?', 2);
+ // no path, no query
+ if (delim_pos == std::string::npos &&
+ delim_pos2 == std::string::npos)
+ {
+ mEscapedAuthority = mEscapedOpaque.substr(2);
+ mEscapedPath = "";
+ }
+ // path exist, no query
+ else if (delim_pos2 == std::string::npos)
+ {
+ mEscapedAuthority = mEscapedOpaque.substr(2,delim_pos-2);
+ mEscapedPath = mEscapedOpaque.substr(delim_pos);
+ }
+ // no path, only query
+ else if (delim_pos == std::string::npos ||
+ delim_pos2 < delim_pos)
+ {
+ mEscapedAuthority = mEscapedOpaque.substr(2,delim_pos2-2);
+ // query part will be broken out later
+ mEscapedPath = mEscapedOpaque.substr(delim_pos2);
+ }
+ // path and query
+ else
+ {
+ mEscapedAuthority = mEscapedOpaque.substr(2,delim_pos-2);
+ // query part will be broken out later
+ mEscapedPath = mEscapedOpaque.substr(delim_pos);
+ }
+ }
+ else if (mScheme == "about")
+ {
+ mEscapedPath = mEscapedOpaque;
+ }
+}
+
+LLURI::LLURI(const std::string& scheme,
+ const std::string& userName,
+ const std::string& password,
+ const std::string& hostName,
+ U16 port,
+ const std::string& escapedPath,
+ const std::string& escapedQuery)
+ : mScheme(scheme),
+ mEscapedPath(escapedPath),
+ mEscapedQuery(escapedQuery)
+{
+ std::ostringstream auth;
+ std::ostringstream opaque;
+
+ opaque << "//";
+
+ if (!userName.empty())
+ {
+ auth << escape(userName);
+ if (!password.empty())
+ {
+ auth << ':' << escape(password);
+ }
+ auth << '@';
+ }
+ auth << hostName;
+ if (!isDefault(scheme, port))
+ {
+ auth << ':' << port;
+ }
+ mEscapedAuthority = auth.str();
+
+ opaque << mEscapedAuthority << escapedPath << escapedQuery;
+
+ mEscapedOpaque = opaque.str();
+}
+
+LLURI::~LLURI()
+{
+}
+
+// static
+LLURI LLURI::buildHTTP(const std::string& prefix,
+ const LLSD& path)
+{
+ LLURI result;
+
+ // TODO: deal with '/' '?' '#' in host_port
+ if (prefix.find("://") != prefix.npos)
+ {
+ // it is a prefix
+ result = LLURI(prefix);
+ }
+ else
+ {
+ // it is just a host and optional port
+ result.mScheme = "http";
+ result.mEscapedAuthority = escapeHostAndPort(prefix);
+ }
+
+ if (path.isArray())
+ {
+ // break out and escape each path component
+ for (LLSD::array_const_iterator it = path.beginArray();
+ it != path.endArray();
+ ++it)
+ {
+ LL_DEBUGS() << "PATH: inserting " << it->asString() << LL_ENDL;
+ result.mEscapedPath += "/" + escapePathComponent(it->asString());
+ }
+ }
+ else if (path.isString())
+ {
+ std::string pathstr(path);
+ // Trailing slash is significant in HTTP land. If caller specified,
+ // make a point of preserving.
+ std::string last_slash;
+ std::string::size_type len(pathstr.length());
+ if (len && pathstr[len-1] == '/')
+ {
+ last_slash = "/";
+ }
+
+ // Escape every individual path component, recombining with slashes.
+ for (boost::split_iterator<std::string::const_iterator>
+ ti(pathstr, boost::first_finder("/")), tend;
+ ti != tend; ++ti)
+ {
+ // Eliminate a leading slash or duplicate slashes anywhere. (Extra
+ // slashes show up here as empty components.) This test also
+ // eliminates a trailing slash, hence last_slash above.
+ if (! ti->empty())
+ {
+ result.mEscapedPath
+ += "/" + escapePathComponent(std::string(ti->begin(), ti->end()));
+ }
+ }
+
+ // Reinstate trailing slash, if any.
+ result.mEscapedPath += last_slash;
+ }
+ else if(path.isUndefined())
+ {
+ // do nothing
+ }
+ else
+ {
+ LL_WARNS() << "Valid path arguments to buildHTTP are array, string, or undef, you passed type"
+ << path.type() << LL_ENDL;
+ }
+ result.mEscapedOpaque = "//" + result.mEscapedAuthority +
+ result.mEscapedPath;
+ return result;
+}
+
+// static
+LLURI LLURI::buildHTTP(const std::string& prefix,
+ const LLSD& path,
+ const LLSD& query)
+{
+ LLURI uri = buildHTTP(prefix, path);
+ // break out and escape each query component
+ uri.mEscapedQuery = mapToQueryString(query);
+ uri.mEscapedOpaque += uri.mEscapedQuery ;
+ uri.mEscapedQuery.erase(0,1); // trim the leading '?'
+ return uri;
+}
+
+// static
+LLURI LLURI::buildHTTP(const std::string& host,
+ const U32& port,
+ const LLSD& path)
+{
+ return LLURI::buildHTTP(llformat("%s:%u", host.c_str(), port), path);
+}
+
+// static
+LLURI LLURI::buildHTTP(const std::string& host,
+ const U32& port,
+ const LLSD& path,
+ const LLSD& query)
+{
+ return LLURI::buildHTTP(llformat("%s:%u", host.c_str(), port), path, query);
+}
+
+std::string LLURI::asString() const
+{
+ if (mScheme.empty())
+ {
+ return mEscapedOpaque;
+ }
+ else
+ {
+ return mScheme + ":" + mEscapedOpaque;
+ }
+}
+
+std::string LLURI::scheme() const
+{
+ return mScheme;
+}
+
+std::string LLURI::opaque() const
+{
+ return unescape(mEscapedOpaque);
+}
+
+std::string LLURI::authority() const
+{
+ return unescape(mEscapedAuthority);
+}
+
+
+namespace {
+ void findAuthorityParts(const std::string& authority,
+ std::string& user,
+ std::string& host,
+ std::string& port)
+ {
+ std::string::size_type start_pos = authority.find('@');
+ if (start_pos == std::string::npos)
+ {
+ user = "";
+ start_pos = 0;
+ }
+ else
+ {
+ user = authority.substr(0, start_pos);
+ start_pos += 1;
+ }
+
+ std::string::size_type end_pos = authority.find(':', start_pos);
+ if (end_pos == std::string::npos)
+ {
+ host = authority.substr(start_pos);
+ port = "";
+ }
+ else
+ {
+ host = authority.substr(start_pos, end_pos - start_pos);
+ port = authority.substr(end_pos + 1);
+ }
+ }
+}
+
+std::string LLURI::hostName() const
+{
+ std::string user, host, port;
+ findAuthorityParts(mEscapedAuthority, user, host, port);
+ return unescape(host);
+}
+
+std::string LLURI::userName() const
+{
+ std::string user, userPass, host, port;
+ findAuthorityParts(mEscapedAuthority, userPass, host, port);
+ std::string::size_type pos = userPass.find(':');
+ if (pos != std::string::npos)
+ {
+ user = userPass.substr(0, pos);
+ }
+ return unescape(user);
+}
+
+std::string LLURI::password() const
+{
+ std::string pass, userPass, host, port;
+ findAuthorityParts(mEscapedAuthority, userPass, host, port);
+ std::string::size_type pos = userPass.find(':');
+ if (pos != std::string::npos)
+ {
+ pass = userPass.substr(pos + 1);
+ }
+ return unescape(pass);
+}
+
+bool LLURI::defaultPort() const
+{
+ return isDefault(mScheme, hostPort());
+}
+
+U16 LLURI::hostPort() const
+{
+ std::string user, host, port;
+ findAuthorityParts(mEscapedAuthority, user, host, port);
+ if (port.empty())
+ {
+ if (mScheme == "http")
+ return 80;
+ if (mScheme == "https")
+ return 443;
+ if (mScheme == "ftp")
+ return 21;
+ return 0;
+ }
+ return atoi(port.c_str());
+}
+
+std::string LLURI::path() const
+{
+ return unescape(mEscapedPath);
+}
+
+LLSD LLURI::pathArray() const
+{
+ typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
+ boost::char_separator<char> sep("/", "", boost::drop_empty_tokens);
+ tokenizer tokens(mEscapedPath, sep);
+ tokenizer::iterator it = tokens.begin();
+ tokenizer::iterator end = tokens.end();
+
+ LLSD params;
+ for (const std::string& str : tokens)
+ {
+ params.append(str);
+ }
+ return params;
+}
+
+std::string LLURI::query() const
+{
+ return unescape(mEscapedQuery);
+}
+
+LLSD LLURI::queryMap() const
+{
+ return queryMap(mEscapedQuery);
+}
+
+// static
+LLSD LLURI::queryMap(std::string escaped_query_string)
+{
+ LL_DEBUGS() << "LLURI::queryMap query params: " << escaped_query_string << LL_ENDL;
+
+ LLSD result = LLSD::emptyArray();
+ while(!escaped_query_string.empty())
+ {
+ // get tuple first
+ std::string tuple;
+ std::string::size_type tuple_begin = escaped_query_string.find('&');
+ if (tuple_begin != std::string::npos)
+ {
+ tuple = escaped_query_string.substr(0, tuple_begin);
+ escaped_query_string = escaped_query_string.substr(tuple_begin+1);
+ }
+ else
+ {
+ tuple = escaped_query_string;
+ escaped_query_string = "";
+ }
+ if (tuple.empty()) continue;
+
+ // parse tuple
+ std::string::size_type key_end = tuple.find('=');
+ if (key_end != std::string::npos)
+ {
+ std::string key = unescape(tuple.substr(0,key_end));
+ std::string value = unescape(tuple.substr(key_end+1));
+ LL_DEBUGS() << "inserting key " << key << " value " << value << LL_ENDL;
+ result[key] = value;
+ }
+ else
+ {
+ LL_DEBUGS() << "inserting key " << unescape(tuple) << " value true" << LL_ENDL;
+ result[unescape(tuple)] = true;
+ }
+ }
+ return result;
+}
+
+std::string LLURI::mapToQueryString(const LLSD& queryMap)
+{
+ std::string query_string;
+ if (queryMap.isMap())
+ {
+ bool first_element = true;
+ LLSD::map_const_iterator iter = queryMap.beginMap();
+ LLSD::map_const_iterator end = queryMap.endMap();
+ std::ostringstream ostr;
+ for (; iter != end; ++iter)
+ {
+ if(first_element)
+ {
+ ostr << "?";
+ first_element = false;
+ }
+ else
+ {
+ ostr << "&";
+ }
+ ostr << escapeQueryVariable(iter->first);
+ if(iter->second.isDefined())
+ {
+ ostr << "=" << escapeQueryValue(iter->second.asString());
+ }
+ }
+ query_string = ostr.str();
+ }
+ return query_string;
+}
+
+bool operator!=(const LLURI& first, const LLURI& second)
+{
+ return (first.asString() != second.asString());
+}
diff --git a/indra/llcommon/lluri.h b/indra/llcommon/lluri.h index d6b10bd564..cc59c6b370 100644 --- a/indra/llcommon/lluri.h +++ b/indra/llcommon/lluri.h @@ -1,193 +1,193 @@ -/** - * @file lluri.h - * @author Phoenix - * @date 2006-02-05 - * @brief Declaration of the URI class. - * - * $LicenseInfo:firstyear=2006&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLURI_H -#define LL_LLURI_H - -#include <string> - -class LLSD; -class LLUUID; -class LLApp; - -/** - * - * LLURI instances are immutable - * See: http://www.ietf.org/rfc/rfc3986.txt - * - */ -class LL_COMMON_API LLURI -{ -public: - LLURI(); - LLURI(const std::string& escaped_str); - LLURI(const std::string& scheme, - const std::string& userName, - const std::string& password, - const std::string& hostName, - U16 hostPort, - const std::string& escapedPath, - const std::string& escapedQuery); - - // construct from escaped string, as would be transmitted on the net - - ~LLURI(); - - static LLURI buildHTTP( - const std::string& prefix, - const LLSD& path); - - static LLURI buildHTTP( - const std::string& prefix, - const LLSD& path, - const LLSD& query); - ///< prefix is either a full URL prefix of the form - /// "http://example.com:8080", or it can be simply a host and - /// optional port like "example.com" or "example.com:8080", in - /// these cases, the "http://" will be added - - static LLURI buildHTTP( - const std::string& host, - const U32& port, - const LLSD& path); - static LLURI buildHTTP( - const std::string& host, - const U32& port, - const LLSD& path, - const LLSD& query); - - std::string asString() const; - ///< the whole URI, escaped as needed - - /** @name Parts of a URI */ - //@{ - // These functions return parts of the decoded URI. The returned - // strings are un-escaped as needed - - // for all schemes - std::string scheme() const; ///< ex.: "http", note lack of colon - std::string opaque() const; ///< everything after the colon - - // for schemes that follow path like syntax (http, https, ftp) - std::string authority() const; // ex.: "host.com:80" - std::string hostName() const; // ex.: "host.com" - std::string userName() const; - std::string password() const; - U16 hostPort() const; // ex.: 80, will include implicit port - bool defaultPort() const; // true if port is default for scheme - const std::string& escapedPath() const { return mEscapedPath; } - std::string path() const; // ex.: "/abc/def", includes leading slash - LLSD pathArray() const; // above decoded into an array of strings - std::string query() const; // ex.: "x=34", section after "?" - const std::string& escapedQuery() const { return mEscapedQuery; } - LLSD queryMap() const; // above decoded into a map - static LLSD queryMap(std::string escaped_query_string); - - /** - * @brief given a name value map, return a serialized query string. - * - - * @param query_map a map of name value. every value must be - * representable as a string. - * @return Returns an url query string of '?n1=v1&n2=v2&...' - */ - static std::string mapToQueryString(const LLSD& query_map); - - /** @name Escaping Utilities */ - //@{ - /** - * @brief 'Escape' symbol into stream - * - * @param ostr Output stream. - * @param val Symbol to encode. - */ - static void encodeCharacter(std::ostream& ostr, std::string::value_type val); - - /** - * @brief Escape the string passed except for unreserved - * - * ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz - * 0123456789 - * -._~ - * - * @see http://www.ietf.org/rfc/rfc1738.txt - * - * @param str The raw URI to escape. - * @return Returns the rfc 1738 escaped uri or an empty string. - */ - static std::string escape(const std::string& str); - - /** - * @brief Escape a string with a specified set of allowed characters. - * - * Escape a string by urlencoding all the characters that aren't - * in the allowed string. - * @param str The raw URI to escape. - * @param allowed Character array of allowed characters - * @param is_allowed_sorted Optimization hint if allowed array is sorted. - * @return Returns the escaped uri or an empty string. - */ - static std::string escape( - const std::string& str, - const std::string& allowed, - bool is_allowed_sorted = false); - - /** - * @brief Break string into data part and path or sheme - * and escape path (if present) and data. - * Data part is not allowed to have path related symbols - * @param str The raw URI to escape. - */ - static std::string escapePathAndData(const std::string &str); - - /** - * @brief unescape an escaped URI string. - * - * @param str The escped URI to unescape. - * @return Returns the unescaped uri or an empty string. - */ - static std::string unescape(const std::string& str); - //@} - -private: - // only "http", "https", "ftp", and "secondlife" schemes are parsed - // secondlife scheme parses authority as "" and includes it as part of - // the path. See lluri_tut.cpp - // i.e. secondlife://app/login has mAuthority = "" and mPath = "/app/login" - void parseAuthorityAndPathUsingOpaque(); - std::string mScheme; - std::string mEscapedOpaque; - std::string mEscapedAuthority; - std::string mEscapedPath; - std::string mEscapedQuery; -}; - -// this operator required for tut -LL_COMMON_API bool operator!=(const LLURI& first, const LLURI& second); - -#endif // LL_LLURI_H +/**
+ * @file lluri.h
+ * @author Phoenix
+ * @date 2006-02-05
+ * @brief Declaration of the URI class.
+ *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLURI_H
+#define LL_LLURI_H
+
+#include <string>
+
+class LLSD;
+class LLUUID;
+class LLApp;
+
+/**
+ *
+ * LLURI instances are immutable
+ * See: http://www.ietf.org/rfc/rfc3986.txt
+ *
+ */
+class LL_COMMON_API LLURI
+{
+public:
+ LLURI();
+ LLURI(const std::string& escaped_str);
+ LLURI(const std::string& scheme,
+ const std::string& userName,
+ const std::string& password,
+ const std::string& hostName,
+ U16 hostPort,
+ const std::string& escapedPath,
+ const std::string& escapedQuery);
+
+ // construct from escaped string, as would be transmitted on the net
+
+ ~LLURI();
+
+ static LLURI buildHTTP(
+ const std::string& prefix,
+ const LLSD& path);
+
+ static LLURI buildHTTP(
+ const std::string& prefix,
+ const LLSD& path,
+ const LLSD& query);
+ ///< prefix is either a full URL prefix of the form
+ /// "http://example.com:8080", or it can be simply a host and
+ /// optional port like "example.com" or "example.com:8080", in
+ /// these cases, the "http://" will be added
+
+ static LLURI buildHTTP(
+ const std::string& host,
+ const U32& port,
+ const LLSD& path);
+ static LLURI buildHTTP(
+ const std::string& host,
+ const U32& port,
+ const LLSD& path,
+ const LLSD& query);
+
+ std::string asString() const;
+ ///< the whole URI, escaped as needed
+
+ /** @name Parts of a URI */
+ //@{
+ // These functions return parts of the decoded URI. The returned
+ // strings are un-escaped as needed
+
+ // for all schemes
+ std::string scheme() const; ///< ex.: "http", note lack of colon
+ std::string opaque() const; ///< everything after the colon
+
+ // for schemes that follow path like syntax (http, https, ftp)
+ std::string authority() const; // ex.: "host.com:80"
+ std::string hostName() const; // ex.: "host.com"
+ std::string userName() const;
+ std::string password() const;
+ U16 hostPort() const; // ex.: 80, will include implicit port
+ bool defaultPort() const; // true if port is default for scheme
+ const std::string& escapedPath() const { return mEscapedPath; }
+ std::string path() const; // ex.: "/abc/def", includes leading slash
+ LLSD pathArray() const; // above decoded into an array of strings
+ std::string query() const; // ex.: "x=34", section after "?"
+ const std::string& escapedQuery() const { return mEscapedQuery; }
+ LLSD queryMap() const; // above decoded into a map
+ static LLSD queryMap(std::string escaped_query_string);
+
+ /**
+ * @brief given a name value map, return a serialized query string.
+ *
+
+ * @param query_map a map of name value. every value must be
+ * representable as a string.
+ * @return Returns an url query string of '?n1=v1&n2=v2&...'
+ */
+ static std::string mapToQueryString(const LLSD& query_map);
+
+ /** @name Escaping Utilities */
+ //@{
+ /**
+ * @brief 'Escape' symbol into stream
+ *
+ * @param ostr Output stream.
+ * @param val Symbol to encode.
+ */
+ static void encodeCharacter(std::ostream& ostr, std::string::value_type val);
+
+ /**
+ * @brief Escape the string passed except for unreserved
+ *
+ * ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
+ * 0123456789
+ * -._~
+ *
+ * @see http://www.ietf.org/rfc/rfc1738.txt
+ *
+ * @param str The raw URI to escape.
+ * @return Returns the rfc 1738 escaped uri or an empty string.
+ */
+ static std::string escape(const std::string& str);
+
+ /**
+ * @brief Escape a string with a specified set of allowed characters.
+ *
+ * Escape a string by urlencoding all the characters that aren't
+ * in the allowed string.
+ * @param str The raw URI to escape.
+ * @param allowed Character array of allowed characters
+ * @param is_allowed_sorted Optimization hint if allowed array is sorted.
+ * @return Returns the escaped uri or an empty string.
+ */
+ static std::string escape(
+ const std::string& str,
+ const std::string& allowed,
+ bool is_allowed_sorted = false);
+
+ /**
+ * @brief Break string into data part and path or sheme
+ * and escape path (if present) and data.
+ * Data part is not allowed to have path related symbols
+ * @param str The raw URI to escape.
+ */
+ static std::string escapePathAndData(const std::string &str);
+
+ /**
+ * @brief unescape an escaped URI string.
+ *
+ * @param str The escped URI to unescape.
+ * @return Returns the unescaped uri or an empty string.
+ */
+ static std::string unescape(const std::string& str);
+ //@}
+
+private:
+ // only "http", "https", "ftp", and "secondlife" schemes are parsed
+ // secondlife scheme parses authority as "" and includes it as part of
+ // the path. See lluri_tut.cpp
+ // i.e. secondlife://app/login has mAuthority = "" and mPath = "/app/login"
+ void parseAuthorityAndPathUsingOpaque();
+ std::string mScheme;
+ std::string mEscapedOpaque;
+ std::string mEscapedAuthority;
+ std::string mEscapedPath;
+ std::string mEscapedQuery;
+};
+
+// this operator required for tut
+LL_COMMON_API bool operator!=(const LLURI& first, const LLURI& second);
+
+#endif // LL_LLURI_H
diff --git a/indra/llcommon/lluriparser.cpp b/indra/llcommon/lluriparser.cpp index f79a98a56d..2ebb7fc742 100644 --- a/indra/llcommon/lluriparser.cpp +++ b/indra/llcommon/lluriparser.cpp @@ -1,4 +1,4 @@ -/** +/** * @file lluriparser.cpp * @author Protey * @date 2014-10-07 @@ -7,21 +7,21 @@ * $LicenseInfo:firstyear=2014&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2014, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -36,129 +36,129 @@ LLUriParser::LLUriParser(const std::string& u) : mTmpScheme(false), mNormalizedTmp(false), mRes(0) { - if (u.find("://") == std::string::npos) - { - mNormalizedUri = "http://"; - mTmpScheme = true; - } + if (u.find("://") == std::string::npos) + { + mNormalizedUri = "http://"; + mTmpScheme = true; + } - mNormalizedUri += u.c_str(); + mNormalizedUri += u.c_str(); - mRes = parse(); + mRes = parse(); } LLUriParser::~LLUriParser() { - uriFreeUriMembersA(&mUri); + uriFreeUriMembersA(&mUri); } S32 LLUriParser::parse() { - mRes = uriParseSingleUriA(&mUri, mNormalizedUri.c_str(), NULL); - return mRes; + mRes = uriParseSingleUriA(&mUri, mNormalizedUri.c_str(), NULL); + return mRes; } const char * LLUriParser::scheme() const { - return mScheme.c_str(); + return mScheme.c_str(); } void LLUriParser::sheme(const std::string& s) { - mTmpScheme = !s.size(); - mScheme = s; + mTmpScheme = !s.size(); + mScheme = s; } const char * LLUriParser::port() const { - return mPort.c_str(); + return mPort.c_str(); } void LLUriParser::port(const std::string& s) { - mPort = s; + mPort = s; } const char * LLUriParser::host() const { - return mHost.c_str(); + return mHost.c_str(); } void LLUriParser::host(const std::string& s) { - mHost = s; + mHost = s; } const char * LLUriParser::path() const { - return mPath.c_str(); + return mPath.c_str(); } void LLUriParser::path(const std::string& s) { - mPath = s; + mPath = s; } const char * LLUriParser::query() const { - return mQuery.c_str(); + return mQuery.c_str(); } void LLUriParser::query(const std::string& s) { - mQuery = s; + mQuery = s; } const char * LLUriParser::fragment() const { - return mFragment.c_str(); + return mFragment.c_str(); } void LLUriParser::fragment(const std::string& s) { - mFragment = s; + mFragment = s; } void LLUriParser::textRangeToString(UriTextRangeA& textRange, std::string& str) { - if (textRange.first != NULL && textRange.afterLast != NULL && textRange.first < textRange.afterLast) - { - const ptrdiff_t len = textRange.afterLast - textRange.first; - str.assign(textRange.first, static_cast<std::string::size_type>(len)); - } - else - { - str = LLStringUtil::null; - } + if (textRange.first != NULL && textRange.afterLast != NULL && textRange.first < textRange.afterLast) + { + const ptrdiff_t len = textRange.afterLast - textRange.first; + str.assign(textRange.first, static_cast<std::string::size_type>(len)); + } + else + { + str = LLStringUtil::null; + } } void LLUriParser::extractParts() { - if (mTmpScheme || mNormalizedTmp) - { - mScheme.clear(); - } - else - { - textRangeToString(mUri.scheme, mScheme); - } - - textRangeToString(mUri.hostText, mHost); - textRangeToString(mUri.portText, mPort); - textRangeToString(mUri.query, mQuery); - textRangeToString(mUri.fragment, mFragment); - - UriPathSegmentA * pathHead = mUri.pathHead; - while (pathHead) - { - std::string partOfPath; - textRangeToString(pathHead->text, partOfPath); - - mPath += '/'; - mPath += partOfPath; - - pathHead = pathHead->next; - } + if (mTmpScheme || mNormalizedTmp) + { + mScheme.clear(); + } + else + { + textRangeToString(mUri.scheme, mScheme); + } + + textRangeToString(mUri.hostText, mHost); + textRangeToString(mUri.portText, mPort); + textRangeToString(mUri.query, mQuery); + textRangeToString(mUri.fragment, mFragment); + + UriPathSegmentA * pathHead = mUri.pathHead; + while (pathHead) + { + std::string partOfPath; + textRangeToString(pathHead->text, partOfPath); + + mPath += '/'; + mPath += partOfPath; + + pathHead = pathHead->next; + } } #if LL_DARWIN @@ -177,14 +177,14 @@ void uri_signal_handler(int signal) S32 LLUriParser::normalize() { - mNormalizedTmp = mTmpScheme; - if (!mRes) - { + mNormalizedTmp = mTmpScheme; + if (!mRes) + { #if LL_DARWIN sighandler_t last_sigill_handler, last_sigbus_handler; - last_sigill_handler = signal(SIGILL, &uri_signal_handler); // illegal instruction + last_sigill_handler = signal(SIGILL, &uri_signal_handler); // illegal instruction last_sigbus_handler = signal(SIGBUS, &uri_signal_handler); - + if (setjmp(return_to_normalize)) { // Issue: external library crashed via signal @@ -230,79 +230,79 @@ S32 LLUriParser::normalize() } } } - } + } - if(mTmpScheme && mNormalizedUri.size() > 7) - { - mNormalizedUri = mNormalizedUri.substr(7); - mTmpScheme = false; - } + if(mTmpScheme && mNormalizedUri.size() > 7) + { + mNormalizedUri = mNormalizedUri.substr(7); + mTmpScheme = false; + } - return mRes; + return mRes; } void LLUriParser::glue(std::string& uri) const { - std::string first_part; - glueFirst(first_part); + std::string first_part; + glueFirst(first_part); - std::string second_part; - glueSecond(second_part); + std::string second_part; + glueSecond(second_part); - uri = first_part + second_part; + uri = first_part + second_part; } void LLUriParser::glueFirst(std::string& uri, bool use_scheme) const { - if (use_scheme && mScheme.size()) - { - uri = mScheme; - uri += "://"; - } - else - { - uri.clear(); - } - - uri += mHost; + if (use_scheme && mScheme.size()) + { + uri = mScheme; + uri += "://"; + } + else + { + uri.clear(); + } + + uri += mHost; } void LLUriParser::glueSecond(std::string& uri) const { - if (mPort.size()) - { - uri = ':'; - uri += mPort; - } - else - { - uri.clear(); - } - - uri += mPath; - - if (mQuery.size()) - { - uri += '?'; - uri += mQuery; - } - - if (mFragment.size()) - { - uri += '#'; - uri += mFragment; - } + if (mPort.size()) + { + uri = ':'; + uri += mPort; + } + else + { + uri.clear(); + } + + uri += mPath; + + if (mQuery.size()) + { + uri += '?'; + uri += mQuery; + } + + if (mFragment.size()) + { + uri += '#'; + uri += mFragment; + } } bool LLUriParser::test() const { - std::string uri; - glue(uri); + std::string uri; + glue(uri); - return uri == mNormalizedUri; + return uri == mNormalizedUri; } const char * LLUriParser::normalizedUri() const { - return mNormalizedUri.c_str(); + return mNormalizedUri.c_str(); } diff --git a/indra/llcommon/lluriparser.h b/indra/llcommon/lluriparser.h index 92626b9054..77eb4031d5 100644 --- a/indra/llcommon/lluriparser.h +++ b/indra/llcommon/lluriparser.h @@ -1,4 +1,4 @@ -/** +/** * @file lluriparser.h * @author Protey * @date 20146-10-07 @@ -7,21 +7,21 @@ * $LicenseInfo:firstyear=2014&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2014, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -35,52 +35,52 @@ class LL_COMMON_API LLUriParser { public: - LLUriParser(const std::string& u); - ~LLUriParser(); + LLUriParser(const std::string& u); + ~LLUriParser(); - const char * scheme() const; - void sheme (const std::string& s); + const char * scheme() const; + void sheme (const std::string& s); - const char * port() const; - void port (const std::string& s); + const char * port() const; + void port (const std::string& s); - const char * host() const; - void host (const std::string& s); + const char * host() const; + void host (const std::string& s); - const char * path() const; - void path (const std::string& s); + const char * path() const; + void path (const std::string& s); - const char * query() const; - void query (const std::string& s); + const char * query() const; + void query (const std::string& s); - const char * fragment() const; - void fragment (const std::string& s); + const char * fragment() const; + void fragment (const std::string& s); - const char * normalizedUri() const; + const char * normalizedUri() const; - void extractParts(); - void glue(std::string& uri) const; - void glueFirst(std::string& uri, bool use_scheme = true) const; - void glueSecond(std::string& uri) const; - bool test() const; - S32 normalize(); + void extractParts(); + void glue(std::string& uri) const; + void glueFirst(std::string& uri, bool use_scheme = true) const; + void glueSecond(std::string& uri) const; + bool test() const; + S32 normalize(); private: - S32 parse(); - void textRangeToString(UriTextRangeA& textRange, std::string& str); - std::string mScheme; - std::string mHost; - std::string mPort; - std::string mPath; - std::string mQuery; - std::string mFragment; - std::string mNormalizedUri; + S32 parse(); + void textRangeToString(UriTextRangeA& textRange, std::string& str); + std::string mScheme; + std::string mHost; + std::string mPort; + std::string mPath; + std::string mQuery; + std::string mFragment; + std::string mNormalizedUri; - UriUriA mUri; + UriUriA mUri; - S32 mRes; - bool mTmpScheme; - bool mNormalizedTmp; + S32 mRes; + bool mTmpScheme; + bool mNormalizedTmp; }; #endif // LL_LLURIPARSER_H diff --git a/indra/llcommon/lluuid.cpp b/indra/llcommon/lluuid.cpp index db33639885..f3821de71b 100644 --- a/indra/llcommon/lluuid.cpp +++ b/indra/llcommon/lluuid.cpp @@ -1,1077 +1,1077 @@ -/** - * @file lluuid.cpp - * - * $LicenseInfo:firstyear=2000&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" - - // We can't use WIN32_LEAN_AND_MEAN here, needs lots of includes. -#if LL_WINDOWS -#include "llwin32headers.h" -// ugh, this is ugly. We need to straighten out our linking for this library -#pragma comment(lib, "IPHLPAPI.lib") -#include <iphlpapi.h> -#endif - -#include "llapp.h" -#include "lldefs.h" -#include "llerror.h" - -#include "lluuid.h" -#include "llerror.h" -#include "llrand.h" -#include "llstring.h" -#include "lltimer.h" -#include "llthread.h" -#include "llmutex.h" -#include "llmd5.h" -#include "hbxxh.h" - -const LLUUID LLUUID::null; -const LLTransactionID LLTransactionID::tnull; - -// static -LLMutex* LLUUID::mMutex = NULL; - - - -/* - -NOT DONE YET!!! - -static char BASE85_TABLE[] = { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', - 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', - 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', - 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', - 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', - 'y', 'z', '!', '#', '$', '%', '&', '(', ')', '*', - '+', '-', ';', '[', '=', '>', '?', '@', '^', '_', - '`', '{', '|', '}', '~', '\0' -}; - - -void encode( char * fiveChars, unsigned int word ) throw( ) -{ -for( int ix = 0; ix < 5; ++ix ) { -fiveChars[4-ix] = encodeTable[ word % 85]; -word /= 85; -} -} - -To decode: -unsigned int decode( char const * fiveChars ) throw( bad_input_data ) -{ -unsigned int ret = 0; -for( int ix = 0; ix < 5; ++ix ) { -char * s = strchr( encodeTable, fiveChars[ ix ] ); -if( s == 0 ) LLTHROW(bad_input_data()); -ret = ret * 85 + (s-encodeTable); -} -return ret; -} - -void LLUUID::toBase85(char* out) -{ - U32* me = (U32*)&(mData[0]); - for(S32 i = 0; i < 4; ++i) - { - char* o = &out[i*i]; - for(S32 j = 0; j < 5; ++j) - { - o[4-j] = BASE85_TABLE[ me[i] % 85]; - word /= 85; - } - } -} - -unsigned int decode( char const * fiveChars ) throw( bad_input_data ) -{ - unsigned int ret = 0; - for( S32 ix = 0; ix < 5; ++ix ) - { - char * s = strchr( encodeTable, fiveChars[ ix ] ); - ret = ret * 85 + (s-encodeTable); - } - return ret; -} -*/ - -#define LL_USE_JANKY_RANDOM_NUMBER_GENERATOR 0 -#if LL_USE_JANKY_RANDOM_NUMBER_GENERATOR -/** - * @brief a global for - */ -static U64 sJankyRandomSeed(LLUUID::getRandomSeed()); - -/** - * @brief generate a random U32. - */ -U32 janky_fast_random_bytes() -{ - sJankyRandomSeed = U64L(1664525) * sJankyRandomSeed + U64L(1013904223); - return (U32)sJankyRandomSeed; -} - -/** - * @brief generate a random U32 from [0, val) - */ -U32 janky_fast_random_byes_range(U32 val) -{ - sJankyRandomSeed = U64L(1664525) * sJankyRandomSeed + U64L(1013904223); - return (U32)(sJankyRandomSeed) % val; -} - -/** - * @brief generate a random U32 from [0, val) - */ -U32 janky_fast_random_seeded_bytes(U32 seed, U32 val) -{ - seed = U64L(1664525) * (U64)(seed)+U64L(1013904223); - return (U32)(seed) % val; -} -#endif - -// Common to all UUID implementations -void LLUUID::toString(std::string& out) const -{ - out = llformat( - "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", - (U8)(mData[0]), - (U8)(mData[1]), - (U8)(mData[2]), - (U8)(mData[3]), - (U8)(mData[4]), - (U8)(mData[5]), - (U8)(mData[6]), - (U8)(mData[7]), - (U8)(mData[8]), - (U8)(mData[9]), - (U8)(mData[10]), - (U8)(mData[11]), - (U8)(mData[12]), - (U8)(mData[13]), - (U8)(mData[14]), - (U8)(mData[15])); -} - -// *TODO: deprecate -void LLUUID::toString(char* out) const -{ - std::string buffer; - toString(buffer); - strcpy(out, buffer.c_str()); /* Flawfinder: ignore */ -} - -void LLUUID::toCompressedString(std::string& out) const -{ - char bytes[UUID_BYTES + 1]; - memcpy(bytes, mData, UUID_BYTES); /* Flawfinder: ignore */ - bytes[UUID_BYTES] = '\0'; - out.assign(bytes, UUID_BYTES); -} - -// *TODO: deprecate -void LLUUID::toCompressedString(char* out) const -{ - memcpy(out, mData, UUID_BYTES); /* Flawfinder: ignore */ - out[UUID_BYTES] = '\0'; -} - -std::string LLUUID::getString() const -{ - return asString(); -} - -std::string LLUUID::asString() const -{ - std::string str; - toString(str); - return str; -} - -bool LLUUID::set(const char* in_string, bool emit) -{ - return set(ll_safe_string(in_string), emit); -} - -bool LLUUID::set(const std::string& in_string, bool emit) -{ - bool broken_format = false; - - // empty strings should make NULL uuid - if (in_string.empty()) - { - setNull(); - return true; - } - - if (in_string.length() != (UUID_STR_LENGTH - 1)) /* Flawfinder: ignore */ - { - // I'm a moron. First implementation didn't have the right UUID format. - // Shouldn't see any of these any more - if (in_string.length() == (UUID_STR_LENGTH - 2)) /* Flawfinder: ignore */ - { - if (emit) - { - LL_WARNS() << "Warning! Using broken UUID string format" << LL_ENDL; - } - broken_format = true; - } - else - { - // Bad UUID string. Spam as INFO, as most cases we don't care. - if (emit) - { - //don't spam the logs because a resident can't spell. - LL_WARNS() << "Bad UUID string: " << in_string << LL_ENDL; - } - setNull(); - return false; - } - } - - U8 cur_pos = 0; - S32 i; - for (i = 0; i < UUID_BYTES; i++) - { - if ((i == 4) || (i == 6) || (i == 8) || (i == 10)) - { - cur_pos++; - if (broken_format && (i == 10)) - { - // Missing - in the broken format - cur_pos--; - } - } - - mData[i] = 0; - - if ((in_string[cur_pos] >= '0') && (in_string[cur_pos] <= '9')) - { - mData[i] += (U8)(in_string[cur_pos] - '0'); - } - else if ((in_string[cur_pos] >= 'a') && (in_string[cur_pos] <= 'f')) - { - mData[i] += (U8)(10 + in_string[cur_pos] - 'a'); - } - else if ((in_string[cur_pos] >= 'A') && (in_string[cur_pos] <= 'F')) - { - mData[i] += (U8)(10 + in_string[cur_pos] - 'A'); - } - else - { - if (emit) - { - LL_WARNS() << "Invalid UUID string character" << LL_ENDL; - } - setNull(); - return false; - } - - mData[i] = mData[i] << 4; - cur_pos++; - - if ((in_string[cur_pos] >= '0') && (in_string[cur_pos] <= '9')) - { - mData[i] += (U8)(in_string[cur_pos] - '0'); - } - else if ((in_string[cur_pos] >= 'a') && (in_string[cur_pos] <= 'f')) - { - mData[i] += (U8)(10 + in_string[cur_pos] - 'a'); - } - else if ((in_string[cur_pos] >= 'A') && (in_string[cur_pos] <= 'F')) - { - mData[i] += (U8)(10 + in_string[cur_pos] - 'A'); - } - else - { - if (emit) - { - LL_WARNS() << "Invalid UUID string character" << LL_ENDL; - } - setNull(); - return false; - } - cur_pos++; - } - - return true; -} - -bool LLUUID::validate(const std::string& in_string) -{ - bool broken_format = false; - if (in_string.length() != (UUID_STR_LENGTH - 1)) /* Flawfinder: ignore */ - { - // I'm a moron. First implementation didn't have the right UUID format. - if (in_string.length() == (UUID_STR_LENGTH - 2)) /* Flawfinder: ignore */ - { - broken_format = true; - } - else - { - return false; - } - } - - U8 cur_pos = 0; - for (U32 i = 0; i < 16; i++) - { - if ((i == 4) || (i == 6) || (i == 8) || (i == 10)) - { - cur_pos++; - if (broken_format && (i == 10)) - { - // Missing - in the broken format - cur_pos--; - } - } - - if ((in_string[cur_pos] >= '0') && (in_string[cur_pos] <= '9')) - { - } - else if ((in_string[cur_pos] >= 'a') && (in_string[cur_pos] <= 'f')) - { - } - else if ((in_string[cur_pos] >= 'A') && (in_string[cur_pos] <= 'F')) - { - } - else - { - return false; - } - - cur_pos++; - - if ((in_string[cur_pos] >= '0') && (in_string[cur_pos] <= '9')) - { - } - else if ((in_string[cur_pos] >= 'a') && (in_string[cur_pos] <= 'f')) - { - } - else if ((in_string[cur_pos] >= 'A') && (in_string[cur_pos] <= 'F')) - { - } - else - { - return false; - } - cur_pos++; - } - return true; -} - -const LLUUID& LLUUID::operator^=(const LLUUID& rhs) -{ - U32* me = (U32*)&(mData[0]); - const U32* other = (U32*)&(rhs.mData[0]); - for (S32 i = 0; i < 4; ++i) - { - me[i] = me[i] ^ other[i]; - } - return *this; -} - -LLUUID LLUUID::operator^(const LLUUID& rhs) const -{ - LLUUID id(*this); - id ^= rhs; - return id; -} - -// WARNING: this algorithm SHALL NOT be changed. It is also used by the server -// and plays a role in some assets validation (e.g. clothing items). Changing -// it would cause invalid assets. -void LLUUID::combine(const LLUUID& other, LLUUID& result) const -{ - LLMD5 md5_uuid; - md5_uuid.update((unsigned char*)mData, 16); - md5_uuid.update((unsigned char*)other.mData, 16); - md5_uuid.finalize(); - md5_uuid.raw_digest(result.mData); -} - -LLUUID LLUUID::combine(const LLUUID& other) const -{ - LLUUID combination; - combine(other, combination); - return combination; -} - -std::ostream& operator<<(std::ostream& s, const LLUUID& uuid) -{ - std::string uuid_str; - uuid.toString(uuid_str); - s << uuid_str; - return s; -} - -std::istream& operator>>(std::istream& s, LLUUID& uuid) -{ - U32 i; - char uuid_str[UUID_STR_LENGTH]; /* Flawfinder: ignore */ - for (i = 0; i < UUID_STR_LENGTH - 1; i++) - { - s >> uuid_str[i]; - } - uuid_str[i] = '\0'; - uuid.set(std::string(uuid_str)); - return s; -} - -static void get_random_bytes(void* buf, int nbytes) -{ - int i; - char* cp = (char*)buf; - - // *NOTE: If we are not using the janky generator ll_rand() - // generates at least 3 good bytes of data since it is 0 to - // RAND_MAX. This could be made more efficient by copying all the - // bytes. - for (i = 0; i < nbytes; i++) -#if LL_USE_JANKY_RANDOM_NUMBER_GENERATOR - * cp++ = janky_fast_random_bytes() & 0xFF; -#else - * cp++ = ll_rand() & 0xFF; -#endif - return; -} - -#if LL_WINDOWS - -typedef struct _ASTAT_ -{ - ADAPTER_STATUS adapt; - NAME_BUFFER NameBuff[30]; -}ASTAT, * PASTAT; - -// static -S32 LLUUID::getNodeID(unsigned char* node_id) -{ - ASTAT Adapter; - NCB Ncb; - UCHAR uRetCode; - LANA_ENUM lenum; - int i; - int retval = 0; - - memset(&Ncb, 0, sizeof(Ncb)); - Ncb.ncb_command = NCBENUM; - Ncb.ncb_buffer = (UCHAR*)&lenum; - Ncb.ncb_length = sizeof(lenum); - uRetCode = Netbios(&Ncb); - - for (i = 0; i < lenum.length; i++) - { - memset(&Ncb, 0, sizeof(Ncb)); - Ncb.ncb_command = NCBRESET; - Ncb.ncb_lana_num = lenum.lana[i]; - - uRetCode = Netbios(&Ncb); - - memset(&Ncb, 0, sizeof(Ncb)); - Ncb.ncb_command = NCBASTAT; - Ncb.ncb_lana_num = lenum.lana[i]; - - strcpy((char*)Ncb.ncb_callname, "* "); /* Flawfinder: ignore */ - Ncb.ncb_buffer = (unsigned char*)&Adapter; - Ncb.ncb_length = sizeof(Adapter); - - uRetCode = Netbios(&Ncb); - if (uRetCode == 0) - { - memcpy(node_id, Adapter.adapt.adapter_address, 6); /* Flawfinder: ignore */ - retval = 1; - } - } - return retval; -} - -#elif LL_DARWIN -// macOS version of the UUID generation code... -/* - * Get an ethernet hardware address, if we can find it... - */ -#include <unistd.h> -#include <sys/types.h> -#include <sys/time.h> -#include <sys/socket.h> -#include <sys/ioctl.h> -#include <net/if.h> -#include <net/if_types.h> -#include <net/if_dl.h> -#include <net/route.h> -#include <ifaddrs.h> - - // static -S32 LLUUID::getNodeID(unsigned char* node_id) -{ - int i; - unsigned char* a = NULL; - struct ifaddrs* ifap, * ifa; - int rv; - S32 result = 0; - - if ((rv = getifaddrs(&ifap)) == -1) - { - return -1; - } - if (ifap == NULL) - { - return -1; - } - - for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) - { - // printf("Interface %s, address family %d, ", ifa->ifa_name, ifa->ifa_addr->sa_family); - for (i = 0; i < ifa->ifa_addr->sa_len; i++) - { - // printf("%02X ", (unsigned char)ifa->ifa_addr->sa_data[i]); - } - // printf("\n"); - - if (ifa->ifa_addr->sa_family == AF_LINK) - { - // This is a link-level address - struct sockaddr_dl* lla = (struct sockaddr_dl*)ifa->ifa_addr; - - // printf("\tLink level address, type %02X\n", lla->sdl_type); - - if (lla->sdl_type == IFT_ETHER) - { - // Use the first ethernet MAC in the list. - // For some reason, the macro LLADDR() defined in net/if_dl.h doesn't expand correctly. This is what it would do. - a = (unsigned char*)&((lla)->sdl_data); - a += (lla)->sdl_nlen; - - if (!a[0] && !a[1] && !a[2] && !a[3] && !a[4] && !a[5]) - { - continue; - } - - if (node_id) - { - memcpy(node_id, a, 6); - result = 1; - } - - // We found one. - break; - } - } - } - freeifaddrs(ifap); - - return result; -} - -#else - -// Linux version of the UUID generation code... -/* - * Get the ethernet hardware address, if we can find it... - */ -#include <unistd.h> -#include <fcntl.h> -#include <errno.h> -#include <sys/types.h> -#include <sys/time.h> -#include <sys/stat.h> -#include <sys/file.h> -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <net/if.h> -#define HAVE_NETINET_IN_H -#ifdef HAVE_NETINET_IN_H -#include <netinet/in.h> -#if !LL_DARWIN -#include <linux/sockios.h> -#endif -#endif - - // static -S32 LLUUID::getNodeID(unsigned char* node_id) -{ - int sd; - struct ifreq ifr, * ifrp; - struct ifconf ifc; - char buf[1024]; - int n, i; - unsigned char* a; - - /* - * BSD 4.4 defines the size of an ifreq to be - * max(sizeof(ifreq), sizeof(ifreq.ifr_name)+ifreq.ifr_addr.sa_len - * However, under earlier systems, sa_len isn't present, so the size is - * just sizeof(struct ifreq) - */ -#ifdef HAVE_SA_LEN -#ifndef max -#define max(a,b) ((a) > (b) ? (a) : (b)) -#endif -#define ifreq_size(i) max(sizeof(struct ifreq),\ - sizeof((i).ifr_name)+(i).ifr_addr.sa_len) -#else -#define ifreq_size(i) sizeof(struct ifreq) -#endif /* HAVE_SA_LEN*/ - - sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP); - if (sd < 0) { - return -1; - } - memset(buf, 0, sizeof(buf)); - ifc.ifc_len = sizeof(buf); - ifc.ifc_buf = buf; - if (ioctl(sd, SIOCGIFCONF, (char*)&ifc) < 0) { - close(sd); - return -1; - } - n = ifc.ifc_len; - for (i = 0; i < n; i += ifreq_size(*ifr)) { - ifrp = (struct ifreq*)((char*)ifc.ifc_buf + i); - strncpy(ifr.ifr_name, ifrp->ifr_name, IFNAMSIZ); /* Flawfinder: ignore */ -#ifdef SIOCGIFHWADDR - if (ioctl(sd, SIOCGIFHWADDR, &ifr) < 0) - continue; - a = (unsigned char*)&ifr.ifr_hwaddr.sa_data; -#else -#ifdef SIOCGENADDR - if (ioctl(sd, SIOCGENADDR, &ifr) < 0) - continue; - a = (unsigned char*)ifr.ifr_enaddr; -#else - /* - * XXX we don't have a way of getting the hardware - * address - */ - close(sd); - return 0; -#endif /* SIOCGENADDR */ -#endif /* SIOCGIFHWADDR */ - if (!a[0] && !a[1] && !a[2] && !a[3] && !a[4] && !a[5]) - continue; - if (node_id) { - memcpy(node_id, a, 6); /* Flawfinder: ignore */ - close(sd); - return 1; - } - } - close(sd); - return 0; -} - -#endif - -S32 LLUUID::cmpTime(uuid_time_t* t1, uuid_time_t* t2) -{ - // Compare two time values. - - if (t1->high < t2->high) return -1; - if (t1->high > t2->high) return 1; - if (t1->low < t2->low) return -1; - if (t1->low > t2->low) return 1; - return 0; -} - -void LLUUID::getSystemTime(uuid_time_t* timestamp) -{ - // Get system time with 100ns precision. Time is since Oct 15, 1582. -#if LL_WINDOWS - ULARGE_INTEGER time; - GetSystemTimeAsFileTime((FILETIME*)&time); - // NT keeps time in FILETIME format which is 100ns ticks since - // Jan 1, 1601. UUIDs use time in 100ns ticks since Oct 15, 1582. - // The difference is 17 Days in Oct + 30 (Nov) + 31 (Dec) - // + 18 years and 5 leap days. - time.QuadPart += - (unsigned __int64)(1000 * 1000 * 10) // seconds - * (unsigned __int64)(60 * 60 * 24) // days - * (unsigned __int64)(17 + 30 + 31 + 365 * 18 + 5); // # of days - - timestamp->high = time.HighPart; - timestamp->low = time.LowPart; -#else - struct timeval tp; - gettimeofday(&tp, 0); - - // Offset between UUID formatted times and Unix formatted times. - // UUID UTC base time is October 15, 1582. - // Unix base time is January 1, 1970. - U64 uuid_time = ((U64)tp.tv_sec * 10000000) + (tp.tv_usec * 10) + - U64L(0x01B21DD213814000); - timestamp->high = (U32)(uuid_time >> 32); - timestamp->low = (U32)(uuid_time & 0xFFFFFFFF); -#endif -} - -void LLUUID::getCurrentTime(uuid_time_t* timestamp) -{ - // Get current time as 60 bit 100ns ticks since whenever. - // Compensate for the fact that real clock resolution is less - // than 100ns. - - const U32 uuids_per_tick = 1024; - - static uuid_time_t time_last; - static U32 uuids_this_tick; - static bool init = false; - - if (!init) { - getSystemTime(&time_last); - uuids_this_tick = uuids_per_tick; - init = true; - mMutex = new LLMutex(); - } - - uuid_time_t time_now = { 0,0 }; - - while (1) { - getSystemTime(&time_now); - - // if clock reading changed since last UUID generated - if (cmpTime(&time_last, &time_now)) { - // reset count of uuid's generated with this clock reading - uuids_this_tick = 0; - break; - } - if (uuids_this_tick < uuids_per_tick) { - uuids_this_tick++; - break; - } - // going too fast for our clock; spin - } - - time_last = time_now; - - if (uuids_this_tick != 0) { - if (time_now.low & 0x80000000) { - time_now.low += uuids_this_tick; - if (!(time_now.low & 0x80000000)) - time_now.high++; - } - else - time_now.low += uuids_this_tick; - } - - timestamp->high = time_now.high; - timestamp->low = time_now.low; -} - -void LLUUID::generate() -{ - // Create a UUID. - uuid_time_t timestamp; - - static unsigned char node_id[6]; /* Flawfinder: ignore */ - static int has_init = 0; - - // Create a UUID. - static uuid_time_t time_last = { 0,0 }; - static U16 clock_seq = 0; -#if LL_USE_JANKY_RANDOM_NUMBER_GENERATOR - static U32 seed = 0L; // dummy seed. reset it below -#endif - if (!has_init) - { - has_init = 1; - if (getNodeID(node_id) <= 0) - { - get_random_bytes(node_id, 6); - /* - * Set multicast bit, to prevent conflicts - * with IEEE 802 addresses obtained from - * network cards - */ - node_id[0] |= 0x80; - } - - getCurrentTime(&time_last); -#if LL_USE_JANKY_RANDOM_NUMBER_GENERATOR - seed = time_last.low; -#endif - -#if LL_USE_JANKY_RANDOM_NUMBER_GENERATOR - clock_seq = (U16)janky_fast_random_seeded_bytes(seed, 65536); -#else - clock_seq = (U16)ll_rand(65536); -#endif - } - - // get current time - getCurrentTime(×tamp); - U16 our_clock_seq = clock_seq; - - // if clock hasn't changed or went backward, change clockseq - if (cmpTime(×tamp, &time_last) != 1) - { - LLMutexLock lock(mMutex); - clock_seq = (clock_seq + 1) & 0x3FFF; - if (clock_seq == 0) - clock_seq++; - our_clock_seq = clock_seq; // Ensure we're using a different clock_seq value from previous time - } - - time_last = timestamp; - - memcpy(mData + 10, node_id, 6); /* Flawfinder: ignore */ - U32 tmp; - tmp = timestamp.low; - mData[3] = (unsigned char)tmp; - tmp >>= 8; - mData[2] = (unsigned char)tmp; - tmp >>= 8; - mData[1] = (unsigned char)tmp; - tmp >>= 8; - mData[0] = (unsigned char)tmp; - - tmp = (U16)timestamp.high; - mData[5] = (unsigned char)tmp; - tmp >>= 8; - mData[4] = (unsigned char)tmp; - - tmp = (timestamp.high >> 16) | 0x1000; - mData[7] = (unsigned char)tmp; - tmp >>= 8; - mData[6] = (unsigned char)tmp; - - tmp = our_clock_seq; - - mData[9] = (unsigned char)tmp; - tmp >>= 8; - mData[8] = (unsigned char)tmp; - - HBXXH128::digest(*this, (const void*)mData, 16); -} - -void LLUUID::generate(const std::string& hash_string) -{ - HBXXH128::digest(*this, hash_string); -} - -U32 LLUUID::getRandomSeed() -{ - static unsigned char seed[16]; /* Flawfinder: ignore */ - - getNodeID(&seed[0]); - - // Incorporate the pid into the seed to prevent - // processes that start on the same host at the same - // time from generating the same seed. - pid_t pid = LLApp::getPid(); - - seed[6] = (unsigned char)(pid >> 8); - seed[7] = (unsigned char)(pid); - getSystemTime((uuid_time_t*)(&seed[8])); - - U64 seed64 = HBXXH64::digest((const void*)seed, 16); - return U32(seed64) ^ U32(seed64 >> 32); -} - -bool LLUUID::parseUUID(const std::string& buf, LLUUID* value) -{ - if (buf.empty() || value == NULL) - { - return false; - } - - std::string temp(buf); - LLStringUtil::trim(temp); - if (LLUUID::validate(temp)) - { - value->set(temp); - return true; - } - return false; -} - -//static -LLUUID LLUUID::generateNewID(std::string hash_string) -{ - LLUUID new_id; - if (hash_string.empty()) - { - new_id.generate(); - } - else - { - new_id.generate(hash_string); - } - return new_id; -} - -LLAssetID LLTransactionID::makeAssetID(const LLUUID& session) const -{ - LLAssetID result; - if (isNull()) - { - result.setNull(); - } - else - { - combine(session, result); - } - return result; -} - -// Construct -LLUUID::LLUUID() -{ - setNull(); -} - - -// Faster than copying from memory -void LLUUID::setNull() -{ - U32* word = (U32*)mData; - word[0] = 0; - word[1] = 0; - word[2] = 0; - word[3] = 0; -} - - -// Compare -bool LLUUID::operator==(const LLUUID& rhs) const -{ - U32* tmp = (U32*)mData; - U32* rhstmp = (U32*)rhs.mData; - // Note: binary & to avoid branching - return - (tmp[0] == rhstmp[0]) & - (tmp[1] == rhstmp[1]) & - (tmp[2] == rhstmp[2]) & - (tmp[3] == rhstmp[3]); -} - - -bool LLUUID::operator!=(const LLUUID& rhs) const -{ - U32* tmp = (U32*)mData; - U32* rhstmp = (U32*)rhs.mData; - // Note: binary | to avoid branching - return - (tmp[0] != rhstmp[0]) | - (tmp[1] != rhstmp[1]) | - (tmp[2] != rhstmp[2]) | - (tmp[3] != rhstmp[3]); -} - -/* -// JC: This is dangerous. It allows UUIDs to be cast automatically -// to integers, among other things. Use isNull() or notNull(). - LLUUID::operator bool() const -{ - U32 *word = (U32 *)mData; - return (word[0] | word[1] | word[2] | word[3]) > 0; -} -*/ - -bool LLUUID::notNull() const -{ - U32* word = (U32*)mData; - return (word[0] | word[1] | word[2] | word[3]) > 0; -} - -// Faster than == LLUUID::null because doesn't require -// as much memory access. -bool LLUUID::isNull() const -{ - U32* word = (U32*)mData; - // If all bits are zero, return !0 == true - return !(word[0] | word[1] | word[2] | word[3]); -} - -LLUUID::LLUUID(const char* in_string) -{ - if (!in_string || in_string[0] == 0) - { - setNull(); - return; - } - - set(in_string); -} - -LLUUID::LLUUID(const std::string& in_string) -{ - if (in_string.empty()) - { - setNull(); - return; - } - - set(in_string); -} - -// IW: DON'T "optimize" these w/ U32s or you'll scoogie the sort order -// IW: this will make me very sad -bool LLUUID::operator<(const LLUUID& rhs) const -{ - U32 i; - for (i = 0; i < (UUID_BYTES - 1); i++) - { - if (mData[i] != rhs.mData[i]) - { - return (mData[i] < rhs.mData[i]); - } - } - return (mData[UUID_BYTES - 1] < rhs.mData[UUID_BYTES - 1]); -} - -bool LLUUID::operator>(const LLUUID& rhs) const -{ - U32 i; - for (i = 0; i < (UUID_BYTES - 1); i++) - { - if (mData[i] != rhs.mData[i]) - { - return (mData[i] > rhs.mData[i]); - } - } - return (mData[UUID_BYTES - 1] > rhs.mData[UUID_BYTES - 1]); -} - -U16 LLUUID::getCRC16() const -{ - // A UUID is 16 bytes, or 8 shorts. - U16* short_data = (U16*)mData; - U16 out = 0; - out += short_data[0]; - out += short_data[1]; - out += short_data[2]; - out += short_data[3]; - out += short_data[4]; - out += short_data[5]; - out += short_data[6]; - out += short_data[7]; - return out; -} - -U32 LLUUID::getCRC32() const -{ - U32* tmp = (U32*)mData; - return tmp[0] + tmp[1] + tmp[2] + tmp[3]; -} +/**
+ * @file lluuid.cpp
+ *
+ * $LicenseInfo:firstyear=2000&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+ // We can't use WIN32_LEAN_AND_MEAN here, needs lots of includes.
+#if LL_WINDOWS
+#include "llwin32headers.h"
+// ugh, this is ugly. We need to straighten out our linking for this library
+#pragma comment(lib, "IPHLPAPI.lib")
+#include <iphlpapi.h>
+#endif
+
+#include "llapp.h"
+#include "lldefs.h"
+#include "llerror.h"
+
+#include "lluuid.h"
+#include "llerror.h"
+#include "llrand.h"
+#include "llstring.h"
+#include "lltimer.h"
+#include "llthread.h"
+#include "llmutex.h"
+#include "llmd5.h"
+#include "hbxxh.h"
+
+const LLUUID LLUUID::null;
+const LLTransactionID LLTransactionID::tnull;
+
+// static
+LLMutex* LLUUID::mMutex = NULL;
+
+
+
+/*
+
+NOT DONE YET!!!
+
+static char BASE85_TABLE[] = {
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
+ 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
+ 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
+ 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
+ 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
+ 'y', 'z', '!', '#', '$', '%', '&', '(', ')', '*',
+ '+', '-', ';', '[', '=', '>', '?', '@', '^', '_',
+ '`', '{', '|', '}', '~', '\0'
+};
+
+
+void encode( char * fiveChars, unsigned int word ) throw( )
+{
+for( int ix = 0; ix < 5; ++ix ) {
+fiveChars[4-ix] = encodeTable[ word % 85];
+word /= 85;
+}
+}
+
+To decode:
+unsigned int decode( char const * fiveChars ) throw( bad_input_data )
+{
+unsigned int ret = 0;
+for( int ix = 0; ix < 5; ++ix ) {
+char * s = strchr( encodeTable, fiveChars[ ix ] );
+if( s == 0 ) LLTHROW(bad_input_data());
+ret = ret * 85 + (s-encodeTable);
+}
+return ret;
+}
+
+void LLUUID::toBase85(char* out)
+{
+ U32* me = (U32*)&(mData[0]);
+ for(S32 i = 0; i < 4; ++i)
+ {
+ char* o = &out[i*i];
+ for(S32 j = 0; j < 5; ++j)
+ {
+ o[4-j] = BASE85_TABLE[ me[i] % 85];
+ word /= 85;
+ }
+ }
+}
+
+unsigned int decode( char const * fiveChars ) throw( bad_input_data )
+{
+ unsigned int ret = 0;
+ for( S32 ix = 0; ix < 5; ++ix )
+ {
+ char * s = strchr( encodeTable, fiveChars[ ix ] );
+ ret = ret * 85 + (s-encodeTable);
+ }
+ return ret;
+}
+*/
+
+#define LL_USE_JANKY_RANDOM_NUMBER_GENERATOR 0
+#if LL_USE_JANKY_RANDOM_NUMBER_GENERATOR
+/**
+ * @brief a global for
+ */
+static U64 sJankyRandomSeed(LLUUID::getRandomSeed());
+
+/**
+ * @brief generate a random U32.
+ */
+U32 janky_fast_random_bytes()
+{
+ sJankyRandomSeed = U64L(1664525) * sJankyRandomSeed + U64L(1013904223);
+ return (U32)sJankyRandomSeed;
+}
+
+/**
+ * @brief generate a random U32 from [0, val)
+ */
+U32 janky_fast_random_byes_range(U32 val)
+{
+ sJankyRandomSeed = U64L(1664525) * sJankyRandomSeed + U64L(1013904223);
+ return (U32)(sJankyRandomSeed) % val;
+}
+
+/**
+ * @brief generate a random U32 from [0, val)
+ */
+U32 janky_fast_random_seeded_bytes(U32 seed, U32 val)
+{
+ seed = U64L(1664525) * (U64)(seed)+U64L(1013904223);
+ return (U32)(seed) % val;
+}
+#endif
+
+// Common to all UUID implementations
+void LLUUID::toString(std::string& out) const
+{
+ out = llformat(
+ "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ (U8)(mData[0]),
+ (U8)(mData[1]),
+ (U8)(mData[2]),
+ (U8)(mData[3]),
+ (U8)(mData[4]),
+ (U8)(mData[5]),
+ (U8)(mData[6]),
+ (U8)(mData[7]),
+ (U8)(mData[8]),
+ (U8)(mData[9]),
+ (U8)(mData[10]),
+ (U8)(mData[11]),
+ (U8)(mData[12]),
+ (U8)(mData[13]),
+ (U8)(mData[14]),
+ (U8)(mData[15]));
+}
+
+// *TODO: deprecate
+void LLUUID::toString(char* out) const
+{
+ std::string buffer;
+ toString(buffer);
+ strcpy(out, buffer.c_str()); /* Flawfinder: ignore */
+}
+
+void LLUUID::toCompressedString(std::string& out) const
+{
+ char bytes[UUID_BYTES + 1];
+ memcpy(bytes, mData, UUID_BYTES); /* Flawfinder: ignore */
+ bytes[UUID_BYTES] = '\0';
+ out.assign(bytes, UUID_BYTES);
+}
+
+// *TODO: deprecate
+void LLUUID::toCompressedString(char* out) const
+{
+ memcpy(out, mData, UUID_BYTES); /* Flawfinder: ignore */
+ out[UUID_BYTES] = '\0';
+}
+
+std::string LLUUID::getString() const
+{
+ return asString();
+}
+
+std::string LLUUID::asString() const
+{
+ std::string str;
+ toString(str);
+ return str;
+}
+
+bool LLUUID::set(const char* in_string, bool emit)
+{
+ return set(ll_safe_string(in_string), emit);
+}
+
+bool LLUUID::set(const std::string& in_string, bool emit)
+{
+ bool broken_format = false;
+
+ // empty strings should make NULL uuid
+ if (in_string.empty())
+ {
+ setNull();
+ return true;
+ }
+
+ if (in_string.length() != (UUID_STR_LENGTH - 1)) /* Flawfinder: ignore */
+ {
+ // I'm a moron. First implementation didn't have the right UUID format.
+ // Shouldn't see any of these any more
+ if (in_string.length() == (UUID_STR_LENGTH - 2)) /* Flawfinder: ignore */
+ {
+ if (emit)
+ {
+ LL_WARNS() << "Warning! Using broken UUID string format" << LL_ENDL;
+ }
+ broken_format = true;
+ }
+ else
+ {
+ // Bad UUID string. Spam as INFO, as most cases we don't care.
+ if (emit)
+ {
+ //don't spam the logs because a resident can't spell.
+ LL_WARNS() << "Bad UUID string: " << in_string << LL_ENDL;
+ }
+ setNull();
+ return false;
+ }
+ }
+
+ U8 cur_pos = 0;
+ S32 i;
+ for (i = 0; i < UUID_BYTES; i++)
+ {
+ if ((i == 4) || (i == 6) || (i == 8) || (i == 10))
+ {
+ cur_pos++;
+ if (broken_format && (i == 10))
+ {
+ // Missing - in the broken format
+ cur_pos--;
+ }
+ }
+
+ mData[i] = 0;
+
+ if ((in_string[cur_pos] >= '0') && (in_string[cur_pos] <= '9'))
+ {
+ mData[i] += (U8)(in_string[cur_pos] - '0');
+ }
+ else if ((in_string[cur_pos] >= 'a') && (in_string[cur_pos] <= 'f'))
+ {
+ mData[i] += (U8)(10 + in_string[cur_pos] - 'a');
+ }
+ else if ((in_string[cur_pos] >= 'A') && (in_string[cur_pos] <= 'F'))
+ {
+ mData[i] += (U8)(10 + in_string[cur_pos] - 'A');
+ }
+ else
+ {
+ if (emit)
+ {
+ LL_WARNS() << "Invalid UUID string character" << LL_ENDL;
+ }
+ setNull();
+ return false;
+ }
+
+ mData[i] = mData[i] << 4;
+ cur_pos++;
+
+ if ((in_string[cur_pos] >= '0') && (in_string[cur_pos] <= '9'))
+ {
+ mData[i] += (U8)(in_string[cur_pos] - '0');
+ }
+ else if ((in_string[cur_pos] >= 'a') && (in_string[cur_pos] <= 'f'))
+ {
+ mData[i] += (U8)(10 + in_string[cur_pos] - 'a');
+ }
+ else if ((in_string[cur_pos] >= 'A') && (in_string[cur_pos] <= 'F'))
+ {
+ mData[i] += (U8)(10 + in_string[cur_pos] - 'A');
+ }
+ else
+ {
+ if (emit)
+ {
+ LL_WARNS() << "Invalid UUID string character" << LL_ENDL;
+ }
+ setNull();
+ return false;
+ }
+ cur_pos++;
+ }
+
+ return true;
+}
+
+bool LLUUID::validate(const std::string& in_string)
+{
+ bool broken_format = false;
+ if (in_string.length() != (UUID_STR_LENGTH - 1)) /* Flawfinder: ignore */
+ {
+ // I'm a moron. First implementation didn't have the right UUID format.
+ if (in_string.length() == (UUID_STR_LENGTH - 2)) /* Flawfinder: ignore */
+ {
+ broken_format = true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ U8 cur_pos = 0;
+ for (U32 i = 0; i < 16; i++)
+ {
+ if ((i == 4) || (i == 6) || (i == 8) || (i == 10))
+ {
+ cur_pos++;
+ if (broken_format && (i == 10))
+ {
+ // Missing - in the broken format
+ cur_pos--;
+ }
+ }
+
+ if ((in_string[cur_pos] >= '0') && (in_string[cur_pos] <= '9'))
+ {
+ }
+ else if ((in_string[cur_pos] >= 'a') && (in_string[cur_pos] <= 'f'))
+ {
+ }
+ else if ((in_string[cur_pos] >= 'A') && (in_string[cur_pos] <= 'F'))
+ {
+ }
+ else
+ {
+ return false;
+ }
+
+ cur_pos++;
+
+ if ((in_string[cur_pos] >= '0') && (in_string[cur_pos] <= '9'))
+ {
+ }
+ else if ((in_string[cur_pos] >= 'a') && (in_string[cur_pos] <= 'f'))
+ {
+ }
+ else if ((in_string[cur_pos] >= 'A') && (in_string[cur_pos] <= 'F'))
+ {
+ }
+ else
+ {
+ return false;
+ }
+ cur_pos++;
+ }
+ return true;
+}
+
+const LLUUID& LLUUID::operator^=(const LLUUID& rhs)
+{
+ U32* me = (U32*)&(mData[0]);
+ const U32* other = (U32*)&(rhs.mData[0]);
+ for (S32 i = 0; i < 4; ++i)
+ {
+ me[i] = me[i] ^ other[i];
+ }
+ return *this;
+}
+
+LLUUID LLUUID::operator^(const LLUUID& rhs) const
+{
+ LLUUID id(*this);
+ id ^= rhs;
+ return id;
+}
+
+// WARNING: this algorithm SHALL NOT be changed. It is also used by the server
+// and plays a role in some assets validation (e.g. clothing items). Changing
+// it would cause invalid assets.
+void LLUUID::combine(const LLUUID& other, LLUUID& result) const
+{
+ LLMD5 md5_uuid;
+ md5_uuid.update((unsigned char*)mData, 16);
+ md5_uuid.update((unsigned char*)other.mData, 16);
+ md5_uuid.finalize();
+ md5_uuid.raw_digest(result.mData);
+}
+
+LLUUID LLUUID::combine(const LLUUID& other) const
+{
+ LLUUID combination;
+ combine(other, combination);
+ return combination;
+}
+
+std::ostream& operator<<(std::ostream& s, const LLUUID& uuid)
+{
+ std::string uuid_str;
+ uuid.toString(uuid_str);
+ s << uuid_str;
+ return s;
+}
+
+std::istream& operator>>(std::istream& s, LLUUID& uuid)
+{
+ U32 i;
+ char uuid_str[UUID_STR_LENGTH]; /* Flawfinder: ignore */
+ for (i = 0; i < UUID_STR_LENGTH - 1; i++)
+ {
+ s >> uuid_str[i];
+ }
+ uuid_str[i] = '\0';
+ uuid.set(std::string(uuid_str));
+ return s;
+}
+
+static void get_random_bytes(void* buf, int nbytes)
+{
+ int i;
+ char* cp = (char*)buf;
+
+ // *NOTE: If we are not using the janky generator ll_rand()
+ // generates at least 3 good bytes of data since it is 0 to
+ // RAND_MAX. This could be made more efficient by copying all the
+ // bytes.
+ for (i = 0; i < nbytes; i++)
+#if LL_USE_JANKY_RANDOM_NUMBER_GENERATOR
+ * cp++ = janky_fast_random_bytes() & 0xFF;
+#else
+ * cp++ = ll_rand() & 0xFF;
+#endif
+ return;
+}
+
+#if LL_WINDOWS
+
+typedef struct _ASTAT_
+{
+ ADAPTER_STATUS adapt;
+ NAME_BUFFER NameBuff[30];
+}ASTAT, * PASTAT;
+
+// static
+S32 LLUUID::getNodeID(unsigned char* node_id)
+{
+ ASTAT Adapter;
+ NCB Ncb;
+ UCHAR uRetCode;
+ LANA_ENUM lenum;
+ int i;
+ int retval = 0;
+
+ memset(&Ncb, 0, sizeof(Ncb));
+ Ncb.ncb_command = NCBENUM;
+ Ncb.ncb_buffer = (UCHAR*)&lenum;
+ Ncb.ncb_length = sizeof(lenum);
+ uRetCode = Netbios(&Ncb);
+
+ for (i = 0; i < lenum.length; i++)
+ {
+ memset(&Ncb, 0, sizeof(Ncb));
+ Ncb.ncb_command = NCBRESET;
+ Ncb.ncb_lana_num = lenum.lana[i];
+
+ uRetCode = Netbios(&Ncb);
+
+ memset(&Ncb, 0, sizeof(Ncb));
+ Ncb.ncb_command = NCBASTAT;
+ Ncb.ncb_lana_num = lenum.lana[i];
+
+ strcpy((char*)Ncb.ncb_callname, "* "); /* Flawfinder: ignore */
+ Ncb.ncb_buffer = (unsigned char*)&Adapter;
+ Ncb.ncb_length = sizeof(Adapter);
+
+ uRetCode = Netbios(&Ncb);
+ if (uRetCode == 0)
+ {
+ memcpy(node_id, Adapter.adapt.adapter_address, 6); /* Flawfinder: ignore */
+ retval = 1;
+ }
+ }
+ return retval;
+}
+
+#elif LL_DARWIN
+// macOS version of the UUID generation code...
+/*
+ * Get an ethernet hardware address, if we can find it...
+ */
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+#include <ifaddrs.h>
+
+ // static
+S32 LLUUID::getNodeID(unsigned char* node_id)
+{
+ int i;
+ unsigned char* a = NULL;
+ struct ifaddrs* ifap, * ifa;
+ int rv;
+ S32 result = 0;
+
+ if ((rv = getifaddrs(&ifap)) == -1)
+ {
+ return -1;
+ }
+ if (ifap == NULL)
+ {
+ return -1;
+ }
+
+ for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next)
+ {
+ // printf("Interface %s, address family %d, ", ifa->ifa_name, ifa->ifa_addr->sa_family);
+ for (i = 0; i < ifa->ifa_addr->sa_len; i++)
+ {
+ // printf("%02X ", (unsigned char)ifa->ifa_addr->sa_data[i]);
+ }
+ // printf("\n");
+
+ if (ifa->ifa_addr->sa_family == AF_LINK)
+ {
+ // This is a link-level address
+ struct sockaddr_dl* lla = (struct sockaddr_dl*)ifa->ifa_addr;
+
+ // printf("\tLink level address, type %02X\n", lla->sdl_type);
+
+ if (lla->sdl_type == IFT_ETHER)
+ {
+ // Use the first ethernet MAC in the list.
+ // For some reason, the macro LLADDR() defined in net/if_dl.h doesn't expand correctly. This is what it would do.
+ a = (unsigned char*)&((lla)->sdl_data);
+ a += (lla)->sdl_nlen;
+
+ if (!a[0] && !a[1] && !a[2] && !a[3] && !a[4] && !a[5])
+ {
+ continue;
+ }
+
+ if (node_id)
+ {
+ memcpy(node_id, a, 6);
+ result = 1;
+ }
+
+ // We found one.
+ break;
+ }
+ }
+ }
+ freeifaddrs(ifap);
+
+ return result;
+}
+
+#else
+
+// Linux version of the UUID generation code...
+/*
+ * Get the ethernet hardware address, if we can find it...
+ */
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#define HAVE_NETINET_IN_H
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#if !LL_DARWIN
+#include <linux/sockios.h>
+#endif
+#endif
+
+ // static
+S32 LLUUID::getNodeID(unsigned char* node_id)
+{
+ int sd;
+ struct ifreq ifr, * ifrp;
+ struct ifconf ifc;
+ char buf[1024];
+ int n, i;
+ unsigned char* a;
+
+ /*
+ * BSD 4.4 defines the size of an ifreq to be
+ * max(sizeof(ifreq), sizeof(ifreq.ifr_name)+ifreq.ifr_addr.sa_len
+ * However, under earlier systems, sa_len isn't present, so the size is
+ * just sizeof(struct ifreq)
+ */
+#ifdef HAVE_SA_LEN
+#ifndef max
+#define max(a,b) ((a) > (b) ? (a) : (b))
+#endif
+#define ifreq_size(i) max(sizeof(struct ifreq),\
+ sizeof((i).ifr_name)+(i).ifr_addr.sa_len)
+#else
+#define ifreq_size(i) sizeof(struct ifreq)
+#endif /* HAVE_SA_LEN*/
+
+ sd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
+ if (sd < 0) {
+ return -1;
+ }
+ memset(buf, 0, sizeof(buf));
+ ifc.ifc_len = sizeof(buf);
+ ifc.ifc_buf = buf;
+ if (ioctl(sd, SIOCGIFCONF, (char*)&ifc) < 0) {
+ close(sd);
+ return -1;
+ }
+ n = ifc.ifc_len;
+ for (i = 0; i < n; i += ifreq_size(*ifr)) {
+ ifrp = (struct ifreq*)((char*)ifc.ifc_buf + i);
+ strncpy(ifr.ifr_name, ifrp->ifr_name, IFNAMSIZ); /* Flawfinder: ignore */
+#ifdef SIOCGIFHWADDR
+ if (ioctl(sd, SIOCGIFHWADDR, &ifr) < 0)
+ continue;
+ a = (unsigned char*)&ifr.ifr_hwaddr.sa_data;
+#else
+#ifdef SIOCGENADDR
+ if (ioctl(sd, SIOCGENADDR, &ifr) < 0)
+ continue;
+ a = (unsigned char*)ifr.ifr_enaddr;
+#else
+ /*
+ * XXX we don't have a way of getting the hardware
+ * address
+ */
+ close(sd);
+ return 0;
+#endif /* SIOCGENADDR */
+#endif /* SIOCGIFHWADDR */
+ if (!a[0] && !a[1] && !a[2] && !a[3] && !a[4] && !a[5])
+ continue;
+ if (node_id) {
+ memcpy(node_id, a, 6); /* Flawfinder: ignore */
+ close(sd);
+ return 1;
+ }
+ }
+ close(sd);
+ return 0;
+}
+
+#endif
+
+S32 LLUUID::cmpTime(uuid_time_t* t1, uuid_time_t* t2)
+{
+ // Compare two time values.
+
+ if (t1->high < t2->high) return -1;
+ if (t1->high > t2->high) return 1;
+ if (t1->low < t2->low) return -1;
+ if (t1->low > t2->low) return 1;
+ return 0;
+}
+
+void LLUUID::getSystemTime(uuid_time_t* timestamp)
+{
+ // Get system time with 100ns precision. Time is since Oct 15, 1582.
+#if LL_WINDOWS
+ ULARGE_INTEGER time;
+ GetSystemTimeAsFileTime((FILETIME*)&time);
+ // NT keeps time in FILETIME format which is 100ns ticks since
+ // Jan 1, 1601. UUIDs use time in 100ns ticks since Oct 15, 1582.
+ // The difference is 17 Days in Oct + 30 (Nov) + 31 (Dec)
+ // + 18 years and 5 leap days.
+ time.QuadPart +=
+ (unsigned __int64)(1000 * 1000 * 10) // seconds
+ * (unsigned __int64)(60 * 60 * 24) // days
+ * (unsigned __int64)(17 + 30 + 31 + 365 * 18 + 5); // # of days
+
+ timestamp->high = time.HighPart;
+ timestamp->low = time.LowPart;
+#else
+ struct timeval tp;
+ gettimeofday(&tp, 0);
+
+ // Offset between UUID formatted times and Unix formatted times.
+ // UUID UTC base time is October 15, 1582.
+ // Unix base time is January 1, 1970.
+ U64 uuid_time = ((U64)tp.tv_sec * 10000000) + (tp.tv_usec * 10) +
+ U64L(0x01B21DD213814000);
+ timestamp->high = (U32)(uuid_time >> 32);
+ timestamp->low = (U32)(uuid_time & 0xFFFFFFFF);
+#endif
+}
+
+void LLUUID::getCurrentTime(uuid_time_t* timestamp)
+{
+ // Get current time as 60 bit 100ns ticks since whenever.
+ // Compensate for the fact that real clock resolution is less
+ // than 100ns.
+
+ const U32 uuids_per_tick = 1024;
+
+ static uuid_time_t time_last;
+ static U32 uuids_this_tick;
+ static bool init = false;
+
+ if (!init) {
+ getSystemTime(&time_last);
+ uuids_this_tick = uuids_per_tick;
+ init = true;
+ mMutex = new LLMutex();
+ }
+
+ uuid_time_t time_now = { 0,0 };
+
+ while (1) {
+ getSystemTime(&time_now);
+
+ // if clock reading changed since last UUID generated
+ if (cmpTime(&time_last, &time_now)) {
+ // reset count of uuid's generated with this clock reading
+ uuids_this_tick = 0;
+ break;
+ }
+ if (uuids_this_tick < uuids_per_tick) {
+ uuids_this_tick++;
+ break;
+ }
+ // going too fast for our clock; spin
+ }
+
+ time_last = time_now;
+
+ if (uuids_this_tick != 0) {
+ if (time_now.low & 0x80000000) {
+ time_now.low += uuids_this_tick;
+ if (!(time_now.low & 0x80000000))
+ time_now.high++;
+ }
+ else
+ time_now.low += uuids_this_tick;
+ }
+
+ timestamp->high = time_now.high;
+ timestamp->low = time_now.low;
+}
+
+void LLUUID::generate()
+{
+ // Create a UUID.
+ uuid_time_t timestamp;
+
+ static unsigned char node_id[6]; /* Flawfinder: ignore */
+ static int has_init = 0;
+
+ // Create a UUID.
+ static uuid_time_t time_last = { 0,0 };
+ static U16 clock_seq = 0;
+#if LL_USE_JANKY_RANDOM_NUMBER_GENERATOR
+ static U32 seed = 0L; // dummy seed. reset it below
+#endif
+ if (!has_init)
+ {
+ has_init = 1;
+ if (getNodeID(node_id) <= 0)
+ {
+ get_random_bytes(node_id, 6);
+ /*
+ * Set multicast bit, to prevent conflicts
+ * with IEEE 802 addresses obtained from
+ * network cards
+ */
+ node_id[0] |= 0x80;
+ }
+
+ getCurrentTime(&time_last);
+#if LL_USE_JANKY_RANDOM_NUMBER_GENERATOR
+ seed = time_last.low;
+#endif
+
+#if LL_USE_JANKY_RANDOM_NUMBER_GENERATOR
+ clock_seq = (U16)janky_fast_random_seeded_bytes(seed, 65536);
+#else
+ clock_seq = (U16)ll_rand(65536);
+#endif
+ }
+
+ // get current time
+ getCurrentTime(×tamp);
+ U16 our_clock_seq = clock_seq;
+
+ // if clock hasn't changed or went backward, change clockseq
+ if (cmpTime(×tamp, &time_last) != 1)
+ {
+ LLMutexLock lock(mMutex);
+ clock_seq = (clock_seq + 1) & 0x3FFF;
+ if (clock_seq == 0)
+ clock_seq++;
+ our_clock_seq = clock_seq; // Ensure we're using a different clock_seq value from previous time
+ }
+
+ time_last = timestamp;
+
+ memcpy(mData + 10, node_id, 6); /* Flawfinder: ignore */
+ U32 tmp;
+ tmp = timestamp.low;
+ mData[3] = (unsigned char)tmp;
+ tmp >>= 8;
+ mData[2] = (unsigned char)tmp;
+ tmp >>= 8;
+ mData[1] = (unsigned char)tmp;
+ tmp >>= 8;
+ mData[0] = (unsigned char)tmp;
+
+ tmp = (U16)timestamp.high;
+ mData[5] = (unsigned char)tmp;
+ tmp >>= 8;
+ mData[4] = (unsigned char)tmp;
+
+ tmp = (timestamp.high >> 16) | 0x1000;
+ mData[7] = (unsigned char)tmp;
+ tmp >>= 8;
+ mData[6] = (unsigned char)tmp;
+
+ tmp = our_clock_seq;
+
+ mData[9] = (unsigned char)tmp;
+ tmp >>= 8;
+ mData[8] = (unsigned char)tmp;
+
+ HBXXH128::digest(*this, (const void*)mData, 16);
+}
+
+void LLUUID::generate(const std::string& hash_string)
+{
+ HBXXH128::digest(*this, hash_string);
+}
+
+U32 LLUUID::getRandomSeed()
+{
+ static unsigned char seed[16]; /* Flawfinder: ignore */
+
+ getNodeID(&seed[0]);
+
+ // Incorporate the pid into the seed to prevent
+ // processes that start on the same host at the same
+ // time from generating the same seed.
+ pid_t pid = LLApp::getPid();
+
+ seed[6] = (unsigned char)(pid >> 8);
+ seed[7] = (unsigned char)(pid);
+ getSystemTime((uuid_time_t*)(&seed[8]));
+
+ U64 seed64 = HBXXH64::digest((const void*)seed, 16);
+ return U32(seed64) ^ U32(seed64 >> 32);
+}
+
+bool LLUUID::parseUUID(const std::string& buf, LLUUID* value)
+{
+ if (buf.empty() || value == NULL)
+ {
+ return false;
+ }
+
+ std::string temp(buf);
+ LLStringUtil::trim(temp);
+ if (LLUUID::validate(temp))
+ {
+ value->set(temp);
+ return true;
+ }
+ return false;
+}
+
+//static
+LLUUID LLUUID::generateNewID(std::string hash_string)
+{
+ LLUUID new_id;
+ if (hash_string.empty())
+ {
+ new_id.generate();
+ }
+ else
+ {
+ new_id.generate(hash_string);
+ }
+ return new_id;
+}
+
+LLAssetID LLTransactionID::makeAssetID(const LLUUID& session) const
+{
+ LLAssetID result;
+ if (isNull())
+ {
+ result.setNull();
+ }
+ else
+ {
+ combine(session, result);
+ }
+ return result;
+}
+
+// Construct
+LLUUID::LLUUID()
+{
+ setNull();
+}
+
+
+// Faster than copying from memory
+void LLUUID::setNull()
+{
+ U32* word = (U32*)mData;
+ word[0] = 0;
+ word[1] = 0;
+ word[2] = 0;
+ word[3] = 0;
+}
+
+
+// Compare
+bool LLUUID::operator==(const LLUUID& rhs) const
+{
+ U32* tmp = (U32*)mData;
+ U32* rhstmp = (U32*)rhs.mData;
+ // Note: binary & to avoid branching
+ return
+ (tmp[0] == rhstmp[0]) &
+ (tmp[1] == rhstmp[1]) &
+ (tmp[2] == rhstmp[2]) &
+ (tmp[3] == rhstmp[3]);
+}
+
+
+bool LLUUID::operator!=(const LLUUID& rhs) const
+{
+ U32* tmp = (U32*)mData;
+ U32* rhstmp = (U32*)rhs.mData;
+ // Note: binary | to avoid branching
+ return
+ (tmp[0] != rhstmp[0]) |
+ (tmp[1] != rhstmp[1]) |
+ (tmp[2] != rhstmp[2]) |
+ (tmp[3] != rhstmp[3]);
+}
+
+/*
+// JC: This is dangerous. It allows UUIDs to be cast automatically
+// to integers, among other things. Use isNull() or notNull().
+ LLUUID::operator bool() const
+{
+ U32 *word = (U32 *)mData;
+ return (word[0] | word[1] | word[2] | word[3]) > 0;
+}
+*/
+
+bool LLUUID::notNull() const
+{
+ U32* word = (U32*)mData;
+ return (word[0] | word[1] | word[2] | word[3]) > 0;
+}
+
+// Faster than == LLUUID::null because doesn't require
+// as much memory access.
+bool LLUUID::isNull() const
+{
+ U32* word = (U32*)mData;
+ // If all bits are zero, return !0 == true
+ return !(word[0] | word[1] | word[2] | word[3]);
+}
+
+LLUUID::LLUUID(const char* in_string)
+{
+ if (!in_string || in_string[0] == 0)
+ {
+ setNull();
+ return;
+ }
+
+ set(in_string);
+}
+
+LLUUID::LLUUID(const std::string& in_string)
+{
+ if (in_string.empty())
+ {
+ setNull();
+ return;
+ }
+
+ set(in_string);
+}
+
+// IW: DON'T "optimize" these w/ U32s or you'll scoogie the sort order
+// IW: this will make me very sad
+bool LLUUID::operator<(const LLUUID& rhs) const
+{
+ U32 i;
+ for (i = 0; i < (UUID_BYTES - 1); i++)
+ {
+ if (mData[i] != rhs.mData[i])
+ {
+ return (mData[i] < rhs.mData[i]);
+ }
+ }
+ return (mData[UUID_BYTES - 1] < rhs.mData[UUID_BYTES - 1]);
+}
+
+bool LLUUID::operator>(const LLUUID& rhs) const
+{
+ U32 i;
+ for (i = 0; i < (UUID_BYTES - 1); i++)
+ {
+ if (mData[i] != rhs.mData[i])
+ {
+ return (mData[i] > rhs.mData[i]);
+ }
+ }
+ return (mData[UUID_BYTES - 1] > rhs.mData[UUID_BYTES - 1]);
+}
+
+U16 LLUUID::getCRC16() const
+{
+ // A UUID is 16 bytes, or 8 shorts.
+ U16* short_data = (U16*)mData;
+ U16 out = 0;
+ out += short_data[0];
+ out += short_data[1];
+ out += short_data[2];
+ out += short_data[3];
+ out += short_data[4];
+ out += short_data[5];
+ out += short_data[6];
+ out += short_data[7];
+ return out;
+}
+
+U32 LLUUID::getCRC32() const
+{
+ U32* tmp = (U32*)mData;
+ return tmp[0] + tmp[1] + tmp[2] + tmp[3];
+}
diff --git a/indra/llcommon/lluuid.h b/indra/llcommon/lluuid.h index 54fb5742a2..526a79f3a7 100644 --- a/indra/llcommon/lluuid.h +++ b/indra/llcommon/lluuid.h @@ -1,194 +1,194 @@ -/** - * @file lluuid.h - * - * $LicenseInfo:firstyear=2000&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLUUID_H -#define LL_LLUUID_H - -#include <iostream> -#include <set> -#include <vector> -#include "stdtypes.h" -#include "llpreprocessor.h" -#include <boost/functional/hash.hpp> - -class LLMutex; - -const S32 UUID_BYTES = 16; -const S32 UUID_WORDS = 4; -const S32 UUID_STR_LENGTH = 37; // actually wrong, should be 36 and use size below -const S32 UUID_STR_SIZE = 37; -const S32 UUID_BASE85_LENGTH = 21; // including the trailing NULL. - -struct uuid_time_t { - U32 high; - U32 low; - }; - -class LL_COMMON_API LLUUID -{ -public: - // - // CREATORS - // - LLUUID(); - explicit LLUUID(const char *in_string); // Convert from string. - explicit LLUUID(const std::string& in_string); // Convert from string. - ~LLUUID() = default; - - // - // MANIPULATORS - // - void generate(); // Generate a new UUID - void generate(const std::string& stream); //Generate a new UUID based on hash of input stream - - static LLUUID generateNewID(std::string stream = ""); //static version of above for use in initializer expressions such as constructor params, etc. - - bool set(const char *in_string, bool emit = true); // Convert from string, if emit is false, do not emit warnings - bool set(const std::string& in_string, bool emit = true); // Convert from string, if emit is false, do not emit warnings - void setNull(); // Faster than setting to LLUUID::null. - - S32 cmpTime(uuid_time_t *t1, uuid_time_t *t2); - static void getSystemTime(uuid_time_t *timestamp); - void getCurrentTime(uuid_time_t *timestamp); - - // - // ACCESSORS - // - bool isNull() const; // Faster than comparing to LLUUID::null. - bool notNull() const; // Faster than comparing to LLUUID::null. - // JC: This is dangerous. It allows UUIDs to be cast automatically - // to integers, among other things. Use isNull() or notNull(). - // operator bool() const; - - // JC: These must return real bool's (not BOOLs) or else use of the STL - // will generate bool-to-int performance warnings. - bool operator==(const LLUUID &rhs) const; - bool operator!=(const LLUUID &rhs) const; - bool operator<(const LLUUID &rhs) const; - bool operator>(const LLUUID &rhs) const; - - // xor functions. Useful since any two random uuids xored together - // will yield a determinate third random unique id that can be - // used as a key in a single uuid that represents 2. - const LLUUID& operator^=(const LLUUID& rhs); - LLUUID operator^(const LLUUID& rhs) const; - - // similar to functions above, but not invertible - // yields a third random UUID that can be reproduced from the two inputs - // but which, given the result and one of the inputs can't be used to - // deduce the other input - LLUUID combine(const LLUUID& other) const; - void combine(const LLUUID& other, LLUUID& result) const; - - 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; - void toCompressedString(char *out) const; // Does not allocate memory, needs 17 characters (including \0) - void toCompressedString(std::string& out) const; - - std::string asString() const; - std::string getString() const; - - U16 getCRC16() const; - U32 getCRC32() const; - - // Returns a 64 bits digest of the UUID, by XORing its two 64 bits long - // words. HB - inline U64 getDigest64() const - { - U64* tmp = (U64*)mData; - return tmp[0] ^ tmp[1]; - } - - static bool validate(const std::string& in_string); // Validate that the UUID string is legal. - - static const LLUUID null; - static LLMutex * mMutex; - - static U32 getRandomSeed(); - static S32 getNodeID(unsigned char * node_id); - - static bool parseUUID(const std::string& buf, LLUUID* value); - - U8 mData[UUID_BYTES]; -}; -static_assert(std::is_trivially_copyable<LLUUID>::value, "LLUUID must be trivial copy"); -static_assert(std::is_trivially_move_assignable<LLUUID>::value, "LLUUID must be trivial move"); -static_assert(std::is_standard_layout<LLUUID>::value, "LLUUID must be a standard layout type"); - -typedef std::vector<LLUUID> uuid_vec_t; -typedef std::set<LLUUID> uuid_set_t; - -// Helper structure for ordering lluuids in stl containers. eg: -// std::map<LLUUID, LLWidget*, lluuid_less> widget_map; -// -// (isn't this the default behavior anyway? I think we could -// everywhere replace these with uuid_set_t, but someone should -// verify.) -struct lluuid_less -{ - bool operator()(const LLUUID& lhs, const LLUUID& rhs) const - { - return lhs < rhs; - } -}; - -typedef std::set<LLUUID, lluuid_less> uuid_list_t; -/* - * Sub-classes for keeping transaction IDs and asset IDs - * straight. - */ -typedef LLUUID LLAssetID; - -class LL_COMMON_API LLTransactionID : public LLUUID -{ -public: - LLTransactionID() : LLUUID() { } - - static const LLTransactionID tnull; - LLAssetID makeAssetID(const LLUUID& session) const; -}; - -// std::hash implementation for LLUUID -namespace std -{ - template<> struct hash<LLUUID> - { - inline size_t operator()(const LLUUID& id) const noexcept - { - return (size_t)id.getDigest64(); - } - }; -} - -// For use with boost containers. -inline size_t hash_value(const LLUUID& id) noexcept -{ - return (size_t)id.getDigest64(); -} - -#endif // LL_LLUUID_H +/**
+ * @file lluuid.h
+ *
+ * $LicenseInfo:firstyear=2000&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLUUID_H
+#define LL_LLUUID_H
+
+#include <iostream>
+#include <set>
+#include <vector>
+#include "stdtypes.h"
+#include "llpreprocessor.h"
+#include <boost/functional/hash.hpp>
+
+class LLMutex;
+
+const S32 UUID_BYTES = 16;
+const S32 UUID_WORDS = 4;
+const S32 UUID_STR_LENGTH = 37; // actually wrong, should be 36 and use size below
+const S32 UUID_STR_SIZE = 37;
+const S32 UUID_BASE85_LENGTH = 21; // including the trailing NULL.
+
+struct uuid_time_t {
+ U32 high;
+ U32 low;
+ };
+
+class LL_COMMON_API LLUUID
+{
+public:
+ //
+ // CREATORS
+ //
+ LLUUID();
+ explicit LLUUID(const char *in_string); // Convert from string.
+ explicit LLUUID(const std::string& in_string); // Convert from string.
+ ~LLUUID() = default;
+
+ //
+ // MANIPULATORS
+ //
+ void generate(); // Generate a new UUID
+ void generate(const std::string& stream); //Generate a new UUID based on hash of input stream
+
+ static LLUUID generateNewID(std::string stream = ""); //static version of above for use in initializer expressions such as constructor params, etc.
+
+ bool set(const char *in_string, bool emit = true); // Convert from string, if emit is false, do not emit warnings
+ bool set(const std::string& in_string, bool emit = true); // Convert from string, if emit is false, do not emit warnings
+ void setNull(); // Faster than setting to LLUUID::null.
+
+ S32 cmpTime(uuid_time_t *t1, uuid_time_t *t2);
+ static void getSystemTime(uuid_time_t *timestamp);
+ void getCurrentTime(uuid_time_t *timestamp);
+
+ //
+ // ACCESSORS
+ //
+ bool isNull() const; // Faster than comparing to LLUUID::null.
+ bool notNull() const; // Faster than comparing to LLUUID::null.
+ // JC: This is dangerous. It allows UUIDs to be cast automatically
+ // to integers, among other things. Use isNull() or notNull().
+ // operator bool() const;
+
+ // JC: These must return real bool's (not BOOLs) or else use of the STL
+ // will generate bool-to-int performance warnings.
+ bool operator==(const LLUUID &rhs) const;
+ bool operator!=(const LLUUID &rhs) const;
+ bool operator<(const LLUUID &rhs) const;
+ bool operator>(const LLUUID &rhs) const;
+
+ // xor functions. Useful since any two random uuids xored together
+ // will yield a determinate third random unique id that can be
+ // used as a key in a single uuid that represents 2.
+ const LLUUID& operator^=(const LLUUID& rhs);
+ LLUUID operator^(const LLUUID& rhs) const;
+
+ // similar to functions above, but not invertible
+ // yields a third random UUID that can be reproduced from the two inputs
+ // but which, given the result and one of the inputs can't be used to
+ // deduce the other input
+ LLUUID combine(const LLUUID& other) const;
+ void combine(const LLUUID& other, LLUUID& result) const;
+
+ 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;
+ void toCompressedString(char *out) const; // Does not allocate memory, needs 17 characters (including \0)
+ void toCompressedString(std::string& out) const;
+
+ std::string asString() const;
+ std::string getString() const;
+
+ U16 getCRC16() const;
+ U32 getCRC32() const;
+
+ // Returns a 64 bits digest of the UUID, by XORing its two 64 bits long
+ // words. HB
+ inline U64 getDigest64() const
+ {
+ U64* tmp = (U64*)mData;
+ return tmp[0] ^ tmp[1];
+ }
+
+ static bool validate(const std::string& in_string); // Validate that the UUID string is legal.
+
+ static const LLUUID null;
+ static LLMutex * mMutex;
+
+ static U32 getRandomSeed();
+ static S32 getNodeID(unsigned char * node_id);
+
+ static bool parseUUID(const std::string& buf, LLUUID* value);
+
+ U8 mData[UUID_BYTES];
+};
+static_assert(std::is_trivially_copyable<LLUUID>::value, "LLUUID must be trivial copy");
+static_assert(std::is_trivially_move_assignable<LLUUID>::value, "LLUUID must be trivial move");
+static_assert(std::is_standard_layout<LLUUID>::value, "LLUUID must be a standard layout type");
+
+typedef std::vector<LLUUID> uuid_vec_t;
+typedef std::set<LLUUID> uuid_set_t;
+
+// Helper structure for ordering lluuids in stl containers. eg:
+// std::map<LLUUID, LLWidget*, lluuid_less> widget_map;
+//
+// (isn't this the default behavior anyway? I think we could
+// everywhere replace these with uuid_set_t, but someone should
+// verify.)
+struct lluuid_less
+{
+ bool operator()(const LLUUID& lhs, const LLUUID& rhs) const
+ {
+ return lhs < rhs;
+ }
+};
+
+typedef std::set<LLUUID, lluuid_less> uuid_list_t;
+/*
+ * Sub-classes for keeping transaction IDs and asset IDs
+ * straight.
+ */
+typedef LLUUID LLAssetID;
+
+class LL_COMMON_API LLTransactionID : public LLUUID
+{
+public:
+ LLTransactionID() : LLUUID() { }
+
+ static const LLTransactionID tnull;
+ LLAssetID makeAssetID(const LLUUID& session) const;
+};
+
+// std::hash implementation for LLUUID
+namespace std
+{
+ template<> struct hash<LLUUID>
+ {
+ inline size_t operator()(const LLUUID& id) const noexcept
+ {
+ return (size_t)id.getDigest64();
+ }
+ };
+}
+
+// For use with boost containers.
+inline size_t hash_value(const LLUUID& id) noexcept
+{
+ return (size_t)id.getDigest64();
+}
+
+#endif // LL_LLUUID_H
diff --git a/indra/llcommon/llwin32headers.h b/indra/llcommon/llwin32headers.h index 8cfa40ada8..f679adc200 100644 --- a/indra/llcommon/llwin32headers.h +++ b/indra/llcommon/llwin32headers.h @@ -1,25 +1,25 @@ -/** +/** * @file llwin32headers.h * @brief sanitized include of windows header files * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llcommon/llwin32headerslean.h b/indra/llcommon/llwin32headerslean.h index 314e7a85d1..97c8edb8cf 100644 --- a/indra/llcommon/llwin32headerslean.h +++ b/indra/llcommon/llwin32headerslean.h @@ -1,25 +1,25 @@ -/** +/** * @file llwin32headerslean.h * @brief sanitized include of windows header files * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llcommon/llworkerthread.cpp b/indra/llcommon/llworkerthread.cpp index 231dbe4310..22a922c94b 100644 --- a/indra/llcommon/llworkerthread.cpp +++ b/indra/llcommon/llworkerthread.cpp @@ -1,398 +1,398 @@ -/** - * @file llworkerthread.cpp - * - * $LicenseInfo:firstyear=2004&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" -#include "llworkerthread.h" -#include "llstl.h" - -#if USE_FRAME_CALLBACK_MANAGER -#include "llframecallbackmanager.h" -#endif - -//============================================================================ -// Run on MAIN thread - -LLWorkerThread::LLWorkerThread(const std::string& name, bool threaded, bool should_pause) : - LLQueuedThread(name, threaded, should_pause) -{ - mDeleteMutex = new LLMutex(); - - if(!mLocalAPRFilePoolp) - { - mLocalAPRFilePoolp = new LLVolatileAPRPool() ; - } -} - -LLWorkerThread::~LLWorkerThread() -{ - // Delete any workers in the delete queue (should be safe - had better be!) - if (!mDeleteList.empty()) - { - LL_WARNS() << "Worker Thread: " << mName << " destroyed with " << mDeleteList.size() - << " entries in delete list." << LL_ENDL; - } - - delete mDeleteMutex; - - // ~LLQueuedThread() will be called here -} - -//called only in destructor. -void LLWorkerThread::clearDeleteList() -{ - // Delete any workers in the delete queue (should be safe - had better be!) - if (!mDeleteList.empty()) - { - LL_WARNS() << "Worker Thread: " << mName << " destroyed with " << mDeleteList.size() - << " entries in delete list." << LL_ENDL; - - mDeleteMutex->lock(); - for (LLWorkerClass* worker : mDeleteList) - { - worker->mRequestHandle = LLWorkerThread::nullHandle(); - worker->clearFlags(LLWorkerClass::WCF_HAVE_WORK); - worker->clearFlags(LLWorkerClass::WCF_WORKING); - delete worker; - } - mDeleteList.clear() ; - mDeleteMutex->unlock() ; - } -} - -// virtual -size_t LLWorkerThread::update(F32 max_time_ms) -{ - auto res = LLQueuedThread::update(max_time_ms); - // Delete scheduled workers - std::vector<LLWorkerClass*> delete_list; - std::vector<LLWorkerClass*> abort_list; - mDeleteMutex->lock(); - for (delete_list_t::iterator iter = mDeleteList.begin(); - iter != mDeleteList.end(); ) - { - delete_list_t::iterator curiter = iter++; - LLWorkerClass* worker = *curiter; - if (worker->deleteOK()) - { - if (worker->getFlags(LLWorkerClass::WCF_WORK_FINISHED)) - { - worker->setFlags(LLWorkerClass::WCF_DELETE_REQUESTED); - delete_list.push_back(worker); - mDeleteList.erase(curiter); - } - else if (!worker->getFlags(LLWorkerClass::WCF_ABORT_REQUESTED)) - { - abort_list.push_back(worker); - } - } - } - mDeleteMutex->unlock(); - // abort and delete after releasing mutex - for (LLWorkerClass* worker : abort_list) - { - worker->abortWork(false); - } - for (LLWorkerClass* worker : delete_list) - { - if (worker->mRequestHandle) - { - // Finished but not completed - completeRequest(worker->mRequestHandle); - worker->mRequestHandle = LLWorkerThread::nullHandle(); - worker->clearFlags(LLWorkerClass::WCF_HAVE_WORK); - } - delete worker; - } - // delete and aborted entries mean there's still work to do - res += delete_list.size() + abort_list.size(); - return res; -} - -//---------------------------------------------------------------------------- - -LLWorkerThread::handle_t LLWorkerThread::addWorkRequest(LLWorkerClass* workerclass, S32 param) -{ - handle_t handle = generateHandle(); - - WorkRequest* req = new WorkRequest(handle, workerclass, param); - - bool res = addRequest(req); - if (!res) - { - LL_ERRS() << "add called after LLWorkerThread::cleanupClass()" << LL_ENDL; - req->deleteRequest(); - handle = nullHandle(); - } - - return handle; -} - -void LLWorkerThread::deleteWorker(LLWorkerClass* workerclass) -{ - mDeleteMutex->lock(); - mDeleteList.push_back(workerclass); - mDeleteMutex->unlock(); -} - -//============================================================================ -// Runs on its OWN thread - -LLWorkerThread::WorkRequest::WorkRequest(handle_t handle, LLWorkerClass* workerclass, S32 param) : - LLQueuedThread::QueuedRequest(handle), - mWorkerClass(workerclass), - mParam(param) -{ -} - -LLWorkerThread::WorkRequest::~WorkRequest() -{ -} - -// virtual (required for access by LLWorkerThread) -void LLWorkerThread::WorkRequest::deleteRequest() -{ - LLQueuedThread::QueuedRequest::deleteRequest(); -} - -// virtual -bool LLWorkerThread::WorkRequest::processRequest() -{ - LL_PROFILE_ZONE_SCOPED; - LLWorkerClass* workerclass = getWorkerClass(); - workerclass->setWorking(true); - bool complete = workerclass->doWork(getParam()); - workerclass->setWorking(false); - return complete; -} - -// virtual -void LLWorkerThread::WorkRequest::finishRequest(bool completed) -{ - LL_PROFILE_ZONE_SCOPED; - LLWorkerClass* workerclass = getWorkerClass(); - workerclass->finishWork(getParam(), completed); - U32 flags = LLWorkerClass::WCF_WORK_FINISHED | (completed ? 0 : LLWorkerClass::WCF_WORK_ABORTED); - workerclass->setFlags(flags); -} - -//============================================================================ -// LLWorkerClass:: operates in main thread - -LLWorkerClass::LLWorkerClass(LLWorkerThread* workerthread, const std::string& name) - : mWorkerThread(workerthread), - mWorkerClassName(name), - mRequestHandle(LLWorkerThread::nullHandle()), - mMutex(), - mWorkFlags(0) -{ - if (!mWorkerThread) - { - LL_ERRS() << "LLWorkerClass() called with NULL workerthread: " << name << LL_ENDL; - } -} - -LLWorkerClass::~LLWorkerClass() -{ - llassert_always(!(mWorkFlags & WCF_WORKING)); - llassert_always(mWorkFlags & WCF_DELETE_REQUESTED); - llassert_always(!mMutex.isLocked()); - if (mRequestHandle != LLWorkerThread::nullHandle()) - { - LLWorkerThread::WorkRequest* workreq = (LLWorkerThread::WorkRequest*)mWorkerThread->getRequest(mRequestHandle); - if (!workreq) - { - LL_ERRS() << "LLWorkerClass destroyed with stale work handle" << LL_ENDL; - } - if (workreq->getStatus() != LLWorkerThread::STATUS_ABORTED && - workreq->getStatus() != LLWorkerThread::STATUS_COMPLETE) - { - LL_ERRS() << "LLWorkerClass destroyed with active worker! Worker Status: " << workreq->getStatus() << LL_ENDL; - } - } -} - -void LLWorkerClass::setWorkerThread(LLWorkerThread* workerthread) -{ - mMutex.lock(); - if (mRequestHandle != LLWorkerThread::nullHandle()) - { - LL_ERRS() << "LLWorkerClass attempt to change WorkerThread with active worker!" << LL_ENDL; - } - mWorkerThread = workerthread; - mMutex.unlock(); -} - -//---------------------------------------------------------------------------- - -//virtual -void LLWorkerClass::finishWork(S32 param, bool success) -{ -} - -//virtual -bool LLWorkerClass::deleteOK() -{ - return true; // default always OK -} - -//---------------------------------------------------------------------------- - -// Called from worker thread -void LLWorkerClass::setWorking(bool working) -{ - mMutex.lock(); - if (working) - { - llassert_always(!(mWorkFlags & WCF_WORKING)); - setFlags(WCF_WORKING); - } - else - { - llassert_always((mWorkFlags & WCF_WORKING)); - clearFlags(WCF_WORKING); - } - mMutex.unlock(); -} - -//---------------------------------------------------------------------------- - -bool LLWorkerClass::yield() -{ - LLThread::yield(); - mWorkerThread->checkPause(); - bool res; - mMutex.lock(); - res = (getFlags() & WCF_ABORT_REQUESTED) != 0; - mMutex.unlock(); - return res; -} - -//---------------------------------------------------------------------------- - -// calls startWork, adds doWork() to queue -void LLWorkerClass::addWork(S32 param) -{ - mMutex.lock(); - llassert_always(!(mWorkFlags & (WCF_WORKING|WCF_HAVE_WORK))); - if (mRequestHandle != LLWorkerThread::nullHandle()) - { - LL_ERRS() << "LLWorkerClass attempt to add work with active worker!" << LL_ENDL; - } -#if _DEBUG -// LL_INFOS() << "addWork: " << mWorkerClassName << " Param: " << param << LL_ENDL; -#endif - startWork(param); - clearFlags(WCF_WORK_FINISHED|WCF_WORK_ABORTED); - setFlags(WCF_HAVE_WORK); - mRequestHandle = mWorkerThread->addWorkRequest(this, param); - mMutex.unlock(); -} - -void LLWorkerClass::abortWork(bool autocomplete) -{ - mMutex.lock(); -#if _DEBUG -// LLWorkerThread::WorkRequest* workreq = mWorkerThread->getRequest(mRequestHandle); -// if (workreq) -// LL_INFOS() << "abortWork: " << mWorkerClassName << " Param: " << workreq->getParam() << LL_ENDL; -#endif - if (mRequestHandle != LLWorkerThread::nullHandle()) - { - mWorkerThread->abortRequest(mRequestHandle, autocomplete); - setFlags(WCF_ABORT_REQUESTED); - } - mMutex.unlock(); -} - -// if doWork is complete or aborted, call endWork() and return true -bool LLWorkerClass::checkWork(bool aborting) -{ - LLMutexLock lock(&mMutex); - bool complete = false, abort = false; - if (mRequestHandle != LLWorkerThread::nullHandle()) - { - LLWorkerThread::WorkRequest* workreq = (LLWorkerThread::WorkRequest*)mWorkerThread->getRequest(mRequestHandle); - if(!workreq) - { - if(mWorkerThread->isQuitting() || mWorkerThread->isStopped()) //the mWorkerThread is not running - { - mRequestHandle = LLWorkerThread::nullHandle(); - clearFlags(WCF_HAVE_WORK); - } - else - { - llassert_always(workreq); - } - return true ; - } - - LLQueuedThread::status_t status = workreq->getStatus(); - if (status == LLWorkerThread::STATUS_ABORTED) - { - complete = true; - abort = true; - } - else if (status == LLWorkerThread::STATUS_COMPLETE) - { - complete = true; - } - else - { - llassert_always(!aborting || (workreq->getFlags() & LLQueuedThread::FLAG_ABORT)); - } - if (complete) - { - llassert_always(!(getFlags(WCF_WORKING))); - endWork(workreq->getParam(), abort); - mWorkerThread->completeRequest(mRequestHandle); - mRequestHandle = LLWorkerThread::nullHandle(); - clearFlags(WCF_HAVE_WORK); - } - } - else - { - complete = true; - } - return complete; -} - -void LLWorkerClass::scheduleDelete() -{ - bool do_delete = false; - mMutex.lock(); - if (!(getFlags(WCF_DELETE_REQUESTED))) - { - setFlags(WCF_DELETE_REQUESTED); - do_delete = true; - } - mMutex.unlock(); - if (do_delete) - { - mWorkerThread->deleteWorker(this); - } -} - -//============================================================================ - +/**
+ * @file llworkerthread.cpp
+ *
+ * $LicenseInfo:firstyear=2004&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+#include "llworkerthread.h"
+#include "llstl.h"
+
+#if USE_FRAME_CALLBACK_MANAGER
+#include "llframecallbackmanager.h"
+#endif
+
+//============================================================================
+// Run on MAIN thread
+
+LLWorkerThread::LLWorkerThread(const std::string& name, bool threaded, bool should_pause) :
+ LLQueuedThread(name, threaded, should_pause)
+{
+ mDeleteMutex = new LLMutex();
+
+ if(!mLocalAPRFilePoolp)
+ {
+ mLocalAPRFilePoolp = new LLVolatileAPRPool() ;
+ }
+}
+
+LLWorkerThread::~LLWorkerThread()
+{
+ // Delete any workers in the delete queue (should be safe - had better be!)
+ if (!mDeleteList.empty())
+ {
+ LL_WARNS() << "Worker Thread: " << mName << " destroyed with " << mDeleteList.size()
+ << " entries in delete list." << LL_ENDL;
+ }
+
+ delete mDeleteMutex;
+
+ // ~LLQueuedThread() will be called here
+}
+
+//called only in destructor.
+void LLWorkerThread::clearDeleteList()
+{
+ // Delete any workers in the delete queue (should be safe - had better be!)
+ if (!mDeleteList.empty())
+ {
+ LL_WARNS() << "Worker Thread: " << mName << " destroyed with " << mDeleteList.size()
+ << " entries in delete list." << LL_ENDL;
+
+ mDeleteMutex->lock();
+ for (LLWorkerClass* worker : mDeleteList)
+ {
+ worker->mRequestHandle = LLWorkerThread::nullHandle();
+ worker->clearFlags(LLWorkerClass::WCF_HAVE_WORK);
+ worker->clearFlags(LLWorkerClass::WCF_WORKING);
+ delete worker;
+ }
+ mDeleteList.clear() ;
+ mDeleteMutex->unlock() ;
+ }
+}
+
+// virtual
+size_t LLWorkerThread::update(F32 max_time_ms)
+{
+ auto res = LLQueuedThread::update(max_time_ms);
+ // Delete scheduled workers
+ std::vector<LLWorkerClass*> delete_list;
+ std::vector<LLWorkerClass*> abort_list;
+ mDeleteMutex->lock();
+ for (delete_list_t::iterator iter = mDeleteList.begin();
+ iter != mDeleteList.end(); )
+ {
+ delete_list_t::iterator curiter = iter++;
+ LLWorkerClass* worker = *curiter;
+ if (worker->deleteOK())
+ {
+ if (worker->getFlags(LLWorkerClass::WCF_WORK_FINISHED))
+ {
+ worker->setFlags(LLWorkerClass::WCF_DELETE_REQUESTED);
+ delete_list.push_back(worker);
+ mDeleteList.erase(curiter);
+ }
+ else if (!worker->getFlags(LLWorkerClass::WCF_ABORT_REQUESTED))
+ {
+ abort_list.push_back(worker);
+ }
+ }
+ }
+ mDeleteMutex->unlock();
+ // abort and delete after releasing mutex
+ for (LLWorkerClass* worker : abort_list)
+ {
+ worker->abortWork(false);
+ }
+ for (LLWorkerClass* worker : delete_list)
+ {
+ if (worker->mRequestHandle)
+ {
+ // Finished but not completed
+ completeRequest(worker->mRequestHandle);
+ worker->mRequestHandle = LLWorkerThread::nullHandle();
+ worker->clearFlags(LLWorkerClass::WCF_HAVE_WORK);
+ }
+ delete worker;
+ }
+ // delete and aborted entries mean there's still work to do
+ res += delete_list.size() + abort_list.size();
+ return res;
+}
+
+//----------------------------------------------------------------------------
+
+LLWorkerThread::handle_t LLWorkerThread::addWorkRequest(LLWorkerClass* workerclass, S32 param)
+{
+ handle_t handle = generateHandle();
+
+ WorkRequest* req = new WorkRequest(handle, workerclass, param);
+
+ bool res = addRequest(req);
+ if (!res)
+ {
+ LL_ERRS() << "add called after LLWorkerThread::cleanupClass()" << LL_ENDL;
+ req->deleteRequest();
+ handle = nullHandle();
+ }
+
+ return handle;
+}
+
+void LLWorkerThread::deleteWorker(LLWorkerClass* workerclass)
+{
+ mDeleteMutex->lock();
+ mDeleteList.push_back(workerclass);
+ mDeleteMutex->unlock();
+}
+
+//============================================================================
+// Runs on its OWN thread
+
+LLWorkerThread::WorkRequest::WorkRequest(handle_t handle, LLWorkerClass* workerclass, S32 param) :
+ LLQueuedThread::QueuedRequest(handle),
+ mWorkerClass(workerclass),
+ mParam(param)
+{
+}
+
+LLWorkerThread::WorkRequest::~WorkRequest()
+{
+}
+
+// virtual (required for access by LLWorkerThread)
+void LLWorkerThread::WorkRequest::deleteRequest()
+{
+ LLQueuedThread::QueuedRequest::deleteRequest();
+}
+
+// virtual
+bool LLWorkerThread::WorkRequest::processRequest()
+{
+ LL_PROFILE_ZONE_SCOPED;
+ LLWorkerClass* workerclass = getWorkerClass();
+ workerclass->setWorking(true);
+ bool complete = workerclass->doWork(getParam());
+ workerclass->setWorking(false);
+ return complete;
+}
+
+// virtual
+void LLWorkerThread::WorkRequest::finishRequest(bool completed)
+{
+ LL_PROFILE_ZONE_SCOPED;
+ LLWorkerClass* workerclass = getWorkerClass();
+ workerclass->finishWork(getParam(), completed);
+ U32 flags = LLWorkerClass::WCF_WORK_FINISHED | (completed ? 0 : LLWorkerClass::WCF_WORK_ABORTED);
+ workerclass->setFlags(flags);
+}
+
+//============================================================================
+// LLWorkerClass:: operates in main thread
+
+LLWorkerClass::LLWorkerClass(LLWorkerThread* workerthread, const std::string& name)
+ : mWorkerThread(workerthread),
+ mWorkerClassName(name),
+ mRequestHandle(LLWorkerThread::nullHandle()),
+ mMutex(),
+ mWorkFlags(0)
+{
+ if (!mWorkerThread)
+ {
+ LL_ERRS() << "LLWorkerClass() called with NULL workerthread: " << name << LL_ENDL;
+ }
+}
+
+LLWorkerClass::~LLWorkerClass()
+{
+ llassert_always(!(mWorkFlags & WCF_WORKING));
+ llassert_always(mWorkFlags & WCF_DELETE_REQUESTED);
+ llassert_always(!mMutex.isLocked());
+ if (mRequestHandle != LLWorkerThread::nullHandle())
+ {
+ LLWorkerThread::WorkRequest* workreq = (LLWorkerThread::WorkRequest*)mWorkerThread->getRequest(mRequestHandle);
+ if (!workreq)
+ {
+ LL_ERRS() << "LLWorkerClass destroyed with stale work handle" << LL_ENDL;
+ }
+ if (workreq->getStatus() != LLWorkerThread::STATUS_ABORTED &&
+ workreq->getStatus() != LLWorkerThread::STATUS_COMPLETE)
+ {
+ LL_ERRS() << "LLWorkerClass destroyed with active worker! Worker Status: " << workreq->getStatus() << LL_ENDL;
+ }
+ }
+}
+
+void LLWorkerClass::setWorkerThread(LLWorkerThread* workerthread)
+{
+ mMutex.lock();
+ if (mRequestHandle != LLWorkerThread::nullHandle())
+ {
+ LL_ERRS() << "LLWorkerClass attempt to change WorkerThread with active worker!" << LL_ENDL;
+ }
+ mWorkerThread = workerthread;
+ mMutex.unlock();
+}
+
+//----------------------------------------------------------------------------
+
+//virtual
+void LLWorkerClass::finishWork(S32 param, bool success)
+{
+}
+
+//virtual
+bool LLWorkerClass::deleteOK()
+{
+ return true; // default always OK
+}
+
+//----------------------------------------------------------------------------
+
+// Called from worker thread
+void LLWorkerClass::setWorking(bool working)
+{
+ mMutex.lock();
+ if (working)
+ {
+ llassert_always(!(mWorkFlags & WCF_WORKING));
+ setFlags(WCF_WORKING);
+ }
+ else
+ {
+ llassert_always((mWorkFlags & WCF_WORKING));
+ clearFlags(WCF_WORKING);
+ }
+ mMutex.unlock();
+}
+
+//----------------------------------------------------------------------------
+
+bool LLWorkerClass::yield()
+{
+ LLThread::yield();
+ mWorkerThread->checkPause();
+ bool res;
+ mMutex.lock();
+ res = (getFlags() & WCF_ABORT_REQUESTED) != 0;
+ mMutex.unlock();
+ return res;
+}
+
+//----------------------------------------------------------------------------
+
+// calls startWork, adds doWork() to queue
+void LLWorkerClass::addWork(S32 param)
+{
+ mMutex.lock();
+ llassert_always(!(mWorkFlags & (WCF_WORKING|WCF_HAVE_WORK)));
+ if (mRequestHandle != LLWorkerThread::nullHandle())
+ {
+ LL_ERRS() << "LLWorkerClass attempt to add work with active worker!" << LL_ENDL;
+ }
+#if _DEBUG
+// LL_INFOS() << "addWork: " << mWorkerClassName << " Param: " << param << LL_ENDL;
+#endif
+ startWork(param);
+ clearFlags(WCF_WORK_FINISHED|WCF_WORK_ABORTED);
+ setFlags(WCF_HAVE_WORK);
+ mRequestHandle = mWorkerThread->addWorkRequest(this, param);
+ mMutex.unlock();
+}
+
+void LLWorkerClass::abortWork(bool autocomplete)
+{
+ mMutex.lock();
+#if _DEBUG
+// LLWorkerThread::WorkRequest* workreq = mWorkerThread->getRequest(mRequestHandle);
+// if (workreq)
+// LL_INFOS() << "abortWork: " << mWorkerClassName << " Param: " << workreq->getParam() << LL_ENDL;
+#endif
+ if (mRequestHandle != LLWorkerThread::nullHandle())
+ {
+ mWorkerThread->abortRequest(mRequestHandle, autocomplete);
+ setFlags(WCF_ABORT_REQUESTED);
+ }
+ mMutex.unlock();
+}
+
+// if doWork is complete or aborted, call endWork() and return true
+bool LLWorkerClass::checkWork(bool aborting)
+{
+ LLMutexLock lock(&mMutex);
+ bool complete = false, abort = false;
+ if (mRequestHandle != LLWorkerThread::nullHandle())
+ {
+ LLWorkerThread::WorkRequest* workreq = (LLWorkerThread::WorkRequest*)mWorkerThread->getRequest(mRequestHandle);
+ if(!workreq)
+ {
+ if(mWorkerThread->isQuitting() || mWorkerThread->isStopped()) //the mWorkerThread is not running
+ {
+ mRequestHandle = LLWorkerThread::nullHandle();
+ clearFlags(WCF_HAVE_WORK);
+ }
+ else
+ {
+ llassert_always(workreq);
+ }
+ return true ;
+ }
+
+ LLQueuedThread::status_t status = workreq->getStatus();
+ if (status == LLWorkerThread::STATUS_ABORTED)
+ {
+ complete = true;
+ abort = true;
+ }
+ else if (status == LLWorkerThread::STATUS_COMPLETE)
+ {
+ complete = true;
+ }
+ else
+ {
+ llassert_always(!aborting || (workreq->getFlags() & LLQueuedThread::FLAG_ABORT));
+ }
+ if (complete)
+ {
+ llassert_always(!(getFlags(WCF_WORKING)));
+ endWork(workreq->getParam(), abort);
+ mWorkerThread->completeRequest(mRequestHandle);
+ mRequestHandle = LLWorkerThread::nullHandle();
+ clearFlags(WCF_HAVE_WORK);
+ }
+ }
+ else
+ {
+ complete = true;
+ }
+ return complete;
+}
+
+void LLWorkerClass::scheduleDelete()
+{
+ bool do_delete = false;
+ mMutex.lock();
+ if (!(getFlags(WCF_DELETE_REQUESTED)))
+ {
+ setFlags(WCF_DELETE_REQUESTED);
+ do_delete = true;
+ }
+ mMutex.unlock();
+ if (do_delete)
+ {
+ mWorkerThread->deleteWorker(this);
+ }
+}
+
+//============================================================================
+
diff --git a/indra/llcommon/llworkerthread.h b/indra/llcommon/llworkerthread.h index a0fe30404d..803fff78d9 100644 --- a/indra/llcommon/llworkerthread.h +++ b/indra/llcommon/llworkerthread.h @@ -1,201 +1,201 @@ -/** - * @file llworkerthread.h - * - * $LicenseInfo:firstyear=2004&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#ifndef LL_LLWORKERTHREAD_H -#define LL_LLWORKERTHREAD_H - -#include <list> -#include <map> -#include <queue> -#include <set> -#include <string> - -#include "llqueuedthread.h" -#include "llatomic.h" -#include "llmutex.h" - -#define USE_FRAME_CALLBACK_MANAGER 0 - -//============================================================================ - -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 LL_COMMON_API LLWorkerThread : public LLQueuedThread -{ - friend class LLWorkerClass; -public: - class WorkRequest : public LLQueuedThread::QueuedRequest - { - protected: - virtual ~WorkRequest(); // use deleteRequest() - - public: - WorkRequest(handle_t handle, LLWorkerClass* workerclass, S32 param); - - S32 getParam() - { - return mParam; - } - LLWorkerClass* getWorkerClass() - { - return mWorkerClass; - } - - /*virtual*/ bool processRequest(); - /*virtual*/ void finishRequest(bool completed); - /*virtual*/ void deleteRequest(); - - private: - LLWorkerClass* mWorkerClass; - S32 mParam; - }; - -protected: - void clearDeleteList() ; - -private: - typedef std::list<LLWorkerClass*> delete_list_t; - delete_list_t mDeleteList; - LLMutex* mDeleteMutex; - -public: - LLWorkerThread(const std::string& name, bool threaded = true, bool should_pause = false); - ~LLWorkerThread(); - - /*virtual*/ size_t update(F32 max_time_ms); - - handle_t addWorkRequest(LLWorkerClass* workerclass, S32 param); - - S32 getNumDeletes() { return (S32)mDeleteList.size(); } // debug - -private: - void deleteWorker(LLWorkerClass* workerclass); // schedule for deletion - -}; - -//============================================================================ - -// This is a base class which any class with worker functions should derive from. -// Example Usage: -// LLMyWorkerClass* foo = new LLMyWorkerClass(); -// foo->fetchData(); // calls addWork() -// while(1) // main loop -// { -// if (foo->hasData()) // calls checkWork() -// foo->processData(); -// } -// -// WorkerClasses only have one set of work functions. If they need to do multiple -// background tasks, use 'param' to switch amnong them. -// Only one background task can be active at a time (per instance). -// i.e. don't call addWork() if haveWork() returns true - -class LL_COMMON_API LLWorkerClass -{ - friend class LLWorkerThread; - friend class LLWorkerThread::WorkRequest; - -public: - typedef LLWorkerThread::handle_t handle_t; - enum FLAGS - { - WCF_HAVE_WORK = 0x01, - WCF_WORKING = 0x02, - WCF_WORK_FINISHED = 0x10, - WCF_WORK_ABORTED = 0x20, - WCF_DELETE_REQUESTED = 0x40, - WCF_ABORT_REQUESTED = 0x80 - }; - -public: - LLWorkerClass(LLWorkerThread* workerthread, const std::string& name); - virtual ~LLWorkerClass(); - - // pure virtual, called from WORKER THREAD, returns true if done - virtual bool doWork(S32 param)=0; // Called from WorkRequest::processRequest() - // virtual, called from finishRequest() after completed or aborted - virtual void finishWork(S32 param, bool completed); // called from finishRequest() (WORK THREAD) - // virtual, returns true if safe to delete the worker - virtual bool deleteOK(); // called from update() (WORK THREAD) - - // schedlueDelete(): schedules deletion once aborted or completed - void scheduleDelete(); - - bool haveWork() { return getFlags(WCF_HAVE_WORK); } // may still be true if aborted - bool isWorking() { return getFlags(WCF_WORKING); } - bool wasAborted() { return getFlags(WCF_ABORT_REQUESTED); } - - const std::string& getName() const { return mWorkerClassName; } - -protected: - // called from WORKER THREAD - void setWorking(bool working); - - // Call from doWork only to avoid eating up cpu time. - // Returns true if work has been aborted - // yields the current thread and calls mWorkerThread->checkPause() - bool yield(); - - void setWorkerThread(LLWorkerThread* workerthread); - - // addWork(): calls startWork, adds doWork() to queue - void addWork(S32 param); - - // abortWork(): requests that work be aborted - void abortWork(bool autocomplete); - - // checkWork(): if doWork is complete or aborted, call endWork() and return true - bool checkWork(bool aborting = false); - -private: - void setFlags(U32 flags) { mWorkFlags = mWorkFlags | flags; } - void clearFlags(U32 flags) { mWorkFlags = mWorkFlags & ~flags; } - U32 getFlags() { return mWorkFlags; } -public: - bool getFlags(U32 flags) { return (mWorkFlags & flags) != 0; } - -private: - // pure virtuals - virtual void startWork(S32 param)=0; // called from addWork() (MAIN THREAD) - virtual void endWork(S32 param, bool aborted)=0; // called from doWork() (MAIN THREAD) - -protected: - LLWorkerThread* mWorkerThread; - std::string mWorkerClassName; - handle_t mRequestHandle; - -private: - LLMutex mMutex; - LLAtomicU32 mWorkFlags; -}; - -//============================================================================ - - -#endif // LL_LLWORKERTHREAD_H +/**
+ * @file llworkerthread.h
+ *
+ * $LicenseInfo:firstyear=2004&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLWORKERTHREAD_H
+#define LL_LLWORKERTHREAD_H
+
+#include <list>
+#include <map>
+#include <queue>
+#include <set>
+#include <string>
+
+#include "llqueuedthread.h"
+#include "llatomic.h"
+#include "llmutex.h"
+
+#define USE_FRAME_CALLBACK_MANAGER 0
+
+//============================================================================
+
+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 LL_COMMON_API LLWorkerThread : public LLQueuedThread
+{
+ friend class LLWorkerClass;
+public:
+ class WorkRequest : public LLQueuedThread::QueuedRequest
+ {
+ protected:
+ virtual ~WorkRequest(); // use deleteRequest()
+
+ public:
+ WorkRequest(handle_t handle, LLWorkerClass* workerclass, S32 param);
+
+ S32 getParam()
+ {
+ return mParam;
+ }
+ LLWorkerClass* getWorkerClass()
+ {
+ return mWorkerClass;
+ }
+
+ /*virtual*/ bool processRequest();
+ /*virtual*/ void finishRequest(bool completed);
+ /*virtual*/ void deleteRequest();
+
+ private:
+ LLWorkerClass* mWorkerClass;
+ S32 mParam;
+ };
+
+protected:
+ void clearDeleteList() ;
+
+private:
+ typedef std::list<LLWorkerClass*> delete_list_t;
+ delete_list_t mDeleteList;
+ LLMutex* mDeleteMutex;
+
+public:
+ LLWorkerThread(const std::string& name, bool threaded = true, bool should_pause = false);
+ ~LLWorkerThread();
+
+ /*virtual*/ size_t update(F32 max_time_ms);
+
+ handle_t addWorkRequest(LLWorkerClass* workerclass, S32 param);
+
+ S32 getNumDeletes() { return (S32)mDeleteList.size(); } // debug
+
+private:
+ void deleteWorker(LLWorkerClass* workerclass); // schedule for deletion
+
+};
+
+//============================================================================
+
+// This is a base class which any class with worker functions should derive from.
+// Example Usage:
+// LLMyWorkerClass* foo = new LLMyWorkerClass();
+// foo->fetchData(); // calls addWork()
+// while(1) // main loop
+// {
+// if (foo->hasData()) // calls checkWork()
+// foo->processData();
+// }
+//
+// WorkerClasses only have one set of work functions. If they need to do multiple
+// background tasks, use 'param' to switch amnong them.
+// Only one background task can be active at a time (per instance).
+// i.e. don't call addWork() if haveWork() returns true
+
+class LL_COMMON_API LLWorkerClass
+{
+ friend class LLWorkerThread;
+ friend class LLWorkerThread::WorkRequest;
+
+public:
+ typedef LLWorkerThread::handle_t handle_t;
+ enum FLAGS
+ {
+ WCF_HAVE_WORK = 0x01,
+ WCF_WORKING = 0x02,
+ WCF_WORK_FINISHED = 0x10,
+ WCF_WORK_ABORTED = 0x20,
+ WCF_DELETE_REQUESTED = 0x40,
+ WCF_ABORT_REQUESTED = 0x80
+ };
+
+public:
+ LLWorkerClass(LLWorkerThread* workerthread, const std::string& name);
+ virtual ~LLWorkerClass();
+
+ // pure virtual, called from WORKER THREAD, returns true if done
+ virtual bool doWork(S32 param)=0; // Called from WorkRequest::processRequest()
+ // virtual, called from finishRequest() after completed or aborted
+ virtual void finishWork(S32 param, bool completed); // called from finishRequest() (WORK THREAD)
+ // virtual, returns true if safe to delete the worker
+ virtual bool deleteOK(); // called from update() (WORK THREAD)
+
+ // schedlueDelete(): schedules deletion once aborted or completed
+ void scheduleDelete();
+
+ bool haveWork() { return getFlags(WCF_HAVE_WORK); } // may still be true if aborted
+ bool isWorking() { return getFlags(WCF_WORKING); }
+ bool wasAborted() { return getFlags(WCF_ABORT_REQUESTED); }
+
+ const std::string& getName() const { return mWorkerClassName; }
+
+protected:
+ // called from WORKER THREAD
+ void setWorking(bool working);
+
+ // Call from doWork only to avoid eating up cpu time.
+ // Returns true if work has been aborted
+ // yields the current thread and calls mWorkerThread->checkPause()
+ bool yield();
+
+ void setWorkerThread(LLWorkerThread* workerthread);
+
+ // addWork(): calls startWork, adds doWork() to queue
+ void addWork(S32 param);
+
+ // abortWork(): requests that work be aborted
+ void abortWork(bool autocomplete);
+
+ // checkWork(): if doWork is complete or aborted, call endWork() and return true
+ bool checkWork(bool aborting = false);
+
+private:
+ void setFlags(U32 flags) { mWorkFlags = mWorkFlags | flags; }
+ void clearFlags(U32 flags) { mWorkFlags = mWorkFlags & ~flags; }
+ U32 getFlags() { return mWorkFlags; }
+public:
+ bool getFlags(U32 flags) { return (mWorkFlags & flags) != 0; }
+
+private:
+ // pure virtuals
+ virtual void startWork(S32 param)=0; // called from addWork() (MAIN THREAD)
+ virtual void endWork(S32 param, bool aborted)=0; // called from doWork() (MAIN THREAD)
+
+protected:
+ LLWorkerThread* mWorkerThread;
+ std::string mWorkerClassName;
+ handle_t mRequestHandle;
+
+private:
+ LLMutex mMutex;
+ LLAtomicU32 mWorkFlags;
+};
+
+//============================================================================
+
+
+#endif // LL_LLWORKERTHREAD_H
diff --git a/indra/llcommon/lockstatic.h b/indra/llcommon/lockstatic.h index 96c53c6473..7cc9b7eec0 100644 --- a/indra/llcommon/lockstatic.h +++ b/indra/llcommon/lockstatic.h @@ -4,7 +4,7 @@ * @date 2019-12-03 * @brief LockStatic class provides mutex-guarded access to the specified * static data. - * + * * $LicenseInfo:firstyear=2019&license=viewerlgpl$ * Copyright (c) 2019, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/mutex.h b/indra/llcommon/mutex.h index 90d0942270..82e46315e2 100644 --- a/indra/llcommon/mutex.h +++ b/indra/llcommon/mutex.h @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2019-12-03 * @brief Wrap <mutex> in odious boilerplate - * + * * $LicenseInfo:firstyear=2019&license=viewerlgpl$ * Copyright (c) 2019, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/stdtypes.h b/indra/llcommon/stdtypes.h index 3aba9dda00..28e50b3d21 100644 --- a/indra/llcommon/stdtypes.h +++ b/indra/llcommon/stdtypes.h @@ -1,25 +1,25 @@ -/** +/** * @file stdtypes.h * @brief Basic type declarations for cross platform compatibility. * * $LicenseInfo:firstyear=2000&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -32,12 +32,12 @@ #include <limits> #include <type_traits> -typedef signed char S8; -typedef unsigned char U8; -typedef signed short S16; -typedef unsigned short U16; -typedef signed int S32; -typedef unsigned int U32; +typedef signed char S8; +typedef unsigned char U8; +typedef signed short S16; +typedef unsigned short U16; +typedef signed int S32; +typedef unsigned int U32; // to express an index that might go negative // (ssize_t is provided by SOME compilers, don't collide) @@ -52,9 +52,9 @@ typedef typename std::make_signed<std::size_t>::type llssize; // The version of clang available with VS 2019 also defines wchar_t as __wchar_t // which is also 16 bits. // In any case, llwchar should be a UTF-32 type. -typedef U32 llwchar; +typedef U32 llwchar; #else -typedef wchar_t llwchar; +typedef wchar_t llwchar; // What we'd actually want is a simple module-scope 'if constexpr' to test // std::is_same<wchar_t, llwchar>::value and use that to define, or not // define, string conversion specializations. Since we don't have that, we'll @@ -63,63 +63,63 @@ typedef wchar_t llwchar; #endif #if LL_WINDOWS -typedef signed __int64 S64; +typedef signed __int64 S64; // probably should be 'hyper' or similiar -#define S64L(a) (a) -typedef unsigned __int64 U64; -#define U64L(a) (a) +#define S64L(a) (a) +typedef unsigned __int64 U64; +#define U64L(a) (a) #else -typedef long long int S64; -typedef long long unsigned int U64; +typedef long long int S64; +typedef long long unsigned int U64; #if LL_DARWIN || LL_LINUX -#define S64L(a) (a##LL) -#define U64L(a) (a##ULL) +#define S64L(a) (a##LL) +#define U64L(a) (a##ULL) #endif #endif -typedef float F32; -typedef double F64; +typedef float F32; +typedef double F64; -typedef S32 BOOL; -typedef U8 KEY; -typedef U32 MASK; -typedef U32 TPACKETID; +typedef S32 BOOL; +typedef U8 KEY; +typedef U32 MASK; +typedef U32 TPACKETID; // Use #define instead of consts to avoid conversion headaches -#define S8_MAX (SCHAR_MAX) -#define U8_MAX (UCHAR_MAX) -#define S16_MAX (SHRT_MAX) -#define U16_MAX (USHRT_MAX) -#define S32_MAX (INT_MAX) -#define U32_MAX (UINT_MAX) -#define F32_MAX (FLT_MAX) -#define F64_MAX (DBL_MAX) - -#define S8_MIN (SCHAR_MIN) -#define U8_MIN (0) -#define S16_MIN (SHRT_MIN) -#define U16_MIN (0) -#define S32_MIN (INT_MIN) -#define U32_MIN (0) -#define F32_MIN (FLT_MIN) -#define F64_MIN (DBL_MIN) +#define S8_MAX (SCHAR_MAX) +#define U8_MAX (UCHAR_MAX) +#define S16_MAX (SHRT_MAX) +#define U16_MAX (USHRT_MAX) +#define S32_MAX (INT_MAX) +#define U32_MAX (UINT_MAX) +#define F32_MAX (FLT_MAX) +#define F64_MAX (DBL_MAX) + +#define S8_MIN (SCHAR_MIN) +#define U8_MIN (0) +#define S16_MIN (SHRT_MIN) +#define U16_MIN (0) +#define S32_MIN (INT_MIN) +#define U32_MIN (0) +#define F32_MIN (FLT_MIN) +#define F64_MIN (DBL_MIN) #ifndef TRUE -#define TRUE (1) +#define TRUE (1) #endif #ifndef FALSE -#define FALSE (0) +#define FALSE (0) #endif #ifndef NULL -#define NULL (0) +#define NULL (0) #endif typedef U8 LLPCode; -#define LL_ARRAY_SIZE( _kArray ) ( sizeof( (_kArray) ) / sizeof( _kArray[0] ) ) +#define LL_ARRAY_SIZE( _kArray ) ( sizeof( (_kArray) ) / sizeof( _kArray[0] ) ) #if LL_LINUX && __GNUC__ <= 2 typedef int intptr_t; diff --git a/indra/llcommon/string_table.h b/indra/llcommon/string_table.h index fe6416fb50..bb525f884b 100644 --- a/indra/llcommon/string_table.h +++ b/indra/llcommon/string_table.h @@ -1,25 +1,25 @@ -/** +/** * @file string_table.h * @brief Legacy wrapper header. * * $LicenseInfo:firstyear=2000&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llcommon/stringize.h b/indra/llcommon/stringize.h index c0b13135f9..536a18abc1 100644 --- a/indra/llcommon/stringize.h +++ b/indra/llcommon/stringize.h @@ -3,25 +3,25 @@ * @author Nat Goodspeed * @date 2008-12-17 * @brief stringize(item) template function and STRINGIZE(expression) macro - * + * * $LicenseInfo:firstyear=2008&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llcommon/tests/StringVec.h b/indra/llcommon/tests/StringVec.h index a380b00a05..4311cba992 100644 --- a/indra/llcommon/tests/StringVec.h +++ b/indra/llcommon/tests/StringVec.h @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2012-02-24 * @brief Extend TUT ensure_equals() to handle std::vector<std::string> - * + * * $LicenseInfo:firstyear=2012&license=viewerlgpl$ * Copyright (c) 2012, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/tests/apply_test.cpp b/indra/llcommon/tests/apply_test.cpp index 56b497e0c8..6d213c2db9 100644 --- a/indra/llcommon/tests/apply_test.cpp +++ b/indra/llcommon/tests/apply_test.cpp @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2022-12-19 * @brief Test for apply. - * + * * $LicenseInfo:firstyear=2022&license=viewerlgpl$ * Copyright (c) 2022, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/tests/bitpack_test.cpp b/indra/llcommon/tests/bitpack_test.cpp index 9bfd567068..2810f926af 100644 --- a/indra/llcommon/tests/bitpack_test.cpp +++ b/indra/llcommon/tests/bitpack_test.cpp @@ -7,25 +7,25 @@ * $LicenseInfo:firstyear=2007&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ - + #include "linden_common.h" #include "../llbitpack.h" @@ -35,84 +35,84 @@ namespace tut { - struct bit_pack - { - }; - typedef test_group<bit_pack> bit_pack_t; - typedef bit_pack_t::object bit_pack_object_t; - tut::bit_pack_t tut_bit_pack("LLBitPack"); - - // pack -> unpack - template<> template<> - void bit_pack_object_t::test<1>() - { - U8 packbuffer[255]; - U8 unpackbuffer[255]; - int pack_bufsize = 0; - int unpack_bufsize = 0; - - LLBitPack bitpack(packbuffer, 255); - - char str[] = "SecondLife is a 3D virtual world"; - int len = sizeof(str); - pack_bufsize = bitpack.bitPack((U8*) str, len*8); - pack_bufsize = bitpack.flushBitPack(); - - LLBitPack bitunpack(packbuffer, pack_bufsize*8); - unpack_bufsize = bitunpack.bitUnpack(unpackbuffer, len*8); - ensure("bitPack: unpack size should be same as string size prior to pack", len == unpack_bufsize); - ensure_memory_matches("str->bitPack->bitUnpack should be equal to string", str, len, unpackbuffer, unpack_bufsize); - } - - // pack large, unpack in individual bytes - template<> template<> - void bit_pack_object_t::test<2>() - { - U8 packbuffer[255]; - U8 unpackbuffer[255]; - int pack_bufsize = 0; - - LLBitPack bitpack(packbuffer, 255); - - char str[] = "SecondLife"; - int len = sizeof(str); - pack_bufsize = bitpack.bitPack((U8*) str, len*8); - pack_bufsize = bitpack.flushBitPack(); - - LLBitPack bitunpack(packbuffer, pack_bufsize*8); - bitunpack.bitUnpack(&unpackbuffer[0], 8); - ensure("bitPack: individual unpack: 0", unpackbuffer[0] == (U8) str[0]); - bitunpack.bitUnpack(&unpackbuffer[0], 8); - ensure("bitPack: individual unpack: 1", unpackbuffer[0] == (U8) str[1]); - bitunpack.bitUnpack(&unpackbuffer[0], 8); - ensure("bitPack: individual unpack: 2", unpackbuffer[0] == (U8) str[2]); - bitunpack.bitUnpack(&unpackbuffer[0], 8); - ensure("bitPack: individual unpack: 3", unpackbuffer[0] == (U8) str[3]); - bitunpack.bitUnpack(&unpackbuffer[0], 8); - ensure("bitPack: individual unpack: 4", unpackbuffer[0] == (U8) str[4]); - bitunpack.bitUnpack(&unpackbuffer[0], 8); - ensure("bitPack: individual unpack: 5", unpackbuffer[0] == (U8) str[5]); - bitunpack.bitUnpack(unpackbuffer, 8*4); // Life - ensure_memory_matches("bitPack: 4 bytes unpack:", unpackbuffer, 4, str+6, 4); - } - - // U32 packing - template<> template<> - void bit_pack_object_t::test<3>() - { - U8 packbuffer[255]; - int pack_bufsize = 0; - - LLBitPack bitpack(packbuffer, 255); - U32 num = 0x41fab67a; - pack_bufsize = bitpack.bitPack((U8*)&num, 8*sizeof(U32)); - pack_bufsize = bitpack.flushBitPack(); - - LLBitPack bitunpack(packbuffer, pack_bufsize*8); - U32 res = 0; - // since packing and unpacking is done on same machine in the unit test run, - // endianness should not matter - bitunpack.bitUnpack((U8*) &res, sizeof(res)*8); - ensure("U32->bitPack->bitUnpack->U32 should be equal", num == res); - } + struct bit_pack + { + }; + typedef test_group<bit_pack> bit_pack_t; + typedef bit_pack_t::object bit_pack_object_t; + tut::bit_pack_t tut_bit_pack("LLBitPack"); + + // pack -> unpack + template<> template<> + void bit_pack_object_t::test<1>() + { + U8 packbuffer[255]; + U8 unpackbuffer[255]; + int pack_bufsize = 0; + int unpack_bufsize = 0; + + LLBitPack bitpack(packbuffer, 255); + + char str[] = "SecondLife is a 3D virtual world"; + int len = sizeof(str); + pack_bufsize = bitpack.bitPack((U8*) str, len*8); + pack_bufsize = bitpack.flushBitPack(); + + LLBitPack bitunpack(packbuffer, pack_bufsize*8); + unpack_bufsize = bitunpack.bitUnpack(unpackbuffer, len*8); + ensure("bitPack: unpack size should be same as string size prior to pack", len == unpack_bufsize); + ensure_memory_matches("str->bitPack->bitUnpack should be equal to string", str, len, unpackbuffer, unpack_bufsize); + } + + // pack large, unpack in individual bytes + template<> template<> + void bit_pack_object_t::test<2>() + { + U8 packbuffer[255]; + U8 unpackbuffer[255]; + int pack_bufsize = 0; + + LLBitPack bitpack(packbuffer, 255); + + char str[] = "SecondLife"; + int len = sizeof(str); + pack_bufsize = bitpack.bitPack((U8*) str, len*8); + pack_bufsize = bitpack.flushBitPack(); + + LLBitPack bitunpack(packbuffer, pack_bufsize*8); + bitunpack.bitUnpack(&unpackbuffer[0], 8); + ensure("bitPack: individual unpack: 0", unpackbuffer[0] == (U8) str[0]); + bitunpack.bitUnpack(&unpackbuffer[0], 8); + ensure("bitPack: individual unpack: 1", unpackbuffer[0] == (U8) str[1]); + bitunpack.bitUnpack(&unpackbuffer[0], 8); + ensure("bitPack: individual unpack: 2", unpackbuffer[0] == (U8) str[2]); + bitunpack.bitUnpack(&unpackbuffer[0], 8); + ensure("bitPack: individual unpack: 3", unpackbuffer[0] == (U8) str[3]); + bitunpack.bitUnpack(&unpackbuffer[0], 8); + ensure("bitPack: individual unpack: 4", unpackbuffer[0] == (U8) str[4]); + bitunpack.bitUnpack(&unpackbuffer[0], 8); + ensure("bitPack: individual unpack: 5", unpackbuffer[0] == (U8) str[5]); + bitunpack.bitUnpack(unpackbuffer, 8*4); // Life + ensure_memory_matches("bitPack: 4 bytes unpack:", unpackbuffer, 4, str+6, 4); + } + + // U32 packing + template<> template<> + void bit_pack_object_t::test<3>() + { + U8 packbuffer[255]; + int pack_bufsize = 0; + + LLBitPack bitpack(packbuffer, 255); + U32 num = 0x41fab67a; + pack_bufsize = bitpack.bitPack((U8*)&num, 8*sizeof(U32)); + pack_bufsize = bitpack.flushBitPack(); + + LLBitPack bitunpack(packbuffer, pack_bufsize*8); + U32 res = 0; + // since packing and unpacking is done on same machine in the unit test run, + // endianness should not matter + bitunpack.bitUnpack((U8*) &res, sizeof(res)*8); + ensure("U32->bitPack->bitUnpack->U32 should be equal", num == res); + } } diff --git a/indra/llcommon/tests/classic_callback_test.cpp b/indra/llcommon/tests/classic_callback_test.cpp index c060775c24..fe79179ce6 100644 --- a/indra/llcommon/tests/classic_callback_test.cpp +++ b/indra/llcommon/tests/classic_callback_test.cpp @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2021-09-22 * @brief Test ClassicCallback and HeapClassicCallback. - * + * * $LicenseInfo:firstyear=2021&license=viewerlgpl$ * Copyright (c) 2021, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/tests/commonmisc_test.cpp b/indra/llcommon/tests/commonmisc_test.cpp index 4b3e07fa75..3deb864c0c 100644 --- a/indra/llcommon/tests/commonmisc_test.cpp +++ b/indra/llcommon/tests/commonmisc_test.cpp @@ -1,4 +1,4 @@ -/** +/** * @file common.cpp * @author Phoenix * @date 2005-10-12 @@ -7,27 +7,27 @@ * $LicenseInfo:firstyear=2005&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ -/** - * +/** + * * THOROUGH_DESCRIPTION of common.cpp * */ @@ -54,388 +54,388 @@ namespace tut { - struct sd_data - { - }; - typedef test_group<sd_data> sd_test; - typedef sd_test::object sd_object; - tut::sd_test sd("LLSD"); - - template<> template<> - void sd_object::test<1>() - { - std::ostringstream resp; - resp << "{'connect':true, 'position':[r128,r128,r128], 'look_at':[r0,r1,r0], 'agent_access':'M', 'region_x':i8192, 'region_y':i8192}"; - std::string str = resp.str(); - LLMemoryStream mstr((U8*)str.c_str(), str.size()); - LLSD response; - S32 count = LLSDSerialize::fromNotation(response, mstr, str.size()); - ensure("stream parsed", response.isDefined()); - ensure_equals("stream parse count", count, 13); - ensure_equals("sd type", response.type(), LLSD::TypeMap); - ensure_equals("map element count", response.size(), 6); - ensure_equals("value connect", response["connect"].asBoolean(), true); - ensure_equals("value region_x", response["region_x"].asInteger(),8192); - ensure_equals("value region_y", response["region_y"].asInteger(),8192); - } - - template<> template<> - void sd_object::test<2>() - { - const std::string decoded("random"); - //const std::string encoded("cmFuZG9t\n"); - const std::string streamed("b(6)\"random\""); - typedef std::vector<U8> buf_t; - buf_t buf; - std::copy( - decoded.begin(), - decoded.end(), - std::back_insert_iterator<buf_t>(buf)); - LLSD sd; - sd = buf; - std::stringstream str; - S32 count = LLSDSerialize::toNotation(sd, str); - ensure_equals("output count", count, 1); - std::string actual(str.str()); - ensure_equals("formatted binary encoding", actual, streamed); - sd.clear(); - LLSDSerialize::fromNotation(sd, str, str.str().size()); - std::vector<U8> after; - after = sd.asBinary(); - ensure_equals("binary decoded size", after.size(), decoded.size()); - ensure("binary decoding", (0 == memcmp( - &after[0], - decoded.c_str(), - decoded.size()))); - } - - template<> template<> - void sd_object::test<3>() - { - for(S32 i = 0; i < 100; ++i) - { - // gen up a starting point - typedef std::vector<U8> buf_t; - buf_t source; - srand(i); /* Flawfinder: ignore */ - S32 size = rand() % 1000 + 10; - std::generate_n( - std::back_insert_iterator<buf_t>(source), - size, - rand); - LLSD sd(source); - std::stringstream str; - S32 count = LLSDSerialize::toNotation(sd, str); - sd.clear(); - ensure_equals("format count", count, 1); - LLSD sd2; - count = LLSDSerialize::fromNotation(sd2, str, str.str().size()); - ensure_equals("parse count", count, 1); - buf_t dest = sd2.asBinary(); - str.str(""); - str << "binary encoding size " << i; - ensure_equals(str.str().c_str(), dest.size(), source.size()); - str.str(""); - str << "binary encoding " << i; - ensure(str.str().c_str(), (source == dest)); - } - } - - template<> template<> - void sd_object::test<4>() - { - std::ostringstream ostr; - ostr << "{'task_id':u1fd77b79-a8e7-25a5-9454-02a4d948ba1c}\n" - << "{\n\tname\tObject|\n}\n"; - std::string expected = ostr.str(); - std::stringstream serialized; - serialized << "'" << LLSDNotationFormatter::escapeString(expected) - << "'"; - LLSD sd; - S32 count = LLSDSerialize::fromNotation( - sd, - serialized, - serialized.str().size()); - ensure_equals("parse count", count, 1); - ensure_equals("String streaming", sd.asString(), expected); - } - - template<> template<> - void sd_object::test<5>() - { - for(S32 i = 0; i < 100; ++i) - { - // gen up a starting point - typedef std::vector<U8> buf_t; - buf_t source; - srand(666 + i); /* Flawfinder: ignore */ - S32 size = rand() % 1000 + 10; - std::generate_n( - std::back_insert_iterator<buf_t>(source), - size, - rand); - std::stringstream str; - str << "b(" << size << ")\""; - str.write((const char*)&source[0], size); - str << "\""; - LLSD sd; - S32 count = LLSDSerialize::fromNotation(sd, str, str.str().size()); - ensure_equals("binary parse", count, 1); - buf_t actual = sd.asBinary(); - ensure_equals("binary size", actual.size(), (size_t)size); - ensure("binary data", (0 == memcmp(&source[0], &actual[0], size))); - } - } - - template<> template<> - void sd_object::test<6>() - { - std::string expected("'{\"task_id\":u1fd77b79-a8e7-25a5-9454-02a4d948ba1c}'\t\n\t\t"); - std::stringstream str; - str << "s(" << expected.size() << ")'"; - str.write(expected.c_str(), expected.size()); - str << "'"; - LLSD sd; - S32 count = LLSDSerialize::fromNotation(sd, str, str.str().size()); - ensure_equals("parse count", count, 1); - std::string actual = sd.asString(); - ensure_equals("string sizes", actual.size(), expected.size()); - ensure_equals("string content", actual, expected); - } - - template<> template<> - void sd_object::test<7>() - { - std::string msg("come on in"); - std::stringstream stream; - stream << "{'connect':1, 'message':'" << msg << "'," - << " 'position':[r45.65,r100.1,r25.5]," - << " 'look_at':[r0,r1,r0]," - << " 'agent_access':'PG'}"; - LLSD sd; - S32 count = LLSDSerialize::fromNotation( - sd, - stream, - stream.str().size()); - ensure_equals("parse count", count, 12); - ensure_equals("bool value", sd["connect"].asBoolean(), true); - ensure_equals("message value", sd["message"].asString(), msg); - ensure_equals("pos x", sd["position"][0].asReal(), 45.65); - ensure_equals("pos y", sd["position"][1].asReal(), 100.1); - ensure_equals("pos z", sd["position"][2].asReal(), 25.5); - ensure_equals("look x", sd["look_at"][0].asReal(), 0.0); - ensure_equals("look y", sd["look_at"][1].asReal(), 1.0); - ensure_equals("look z", sd["look_at"][2].asReal(), 0.0); - } - - template<> template<> - void sd_object::test<8>() - { - std::stringstream resp; - resp << "{'label':'short string test', 'singlechar':'a', 'empty':'', 'endoftest':'end' }"; - LLSD response; - S32 count = LLSDSerialize::fromNotation( - response, - resp, - resp.str().size()); - ensure_equals("parse count", count, 5); - ensure_equals("sd type", response.type(), LLSD::TypeMap); - ensure_equals("map element count", response.size(), 4); - ensure_equals("singlechar", response["singlechar"].asString(), "a"); - ensure_equals("empty", response["empty"].asString(), ""); - } - - template<> template<> - void sd_object::test<9>() - { - std::ostringstream resp; - resp << "{'label':'short binary test', 'singlebinary':b(1)\"A\", 'singlerawstring':s(1)\"A\", 'endoftest':'end' }"; - std::string str = resp.str(); - LLSD sd; - LLMemoryStream mstr((U8*)str.c_str(), str.size()); - S32 count = LLSDSerialize::fromNotation(sd, mstr, str.size()); - ensure_equals("parse count", count, 5); - ensure("sd created", sd.isDefined()); - ensure_equals("sd type", sd.type(), LLSD::TypeMap); - ensure_equals("map element count", sd.size(), 4); - ensure_equals( - "label", - sd["label"].asString(), - "short binary test"); - std::vector<U8> bin = sd["singlebinary"].asBinary(); - std::vector<U8> expected; - expected.resize(1); - expected[0] = 'A'; - ensure("single binary", (0 == memcmp(&bin[0], &expected[0], 1))); - ensure_equals( - "single string", - sd["singlerawstring"].asString(), - std::string("A")); - ensure_equals("end", sd["endoftest"].asString(), "end"); - } - - template<> template<> - void sd_object::test<10>() - { - - std::string message("parcel '' is naughty."); - std::stringstream str; - str << "{'message':'" << LLSDNotationFormatter::escapeString(message) - << "'}"; - std::string expected_str("{'message':'parcel \\'\\' is naughty.'}"); - std::string actual_str = str.str(); - ensure_equals("stream contents", actual_str, expected_str); - LLSD sd; - S32 count = LLSDSerialize::fromNotation(sd, str, actual_str.size()); - ensure_equals("parse count", count, 2); - ensure("valid parse", sd.isDefined()); - std::string actual = sd["message"].asString(); - ensure_equals("message contents", actual, message); - } - - template<> template<> - void sd_object::test<11>() - { - std::string expected("\"\"\"\"''''''\""); - std::stringstream str; - str << "'" << LLSDNotationFormatter::escapeString(expected) << "'"; - LLSD sd; - S32 count = LLSDSerialize::fromNotation(sd, str, str.str().size()); - ensure_equals("parse count", count, 1); - ensure_equals("string value", sd.asString(), expected); - } - - template<> template<> - void sd_object::test<12>() - { - std::string expected("mytest\\"); - std::stringstream str; - str << "'" << LLSDNotationFormatter::escapeString(expected) << "'"; - LLSD sd; - S32 count = LLSDSerialize::fromNotation(sd, str, str.str().size()); - ensure_equals("parse count", count, 1); - ensure_equals("string value", sd.asString(), expected); - } - - template<> template<> - void sd_object::test<13>() - { - for(S32 i = 0; i < 1000; ++i) - { - // gen up a starting point - std::string expected; - srand(1337 + i); /* Flawfinder: ignore */ - S32 size = rand() % 30 + 5; - std::generate_n( - std::back_insert_iterator<std::string>(expected), - size, - rand); - std::stringstream str; - str << "'" << LLSDNotationFormatter::escapeString(expected) << "'"; - LLSD sd; - S32 count = LLSDSerialize::fromNotation(sd, str, expected.size()); - ensure_equals("parse count", count, 1); - std::string actual = sd.asString(); + struct sd_data + { + }; + typedef test_group<sd_data> sd_test; + typedef sd_test::object sd_object; + tut::sd_test sd("LLSD"); + + template<> template<> + void sd_object::test<1>() + { + std::ostringstream resp; + resp << "{'connect':true, 'position':[r128,r128,r128], 'look_at':[r0,r1,r0], 'agent_access':'M', 'region_x':i8192, 'region_y':i8192}"; + std::string str = resp.str(); + LLMemoryStream mstr((U8*)str.c_str(), str.size()); + LLSD response; + S32 count = LLSDSerialize::fromNotation(response, mstr, str.size()); + ensure("stream parsed", response.isDefined()); + ensure_equals("stream parse count", count, 13); + ensure_equals("sd type", response.type(), LLSD::TypeMap); + ensure_equals("map element count", response.size(), 6); + ensure_equals("value connect", response["connect"].asBoolean(), true); + ensure_equals("value region_x", response["region_x"].asInteger(),8192); + ensure_equals("value region_y", response["region_y"].asInteger(),8192); + } + + template<> template<> + void sd_object::test<2>() + { + const std::string decoded("random"); + //const std::string encoded("cmFuZG9t\n"); + const std::string streamed("b(6)\"random\""); + typedef std::vector<U8> buf_t; + buf_t buf; + std::copy( + decoded.begin(), + decoded.end(), + std::back_insert_iterator<buf_t>(buf)); + LLSD sd; + sd = buf; + std::stringstream str; + S32 count = LLSDSerialize::toNotation(sd, str); + ensure_equals("output count", count, 1); + std::string actual(str.str()); + ensure_equals("formatted binary encoding", actual, streamed); + sd.clear(); + LLSDSerialize::fromNotation(sd, str, str.str().size()); + std::vector<U8> after; + after = sd.asBinary(); + ensure_equals("binary decoded size", after.size(), decoded.size()); + ensure("binary decoding", (0 == memcmp( + &after[0], + decoded.c_str(), + decoded.size()))); + } + + template<> template<> + void sd_object::test<3>() + { + for(S32 i = 0; i < 100; ++i) + { + // gen up a starting point + typedef std::vector<U8> buf_t; + buf_t source; + srand(i); /* Flawfinder: ignore */ + S32 size = rand() % 1000 + 10; + std::generate_n( + std::back_insert_iterator<buf_t>(source), + size, + rand); + LLSD sd(source); + std::stringstream str; + S32 count = LLSDSerialize::toNotation(sd, str); + sd.clear(); + ensure_equals("format count", count, 1); + LLSD sd2; + count = LLSDSerialize::fromNotation(sd2, str, str.str().size()); + ensure_equals("parse count", count, 1); + buf_t dest = sd2.asBinary(); + str.str(""); + str << "binary encoding size " << i; + ensure_equals(str.str().c_str(), dest.size(), source.size()); + str.str(""); + str << "binary encoding " << i; + ensure(str.str().c_str(), (source == dest)); + } + } + + template<> template<> + void sd_object::test<4>() + { + std::ostringstream ostr; + ostr << "{'task_id':u1fd77b79-a8e7-25a5-9454-02a4d948ba1c}\n" + << "{\n\tname\tObject|\n}\n"; + std::string expected = ostr.str(); + std::stringstream serialized; + serialized << "'" << LLSDNotationFormatter::escapeString(expected) + << "'"; + LLSD sd; + S32 count = LLSDSerialize::fromNotation( + sd, + serialized, + serialized.str().size()); + ensure_equals("parse count", count, 1); + ensure_equals("String streaming", sd.asString(), expected); + } + + template<> template<> + void sd_object::test<5>() + { + for(S32 i = 0; i < 100; ++i) + { + // gen up a starting point + typedef std::vector<U8> buf_t; + buf_t source; + srand(666 + i); /* Flawfinder: ignore */ + S32 size = rand() % 1000 + 10; + std::generate_n( + std::back_insert_iterator<buf_t>(source), + size, + rand); + std::stringstream str; + str << "b(" << size << ")\""; + str.write((const char*)&source[0], size); + str << "\""; + LLSD sd; + S32 count = LLSDSerialize::fromNotation(sd, str, str.str().size()); + ensure_equals("binary parse", count, 1); + buf_t actual = sd.asBinary(); + ensure_equals("binary size", actual.size(), (size_t)size); + ensure("binary data", (0 == memcmp(&source[0], &actual[0], size))); + } + } + + template<> template<> + void sd_object::test<6>() + { + std::string expected("'{\"task_id\":u1fd77b79-a8e7-25a5-9454-02a4d948ba1c}'\t\n\t\t"); + std::stringstream str; + str << "s(" << expected.size() << ")'"; + str.write(expected.c_str(), expected.size()); + str << "'"; + LLSD sd; + S32 count = LLSDSerialize::fromNotation(sd, str, str.str().size()); + ensure_equals("parse count", count, 1); + std::string actual = sd.asString(); + ensure_equals("string sizes", actual.size(), expected.size()); + ensure_equals("string content", actual, expected); + } + + template<> template<> + void sd_object::test<7>() + { + std::string msg("come on in"); + std::stringstream stream; + stream << "{'connect':1, 'message':'" << msg << "'," + << " 'position':[r45.65,r100.1,r25.5]," + << " 'look_at':[r0,r1,r0]," + << " 'agent_access':'PG'}"; + LLSD sd; + S32 count = LLSDSerialize::fromNotation( + sd, + stream, + stream.str().size()); + ensure_equals("parse count", count, 12); + ensure_equals("bool value", sd["connect"].asBoolean(), true); + ensure_equals("message value", sd["message"].asString(), msg); + ensure_equals("pos x", sd["position"][0].asReal(), 45.65); + ensure_equals("pos y", sd["position"][1].asReal(), 100.1); + ensure_equals("pos z", sd["position"][2].asReal(), 25.5); + ensure_equals("look x", sd["look_at"][0].asReal(), 0.0); + ensure_equals("look y", sd["look_at"][1].asReal(), 1.0); + ensure_equals("look z", sd["look_at"][2].asReal(), 0.0); + } + + template<> template<> + void sd_object::test<8>() + { + std::stringstream resp; + resp << "{'label':'short string test', 'singlechar':'a', 'empty':'', 'endoftest':'end' }"; + LLSD response; + S32 count = LLSDSerialize::fromNotation( + response, + resp, + resp.str().size()); + ensure_equals("parse count", count, 5); + ensure_equals("sd type", response.type(), LLSD::TypeMap); + ensure_equals("map element count", response.size(), 4); + ensure_equals("singlechar", response["singlechar"].asString(), "a"); + ensure_equals("empty", response["empty"].asString(), ""); + } + + template<> template<> + void sd_object::test<9>() + { + std::ostringstream resp; + resp << "{'label':'short binary test', 'singlebinary':b(1)\"A\", 'singlerawstring':s(1)\"A\", 'endoftest':'end' }"; + std::string str = resp.str(); + LLSD sd; + LLMemoryStream mstr((U8*)str.c_str(), str.size()); + S32 count = LLSDSerialize::fromNotation(sd, mstr, str.size()); + ensure_equals("parse count", count, 5); + ensure("sd created", sd.isDefined()); + ensure_equals("sd type", sd.type(), LLSD::TypeMap); + ensure_equals("map element count", sd.size(), 4); + ensure_equals( + "label", + sd["label"].asString(), + "short binary test"); + std::vector<U8> bin = sd["singlebinary"].asBinary(); + std::vector<U8> expected; + expected.resize(1); + expected[0] = 'A'; + ensure("single binary", (0 == memcmp(&bin[0], &expected[0], 1))); + ensure_equals( + "single string", + sd["singlerawstring"].asString(), + std::string("A")); + ensure_equals("end", sd["endoftest"].asString(), "end"); + } + + template<> template<> + void sd_object::test<10>() + { + + std::string message("parcel '' is naughty."); + std::stringstream str; + str << "{'message':'" << LLSDNotationFormatter::escapeString(message) + << "'}"; + std::string expected_str("{'message':'parcel \\'\\' is naughty.'}"); + std::string actual_str = str.str(); + ensure_equals("stream contents", actual_str, expected_str); + LLSD sd; + S32 count = LLSDSerialize::fromNotation(sd, str, actual_str.size()); + ensure_equals("parse count", count, 2); + ensure("valid parse", sd.isDefined()); + std::string actual = sd["message"].asString(); + ensure_equals("message contents", actual, message); + } + + template<> template<> + void sd_object::test<11>() + { + std::string expected("\"\"\"\"''''''\""); + std::stringstream str; + str << "'" << LLSDNotationFormatter::escapeString(expected) << "'"; + LLSD sd; + S32 count = LLSDSerialize::fromNotation(sd, str, str.str().size()); + ensure_equals("parse count", count, 1); + ensure_equals("string value", sd.asString(), expected); + } + + template<> template<> + void sd_object::test<12>() + { + std::string expected("mytest\\"); + std::stringstream str; + str << "'" << LLSDNotationFormatter::escapeString(expected) << "'"; + LLSD sd; + S32 count = LLSDSerialize::fromNotation(sd, str, str.str().size()); + ensure_equals("parse count", count, 1); + ensure_equals("string value", sd.asString(), expected); + } + + template<> template<> + void sd_object::test<13>() + { + for(S32 i = 0; i < 1000; ++i) + { + // gen up a starting point + std::string expected; + srand(1337 + i); /* Flawfinder: ignore */ + S32 size = rand() % 30 + 5; + std::generate_n( + std::back_insert_iterator<std::string>(expected), + size, + rand); + std::stringstream str; + str << "'" << LLSDNotationFormatter::escapeString(expected) << "'"; + LLSD sd; + S32 count = LLSDSerialize::fromNotation(sd, str, expected.size()); + ensure_equals("parse count", count, 1); + std::string actual = sd.asString(); /* - if(actual != expected) - { - LL_WARNS() << "iteration " << i << LL_ENDL; - std::ostringstream e_str; - std::string::iterator iter = expected.begin(); - std::string::iterator end = expected.end(); - for(; iter != end; ++iter) - { - e_str << (S32)((U8)(*iter)) << " "; - } - e_str << std::endl; - llsd_serialize_string(e_str, expected); - LL_WARNS() << "expected size: " << expected.size() << LL_ENDL; - LL_WARNS() << "expected: " << e_str.str() << LL_ENDL; - - std::ostringstream a_str; - iter = actual.begin(); - end = actual.end(); - for(; iter != end; ++iter) - { - a_str << (S32)((U8)(*iter)) << " "; - } - a_str << std::endl; - llsd_serialize_string(a_str, actual); - LL_WARNS() << "actual size: " << actual.size() << LL_ENDL; - LL_WARNS() << "actual: " << a_str.str() << LL_ENDL; - } + if(actual != expected) + { + LL_WARNS() << "iteration " << i << LL_ENDL; + std::ostringstream e_str; + std::string::iterator iter = expected.begin(); + std::string::iterator end = expected.end(); + for(; iter != end; ++iter) + { + e_str << (S32)((U8)(*iter)) << " "; + } + e_str << std::endl; + llsd_serialize_string(e_str, expected); + LL_WARNS() << "expected size: " << expected.size() << LL_ENDL; + LL_WARNS() << "expected: " << e_str.str() << LL_ENDL; + + std::ostringstream a_str; + iter = actual.begin(); + end = actual.end(); + for(; iter != end; ++iter) + { + a_str << (S32)((U8)(*iter)) << " "; + } + a_str << std::endl; + llsd_serialize_string(a_str, actual); + LL_WARNS() << "actual size: " << actual.size() << LL_ENDL; + LL_WARNS() << "actual: " << a_str.str() << LL_ENDL; + } */ - ensure_equals("string value", actual, expected); - } - } + ensure_equals("string value", actual, expected); + } + } - template<> template<> - void sd_object::test<14>() - { + template<> template<> + void sd_object::test<14>() + { //#if LL_WINDOWS && _MSC_VER >= 1400 // skip_fail("Fails on VS2005 due to broken LLSDSerialize::fromNotation() parser."); //#endif - std::string param = "[{'version':i1},{'data':{'binary_bucket':b(0)\"\"},'from_id':u3c115e51-04f4-523c-9fa6-98aff1034730,'from_name':'Phoenix Linden','id':u004e45e5-5576-277a-fba7-859d6a4cb5c8,'message':'hey','offline':i0,'timestamp':i0,'to_id':u3c5f1bb4-5182-7546-6401-1d329b4ff2f8,'type':i0},{'agent_id':u3c115e51-04f4-523c-9fa6-98aff1034730,'god_level':i0,'limited_to_estate':i1}]"; - std::istringstream istr; - istr.str(param); - LLSD param_sd; - LLSDSerialize::fromNotation(param_sd, istr, param.size()); - ensure_equals("parsed type", param_sd.type(), LLSD::TypeArray); - LLSD version_sd = param_sd[0]; - ensure_equals("version type", version_sd.type(), LLSD::TypeMap); - ensure("has version", version_sd.has("version")); - ensure_equals("version number", version_sd["version"].asInteger(), 1); - LLSD src_sd = param_sd[1]; - ensure_equals("src type", src_sd.type(), LLSD::TypeMap); - LLSD dst_sd = param_sd[2]; - ensure_equals("dst type", dst_sd.type(), LLSD::TypeMap); - } - - template<> template<> - void sd_object::test<15>() - { - std::string val = "[{'failures':!,'successfuls':[u3c115e51-04f4-523c-9fa6-98aff1034730]}]"; - std::istringstream istr; - istr.str(val); - LLSD sd; - LLSDSerialize::fromNotation(sd, istr, val.size()); - ensure_equals("parsed type", sd.type(), LLSD::TypeArray); - ensure_equals("parsed size", sd.size(), 1); - LLSD failures = sd[0]["failures"]; - ensure("no failures.", failures.isUndefined()); - LLSD success = sd[0]["successfuls"]; - ensure_equals("success type", success.type(), LLSD::TypeArray); - ensure_equals("success size", success.size(), 1); - ensure_equals("success instance type", success[0].type(), LLSD::TypeUUID); - } - - template<> template<> - void sd_object::test<16>() - { - std::string val = "[f,t,0,1,{'foo':t,'bar':f}]"; - std::istringstream istr; - istr.str(val); - LLSD sd; - LLSDSerialize::fromNotation(sd, istr, val.size()); - ensure_equals("parsed type", sd.type(), LLSD::TypeArray); - ensure_equals("parsed size", sd.size(), 5); - ensure_equals("element 0 false", sd[0].asBoolean(), false); - ensure_equals("element 1 true", sd[1].asBoolean(), true); - ensure_equals("element 2 false", sd[2].asBoolean(), false); - ensure_equals("element 3 true", sd[3].asBoolean(), true); - LLSD map = sd[4]; - ensure_equals("element 4 type", map.type(), LLSD::TypeMap); - ensure_equals("map foo type", map["foo"].type(), LLSD::TypeBoolean); - ensure_equals("map foo value", map["foo"].asBoolean(), true); - ensure_equals("map bar type", map["bar"].type(), LLSD::TypeBoolean); - ensure_equals("map bar value", map["bar"].asBoolean(), false); - } + std::string param = "[{'version':i1},{'data':{'binary_bucket':b(0)\"\"},'from_id':u3c115e51-04f4-523c-9fa6-98aff1034730,'from_name':'Phoenix Linden','id':u004e45e5-5576-277a-fba7-859d6a4cb5c8,'message':'hey','offline':i0,'timestamp':i0,'to_id':u3c5f1bb4-5182-7546-6401-1d329b4ff2f8,'type':i0},{'agent_id':u3c115e51-04f4-523c-9fa6-98aff1034730,'god_level':i0,'limited_to_estate':i1}]"; + std::istringstream istr; + istr.str(param); + LLSD param_sd; + LLSDSerialize::fromNotation(param_sd, istr, param.size()); + ensure_equals("parsed type", param_sd.type(), LLSD::TypeArray); + LLSD version_sd = param_sd[0]; + ensure_equals("version type", version_sd.type(), LLSD::TypeMap); + ensure("has version", version_sd.has("version")); + ensure_equals("version number", version_sd["version"].asInteger(), 1); + LLSD src_sd = param_sd[1]; + ensure_equals("src type", src_sd.type(), LLSD::TypeMap); + LLSD dst_sd = param_sd[2]; + ensure_equals("dst type", dst_sd.type(), LLSD::TypeMap); + } + + template<> template<> + void sd_object::test<15>() + { + std::string val = "[{'failures':!,'successfuls':[u3c115e51-04f4-523c-9fa6-98aff1034730]}]"; + std::istringstream istr; + istr.str(val); + LLSD sd; + LLSDSerialize::fromNotation(sd, istr, val.size()); + ensure_equals("parsed type", sd.type(), LLSD::TypeArray); + ensure_equals("parsed size", sd.size(), 1); + LLSD failures = sd[0]["failures"]; + ensure("no failures.", failures.isUndefined()); + LLSD success = sd[0]["successfuls"]; + ensure_equals("success type", success.type(), LLSD::TypeArray); + ensure_equals("success size", success.size(), 1); + ensure_equals("success instance type", success[0].type(), LLSD::TypeUUID); + } + + template<> template<> + void sd_object::test<16>() + { + std::string val = "[f,t,0,1,{'foo':t,'bar':f}]"; + std::istringstream istr; + istr.str(val); + LLSD sd; + LLSDSerialize::fromNotation(sd, istr, val.size()); + ensure_equals("parsed type", sd.type(), LLSD::TypeArray); + ensure_equals("parsed size", sd.size(), 5); + ensure_equals("element 0 false", sd[0].asBoolean(), false); + ensure_equals("element 1 true", sd[1].asBoolean(), true); + ensure_equals("element 2 false", sd[2].asBoolean(), false); + ensure_equals("element 3 true", sd[3].asBoolean(), true); + LLSD map = sd[4]; + ensure_equals("element 4 type", map.type(), LLSD::TypeMap); + ensure_equals("map foo type", map["foo"].type(), LLSD::TypeBoolean); + ensure_equals("map foo value", map["foo"].asBoolean(), true); + ensure_equals("map bar type", map["bar"].type(), LLSD::TypeBoolean); + ensure_equals("map bar value", map["bar"].asBoolean(), false); + } /* - template<> template<> - void sd_object::test<16>() - { - } + template<> template<> + void sd_object::test<16>() + { + } */ } @@ -445,227 +445,227 @@ namespace tut namespace tut { - struct mem_data - { - }; - typedef test_group<mem_data> mem_test; - typedef mem_test::object mem_object; - tut::mem_test mem_stream("LLMemoryStream"); - - template<> template<> - void mem_object::test<1>() - { - const char HELLO_WORLD[] = "hello world"; - LLMemoryStream mem((U8*)&HELLO_WORLD[0], strlen(HELLO_WORLD)); /* Flawfinder: ignore */ - std::string hello; - std::string world; - mem >> hello >> world; - ensure_equals("first word", hello, std::string("hello")); - ensure_equals("second word", world, std::string("world")); - } + struct mem_data + { + }; + typedef test_group<mem_data> mem_test; + typedef mem_test::object mem_object; + tut::mem_test mem_stream("LLMemoryStream"); + + template<> template<> + void mem_object::test<1>() + { + const char HELLO_WORLD[] = "hello world"; + LLMemoryStream mem((U8*)&HELLO_WORLD[0], strlen(HELLO_WORLD)); /* Flawfinder: ignore */ + std::string hello; + std::string world; + mem >> hello >> world; + ensure_equals("first word", hello, std::string("hello")); + ensure_equals("second word", world, std::string("world")); + } } namespace tut { - struct U64_data - { - }; - typedef test_group<U64_data> U64_test; - typedef U64_test::object U64_object; - tut::U64_test U64_testcase("U64_conversion"); - - // U64_to_str - template<> template<> - void U64_object::test<1>() - { - U64 val; - std::string val_str; - char result[256]; - std::string result_str; - - val = U64L(18446744073709551610); // slightly less than MAX_U64 - val_str = "18446744073709551610"; - - U64_to_str(val, result, sizeof(result)); - result_str = (const char*) result; - ensure_equals("U64_to_str converted 1.1", val_str, result_str); - - val = 0; - val_str = "0"; - U64_to_str(val, result, sizeof(result)); - result_str = (const char*) result; - ensure_equals("U64_to_str converted 1.2", val_str, result_str); - - val = U64L(18446744073709551615); // 0xFFFFFFFFFFFFFFFF - val_str = "18446744073709551615"; - U64_to_str(val, result, sizeof(result)); - result_str = (const char*) result; - ensure_equals("U64_to_str converted 1.3", val_str, result_str); - - // overflow - will result in warning at compile time - val = U64L(18446744073709551615) + 1; // overflow 0xFFFFFFFFFFFFFFFF + 1 == 0 - val_str = "0"; - U64_to_str(val, result, sizeof(result)); - result_str = (const char*) result; - ensure_equals("U64_to_str converted 1.4", val_str, result_str); - - val = U64L(-1); // 0xFFFFFFFFFFFFFFFF == 18446744073709551615 - val_str = "18446744073709551615"; - U64_to_str(val, result, sizeof(result)); - result_str = (const char*) result; - ensure_equals("U64_to_str converted 1.5", val_str, result_str); - - val = U64L(10000000000000000000); // testing preserving of 0s - val_str = "10000000000000000000"; - U64_to_str(val, result, sizeof(result)); - result_str = (const char*) result; - ensure_equals("U64_to_str converted 1.6", val_str, result_str); - - val = 1; // testing no leading 0s - val_str = "1"; - U64_to_str(val, result, sizeof(result)); - result_str = (const char*) result; - ensure_equals("U64_to_str converted 1.7", val_str, result_str); - - val = U64L(18446744073709551615); // testing exact sized buffer for result - val_str = "18446744073709551615"; - memset(result, 'A', sizeof(result)); // initialize buffer with all 'A' - U64_to_str(val, result, sizeof("18446744073709551615")); //pass in the exact size - result_str = (const char*) result; - ensure_equals("U64_to_str converted 1.8", val_str, result_str); - - val = U64L(18446744073709551615); // testing smaller sized buffer for result - val_str = "1844"; - memset(result, 'A', sizeof(result)); // initialize buffer with all 'A' - U64_to_str(val, result, 5); //pass in a size of 5. should only copy first 4 integers and add a null terminator - result_str = (const char*) result; - ensure_equals("U64_to_str converted 1.9", val_str, result_str); - } - - // str_to_U64 - template<> template<> - void U64_object::test<2>() - { - U64 val; - U64 result; - - val = U64L(18446744073709551610); // slightly less than MAX_U64 - result = str_to_U64("18446744073709551610"); - ensure_equals("str_to_U64 converted 2.1", val, result); - - val = U64L(0); // empty string - result = str_to_U64(LLStringUtil::null); - ensure_equals("str_to_U64 converted 2.2", val, result); - - val = U64L(0); // 0 - result = str_to_U64("0"); - ensure_equals("str_to_U64 converted 2.3", val, result); - - val = U64L(18446744073709551615); // 0xFFFFFFFFFFFFFFFF - result = str_to_U64("18446744073709551615"); - ensure_equals("str_to_U64 converted 2.4", val, result); - - // overflow - will result in warning at compile time - val = U64L(18446744073709551615) + 1; // overflow 0xFFFFFFFFFFFFFFFF + 1 == 0 - result = str_to_U64("18446744073709551616"); - ensure_equals("str_to_U64 converted 2.5", val, result); - - val = U64L(1234); // process till first non-integral character - result = str_to_U64("1234A5678"); - ensure_equals("str_to_U64 converted 2.6", val, result); - - val = U64L(5678); // skip all non-integral characters - result = str_to_U64("ABCD5678"); - ensure_equals("str_to_U64 converted 2.7", val, result); - - // should it skip negative sign and process - // rest of string or return 0 - val = U64L(1234); // skip initial negative sign - result = str_to_U64("-1234"); - ensure_equals("str_to_U64 converted 2.8", val, result); - - val = U64L(5678); // stop at negative sign in the middle - result = str_to_U64("5678-1234"); - ensure_equals("str_to_U64 converted 2.9", val, result); - - val = U64L(0); // no integers - result = str_to_U64("AaCD"); - ensure_equals("str_to_U64 converted 2.10", val, result); - } - - // U64_to_F64 - template<> template<> - void U64_object::test<3>() - { - F64 val; - F64 result; - - result = 18446744073709551610.0; - val = U64_to_F64(U64L(18446744073709551610)); - ensure_equals("U64_to_F64 converted 3.1", val, result); - - result = 18446744073709551615.0; // 0xFFFFFFFFFFFFFFFF - val = U64_to_F64(U64L(18446744073709551615)); - ensure_equals("U64_to_F64 converted 3.2", val, result); - - result = 0.0; // overflow 0xFFFFFFFFFFFFFFFF + 1 == 0 - // overflow - will result in warning at compile time - val = U64_to_F64(U64L(18446744073709551615)+1); - ensure_equals("U64_to_F64 converted 3.3", val, result); - - result = 0.0; // 0 - val = U64_to_F64(U64L(0)); - ensure_equals("U64_to_F64 converted 3.4", val, result); - - result = 1.0; // odd - val = U64_to_F64(U64L(1)); - ensure_equals("U64_to_F64 converted 3.5", val, result); - - result = 2.0; // even - val = U64_to_F64(U64L(2)); - ensure_equals("U64_to_F64 converted 3.6", val, result); - - result = U64L(0x7FFFFFFFFFFFFFFF) * 1.0L; // 0x7FFFFFFFFFFFFFFF - val = U64_to_F64(U64L(0x7FFFFFFFFFFFFFFF)); - ensure_equals("U64_to_F64 converted 3.7", val, result); - } - - // llstrtou64 - // seems to be deprecated - could not find it being used - // anywhere in the tarball - skipping unit tests for now + struct U64_data + { + }; + typedef test_group<U64_data> U64_test; + typedef U64_test::object U64_object; + tut::U64_test U64_testcase("U64_conversion"); + + // U64_to_str + template<> template<> + void U64_object::test<1>() + { + U64 val; + std::string val_str; + char result[256]; + std::string result_str; + + val = U64L(18446744073709551610); // slightly less than MAX_U64 + val_str = "18446744073709551610"; + + U64_to_str(val, result, sizeof(result)); + result_str = (const char*) result; + ensure_equals("U64_to_str converted 1.1", val_str, result_str); + + val = 0; + val_str = "0"; + U64_to_str(val, result, sizeof(result)); + result_str = (const char*) result; + ensure_equals("U64_to_str converted 1.2", val_str, result_str); + + val = U64L(18446744073709551615); // 0xFFFFFFFFFFFFFFFF + val_str = "18446744073709551615"; + U64_to_str(val, result, sizeof(result)); + result_str = (const char*) result; + ensure_equals("U64_to_str converted 1.3", val_str, result_str); + + // overflow - will result in warning at compile time + val = U64L(18446744073709551615) + 1; // overflow 0xFFFFFFFFFFFFFFFF + 1 == 0 + val_str = "0"; + U64_to_str(val, result, sizeof(result)); + result_str = (const char*) result; + ensure_equals("U64_to_str converted 1.4", val_str, result_str); + + val = U64L(-1); // 0xFFFFFFFFFFFFFFFF == 18446744073709551615 + val_str = "18446744073709551615"; + U64_to_str(val, result, sizeof(result)); + result_str = (const char*) result; + ensure_equals("U64_to_str converted 1.5", val_str, result_str); + + val = U64L(10000000000000000000); // testing preserving of 0s + val_str = "10000000000000000000"; + U64_to_str(val, result, sizeof(result)); + result_str = (const char*) result; + ensure_equals("U64_to_str converted 1.6", val_str, result_str); + + val = 1; // testing no leading 0s + val_str = "1"; + U64_to_str(val, result, sizeof(result)); + result_str = (const char*) result; + ensure_equals("U64_to_str converted 1.7", val_str, result_str); + + val = U64L(18446744073709551615); // testing exact sized buffer for result + val_str = "18446744073709551615"; + memset(result, 'A', sizeof(result)); // initialize buffer with all 'A' + U64_to_str(val, result, sizeof("18446744073709551615")); //pass in the exact size + result_str = (const char*) result; + ensure_equals("U64_to_str converted 1.8", val_str, result_str); + + val = U64L(18446744073709551615); // testing smaller sized buffer for result + val_str = "1844"; + memset(result, 'A', sizeof(result)); // initialize buffer with all 'A' + U64_to_str(val, result, 5); //pass in a size of 5. should only copy first 4 integers and add a null terminator + result_str = (const char*) result; + ensure_equals("U64_to_str converted 1.9", val_str, result_str); + } + + // str_to_U64 + template<> template<> + void U64_object::test<2>() + { + U64 val; + U64 result; + + val = U64L(18446744073709551610); // slightly less than MAX_U64 + result = str_to_U64("18446744073709551610"); + ensure_equals("str_to_U64 converted 2.1", val, result); + + val = U64L(0); // empty string + result = str_to_U64(LLStringUtil::null); + ensure_equals("str_to_U64 converted 2.2", val, result); + + val = U64L(0); // 0 + result = str_to_U64("0"); + ensure_equals("str_to_U64 converted 2.3", val, result); + + val = U64L(18446744073709551615); // 0xFFFFFFFFFFFFFFFF + result = str_to_U64("18446744073709551615"); + ensure_equals("str_to_U64 converted 2.4", val, result); + + // overflow - will result in warning at compile time + val = U64L(18446744073709551615) + 1; // overflow 0xFFFFFFFFFFFFFFFF + 1 == 0 + result = str_to_U64("18446744073709551616"); + ensure_equals("str_to_U64 converted 2.5", val, result); + + val = U64L(1234); // process till first non-integral character + result = str_to_U64("1234A5678"); + ensure_equals("str_to_U64 converted 2.6", val, result); + + val = U64L(5678); // skip all non-integral characters + result = str_to_U64("ABCD5678"); + ensure_equals("str_to_U64 converted 2.7", val, result); + + // should it skip negative sign and process + // rest of string or return 0 + val = U64L(1234); // skip initial negative sign + result = str_to_U64("-1234"); + ensure_equals("str_to_U64 converted 2.8", val, result); + + val = U64L(5678); // stop at negative sign in the middle + result = str_to_U64("5678-1234"); + ensure_equals("str_to_U64 converted 2.9", val, result); + + val = U64L(0); // no integers + result = str_to_U64("AaCD"); + ensure_equals("str_to_U64 converted 2.10", val, result); + } + + // U64_to_F64 + template<> template<> + void U64_object::test<3>() + { + F64 val; + F64 result; + + result = 18446744073709551610.0; + val = U64_to_F64(U64L(18446744073709551610)); + ensure_equals("U64_to_F64 converted 3.1", val, result); + + result = 18446744073709551615.0; // 0xFFFFFFFFFFFFFFFF + val = U64_to_F64(U64L(18446744073709551615)); + ensure_equals("U64_to_F64 converted 3.2", val, result); + + result = 0.0; // overflow 0xFFFFFFFFFFFFFFFF + 1 == 0 + // overflow - will result in warning at compile time + val = U64_to_F64(U64L(18446744073709551615)+1); + ensure_equals("U64_to_F64 converted 3.3", val, result); + + result = 0.0; // 0 + val = U64_to_F64(U64L(0)); + ensure_equals("U64_to_F64 converted 3.4", val, result); + + result = 1.0; // odd + val = U64_to_F64(U64L(1)); + ensure_equals("U64_to_F64 converted 3.5", val, result); + + result = 2.0; // even + val = U64_to_F64(U64L(2)); + ensure_equals("U64_to_F64 converted 3.6", val, result); + + result = U64L(0x7FFFFFFFFFFFFFFF) * 1.0L; // 0x7FFFFFFFFFFFFFFF + val = U64_to_F64(U64L(0x7FFFFFFFFFFFFFFF)); + ensure_equals("U64_to_F64 converted 3.7", val, result); + } + + // llstrtou64 + // seems to be deprecated - could not find it being used + // anywhere in the tarball - skipping unit tests for now } namespace tut { - struct hash_data - { - }; - typedef test_group<hash_data> hash_test; - typedef hash_test::object hash_object; - tut::hash_test hash_tester("LLHash"); + struct hash_data + { + }; + typedef test_group<hash_data> hash_test; + typedef hash_test::object hash_object; + tut::hash_test hash_tester("LLHash"); + + template<> template<> + void hash_object::test<1>() + { + const char * str1 = "test string one"; + const char * same_as_str1 = "test string one"; + + size_t hash1 = llhash(str1); + size_t same_as_hash1 = llhash(same_as_str1); - template<> template<> - void hash_object::test<1>() - { - const char * str1 = "test string one"; - const char * same_as_str1 = "test string one"; - size_t hash1 = llhash(str1); - size_t same_as_hash1 = llhash(same_as_str1); + ensure("Hashes from identical strings should be equal", hash1 == same_as_hash1); + char str[100]; + strcpy( str, "Another test" ); - ensure("Hashes from identical strings should be equal", hash1 == same_as_hash1); - - char str[100]; - strcpy( str, "Another test" ); + size_t hash2 = llhash(str); - size_t hash2 = llhash(str); - - strcpy( str, "Different string, same pointer" ); + strcpy( str, "Different string, same pointer" ); - size_t hash3 = llhash(str); + size_t hash3 = llhash(str); - ensure("Hashes from same pointer but different string should not be equal", hash2 != hash3); - } + ensure("Hashes from same pointer but different string should not be equal", hash2 != hash3); + } } diff --git a/indra/llcommon/tests/lazyeventapi_test.cpp b/indra/llcommon/tests/lazyeventapi_test.cpp index 31b2d6d17f..f3fcf84e49 100644 --- a/indra/llcommon/tests/lazyeventapi_test.cpp +++ b/indra/llcommon/tests/lazyeventapi_test.cpp @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2022-06-18 * @brief Test for lazyeventapi. - * + * * $LicenseInfo:firstyear=2022&license=viewerlgpl$ * Copyright (c) 2022, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/tests/listener.h b/indra/llcommon/tests/listener.h index 6072060bb6..887a2c9082 100644 --- a/indra/llcommon/tests/listener.h +++ b/indra/llcommon/tests/listener.h @@ -3,25 +3,25 @@ * @author Nat Goodspeed * @date 2009-03-06 * @brief Useful for tests of the LLEventPump family of classes - * + * * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llcommon/tests/llallocator_heap_profile_test.cpp b/indra/llcommon/tests/llallocator_heap_profile_test.cpp index 44a9705803..da8b54ffc0 100644 --- a/indra/llcommon/tests/llallocator_heap_profile_test.cpp +++ b/indra/llcommon/tests/llallocator_heap_profile_test.cpp @@ -3,25 +3,25 @@ * @author Brad Kittenbrink * @date 2008-02- * @brief Test for llallocator_heap_profile.cpp. - * + * * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llcommon/tests/llallocator_test.cpp b/indra/llcommon/tests/llallocator_test.cpp index 4e62eaee67..a7573641c7 100644 --- a/indra/llcommon/tests/llallocator_test.cpp +++ b/indra/llcommon/tests/llallocator_test.cpp @@ -3,25 +3,25 @@ * @author Brad Kittenbrink * @date 2008-02- * @brief Test for llallocator.cpp. - * + * * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -71,7 +71,7 @@ namespace tut delete [] test_alloc; - llallocator.getProfile(); + llallocator.getProfile(); // *NOTE - this test isn't ensuring anything right now other than no // exceptions are thrown. diff --git a/indra/llcommon/tests/llbase64_test.cpp b/indra/llcommon/tests/llbase64_test.cpp index d0394150fa..69b062fc0c 100644 --- a/indra/llcommon/tests/llbase64_test.cpp +++ b/indra/llcommon/tests/llbase64_test.cpp @@ -1,4 +1,4 @@ -/** +/** * @file llbase64_test.cpp * @author James Cook * @date 2007-02-04 @@ -6,21 +6,21 @@ * $LicenseInfo:firstyear=2007&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -36,42 +36,42 @@ namespace tut { - struct base64_data - { - }; - typedef test_group<base64_data> base64_test; - typedef base64_test::object base64_object; - tut::base64_test base64("LLBase64"); + struct base64_data + { + }; + typedef test_group<base64_data> base64_test; + typedef base64_test::object base64_object; + tut::base64_test base64("LLBase64"); - template<> template<> - void base64_object::test<1>() - { - std::string result; + template<> template<> + void base64_object::test<1>() + { + std::string result; - result = LLBase64::encode(NULL, 0); - ensure("encode nothing", (result == "") ); + result = LLBase64::encode(NULL, 0); + ensure("encode nothing", (result == "") ); - LLUUID nothing; - result = LLBase64::encode(¬hing.mData[0], UUID_BYTES); - ensure("encode blank uuid", - (result == "AAAAAAAAAAAAAAAAAAAAAA==") ); + LLUUID nothing; + result = LLBase64::encode(¬hing.mData[0], UUID_BYTES); + ensure("encode blank uuid", + (result == "AAAAAAAAAAAAAAAAAAAAAA==") ); - LLUUID id("526a1e07-a19d-baed-84c4-ff08a488d15e"); - result = LLBase64::encode(&id.mData[0], UUID_BYTES); - ensure("encode random uuid", - (result == "UmoeB6Gduu2ExP8IpIjRXg==") ); + LLUUID id("526a1e07-a19d-baed-84c4-ff08a488d15e"); + result = LLBase64::encode(&id.mData[0], UUID_BYTES); + ensure("encode random uuid", + (result == "UmoeB6Gduu2ExP8IpIjRXg==") ); - } + } - template<> template<> - void base64_object::test<2>() - { - std::string result; + template<> template<> + void base64_object::test<2>() + { + std::string result; - U8 blob[40] = { 115, 223, 172, 255, 140, 70, 49, 125, 236, 155, 45, 199, 101, 17, 164, 131, 230, 19, 80, 64, 112, 53, 135, 98, 237, 12, 26, 72, 126, 14, 145, 143, 118, 196, 11, 177, 132, 169, 195, 134 }; - result = LLBase64::encode(&blob[0], 40); - ensure("encode 40 bytes", - (result == "c9+s/4xGMX3smy3HZRGkg+YTUEBwNYdi7QwaSH4OkY92xAuxhKnDhg==") ); - } + U8 blob[40] = { 115, 223, 172, 255, 140, 70, 49, 125, 236, 155, 45, 199, 101, 17, 164, 131, 230, 19, 80, 64, 112, 53, 135, 98, 237, 12, 26, 72, 126, 14, 145, 143, 118, 196, 11, 177, 132, 169, 195, 134 }; + result = LLBase64::encode(&blob[0], 40); + ensure("encode 40 bytes", + (result == "c9+s/4xGMX3smy3HZRGkg+YTUEBwNYdi7QwaSH4OkY92xAuxhKnDhg==") ); + } } diff --git a/indra/llcommon/tests/llcond_test.cpp b/indra/llcommon/tests/llcond_test.cpp index 478149eacf..f2a302ed13 100644 --- a/indra/llcommon/tests/llcond_test.cpp +++ b/indra/llcommon/tests/llcond_test.cpp @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2019-07-18 * @brief Test for llcond. - * + * * $LicenseInfo:firstyear=2019&license=viewerlgpl$ * Copyright (c) 2019, Linden Research, Inc. * $/LicenseInfo$ @@ -38,7 +38,7 @@ namespace tut { set_test_name("Immediate gratification"); cond.set_one(1); - ensure("wait_for_equal() failed", + ensure("wait_for_equal() failed", cond.wait_for_equal(F32Milliseconds(1), 1)); ensure("wait_for_unequal() should have failed", ! cond.wait_for_unequal(F32Milliseconds(1), 1)); diff --git a/indra/llcommon/tests/lldate_test.cpp b/indra/llcommon/tests/lldate_test.cpp index 7c95ccb91f..6d38b71649 100644 --- a/indra/llcommon/tests/lldate_test.cpp +++ b/indra/llcommon/tests/lldate_test.cpp @@ -7,21 +7,21 @@ * $LicenseInfo:firstyear=2007&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -34,180 +34,180 @@ #include "../test/lltut.h" -#define VALID_DATE "2003-04-30T04:00:00Z" -#define VALID_DATE_LEAP "2004-02-29T04:00:00Z" -#define VALID_DATE_HOUR_BOUNDARY "2003-04-30T23:59:59Z" -#define VALID_DATE_FRACTIONAL_SECS "2007-09-26T20:31:33.70Z" +#define VALID_DATE "2003-04-30T04:00:00Z" +#define VALID_DATE_LEAP "2004-02-29T04:00:00Z" +#define VALID_DATE_HOUR_BOUNDARY "2003-04-30T23:59:59Z" +#define VALID_DATE_FRACTIONAL_SECS "2007-09-26T20:31:33.70Z" // invalid format -#define INVALID_DATE_MISSING_YEAR "-04-30T22:59:59Z" -#define INVALID_DATE_MISSING_MONTH "1900-0430T22:59:59Z" -#define INVALID_DATE_MISSING_DATE "1900-0430-T22:59:59Z" -#define INVALID_DATE_MISSING_T "1900-04-30-22:59:59Z" -#define INVALID_DATE_MISSING_HOUR "1900-04-30T:59:59Z" -#define INVALID_DATE_MISSING_MIN "1900-04-30T01::59Z" -#define INVALID_DATE_MISSING_SEC "1900-04-30T01:59Z" -#define INVALID_DATE_MISSING_Z "1900-04-30T01:59:23" -#define INVALID_DATE_EMPTY "" +#define INVALID_DATE_MISSING_YEAR "-04-30T22:59:59Z" +#define INVALID_DATE_MISSING_MONTH "1900-0430T22:59:59Z" +#define INVALID_DATE_MISSING_DATE "1900-0430-T22:59:59Z" +#define INVALID_DATE_MISSING_T "1900-04-30-22:59:59Z" +#define INVALID_DATE_MISSING_HOUR "1900-04-30T:59:59Z" +#define INVALID_DATE_MISSING_MIN "1900-04-30T01::59Z" +#define INVALID_DATE_MISSING_SEC "1900-04-30T01:59Z" +#define INVALID_DATE_MISSING_Z "1900-04-30T01:59:23" +#define INVALID_DATE_EMPTY "" // invalid values // apr 1.1.1 seems to not care about constraining the date to valid // dates. Put these back when the parser checks. #define LL_DATE_PARSER_CHECKS_BOUNDARY 0 -//#define INVALID_DATE_24HOUR_BOUNDARY "2003-04-30T24:00:00Z" -//#define INVALID_DATE_LEAP "2003-04-29T04:00:00Z" -//#define INVALID_DATE_HOUR "2003-04-30T24:59:59Z" -//#define INVALID_DATE_MIN "2003-04-30T22:69:59Z" -//#define INVALID_DATE_SEC "2003-04-30T22:59:69Z" -//#define INVALID_DATE_YEAR "0-04-30T22:59:59Z" -//#define INVALID_DATE_MONTH "2003-13-30T22:59:59Z" -//#define INVALID_DATE_DAY "2003-04-35T22:59:59Z" +//#define INVALID_DATE_24HOUR_BOUNDARY "2003-04-30T24:00:00Z" +//#define INVALID_DATE_LEAP "2003-04-29T04:00:00Z" +//#define INVALID_DATE_HOUR "2003-04-30T24:59:59Z" +//#define INVALID_DATE_MIN "2003-04-30T22:69:59Z" +//#define INVALID_DATE_SEC "2003-04-30T22:59:69Z" +//#define INVALID_DATE_YEAR "0-04-30T22:59:59Z" +//#define INVALID_DATE_MONTH "2003-13-30T22:59:59Z" +//#define INVALID_DATE_DAY "2003-04-35T22:59:59Z" namespace tut { - struct date_test - { + struct date_test + { - }; - typedef test_group<date_test> date_test_t; - typedef date_test_t::object date_test_object_t; - tut::date_test_t tut_date_test("LLDate"); + }; + typedef test_group<date_test> date_test_t; + typedef date_test_t::object date_test_object_t; + tut::date_test_t tut_date_test("LLDate"); - /* format validation */ - template<> template<> - void date_test_object_t::test<1>() - { - LLDate date(VALID_DATE); - std::string expected_string; - bool result; - expected_string = VALID_DATE; - ensure_equals("Valid Date failed" , expected_string, date.asString()); + /* format validation */ + template<> template<> + void date_test_object_t::test<1>() + { + LLDate date(VALID_DATE); + std::string expected_string; + bool result; + expected_string = VALID_DATE; + ensure_equals("Valid Date failed" , expected_string, date.asString()); - result = date.fromString(VALID_DATE_LEAP); - expected_string = VALID_DATE_LEAP; - ensure_equals("VALID_DATE_LEAP failed" , expected_string, date.asString()); + result = date.fromString(VALID_DATE_LEAP); + expected_string = VALID_DATE_LEAP; + ensure_equals("VALID_DATE_LEAP failed" , expected_string, date.asString()); - result = date.fromString(VALID_DATE_HOUR_BOUNDARY); - expected_string = VALID_DATE_HOUR_BOUNDARY; - ensure_equals("VALID_DATE_HOUR_BOUNDARY failed" , expected_string, date.asString()); + result = date.fromString(VALID_DATE_HOUR_BOUNDARY); + expected_string = VALID_DATE_HOUR_BOUNDARY; + ensure_equals("VALID_DATE_HOUR_BOUNDARY failed" , expected_string, date.asString()); - result = date.fromString(VALID_DATE_FRACTIONAL_SECS); - expected_string = VALID_DATE_FRACTIONAL_SECS; - ensure_equals("VALID_DATE_FRACTIONAL_SECS failed" , expected_string, date.asString()); + result = date.fromString(VALID_DATE_FRACTIONAL_SECS); + expected_string = VALID_DATE_FRACTIONAL_SECS; + ensure_equals("VALID_DATE_FRACTIONAL_SECS failed" , expected_string, date.asString()); - result = date.fromString(INVALID_DATE_MISSING_YEAR); - ensure_equals("INVALID_DATE_MISSING_YEAR should have failed" , result, false); + result = date.fromString(INVALID_DATE_MISSING_YEAR); + ensure_equals("INVALID_DATE_MISSING_YEAR should have failed" , result, false); - result = date.fromString(INVALID_DATE_MISSING_MONTH); - ensure_equals("INVALID_DATE_MISSING_MONTH should have failed" , result, false); + result = date.fromString(INVALID_DATE_MISSING_MONTH); + ensure_equals("INVALID_DATE_MISSING_MONTH should have failed" , result, false); - result = date.fromString(INVALID_DATE_MISSING_DATE); - ensure_equals("INVALID_DATE_MISSING_DATE should have failed" , result, false); + result = date.fromString(INVALID_DATE_MISSING_DATE); + ensure_equals("INVALID_DATE_MISSING_DATE should have failed" , result, false); - result = date.fromString(INVALID_DATE_MISSING_T); - ensure_equals("INVALID_DATE_MISSING_T should have failed" , result, false); + result = date.fromString(INVALID_DATE_MISSING_T); + ensure_equals("INVALID_DATE_MISSING_T should have failed" , result, false); - result = date.fromString(INVALID_DATE_MISSING_HOUR); - ensure_equals("INVALID_DATE_MISSING_HOUR should have failed" , result, false); + result = date.fromString(INVALID_DATE_MISSING_HOUR); + ensure_equals("INVALID_DATE_MISSING_HOUR should have failed" , result, false); - result = date.fromString(INVALID_DATE_MISSING_MIN); - ensure_equals("INVALID_DATE_MISSING_MIN should have failed" , result, false); + result = date.fromString(INVALID_DATE_MISSING_MIN); + ensure_equals("INVALID_DATE_MISSING_MIN should have failed" , result, false); - result = date.fromString(INVALID_DATE_MISSING_SEC); - ensure_equals("INVALID_DATE_MISSING_SEC should have failed" , result, false); + result = date.fromString(INVALID_DATE_MISSING_SEC); + ensure_equals("INVALID_DATE_MISSING_SEC should have failed" , result, false); - result = date.fromString(INVALID_DATE_MISSING_Z); - ensure_equals("INVALID_DATE_MISSING_Z should have failed" , result, false); + result = date.fromString(INVALID_DATE_MISSING_Z); + ensure_equals("INVALID_DATE_MISSING_Z should have failed" , result, false); - result = date.fromString(INVALID_DATE_EMPTY); - ensure_equals("INVALID_DATE_EMPTY should have failed" , result, false); - } + result = date.fromString(INVALID_DATE_EMPTY); + ensure_equals("INVALID_DATE_EMPTY should have failed" , result, false); + } - /* Invalid Value Handling */ - template<> template<> - void date_test_object_t::test<2>() - { + /* Invalid Value Handling */ + template<> template<> + void date_test_object_t::test<2>() + { #if LL_DATE_PARSER_CHECKS_BOUNDARY - LLDate date; - std::string expected_string; - bool result; + LLDate date; + std::string expected_string; + bool result; - result = date.fromString(INVALID_DATE_24HOUR_BOUNDARY); - ensure_equals("INVALID_DATE_24HOUR_BOUNDARY should have failed" , result, false); - ensure_equals("INVALID_DATE_24HOUR_BOUNDARY date still set to old value on failure!" , date.secondsSinceEpoch(), 0); + result = date.fromString(INVALID_DATE_24HOUR_BOUNDARY); + ensure_equals("INVALID_DATE_24HOUR_BOUNDARY should have failed" , result, false); + ensure_equals("INVALID_DATE_24HOUR_BOUNDARY date still set to old value on failure!" , date.secondsSinceEpoch(), 0); - result = date.fromString(INVALID_DATE_LEAP); - ensure_equals("INVALID_DATE_LEAP should have failed" , result, false); + result = date.fromString(INVALID_DATE_LEAP); + ensure_equals("INVALID_DATE_LEAP should have failed" , result, false); - result = date.fromString(INVALID_DATE_HOUR); - ensure_equals("INVALID_DATE_HOUR should have failed" , result, false); + result = date.fromString(INVALID_DATE_HOUR); + ensure_equals("INVALID_DATE_HOUR should have failed" , result, false); - result = date.fromString(INVALID_DATE_MIN); - ensure_equals("INVALID_DATE_MIN should have failed" , result, false); + result = date.fromString(INVALID_DATE_MIN); + ensure_equals("INVALID_DATE_MIN should have failed" , result, false); - result = date.fromString(INVALID_DATE_SEC); - ensure_equals("INVALID_DATE_SEC should have failed" , result, false); + result = date.fromString(INVALID_DATE_SEC); + ensure_equals("INVALID_DATE_SEC should have failed" , result, false); - result = date.fromString(INVALID_DATE_YEAR); - ensure_equals("INVALID_DATE_YEAR should have failed" , result, false); + result = date.fromString(INVALID_DATE_YEAR); + ensure_equals("INVALID_DATE_YEAR should have failed" , result, false); - result = date.fromString(INVALID_DATE_MONTH); - ensure_equals("INVALID_DATE_MONTH should have failed" , result, false); + result = date.fromString(INVALID_DATE_MONTH); + ensure_equals("INVALID_DATE_MONTH should have failed" , result, false); - result = date.fromString(INVALID_DATE_DAY); - ensure_equals("INVALID_DATE_DAY should have failed" , result, false); + result = date.fromString(INVALID_DATE_DAY); + ensure_equals("INVALID_DATE_DAY should have failed" , result, false); #endif - } - - /* API checks */ - template<> template<> - void date_test_object_t::test<3>() - { - LLDate date; - std::istringstream stream(VALID_DATE); - std::string expected_string = VALID_DATE; - date.fromStream(stream); - ensure_equals("fromStream failed", date.asString(), expected_string); - } - - template<> template<> - void date_test_object_t::test<4>() - { - LLDate date1(VALID_DATE); - LLDate date2(date1); - ensure_equals("LLDate(const LLDate& date) constructor failed", date1.asString(), date2.asString()); - } - - template<> template<> - void date_test_object_t::test<5>() - { - LLDate date1(VALID_DATE); - LLDate date2(date1.secondsSinceEpoch()); - ensure_equals("secondsSinceEpoch not equal",date1.secondsSinceEpoch(), date2.secondsSinceEpoch()); - ensure_equals("LLDate created using secondsSinceEpoch not equal", date1.asString(), date2.asString()); - } - - template<> template<> - void date_test_object_t::test<6>() - { - LLDate date(VALID_DATE); - std::ostringstream stream; - stream << date; - std::string expected_str = VALID_DATE; - ensure_equals("ostringstream failed", expected_str, stream.str()); - } - - template<> template<> - void date_test_object_t::test<7>() - { - LLDate date; - std::istringstream stream(VALID_DATE); - stream >> date; - std::string expected_str = VALID_DATE; - std::ostringstream out_stream; + } + + /* API checks */ + template<> template<> + void date_test_object_t::test<3>() + { + LLDate date; + std::istringstream stream(VALID_DATE); + std::string expected_string = VALID_DATE; + date.fromStream(stream); + ensure_equals("fromStream failed", date.asString(), expected_string); + } + + template<> template<> + void date_test_object_t::test<4>() + { + LLDate date1(VALID_DATE); + LLDate date2(date1); + ensure_equals("LLDate(const LLDate& date) constructor failed", date1.asString(), date2.asString()); + } + + template<> template<> + void date_test_object_t::test<5>() + { + LLDate date1(VALID_DATE); + LLDate date2(date1.secondsSinceEpoch()); + ensure_equals("secondsSinceEpoch not equal",date1.secondsSinceEpoch(), date2.secondsSinceEpoch()); + ensure_equals("LLDate created using secondsSinceEpoch not equal", date1.asString(), date2.asString()); + } + + template<> template<> + void date_test_object_t::test<6>() + { + LLDate date(VALID_DATE); + std::ostringstream stream; + stream << date; + std::string expected_str = VALID_DATE; + ensure_equals("ostringstream failed", expected_str, stream.str()); + } + + template<> template<> + void date_test_object_t::test<7>() + { + LLDate date; + std::istringstream stream(VALID_DATE); + stream >> date; + std::string expected_str = VALID_DATE; + std::ostringstream out_stream; out_stream << date; - ensure_equals("<< failed", date.asString(),expected_str); - ensure_equals("<< to >> failed", stream.str(),out_stream.str()); - } + ensure_equals("<< failed", date.asString(),expected_str); + ensure_equals("<< to >> failed", stream.str(),out_stream.str()); + } } diff --git a/indra/llcommon/tests/lldeadmantimer_test.cpp b/indra/llcommon/tests/lldeadmantimer_test.cpp index 23167762c3..01e6e8e2f7 100644 --- a/indra/llcommon/tests/lldeadmantimer_test.cpp +++ b/indra/llcommon/tests/lldeadmantimer_test.cpp @@ -1,25 +1,25 @@ -/** +/** * @file lldeadmantimer_test.cpp * @brief Tests for the LLDeadmanTimer class. * * $LicenseInfo:firstyear=2013&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2013, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -37,12 +37,12 @@ static LLDeadmanTimer::time_type float_time_to_u64(F64 delta) { - return LLDeadmanTimer::time_type(delta * get_timer_info().mClockFrequency); + return LLDeadmanTimer::time_type(delta * get_timer_info().mClockFrequency); } static F64 u64_time_to_float(LLDeadmanTimer::time_type delta) { - return delta * get_timer_info().mClockFrequencyInv; + return delta * get_timer_info().mClockFrequencyInv; } @@ -51,11 +51,11 @@ namespace tut struct deadmantimer_test { - deadmantimer_test() - { - // LLTimer internals updating - get_timer_info().update(); - } + deadmantimer_test() + { + // LLTimer internals updating + get_timer_info().update(); + } }; typedef test_group<deadmantimer_test> deadmantimer_group_t; @@ -66,31 +66,31 @@ tut::deadmantimer_group_t deadmantimer_instance("LLDeadmanTimer"); template<> template<> void deadmantimer_object_t::test<1>() { - { - // Without cpu metrics - F64 started(42.0), stopped(97.0); - U64 count(U64L(8)); - LLDeadmanTimer timer(10.0, false); - - ensure_equals("WOCM isExpired() returns false after ctor()", timer.isExpired(0, started, stopped, count), false); - ensure_approximately_equals("WOCM t1 - isExpired() does not modify started", started, F64(42.0), 2); - ensure_approximately_equals("WOCM t1 - isExpired() does not modify stopped", stopped, F64(97.0), 2); - ensure_equals("WOCM t1 - isExpired() does not modify count", count, U64L(8)); - } - - { - // With cpu metrics - F64 started(42.0), stopped(97.0); - U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000); - LLDeadmanTimer timer(10.0, true); - - ensure_equals("WCM isExpired() returns false after ctor()", timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), false); - ensure_approximately_equals("WCM t1 - isExpired() does not modify started", started, F64(42.0), 2); - ensure_approximately_equals("WCM t1 - isExpired() does not modify stopped", stopped, F64(97.0), 2); - ensure_equals("WCM t1 - isExpired() does not modify count", count, U64L(8)); - ensure_equals("WCM t1 - isExpired() does not modify user_cpu", user_cpu, U64L(29000)); - ensure_equals("WCM t1 - isExpired() does not modify sys_cpu", sys_cpu, U64L(57000)); - } + { + // Without cpu metrics + F64 started(42.0), stopped(97.0); + U64 count(U64L(8)); + LLDeadmanTimer timer(10.0, false); + + ensure_equals("WOCM isExpired() returns false after ctor()", timer.isExpired(0, started, stopped, count), false); + ensure_approximately_equals("WOCM t1 - isExpired() does not modify started", started, F64(42.0), 2); + ensure_approximately_equals("WOCM t1 - isExpired() does not modify stopped", stopped, F64(97.0), 2); + ensure_equals("WOCM t1 - isExpired() does not modify count", count, U64L(8)); + } + + { + // With cpu metrics + F64 started(42.0), stopped(97.0); + U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000); + LLDeadmanTimer timer(10.0, true); + + ensure_equals("WCM isExpired() returns false after ctor()", timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), false); + ensure_approximately_equals("WCM t1 - isExpired() does not modify started", started, F64(42.0), 2); + ensure_approximately_equals("WCM t1 - isExpired() does not modify stopped", stopped, F64(97.0), 2); + ensure_equals("WCM t1 - isExpired() does not modify count", count, U64L(8)); + ensure_equals("WCM t1 - isExpired() does not modify user_cpu", user_cpu, U64L(29000)); + ensure_equals("WCM t1 - isExpired() does not modify sys_cpu", sys_cpu, U64L(57000)); + } } @@ -98,25 +98,25 @@ void deadmantimer_object_t::test<1>() template<> template<> void deadmantimer_object_t::test<2>() { - { - // Without cpu metrics - F64 started(42.0), stopped(97.0); - U64 count(U64L(8)); - LLDeadmanTimer timer(0.0, false); // Zero is pre-expired - - ensure_equals("WOCM isExpired() still returns false with 0.0 time ctor()", - timer.isExpired(0, started, stopped, count), false); - } - - { - // With cpu metrics - F64 started(42.0), stopped(97.0); - U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000); - LLDeadmanTimer timer(0.0, true); // Zero is pre-expired - - ensure_equals("WCM isExpired() still returns false with 0.0 time ctor()", - timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), false); - } + { + // Without cpu metrics + F64 started(42.0), stopped(97.0); + U64 count(U64L(8)); + LLDeadmanTimer timer(0.0, false); // Zero is pre-expired + + ensure_equals("WOCM isExpired() still returns false with 0.0 time ctor()", + timer.isExpired(0, started, stopped, count), false); + } + + { + // With cpu metrics + F64 started(42.0), stopped(97.0); + U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000); + LLDeadmanTimer timer(0.0, true); // Zero is pre-expired + + ensure_equals("WCM isExpired() still returns false with 0.0 time ctor()", + timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), false); + } } @@ -125,28 +125,28 @@ void deadmantimer_object_t::test<2>() template<> template<> void deadmantimer_object_t::test<3>() { - { - // Without cpu metrics - F64 started(42.0), stopped(97.0); - U64 count(U64L(8)); - LLDeadmanTimer timer(0.0, false); - - timer.start(0); - ensure_equals("WOCM isExpired() returns true with 0.0 horizon time", - timer.isExpired(0, started, stopped, count), true); - ensure_approximately_equals("WOCM expired timer with no bell ringing has stopped == started", started, stopped, 8); - } - { - // With cpu metrics - F64 started(42.0), stopped(97.0); - U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000); - LLDeadmanTimer timer(0.0, true); - - timer.start(0); - ensure_equals("WCM isExpired() returns true with 0.0 horizon time", - timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), true); - ensure_approximately_equals("WCM expired timer with no bell ringing has stopped == started", started, stopped, 8); - } + { + // Without cpu metrics + F64 started(42.0), stopped(97.0); + U64 count(U64L(8)); + LLDeadmanTimer timer(0.0, false); + + timer.start(0); + ensure_equals("WOCM isExpired() returns true with 0.0 horizon time", + timer.isExpired(0, started, stopped, count), true); + ensure_approximately_equals("WOCM expired timer with no bell ringing has stopped == started", started, stopped, 8); + } + { + // With cpu metrics + F64 started(42.0), stopped(97.0); + U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000); + LLDeadmanTimer timer(0.0, true); + + timer.start(0); + ensure_equals("WCM isExpired() returns true with 0.0 horizon time", + timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), true); + ensure_approximately_equals("WCM expired timer with no bell ringing has stopped == started", started, stopped, 8); + } } @@ -154,30 +154,30 @@ void deadmantimer_object_t::test<3>() template<> template<> void deadmantimer_object_t::test<4>() { - { - // Without cpu metrics - F64 started(42.0), stopped(97.0); - U64 count(U64L(8)); - LLDeadmanTimer timer(0.0, false); - - timer.start(0); - timer.ringBell(LLDeadmanTimer::getNow() + float_time_to_u64(1000.0), 1); - ensure_equals("WOCM isExpired() returns true with 0.0 horizon time after bell ring", - timer.isExpired(0, started, stopped, count), true); - ensure_approximately_equals("WOCM ringBell has no impact on expired timer leaving stopped == started", started, stopped, 8); - } - { - // With cpu metrics - F64 started(42.0), stopped(97.0); - U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000); - LLDeadmanTimer timer(0.0, true); - - timer.start(0); - timer.ringBell(LLDeadmanTimer::getNow() + float_time_to_u64(1000.0), 1); - ensure_equals("WCM isExpired() returns true with 0.0 horizon time after bell ring", - timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), true); - ensure_approximately_equals("WCM ringBell has no impact on expired timer leaving stopped == started", started, stopped, 8); - } + { + // Without cpu metrics + F64 started(42.0), stopped(97.0); + U64 count(U64L(8)); + LLDeadmanTimer timer(0.0, false); + + timer.start(0); + timer.ringBell(LLDeadmanTimer::getNow() + float_time_to_u64(1000.0), 1); + ensure_equals("WOCM isExpired() returns true with 0.0 horizon time after bell ring", + timer.isExpired(0, started, stopped, count), true); + ensure_approximately_equals("WOCM ringBell has no impact on expired timer leaving stopped == started", started, stopped, 8); + } + { + // With cpu metrics + F64 started(42.0), stopped(97.0); + U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000); + LLDeadmanTimer timer(0.0, true); + + timer.start(0); + timer.ringBell(LLDeadmanTimer::getNow() + float_time_to_u64(1000.0), 1); + ensure_equals("WCM isExpired() returns true with 0.0 horizon time after bell ring", + timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), true); + ensure_approximately_equals("WCM ringBell has no impact on expired timer leaving stopped == started", started, stopped, 8); + } } @@ -185,34 +185,34 @@ void deadmantimer_object_t::test<4>() template<> template<> void deadmantimer_object_t::test<5>() { - { - // Without cpu metrics - F64 started(42.0), stopped(97.0); - U64 count(U64L(8)); - LLDeadmanTimer timer(10.0, false); - - timer.start(0); - ensure_equals("WOCM isExpired() returns false after starting with 10.0 horizon time", - timer.isExpired(0, started, stopped, count), false); - ensure_approximately_equals("WOCM t5 - isExpired() does not modify started", started, F64(42.0), 2); - ensure_approximately_equals("WOCM t5 - isExpired() does not modify stopped", stopped, F64(97.0), 2); - ensure_equals("WOCM t5 - isExpired() does not modify count", count, U64L(8)); - } - { - // With cpu metrics - F64 started(42.0), stopped(97.0); - U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000); - LLDeadmanTimer timer(10.0, true); - - timer.start(0); - ensure_equals("WCM isExpired() returns false after starting with 10.0 horizon time", - timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), false); - ensure_approximately_equals("WCM t5 - isExpired() does not modify started", started, F64(42.0), 2); - ensure_approximately_equals("WCM t5 - isExpired() does not modify stopped", stopped, F64(97.0), 2); - ensure_equals("WCM t5 - isExpired() does not modify count", count, U64L(8)); - ensure_equals("WCM t5 - isExpired() does not modify user_cpu", user_cpu, U64L(29000)); - ensure_equals("WCM t5 - isExpired() does not modify sys_cpu", sys_cpu, U64L(57000)); - } + { + // Without cpu metrics + F64 started(42.0), stopped(97.0); + U64 count(U64L(8)); + LLDeadmanTimer timer(10.0, false); + + timer.start(0); + ensure_equals("WOCM isExpired() returns false after starting with 10.0 horizon time", + timer.isExpired(0, started, stopped, count), false); + ensure_approximately_equals("WOCM t5 - isExpired() does not modify started", started, F64(42.0), 2); + ensure_approximately_equals("WOCM t5 - isExpired() does not modify stopped", stopped, F64(97.0), 2); + ensure_equals("WOCM t5 - isExpired() does not modify count", count, U64L(8)); + } + { + // With cpu metrics + F64 started(42.0), stopped(97.0); + U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000); + LLDeadmanTimer timer(10.0, true); + + timer.start(0); + ensure_equals("WCM isExpired() returns false after starting with 10.0 horizon time", + timer.isExpired(0, started, stopped, count, user_cpu, sys_cpu), false); + ensure_approximately_equals("WCM t5 - isExpired() does not modify started", started, F64(42.0), 2); + ensure_approximately_equals("WCM t5 - isExpired() does not modify stopped", stopped, F64(97.0), 2); + ensure_equals("WCM t5 - isExpired() does not modify count", count, U64L(8)); + ensure_equals("WCM t5 - isExpired() does not modify user_cpu", user_cpu, U64L(29000)); + ensure_equals("WCM t5 - isExpired() does not modify sys_cpu", sys_cpu, U64L(57000)); + } } @@ -220,46 +220,46 @@ void deadmantimer_object_t::test<5>() template<> template<> void deadmantimer_object_t::test<6>() { - { - // Without cpu metrics - F64 started(42.0), stopped(97.0); - U64 count(U64L(8)); - LLDeadmanTimer timer(10.0, false); - - // Would like to do subtraction on current time but can't because - // the implementation on Windows is zero-based. We wrap around - // the backside resulting in a large U64 number. - - LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow()); - LLDeadmanTimer::time_type now(the_past + float_time_to_u64(5.0)); - timer.start(the_past); - ensure_equals("WOCM t6 - isExpired() returns false with 10.0 horizon time starting 5.0 in past", - timer.isExpired(now, started, stopped, count), false); - ensure_approximately_equals("WOCM t6 - isExpired() does not modify started", started, F64(42.0), 2); - ensure_approximately_equals("WOCM t6 - isExpired() does not modify stopped", stopped, F64(97.0), 2); - ensure_equals("WOCM t6 - isExpired() does not modify count", count, U64L(8)); - } - { - // With cpu metrics - F64 started(42.0), stopped(97.0); - U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000); - LLDeadmanTimer timer(10.0, true); - - // Would like to do subtraction on current time but can't because - // the implementation on Windows is zero-based. We wrap around - // the backside resulting in a large U64 number. - - LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow()); - LLDeadmanTimer::time_type now(the_past + float_time_to_u64(5.0)); - timer.start(the_past); - ensure_equals("WCM t6 - isExpired() returns false with 10.0 horizon time starting 5.0 in past", - timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false); - ensure_approximately_equals("WCM t6 - isExpired() does not modify started", started, F64(42.0), 2); - ensure_approximately_equals("WCM t6 - isExpired() does not modify stopped", stopped, F64(97.0), 2); - ensure_equals("t6 - isExpired() does not modify count", count, U64L(8)); - ensure_equals("WCM t6 - isExpired() does not modify user_cpu", user_cpu, U64L(29000)); - ensure_equals("WCM t6 - isExpired() does not modify sys_cpu", sys_cpu, U64L(57000)); - } + { + // Without cpu metrics + F64 started(42.0), stopped(97.0); + U64 count(U64L(8)); + LLDeadmanTimer timer(10.0, false); + + // Would like to do subtraction on current time but can't because + // the implementation on Windows is zero-based. We wrap around + // the backside resulting in a large U64 number. + + LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow()); + LLDeadmanTimer::time_type now(the_past + float_time_to_u64(5.0)); + timer.start(the_past); + ensure_equals("WOCM t6 - isExpired() returns false with 10.0 horizon time starting 5.0 in past", + timer.isExpired(now, started, stopped, count), false); + ensure_approximately_equals("WOCM t6 - isExpired() does not modify started", started, F64(42.0), 2); + ensure_approximately_equals("WOCM t6 - isExpired() does not modify stopped", stopped, F64(97.0), 2); + ensure_equals("WOCM t6 - isExpired() does not modify count", count, U64L(8)); + } + { + // With cpu metrics + F64 started(42.0), stopped(97.0); + U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000); + LLDeadmanTimer timer(10.0, true); + + // Would like to do subtraction on current time but can't because + // the implementation on Windows is zero-based. We wrap around + // the backside resulting in a large U64 number. + + LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow()); + LLDeadmanTimer::time_type now(the_past + float_time_to_u64(5.0)); + timer.start(the_past); + ensure_equals("WCM t6 - isExpired() returns false with 10.0 horizon time starting 5.0 in past", + timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false); + ensure_approximately_equals("WCM t6 - isExpired() does not modify started", started, F64(42.0), 2); + ensure_approximately_equals("WCM t6 - isExpired() does not modify stopped", stopped, F64(97.0), 2); + ensure_equals("t6 - isExpired() does not modify count", count, U64L(8)); + ensure_equals("WCM t6 - isExpired() does not modify user_cpu", user_cpu, U64L(29000)); + ensure_equals("WCM t6 - isExpired() does not modify sys_cpu", sys_cpu, U64L(57000)); + } } @@ -267,40 +267,40 @@ void deadmantimer_object_t::test<6>() template<> template<> void deadmantimer_object_t::test<7>() { - { - // Without cpu metrics - F64 started(42.0), stopped(97.0); - U64 count(U64L(8)); - LLDeadmanTimer timer(10.0, false); - - // Would like to do subtraction on current time but can't because - // the implementation on Windows is zero-based. We wrap around - // the backside resulting in a large U64 number. - - LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow()); - LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0)); - timer.start(the_past); - ensure_equals("WOCM t7 - isExpired() returns true with 10.0 horizon time starting 20.0 in past", - timer.isExpired(now,started, stopped, count), true); - ensure_approximately_equals("WOCM t7 - starting before horizon still gives equal started / stopped", started, stopped, 8); - } - { - // With cpu metrics - F64 started(42.0), stopped(97.0); - U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000); - LLDeadmanTimer timer(10.0, true); - - // Would like to do subtraction on current time but can't because - // the implementation on Windows is zero-based. We wrap around - // the backside resulting in a large U64 number. - - LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow()); - LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0)); - timer.start(the_past); - ensure_equals("WCM t7 - isExpired() returns true with 10.0 horizon time starting 20.0 in past", - timer.isExpired(now,started, stopped, count, user_cpu, sys_cpu), true); - ensure_approximately_equals("WOCM t7 - starting before horizon still gives equal started / stopped", started, stopped, 8); - } + { + // Without cpu metrics + F64 started(42.0), stopped(97.0); + U64 count(U64L(8)); + LLDeadmanTimer timer(10.0, false); + + // Would like to do subtraction on current time but can't because + // the implementation on Windows is zero-based. We wrap around + // the backside resulting in a large U64 number. + + LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow()); + LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0)); + timer.start(the_past); + ensure_equals("WOCM t7 - isExpired() returns true with 10.0 horizon time starting 20.0 in past", + timer.isExpired(now,started, stopped, count), true); + ensure_approximately_equals("WOCM t7 - starting before horizon still gives equal started / stopped", started, stopped, 8); + } + { + // With cpu metrics + F64 started(42.0), stopped(97.0); + U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000); + LLDeadmanTimer timer(10.0, true); + + // Would like to do subtraction on current time but can't because + // the implementation on Windows is zero-based. We wrap around + // the backside resulting in a large U64 number. + + LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow()); + LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0)); + timer.start(the_past); + ensure_equals("WCM t7 - isExpired() returns true with 10.0 horizon time starting 20.0 in past", + timer.isExpired(now,started, stopped, count, user_cpu, sys_cpu), true); + ensure_approximately_equals("WOCM t7 - starting before horizon still gives equal started / stopped", started, stopped, 8); + } } @@ -308,60 +308,60 @@ void deadmantimer_object_t::test<7>() template<> template<> void deadmantimer_object_t::test<8>() { - { - // Without cpu metrics - F64 started(42.0), stopped(97.0); - U64 count(U64L(8)); - LLDeadmanTimer timer(10.0, false); - - // Would like to do subtraction on current time but can't because - // the implementation on Windows is zero-based. We wrap around - // the backside resulting in a large U64 number. - - LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow()); - LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0)); - timer.start(the_past); - ensure_equals("WOCM t8 - isExpired() returns true with 10.0 horizon time starting 20.0 in past", - timer.isExpired(now, started, stopped, count), true); - - started = 42.0; - stopped = 97.0; - count = U64L(8); - ensure_equals("WOCM t8 - second isExpired() returns false after true", - timer.isExpired(now, started, stopped, count), false); - ensure_approximately_equals("WOCM t8 - 2nd isExpired() does not modify started", started, F64(42.0), 2); - ensure_approximately_equals("WOCM t8 - 2nd isExpired() does not modify stopped", stopped, F64(97.0), 2); - ensure_equals("WOCM t8 - 2nd isExpired() does not modify count", count, U64L(8)); - } - { - // With cpu metrics - F64 started(42.0), stopped(97.0); - U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000); - LLDeadmanTimer timer(10.0, true); - - // Would like to do subtraction on current time but can't because - // the implementation on Windows is zero-based. We wrap around - // the backside resulting in a large U64 number. - - LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow()); - LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0)); - timer.start(the_past); - ensure_equals("WCM t8 - isExpired() returns true with 10.0 horizon time starting 20.0 in past", - timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), true); - - started = 42.0; - stopped = 97.0; - count = U64L(8); - user_cpu = 29000; - sys_cpu = 57000; - ensure_equals("WCM t8 - second isExpired() returns false after true", - timer.isExpired(now, started, stopped, count), false); - ensure_approximately_equals("WCM t8 - 2nd isExpired() does not modify started", started, F64(42.0), 2); - ensure_approximately_equals("WCM t8 - 2nd isExpired() does not modify stopped", stopped, F64(97.0), 2); - ensure_equals("WCM t8 - 2nd isExpired() does not modify count", count, U64L(8)); - ensure_equals("WCM t8 - 2nd isExpired() does not modify user_cpu", user_cpu, U64L(29000)); - ensure_equals("WCM t8 - 2nd isExpired() does not modify sys_cpu", sys_cpu, U64L(57000)); - } + { + // Without cpu metrics + F64 started(42.0), stopped(97.0); + U64 count(U64L(8)); + LLDeadmanTimer timer(10.0, false); + + // Would like to do subtraction on current time but can't because + // the implementation on Windows is zero-based. We wrap around + // the backside resulting in a large U64 number. + + LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow()); + LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0)); + timer.start(the_past); + ensure_equals("WOCM t8 - isExpired() returns true with 10.0 horizon time starting 20.0 in past", + timer.isExpired(now, started, stopped, count), true); + + started = 42.0; + stopped = 97.0; + count = U64L(8); + ensure_equals("WOCM t8 - second isExpired() returns false after true", + timer.isExpired(now, started, stopped, count), false); + ensure_approximately_equals("WOCM t8 - 2nd isExpired() does not modify started", started, F64(42.0), 2); + ensure_approximately_equals("WOCM t8 - 2nd isExpired() does not modify stopped", stopped, F64(97.0), 2); + ensure_equals("WOCM t8 - 2nd isExpired() does not modify count", count, U64L(8)); + } + { + // With cpu metrics + F64 started(42.0), stopped(97.0); + U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000); + LLDeadmanTimer timer(10.0, true); + + // Would like to do subtraction on current time but can't because + // the implementation on Windows is zero-based. We wrap around + // the backside resulting in a large U64 number. + + LLDeadmanTimer::time_type the_past(LLDeadmanTimer::getNow()); + LLDeadmanTimer::time_type now(the_past + float_time_to_u64(20.0)); + timer.start(the_past); + ensure_equals("WCM t8 - isExpired() returns true with 10.0 horizon time starting 20.0 in past", + timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), true); + + started = 42.0; + stopped = 97.0; + count = U64L(8); + user_cpu = 29000; + sys_cpu = 57000; + ensure_equals("WCM t8 - second isExpired() returns false after true", + timer.isExpired(now, started, stopped, count), false); + ensure_approximately_equals("WCM t8 - 2nd isExpired() does not modify started", started, F64(42.0), 2); + ensure_approximately_equals("WCM t8 - 2nd isExpired() does not modify stopped", stopped, F64(97.0), 2); + ensure_equals("WCM t8 - 2nd isExpired() does not modify count", count, U64L(8)); + ensure_equals("WCM t8 - 2nd isExpired() does not modify user_cpu", user_cpu, U64L(29000)); + ensure_equals("WCM t8 - 2nd isExpired() does not modify sys_cpu", sys_cpu, U64L(57000)); + } } @@ -369,92 +369,92 @@ void deadmantimer_object_t::test<8>() template<> template<> void deadmantimer_object_t::test<9>() { - { - // Without cpu metrics - F64 started(42.0), stopped(97.0); - U64 count(U64L(8)); - LLDeadmanTimer timer(5.0, false); - - LLDeadmanTimer::time_type now(LLDeadmanTimer::getNow()); - F64 real_start(u64_time_to_float(now)); - timer.start(0); - - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - ensure_equals("WOCM t9 - 5.0 horizon timer has not timed out after 10 1-second bell rings", - timer.isExpired(now, started, stopped, count), false); - F64 last_good_ring(u64_time_to_float(now)); - - // Jump forward and expire - now += float_time_to_u64(10.0); - ensure_equals("WOCM t9 - 5.0 horizon timer expires on 10-second jump", - timer.isExpired(now, started, stopped, count), true); - ensure_approximately_equals("WOCM t9 - started matches start() time", started, real_start, 4); - ensure_approximately_equals("WOCM t9 - stopped matches last ringBell() time", stopped, last_good_ring, 4); - ensure_equals("WOCM t9 - 10 good ringBell()s", count, U64L(10)); - ensure_equals("WOCM t9 - single read only", timer.isExpired(now, started, stopped, count), false); - } - { - // With cpu metrics - F64 started(42.0), stopped(97.0); - U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000); - LLDeadmanTimer timer(5.0, true); - - LLDeadmanTimer::time_type now(LLDeadmanTimer::getNow()); - F64 real_start(u64_time_to_float(now)); - timer.start(0); - - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - ensure_equals("WCM t9 - 5.0 horizon timer has not timed out after 10 1-second bell rings", - timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false); - F64 last_good_ring(u64_time_to_float(now)); - - // Jump forward and expire - now += float_time_to_u64(10.0); - ensure_equals("WCM t9 - 5.0 horizon timer expires on 10-second jump", - timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), true); - ensure_approximately_equals("WCM t9 - started matches start() time", started, real_start, 4); - ensure_approximately_equals("WCM t9 - stopped matches last ringBell() time", stopped, last_good_ring, 4); - ensure_equals("WCM t9 - 10 good ringBell()s", count, U64L(10)); - ensure_equals("WCM t9 - single read only", timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false); - } + { + // Without cpu metrics + F64 started(42.0), stopped(97.0); + U64 count(U64L(8)); + LLDeadmanTimer timer(5.0, false); + + LLDeadmanTimer::time_type now(LLDeadmanTimer::getNow()); + F64 real_start(u64_time_to_float(now)); + timer.start(0); + + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + ensure_equals("WOCM t9 - 5.0 horizon timer has not timed out after 10 1-second bell rings", + timer.isExpired(now, started, stopped, count), false); + F64 last_good_ring(u64_time_to_float(now)); + + // Jump forward and expire + now += float_time_to_u64(10.0); + ensure_equals("WOCM t9 - 5.0 horizon timer expires on 10-second jump", + timer.isExpired(now, started, stopped, count), true); + ensure_approximately_equals("WOCM t9 - started matches start() time", started, real_start, 4); + ensure_approximately_equals("WOCM t9 - stopped matches last ringBell() time", stopped, last_good_ring, 4); + ensure_equals("WOCM t9 - 10 good ringBell()s", count, U64L(10)); + ensure_equals("WOCM t9 - single read only", timer.isExpired(now, started, stopped, count), false); + } + { + // With cpu metrics + F64 started(42.0), stopped(97.0); + U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000); + LLDeadmanTimer timer(5.0, true); + + LLDeadmanTimer::time_type now(LLDeadmanTimer::getNow()); + F64 real_start(u64_time_to_float(now)); + timer.start(0); + + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + ensure_equals("WCM t9 - 5.0 horizon timer has not timed out after 10 1-second bell rings", + timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false); + F64 last_good_ring(u64_time_to_float(now)); + + // Jump forward and expire + now += float_time_to_u64(10.0); + ensure_equals("WCM t9 - 5.0 horizon timer expires on 10-second jump", + timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), true); + ensure_approximately_equals("WCM t9 - started matches start() time", started, real_start, 4); + ensure_approximately_equals("WCM t9 - stopped matches last ringBell() time", stopped, last_good_ring, 4); + ensure_equals("WCM t9 - 10 good ringBell()s", count, U64L(10)); + ensure_equals("WCM t9 - single read only", timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false); + } } @@ -462,165 +462,165 @@ void deadmantimer_object_t::test<9>() template<> template<> void deadmantimer_object_t::test<10>() { - { - // Without cpu metrics - F64 started(42.0), stopped(97.0); - U64 count(U64L(8)); - LLDeadmanTimer timer(5.0, false); - - LLDeadmanTimer::time_type now(LLDeadmanTimer::getNow()); - F64 real_start(u64_time_to_float(now)); - timer.start(0); - - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - ensure_equals("WOCM t10 - 5.0 horizon timer has not timed out after 10 1-second bell rings", - timer.isExpired(now, started, stopped, count), false); - F64 last_good_ring(u64_time_to_float(now)); - - // Jump forward and expire - now += float_time_to_u64(10.0); - ensure_equals("WOCM t10 - 5.0 horizon timer expires on 10-second jump", - timer.isExpired(now, started, stopped, count), true); - ensure_approximately_equals("WOCM t10 - started matches start() time", started, real_start, 4); - ensure_approximately_equals("WOCM t10 - stopped matches last ringBell() time", stopped, last_good_ring, 4); - ensure_equals("WOCM t10 - 10 good ringBell()s", count, U64L(10)); - ensure_equals("WOCM t10 - single read only", timer.isExpired(now, started, stopped, count), false); - - // Jump forward and restart - now += float_time_to_u64(1.0); - real_start = u64_time_to_float(now); - timer.start(now); - - // Run a modified bell ring sequence - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - ensure_equals("WOCM t10 - 5.0 horizon timer has not timed out after 8 1-second bell rings", - timer.isExpired(now, started, stopped, count), false); - last_good_ring = u64_time_to_float(now); - - // Jump forward and expire - now += float_time_to_u64(10.0); - ensure_equals("WOCM t10 - 5.0 horizon timer expires on 8-second jump", - timer.isExpired(now, started, stopped, count), true); - ensure_approximately_equals("WOCM t10 - 2nd started matches start() time", started, real_start, 4); - ensure_approximately_equals("WOCM t10 - 2nd stopped matches last ringBell() time", stopped, last_good_ring, 4); - ensure_equals("WOCM t10 - 8 good ringBell()s", count, U64L(8)); - ensure_equals("WOCM t10 - single read only - 2nd start", - timer.isExpired(now, started, stopped, count), false); - } - { - // With cpu metrics - F64 started(42.0), stopped(97.0); - U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000); - - LLDeadmanTimer timer(5.0, true); - - LLDeadmanTimer::time_type now(LLDeadmanTimer::getNow()); - F64 real_start(u64_time_to_float(now)); - timer.start(0); - - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - ensure_equals("WCM t10 - 5.0 horizon timer has not timed out after 10 1-second bell rings", - timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false); - F64 last_good_ring(u64_time_to_float(now)); - - // Jump forward and expire - now += float_time_to_u64(10.0); - ensure_equals("WCM t10 - 5.0 horizon timer expires on 10-second jump", - timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), true); - ensure_approximately_equals("WCM t10 - started matches start() time", started, real_start, 4); - ensure_approximately_equals("WCM t10 - stopped matches last ringBell() time", stopped, last_good_ring, 4); - ensure_equals("WCM t10 - 10 good ringBell()s", count, U64L(10)); - ensure_equals("WCM t10 - single read only", timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false); - - // Jump forward and restart - now += float_time_to_u64(1.0); - real_start = u64_time_to_float(now); - timer.start(now); - - // Run a modified bell ring sequence - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - now += float_time_to_u64(1.0); - timer.ringBell(now, 1); - ensure_equals("WCM t10 - 5.0 horizon timer has not timed out after 8 1-second bell rings", - timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false); - last_good_ring = u64_time_to_float(now); - - // Jump forward and expire - now += float_time_to_u64(10.0); - ensure_equals("WCM t10 - 5.0 horizon timer expires on 8-second jump", - timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), true); - ensure_approximately_equals("WCM t10 - 2nd started matches start() time", started, real_start, 4); - ensure_approximately_equals("WCM t10 - 2nd stopped matches last ringBell() time", stopped, last_good_ring, 4); - ensure_equals("WCM t10 - 8 good ringBell()s", count, U64L(8)); - ensure_equals("WCM t10 - single read only - 2nd start", - timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false); - } + { + // Without cpu metrics + F64 started(42.0), stopped(97.0); + U64 count(U64L(8)); + LLDeadmanTimer timer(5.0, false); + + LLDeadmanTimer::time_type now(LLDeadmanTimer::getNow()); + F64 real_start(u64_time_to_float(now)); + timer.start(0); + + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + ensure_equals("WOCM t10 - 5.0 horizon timer has not timed out after 10 1-second bell rings", + timer.isExpired(now, started, stopped, count), false); + F64 last_good_ring(u64_time_to_float(now)); + + // Jump forward and expire + now += float_time_to_u64(10.0); + ensure_equals("WOCM t10 - 5.0 horizon timer expires on 10-second jump", + timer.isExpired(now, started, stopped, count), true); + ensure_approximately_equals("WOCM t10 - started matches start() time", started, real_start, 4); + ensure_approximately_equals("WOCM t10 - stopped matches last ringBell() time", stopped, last_good_ring, 4); + ensure_equals("WOCM t10 - 10 good ringBell()s", count, U64L(10)); + ensure_equals("WOCM t10 - single read only", timer.isExpired(now, started, stopped, count), false); + + // Jump forward and restart + now += float_time_to_u64(1.0); + real_start = u64_time_to_float(now); + timer.start(now); + + // Run a modified bell ring sequence + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + ensure_equals("WOCM t10 - 5.0 horizon timer has not timed out after 8 1-second bell rings", + timer.isExpired(now, started, stopped, count), false); + last_good_ring = u64_time_to_float(now); + + // Jump forward and expire + now += float_time_to_u64(10.0); + ensure_equals("WOCM t10 - 5.0 horizon timer expires on 8-second jump", + timer.isExpired(now, started, stopped, count), true); + ensure_approximately_equals("WOCM t10 - 2nd started matches start() time", started, real_start, 4); + ensure_approximately_equals("WOCM t10 - 2nd stopped matches last ringBell() time", stopped, last_good_ring, 4); + ensure_equals("WOCM t10 - 8 good ringBell()s", count, U64L(8)); + ensure_equals("WOCM t10 - single read only - 2nd start", + timer.isExpired(now, started, stopped, count), false); + } + { + // With cpu metrics + F64 started(42.0), stopped(97.0); + U64 count(U64L(8)), user_cpu(29000), sys_cpu(57000); + + LLDeadmanTimer timer(5.0, true); + + LLDeadmanTimer::time_type now(LLDeadmanTimer::getNow()); + F64 real_start(u64_time_to_float(now)); + timer.start(0); + + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + ensure_equals("WCM t10 - 5.0 horizon timer has not timed out after 10 1-second bell rings", + timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false); + F64 last_good_ring(u64_time_to_float(now)); + + // Jump forward and expire + now += float_time_to_u64(10.0); + ensure_equals("WCM t10 - 5.0 horizon timer expires on 10-second jump", + timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), true); + ensure_approximately_equals("WCM t10 - started matches start() time", started, real_start, 4); + ensure_approximately_equals("WCM t10 - stopped matches last ringBell() time", stopped, last_good_ring, 4); + ensure_equals("WCM t10 - 10 good ringBell()s", count, U64L(10)); + ensure_equals("WCM t10 - single read only", timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false); + + // Jump forward and restart + now += float_time_to_u64(1.0); + real_start = u64_time_to_float(now); + timer.start(now); + + // Run a modified bell ring sequence + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + now += float_time_to_u64(1.0); + timer.ringBell(now, 1); + ensure_equals("WCM t10 - 5.0 horizon timer has not timed out after 8 1-second bell rings", + timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false); + last_good_ring = u64_time_to_float(now); + + // Jump forward and expire + now += float_time_to_u64(10.0); + ensure_equals("WCM t10 - 5.0 horizon timer expires on 8-second jump", + timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), true); + ensure_approximately_equals("WCM t10 - 2nd started matches start() time", started, real_start, 4); + ensure_approximately_equals("WCM t10 - 2nd stopped matches last ringBell() time", stopped, last_good_ring, 4); + ensure_equals("WCM t10 - 8 good ringBell()s", count, U64L(8)); + ensure_equals("WCM t10 - single read only - 2nd start", + timer.isExpired(now, started, stopped, count, user_cpu, sys_cpu), false); + } } diff --git a/indra/llcommon/tests/lldependencies_test.cpp b/indra/llcommon/tests/lldependencies_test.cpp index b5e189a465..84eb41b5fe 100644 --- a/indra/llcommon/tests/lldependencies_test.cpp +++ b/indra/llcommon/tests/lldependencies_test.cpp @@ -3,25 +3,25 @@ * @author Nat Goodspeed * @date 2008-09-17 * @brief Test of lldependencies.h - * + * * $LicenseInfo:firstyear=2008&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llcommon/tests/llerror_test.cpp b/indra/llcommon/tests/llerror_test.cpp index b4cdbdc6bf..3ec429530c 100644 --- a/indra/llcommon/tests/llerror_test.cpp +++ b/indra/llcommon/tests/llerror_test.cpp @@ -47,7 +47,7 @@ enum LogFieldIndex MSG_FIELD }; -static const char* FieldName[] = +static const char* FieldName[] = { "TIME", "LEVEL", @@ -62,15 +62,15 @@ namespace #ifdef __clang__ # pragma clang diagnostic ignored "-Wunused-function" #endif - void test_that_error_h_includes_enough_things_to_compile_a_message() - { - LL_INFOS() << "!" << LL_ENDL; - } + void test_that_error_h_includes_enough_things_to_compile_a_message() + { + LL_INFOS() << "!" << LL_ENDL; + } } namespace { - static bool fatalWasCalled = false; + static bool fatalWasCalled = false; struct FatalWasCalled: public std::runtime_error { FatalWasCalled(const std::string& what): std::runtime_error(what) {} @@ -96,95 +96,95 @@ namespace namespace tut { - class TestRecorder : public LLError::Recorder - { - public: - TestRecorder() - { - showTime(false); - } - virtual ~TestRecorder() - {} - - virtual void recordMessage(LLError::ELevel level, - const std::string& message) - { - mMessages.push_back(message); - } - - int countMessages() { return (int) mMessages.size(); } - void clearMessages() { mMessages.clear(); } - - std::string message(int n) - { - std::ostringstream test_name; - test_name << "testing message " << n << ", not enough messages"; - - tut::ensure(test_name.str(), n < countMessages()); - return mMessages[n]; - } - - private: - typedef std::vector<std::string> MessageVector; - MessageVector mMessages; - }; - - struct ErrorTestData - { - LLError::RecorderPtr mRecorder; - LLError::SettingsStoragePtr mPriorErrorSettings; - - ErrorTestData(): - mRecorder(new TestRecorder()) - { - fatalWasCalled = false; - - mPriorErrorSettings = LLError::saveAndResetSettings(); - LLError::setDefaultLevel(LLError::LEVEL_DEBUG); - LLError::setFatalFunction(fatalCall); - LLError::addRecorder(mRecorder); - } - - ~ErrorTestData() - { - LLError::removeRecorder(mRecorder); - LLError::restoreSettings(mPriorErrorSettings); - } - - int countMessages() - { - return std::dynamic_pointer_cast<TestRecorder>(mRecorder)->countMessages(); - } - - void clearMessages() - { - std::dynamic_pointer_cast<TestRecorder>(mRecorder)->clearMessages(); - } - - void setWantsTime(bool t) + class TestRecorder : public LLError::Recorder + { + public: + TestRecorder() + { + showTime(false); + } + virtual ~TestRecorder() + {} + + virtual void recordMessage(LLError::ELevel level, + const std::string& message) + { + mMessages.push_back(message); + } + + int countMessages() { return (int) mMessages.size(); } + void clearMessages() { mMessages.clear(); } + + std::string message(int n) + { + std::ostringstream test_name; + test_name << "testing message " << n << ", not enough messages"; + + tut::ensure(test_name.str(), n < countMessages()); + return mMessages[n]; + } + + private: + typedef std::vector<std::string> MessageVector; + MessageVector mMessages; + }; + + struct ErrorTestData + { + LLError::RecorderPtr mRecorder; + LLError::SettingsStoragePtr mPriorErrorSettings; + + ErrorTestData(): + mRecorder(new TestRecorder()) + { + fatalWasCalled = false; + + mPriorErrorSettings = LLError::saveAndResetSettings(); + LLError::setDefaultLevel(LLError::LEVEL_DEBUG); + LLError::setFatalFunction(fatalCall); + LLError::addRecorder(mRecorder); + } + + ~ErrorTestData() + { + LLError::removeRecorder(mRecorder); + LLError::restoreSettings(mPriorErrorSettings); + } + + int countMessages() + { + return std::dynamic_pointer_cast<TestRecorder>(mRecorder)->countMessages(); + } + + void clearMessages() + { + std::dynamic_pointer_cast<TestRecorder>(mRecorder)->clearMessages(); + } + + void setWantsTime(bool t) { std::dynamic_pointer_cast<TestRecorder>(mRecorder)->showTime(t); } - void setWantsMultiline(bool t) + void setWantsMultiline(bool t) { std::dynamic_pointer_cast<TestRecorder>(mRecorder)->showMultiline(t); } - std::string message(int n) - { - return std::dynamic_pointer_cast<TestRecorder>(mRecorder)->message(n); - } + std::string message(int n) + { + return std::dynamic_pointer_cast<TestRecorder>(mRecorder)->message(n); + } - void ensure_message_count(int expectedCount) - { - ensure_equals("message count", countMessages(), expectedCount); - } + void ensure_message_count(int expectedCount) + { + ensure_equals("message count", countMessages(), expectedCount); + } std::string message_field(int msgnum, LogFieldIndex fieldnum) { std::ostringstream test_name; - test_name << "testing message " << msgnum << ", not enough messages"; + test_name << "testing message " << msgnum << ", not enough messages"; tut::ensure(test_name.str(), msgnum < countMessages()); std::string msg(message(msgnum)); @@ -204,7 +204,7 @@ namespace tut on_field++; } // except function, which may have embedded spaces so ends with " : " - else if ( ( on_field == FUNCTION_FIELD ) + else if ( ( on_field == FUNCTION_FIELD ) && ( ':' == msg[scan+1] && ' ' == msg[scan+2] ) ) { @@ -220,9 +220,9 @@ namespace tut { fieldlen = msg.find(' ', start_field) - start_field; } - else if ( fieldnum == FUNCTION_FIELD ) + else if ( fieldnum == FUNCTION_FIELD ) { - fieldlen = msg.find(" : ", start_field) - start_field; + fieldlen = msg.find(" : ", start_field) - start_field; } else if ( MSG_FIELD == fieldnum ) // no delimiter, just everything to the end { @@ -231,8 +231,8 @@ namespace tut return msg.substr(start_field, fieldlen); } - - void ensure_message_field_equals(int msgnum, LogFieldIndex fieldnum, const std::string& expectedText) + + void ensure_message_field_equals(int msgnum, LogFieldIndex fieldnum, const std::string& expectedText) { std::ostringstream test_name; test_name << "testing message " << msgnum << " field " << FieldName[fieldnum] << "\n message: \"" << message(msgnum) << "\"\n "; @@ -240,105 +240,105 @@ namespace tut ensure_equals(test_name.str(), message_field(msgnum, fieldnum), expectedText); } - void ensure_message_does_not_contain(int n, const std::string& expectedText) - { - std::ostringstream test_name; - test_name << "testing message " << n; + void ensure_message_does_not_contain(int n, const std::string& expectedText) + { + std::ostringstream test_name; + test_name << "testing message " << n; - ensure_does_not_contain(test_name.str(), message(n), expectedText); - } - }; + ensure_does_not_contain(test_name.str(), message(n), expectedText); + } + }; - typedef test_group<ErrorTestData> ErrorTestGroup; - typedef ErrorTestGroup::object ErrorTestObject; + typedef test_group<ErrorTestData> ErrorTestGroup; + typedef ErrorTestGroup::object ErrorTestObject; - ErrorTestGroup errorTestGroup("error"); + ErrorTestGroup errorTestGroup("error"); - template<> template<> - void ErrorTestObject::test<1>() - // basic test of output - { - LL_INFOS() << "test" << LL_ENDL; - LL_INFOS() << "bob" << LL_ENDL; + template<> template<> + void ErrorTestObject::test<1>() + // basic test of output + { + LL_INFOS() << "test" << LL_ENDL; + LL_INFOS() << "bob" << LL_ENDL; - ensure_message_field_equals(0, MSG_FIELD, "test"); - ensure_message_field_equals(1, MSG_FIELD, "bob"); - } + ensure_message_field_equals(0, MSG_FIELD, "test"); + ensure_message_field_equals(1, MSG_FIELD, "bob"); + } } namespace { - void writeSome() - { - LL_DEBUGS("WriteTag","AnotherTag") << "one" << LL_ENDL; - LL_INFOS("WriteTag") << "two" << LL_ENDL; - LL_WARNS("WriteTag") << "three" << LL_ENDL; - CATCH(LL_ERRS("WriteTag"), "four"); - } + void writeSome() + { + LL_DEBUGS("WriteTag","AnotherTag") << "one" << LL_ENDL; + LL_INFOS("WriteTag") << "two" << LL_ENDL; + LL_WARNS("WriteTag") << "three" << LL_ENDL; + CATCH(LL_ERRS("WriteTag"), "four"); + } }; namespace tut { - template<> template<> - void ErrorTestObject::test<2>() - // messages are filtered based on default level - { - LLError::setDefaultLevel(LLError::LEVEL_DEBUG); - writeSome(); - ensure_message_field_equals(0, MSG_FIELD, "one"); - ensure_message_field_equals(0, LEVEL_FIELD, "DEBUG"); - ensure_message_field_equals(0, TAGS_FIELD, "#WriteTag#AnotherTag#"); - ensure_message_field_equals(1, MSG_FIELD, "two"); - ensure_message_field_equals(1, LEVEL_FIELD, "INFO"); - ensure_message_field_equals(1, TAGS_FIELD, "#WriteTag#"); - ensure_message_field_equals(2, MSG_FIELD, "three"); - ensure_message_field_equals(2, LEVEL_FIELD, "WARNING"); - ensure_message_field_equals(2, TAGS_FIELD, "#WriteTag#"); - ensure_message_field_equals(3, MSG_FIELD, "four"); - ensure_message_field_equals(3, LEVEL_FIELD, "ERROR"); - ensure_message_field_equals(3, TAGS_FIELD, "#WriteTag#"); - ensure_message_count(4); - - LLError::setDefaultLevel(LLError::LEVEL_INFO); - writeSome(); - ensure_message_field_equals(4, MSG_FIELD, "two"); - ensure_message_field_equals(5, MSG_FIELD, "three"); - ensure_message_field_equals(6, MSG_FIELD, "four"); - ensure_message_count(7); - - LLError::setDefaultLevel(LLError::LEVEL_WARN); - writeSome(); - ensure_message_field_equals(7, MSG_FIELD, "three"); - ensure_message_field_equals(8, MSG_FIELD, "four"); - ensure_message_count(9); - - LLError::setDefaultLevel(LLError::LEVEL_ERROR); - writeSome(); - ensure_message_field_equals(9, MSG_FIELD, "four"); - ensure_message_count(10); - - LLError::setDefaultLevel(LLError::LEVEL_NONE); - writeSome(); - ensure_message_count(10); - } - - template<> template<> - void ErrorTestObject::test<3>() - // error type string in output - { - writeSome(); - ensure_message_field_equals(0, LEVEL_FIELD, "DEBUG"); - ensure_message_field_equals(1, LEVEL_FIELD, "INFO"); - ensure_message_field_equals(2, LEVEL_FIELD, "WARNING"); - ensure_message_field_equals(3, LEVEL_FIELD, "ERROR"); - ensure_message_count(4); - } - - template<> template<> - void ErrorTestObject::test<4>() - // file abbreviation - { - std::string prev, abbreviateFile = __FILE__; + template<> template<> + void ErrorTestObject::test<2>() + // messages are filtered based on default level + { + LLError::setDefaultLevel(LLError::LEVEL_DEBUG); + writeSome(); + ensure_message_field_equals(0, MSG_FIELD, "one"); + ensure_message_field_equals(0, LEVEL_FIELD, "DEBUG"); + ensure_message_field_equals(0, TAGS_FIELD, "#WriteTag#AnotherTag#"); + ensure_message_field_equals(1, MSG_FIELD, "two"); + ensure_message_field_equals(1, LEVEL_FIELD, "INFO"); + ensure_message_field_equals(1, TAGS_FIELD, "#WriteTag#"); + ensure_message_field_equals(2, MSG_FIELD, "three"); + ensure_message_field_equals(2, LEVEL_FIELD, "WARNING"); + ensure_message_field_equals(2, TAGS_FIELD, "#WriteTag#"); + ensure_message_field_equals(3, MSG_FIELD, "four"); + ensure_message_field_equals(3, LEVEL_FIELD, "ERROR"); + ensure_message_field_equals(3, TAGS_FIELD, "#WriteTag#"); + ensure_message_count(4); + + LLError::setDefaultLevel(LLError::LEVEL_INFO); + writeSome(); + ensure_message_field_equals(4, MSG_FIELD, "two"); + ensure_message_field_equals(5, MSG_FIELD, "three"); + ensure_message_field_equals(6, MSG_FIELD, "four"); + ensure_message_count(7); + + LLError::setDefaultLevel(LLError::LEVEL_WARN); + writeSome(); + ensure_message_field_equals(7, MSG_FIELD, "three"); + ensure_message_field_equals(8, MSG_FIELD, "four"); + ensure_message_count(9); + + LLError::setDefaultLevel(LLError::LEVEL_ERROR); + writeSome(); + ensure_message_field_equals(9, MSG_FIELD, "four"); + ensure_message_count(10); + + LLError::setDefaultLevel(LLError::LEVEL_NONE); + writeSome(); + ensure_message_count(10); + } + + template<> template<> + void ErrorTestObject::test<3>() + // error type string in output + { + writeSome(); + ensure_message_field_equals(0, LEVEL_FIELD, "DEBUG"); + ensure_message_field_equals(1, LEVEL_FIELD, "INFO"); + ensure_message_field_equals(2, LEVEL_FIELD, "WARNING"); + ensure_message_field_equals(3, LEVEL_FIELD, "ERROR"); + ensure_message_count(4); + } + + template<> template<> + void ErrorTestObject::test<4>() + // file abbreviation + { + std::string prev, abbreviateFile = __FILE__; do { prev = abbreviateFile; @@ -354,169 +354,169 @@ namespace tut // argument unchanged, THEN check. } while (abbreviateFile != prev); - ensure_ends_with("file name abbreviation", - abbreviateFile, - "llcommon/tests/llerror_test.cpp" - ); - ensure_does_not_contain("file name abbreviation", - abbreviateFile, "indra"); + ensure_ends_with("file name abbreviation", + abbreviateFile, + "llcommon/tests/llerror_test.cpp" + ); + ensure_does_not_contain("file name abbreviation", + abbreviateFile, "indra"); - std::string someFile = + std::string someFile = #if LL_WINDOWS - "C:/amy/bob/cam.cpp" + "C:/amy/bob/cam.cpp" #else - "/amy/bob/cam.cpp" + "/amy/bob/cam.cpp" #endif - ; - std::string someAbbreviation = LLError::abbreviateFile(someFile); + ; + std::string someAbbreviation = LLError::abbreviateFile(someFile); - ensure_equals("non-indra file abbreviation", - someAbbreviation, someFile); - } + ensure_equals("non-indra file abbreviation", + someAbbreviation, someFile); + } } namespace { - std::string locationString(int line) - { - std::ostringstream location; - location << LLError::abbreviateFile(__FILE__) - << "(" << line << ")"; - - return location.str(); - } - - std::string writeReturningLocation() - { - LL_INFOS() << "apple" << LL_ENDL; int this_line = __LINE__; - return locationString(this_line); - } - - void writeReturningLocationAndFunction(std::string& location, std::string& function) - { - LL_INFOS() << "apple" << LL_ENDL; int this_line = __LINE__; - location = locationString(this_line); - function = __FUNCTION__; - } - - std::string errorReturningLocation() - { - int this_line = __LINE__; CATCH(LL_ERRS(), "die"); - return locationString(this_line); - } + std::string locationString(int line) + { + std::ostringstream location; + location << LLError::abbreviateFile(__FILE__) + << "(" << line << ")"; + + return location.str(); + } + + std::string writeReturningLocation() + { + LL_INFOS() << "apple" << LL_ENDL; int this_line = __LINE__; + return locationString(this_line); + } + + void writeReturningLocationAndFunction(std::string& location, std::string& function) + { + LL_INFOS() << "apple" << LL_ENDL; int this_line = __LINE__; + location = locationString(this_line); + function = __FUNCTION__; + } + + std::string errorReturningLocation() + { + int this_line = __LINE__; CATCH(LL_ERRS(), "die"); + return locationString(this_line); + } } /* The following helper functions and class members all log a simple message - from some particular function scope. Each function takes a bool argument - that indicates if it should log its own name or not (in the manner that - existing log messages often do.) The functions all return their C++ - name so that test can be substantial mechanized. + from some particular function scope. Each function takes a bool argument + that indicates if it should log its own name or not (in the manner that + existing log messages often do.) The functions all return their C++ + name so that test can be substantial mechanized. */ std::string logFromGlobal(bool id) { - LL_INFOS() << (id ? "logFromGlobal: " : "") << "hi" << LL_ENDL; - return "logFromGlobal"; + LL_INFOS() << (id ? "logFromGlobal: " : "") << "hi" << LL_ENDL; + return "logFromGlobal"; } static std::string logFromStatic(bool id) { - LL_INFOS() << (id ? "logFromStatic: " : "") << "hi" << LL_ENDL; - return "logFromStatic"; + LL_INFOS() << (id ? "logFromStatic: " : "") << "hi" << LL_ENDL; + return "logFromStatic"; } namespace { - std::string logFromAnon(bool id) - { - LL_INFOS() << (id ? "logFromAnon: " : "") << "hi" << LL_ENDL; - return "logFromAnon"; - } + std::string logFromAnon(bool id) + { + LL_INFOS() << (id ? "logFromAnon: " : "") << "hi" << LL_ENDL; + return "logFromAnon"; + } } namespace Foo { - std::string logFromNamespace(bool id) - { - LL_INFOS() << (id ? "Foo::logFromNamespace: " : "") << "hi" << LL_ENDL; - //return "Foo::logFromNamespace"; - // there is no standard way to get the namespace name, hence - // we won't be testing for it - return "logFromNamespace"; - } + std::string logFromNamespace(bool id) + { + LL_INFOS() << (id ? "Foo::logFromNamespace: " : "") << "hi" << LL_ENDL; + //return "Foo::logFromNamespace"; + // there is no standard way to get the namespace name, hence + // we won't be testing for it + return "logFromNamespace"; + } } namespace { - class ClassWithNoLogType { - public: - std::string logFromMember(bool id) - { - LL_INFOS() << (id ? "ClassWithNoLogType::logFromMember: " : "") << "hi" << LL_ENDL; - return "ClassWithNoLogType::logFromMember"; - } - static std::string logFromStatic(bool id) - { - LL_INFOS() << (id ? "ClassWithNoLogType::logFromStatic: " : "") << "hi" << LL_ENDL; - return "ClassWithNoLogType::logFromStatic"; - } - }; - - class ClassWithLogType { - LOG_CLASS(ClassWithLogType); - public: - std::string logFromMember(bool id) - { - LL_INFOS() << (id ? "ClassWithLogType::logFromMember: " : "") << "hi" << LL_ENDL; - return "ClassWithLogType::logFromMember"; - } - static std::string logFromStatic(bool id) - { - LL_INFOS() << (id ? "ClassWithLogType::logFromStatic: " : "") << "hi" << LL_ENDL; - return "ClassWithLogType::logFromStatic"; - } - }; - - std::string logFromNamespace(bool id) { return Foo::logFromNamespace(id); } - std::string logFromClassWithLogTypeMember(bool id) { ClassWithLogType c; return c.logFromMember(id); } - std::string logFromClassWithLogTypeStatic(bool id) { return ClassWithLogType::logFromStatic(id); } - - void ensure_has(const std::string& message, - const std::string& actual, const std::string& expected) - { - std::string::size_type n1 = actual.find(expected); - if (n1 == std::string::npos) - { - std::stringstream ss; - ss << message << ": " << "expected to find a copy of '" << expected - << "' in actual '" << actual << "'"; - throw tut::failure(ss.str().c_str()); - } - } - - typedef std::string (*LogFromFunction)(bool); - void testLogName(LLError::RecorderPtr recorder, LogFromFunction f, - const std::string& class_name = "") - { - std::dynamic_pointer_cast<tut::TestRecorder>(recorder)->clearMessages(); - std::string name = f(false); - f(true); - - std::string messageWithoutName = std::dynamic_pointer_cast<tut::TestRecorder>(recorder)->message(0); - std::string messageWithName = std::dynamic_pointer_cast<tut::TestRecorder>(recorder)->message(1); - - ensure_has(name + " logged without name", - messageWithoutName, name); - ensure_has(name + " logged with name", - messageWithName, name); - - if (!class_name.empty()) - { - ensure_has(name + "logged without name", - messageWithoutName, class_name); - ensure_has(name + "logged with name", - messageWithName, class_name); - } - } + class ClassWithNoLogType { + public: + std::string logFromMember(bool id) + { + LL_INFOS() << (id ? "ClassWithNoLogType::logFromMember: " : "") << "hi" << LL_ENDL; + return "ClassWithNoLogType::logFromMember"; + } + static std::string logFromStatic(bool id) + { + LL_INFOS() << (id ? "ClassWithNoLogType::logFromStatic: " : "") << "hi" << LL_ENDL; + return "ClassWithNoLogType::logFromStatic"; + } + }; + + class ClassWithLogType { + LOG_CLASS(ClassWithLogType); + public: + std::string logFromMember(bool id) + { + LL_INFOS() << (id ? "ClassWithLogType::logFromMember: " : "") << "hi" << LL_ENDL; + return "ClassWithLogType::logFromMember"; + } + static std::string logFromStatic(bool id) + { + LL_INFOS() << (id ? "ClassWithLogType::logFromStatic: " : "") << "hi" << LL_ENDL; + return "ClassWithLogType::logFromStatic"; + } + }; + + std::string logFromNamespace(bool id) { return Foo::logFromNamespace(id); } + std::string logFromClassWithLogTypeMember(bool id) { ClassWithLogType c; return c.logFromMember(id); } + std::string logFromClassWithLogTypeStatic(bool id) { return ClassWithLogType::logFromStatic(id); } + + void ensure_has(const std::string& message, + const std::string& actual, const std::string& expected) + { + std::string::size_type n1 = actual.find(expected); + if (n1 == std::string::npos) + { + std::stringstream ss; + ss << message << ": " << "expected to find a copy of '" << expected + << "' in actual '" << actual << "'"; + throw tut::failure(ss.str().c_str()); + } + } + + typedef std::string (*LogFromFunction)(bool); + void testLogName(LLError::RecorderPtr recorder, LogFromFunction f, + const std::string& class_name = "") + { + std::dynamic_pointer_cast<tut::TestRecorder>(recorder)->clearMessages(); + std::string name = f(false); + f(true); + + std::string messageWithoutName = std::dynamic_pointer_cast<tut::TestRecorder>(recorder)->message(0); + std::string messageWithName = std::dynamic_pointer_cast<tut::TestRecorder>(recorder)->message(1); + + ensure_has(name + " logged without name", + messageWithoutName, name); + ensure_has(name + " logged with name", + messageWithName, name); + + if (!class_name.empty()) + { + ensure_has(name + "logged without name", + messageWithoutName, class_name); + ensure_has(name + "logged with name", + messageWithName, class_name); + } + } } namespace @@ -540,7 +540,7 @@ namespace tut // backslash, return, and newline are not escaped with backslashes { LLError::setDefaultLevel(LLError::LEVEL_DEBUG); - setWantsMultiline(true); + setWantsMultiline(true); writeMsgNeedsEscaping(); // but should not be now ensure_message_field_equals(0, MSG_FIELD, "backslash\\"); ensure_message_field_equals(1, MSG_FIELD, "newline\nafternewline"); @@ -554,324 +554,324 @@ namespace tut namespace tut { - template<> template<> - // class/function information in output - void ErrorTestObject::test<6>() - { - testLogName(mRecorder, logFromGlobal); - testLogName(mRecorder, logFromStatic); - testLogName(mRecorder, logFromAnon); - testLogName(mRecorder, logFromNamespace); - testLogName(mRecorder, logFromClassWithLogTypeMember, "ClassWithLogType"); - testLogName(mRecorder, logFromClassWithLogTypeStatic, "ClassWithLogType"); - } + template<> template<> + // class/function information in output + void ErrorTestObject::test<6>() + { + testLogName(mRecorder, logFromGlobal); + testLogName(mRecorder, logFromStatic); + testLogName(mRecorder, logFromAnon); + testLogName(mRecorder, logFromNamespace); + testLogName(mRecorder, logFromClassWithLogTypeMember, "ClassWithLogType"); + testLogName(mRecorder, logFromClassWithLogTypeStatic, "ClassWithLogType"); + } } namespace { - std::string innerLogger() - { - LL_INFOS() << "inside" << LL_ENDL; - return "moo"; - } - - std::string outerLogger() - { - LL_INFOS() << "outside(" << innerLogger() << ")" << LL_ENDL; - return "bar"; - } - - class LogWhileLogging - { - public: - void print(std::ostream& out) const - { - LL_INFOS() << "logging" << LL_ENDL; - out << "baz"; - } - }; - - std::ostream& operator<<(std::ostream& out, const LogWhileLogging& l) - { l.print(out); return out; } - - void metaLogger() - { - LogWhileLogging l; - LL_INFOS() << "meta(" << l << ")" << LL_ENDL; - } + std::string innerLogger() + { + LL_INFOS() << "inside" << LL_ENDL; + return "moo"; + } + + std::string outerLogger() + { + LL_INFOS() << "outside(" << innerLogger() << ")" << LL_ENDL; + return "bar"; + } + + class LogWhileLogging + { + public: + void print(std::ostream& out) const + { + LL_INFOS() << "logging" << LL_ENDL; + out << "baz"; + } + }; + + std::ostream& operator<<(std::ostream& out, const LogWhileLogging& l) + { l.print(out); return out; } + + void metaLogger() + { + LogWhileLogging l; + LL_INFOS() << "meta(" << l << ")" << LL_ENDL; + } } namespace tut { - template<> template<> - // handle nested logging - void ErrorTestObject::test<7>() - { - outerLogger(); - ensure_message_field_equals(0, MSG_FIELD, "inside"); - ensure_message_field_equals(1, MSG_FIELD, "outside(moo)"); - ensure_message_count(2); - - metaLogger(); - ensure_message_field_equals(2, MSG_FIELD, "logging"); - ensure_message_field_equals(3, MSG_FIELD, "meta(baz)"); - ensure_message_count(4); - } - - template<> template<> - // special handling of LL_ERRS() calls - void ErrorTestObject::test<8>() - { - std::string location = errorReturningLocation(); - - ensure_message_field_equals(0, LOCATION_FIELD, location); - ensure_message_field_equals(0, MSG_FIELD, "die"); - ensure_message_count(1); - - ensure("fatal callback called", fatalWasCalled); - } + template<> template<> + // handle nested logging + void ErrorTestObject::test<7>() + { + outerLogger(); + ensure_message_field_equals(0, MSG_FIELD, "inside"); + ensure_message_field_equals(1, MSG_FIELD, "outside(moo)"); + ensure_message_count(2); + + metaLogger(); + ensure_message_field_equals(2, MSG_FIELD, "logging"); + ensure_message_field_equals(3, MSG_FIELD, "meta(baz)"); + ensure_message_count(4); + } + + template<> template<> + // special handling of LL_ERRS() calls + void ErrorTestObject::test<8>() + { + std::string location = errorReturningLocation(); + + ensure_message_field_equals(0, LOCATION_FIELD, location); + ensure_message_field_equals(0, MSG_FIELD, "die"); + ensure_message_count(1); + + ensure("fatal callback called", fatalWasCalled); + } } namespace { - std::string roswell() - { - return "1947-07-08T03:04:05Z"; - } - - void ufoSighting() - { - LL_INFOS() << "ufo" << LL_ENDL; - } + std::string roswell() + { + return "1947-07-08T03:04:05Z"; + } + + void ufoSighting() + { + LL_INFOS() << "ufo" << LL_ENDL; + } } namespace tut { - template<> template<> - // time in output (for recorders that need it) - void ErrorTestObject::test<9>() - { - LLError::setTimeFunction(roswell); - - setWantsTime(false); - ufoSighting(); - ensure_message_field_equals(0, MSG_FIELD, "ufo"); - ensure_message_does_not_contain(0, roswell()); - - setWantsTime(true); - ufoSighting(); - ensure_message_field_equals(1, MSG_FIELD, "ufo"); - ensure_message_field_equals(1, TIME_FIELD, roswell()); - } - - template<> template<> - // output order - void ErrorTestObject::test<10>() - { - LLError::setTimeFunction(roswell); - setWantsTime(true); - - std::string location, - function; - writeReturningLocationAndFunction(location, function); - - ensure_equals("order is time level tags location function message", + template<> template<> + // time in output (for recorders that need it) + void ErrorTestObject::test<9>() + { + LLError::setTimeFunction(roswell); + + setWantsTime(false); + ufoSighting(); + ensure_message_field_equals(0, MSG_FIELD, "ufo"); + ensure_message_does_not_contain(0, roswell()); + + setWantsTime(true); + ufoSighting(); + ensure_message_field_equals(1, MSG_FIELD, "ufo"); + ensure_message_field_equals(1, TIME_FIELD, roswell()); + } + + template<> template<> + // output order + void ErrorTestObject::test<10>() + { + LLError::setTimeFunction(roswell); + setWantsTime(true); + + std::string location, + function; + writeReturningLocationAndFunction(location, function); + + ensure_equals("order is time level tags location function message", message(0), roswell() + " INFO " + "# " /* no tag */ + location + " " + function + " : " + "apple"); - } + } - template<> template<> - // multiple recorders - void ErrorTestObject::test<11>() - { - LLError::RecorderPtr altRecorder(new TestRecorder()); - LLError::addRecorder(altRecorder); + template<> template<> + // multiple recorders + void ErrorTestObject::test<11>() + { + LLError::RecorderPtr altRecorder(new TestRecorder()); + LLError::addRecorder(altRecorder); - LL_INFOS() << "boo" << LL_ENDL; + LL_INFOS() << "boo" << LL_ENDL; - ensure_message_field_equals(0, MSG_FIELD, "boo"); - ensure_equals("alt recorder count", std::dynamic_pointer_cast<TestRecorder>(altRecorder)->countMessages(), 1); - ensure_contains("alt recorder message 0", std::dynamic_pointer_cast<TestRecorder>(altRecorder)->message(0), "boo"); + ensure_message_field_equals(0, MSG_FIELD, "boo"); + ensure_equals("alt recorder count", std::dynamic_pointer_cast<TestRecorder>(altRecorder)->countMessages(), 1); + ensure_contains("alt recorder message 0", std::dynamic_pointer_cast<TestRecorder>(altRecorder)->message(0), "boo"); - LLError::setTimeFunction(roswell); + LLError::setTimeFunction(roswell); - LLError::RecorderPtr anotherRecorder(new TestRecorder()); - std::dynamic_pointer_cast<TestRecorder>(anotherRecorder)->showTime(true); - LLError::addRecorder(anotherRecorder); + LLError::RecorderPtr anotherRecorder(new TestRecorder()); + std::dynamic_pointer_cast<TestRecorder>(anotherRecorder)->showTime(true); + LLError::addRecorder(anotherRecorder); - LL_INFOS() << "baz" << LL_ENDL; + LL_INFOS() << "baz" << LL_ENDL; - std::string when = roswell(); + std::string when = roswell(); - ensure_message_does_not_contain(1, when); - ensure_equals("alt recorder count", std::dynamic_pointer_cast<TestRecorder>(altRecorder)->countMessages(), 2); - ensure_does_not_contain("alt recorder message 1", std::dynamic_pointer_cast<TestRecorder>(altRecorder)->message(1), when); - ensure_equals("another recorder count", std::dynamic_pointer_cast<TestRecorder>(anotherRecorder)->countMessages(), 1); - ensure_contains("another recorder message 0", std::dynamic_pointer_cast<TestRecorder>(anotherRecorder)->message(0), when); + ensure_message_does_not_contain(1, when); + ensure_equals("alt recorder count", std::dynamic_pointer_cast<TestRecorder>(altRecorder)->countMessages(), 2); + ensure_does_not_contain("alt recorder message 1", std::dynamic_pointer_cast<TestRecorder>(altRecorder)->message(1), when); + ensure_equals("another recorder count", std::dynamic_pointer_cast<TestRecorder>(anotherRecorder)->countMessages(), 1); + ensure_contains("another recorder message 0", std::dynamic_pointer_cast<TestRecorder>(anotherRecorder)->message(0), when); - LLError::removeRecorder(altRecorder); - LLError::removeRecorder(anotherRecorder); - } + LLError::removeRecorder(altRecorder); + LLError::removeRecorder(anotherRecorder); + } } class TestAlpha { - LOG_CLASS(TestAlpha); + LOG_CLASS(TestAlpha); public: - static void doDebug() { LL_DEBUGS() << "add dice" << LL_ENDL; } - static void doInfo() { LL_INFOS() << "any idea" << LL_ENDL; } - static void doWarn() { LL_WARNS() << "aim west" << LL_ENDL; } - static void doError() { CATCH(LL_ERRS(), "ate eels"); } - static void doAll() { doDebug(); doInfo(); doWarn(); doError(); } + static void doDebug() { LL_DEBUGS() << "add dice" << LL_ENDL; } + static void doInfo() { LL_INFOS() << "any idea" << LL_ENDL; } + static void doWarn() { LL_WARNS() << "aim west" << LL_ENDL; } + static void doError() { CATCH(LL_ERRS(), "ate eels"); } + static void doAll() { doDebug(); doInfo(); doWarn(); doError(); } }; class TestBeta { - LOG_CLASS(TestBeta); + LOG_CLASS(TestBeta); public: - static void doDebug() { LL_DEBUGS() << "bed down" << LL_ENDL; } - static void doInfo() { LL_INFOS() << "buy iron" << LL_ENDL; } - static void doWarn() { LL_WARNS() << "bad word" << LL_ENDL; } - static void doError() { CATCH(LL_ERRS(), "big easy"); } - static void doAll() { doDebug(); doInfo(); doWarn(); doError(); } + static void doDebug() { LL_DEBUGS() << "bed down" << LL_ENDL; } + static void doInfo() { LL_INFOS() << "buy iron" << LL_ENDL; } + static void doWarn() { LL_WARNS() << "bad word" << LL_ENDL; } + static void doError() { CATCH(LL_ERRS(), "big easy"); } + static void doAll() { doDebug(); doInfo(); doWarn(); doError(); } }; namespace tut { - template<> template<> - // filtering by class - void ErrorTestObject::test<12>() - { - LLError::setDefaultLevel(LLError::LEVEL_WARN); - LLError::setClassLevel("TestBeta", LLError::LEVEL_INFO); - - TestAlpha::doAll(); - TestBeta::doAll(); - - ensure_message_field_equals(0, MSG_FIELD, "aim west"); - ensure_message_field_equals(1, MSG_FIELD, "ate eels"); - ensure_message_field_equals(2, MSG_FIELD, "buy iron"); - ensure_message_field_equals(3, MSG_FIELD, "bad word"); - ensure_message_field_equals(4, MSG_FIELD, "big easy"); - ensure_message_count(5); - } - - template<> template<> - // filtering by function, and that it will override class filtering - void ErrorTestObject::test<13>() - { - LLError::setDefaultLevel(LLError::LEVEL_DEBUG); - LLError::setClassLevel("TestBeta", LLError::LEVEL_WARN); - LLError::setFunctionLevel("TestBeta::doInfo", LLError::LEVEL_DEBUG); - LLError::setFunctionLevel("TestBeta::doError", LLError::LEVEL_NONE); - - TestBeta::doAll(); - ensure_message_field_equals(0, MSG_FIELD, "buy iron"); - ensure_message_field_equals(1, MSG_FIELD, "bad word"); - ensure_message_count(2); - } - - template<> template<> - // filtering by file - // and that it is overridden by both class and function filtering - void ErrorTestObject::test<14>() - { - LLError::setDefaultLevel(LLError::LEVEL_DEBUG); - LLError::setFileLevel(LLError::abbreviateFile(__FILE__), - LLError::LEVEL_WARN); - LLError::setClassLevel("TestAlpha", LLError::LEVEL_INFO); - LLError::setFunctionLevel("TestAlpha::doError", - LLError::LEVEL_NONE); - LLError::setFunctionLevel("TestBeta::doError", - LLError::LEVEL_NONE); - - TestAlpha::doAll(); - TestBeta::doAll(); - ensure_message_field_equals(0, MSG_FIELD, "any idea"); - ensure_message_field_equals(1, MSG_FIELD, "aim west"); - ensure_message_field_equals(2, MSG_FIELD, "bad word"); - ensure_message_count(3); - } - - template<> template<> - // proper cached, efficient lookup of filtering - void ErrorTestObject::test<15>() - { - LLError::setDefaultLevel(LLError::LEVEL_NONE); - - TestAlpha::doInfo(); - ensure_message_count(0); - ensure_equals("first check", LLError::shouldLogCallCount(), 1); - TestAlpha::doInfo(); - ensure_message_count(0); - ensure_equals("second check", LLError::shouldLogCallCount(), 1); - - LLError::setClassLevel("TestAlpha", LLError::LEVEL_DEBUG); - TestAlpha::doInfo(); - ensure_message_count(1); - ensure_equals("third check", LLError::shouldLogCallCount(), 2); - TestAlpha::doInfo(); - ensure_message_count(2); - ensure_equals("fourth check", LLError::shouldLogCallCount(), 2); - - LLError::setClassLevel("TestAlpha", LLError::LEVEL_WARN); - TestAlpha::doInfo(); - ensure_message_count(2); - ensure_equals("fifth check", LLError::shouldLogCallCount(), 3); - TestAlpha::doInfo(); - ensure_message_count(2); - ensure_equals("sixth check", LLError::shouldLogCallCount(), 3); - } - - template<> template<> - // configuration from LLSD - void ErrorTestObject::test<16>() - { - LLSD config; - config["print-location"] = true; - config["default-level"] = "DEBUG"; - - LLSD set1; - set1["level"] = "WARN"; + template<> template<> + // filtering by class + void ErrorTestObject::test<12>() + { + LLError::setDefaultLevel(LLError::LEVEL_WARN); + LLError::setClassLevel("TestBeta", LLError::LEVEL_INFO); + + TestAlpha::doAll(); + TestBeta::doAll(); + + ensure_message_field_equals(0, MSG_FIELD, "aim west"); + ensure_message_field_equals(1, MSG_FIELD, "ate eels"); + ensure_message_field_equals(2, MSG_FIELD, "buy iron"); + ensure_message_field_equals(3, MSG_FIELD, "bad word"); + ensure_message_field_equals(4, MSG_FIELD, "big easy"); + ensure_message_count(5); + } + + template<> template<> + // filtering by function, and that it will override class filtering + void ErrorTestObject::test<13>() + { + LLError::setDefaultLevel(LLError::LEVEL_DEBUG); + LLError::setClassLevel("TestBeta", LLError::LEVEL_WARN); + LLError::setFunctionLevel("TestBeta::doInfo", LLError::LEVEL_DEBUG); + LLError::setFunctionLevel("TestBeta::doError", LLError::LEVEL_NONE); + + TestBeta::doAll(); + ensure_message_field_equals(0, MSG_FIELD, "buy iron"); + ensure_message_field_equals(1, MSG_FIELD, "bad word"); + ensure_message_count(2); + } + + template<> template<> + // filtering by file + // and that it is overridden by both class and function filtering + void ErrorTestObject::test<14>() + { + LLError::setDefaultLevel(LLError::LEVEL_DEBUG); + LLError::setFileLevel(LLError::abbreviateFile(__FILE__), + LLError::LEVEL_WARN); + LLError::setClassLevel("TestAlpha", LLError::LEVEL_INFO); + LLError::setFunctionLevel("TestAlpha::doError", + LLError::LEVEL_NONE); + LLError::setFunctionLevel("TestBeta::doError", + LLError::LEVEL_NONE); + + TestAlpha::doAll(); + TestBeta::doAll(); + ensure_message_field_equals(0, MSG_FIELD, "any idea"); + ensure_message_field_equals(1, MSG_FIELD, "aim west"); + ensure_message_field_equals(2, MSG_FIELD, "bad word"); + ensure_message_count(3); + } + + template<> template<> + // proper cached, efficient lookup of filtering + void ErrorTestObject::test<15>() + { + LLError::setDefaultLevel(LLError::LEVEL_NONE); + + TestAlpha::doInfo(); + ensure_message_count(0); + ensure_equals("first check", LLError::shouldLogCallCount(), 1); + TestAlpha::doInfo(); + ensure_message_count(0); + ensure_equals("second check", LLError::shouldLogCallCount(), 1); + + LLError::setClassLevel("TestAlpha", LLError::LEVEL_DEBUG); + TestAlpha::doInfo(); + ensure_message_count(1); + ensure_equals("third check", LLError::shouldLogCallCount(), 2); + TestAlpha::doInfo(); + ensure_message_count(2); + ensure_equals("fourth check", LLError::shouldLogCallCount(), 2); + + LLError::setClassLevel("TestAlpha", LLError::LEVEL_WARN); + TestAlpha::doInfo(); + ensure_message_count(2); + ensure_equals("fifth check", LLError::shouldLogCallCount(), 3); + TestAlpha::doInfo(); + ensure_message_count(2); + ensure_equals("sixth check", LLError::shouldLogCallCount(), 3); + } + + template<> template<> + // configuration from LLSD + void ErrorTestObject::test<16>() + { + LLSD config; + config["print-location"] = true; + config["default-level"] = "DEBUG"; + + LLSD set1; + set1["level"] = "WARN"; set1["files"][0] = LLError::abbreviateFile(__FILE__); - LLSD set2; - set2["level"] = "INFO"; - set2["classes"][0] = "TestAlpha"; - - LLSD set3; - set3["level"] = "NONE"; - set3["functions"][0] = "TestAlpha::doError"; - set3["functions"][1] = "TestBeta::doError"; - - config["settings"][0] = set1; - config["settings"][1] = set2; - config["settings"][2] = set3; - - LLError::configure(config); - - TestAlpha::doAll(); - TestBeta::doAll(); - ensure_message_field_equals(0, MSG_FIELD, "any idea"); - ensure_message_field_equals(1, MSG_FIELD, "aim west"); - ensure_message_field_equals(2, MSG_FIELD, "bad word"); - ensure_message_count(3); - - // make sure reconfiguring works - LLSD config2; - config2["default-level"] = "WARN"; - - LLError::configure(config2); - - TestAlpha::doAll(); - TestBeta::doAll(); - ensure_message_field_equals(3, MSG_FIELD, "aim west"); - ensure_message_field_equals(4, MSG_FIELD, "ate eels"); - ensure_message_field_equals(5, MSG_FIELD, "bad word"); - ensure_message_field_equals(6, MSG_FIELD, "big easy"); - ensure_message_count(7); - } + LLSD set2; + set2["level"] = "INFO"; + set2["classes"][0] = "TestAlpha"; + + LLSD set3; + set3["level"] = "NONE"; + set3["functions"][0] = "TestAlpha::doError"; + set3["functions"][1] = "TestBeta::doError"; + + config["settings"][0] = set1; + config["settings"][1] = set2; + config["settings"][2] = set3; + + LLError::configure(config); + + TestAlpha::doAll(); + TestBeta::doAll(); + ensure_message_field_equals(0, MSG_FIELD, "any idea"); + ensure_message_field_equals(1, MSG_FIELD, "aim west"); + ensure_message_field_equals(2, MSG_FIELD, "bad word"); + ensure_message_count(3); + + // make sure reconfiguring works + LLSD config2; + config2["default-level"] = "WARN"; + + LLError::configure(config2); + + TestAlpha::doAll(); + TestBeta::doAll(); + ensure_message_field_equals(3, MSG_FIELD, "aim west"); + ensure_message_field_equals(4, MSG_FIELD, "ate eels"); + ensure_message_field_equals(5, MSG_FIELD, "bad word"); + ensure_message_field_equals(6, MSG_FIELD, "big easy"); + ensure_message_count(7); + } } namespace tut @@ -919,16 +919,16 @@ namespace tut } /* Tests left: - handling of classes without LOG_CLASS + handling of classes without LOG_CLASS - live update of filtering from file + live update of filtering from file - syslog recorder - file recorder - cerr/stderr recorder - fixed buffer recorder - windows recorder + syslog recorder + file recorder + cerr/stderr recorder + fixed buffer recorder + windows recorder - mutex use when logging (?) - strange careful about to crash handling (?) + mutex use when logging (?) + strange careful about to crash handling (?) */ diff --git a/indra/llcommon/tests/lleventcoro_test.cpp b/indra/llcommon/tests/lleventcoro_test.cpp index 01104545c6..a3c54ffaa2 100644 --- a/indra/llcommon/tests/lleventcoro_test.cpp +++ b/indra/llcommon/tests/lleventcoro_test.cpp @@ -3,25 +3,25 @@ * @author Nat Goodspeed * @date 2009-04-22 * @brief Test for coroutine. - * + * * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llcommon/tests/lleventdispatcher_test.cpp b/indra/llcommon/tests/lleventdispatcher_test.cpp index b0c532887c..a99acba848 100644 --- a/indra/llcommon/tests/lleventdispatcher_test.cpp +++ b/indra/llcommon/tests/lleventdispatcher_test.cpp @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2011-01-20 * @brief Test for lleventdispatcher. - * + * * $LicenseInfo:firstyear=2011&license=viewerlgpl$ * Copyright (c) 2011, Linden Research, Inc. * $/LicenseInfo$ @@ -470,7 +470,7 @@ namespace tut params["a"], "\n" "params[\"b\"]:\n", params["b"]); - // default LLSD::Binary value + // default LLSD::Binary value std::vector<U8> binary; for (size_t ix = 0, h = 0xaa; ix < 6; ++ix, h += 0x11) { diff --git a/indra/llcommon/tests/lleventfilter_test.cpp b/indra/llcommon/tests/lleventfilter_test.cpp index fa2cb03e95..a01d7fe415 100644 --- a/indra/llcommon/tests/lleventfilter_test.cpp +++ b/indra/llcommon/tests/lleventfilter_test.cpp @@ -3,25 +3,25 @@ * @author Nat Goodspeed * @date 2009-03-06 * @brief Test for lleventfilter. - * + * * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llcommon/tests/llframetimer_test.cpp b/indra/llcommon/tests/llframetimer_test.cpp index be372bb855..b9a8c91abf 100644 --- a/indra/llcommon/tests/llframetimer_test.cpp +++ b/indra/llcommon/tests/llframetimer_test.cpp @@ -1,4 +1,4 @@ -/** +/** * @file lltiming_test.cpp * @date 2006-07-23 * @brief Tests the timers. @@ -6,21 +6,21 @@ * $LicenseInfo:firstyear=2006&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -34,88 +34,88 @@ namespace tut { - struct frametimer_test - { - frametimer_test() - { - LLFrameTimer::updateFrameTime(); - } - }; - typedef test_group<frametimer_test> frametimer_group_t; - typedef frametimer_group_t::object frametimer_object_t; - tut::frametimer_group_t frametimer_instance("LLFrameTimer"); + struct frametimer_test + { + frametimer_test() + { + LLFrameTimer::updateFrameTime(); + } + }; + typedef test_group<frametimer_test> frametimer_group_t; + typedef frametimer_group_t::object frametimer_object_t; + tut::frametimer_group_t frametimer_instance("LLFrameTimer"); + + template<> template<> + void frametimer_object_t::test<1>() + { + F64 seconds_since_epoch = LLFrameTimer::getTotalSeconds(); + LLFrameTimer timer; + timer.setExpiryAt(seconds_since_epoch); + F64 expires_at = timer.expiresAt(); + ensure_distance( + "set expiry matches get expiry", + expires_at, + seconds_since_epoch, + 0.001); + } - template<> template<> - void frametimer_object_t::test<1>() - { - F64 seconds_since_epoch = LLFrameTimer::getTotalSeconds(); - LLFrameTimer timer; - timer.setExpiryAt(seconds_since_epoch); - F64 expires_at = timer.expiresAt(); - ensure_distance( - "set expiry matches get expiry", - expires_at, - seconds_since_epoch, - 0.001); - } + template<> template<> + void frametimer_object_t::test<2>() + { + F64 seconds_since_epoch = LLFrameTimer::getTotalSeconds(); + seconds_since_epoch += 10.0; + LLFrameTimer timer; + timer.setExpiryAt(seconds_since_epoch); + F64 expires_at = timer.expiresAt(); + ensure_distance( + "set expiry matches get expiry 1", + expires_at, + seconds_since_epoch, + 0.001); + seconds_since_epoch += 10.0; + timer.setExpiryAt(seconds_since_epoch); + expires_at = timer.expiresAt(); + ensure_distance( + "set expiry matches get expiry 2", + expires_at, + seconds_since_epoch, + 0.001); + } + template<> template<> + void frametimer_object_t::test<3>() + { + clock_t t1 = clock(); + ms_sleep(200); + clock_t t2 = clock(); + clock_t elapsed = t2 - t1 + 1; + std::cout << "Note: using clock(), ms_sleep() actually took " << (long)elapsed << "ms" << std::endl; - template<> template<> - void frametimer_object_t::test<2>() - { - F64 seconds_since_epoch = LLFrameTimer::getTotalSeconds(); - seconds_since_epoch += 10.0; - LLFrameTimer timer; - timer.setExpiryAt(seconds_since_epoch); - F64 expires_at = timer.expiresAt(); - ensure_distance( - "set expiry matches get expiry 1", - expires_at, - seconds_since_epoch, - 0.001); - seconds_since_epoch += 10.0; - timer.setExpiryAt(seconds_since_epoch); - expires_at = timer.expiresAt(); - ensure_distance( - "set expiry matches get expiry 2", - expires_at, - seconds_since_epoch, - 0.001); - } - template<> template<> - void frametimer_object_t::test<3>() - { - clock_t t1 = clock(); - ms_sleep(200); - clock_t t2 = clock(); - clock_t elapsed = t2 - t1 + 1; - std::cout << "Note: using clock(), ms_sleep() actually took " << (long)elapsed << "ms" << std::endl; + F64 seconds_since_epoch = LLFrameTimer::getTotalSeconds(); + seconds_since_epoch += 2.0; + LLFrameTimer timer; + timer.setExpiryAt(seconds_since_epoch); + /* + * Note that the ms_sleep(200) below is only guaranteed to return + * in 200ms _or_more_, so it should be true that by the 10th + * iteration we've gotten to the 2 seconds requested above + * and the timer should expire, but it can expire in fewer iterations + * if one or more of the ms_sleep calls takes longer. + * (as it did when we moved to Mac OS X 10.10) + */ + int iterations_until_expiration = 0; + while ( !timer.hasExpired() ) + { + ms_sleep(200); + LLFrameTimer::updateFrameTime(); + iterations_until_expiration++; + } + ensure("timer took too long to expire", iterations_until_expiration <= 10); + } - F64 seconds_since_epoch = LLFrameTimer::getTotalSeconds(); - seconds_since_epoch += 2.0; - LLFrameTimer timer; - timer.setExpiryAt(seconds_since_epoch); - /* - * Note that the ms_sleep(200) below is only guaranteed to return - * in 200ms _or_more_, so it should be true that by the 10th - * iteration we've gotten to the 2 seconds requested above - * and the timer should expire, but it can expire in fewer iterations - * if one or more of the ms_sleep calls takes longer. - * (as it did when we moved to Mac OS X 10.10) - */ - int iterations_until_expiration = 0; - while ( !timer.hasExpired() ) - { - ms_sleep(200); - LLFrameTimer::updateFrameTime(); - iterations_until_expiration++; - } - ensure("timer took too long to expire", iterations_until_expiration <= 10); - } - /* - template<> template<> - void frametimer_object_t::test<4>() - { - } + template<> template<> + void frametimer_object_t::test<4>() + { + } */ } diff --git a/indra/llcommon/tests/llheteromap_test.cpp b/indra/llcommon/tests/llheteromap_test.cpp index 686bffb878..cabfb91593 100644 --- a/indra/llcommon/tests/llheteromap_test.cpp +++ b/indra/llcommon/tests/llheteromap_test.cpp @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2016-10-12 * @brief Test for llheteromap. - * + * * $LicenseInfo:firstyear=2016&license=viewerlgpl$ * Copyright (c) 2016, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/tests/llinstancetracker_test.cpp b/indra/llcommon/tests/llinstancetracker_test.cpp index 95af9c2a50..c6eb0fdf75 100644 --- a/indra/llcommon/tests/llinstancetracker_test.cpp +++ b/indra/llcommon/tests/llinstancetracker_test.cpp @@ -3,25 +3,25 @@ * @author Nat Goodspeed * @date 2009-11-10 * @brief Test for llinstancetracker. - * + * * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -170,7 +170,7 @@ namespace tut { Unkeyed one, two, three; typedef std::set<Unkeyed*> KeySet; - + KeySet instances; instances.insert(&one); instances.insert(&two); diff --git a/indra/llcommon/tests/lllazy_test.cpp b/indra/llcommon/tests/lllazy_test.cpp index 542306ee22..923fe952a9 100644 --- a/indra/llcommon/tests/lllazy_test.cpp +++ b/indra/llcommon/tests/lllazy_test.cpp @@ -3,25 +3,25 @@ * @author Nat Goodspeed * @date 2009-01-28 * @brief Tests of lllazy.h. - * + * * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llcommon/tests/llleap_test.cpp b/indra/llcommon/tests/llleap_test.cpp index 7197dedfbf..fa48bcdefd 100644 --- a/indra/llcommon/tests/llleap_test.cpp +++ b/indra/llcommon/tests/llleap_test.cpp @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2012-02-21 * @brief Test for llleap. - * + * * $LicenseInfo:firstyear=2012&license=viewerlgpl$ * Copyright (c) 2012, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/tests/llmainthreadtask_test.cpp b/indra/llcommon/tests/llmainthreadtask_test.cpp index 69b11ccafb..9ccf391327 100644 --- a/indra/llcommon/tests/llmainthreadtask_test.cpp +++ b/indra/llcommon/tests/llmainthreadtask_test.cpp @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2019-12-05 * @brief Test for llmainthreadtask. - * + * * $LicenseInfo:firstyear=2019&license=viewerlgpl$ * Copyright (c) 2019, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/tests/llmemtype_test.cpp b/indra/llcommon/tests/llmemtype_test.cpp index 1f050d6dc7..2d64d342ae 100644 --- a/indra/llcommon/tests/llmemtype_test.cpp +++ b/indra/llcommon/tests/llmemtype_test.cpp @@ -3,25 +3,25 @@ * @author Palmer Truelson * @date 2008-03- * @brief Test for llmemtype.cpp. - * + * * $LicenseInfo:firstyear=2009&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -37,14 +37,14 @@ std::stack<S32> memTypeStack; void LLAllocator::pushMemType(S32 i) { - memTypeStack.push(i); + memTypeStack.push(i); } S32 LLAllocator::popMemType(void) { - S32 ret = memTypeStack.top(); - memTypeStack.pop(); - return ret; + S32 ret = memTypeStack.top(); + memTypeStack.pop(); + return ret; } namespace tut @@ -69,49 +69,49 @@ namespace tut ensure("Simplest test ever", true); } - // test with no scripts - template<> template<> - void object::test<2>() - { - { - LLMemType m1(LLMemType::MTYPE_INIT); - } - ensure("Test that you can construct and destruct the mem type"); - } - - // test creation and stack testing - template<> template<> - void object::test<3>() - { - { - ensure("Test that creation and destruction properly inc/dec the stack"); - ensure_equals(memTypeStack.size(), 0); - { - LLMemType m1(LLMemType::MTYPE_INIT); - ensure_equals(memTypeStack.size(), 1); - LLMemType m2(LLMemType::MTYPE_STARTUP); - ensure_equals(memTypeStack.size(), 2); - } - ensure_equals(memTypeStack.size(), 0); - } - } - - // test with no scripts - template<> template<> - void object::test<4>() - { - // catch the begining and end - std::string test_name = LLMemType::getNameFromID(LLMemType::MTYPE_INIT.mID); - ensure_equals("Init name", test_name, "Init"); - - std::string test_name2 = LLMemType::getNameFromID(LLMemType::MTYPE_VOLUME.mID); - ensure_equals("Volume name", test_name2, "Volume"); - - std::string test_name3 = LLMemType::getNameFromID(LLMemType::MTYPE_OTHER.mID); - ensure_equals("Other name", test_name3, "Other"); + // test with no scripts + template<> template<> + void object::test<2>() + { + { + LLMemType m1(LLMemType::MTYPE_INIT); + } + ensure("Test that you can construct and destruct the mem type"); + } + + // test creation and stack testing + template<> template<> + void object::test<3>() + { + { + ensure("Test that creation and destruction properly inc/dec the stack"); + ensure_equals(memTypeStack.size(), 0); + { + LLMemType m1(LLMemType::MTYPE_INIT); + ensure_equals(memTypeStack.size(), 1); + LLMemType m2(LLMemType::MTYPE_STARTUP); + ensure_equals(memTypeStack.size(), 2); + } + ensure_equals(memTypeStack.size(), 0); + } + } + + // test with no scripts + template<> template<> + void object::test<4>() + { + // catch the begining and end + std::string test_name = LLMemType::getNameFromID(LLMemType::MTYPE_INIT.mID); + ensure_equals("Init name", test_name, "Init"); + + std::string test_name2 = LLMemType::getNameFromID(LLMemType::MTYPE_VOLUME.mID); + ensure_equals("Volume name", test_name2, "Volume"); + + std::string test_name3 = LLMemType::getNameFromID(LLMemType::MTYPE_OTHER.mID); + ensure_equals("Other name", test_name3, "Other"); std::string test_name4 = LLMemType::getNameFromID(-1); ensure_equals("Invalid name", test_name4, "INVALID"); - } + } }; diff --git a/indra/llcommon/tests/llpounceable_test.cpp b/indra/llcommon/tests/llpounceable_test.cpp index 2f4915ce11..b3024966c5 100644 --- a/indra/llcommon/tests/llpounceable_test.cpp +++ b/indra/llcommon/tests/llpounceable_test.cpp @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2015-05-22 * @brief Test for llpounceable. - * + * * $LicenseInfo:firstyear=2015&license=viewerlgpl$ * Copyright (c) 2015, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/tests/llprocess_test.cpp b/indra/llcommon/tests/llprocess_test.cpp index 628f046f55..6e8422ca0c 100644 --- a/indra/llcommon/tests/llprocess_test.cpp +++ b/indra/llcommon/tests/llprocess_test.cpp @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2011-12-19 * @brief Test for llprocess. - * + * * $LicenseInfo:firstyear=2011&license=viewerlgpl$ * Copyright (c) 2011, Linden Research, Inc. * $/LicenseInfo$ @@ -1075,7 +1075,7 @@ namespace tut { EventListener(LLEventPump& pump) { - mConnection = + mConnection = pump.listen("EventListener", boost::bind(&EventListener::tick, this, _1)); } diff --git a/indra/llcommon/tests/llprocessor_test.cpp b/indra/llcommon/tests/llprocessor_test.cpp index 884e1b5e5b..a2467d9205 100644 --- a/indra/llcommon/tests/llprocessor_test.cpp +++ b/indra/llcommon/tests/llprocessor_test.cpp @@ -1,25 +1,25 @@ -/** +/** * @file llprocessor_test.cpp * @date 2010-06-01 * * $LicenseInfo:firstyear=2010&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -32,30 +32,30 @@ namespace tut { - struct processor - { - }; - - typedef test_group<processor> processor_t; - typedef processor_t::object processor_object_t; - tut::processor_t tut_processor("LLProcessor"); - - template<> template<> - void processor_object_t::test<1>() - { - set_test_name("LLProcessorInfo regression test"); - - LLProcessorInfo pi; - F64 freq = pi.getCPUFrequency(); - //bool sse = pi.hasSSE(); - //bool sse2 = pi.hasSSE2(); - //bool alitvec = pi.hasAltivec(); - std::string family = pi.getCPUFamilyName(); - std::string brand = pi.getCPUBrandName(); - //std::string steam = pi.getCPUFeatureDescription(); - - ensure_not_equals("Unknown Brand name", brand, "Unknown"); - ensure_not_equals("Unknown Family name", family, "Unknown"); - ensure("Reasonable CPU Frequency > 100 && < 10000", freq > 100 && freq < 10000); - } + struct processor + { + }; + + typedef test_group<processor> processor_t; + typedef processor_t::object processor_object_t; + tut::processor_t tut_processor("LLProcessor"); + + template<> template<> + void processor_object_t::test<1>() + { + set_test_name("LLProcessorInfo regression test"); + + LLProcessorInfo pi; + F64 freq = pi.getCPUFrequency(); + //bool sse = pi.hasSSE(); + //bool sse2 = pi.hasSSE2(); + //bool alitvec = pi.hasAltivec(); + std::string family = pi.getCPUFamilyName(); + std::string brand = pi.getCPUBrandName(); + //std::string steam = pi.getCPUFeatureDescription(); + + ensure_not_equals("Unknown Brand name", brand, "Unknown"); + ensure_not_equals("Unknown Family name", family, "Unknown"); + ensure("Reasonable CPU Frequency > 100 && < 10000", freq > 100 && freq < 10000); + } } diff --git a/indra/llcommon/tests/llprocinfo_test.cpp b/indra/llcommon/tests/llprocinfo_test.cpp index 12d5a695ee..6a151048c2 100644 --- a/indra/llcommon/tests/llprocinfo_test.cpp +++ b/indra/llcommon/tests/llprocinfo_test.cpp @@ -1,25 +1,25 @@ -/** +/** * @file llprocinfo_test.cpp * @brief Tests for the LLProcInfo class. * * $LicenseInfo:firstyear=2013&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2013, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -40,9 +40,9 @@ namespace tut struct procinfo_test { - procinfo_test() - { - } + procinfo_test() + { + } }; typedef test_group<procinfo_test> procinfo_group_t; @@ -54,14 +54,14 @@ tut::procinfo_group_t procinfo_instance("LLProcInfo"); template<> template<> void procinfo_object_t::test<1>() { - LLProcInfo::time_type user(bad_user), system(bad_system); + LLProcInfo::time_type user(bad_user), system(bad_system); + + set_test_name("getCPUUsage() basic function"); - set_test_name("getCPUUsage() basic function"); + LLProcInfo::getCPUUsage(user, system); - LLProcInfo::getCPUUsage(user, system); - - ensure_not_equals("getCPUUsage() writes to its user argument", user, bad_user); - ensure_not_equals("getCPUUsage() writes to its system argument", system, bad_system); + ensure_not_equals("getCPUUsage() writes to its user argument", user, bad_user); + ensure_not_equals("getCPUUsage() writes to its system argument", system, bad_system); } @@ -69,22 +69,22 @@ void procinfo_object_t::test<1>() template<> template<> void procinfo_object_t::test<2>() { - LLProcInfo::time_type user(bad_user), system(bad_system); - LLProcInfo::time_type user2(bad_user), system2(bad_system); - - set_test_name("getCPUUsage() increases over time"); - - LLProcInfo::getCPUUsage(user, system); - - for (int i(0); i < 100000; ++i) - { - ms_sleep(0); - } - - LLProcInfo::getCPUUsage(user2, system2); - - ensure_equals("getCPUUsage() user value doesn't decrease over time", user2 >= user, true); - ensure_equals("getCPUUsage() system value doesn't decrease over time", system2 >= system, true); + LLProcInfo::time_type user(bad_user), system(bad_system); + LLProcInfo::time_type user2(bad_user), system2(bad_system); + + set_test_name("getCPUUsage() increases over time"); + + LLProcInfo::getCPUUsage(user, system); + + for (int i(0); i < 100000; ++i) + { + ms_sleep(0); + } + + LLProcInfo::getCPUUsage(user2, system2); + + ensure_equals("getCPUUsage() user value doesn't decrease over time", user2 >= user, true); + ensure_equals("getCPUUsage() system value doesn't decrease over time", system2 >= system, true); } diff --git a/indra/llcommon/tests/llrand_test.cpp b/indra/llcommon/tests/llrand_test.cpp index ac5a33d0ba..a0dd4ef576 100644 --- a/indra/llcommon/tests/llrand_test.cpp +++ b/indra/llcommon/tests/llrand_test.cpp @@ -1,4 +1,4 @@ -/** +/** * @file llrandom_test.cpp * @author Phoenix * @date 2007-01-25 @@ -6,21 +6,21 @@ * $LicenseInfo:firstyear=2007&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -49,76 +49,76 @@ void ensure_in_range(const std::string_view& name, namespace tut { - struct random - { - }; + struct random + { + }; - typedef test_group<random> random_t; - typedef random_t::object random_object_t; - tut::random_t tut_random("LLSeedRand"); + typedef test_group<random> random_t; + typedef random_t::object random_object_t; + tut::random_t tut_random("LLSeedRand"); - template<> template<> - void random_object_t::test<1>() - { - for(S32 ii = 0; ii < 100000; ++ii) - { - ensure_in_range("frand", ll_frand(), 0.0f, 1.0f); - } - } + template<> template<> + void random_object_t::test<1>() + { + for(S32 ii = 0; ii < 100000; ++ii) + { + ensure_in_range("frand", ll_frand(), 0.0f, 1.0f); + } + } - template<> template<> - void random_object_t::test<2>() - { - for(S32 ii = 0; ii < 100000; ++ii) - { - ensure_in_range("drand", ll_drand(), 0.0, 1.0); - } - } + template<> template<> + void random_object_t::test<2>() + { + for(S32 ii = 0; ii < 100000; ++ii) + { + ensure_in_range("drand", ll_drand(), 0.0, 1.0); + } + } - template<> template<> - void random_object_t::test<3>() - { - for(S32 ii = 0; ii < 100000; ++ii) - { - ensure_in_range("frand(2.0f)", ll_frand(2.0f) - 1.0f, -1.0f, 1.0f); - } - } + template<> template<> + void random_object_t::test<3>() + { + for(S32 ii = 0; ii < 100000; ++ii) + { + ensure_in_range("frand(2.0f)", ll_frand(2.0f) - 1.0f, -1.0f, 1.0f); + } + } - template<> template<> - void random_object_t::test<4>() - { - for(S32 ii = 0; ii < 100000; ++ii) - { - // Negate the result so we don't have to allow a templated low-end - // comparison as well. - ensure_in_range("-frand(-7.0)", -ll_frand(-7.0), 0.0f, 7.0f); - } - } + template<> template<> + void random_object_t::test<4>() + { + for(S32 ii = 0; ii < 100000; ++ii) + { + // Negate the result so we don't have to allow a templated low-end + // comparison as well. + ensure_in_range("-frand(-7.0)", -ll_frand(-7.0), 0.0f, 7.0f); + } + } - template<> template<> - void random_object_t::test<5>() - { - for(S32 ii = 0; ii < 100000; ++ii) - { - ensure_in_range("-drand(-2.0)", -ll_drand(-2.0), 0.0, 2.0); - } - } + template<> template<> + void random_object_t::test<5>() + { + for(S32 ii = 0; ii < 100000; ++ii) + { + ensure_in_range("-drand(-2.0)", -ll_drand(-2.0), 0.0, 2.0); + } + } - template<> template<> - void random_object_t::test<6>() - { - for(S32 ii = 0; ii < 100000; ++ii) - { - ensure_in_range("rand(100)", ll_rand(100), 0, 100); - } - } + template<> template<> + void random_object_t::test<6>() + { + for(S32 ii = 0; ii < 100000; ++ii) + { + ensure_in_range("rand(100)", ll_rand(100), 0, 100); + } + } - template<> template<> - void random_object_t::test<7>() - { - for(S32 ii = 0; ii < 100000; ++ii) - { - ensure_in_range("-rand(-127)", -ll_rand(-127), 0, 127); - } - } + template<> template<> + void random_object_t::test<7>() + { + for(S32 ii = 0; ii < 100000; ++ii) + { + ensure_in_range("-rand(-127)", -ll_rand(-127), 0, 127); + } + } } diff --git a/indra/llcommon/tests/llsdserialize_test.cpp b/indra/llcommon/tests/llsdserialize_test.cpp index ac40125f75..56fdc51e82 100644 --- a/indra/llcommon/tests/llsdserialize_test.cpp +++ b/indra/llcommon/tests/llsdserialize_test.cpp @@ -1,4 +1,4 @@ -/** +/** * @file llsdserialize_test.cpp * @date 2006-04 * @brief LLSDSerialize unit tests @@ -6,21 +6,21 @@ * $LicenseInfo:firstyear=2006&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -64,313 +64,313 @@ typedef std::function<bool(std::istream& istr, LLSD& data, llssize max_bytes)> P std::vector<U8> string_to_vector(const std::string& str) { - return std::vector<U8>(str.begin(), str.end()); + return std::vector<U8>(str.begin(), str.end()); } namespace tut { - struct sd_xml_data - { - sd_xml_data() - { - mFormatter = new LLSDXMLFormatter; - } - LLSD mSD; - LLPointer<LLSDXMLFormatter> mFormatter; - void xml_test(const char* name, const std::string& expected) - { - std::ostringstream ostr; - mFormatter->format(mSD, ostr); - ensure_equals(name, ostr.str(), expected); - } - }; - - typedef test_group<sd_xml_data> sd_xml_test; - typedef sd_xml_test::object sd_xml_object; - tut::sd_xml_test sd_xml_stream("LLSDXMLFormatter"); - - template<> template<> - void sd_xml_object::test<1>() - { - // random atomic tests - std::string expected; - - expected = "<llsd><undef /></llsd>\n"; - xml_test("undef", expected); - - mSD = 3463; - expected = "<llsd><integer>3463</integer></llsd>\n"; - xml_test("integer", expected); - - mSD = ""; - expected = "<llsd><string /></llsd>\n"; - xml_test("empty string", expected); - - mSD = "foobar"; - expected = "<llsd><string>foobar</string></llsd>\n"; - xml_test("string", expected); - - mSD = LLUUID::null; - expected = "<llsd><uuid /></llsd>\n"; - xml_test("null uuid", expected); - - mSD = LLUUID("c96f9b1e-f589-4100-9774-d98643ce0bed"); - expected = "<llsd><uuid>c96f9b1e-f589-4100-9774-d98643ce0bed</uuid></llsd>\n"; - xml_test("uuid", expected); - - mSD = LLURI("https://secondlife.com/login"); - expected = "<llsd><uri>https://secondlife.com/login</uri></llsd>\n"; - xml_test("uri", expected); - - mSD = LLDate("2006-04-24T16:11:33Z"); - expected = "<llsd><date>2006-04-24T16:11:33Z</date></llsd>\n"; - xml_test("date", expected); - - // 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<> - void sd_xml_object::test<2>() - { - // tests with boolean values. - std::string expected; - - mFormatter->boolalpha(true); - mSD = true; - expected = "<llsd><boolean>true</boolean></llsd>\n"; - xml_test("bool alpha true", expected); - mSD = false; - expected = "<llsd><boolean>false</boolean></llsd>\n"; - xml_test("bool alpha false", expected); - - mFormatter->boolalpha(false); - mSD = true; - expected = "<llsd><boolean>1</boolean></llsd>\n"; - xml_test("bool true", expected); - mSD = false; - expected = "<llsd><boolean>0</boolean></llsd>\n"; - xml_test("bool false", expected); - } - - - template<> template<> - void sd_xml_object::test<3>() - { - // tests with real values. - std::string expected; - - mFormatter->realFormat("%.2f"); - mSD = 1.0; - expected = "<llsd><real>1.00</real></llsd>\n"; - xml_test("real 1", expected); - - mSD = -34379.0438; - expected = "<llsd><real>-34379.04</real></llsd>\n"; - xml_test("real reduced precision", expected); - mFormatter->realFormat("%.4f"); - expected = "<llsd><real>-34379.0438</real></llsd>\n"; - xml_test("higher precision", expected); - - mFormatter->realFormat("%.0f"); - mSD = 0.0; - expected = "<llsd><real>0</real></llsd>\n"; - xml_test("no decimal 0", expected); - mSD = 3287.4387; - expected = "<llsd><real>3287</real></llsd>\n"; - xml_test("no decimal real number", expected); - } - - template<> template<> - void sd_xml_object::test<4>() - { - // tests with arrays - std::string expected; - - mSD = LLSD::emptyArray(); - expected = "<llsd><array /></llsd>\n"; - xml_test("empty array", expected); - - mSD.append(LLSD()); - expected = "<llsd><array><undef /></array></llsd>\n"; - xml_test("1 element array", expected); - - mSD.append(1); - expected = "<llsd><array><undef /><integer>1</integer></array></llsd>\n"; - xml_test("2 element array", expected); - } - - template<> template<> - void sd_xml_object::test<5>() - { - // tests with arrays - std::string expected; - - mSD = LLSD::emptyMap(); - expected = "<llsd><map /></llsd>\n"; - xml_test("empty map", expected); - - mSD["foo"] = "bar"; - expected = "<llsd><map><key>foo</key><string>bar</string></map></llsd>\n"; - xml_test("1 element map", expected); - - mSD["baz"] = LLSD(); - expected = "<llsd><map><key>baz</key><undef /><key>foo</key><string>bar</string></map></llsd>\n"; - 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 - { - public: - TestLLSDSerializeData(); - ~TestLLSDSerializeData(); - - void doRoundTripTests(const std::string&); - void checkRoundTrip(const std::string&, const LLSD& v); - - void setFormatterParser(LLPointer<LLSDFormatter> formatter, LLPointer<LLSDParser> parser) - { - mFormatter = [formatter](const LLSD& data, std::ostream& str) - { - formatter->format(data, str); - }; - // this lambda must be mutable since otherwise the bound 'parser' - // is assumed to point to a const LLSDParser - mParser = [parser](std::istream& istr, LLSD& data, llssize max_bytes) mutable - { - // reset() call is needed since test code re-uses parser object - parser->reset(); - return (parser->parse(istr, data, max_bytes) > 0); - }; - } - - void setParser(bool (*parser)(LLSD&, std::istream&, llssize)) - { - // why does LLSDSerialize::deserialize() reverse the parse() params?? - mParser = [parser](std::istream& istr, LLSD& data, llssize max_bytes) - { - return parser(data, istr, max_bytes); - }; - } - - FormatterFunction mFormatter; - ParserFunction mParser; - }; - - TestLLSDSerializeData::TestLLSDSerializeData() - { - } - - TestLLSDSerializeData::~TestLLSDSerializeData() - { - } - - void TestLLSDSerializeData::checkRoundTrip(const std::string& msg, const LLSD& v) - { - std::stringstream stream; - mFormatter(v, stream); - //LL_INFOS() << "checkRoundTrip: length " << stream.str().length() << LL_ENDL; - LLSD w; - mParser(stream, w, stream.str().size()); - - try - { - ensure_equals(msg, w, v); - } - catch (...) - { - std::cerr << "the serialized string was:" << std::endl; - std::cerr << stream.str() << std::endl; - throw; - } - } - - static void fillmap(LLSD& root, U32 width, U32 depth) - { - if(depth == 0) - { - root["foo"] = "bar"; - return; - } - - for(U32 i = 0; i < width; ++i) - { - std::string key = llformat("child %d", i); - root[key] = LLSD::emptyMap(); - fillmap(root[key], width, depth - 1); - } - } - - void TestLLSDSerializeData::doRoundTripTests(const std::string& msg) - { - LLSD v; - checkRoundTrip(msg + " undefined", v); - - v = true; - checkRoundTrip(msg + " true bool", v); - - v = false; - checkRoundTrip(msg + " false bool", v); - - v = 1; - checkRoundTrip(msg + " positive int", v); - - v = 0; - checkRoundTrip(msg + " zero int", v); - - v = -1; - checkRoundTrip(msg + " negative int", v); - - v = 1234.5f; - checkRoundTrip(msg + " positive float", v); - - v = 0.0f; - checkRoundTrip(msg + " zero float", v); - - v = -1234.5f; - checkRoundTrip(msg + " negative float", v); - - // FIXME: need a NaN test - - v = LLUUID::null; - checkRoundTrip(msg + " null uuid", v); - - LLUUID newUUID; - newUUID.generate(); - v = newUUID; - checkRoundTrip(msg + " new uuid", v); - - v = ""; - checkRoundTrip(msg + " empty string", v); - - v = "some string"; - checkRoundTrip(msg + " non-empty string", v); - - v = + struct sd_xml_data + { + sd_xml_data() + { + mFormatter = new LLSDXMLFormatter; + } + LLSD mSD; + LLPointer<LLSDXMLFormatter> mFormatter; + void xml_test(const char* name, const std::string& expected) + { + std::ostringstream ostr; + mFormatter->format(mSD, ostr); + ensure_equals(name, ostr.str(), expected); + } + }; + + typedef test_group<sd_xml_data> sd_xml_test; + typedef sd_xml_test::object sd_xml_object; + tut::sd_xml_test sd_xml_stream("LLSDXMLFormatter"); + + template<> template<> + void sd_xml_object::test<1>() + { + // random atomic tests + std::string expected; + + expected = "<llsd><undef /></llsd>\n"; + xml_test("undef", expected); + + mSD = 3463; + expected = "<llsd><integer>3463</integer></llsd>\n"; + xml_test("integer", expected); + + mSD = ""; + expected = "<llsd><string /></llsd>\n"; + xml_test("empty string", expected); + + mSD = "foobar"; + expected = "<llsd><string>foobar</string></llsd>\n"; + xml_test("string", expected); + + mSD = LLUUID::null; + expected = "<llsd><uuid /></llsd>\n"; + xml_test("null uuid", expected); + + mSD = LLUUID("c96f9b1e-f589-4100-9774-d98643ce0bed"); + expected = "<llsd><uuid>c96f9b1e-f589-4100-9774-d98643ce0bed</uuid></llsd>\n"; + xml_test("uuid", expected); + + mSD = LLURI("https://secondlife.com/login"); + expected = "<llsd><uri>https://secondlife.com/login</uri></llsd>\n"; + xml_test("uri", expected); + + mSD = LLDate("2006-04-24T16:11:33Z"); + expected = "<llsd><date>2006-04-24T16:11:33Z</date></llsd>\n"; + xml_test("date", expected); + + // 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<> + void sd_xml_object::test<2>() + { + // tests with boolean values. + std::string expected; + + mFormatter->boolalpha(true); + mSD = true; + expected = "<llsd><boolean>true</boolean></llsd>\n"; + xml_test("bool alpha true", expected); + mSD = false; + expected = "<llsd><boolean>false</boolean></llsd>\n"; + xml_test("bool alpha false", expected); + + mFormatter->boolalpha(false); + mSD = true; + expected = "<llsd><boolean>1</boolean></llsd>\n"; + xml_test("bool true", expected); + mSD = false; + expected = "<llsd><boolean>0</boolean></llsd>\n"; + xml_test("bool false", expected); + } + + + template<> template<> + void sd_xml_object::test<3>() + { + // tests with real values. + std::string expected; + + mFormatter->realFormat("%.2f"); + mSD = 1.0; + expected = "<llsd><real>1.00</real></llsd>\n"; + xml_test("real 1", expected); + + mSD = -34379.0438; + expected = "<llsd><real>-34379.04</real></llsd>\n"; + xml_test("real reduced precision", expected); + mFormatter->realFormat("%.4f"); + expected = "<llsd><real>-34379.0438</real></llsd>\n"; + xml_test("higher precision", expected); + + mFormatter->realFormat("%.0f"); + mSD = 0.0; + expected = "<llsd><real>0</real></llsd>\n"; + xml_test("no decimal 0", expected); + mSD = 3287.4387; + expected = "<llsd><real>3287</real></llsd>\n"; + xml_test("no decimal real number", expected); + } + + template<> template<> + void sd_xml_object::test<4>() + { + // tests with arrays + std::string expected; + + mSD = LLSD::emptyArray(); + expected = "<llsd><array /></llsd>\n"; + xml_test("empty array", expected); + + mSD.append(LLSD()); + expected = "<llsd><array><undef /></array></llsd>\n"; + xml_test("1 element array", expected); + + mSD.append(1); + expected = "<llsd><array><undef /><integer>1</integer></array></llsd>\n"; + xml_test("2 element array", expected); + } + + template<> template<> + void sd_xml_object::test<5>() + { + // tests with arrays + std::string expected; + + mSD = LLSD::emptyMap(); + expected = "<llsd><map /></llsd>\n"; + xml_test("empty map", expected); + + mSD["foo"] = "bar"; + expected = "<llsd><map><key>foo</key><string>bar</string></map></llsd>\n"; + xml_test("1 element map", expected); + + mSD["baz"] = LLSD(); + expected = "<llsd><map><key>baz</key><undef /><key>foo</key><string>bar</string></map></llsd>\n"; + 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 + { + public: + TestLLSDSerializeData(); + ~TestLLSDSerializeData(); + + void doRoundTripTests(const std::string&); + void checkRoundTrip(const std::string&, const LLSD& v); + + void setFormatterParser(LLPointer<LLSDFormatter> formatter, LLPointer<LLSDParser> parser) + { + mFormatter = [formatter](const LLSD& data, std::ostream& str) + { + formatter->format(data, str); + }; + // this lambda must be mutable since otherwise the bound 'parser' + // is assumed to point to a const LLSDParser + mParser = [parser](std::istream& istr, LLSD& data, llssize max_bytes) mutable + { + // reset() call is needed since test code re-uses parser object + parser->reset(); + return (parser->parse(istr, data, max_bytes) > 0); + }; + } + + void setParser(bool (*parser)(LLSD&, std::istream&, llssize)) + { + // why does LLSDSerialize::deserialize() reverse the parse() params?? + mParser = [parser](std::istream& istr, LLSD& data, llssize max_bytes) + { + return parser(data, istr, max_bytes); + }; + } + + FormatterFunction mFormatter; + ParserFunction mParser; + }; + + TestLLSDSerializeData::TestLLSDSerializeData() + { + } + + TestLLSDSerializeData::~TestLLSDSerializeData() + { + } + + void TestLLSDSerializeData::checkRoundTrip(const std::string& msg, const LLSD& v) + { + std::stringstream stream; + mFormatter(v, stream); + //LL_INFOS() << "checkRoundTrip: length " << stream.str().length() << LL_ENDL; + LLSD w; + mParser(stream, w, stream.str().size()); + + try + { + ensure_equals(msg, w, v); + } + catch (...) + { + std::cerr << "the serialized string was:" << std::endl; + std::cerr << stream.str() << std::endl; + throw; + } + } + + static void fillmap(LLSD& root, U32 width, U32 depth) + { + if(depth == 0) + { + root["foo"] = "bar"; + return; + } + + for(U32 i = 0; i < width; ++i) + { + std::string key = llformat("child %d", i); + root[key] = LLSD::emptyMap(); + fillmap(root[key], width, depth - 1); + } + } + + void TestLLSDSerializeData::doRoundTripTests(const std::string& msg) + { + LLSD v; + checkRoundTrip(msg + " undefined", v); + + v = true; + checkRoundTrip(msg + " true bool", v); + + v = false; + checkRoundTrip(msg + " false bool", v); + + v = 1; + checkRoundTrip(msg + " positive int", v); + + v = 0; + checkRoundTrip(msg + " zero int", v); + + v = -1; + checkRoundTrip(msg + " negative int", v); + + v = 1234.5f; + checkRoundTrip(msg + " positive float", v); + + v = 0.0f; + checkRoundTrip(msg + " zero float", v); + + v = -1234.5f; + checkRoundTrip(msg + " negative float", v); + + // FIXME: need a NaN test + + v = LLUUID::null; + checkRoundTrip(msg + " null uuid", v); + + LLUUID newUUID; + newUUID.generate(); + v = newUUID; + checkRoundTrip(msg + " new uuid", v); + + v = ""; + checkRoundTrip(msg + " empty string", v); + + v = "some string"; + checkRoundTrip(msg + " non-empty string", v); + + v = "Second Life is a 3-D virtual world entirely built and owned by its residents. " "Since opening to the public in 2003, it has grown explosively and today is " "inhabited by nearly 100,000 people from around the globe.\n" @@ -390,437 +390,437 @@ namespace tut "currency exchanges.\n" "\n" "Welcome to Second Life. We look forward to seeing you in-world!\n" - ; - checkRoundTrip(msg + " long string", v); - - static const U32 block_size = 0x000020; - for (U32 block = 0x000000; block <= 0x10ffff; block += block_size) - { - std::ostringstream out; - - for (U32 c = block; c < block + block_size; ++c) - { - if (c <= 0x000001f - && c != 0x000009 - && c != 0x00000a) - { - // see XML standard, sections 2.2 and 4.1 - continue; - } - if (0x00d800 <= c && c <= 0x00dfff) { continue; } - if (0x00fdd0 <= c && c <= 0x00fdef) { continue; } - if ((c & 0x00fffe) == 0x00fffe) { continue; } - // see Unicode standard, section 15.8 - - if (c <= 0x00007f) - { - out << (char)(c & 0x7f); - } - else if (c <= 0x0007ff) - { - out << (char)(0xc0 | ((c >> 6) & 0x1f)); - out << (char)(0x80 | ((c >> 0) & 0x3f)); - } - else if (c <= 0x00ffff) - { - out << (char)(0xe0 | ((c >> 12) & 0x0f)); - out << (char)(0x80 | ((c >> 6) & 0x3f)); - out << (char)(0x80 | ((c >> 0) & 0x3f)); - } - else - { - out << (char)(0xf0 | ((c >> 18) & 0x07)); - out << (char)(0x80 | ((c >> 12) & 0x3f)); - out << (char)(0x80 | ((c >> 6) & 0x3f)); - out << (char)(0x80 | ((c >> 0) & 0x3f)); - } - } - - v = out.str(); - - std::ostringstream blockmsg; - blockmsg << msg << " unicode string block 0x" << std::hex << block; - checkRoundTrip(blockmsg.str(), v); - } - - LLDate epoch; - v = epoch; - checkRoundTrip(msg + " epoch date", v); - - LLDate aDay("2002-12-07T05:07:15.00Z"); - v = aDay; - checkRoundTrip(msg + " date", v); - - LLURI path("http://slurl.com/secondlife/Ambleside/57/104/26/"); - v = path; - checkRoundTrip(msg + " url", v); - - const char source[] = "it must be a blue moon again"; - std::vector<U8> data; - // note, includes terminating '\0' - copy(&source[0], &source[sizeof(source)], back_inserter(data)); - - v = data; - checkRoundTrip(msg + " binary", v); - - v = LLSD::emptyMap(); - checkRoundTrip(msg + " empty map", v); - - v = LLSD::emptyMap(); - v["name"] = "luke"; //v.insert("name", "luke"); - v["age"] = 3; //v.insert("age", 3); - checkRoundTrip(msg + " map", v); - - v.clear(); - v["a"]["1"] = true; - v["b"]["0"] = false; - checkRoundTrip(msg + " nested maps", v); - - v = LLSD::emptyArray(); - checkRoundTrip(msg + " empty array", v); - - v = LLSD::emptyArray(); - v.append("ali"); - v.append(28); - checkRoundTrip(msg + " array", v); - - v.clear(); - v[0][0] = true; - v[1][0] = false; - checkRoundTrip(msg + " nested arrays", v); - - v = LLSD::emptyMap(); - fillmap(v, 10, 3); // 10^6 maps - checkRoundTrip(msg + " many nested maps", v); - } - - typedef tut::test_group<TestLLSDSerializeData> TestLLSDSerializeGroup; - typedef TestLLSDSerializeGroup::object TestLLSDSerializeObject; - TestLLSDSerializeGroup gTestLLSDSerializeGroup("llsd serialization"); - - template<> template<> - void TestLLSDSerializeObject::test<1>() - { - setFormatterParser(new LLSDNotationFormatter(false, "", LLSDFormatter::OPTIONS_PRETTY_BINARY), - new LLSDNotationParser()); - doRoundTripTests("pretty binary notation serialization"); - } - - template<> template<> - void TestLLSDSerializeObject::test<2>() - { - setFormatterParser(new LLSDNotationFormatter(false, "", LLSDFormatter::OPTIONS_NONE), - new LLSDNotationParser()); - doRoundTripTests("raw binary notation serialization"); - } - - template<> template<> - void TestLLSDSerializeObject::test<3>() - { - setFormatterParser(new LLSDXMLFormatter(), new LLSDXMLParser()); - doRoundTripTests("xml serialization"); - } - - template<> template<> - void TestLLSDSerializeObject::test<4>() - { - setFormatterParser(new LLSDBinaryFormatter(), new LLSDBinaryParser()); - doRoundTripTests("binary serialization"); - } - - template<> template<> - void TestLLSDSerializeObject::test<5>() - { - mFormatter = [](const LLSD& sd, std::ostream& str) - { - LLSDSerialize::serialize(sd, str, LLSDSerialize::LLSD_BINARY); - }; - setParser(LLSDSerialize::deserialize); - doRoundTripTests("serialize(LLSD_BINARY)"); - }; - - template<> template<> - void TestLLSDSerializeObject::test<6>() - { - mFormatter = [](const LLSD& sd, std::ostream& str) - { - LLSDSerialize::serialize(sd, str, LLSDSerialize::LLSD_XML); - }; - setParser(LLSDSerialize::deserialize); - doRoundTripTests("serialize(LLSD_XML)"); - }; - - template<> template<> - void TestLLSDSerializeObject::test<7>() - { - mFormatter = [](const LLSD& sd, std::ostream& str) - { - LLSDSerialize::serialize(sd, str, LLSDSerialize::LLSD_NOTATION); - }; - setParser(LLSDSerialize::deserialize); - // In this test, serialize(LLSD_NOTATION) emits a header recognized by - // deserialize(). - doRoundTripTests("serialize(LLSD_NOTATION)"); - }; - - template<> template<> - void TestLLSDSerializeObject::test<8>() - { - setFormatterParser(new LLSDNotationFormatter(false, "", LLSDFormatter::OPTIONS_NONE), - new LLSDNotationParser()); - setParser(LLSDSerialize::deserialize); - // This is an interesting test because LLSDNotationFormatter does not - // emit an llsd/notation header. - doRoundTripTests("LLSDNotationFormatter -> deserialize"); - }; - - template<> template<> - void TestLLSDSerializeObject::test<9>() - { - setFormatterParser(new LLSDXMLFormatter(false, "", LLSDFormatter::OPTIONS_NONE), - new LLSDXMLParser()); - setParser(LLSDSerialize::deserialize); - // This is an interesting test because LLSDXMLFormatter does not - // emit an LLSD/XML header. - doRoundTripTests("LLSDXMLFormatter -> deserialize"); - }; + ; + checkRoundTrip(msg + " long string", v); + + static const U32 block_size = 0x000020; + for (U32 block = 0x000000; block <= 0x10ffff; block += block_size) + { + std::ostringstream out; + + for (U32 c = block; c < block + block_size; ++c) + { + if (c <= 0x000001f + && c != 0x000009 + && c != 0x00000a) + { + // see XML standard, sections 2.2 and 4.1 + continue; + } + if (0x00d800 <= c && c <= 0x00dfff) { continue; } + if (0x00fdd0 <= c && c <= 0x00fdef) { continue; } + if ((c & 0x00fffe) == 0x00fffe) { continue; } + // see Unicode standard, section 15.8 + + if (c <= 0x00007f) + { + out << (char)(c & 0x7f); + } + else if (c <= 0x0007ff) + { + out << (char)(0xc0 | ((c >> 6) & 0x1f)); + out << (char)(0x80 | ((c >> 0) & 0x3f)); + } + else if (c <= 0x00ffff) + { + out << (char)(0xe0 | ((c >> 12) & 0x0f)); + out << (char)(0x80 | ((c >> 6) & 0x3f)); + out << (char)(0x80 | ((c >> 0) & 0x3f)); + } + else + { + out << (char)(0xf0 | ((c >> 18) & 0x07)); + out << (char)(0x80 | ((c >> 12) & 0x3f)); + out << (char)(0x80 | ((c >> 6) & 0x3f)); + out << (char)(0x80 | ((c >> 0) & 0x3f)); + } + } + + v = out.str(); + + std::ostringstream blockmsg; + blockmsg << msg << " unicode string block 0x" << std::hex << block; + checkRoundTrip(blockmsg.str(), v); + } + + LLDate epoch; + v = epoch; + checkRoundTrip(msg + " epoch date", v); + + LLDate aDay("2002-12-07T05:07:15.00Z"); + v = aDay; + checkRoundTrip(msg + " date", v); + + LLURI path("http://slurl.com/secondlife/Ambleside/57/104/26/"); + v = path; + checkRoundTrip(msg + " url", v); + + const char source[] = "it must be a blue moon again"; + std::vector<U8> data; + // note, includes terminating '\0' + copy(&source[0], &source[sizeof(source)], back_inserter(data)); + + v = data; + checkRoundTrip(msg + " binary", v); + + v = LLSD::emptyMap(); + checkRoundTrip(msg + " empty map", v); + + v = LLSD::emptyMap(); + v["name"] = "luke"; //v.insert("name", "luke"); + v["age"] = 3; //v.insert("age", 3); + checkRoundTrip(msg + " map", v); + + v.clear(); + v["a"]["1"] = true; + v["b"]["0"] = false; + checkRoundTrip(msg + " nested maps", v); + + v = LLSD::emptyArray(); + checkRoundTrip(msg + " empty array", v); + + v = LLSD::emptyArray(); + v.append("ali"); + v.append(28); + checkRoundTrip(msg + " array", v); + + v.clear(); + v[0][0] = true; + v[1][0] = false; + checkRoundTrip(msg + " nested arrays", v); + + v = LLSD::emptyMap(); + fillmap(v, 10, 3); // 10^6 maps + checkRoundTrip(msg + " many nested maps", v); + } + + typedef tut::test_group<TestLLSDSerializeData> TestLLSDSerializeGroup; + typedef TestLLSDSerializeGroup::object TestLLSDSerializeObject; + TestLLSDSerializeGroup gTestLLSDSerializeGroup("llsd serialization"); + + template<> template<> + void TestLLSDSerializeObject::test<1>() + { + setFormatterParser(new LLSDNotationFormatter(false, "", LLSDFormatter::OPTIONS_PRETTY_BINARY), + new LLSDNotationParser()); + doRoundTripTests("pretty binary notation serialization"); + } + + template<> template<> + void TestLLSDSerializeObject::test<2>() + { + setFormatterParser(new LLSDNotationFormatter(false, "", LLSDFormatter::OPTIONS_NONE), + new LLSDNotationParser()); + doRoundTripTests("raw binary notation serialization"); + } + + template<> template<> + void TestLLSDSerializeObject::test<3>() + { + setFormatterParser(new LLSDXMLFormatter(), new LLSDXMLParser()); + doRoundTripTests("xml serialization"); + } + + template<> template<> + void TestLLSDSerializeObject::test<4>() + { + setFormatterParser(new LLSDBinaryFormatter(), new LLSDBinaryParser()); + doRoundTripTests("binary serialization"); + } + + template<> template<> + void TestLLSDSerializeObject::test<5>() + { + mFormatter = [](const LLSD& sd, std::ostream& str) + { + LLSDSerialize::serialize(sd, str, LLSDSerialize::LLSD_BINARY); + }; + setParser(LLSDSerialize::deserialize); + doRoundTripTests("serialize(LLSD_BINARY)"); + }; + + template<> template<> + void TestLLSDSerializeObject::test<6>() + { + mFormatter = [](const LLSD& sd, std::ostream& str) + { + LLSDSerialize::serialize(sd, str, LLSDSerialize::LLSD_XML); + }; + setParser(LLSDSerialize::deserialize); + doRoundTripTests("serialize(LLSD_XML)"); + }; + + template<> template<> + void TestLLSDSerializeObject::test<7>() + { + mFormatter = [](const LLSD& sd, std::ostream& str) + { + LLSDSerialize::serialize(sd, str, LLSDSerialize::LLSD_NOTATION); + }; + setParser(LLSDSerialize::deserialize); + // In this test, serialize(LLSD_NOTATION) emits a header recognized by + // deserialize(). + doRoundTripTests("serialize(LLSD_NOTATION)"); + }; + + template<> template<> + void TestLLSDSerializeObject::test<8>() + { + setFormatterParser(new LLSDNotationFormatter(false, "", LLSDFormatter::OPTIONS_NONE), + new LLSDNotationParser()); + setParser(LLSDSerialize::deserialize); + // This is an interesting test because LLSDNotationFormatter does not + // emit an llsd/notation header. + doRoundTripTests("LLSDNotationFormatter -> deserialize"); + }; + + template<> template<> + void TestLLSDSerializeObject::test<9>() + { + setFormatterParser(new LLSDXMLFormatter(false, "", LLSDFormatter::OPTIONS_NONE), + new LLSDXMLParser()); + setParser(LLSDSerialize::deserialize); + // This is an interesting test because LLSDXMLFormatter does not + // emit an LLSD/XML header. + doRoundTripTests("LLSDXMLFormatter -> deserialize"); + }; /*==========================================================================*| - // We do not expect this test to succeed. Without a header, neither - // notation LLSD nor binary LLSD reliably start with a distinct character, - // the way XML LLSD starts with '<'. By convention, we default to notation - // rather than binary. - template<> template<> - void TestLLSDSerializeObject::test<10>() - { - setFormatterParser(new LLSDBinaryFormatter(false, "", LLSDFormatter::OPTIONS_NONE), - new LLSDBinaryParser()); - setParser(LLSDSerialize::deserialize); - // This is an interesting test because LLSDBinaryFormatter does not - // emit an LLSD/Binary header. - doRoundTripTests("LLSDBinaryFormatter -> deserialize"); - }; + // We do not expect this test to succeed. Without a header, neither + // notation LLSD nor binary LLSD reliably start with a distinct character, + // the way XML LLSD starts with '<'. By convention, we default to notation + // rather than binary. + template<> template<> + void TestLLSDSerializeObject::test<10>() + { + setFormatterParser(new LLSDBinaryFormatter(false, "", LLSDFormatter::OPTIONS_NONE), + new LLSDBinaryParser()); + setParser(LLSDSerialize::deserialize); + // This is an interesting test because LLSDBinaryFormatter does not + // emit an LLSD/Binary header. + doRoundTripTests("LLSDBinaryFormatter -> deserialize"); + }; |*==========================================================================*/ - /** - * @class TestLLSDParsing - * @brief Base class for of a parse tester. - */ - template <class parser_t> - class TestLLSDParsing - { - public: - TestLLSDParsing() - { - mParser = new parser_t; - } - - void ensureParse( - const std::string& msg, - const std::string& in, - const LLSD& expected_value, - S32 expected_count, - S32 depth_limit = -1) - { - std::stringstream input; - input.str(in); - - LLSD parsed_result; - mParser->reset(); // reset() call is needed since test code re-uses mParser - S32 parsed_count = mParser->parse(input, parsed_result, in.size(), depth_limit); - ensure_equals(msg.c_str(), parsed_result, expected_value); - - // This count check is really only useful for expected - // parse failures, since the ensures equal will already - // require equality. - std::string count_msg(msg); - count_msg += " (count)"; - ensure_equals(count_msg, parsed_count, expected_count); - } - - LLPointer<parser_t> mParser; - }; - - - /** - * @class TestLLSDXMLParsing - * @brief Concrete instance of a parse tester. - */ - class TestLLSDXMLParsing : public TestLLSDParsing<LLSDXMLParser> - { - public: - TestLLSDXMLParsing() {} - }; - - typedef tut::test_group<TestLLSDXMLParsing> TestLLSDXMLParsingGroup; - typedef TestLLSDXMLParsingGroup::object TestLLSDXMLParsingObject; - TestLLSDXMLParsingGroup gTestLLSDXMLParsingGroup("llsd XML parsing"); - - template<> template<> - void TestLLSDXMLParsingObject::test<1>() - { - // test handling of xml not recognized as llsd results in an - // LLSD Undefined - ensureParse( - "malformed xml", - "<llsd><string>ha ha</string>", - LLSD(), - LLSDParser::PARSE_FAILURE); - ensureParse( - "not llsd", - "<html><body><p>ha ha</p></body></html>", - LLSD(), - LLSDParser::PARSE_FAILURE); - ensureParse( - "value without llsd", - "<string>ha ha</string>", - LLSD(), - LLSDParser::PARSE_FAILURE); - ensureParse( - "key without llsd", - "<key>ha ha</key>", - LLSD(), - LLSDParser::PARSE_FAILURE); - } - - - template<> template<> - void TestLLSDXMLParsingObject::test<2>() - { - // test handling of unrecognized or unparseable llsd values - LLSD v; - v["amy"] = 23; - v["bob"] = LLSD(); - v["cam"] = 1.23; - - ensureParse( - "unknown data type", - "<llsd><map>" - "<key>amy</key><integer>23</integer>" - "<key>bob</key><bigint>99999999999999999</bigint>" - "<key>cam</key><real>1.23</real>" - "</map></llsd>", - v, - v.size() + 1); - } - - template<> template<> - void TestLLSDXMLParsingObject::test<3>() - { - // test handling of nested bad data - - LLSD v; - v["amy"] = 23; - v["cam"] = 1.23; - - ensureParse( - "map with html", - "<llsd><map>" - "<key>amy</key><integer>23</integer>" - "<html><body>ha ha</body></html>" - "<key>cam</key><real>1.23</real>" - "</map></llsd>", - v, - v.size() + 1); - - v.clear(); - v["amy"] = 23; - v["cam"] = 1.23; - ensureParse( - "map with value for key", - "<llsd><map>" - "<key>amy</key><integer>23</integer>" - "<string>ha ha</string>" - "<key>cam</key><real>1.23</real>" - "</map></llsd>", - v, - v.size() + 1); - - v.clear(); - v["amy"] = 23; - v["bob"] = LLSD::emptyMap(); - v["cam"] = 1.23; - ensureParse( - "map with map of html", - "<llsd><map>" - "<key>amy</key><integer>23</integer>" - "<key>bob</key>" - "<map>" - "<html><body>ha ha</body></html>" - "</map>" - "<key>cam</key><real>1.23</real>" - "</map></llsd>", - v, - v.size() + 1); - - v.clear(); - v[0] = 23; - v[1] = LLSD(); - v[2] = 1.23; - - ensureParse( - "array value of html", - "<llsd><array>" - "<integer>23</integer>" - "<html><body>ha ha</body></html>" - "<real>1.23</real>" - "</array></llsd>", - v, - v.size() + 1); - - v.clear(); - v[0] = 23; - v[1] = LLSD::emptyMap(); - v[2] = 1.23; - ensureParse( - "array with map of html", - "<llsd><array>" - "<integer>23</integer>" - "<map>" - "<html><body>ha ha</body></html>" - "</map>" - "<real>1.23</real>" - "</array></llsd>", - v, - 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); - } + /** + * @class TestLLSDParsing + * @brief Base class for of a parse tester. + */ + template <class parser_t> + class TestLLSDParsing + { + public: + TestLLSDParsing() + { + mParser = new parser_t; + } + + void ensureParse( + const std::string& msg, + const std::string& in, + const LLSD& expected_value, + S32 expected_count, + S32 depth_limit = -1) + { + std::stringstream input; + input.str(in); + + LLSD parsed_result; + mParser->reset(); // reset() call is needed since test code re-uses mParser + S32 parsed_count = mParser->parse(input, parsed_result, in.size(), depth_limit); + ensure_equals(msg.c_str(), parsed_result, expected_value); + + // This count check is really only useful for expected + // parse failures, since the ensures equal will already + // require equality. + std::string count_msg(msg); + count_msg += " (count)"; + ensure_equals(count_msg, parsed_count, expected_count); + } + + LLPointer<parser_t> mParser; + }; + + + /** + * @class TestLLSDXMLParsing + * @brief Concrete instance of a parse tester. + */ + class TestLLSDXMLParsing : public TestLLSDParsing<LLSDXMLParser> + { + public: + TestLLSDXMLParsing() {} + }; + + typedef tut::test_group<TestLLSDXMLParsing> TestLLSDXMLParsingGroup; + typedef TestLLSDXMLParsingGroup::object TestLLSDXMLParsingObject; + TestLLSDXMLParsingGroup gTestLLSDXMLParsingGroup("llsd XML parsing"); + + template<> template<> + void TestLLSDXMLParsingObject::test<1>() + { + // test handling of xml not recognized as llsd results in an + // LLSD Undefined + ensureParse( + "malformed xml", + "<llsd><string>ha ha</string>", + LLSD(), + LLSDParser::PARSE_FAILURE); + ensureParse( + "not llsd", + "<html><body><p>ha ha</p></body></html>", + LLSD(), + LLSDParser::PARSE_FAILURE); + ensureParse( + "value without llsd", + "<string>ha ha</string>", + LLSD(), + LLSDParser::PARSE_FAILURE); + ensureParse( + "key without llsd", + "<key>ha ha</key>", + LLSD(), + LLSDParser::PARSE_FAILURE); + } + + + template<> template<> + void TestLLSDXMLParsingObject::test<2>() + { + // test handling of unrecognized or unparseable llsd values + LLSD v; + v["amy"] = 23; + v["bob"] = LLSD(); + v["cam"] = 1.23; + + ensureParse( + "unknown data type", + "<llsd><map>" + "<key>amy</key><integer>23</integer>" + "<key>bob</key><bigint>99999999999999999</bigint>" + "<key>cam</key><real>1.23</real>" + "</map></llsd>", + v, + v.size() + 1); + } + + template<> template<> + void TestLLSDXMLParsingObject::test<3>() + { + // test handling of nested bad data + + LLSD v; + v["amy"] = 23; + v["cam"] = 1.23; + + ensureParse( + "map with html", + "<llsd><map>" + "<key>amy</key><integer>23</integer>" + "<html><body>ha ha</body></html>" + "<key>cam</key><real>1.23</real>" + "</map></llsd>", + v, + v.size() + 1); + + v.clear(); + v["amy"] = 23; + v["cam"] = 1.23; + ensureParse( + "map with value for key", + "<llsd><map>" + "<key>amy</key><integer>23</integer>" + "<string>ha ha</string>" + "<key>cam</key><real>1.23</real>" + "</map></llsd>", + v, + v.size() + 1); + + v.clear(); + v["amy"] = 23; + v["bob"] = LLSD::emptyMap(); + v["cam"] = 1.23; + ensureParse( + "map with map of html", + "<llsd><map>" + "<key>amy</key><integer>23</integer>" + "<key>bob</key>" + "<map>" + "<html><body>ha ha</body></html>" + "</map>" + "<key>cam</key><real>1.23</real>" + "</map></llsd>", + v, + v.size() + 1); + + v.clear(); + v[0] = 23; + v[1] = LLSD(); + v[2] = 1.23; + + ensureParse( + "array value of html", + "<llsd><array>" + "<integer>23</integer>" + "<html><body>ha ha</body></html>" + "<real>1.23</real>" + "</array></llsd>", + v, + v.size() + 1); + + v.clear(); + v[0] = 23; + v[1] = LLSD::emptyMap(); + v[2] = 1.23; + ensureParse( + "array with map of html", + "<llsd><array>" + "<integer>23</integer>" + "<map>" + "<html><body>ha ha</body></html>" + "</map>" + "<real>1.23</real>" + "</array></llsd>", + v, + 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); + } template<> template<> void TestLLSDXMLParsingObject::test<5>() @@ -858,272 +858,272 @@ namespace tut } - /* - TODO: - test XML parsing - binary with unrecognized encoding - nested LLSD tags - multiple values inside an LLSD - */ - - - /** - * @class TestLLSDNotationParsing - * @brief Concrete instance of a parse tester. - */ - class TestLLSDNotationParsing : public TestLLSDParsing<LLSDNotationParser> - { - public: - TestLLSDNotationParsing() {} - }; - - typedef tut::test_group<TestLLSDNotationParsing> TestLLSDNotationParsingGroup; - typedef TestLLSDNotationParsingGroup::object TestLLSDNotationParsingObject; - TestLLSDNotationParsingGroup gTestLLSDNotationParsingGroup( - "llsd notation parsing"); - - template<> template<> - void TestLLSDNotationParsingObject::test<1>() - { - // test handling of xml not recognized as llsd results in an - // LLSD Undefined - ensureParse( - "malformed notation map", - "{'ha ha'", - LLSD(), - LLSDParser::PARSE_FAILURE); - ensureParse( - "malformed notation array", - "['ha ha'", - LLSD(), - LLSDParser::PARSE_FAILURE); - ensureParse( - "malformed notation string", - "'ha ha", - LLSD(), - LLSDParser::PARSE_FAILURE); - ensureParse( - "bad notation noise", - "g48ejlnfr", - LLSD(), - LLSDParser::PARSE_FAILURE); - } - - template<> template<> - void TestLLSDNotationParsingObject::test<2>() - { - ensureParse("valid undef", "!", LLSD(), 1); - } - - template<> template<> - void TestLLSDNotationParsingObject::test<3>() - { - LLSD val = false; - ensureParse("valid boolean false 0", "false", val, 1); - ensureParse("valid boolean false 1", "f", val, 1); - ensureParse("valid boolean false 2", "0", val, 1); - ensureParse("valid boolean false 3", "F", val, 1); - ensureParse("valid boolean false 4", "FALSE", val, 1); - val = true; - ensureParse("valid boolean true 0", "true", val, 1); - ensureParse("valid boolean true 1", "t", val, 1); - ensureParse("valid boolean true 2", "1", val, 1); - ensureParse("valid boolean true 3", "T", val, 1); - ensureParse("valid boolean true 4", "TRUE", val, 1); - - val.clear(); - ensureParse("invalid true", "TR", val, LLSDParser::PARSE_FAILURE); - ensureParse("invalid false", "FAL", val, LLSDParser::PARSE_FAILURE); - } - - template<> template<> - void TestLLSDNotationParsingObject::test<4>() - { - LLSD val = 123; - ensureParse("valid integer", "i123", val, 1); - val.clear(); - ensureParse("invalid integer", "421", val, LLSDParser::PARSE_FAILURE); - } - - template<> template<> - void TestLLSDNotationParsingObject::test<5>() - { - LLSD val = 456.7; - ensureParse("valid real", "r456.7", val, 1); - val.clear(); - ensureParse("invalid real", "456.7", val, LLSDParser::PARSE_FAILURE); - } - - template<> template<> - void TestLLSDNotationParsingObject::test<6>() - { - LLUUID id; - LLSD val = id; - ensureParse( - "unparseable uuid", - "u123", - LLSD(), - LLSDParser::PARSE_FAILURE); - id.generate(); - val = id; - std::string uuid_str("u"); - uuid_str += id.asString(); - ensureParse("valid uuid", uuid_str.c_str(), val, 1); - } - - template<> template<> - void TestLLSDNotationParsingObject::test<7>() - { - LLSD val = std::string("foolish"); - ensureParse("valid string 1", "\"foolish\"", val, 1); - val = std::string("g'day"); - ensureParse("valid string 2", "\"g'day\"", val, 1); - val = std::string("have a \"nice\" day"); - ensureParse("valid string 3", "'have a \"nice\" day'", val, 1); - val = std::string("whatever"); - ensureParse("valid string 4", "s(8)\"whatever\"", val, 1); - } - - template<> template<> - void TestLLSDNotationParsingObject::test<8>() - { - ensureParse( - "invalid string 1", - "s(7)\"whatever\"", - LLSD(), - LLSDParser::PARSE_FAILURE); - ensureParse( - "invalid string 2", - "s(9)\"whatever\"", - LLSD(), - LLSDParser::PARSE_FAILURE); - } - - template<> template<> - void TestLLSDNotationParsingObject::test<9>() - { - LLSD val = LLURI("http://www.google.com"); - ensureParse("valid uri", "l\"http://www.google.com\"", val, 1); - } - - template<> template<> - void TestLLSDNotationParsingObject::test<10>() - { - LLSD val = LLDate("2007-12-28T09:22:53.10Z"); - ensureParse("valid date", "d\"2007-12-28T09:22:53.10Z\"", val, 1); - } - - template<> template<> - void TestLLSDNotationParsingObject::test<11>() - { - std::vector<U8> vec; - vec.push_back((U8)'a'); vec.push_back((U8)'b'); vec.push_back((U8)'c'); - vec.push_back((U8)'3'); vec.push_back((U8)'2'); vec.push_back((U8)'1'); - LLSD val = vec; - ensureParse("valid binary b64", "b64\"YWJjMzIx\"", val, 1); - ensureParse("valid bainry b16", "b16\"616263333231\"", val, 1); - ensureParse("valid bainry raw", "b(6)\"abc321\"", val, 1); - } - - template<> template<> - void TestLLSDNotationParsingObject::test<12>() - { - ensureParse( - "invalid -- binary length specified too long", - "b(7)\"abc321\"", - LLSD(), - LLSDParser::PARSE_FAILURE); - ensureParse( - "invalid -- binary length specified way too long", - "b(1000000)\"abc321\"", - LLSD(), - LLSDParser::PARSE_FAILURE); - } - - template<> template<> - void TestLLSDNotationParsingObject::test<13>() - { - LLSD val; - val["amy"] = 23; - val["bob"] = LLSD(); - val["cam"] = 1.23; - ensureParse("simple map", "{'amy':i23,'bob':!,'cam':r1.23}", val, 4); - - val["bob"] = LLSD::emptyMap(); - val["bob"]["vehicle"] = std::string("bicycle"); - ensureParse( - "nested map", - "{'amy':i23,'bob':{'vehicle':'bicycle'},'cam':r1.23}", - val, - 5); - } - - template<> template<> - void TestLLSDNotationParsingObject::test<14>() - { - LLSD val; - val.append(23); - val.append(LLSD()); - val.append(1.23); - ensureParse("simple array", "[i23,!,r1.23]", val, 4); - val[1] = LLSD::emptyArray(); - val[1].append("bicycle"); - ensureParse("nested array", "[i23,['bicycle'],r1.23]", val, 5); - } - - template<> template<> - void TestLLSDNotationParsingObject::test<15>() - { - LLSD val; - val["amy"] = 23; - val["bob"]["dogs"] = LLSD::emptyArray(); - val["bob"]["dogs"].append(LLSD::emptyMap()); - val["bob"]["dogs"][0]["name"] = std::string("groove"); - val["bob"]["dogs"][0]["breed"] = std::string("samoyed"); - val["bob"]["dogs"].append(LLSD::emptyMap()); - val["bob"]["dogs"][1]["name"] = std::string("greyley"); - val["bob"]["dogs"][1]["breed"] = std::string("chow/husky"); - val["cam"] = 1.23; - ensureParse( - "nested notation", - "{'amy':i23," - " 'bob':{'dogs':[" - "{'name':'groove', 'breed':'samoyed'}," - "{'name':'greyley', 'breed':'chow/husky'}]}," - " 'cam':r1.23}", - val, - 11); - } - - template<> template<> - void TestLLSDNotationParsingObject::test<16>() - { - // text to make sure that incorrect sizes bail because - std::string bad_str("s(5)\"hi\""); - ensureParse( - "size longer than bytes left", - bad_str, - LLSD(), - LLSDParser::PARSE_FAILURE); - } - - template<> template<> - void TestLLSDNotationParsingObject::test<17>() - { - // text to make sure that incorrect sizes bail because - std::string bad_bin("b(5)\"hi\""); - ensureParse( - "size longer than bytes left", - bad_bin, - LLSD(), - LLSDParser::PARSE_FAILURE); - } + /* + TODO: + test XML parsing + binary with unrecognized encoding + nested LLSD tags + multiple values inside an LLSD + */ + + + /** + * @class TestLLSDNotationParsing + * @brief Concrete instance of a parse tester. + */ + class TestLLSDNotationParsing : public TestLLSDParsing<LLSDNotationParser> + { + public: + TestLLSDNotationParsing() {} + }; + + typedef tut::test_group<TestLLSDNotationParsing> TestLLSDNotationParsingGroup; + typedef TestLLSDNotationParsingGroup::object TestLLSDNotationParsingObject; + TestLLSDNotationParsingGroup gTestLLSDNotationParsingGroup( + "llsd notation parsing"); + + template<> template<> + void TestLLSDNotationParsingObject::test<1>() + { + // test handling of xml not recognized as llsd results in an + // LLSD Undefined + ensureParse( + "malformed notation map", + "{'ha ha'", + LLSD(), + LLSDParser::PARSE_FAILURE); + ensureParse( + "malformed notation array", + "['ha ha'", + LLSD(), + LLSDParser::PARSE_FAILURE); + ensureParse( + "malformed notation string", + "'ha ha", + LLSD(), + LLSDParser::PARSE_FAILURE); + ensureParse( + "bad notation noise", + "g48ejlnfr", + LLSD(), + LLSDParser::PARSE_FAILURE); + } + + template<> template<> + void TestLLSDNotationParsingObject::test<2>() + { + ensureParse("valid undef", "!", LLSD(), 1); + } + + template<> template<> + void TestLLSDNotationParsingObject::test<3>() + { + LLSD val = false; + ensureParse("valid boolean false 0", "false", val, 1); + ensureParse("valid boolean false 1", "f", val, 1); + ensureParse("valid boolean false 2", "0", val, 1); + ensureParse("valid boolean false 3", "F", val, 1); + ensureParse("valid boolean false 4", "FALSE", val, 1); + val = true; + ensureParse("valid boolean true 0", "true", val, 1); + ensureParse("valid boolean true 1", "t", val, 1); + ensureParse("valid boolean true 2", "1", val, 1); + ensureParse("valid boolean true 3", "T", val, 1); + ensureParse("valid boolean true 4", "TRUE", val, 1); + + val.clear(); + ensureParse("invalid true", "TR", val, LLSDParser::PARSE_FAILURE); + ensureParse("invalid false", "FAL", val, LLSDParser::PARSE_FAILURE); + } + + template<> template<> + void TestLLSDNotationParsingObject::test<4>() + { + LLSD val = 123; + ensureParse("valid integer", "i123", val, 1); + val.clear(); + ensureParse("invalid integer", "421", val, LLSDParser::PARSE_FAILURE); + } + + template<> template<> + void TestLLSDNotationParsingObject::test<5>() + { + LLSD val = 456.7; + ensureParse("valid real", "r456.7", val, 1); + val.clear(); + ensureParse("invalid real", "456.7", val, LLSDParser::PARSE_FAILURE); + } + + template<> template<> + void TestLLSDNotationParsingObject::test<6>() + { + LLUUID id; + LLSD val = id; + ensureParse( + "unparseable uuid", + "u123", + LLSD(), + LLSDParser::PARSE_FAILURE); + id.generate(); + val = id; + std::string uuid_str("u"); + uuid_str += id.asString(); + ensureParse("valid uuid", uuid_str.c_str(), val, 1); + } + + template<> template<> + void TestLLSDNotationParsingObject::test<7>() + { + LLSD val = std::string("foolish"); + ensureParse("valid string 1", "\"foolish\"", val, 1); + val = std::string("g'day"); + ensureParse("valid string 2", "\"g'day\"", val, 1); + val = std::string("have a \"nice\" day"); + ensureParse("valid string 3", "'have a \"nice\" day'", val, 1); + val = std::string("whatever"); + ensureParse("valid string 4", "s(8)\"whatever\"", val, 1); + } + + template<> template<> + void TestLLSDNotationParsingObject::test<8>() + { + ensureParse( + "invalid string 1", + "s(7)\"whatever\"", + LLSD(), + LLSDParser::PARSE_FAILURE); + ensureParse( + "invalid string 2", + "s(9)\"whatever\"", + LLSD(), + LLSDParser::PARSE_FAILURE); + } + + template<> template<> + void TestLLSDNotationParsingObject::test<9>() + { + LLSD val = LLURI("http://www.google.com"); + ensureParse("valid uri", "l\"http://www.google.com\"", val, 1); + } + + template<> template<> + void TestLLSDNotationParsingObject::test<10>() + { + LLSD val = LLDate("2007-12-28T09:22:53.10Z"); + ensureParse("valid date", "d\"2007-12-28T09:22:53.10Z\"", val, 1); + } + + template<> template<> + void TestLLSDNotationParsingObject::test<11>() + { + std::vector<U8> vec; + vec.push_back((U8)'a'); vec.push_back((U8)'b'); vec.push_back((U8)'c'); + vec.push_back((U8)'3'); vec.push_back((U8)'2'); vec.push_back((U8)'1'); + LLSD val = vec; + ensureParse("valid binary b64", "b64\"YWJjMzIx\"", val, 1); + ensureParse("valid bainry b16", "b16\"616263333231\"", val, 1); + ensureParse("valid bainry raw", "b(6)\"abc321\"", val, 1); + } + + template<> template<> + void TestLLSDNotationParsingObject::test<12>() + { + ensureParse( + "invalid -- binary length specified too long", + "b(7)\"abc321\"", + LLSD(), + LLSDParser::PARSE_FAILURE); + ensureParse( + "invalid -- binary length specified way too long", + "b(1000000)\"abc321\"", + LLSD(), + LLSDParser::PARSE_FAILURE); + } + + template<> template<> + void TestLLSDNotationParsingObject::test<13>() + { + LLSD val; + val["amy"] = 23; + val["bob"] = LLSD(); + val["cam"] = 1.23; + ensureParse("simple map", "{'amy':i23,'bob':!,'cam':r1.23}", val, 4); + + val["bob"] = LLSD::emptyMap(); + val["bob"]["vehicle"] = std::string("bicycle"); + ensureParse( + "nested map", + "{'amy':i23,'bob':{'vehicle':'bicycle'},'cam':r1.23}", + val, + 5); + } + + template<> template<> + void TestLLSDNotationParsingObject::test<14>() + { + LLSD val; + val.append(23); + val.append(LLSD()); + val.append(1.23); + ensureParse("simple array", "[i23,!,r1.23]", val, 4); + val[1] = LLSD::emptyArray(); + val[1].append("bicycle"); + ensureParse("nested array", "[i23,['bicycle'],r1.23]", val, 5); + } + + template<> template<> + void TestLLSDNotationParsingObject::test<15>() + { + LLSD val; + val["amy"] = 23; + val["bob"]["dogs"] = LLSD::emptyArray(); + val["bob"]["dogs"].append(LLSD::emptyMap()); + val["bob"]["dogs"][0]["name"] = std::string("groove"); + val["bob"]["dogs"][0]["breed"] = std::string("samoyed"); + val["bob"]["dogs"].append(LLSD::emptyMap()); + val["bob"]["dogs"][1]["name"] = std::string("greyley"); + val["bob"]["dogs"][1]["breed"] = std::string("chow/husky"); + val["cam"] = 1.23; + ensureParse( + "nested notation", + "{'amy':i23," + " 'bob':{'dogs':[" + "{'name':'groove', 'breed':'samoyed'}," + "{'name':'greyley', 'breed':'chow/husky'}]}," + " 'cam':r1.23}", + val, + 11); + } + + template<> template<> + void TestLLSDNotationParsingObject::test<16>() + { + // text to make sure that incorrect sizes bail because + std::string bad_str("s(5)\"hi\""); + ensureParse( + "size longer than bytes left", + bad_str, + LLSD(), + LLSDParser::PARSE_FAILURE); + } + + template<> template<> + void TestLLSDNotationParsingObject::test<17>() + { + // text to make sure that incorrect sizes bail because + std::string bad_bin("b(5)\"hi\""); + ensureParse( + "size longer than bytes left", + bad_bin, + LLSD(), + LLSDParser::PARSE_FAILURE); + } template<> template<> void TestLLSDNotationParsingObject::test<18>() { - LLSD level_1 = LLSD::emptyMap(); level_1["level_2"] = 99; - LLSD level_0 = LLSD::emptyMap(); level_0["level_1"] = level_1; + LLSD level_1 = LLSD::emptyMap(); level_1["level_2"] = 99; + LLSD level_0 = LLSD::emptyMap(); level_0["level_1"] = level_1; LLSD deep = LLSD::emptyMap(); deep["level_0"] = level_0; @@ -1168,7 +1168,7 @@ namespace tut template<> template<> void TestLLSDNotationParsingObject::test<20>() { - LLSD end = LLSD::emptyMap(); end["end"] = (S32)99; + LLSD end = LLSD::emptyMap(); end["end"] = (S32)99; LLSD level_49 = LLSD::emptyMap(); level_49["level_49"] = end; LLSD level_48 = LLSD::emptyMap(); level_48["level_48"] = level_49; @@ -1259,536 +1259,536 @@ namespace tut 9); } - /** - * @class TestLLSDBinaryParsing - * @brief Concrete instance of a parse tester. - */ - class TestLLSDBinaryParsing : public TestLLSDParsing<LLSDBinaryParser> - { - public: - TestLLSDBinaryParsing() {} - }; - - typedef tut::test_group<TestLLSDBinaryParsing> TestLLSDBinaryParsingGroup; - typedef TestLLSDBinaryParsingGroup::object TestLLSDBinaryParsingObject; - TestLLSDBinaryParsingGroup gTestLLSDBinaryParsingGroup( - "llsd binary parsing"); - - template<> template<> - void TestLLSDBinaryParsingObject::test<1>() - { - std::vector<U8> vec; - vec.resize(6); - vec[0] = 'a'; vec[1] = 'b'; vec[2] = 'c'; - vec[3] = '3'; vec[4] = '2'; vec[5] = '1'; - std::string string_expected((char*)&vec[0], vec.size()); - LLSD value = string_expected; - - vec.resize(11); - vec[0] = 's'; // for string - vec[5] = 'a'; vec[6] = 'b'; vec[7] = 'c'; - vec[8] = '3'; vec[9] = '2'; vec[10] = '1'; - - uint32_t size = htonl(6); - memcpy(&vec[1], &size, sizeof(uint32_t)); - std::string str_good((char*)&vec[0], vec.size()); - ensureParse("correct string parse", str_good, value, 1); - - size = htonl(7); - memcpy(&vec[1], &size, sizeof(uint32_t)); - std::string str_bad_1((char*)&vec[0], vec.size()); - ensureParse( - "incorrect size string parse", - str_bad_1, - LLSD(), - LLSDParser::PARSE_FAILURE); - - size = htonl(100000); - memcpy(&vec[1], &size, sizeof(uint32_t)); - std::string str_bad_2((char*)&vec[0], vec.size()); - ensureParse( - "incorrect size string parse", - str_bad_2, - LLSD(), - LLSDParser::PARSE_FAILURE); - } - - template<> template<> - void TestLLSDBinaryParsingObject::test<2>() - { - std::vector<U8> vec; - vec.resize(6); - vec[0] = 'a'; vec[1] = 'b'; vec[2] = 'c'; - vec[3] = '3'; vec[4] = '2'; vec[5] = '1'; - LLSD value = vec; - - vec.resize(11); - vec[0] = 'b'; // for binary - vec[5] = 'a'; vec[6] = 'b'; vec[7] = 'c'; - vec[8] = '3'; vec[9] = '2'; vec[10] = '1'; - - uint32_t size = htonl(6); - memcpy(&vec[1], &size, sizeof(uint32_t)); - std::string str_good((char*)&vec[0], vec.size()); - ensureParse("correct binary parse", str_good, value, 1); - - size = htonl(7); - memcpy(&vec[1], &size, sizeof(uint32_t)); - std::string str_bad_1((char*)&vec[0], vec.size()); - ensureParse( - "incorrect size binary parse 1", - str_bad_1, - LLSD(), - LLSDParser::PARSE_FAILURE); - - size = htonl(100000); - memcpy(&vec[1], &size, sizeof(uint32_t)); - std::string str_bad_2((char*)&vec[0], vec.size()); - ensureParse( - "incorrect size binary parse 2", - str_bad_2, - LLSD(), - LLSDParser::PARSE_FAILURE); - } - - template<> template<> - void TestLLSDBinaryParsingObject::test<3>() - { - // test handling of xml not recognized as llsd results in an - // LLSD Undefined - ensureParse( - "malformed binary map", - "{'ha ha'", - LLSD(), - LLSDParser::PARSE_FAILURE); - ensureParse( - "malformed binary array", - "['ha ha'", - LLSD(), - LLSDParser::PARSE_FAILURE); - ensureParse( - "malformed binary string", - "'ha ha", - LLSD(), - LLSDParser::PARSE_FAILURE); - ensureParse( - "bad noise", - "g48ejlnfr", - LLSD(), - LLSDParser::PARSE_FAILURE); - } - template<> template<> - void TestLLSDBinaryParsingObject::test<4>() - { - ensureParse("valid undef", "!", LLSD(), 1); - } - - template<> template<> - void TestLLSDBinaryParsingObject::test<5>() - { - LLSD val = false; - ensureParse("valid boolean false 2", "0", val, 1); - val = true; - ensureParse("valid boolean true 2", "1", val, 1); - - val.clear(); - ensureParse("invalid true", "t", val, LLSDParser::PARSE_FAILURE); - ensureParse("invalid false", "f", val, LLSDParser::PARSE_FAILURE); - } - - template<> template<> - void TestLLSDBinaryParsingObject::test<6>() - { - std::vector<U8> vec; - vec.push_back('{'); - vec.resize(vec.size() + 4); - uint32_t size = htonl(1); - memcpy(&vec[1], &size, sizeof(uint32_t)); - vec.push_back('k'); - int key_size_loc = vec.size(); - size = htonl(1); // 1 too short - vec.resize(vec.size() + 4); - memcpy(&vec[key_size_loc], &size, sizeof(uint32_t)); - vec.push_back('a'); vec.push_back('m'); vec.push_back('y'); - vec.push_back('i'); - int integer_loc = vec.size(); - vec.resize(vec.size() + 4); - uint32_t val_int = htonl(23); - memcpy(&vec[integer_loc], &val_int, sizeof(uint32_t)); - std::string str_bad_1((char*)&vec[0], vec.size()); - ensureParse( - "invalid key size", - str_bad_1, - LLSD(), - LLSDParser::PARSE_FAILURE); - - // check with correct size, but unterminated map (missing '}') - size = htonl(3); // correct size - memcpy(&vec[key_size_loc], &size, sizeof(uint32_t)); - std::string str_bad_2((char*)&vec[0], vec.size()); - ensureParse( - "valid key size, unterminated map", - str_bad_2, - LLSD(), - LLSDParser::PARSE_FAILURE); - - // check w/ correct size and correct map termination - LLSD val; - val["amy"] = 23; - vec.push_back('}'); - std::string str_good((char*)&vec[0], vec.size()); - ensureParse( - "valid map", - str_good, - val, - 2); - - // check w/ incorrect sizes and correct map termination - size = htonl(0); // 1 too few (for the map entry) - memcpy(&vec[1], &size, sizeof(uint32_t)); - std::string str_bad_3((char*)&vec[0], vec.size()); - ensureParse( - "invalid map too long", - str_bad_3, - LLSD(), - LLSDParser::PARSE_FAILURE); - - size = htonl(2); // 1 too many - memcpy(&vec[1], &size, sizeof(uint32_t)); - std::string str_bad_4((char*)&vec[0], vec.size()); - ensureParse( - "invalid map too short", - str_bad_4, - LLSD(), - LLSDParser::PARSE_FAILURE); - } - - template<> template<> - void TestLLSDBinaryParsingObject::test<7>() - { - std::vector<U8> vec; - vec.push_back('['); - vec.resize(vec.size() + 4); - uint32_t size = htonl(1); // 1 too short - memcpy(&vec[1], &size, sizeof(uint32_t)); - vec.push_back('"'); vec.push_back('a'); vec.push_back('m'); - vec.push_back('y'); vec.push_back('"'); vec.push_back('i'); - int integer_loc = vec.size(); - vec.resize(vec.size() + 4); - uint32_t val_int = htonl(23); - memcpy(&vec[integer_loc], &val_int, sizeof(uint32_t)); - - std::string str_bad_1((char*)&vec[0], vec.size()); - ensureParse( - "invalid array size", - str_bad_1, - LLSD(), - LLSDParser::PARSE_FAILURE); - - // check with correct size, but unterminated map (missing ']') - size = htonl(2); // correct size - memcpy(&vec[1], &size, sizeof(uint32_t)); - std::string str_bad_2((char*)&vec[0], vec.size()); - ensureParse( - "unterminated array", - str_bad_2, - LLSD(), - LLSDParser::PARSE_FAILURE); - - // check w/ correct size and correct map termination - LLSD val; - val.append("amy"); - val.append(23); - vec.push_back(']'); - std::string str_good((char*)&vec[0], vec.size()); - ensureParse( - "valid array", - str_good, - val, - 3); - - // check with too many elements - size = htonl(3); // 1 too long - memcpy(&vec[1], &size, sizeof(uint32_t)); - std::string str_bad_3((char*)&vec[0], vec.size()); - ensureParse( - "array too short", - str_bad_3, - LLSD(), - LLSDParser::PARSE_FAILURE); - } - - template<> template<> - void TestLLSDBinaryParsingObject::test<8>() - { - std::vector<U8> vec; - vec.push_back('{'); - vec.resize(vec.size() + 4); - memset(&vec[1], 0, 4); - vec.push_back('}'); - std::string str_good((char*)&vec[0], vec.size()); - LLSD val = LLSD::emptyMap(); - ensureParse( - "empty map", - str_good, - val, - 1); - } - - template<> template<> - void TestLLSDBinaryParsingObject::test<9>() - { - std::vector<U8> vec; - vec.push_back('['); - vec.resize(vec.size() + 4); - memset(&vec[1], 0, 4); - vec.push_back(']'); - std::string str_good((char*)&vec[0], vec.size()); - LLSD val = LLSD::emptyArray(); - ensureParse( - "empty array", - str_good, - val, - 1); - } - - template<> template<> - void TestLLSDBinaryParsingObject::test<10>() - { - std::vector<U8> vec; - vec.push_back('l'); - vec.resize(vec.size() + 4); - uint32_t size = htonl(14); // 1 too long - memcpy(&vec[1], &size, sizeof(uint32_t)); - vec.push_back('h'); vec.push_back('t'); vec.push_back('t'); - vec.push_back('p'); vec.push_back(':'); vec.push_back('/'); - vec.push_back('/'); vec.push_back('s'); vec.push_back('l'); - vec.push_back('.'); vec.push_back('c'); vec.push_back('o'); - vec.push_back('m'); - std::string str_bad((char*)&vec[0], vec.size()); - ensureParse( - "invalid uri length size", - str_bad, - LLSD(), - LLSDParser::PARSE_FAILURE); - - LLSD val; - val = LLURI("http://sl.com"); - size = htonl(13); // correct length - memcpy(&vec[1], &size, sizeof(uint32_t)); - std::string str_good((char*)&vec[0], vec.size()); - ensureParse( - "valid key size", - str_good, - val, - 1); - } + /** + * @class TestLLSDBinaryParsing + * @brief Concrete instance of a parse tester. + */ + class TestLLSDBinaryParsing : public TestLLSDParsing<LLSDBinaryParser> + { + public: + TestLLSDBinaryParsing() {} + }; + + typedef tut::test_group<TestLLSDBinaryParsing> TestLLSDBinaryParsingGroup; + typedef TestLLSDBinaryParsingGroup::object TestLLSDBinaryParsingObject; + TestLLSDBinaryParsingGroup gTestLLSDBinaryParsingGroup( + "llsd binary parsing"); + + template<> template<> + void TestLLSDBinaryParsingObject::test<1>() + { + std::vector<U8> vec; + vec.resize(6); + vec[0] = 'a'; vec[1] = 'b'; vec[2] = 'c'; + vec[3] = '3'; vec[4] = '2'; vec[5] = '1'; + std::string string_expected((char*)&vec[0], vec.size()); + LLSD value = string_expected; + + vec.resize(11); + vec[0] = 's'; // for string + vec[5] = 'a'; vec[6] = 'b'; vec[7] = 'c'; + vec[8] = '3'; vec[9] = '2'; vec[10] = '1'; + + uint32_t size = htonl(6); + memcpy(&vec[1], &size, sizeof(uint32_t)); + std::string str_good((char*)&vec[0], vec.size()); + ensureParse("correct string parse", str_good, value, 1); + + size = htonl(7); + memcpy(&vec[1], &size, sizeof(uint32_t)); + std::string str_bad_1((char*)&vec[0], vec.size()); + ensureParse( + "incorrect size string parse", + str_bad_1, + LLSD(), + LLSDParser::PARSE_FAILURE); + + size = htonl(100000); + memcpy(&vec[1], &size, sizeof(uint32_t)); + std::string str_bad_2((char*)&vec[0], vec.size()); + ensureParse( + "incorrect size string parse", + str_bad_2, + LLSD(), + LLSDParser::PARSE_FAILURE); + } + + template<> template<> + void TestLLSDBinaryParsingObject::test<2>() + { + std::vector<U8> vec; + vec.resize(6); + vec[0] = 'a'; vec[1] = 'b'; vec[2] = 'c'; + vec[3] = '3'; vec[4] = '2'; vec[5] = '1'; + LLSD value = vec; + + vec.resize(11); + vec[0] = 'b'; // for binary + vec[5] = 'a'; vec[6] = 'b'; vec[7] = 'c'; + vec[8] = '3'; vec[9] = '2'; vec[10] = '1'; + + uint32_t size = htonl(6); + memcpy(&vec[1], &size, sizeof(uint32_t)); + std::string str_good((char*)&vec[0], vec.size()); + ensureParse("correct binary parse", str_good, value, 1); + + size = htonl(7); + memcpy(&vec[1], &size, sizeof(uint32_t)); + std::string str_bad_1((char*)&vec[0], vec.size()); + ensureParse( + "incorrect size binary parse 1", + str_bad_1, + LLSD(), + LLSDParser::PARSE_FAILURE); + + size = htonl(100000); + memcpy(&vec[1], &size, sizeof(uint32_t)); + std::string str_bad_2((char*)&vec[0], vec.size()); + ensureParse( + "incorrect size binary parse 2", + str_bad_2, + LLSD(), + LLSDParser::PARSE_FAILURE); + } + + template<> template<> + void TestLLSDBinaryParsingObject::test<3>() + { + // test handling of xml not recognized as llsd results in an + // LLSD Undefined + ensureParse( + "malformed binary map", + "{'ha ha'", + LLSD(), + LLSDParser::PARSE_FAILURE); + ensureParse( + "malformed binary array", + "['ha ha'", + LLSD(), + LLSDParser::PARSE_FAILURE); + ensureParse( + "malformed binary string", + "'ha ha", + LLSD(), + LLSDParser::PARSE_FAILURE); + ensureParse( + "bad noise", + "g48ejlnfr", + LLSD(), + LLSDParser::PARSE_FAILURE); + } + template<> template<> + void TestLLSDBinaryParsingObject::test<4>() + { + ensureParse("valid undef", "!", LLSD(), 1); + } + + template<> template<> + void TestLLSDBinaryParsingObject::test<5>() + { + LLSD val = false; + ensureParse("valid boolean false 2", "0", val, 1); + val = true; + ensureParse("valid boolean true 2", "1", val, 1); + + val.clear(); + ensureParse("invalid true", "t", val, LLSDParser::PARSE_FAILURE); + ensureParse("invalid false", "f", val, LLSDParser::PARSE_FAILURE); + } + + template<> template<> + void TestLLSDBinaryParsingObject::test<6>() + { + std::vector<U8> vec; + vec.push_back('{'); + vec.resize(vec.size() + 4); + uint32_t size = htonl(1); + memcpy(&vec[1], &size, sizeof(uint32_t)); + vec.push_back('k'); + int key_size_loc = vec.size(); + size = htonl(1); // 1 too short + vec.resize(vec.size() + 4); + memcpy(&vec[key_size_loc], &size, sizeof(uint32_t)); + vec.push_back('a'); vec.push_back('m'); vec.push_back('y'); + vec.push_back('i'); + int integer_loc = vec.size(); + vec.resize(vec.size() + 4); + uint32_t val_int = htonl(23); + memcpy(&vec[integer_loc], &val_int, sizeof(uint32_t)); + std::string str_bad_1((char*)&vec[0], vec.size()); + ensureParse( + "invalid key size", + str_bad_1, + LLSD(), + LLSDParser::PARSE_FAILURE); + + // check with correct size, but unterminated map (missing '}') + size = htonl(3); // correct size + memcpy(&vec[key_size_loc], &size, sizeof(uint32_t)); + std::string str_bad_2((char*)&vec[0], vec.size()); + ensureParse( + "valid key size, unterminated map", + str_bad_2, + LLSD(), + LLSDParser::PARSE_FAILURE); + + // check w/ correct size and correct map termination + LLSD val; + val["amy"] = 23; + vec.push_back('}'); + std::string str_good((char*)&vec[0], vec.size()); + ensureParse( + "valid map", + str_good, + val, + 2); + + // check w/ incorrect sizes and correct map termination + size = htonl(0); // 1 too few (for the map entry) + memcpy(&vec[1], &size, sizeof(uint32_t)); + std::string str_bad_3((char*)&vec[0], vec.size()); + ensureParse( + "invalid map too long", + str_bad_3, + LLSD(), + LLSDParser::PARSE_FAILURE); + + size = htonl(2); // 1 too many + memcpy(&vec[1], &size, sizeof(uint32_t)); + std::string str_bad_4((char*)&vec[0], vec.size()); + ensureParse( + "invalid map too short", + str_bad_4, + LLSD(), + LLSDParser::PARSE_FAILURE); + } + + template<> template<> + void TestLLSDBinaryParsingObject::test<7>() + { + std::vector<U8> vec; + vec.push_back('['); + vec.resize(vec.size() + 4); + uint32_t size = htonl(1); // 1 too short + memcpy(&vec[1], &size, sizeof(uint32_t)); + vec.push_back('"'); vec.push_back('a'); vec.push_back('m'); + vec.push_back('y'); vec.push_back('"'); vec.push_back('i'); + int integer_loc = vec.size(); + vec.resize(vec.size() + 4); + uint32_t val_int = htonl(23); + memcpy(&vec[integer_loc], &val_int, sizeof(uint32_t)); + + std::string str_bad_1((char*)&vec[0], vec.size()); + ensureParse( + "invalid array size", + str_bad_1, + LLSD(), + LLSDParser::PARSE_FAILURE); + + // check with correct size, but unterminated map (missing ']') + size = htonl(2); // correct size + memcpy(&vec[1], &size, sizeof(uint32_t)); + std::string str_bad_2((char*)&vec[0], vec.size()); + ensureParse( + "unterminated array", + str_bad_2, + LLSD(), + LLSDParser::PARSE_FAILURE); + + // check w/ correct size and correct map termination + LLSD val; + val.append("amy"); + val.append(23); + vec.push_back(']'); + std::string str_good((char*)&vec[0], vec.size()); + ensureParse( + "valid array", + str_good, + val, + 3); + + // check with too many elements + size = htonl(3); // 1 too long + memcpy(&vec[1], &size, sizeof(uint32_t)); + std::string str_bad_3((char*)&vec[0], vec.size()); + ensureParse( + "array too short", + str_bad_3, + LLSD(), + LLSDParser::PARSE_FAILURE); + } + + template<> template<> + void TestLLSDBinaryParsingObject::test<8>() + { + std::vector<U8> vec; + vec.push_back('{'); + vec.resize(vec.size() + 4); + memset(&vec[1], 0, 4); + vec.push_back('}'); + std::string str_good((char*)&vec[0], vec.size()); + LLSD val = LLSD::emptyMap(); + ensureParse( + "empty map", + str_good, + val, + 1); + } + + template<> template<> + void TestLLSDBinaryParsingObject::test<9>() + { + std::vector<U8> vec; + vec.push_back('['); + vec.resize(vec.size() + 4); + memset(&vec[1], 0, 4); + vec.push_back(']'); + std::string str_good((char*)&vec[0], vec.size()); + LLSD val = LLSD::emptyArray(); + ensureParse( + "empty array", + str_good, + val, + 1); + } + + template<> template<> + void TestLLSDBinaryParsingObject::test<10>() + { + std::vector<U8> vec; + vec.push_back('l'); + vec.resize(vec.size() + 4); + uint32_t size = htonl(14); // 1 too long + memcpy(&vec[1], &size, sizeof(uint32_t)); + vec.push_back('h'); vec.push_back('t'); vec.push_back('t'); + vec.push_back('p'); vec.push_back(':'); vec.push_back('/'); + vec.push_back('/'); vec.push_back('s'); vec.push_back('l'); + vec.push_back('.'); vec.push_back('c'); vec.push_back('o'); + vec.push_back('m'); + std::string str_bad((char*)&vec[0], vec.size()); + ensureParse( + "invalid uri length size", + str_bad, + LLSD(), + LLSDParser::PARSE_FAILURE); + + LLSD val; + val = LLURI("http://sl.com"); + size = htonl(13); // correct length + memcpy(&vec[1], &size, sizeof(uint32_t)); + std::string str_good((char*)&vec[0], vec.size()); + ensureParse( + "valid key size", + str_good, + val, + 1); + } /* - template<> template<> - void TestLLSDBinaryParsingObject::test<11>() - { - } + template<> template<> + void TestLLSDBinaryParsingObject::test<11>() + { + } */ /** - * @class TestLLSDCrossCompatible - * @brief Miscellaneous serialization and parsing tests - */ - class TestLLSDCrossCompatible - { - public: - TestLLSDCrossCompatible() {} - - void ensureBinaryAndNotation( - const std::string& msg, - const LLSD& input) - { - // to binary, and back again - std::stringstream str1; - S32 count1 = LLSDSerialize::toBinary(input, str1); - LLSD actual_value_bin; - S32 count2 = LLSDSerialize::fromBinary( - actual_value_bin, - str1, - LLSDSerialize::SIZE_UNLIMITED); - ensure_equals( - "ensureBinaryAndNotation binary count", - count2, - count1); - - // to notation and back again - std::stringstream str2; - S32 count3 = LLSDSerialize::toNotation(actual_value_bin, str2); - ensure_equals( - "ensureBinaryAndNotation notation count1", - count3, - count2); - LLSD actual_value_notation; - S32 count4 = LLSDSerialize::fromNotation( - actual_value_notation, - str2, - LLSDSerialize::SIZE_UNLIMITED); - ensure_equals( - "ensureBinaryAndNotation notation count2", - count4, - count3); - ensure_equals( - (msg + " (binaryandnotation)").c_str(), - actual_value_notation, - input); - } - - void ensureBinaryAndXML( - const std::string& msg, - const LLSD& input) - { - // to binary, and back again - std::stringstream str1; - S32 count1 = LLSDSerialize::toBinary(input, str1); - LLSD actual_value_bin; - S32 count2 = LLSDSerialize::fromBinary( - actual_value_bin, - str1, - LLSDSerialize::SIZE_UNLIMITED); - ensure_equals( - "ensureBinaryAndXML binary count", - count2, - count1); - - // to xml and back again - std::stringstream str2; - S32 count3 = LLSDSerialize::toXML(actual_value_bin, str2); - ensure_equals( - "ensureBinaryAndXML xml count1", - count3, - count2); - LLSD actual_value_xml; - S32 count4 = LLSDSerialize::fromXML(actual_value_xml, str2); - ensure_equals( - "ensureBinaryAndXML xml count2", - count4, - count3); - ensure_equals((msg + " (binaryandxml)").c_str(), actual_value_xml, input); - } - }; - - typedef tut::test_group<TestLLSDCrossCompatible> TestLLSDCompatibleGroup; - typedef TestLLSDCompatibleGroup::object TestLLSDCompatibleObject; - TestLLSDCompatibleGroup gTestLLSDCompatibleGroup( - "llsd serialize compatible"); - - template<> template<> - void TestLLSDCompatibleObject::test<1>() - { - LLSD test; - ensureBinaryAndNotation("undef", test); - ensureBinaryAndXML("undef", test); - test = true; - ensureBinaryAndNotation("boolean true", test); - ensureBinaryAndXML("boolean true", test); - test = false; - ensureBinaryAndNotation("boolean false", test); - ensureBinaryAndXML("boolean false", test); - test = 0; - ensureBinaryAndNotation("integer zero", test); - ensureBinaryAndXML("integer zero", test); - test = 1; - ensureBinaryAndNotation("integer positive", test); - ensureBinaryAndXML("integer positive", test); - test = -234567; - ensureBinaryAndNotation("integer negative", test); - ensureBinaryAndXML("integer negative", test); - test = 0.0; - ensureBinaryAndNotation("real zero", test); - ensureBinaryAndXML("real zero", test); - test = 1.0; - ensureBinaryAndNotation("real positive", test); - ensureBinaryAndXML("real positive", test); - test = -1.0; - ensureBinaryAndNotation("real negative", test); - ensureBinaryAndXML("real negative", test); - } - - template<> template<> - void TestLLSDCompatibleObject::test<2>() - { - LLSD test; - test = "foobar"; - ensureBinaryAndNotation("string", test); - ensureBinaryAndXML("string", test); - } - - template<> template<> - void TestLLSDCompatibleObject::test<3>() - { - LLSD test; - LLUUID id; - id.generate(); - test = id; - ensureBinaryAndNotation("uuid", test); - ensureBinaryAndXML("uuid", test); - } - - template<> template<> - void TestLLSDCompatibleObject::test<4>() - { - LLSD test; - test = LLDate(12345.0); - ensureBinaryAndNotation("date", test); - ensureBinaryAndXML("date", test); - } - - template<> template<> - void TestLLSDCompatibleObject::test<5>() - { - LLSD test; - test = LLURI("http://www.secondlife.com/"); - ensureBinaryAndNotation("uri", test); - ensureBinaryAndXML("uri", test); - } - - template<> template<> - void TestLLSDCompatibleObject::test<6>() - { - LLSD test; - typedef std::vector<U8> buf_t; - buf_t val; - for(int ii = 0; ii < 100; ++ii) - { - srand(ii); /* Flawfinder: ignore */ - S32 size = rand() % 100 + 10; - std::generate_n( - std::back_insert_iterator<buf_t>(val), - size, - rand); - } - test = val; - ensureBinaryAndNotation("binary", test); - ensureBinaryAndXML("binary", test); - } - - template<> template<> - void TestLLSDCompatibleObject::test<7>() - { - LLSD test; - test = LLSD::emptyArray(); - test.append(1); - test.append("hello"); - ensureBinaryAndNotation("array", test); - ensureBinaryAndXML("array", test); - } - - template<> template<> - void TestLLSDCompatibleObject::test<8>() - { - LLSD test; - test = LLSD::emptyArray(); - test["foo"] = "bar"; - test["baz"] = 100; - ensureBinaryAndNotation("map", test); - ensureBinaryAndXML("map", test); - } + * @class TestLLSDCrossCompatible + * @brief Miscellaneous serialization and parsing tests + */ + class TestLLSDCrossCompatible + { + public: + TestLLSDCrossCompatible() {} + + void ensureBinaryAndNotation( + const std::string& msg, + const LLSD& input) + { + // to binary, and back again + std::stringstream str1; + S32 count1 = LLSDSerialize::toBinary(input, str1); + LLSD actual_value_bin; + S32 count2 = LLSDSerialize::fromBinary( + actual_value_bin, + str1, + LLSDSerialize::SIZE_UNLIMITED); + ensure_equals( + "ensureBinaryAndNotation binary count", + count2, + count1); + + // to notation and back again + std::stringstream str2; + S32 count3 = LLSDSerialize::toNotation(actual_value_bin, str2); + ensure_equals( + "ensureBinaryAndNotation notation count1", + count3, + count2); + LLSD actual_value_notation; + S32 count4 = LLSDSerialize::fromNotation( + actual_value_notation, + str2, + LLSDSerialize::SIZE_UNLIMITED); + ensure_equals( + "ensureBinaryAndNotation notation count2", + count4, + count3); + ensure_equals( + (msg + " (binaryandnotation)").c_str(), + actual_value_notation, + input); + } + + void ensureBinaryAndXML( + const std::string& msg, + const LLSD& input) + { + // to binary, and back again + std::stringstream str1; + S32 count1 = LLSDSerialize::toBinary(input, str1); + LLSD actual_value_bin; + S32 count2 = LLSDSerialize::fromBinary( + actual_value_bin, + str1, + LLSDSerialize::SIZE_UNLIMITED); + ensure_equals( + "ensureBinaryAndXML binary count", + count2, + count1); + + // to xml and back again + std::stringstream str2; + S32 count3 = LLSDSerialize::toXML(actual_value_bin, str2); + ensure_equals( + "ensureBinaryAndXML xml count1", + count3, + count2); + LLSD actual_value_xml; + S32 count4 = LLSDSerialize::fromXML(actual_value_xml, str2); + ensure_equals( + "ensureBinaryAndXML xml count2", + count4, + count3); + ensure_equals((msg + " (binaryandxml)").c_str(), actual_value_xml, input); + } + }; + + typedef tut::test_group<TestLLSDCrossCompatible> TestLLSDCompatibleGroup; + typedef TestLLSDCompatibleGroup::object TestLLSDCompatibleObject; + TestLLSDCompatibleGroup gTestLLSDCompatibleGroup( + "llsd serialize compatible"); + + template<> template<> + void TestLLSDCompatibleObject::test<1>() + { + LLSD test; + ensureBinaryAndNotation("undef", test); + ensureBinaryAndXML("undef", test); + test = true; + ensureBinaryAndNotation("boolean true", test); + ensureBinaryAndXML("boolean true", test); + test = false; + ensureBinaryAndNotation("boolean false", test); + ensureBinaryAndXML("boolean false", test); + test = 0; + ensureBinaryAndNotation("integer zero", test); + ensureBinaryAndXML("integer zero", test); + test = 1; + ensureBinaryAndNotation("integer positive", test); + ensureBinaryAndXML("integer positive", test); + test = -234567; + ensureBinaryAndNotation("integer negative", test); + ensureBinaryAndXML("integer negative", test); + test = 0.0; + ensureBinaryAndNotation("real zero", test); + ensureBinaryAndXML("real zero", test); + test = 1.0; + ensureBinaryAndNotation("real positive", test); + ensureBinaryAndXML("real positive", test); + test = -1.0; + ensureBinaryAndNotation("real negative", test); + ensureBinaryAndXML("real negative", test); + } + + template<> template<> + void TestLLSDCompatibleObject::test<2>() + { + LLSD test; + test = "foobar"; + ensureBinaryAndNotation("string", test); + ensureBinaryAndXML("string", test); + } + + template<> template<> + void TestLLSDCompatibleObject::test<3>() + { + LLSD test; + LLUUID id; + id.generate(); + test = id; + ensureBinaryAndNotation("uuid", test); + ensureBinaryAndXML("uuid", test); + } + + template<> template<> + void TestLLSDCompatibleObject::test<4>() + { + LLSD test; + test = LLDate(12345.0); + ensureBinaryAndNotation("date", test); + ensureBinaryAndXML("date", test); + } + + template<> template<> + void TestLLSDCompatibleObject::test<5>() + { + LLSD test; + test = LLURI("http://www.secondlife.com/"); + ensureBinaryAndNotation("uri", test); + ensureBinaryAndXML("uri", test); + } + + template<> template<> + void TestLLSDCompatibleObject::test<6>() + { + LLSD test; + typedef std::vector<U8> buf_t; + buf_t val; + for(int ii = 0; ii < 100; ++ii) + { + srand(ii); /* Flawfinder: ignore */ + S32 size = rand() % 100 + 10; + std::generate_n( + std::back_insert_iterator<buf_t>(val), + size, + rand); + } + test = val; + ensureBinaryAndNotation("binary", test); + ensureBinaryAndXML("binary", test); + } + + template<> template<> + void TestLLSDCompatibleObject::test<7>() + { + LLSD test; + test = LLSD::emptyArray(); + test.append(1); + test.append("hello"); + ensureBinaryAndNotation("array", test); + ensureBinaryAndXML("array", test); + } + + template<> template<> + void TestLLSDCompatibleObject::test<8>() + { + LLSD test; + test = LLSD::emptyArray(); + test["foo"] = "bar"; + test["baz"] = 100; + ensureBinaryAndNotation("map", test); + ensureBinaryAndXML("map", test); + } // helper for TestPythonCompatible static std::string import_llsd("import os.path\n" @@ -2130,7 +2130,7 @@ namespace tut item.asReal(), 3.14, 7); // 7 bits ~= 0.01 ensure("Failed to read LLSD::String from Python", itemFromStream(inf, item, parse)); - ensure_equals(item.asString(), + ensure_equals(item.asString(), "This string\n" "has several\n" "lines."); diff --git a/indra/llcommon/tests/llsingleton_test.cpp b/indra/llcommon/tests/llsingleton_test.cpp index 6f8aaaa0cb..adf5804272 100644 --- a/indra/llcommon/tests/llsingleton_test.cpp +++ b/indra/llcommon/tests/llsingleton_test.cpp @@ -1,4 +1,4 @@ -/** +/** * @file llsingleton_test.cpp * @date 2011-08-11 * @brief Unit test for the LLSingleton class diff --git a/indra/llcommon/tests/llstreamqueue_test.cpp b/indra/llcommon/tests/llstreamqueue_test.cpp index 8af057328b..82b451119e 100644 --- a/indra/llcommon/tests/llstreamqueue_test.cpp +++ b/indra/llcommon/tests/llstreamqueue_test.cpp @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2012-01-05 * @brief Test for llstreamqueue. - * + * * $LicenseInfo:firstyear=2012&license=viewerlgpl$ * Copyright (c) 2012, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/tests/llstring_test.cpp b/indra/llcommon/tests/llstring_test.cpp index 1e4bc1c910..89ef5b3450 100644 --- a/indra/llcommon/tests/llstring_test.cpp +++ b/indra/llcommon/tests/llstring_test.cpp @@ -1,871 +1,871 @@ -/** - * @file llstring_test.cpp - * @author Adroit, Steve Linden, Tofu Linden - * @date 2006-12-24 - * @brief Test cases of llstring.cpp - * - * $LicenseInfo:firstyear=2007&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "linden_common.h" - -#include <boost/assign/list_of.hpp> -#include "../llstring.h" -#include "StringVec.h" // must come BEFORE lltut.h -#include "../test/lltut.h" - -using boost::assign::list_of; - -namespace tut -{ - struct string_index - { - }; - typedef test_group<string_index> string_index_t; - typedef string_index_t::object string_index_object_t; - tut::string_index_t tut_string_index("LLString"); - - template<> template<> - void string_index_object_t::test<1>() - { - std::string llstr1; - ensure("Empty std::string", (llstr1.size() == 0) && llstr1.empty()); - - std::string llstr2("Hello"); - ensure("std::string = Hello", (!strcmp(llstr2.c_str(), "Hello")) && (llstr2.size() == 5) && !llstr2.empty()); - - std::string llstr3(llstr2); - ensure("std::string = std::string(std::string)", (!strcmp(llstr3.c_str(), "Hello")) && (llstr3.size() == 5) && !llstr3.empty()); - - std::string str("Hello World"); - std::string llstr4(str, 6); - ensure("std::string = std::string(s, size_type pos, size_type n = npos)", (!strcmp(llstr4.c_str(), "World")) && (llstr4.size() == 5) && !llstr4.empty()); - - std::string llstr5(str, str.size()); - ensure("std::string = std::string(s, size_type pos, size_type n = npos)", (llstr5.size() == 0) && llstr5.empty()); - - std::string llstr6(5, 'A'); - ensure("std::string = std::string(count, c)", (!strcmp(llstr6.c_str(), "AAAAA")) && (llstr6.size() == 5) && !llstr6.empty()); - - std::string llstr7("Hello World", 5); - ensure("std::string(s, n)", (!strcmp(llstr7.c_str(), "Hello")) && (llstr7.size() == 5) && !llstr7.empty()); - - std::string llstr8("Hello World", 6, 5); - ensure("std::string(s, n, count)", (!strcmp(llstr8.c_str(), "World")) && (llstr8.size() == 5) && !llstr8.empty()); - - std::string llstr9("Hello World", sizeof("Hello World")-1, 5); // go past end - ensure("std::string(s, n, count) goes past end", (llstr9.size() == 0) && llstr9.empty()); - } - - template<> template<> - void string_index_object_t::test<3>() - { - std::string str("Len=5"); - ensure("isValidIndex failed", LLStringUtil::isValidIndex(str, 0) == true && - LLStringUtil::isValidIndex(str, 5) == true && - LLStringUtil::isValidIndex(str, 6) == false); - - std::string str1; - ensure("isValidIndex failed fo rempty string", LLStringUtil::isValidIndex(str1, 0) == false); - } - - template<> template<> - void string_index_object_t::test<4>() - { - std::string str_val(" Testing the extra whitespaces "); - LLStringUtil::trimHead(str_val); - ensure_equals("1: trimHead failed", str_val, "Testing the extra whitespaces "); - - std::string str_val1("\n\t\r\n Testing the extra whitespaces "); - LLStringUtil::trimHead(str_val1); - ensure_equals("2: trimHead failed", str_val1, "Testing the extra whitespaces "); - } - - template<> template<> - void string_index_object_t::test<5>() - { - std::string str_val(" Testing the extra whitespaces "); - LLStringUtil::trimTail(str_val); - ensure_equals("1: trimTail failed", str_val, " Testing the extra whitespaces"); - - std::string str_val1("\n Testing the extra whitespaces \n\t\r\n "); - LLStringUtil::trimTail(str_val1); - ensure_equals("2: trimTail failed", str_val1, "\n Testing the extra whitespaces"); - } - - - template<> template<> - void string_index_object_t::test<6>() - { - std::string str_val(" \t \r Testing the extra \r\n whitespaces \n \t "); - LLStringUtil::trim(str_val); - ensure_equals("1: trim failed", str_val, "Testing the extra \r\n whitespaces"); - } - - template<> template<> - void string_index_object_t::test<7>() - { - std::string str("Second LindenLabs"); - LLStringUtil::truncate(str, 6); - ensure_equals("1: truncate", str, "Second"); - - // further truncate more than the length - LLStringUtil::truncate(str, 0); - ensure_equals("2: truncate", str, ""); - } - - template<> template<> - void string_index_object_t::test<8>() - { - std::string str_val("SecondLife Source"); - LLStringUtil::toUpper(str_val); - ensure_equals("toUpper failed", str_val, "SECONDLIFE SOURCE"); - } - - template<> template<> - void string_index_object_t::test<9>() - { - std::string str_val("SecondLife Source"); - LLStringUtil::toLower(str_val); - ensure_equals("toLower failed", str_val, "secondlife source"); - } - - template<> template<> - void string_index_object_t::test<10>() - { - std::string str_val("Second"); - ensure("1. isHead failed", LLStringUtil::isHead(str_val, "SecondLife Source") == true); - ensure("2. isHead failed", LLStringUtil::isHead(str_val, " SecondLife Source") == false); - std::string str_val2(""); - ensure("3. isHead failed", LLStringUtil::isHead(str_val2, "") == false); - } - - template<> template<> - void string_index_object_t::test<11>() - { - std::string str_val("Hello.\n\n Lindenlabs. \n This is \na simple test.\n"); - std::string orig_str_val(str_val); - LLStringUtil::addCRLF(str_val); - ensure_equals("addCRLF failed", str_val, "Hello.\r\n\r\n Lindenlabs. \r\n This is \r\na simple test.\r\n"); - LLStringUtil::removeCRLF(str_val); - ensure_equals("removeCRLF failed", str_val, orig_str_val); - } - - template<> template<> - void string_index_object_t::test<12>() - { - std::string str_val("Hello.\n\n\t \t Lindenlabs. \t\t"); - std::string orig_str_val(str_val); - LLStringUtil::replaceTabsWithSpaces(str_val, 1); - ensure_equals("replaceTabsWithSpaces failed", str_val, "Hello.\n\n Lindenlabs. "); - LLStringUtil::replaceTabsWithSpaces(orig_str_val, 0); - ensure_equals("replaceTabsWithSpaces failed for 0", orig_str_val, "Hello.\n\n Lindenlabs. "); - - str_val = "\t\t\t\t"; - LLStringUtil::replaceTabsWithSpaces(str_val, 0); - ensure_equals("replaceTabsWithSpaces failed for all tabs", str_val, ""); - } - - template<> template<> - void string_index_object_t::test<13>() - { - std::string str_val("Hello.\n\n\t\t\r\nLindenlabsX."); - LLStringUtil::replaceNonstandardASCII(str_val, 'X'); - ensure_equals("replaceNonstandardASCII failed", str_val, "Hello.\n\nXXX\nLindenlabsX."); - } - - template<> template<> - void string_index_object_t::test<14>() - { - std::string str_val("Hello.\n\t\r\nABCDEFGHIABABAB"); - LLStringUtil::replaceChar(str_val, 'A', 'X'); - ensure_equals("1: replaceChar failed", str_val, "Hello.\n\t\r\nXBCDEFGHIXBXBXB"); - std::string str_val1("Hello.\n\t\r\nABCDEFGHIABABAB"); - } - - template<> template<> - void string_index_object_t::test<15>() - { - std::string str_val("Hello.\n\r\t"); - ensure("containsNonprintable failed", LLStringUtil::containsNonprintable(str_val) == true); - - str_val = "ABC "; - ensure("containsNonprintable failed", LLStringUtil::containsNonprintable(str_val) == false); - } - - template<> template<> - void string_index_object_t::test<16>() - { - std::string str_val("Hello.\n\r\t Again!"); - LLStringUtil::stripNonprintable(str_val); - ensure_equals("stripNonprintable failed", str_val, "Hello. Again!"); - - str_val = "\r\n\t\t"; - LLStringUtil::stripNonprintable(str_val); - ensure_equals("stripNonprintable resulting in empty string failed", str_val, ""); - - str_val = ""; - LLStringUtil::stripNonprintable(str_val); - ensure_equals("stripNonprintable of empty string resulting in empty string failed", str_val, ""); - } - - template<> template<> - void string_index_object_t::test<17>() - { - bool value; - std::string str_val("1"); - ensure("convertToBOOL 1 failed", LLStringUtil::convertToBOOL(str_val, value) && value); - str_val = "T"; - ensure("convertToBOOL T failed", LLStringUtil::convertToBOOL(str_val, value) && value); - str_val = "t"; - ensure("convertToBOOL t failed", LLStringUtil::convertToBOOL(str_val, value) && value); - str_val = "TRUE"; - ensure("convertToBOOL TRUE failed", LLStringUtil::convertToBOOL(str_val, value) && value); - str_val = "True"; - ensure("convertToBOOL True failed", LLStringUtil::convertToBOOL(str_val, value) && value); - str_val = "true"; - ensure("convertToBOOL true failed", LLStringUtil::convertToBOOL(str_val, value) && value); - - str_val = "0"; - ensure("convertToBOOL 0 failed", LLStringUtil::convertToBOOL(str_val, value) && !value); - str_val = "F"; - ensure("convertToBOOL F failed", LLStringUtil::convertToBOOL(str_val, value) && !value); - str_val = "f"; - ensure("convertToBOOL f failed", LLStringUtil::convertToBOOL(str_val, value) && !value); - str_val = "FALSE"; - ensure("convertToBOOL FASLE failed", LLStringUtil::convertToBOOL(str_val, value) && !value); - str_val = "False"; - ensure("convertToBOOL False failed", LLStringUtil::convertToBOOL(str_val, value) && !value); - str_val = "false"; - ensure("convertToBOOL false failed", LLStringUtil::convertToBOOL(str_val, value) && !value); - - str_val = "Tblah"; - ensure("convertToBOOL false failed", !LLStringUtil::convertToBOOL(str_val, value)); - } - - template<> template<> - void string_index_object_t::test<18>() - { - U8 value; - std::string str_val("255"); - ensure("1: convertToU8 failed", LLStringUtil::convertToU8(str_val, value) && value == 255); - - str_val = "0"; - ensure("2: convertToU8 failed", LLStringUtil::convertToU8(str_val, value) && value == 0); - - str_val = "-1"; - ensure("3: convertToU8 failed", !LLStringUtil::convertToU8(str_val, value)); - - str_val = "256"; // bigger than MAX_U8 - ensure("4: convertToU8 failed", !LLStringUtil::convertToU8(str_val, value)); - } - - template<> template<> - void string_index_object_t::test<19>() - { - S8 value; - std::string str_val("127"); - ensure("1: convertToS8 failed", LLStringUtil::convertToS8(str_val, value) && value == 127); - - str_val = "0"; - ensure("2: convertToS8 failed", LLStringUtil::convertToS8(str_val, value) && value == 0); - - str_val = "-128"; - ensure("3: convertToS8 failed", LLStringUtil::convertToS8(str_val, value) && value == -128); - - str_val = "128"; // bigger than MAX_S8 - ensure("4: convertToS8 failed", !LLStringUtil::convertToS8(str_val, value)); - - str_val = "-129"; - ensure("5: convertToS8 failed", !LLStringUtil::convertToS8(str_val, value)); - } - - template<> template<> - void string_index_object_t::test<20>() - { - S16 value; - std::string str_val("32767"); - ensure("1: convertToS16 failed", LLStringUtil::convertToS16(str_val, value) && value == 32767); - - str_val = "0"; - ensure("2: convertToS16 failed", LLStringUtil::convertToS16(str_val, value) && value == 0); - - str_val = "-32768"; - ensure("3: convertToS16 failed", LLStringUtil::convertToS16(str_val, value) && value == -32768); - - str_val = "32768"; - ensure("4: convertToS16 failed", !LLStringUtil::convertToS16(str_val, value)); - - str_val = "-32769"; - ensure("5: convertToS16 failed", !LLStringUtil::convertToS16(str_val, value)); - } - - template<> template<> - void string_index_object_t::test<21>() - { - U16 value; - std::string str_val("65535"); //0xFFFF - ensure("1: convertToU16 failed", LLStringUtil::convertToU16(str_val, value) && value == 65535); - - str_val = "0"; - ensure("2: convertToU16 failed", LLStringUtil::convertToU16(str_val, value) && value == 0); - - str_val = "-1"; - ensure("3: convertToU16 failed", !LLStringUtil::convertToU16(str_val, value)); - - str_val = "65536"; - ensure("4: convertToU16 failed", !LLStringUtil::convertToU16(str_val, value)); - } - - template<> template<> - void string_index_object_t::test<22>() - { - U32 value; - std::string str_val("4294967295"); //0xFFFFFFFF - ensure("1: convertToU32 failed", LLStringUtil::convertToU32(str_val, value) && value == 4294967295UL); - - str_val = "0"; - ensure("2: convertToU32 failed", LLStringUtil::convertToU32(str_val, value) && value == 0); - - str_val = "4294967296"; - ensure("3: convertToU32 failed", !LLStringUtil::convertToU32(str_val, value)); - } - - template<> template<> - void string_index_object_t::test<23>() - { - S32 value; - std::string str_val("2147483647"); //0x7FFFFFFF - ensure("1: convertToS32 failed", LLStringUtil::convertToS32(str_val, value) && value == 2147483647); - - str_val = "0"; - ensure("2: convertToS32 failed", LLStringUtil::convertToS32(str_val, value) && value == 0); - - // Avoid "unary minus operator applied to unsigned type" warning on VC++. JC - S32 min_val = -2147483647 - 1; - str_val = "-2147483648"; - ensure("3: convertToS32 failed", LLStringUtil::convertToS32(str_val, value) && value == min_val); - - str_val = "2147483648"; - ensure("4: convertToS32 failed", !LLStringUtil::convertToS32(str_val, value)); - - str_val = "-2147483649"; - ensure("5: convertToS32 failed", !LLStringUtil::convertToS32(str_val, value)); - } - - template<> template<> - void string_index_object_t::test<24>() - { - F32 value; - std::string str_val("2147483647"); //0x7FFFFFFF - ensure("1: convertToF32 failed", LLStringUtil::convertToF32(str_val, value) && value == 2147483647); - - str_val = "0"; - ensure("2: convertToF32 failed", LLStringUtil::convertToF32(str_val, value) && value == 0); - - /* Need to find max/min F32 values - str_val = "-2147483648"; - ensure("3: convertToF32 failed", LLStringUtil::convertToF32(str_val, value) && value == -2147483648); - - str_val = "2147483648"; - ensure("4: convertToF32 failed", !LLStringUtil::convertToF32(str_val, value)); - - str_val = "-2147483649"; - ensure("5: convertToF32 failed", !LLStringUtil::convertToF32(str_val, value)); - */ - } - - template<> template<> - void string_index_object_t::test<25>() - { - F64 value; - std::string str_val("9223372036854775807"); //0x7FFFFFFFFFFFFFFF - ensure("1: convertToF64 failed", LLStringUtil::convertToF64(str_val, value) && value == 9223372036854775807LL); - - str_val = "0"; - ensure("2: convertToF64 failed", LLStringUtil::convertToF64(str_val, value) && value == 0.0F); - - /* Need to find max/min F64 values - str_val = "-2147483648"; - ensure("3: convertToF32 failed", LLStringUtil::convertToF32(str_val, value) && value == -2147483648); - - str_val = "2147483648"; - ensure("4: convertToF32 failed", !LLStringUtil::convertToF32(str_val, value)); - - str_val = "-2147483649"; - ensure("5: convertToF32 failed", !LLStringUtil::convertToF32(str_val, value)); - */ - } - - template<> template<> - void string_index_object_t::test<26>() - { - const char* str1 = NULL; - const char* str2 = NULL; - - ensure("1: compareStrings failed", LLStringUtil::compareStrings(str1, str2) == 0); - str2 = "A"; - ensure("2: compareStrings failed", LLStringUtil::compareStrings(str1, str2) > 0); - ensure("3: compareStrings failed", LLStringUtil::compareStrings(str2, str1) < 0); - - str1 = "A is smaller than B"; - str2 = "B is greater than A"; - ensure("4: compareStrings failed", LLStringUtil::compareStrings(str1, str2) < 0); - - str2 = "A is smaller than B"; - ensure("5: compareStrings failed", LLStringUtil::compareStrings(str1, str2) == 0); - } - - template<> template<> - void string_index_object_t::test<27>() - { - const char* str1 = NULL; - const char* str2 = NULL; - - ensure("1: compareInsensitive failed", LLStringUtil::compareInsensitive(str1, str2) == 0); - str2 = "A"; - ensure("2: compareInsensitive failed", LLStringUtil::compareInsensitive(str1, str2) > 0); - ensure("3: compareInsensitive failed", LLStringUtil::compareInsensitive(str2, str1) < 0); - - str1 = "A is equal to a"; - str2 = "a is EQUAL to A"; - ensure("4: compareInsensitive failed", LLStringUtil::compareInsensitive(str1, str2) == 0); - } - - template<> template<> - void string_index_object_t::test<28>() - { - std::string lhs_str("PROgraM12files"); - std::string rhs_str("PROgram12Files"); - ensure("compareDict 1 failed", LLStringUtil::compareDict(lhs_str, rhs_str) < 0); - ensure("precedesDict 1 failed", LLStringUtil::precedesDict(lhs_str, rhs_str) == true); - - lhs_str = "PROgram12Files"; - rhs_str = "PROgram12Files"; - ensure("compareDict 2 failed", LLStringUtil::compareDict(lhs_str, rhs_str) == 0); - ensure("precedesDict 2 failed", LLStringUtil::precedesDict(lhs_str, rhs_str) == false); - - lhs_str = "PROgram12Files"; - rhs_str = "PROgRAM12FILES"; - ensure("compareDict 3 failed", LLStringUtil::compareDict(lhs_str, rhs_str) > 0); - ensure("precedesDict 3 failed", LLStringUtil::precedesDict(lhs_str, rhs_str) == false); - } - - template<> template<> - void string_index_object_t::test<29>() - { - char str1[] = "First String..."; - char str2[100]; - - LLStringUtil::copy(str2, str1, 100); - ensure("LLStringUtil::copy with enough dest length failed", strcmp(str2, str1) == 0); - LLStringUtil::copy(str2, str1, sizeof("First")); - ensure("LLStringUtil::copy with less dest length failed", strcmp(str2, "First") == 0); - } - - template<> template<> - void string_index_object_t::test<30>() - { - std::string str1 = "This is the sentence..."; - std::string str2 = "This is the "; - std::string str3 = "first "; - std::string str4 = "This is the first sentence..."; - std::string str5 = "This is the sentence...first "; - std::string dest; - - dest = str1; - LLStringUtil::copyInto(dest, str3, str2.length()); - ensure("LLStringUtil::copyInto insert failed", dest == str4); - - dest = str1; - LLStringUtil::copyInto(dest, str3, dest.length()); - ensure("LLStringUtil::copyInto append failed", dest == str5); - } - - template<> template<> - void string_index_object_t::test<31>() - { - std::string stripped; - - // Plain US ASCII text, including spaces and punctuation, - // should not be altered. - std::string simple_text = "Hello, world!"; - stripped = LLStringFn::strip_invalid_xml(simple_text); - ensure("Simple text passed unchanged", stripped == simple_text); - - // Control characters should be removed - // except for 0x09, 0x0a, 0x0d - std::string control_chars; - for (char c = 0x01; c < 0x20; c++) - { - control_chars.push_back(c); - } - std::string allowed_control_chars; - allowed_control_chars.push_back( (char)0x09 ); - allowed_control_chars.push_back( (char)0x0a ); - allowed_control_chars.push_back( (char)0x0d ); - - stripped = LLStringFn::strip_invalid_xml(control_chars); - ensure("Only tab, LF, CR control characters allowed", - stripped == allowed_control_chars); - - // UTF-8 should be passed intact, including high byte - // characters. Try Francais (with C squiggle cedilla) - std::string french = "Fran"; - french.push_back( (char)0xC3 ); - french.push_back( (char)0xA7 ); - french += "ais"; - stripped = LLStringFn::strip_invalid_xml( french ); - ensure("UTF-8 high byte text is allowed", french == stripped ); - } - - template<> template<> - void string_index_object_t::test<32>() - { - // Test LLStringUtil::format() string interpolation - LLStringUtil::format_map_t fmt_map; - std::string s; - int subcount; - - fmt_map["[TRICK1]"] = "[A]"; - fmt_map["[A]"] = "a"; - fmt_map["[B]"] = "b"; - fmt_map["[AAA]"] = "aaa"; - fmt_map["[BBB]"] = "bbb"; - fmt_map["[TRICK2]"] = "[A]"; - fmt_map["[EXPLOIT]"] = "!!!!!!!!!!!![EXPLOIT]!!!!!!!!!!!!"; - fmt_map["[KEYLONGER]"] = "short"; - fmt_map["[KEYSHORTER]"] = "Am I not a long string?"; - fmt_map["?"] = "?"; - fmt_map["[DELETE]"] = ""; - fmt_map["[]"] = "[]"; // doesn't do a substitution, but shouldn't crash either - - for (LLStringUtil::format_map_t::const_iterator iter = fmt_map.begin(); iter != fmt_map.end(); ++iter) - { - // Test when source string is entirely one key - std::string s1 = (std::string)iter->first; - std::string s2 = (std::string)iter->second; - subcount = LLStringUtil::format(s1, fmt_map); - ensure_equals("LLStringUtil::format: Raw interpolation result", s1, s2); - if (s1 == "?" || s1 == "[]") // no interp expected - { - ensure_equals("LLStringUtil::format: Raw interpolation result count", 0, subcount); - } - else - { - ensure_equals("LLStringUtil::format: Raw interpolation result count", 1, subcount); - } - } - - for (LLStringUtil::format_map_t::const_iterator iter = fmt_map.begin(); iter != fmt_map.end(); ++iter) - { - // Test when source string is one key, duplicated - std::string s1 = (std::string)iter->first; - std::string s2 = (std::string)iter->second; - s = s1 + s1 + s1 + s1; - subcount = LLStringUtil::format(s, fmt_map); - ensure_equals("LLStringUtil::format: Rawx4 interpolation result", s, s2 + s2 + s2 + s2); - if (s1 == "?" || s1 == "[]") // no interp expected - { - ensure_equals("LLStringUtil::format: Rawx4 interpolation result count", 0, subcount); - } - else - { - ensure_equals("LLStringUtil::format: Rawx4 interpolation result count", 4, subcount); - } - } - - // Test when source string has no keys - std::string srcs = "!!!!!!!!!!!!!!!!"; - s = srcs; - subcount = LLStringUtil::format(s, fmt_map); - ensure_equals("LLStringUtil::format: No key test result", s, srcs); - ensure_equals("LLStringUtil::format: No key test result count", 0, subcount); - - // Test when source string has no keys and is empty - std::string srcs3; - s = srcs3; - subcount = LLStringUtil::format(s, fmt_map); - ensure("LLStringUtil::format: No key test3 result", s.empty()); - ensure_equals("LLStringUtil::format: No key test3 result count", 0, subcount); - - // Test a substitution where a key is substituted with blankness - std::string srcs2 = "[DELETE]"; - s = srcs2; - subcount = LLStringUtil::format(s, fmt_map); - ensure("LLStringUtil::format: Delete key test2 result", s.empty()); - ensure_equals("LLStringUtil::format: Delete key test2 result count", 1, subcount); - - // Test an assorted substitution - std::string srcs4 = "[TRICK1][A][B][AAA][BBB][TRICK2][KEYLONGER][KEYSHORTER]?[DELETE]"; - s = srcs4; - subcount = LLStringUtil::format(s, fmt_map); - ensure_equals("LLStringUtil::format: Assorted Test1 result", s, "[A]abaaabbb[A]shortAm I not a long string??"); - ensure_equals("LLStringUtil::format: Assorted Test1 result count", 9, subcount); - - // Test an assorted substitution - std::string srcs5 = "[DELETE]?[KEYSHORTER][KEYLONGER][TRICK2][BBB][AAA][B][A][TRICK1]"; - s = srcs5; - subcount = LLStringUtil::format(s, fmt_map); - ensure_equals("LLStringUtil::format: Assorted Test2 result", s, "?Am I not a long string?short[A]bbbaaaba[A]"); - ensure_equals("LLStringUtil::format: Assorted Test2 result count", 9, subcount); - - // Test on nested brackets - std::string srcs6 = "[[TRICK1]][[A]][[B]][[AAA]][[BBB]][[TRICK2]][[KEYLONGER]][[KEYSHORTER]]?[[DELETE]]"; - s = srcs6; - subcount = LLStringUtil::format(s, fmt_map); - ensure_equals("LLStringUtil::format: Assorted Test2 result", s, "[[A]][a][b][aaa][bbb][[A]][short][Am I not a long string?]?[]"); - ensure_equals("LLStringUtil::format: Assorted Test2 result count", 9, subcount); - - - // Test an assorted substitution - std::string srcs8 = "foo[DELETE]bar?"; - s = srcs8; - subcount = LLStringUtil::format(s, fmt_map); - ensure_equals("LLStringUtil::format: Assorted Test3 result", s, "foobar?"); - ensure_equals("LLStringUtil::format: Assorted Test3 result count", 1, subcount); - } - - template<> template<> - void string_index_object_t::test<33>() - { - // Test LLStringUtil::format() string interpolation - LLStringUtil::format_map_t blank_fmt_map; - std::string s; - int subcount; - - // Test substituting out of a blank format_map - std::string srcs6 = "12345"; - s = srcs6; - subcount = LLStringUtil::format(s, blank_fmt_map); - ensure_equals("LLStringUtil::format: Blankfmt Test1 result", s, "12345"); - ensure_equals("LLStringUtil::format: Blankfmt Test1 result count", 0, subcount); - - // Test substituting a blank string out of a blank format_map - std::string srcs7; - s = srcs7; - subcount = LLStringUtil::format(s, blank_fmt_map); - ensure("LLStringUtil::format: Blankfmt Test2 result", s.empty()); - ensure_equals("LLStringUtil::format: Blankfmt Test2 result count", 0, subcount); - } - - template<> template<> - void string_index_object_t::test<34>() - { - // Test that incorrect LLStringUtil::format() use does not explode. - LLStringUtil::format_map_t nasty_fmt_map; - std::string s; - int subcount; - - nasty_fmt_map[""] = "never used"; // see, this is nasty. - - // Test substituting out of a nasty format_map - std::string srcs6 = "12345"; - s = srcs6; - subcount = LLStringUtil::format(s, nasty_fmt_map); - ensure_equals("LLStringUtil::format: Nastyfmt Test1 result", s, "12345"); - ensure_equals("LLStringUtil::format: Nastyfmt Test1 result count", 0, subcount); - - // Test substituting a blank string out of a nasty format_map - std::string srcs7; - s = srcs7; - subcount = LLStringUtil::format(s, nasty_fmt_map); - ensure("LLStringUtil::format: Nastyfmt Test2 result", s.empty()); - ensure_equals("LLStringUtil::format: Nastyfmt Test2 result count", 0, subcount); - } - - template<> template<> - void string_index_object_t::test<35>() - { - // Make sure startsWith works - std::string string("anybody in there?"); - std::string substr("anybody"); - ensure("startsWith works.", LLStringUtil::startsWith(string, substr)); - } - - template<> template<> - void string_index_object_t::test<36>() - { - // Make sure startsWith correctly fails - std::string string("anybody in there?"); - std::string substr("there"); - ensure("startsWith fails.", !LLStringUtil::startsWith(string, substr)); - } - - template<> template<> - void string_index_object_t::test<37>() - { - // startsWith fails on empty strings - std::string value("anybody in there?"); - std::string empty; - ensure("empty string.", !LLStringUtil::startsWith(value, empty)); - ensure("empty substr.", !LLStringUtil::startsWith(empty, value)); - ensure("empty everything.", !LLStringUtil::startsWith(empty, empty)); - } - - template<> template<> - void string_index_object_t::test<38>() - { - // Make sure endsWith works correctly - std::string string("anybody in there?"); - std::string substr("there?"); - ensure("endsWith works.", LLStringUtil::endsWith(string, substr)); - } - - template<> template<> - void string_index_object_t::test<39>() - { - // Make sure endsWith correctly fails - std::string string("anybody in there?"); - std::string substr("anybody"); - ensure("endsWith fails.", !LLStringUtil::endsWith(string, substr)); - substr = "there"; - ensure("endsWith fails.", !LLStringUtil::endsWith(string, substr)); - substr = "ther?"; - ensure("endsWith fails.", !LLStringUtil::endsWith(string, substr)); - } - - template<> template<> - void string_index_object_t::test<40>() - { - // endsWith fails on empty strings - std::string value("anybody in there?"); - std::string empty; - ensure("empty string.", !LLStringUtil::endsWith(value, empty)); - ensure("empty substr.", !LLStringUtil::endsWith(empty, value)); - ensure("empty everything.", !LLStringUtil::endsWith(empty, empty)); - } - - template<> template<> - void string_index_object_t::test<41>() - { - set_test_name("getTokens(\"delims\")"); - ensure_equals("empty string", LLStringUtil::getTokens("", " "), StringVec()); - ensure_equals("only delims", - LLStringUtil::getTokens(" \r\n ", " \r\n"), StringVec()); - ensure_equals("sequence of delims", - LLStringUtil::getTokens(",,, one ,,,", ","), list_of("one")); - // nat considers this a dubious implementation side effect, but I'd - // hate to change it now... - ensure_equals("noncontiguous tokens", - LLStringUtil::getTokens(", ,, , one ,,,", ","), list_of("")("")("one")); - ensure_equals("space-padded tokens", - LLStringUtil::getTokens(", one , two ,", ","), list_of("one")("two")); - ensure_equals("no delims", LLStringUtil::getTokens("one", ","), list_of("one")); - } - - // Shorthand for verifying that getTokens() behaves the same when you - // don't pass a string of escape characters, when you pass an empty string - // (different overloads), and when you pass a string of characters that - // aren't actually present. - void ensure_getTokens(const std::string& desc, - const std::string& string, - const std::string& drop_delims, - const std::string& keep_delims, - const std::string& quotes, - const std::vector<std::string>& expect) - { - ensure_equals(desc + " - no esc", - LLStringUtil::getTokens(string, drop_delims, keep_delims, quotes), - expect); - ensure_equals(desc + " - empty esc", - LLStringUtil::getTokens(string, drop_delims, keep_delims, quotes, ""), - expect); - ensure_equals(desc + " - unused esc", - LLStringUtil::getTokens(string, drop_delims, keep_delims, quotes, "!"), - expect); - } - - void ensure_getTokens(const std::string& desc, - const std::string& string, - const std::string& drop_delims, - const std::string& keep_delims, - const std::vector<std::string>& expect) - { - ensure_getTokens(desc, string, drop_delims, keep_delims, "", expect); - } - - template<> template<> - void string_index_object_t::test<42>() - { - set_test_name("getTokens(\"delims\", etc.)"); - // Signatures to test in this method: - // getTokens(string, drop_delims, keep_delims [, quotes [, escapes]]) - // If you omit keep_delims, you get the older function (test above). - - // cases like the getTokens(string, delims) tests above - ensure_getTokens("empty string", "", " ", "", StringVec()); - ensure_getTokens("only delims", - " \r\n ", " \r\n", "", StringVec()); - ensure_getTokens("sequence of delims", - ",,, one ,,,", ", ", "", list_of("one")); - // Note contrast with the case in the previous method - ensure_getTokens("noncontiguous tokens", - ", ,, , one ,,,", ", ", "", list_of("one")); - ensure_getTokens("space-padded tokens", - ", one , two ,", ", ", "", - list_of("one")("two")); - ensure_getTokens("no delims", "one", ",", "", list_of("one")); - - // drop_delims vs. keep_delims - ensure_getTokens("arithmetic", - " ab+def / xx* yy ", " ", "+-*/", - list_of("ab")("+")("def")("/")("xx")("*")("yy")); - - // quotes - ensure_getTokens("no quotes", - "She said, \"Don't go.\"", " ", ",", "", - list_of("She")("said")(",")("\"Don't")("go.\"")); - ensure_getTokens("quotes", - "She said, \"Don't go.\"", " ", ",", "\"", - list_of("She")("said")(",")("Don't go.")); - ensure_getTokens("quotes and delims", - "run c:/'Documents and Settings'/someone", " ", "", "'", - list_of("run")("c:/Documents and Settings/someone")); - ensure_getTokens("unmatched quote", - "baby don't leave", " ", "", "'", - list_of("baby")("don't")("leave")); - ensure_getTokens("adjacent quoted", - "abc'def \"ghi'\"jkl' mno\"pqr", " ", "", "\"'", - list_of("abcdef \"ghijkl' mnopqr")); - ensure_getTokens("quoted empty string", - "--set SomeVar ''", " ", "", "'", - list_of("--set")("SomeVar")("")); - - // escapes - // Don't use backslash as an escape for these tests -- you'll go nuts - // between the C++ string scanner and getTokens() escapes. Test with - // something else! - ensure_equals("escaped delims", - LLStringUtil::getTokens("^ a - dog^-gone^ phrase", " ", "-", "", "^"), - list_of(" a")("-")("dog-gone phrase")); - ensure_equals("escaped quotes", - LLStringUtil::getTokens("say: 'this isn^'t w^orking'.", " ", "", "'", "^"), - list_of("say:")("this isn't working.")); - ensure_equals("escaped escape", - LLStringUtil::getTokens("want x^^2", " ", "", "", "^"), - list_of("want")("x^2")); - ensure_equals("escape at end", - LLStringUtil::getTokens("it's^ up there^", " ", "", "'", "^"), - list_of("it's up")("there^")); - } -} +/**
+ * @file llstring_test.cpp
+ * @author Adroit, Steve Linden, Tofu Linden
+ * @date 2006-12-24
+ * @brief Test cases of llstring.cpp
+ *
+ * $LicenseInfo:firstyear=2007&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2010, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "linden_common.h"
+
+#include <boost/assign/list_of.hpp>
+#include "../llstring.h"
+#include "StringVec.h" // must come BEFORE lltut.h
+#include "../test/lltut.h"
+
+using boost::assign::list_of;
+
+namespace tut
+{
+ struct string_index
+ {
+ };
+ typedef test_group<string_index> string_index_t;
+ typedef string_index_t::object string_index_object_t;
+ tut::string_index_t tut_string_index("LLString");
+
+ template<> template<>
+ void string_index_object_t::test<1>()
+ {
+ std::string llstr1;
+ ensure("Empty std::string", (llstr1.size() == 0) && llstr1.empty());
+
+ std::string llstr2("Hello");
+ ensure("std::string = Hello", (!strcmp(llstr2.c_str(), "Hello")) && (llstr2.size() == 5) && !llstr2.empty());
+
+ std::string llstr3(llstr2);
+ ensure("std::string = std::string(std::string)", (!strcmp(llstr3.c_str(), "Hello")) && (llstr3.size() == 5) && !llstr3.empty());
+
+ std::string str("Hello World");
+ std::string llstr4(str, 6);
+ ensure("std::string = std::string(s, size_type pos, size_type n = npos)", (!strcmp(llstr4.c_str(), "World")) && (llstr4.size() == 5) && !llstr4.empty());
+
+ std::string llstr5(str, str.size());
+ ensure("std::string = std::string(s, size_type pos, size_type n = npos)", (llstr5.size() == 0) && llstr5.empty());
+
+ std::string llstr6(5, 'A');
+ ensure("std::string = std::string(count, c)", (!strcmp(llstr6.c_str(), "AAAAA")) && (llstr6.size() == 5) && !llstr6.empty());
+
+ std::string llstr7("Hello World", 5);
+ ensure("std::string(s, n)", (!strcmp(llstr7.c_str(), "Hello")) && (llstr7.size() == 5) && !llstr7.empty());
+
+ std::string llstr8("Hello World", 6, 5);
+ ensure("std::string(s, n, count)", (!strcmp(llstr8.c_str(), "World")) && (llstr8.size() == 5) && !llstr8.empty());
+
+ std::string llstr9("Hello World", sizeof("Hello World")-1, 5); // go past end
+ ensure("std::string(s, n, count) goes past end", (llstr9.size() == 0) && llstr9.empty());
+ }
+
+ template<> template<>
+ void string_index_object_t::test<3>()
+ {
+ std::string str("Len=5");
+ ensure("isValidIndex failed", LLStringUtil::isValidIndex(str, 0) == true &&
+ LLStringUtil::isValidIndex(str, 5) == true &&
+ LLStringUtil::isValidIndex(str, 6) == false);
+
+ std::string str1;
+ ensure("isValidIndex failed fo rempty string", LLStringUtil::isValidIndex(str1, 0) == false);
+ }
+
+ template<> template<>
+ void string_index_object_t::test<4>()
+ {
+ std::string str_val(" Testing the extra whitespaces ");
+ LLStringUtil::trimHead(str_val);
+ ensure_equals("1: trimHead failed", str_val, "Testing the extra whitespaces ");
+
+ std::string str_val1("\n\t\r\n Testing the extra whitespaces ");
+ LLStringUtil::trimHead(str_val1);
+ ensure_equals("2: trimHead failed", str_val1, "Testing the extra whitespaces ");
+ }
+
+ template<> template<>
+ void string_index_object_t::test<5>()
+ {
+ std::string str_val(" Testing the extra whitespaces ");
+ LLStringUtil::trimTail(str_val);
+ ensure_equals("1: trimTail failed", str_val, " Testing the extra whitespaces");
+
+ std::string str_val1("\n Testing the extra whitespaces \n\t\r\n ");
+ LLStringUtil::trimTail(str_val1);
+ ensure_equals("2: trimTail failed", str_val1, "\n Testing the extra whitespaces");
+ }
+
+
+ template<> template<>
+ void string_index_object_t::test<6>()
+ {
+ std::string str_val(" \t \r Testing the extra \r\n whitespaces \n \t ");
+ LLStringUtil::trim(str_val);
+ ensure_equals("1: trim failed", str_val, "Testing the extra \r\n whitespaces");
+ }
+
+ template<> template<>
+ void string_index_object_t::test<7>()
+ {
+ std::string str("Second LindenLabs");
+ LLStringUtil::truncate(str, 6);
+ ensure_equals("1: truncate", str, "Second");
+
+ // further truncate more than the length
+ LLStringUtil::truncate(str, 0);
+ ensure_equals("2: truncate", str, "");
+ }
+
+ template<> template<>
+ void string_index_object_t::test<8>()
+ {
+ std::string str_val("SecondLife Source");
+ LLStringUtil::toUpper(str_val);
+ ensure_equals("toUpper failed", str_val, "SECONDLIFE SOURCE");
+ }
+
+ template<> template<>
+ void string_index_object_t::test<9>()
+ {
+ std::string str_val("SecondLife Source");
+ LLStringUtil::toLower(str_val);
+ ensure_equals("toLower failed", str_val, "secondlife source");
+ }
+
+ template<> template<>
+ void string_index_object_t::test<10>()
+ {
+ std::string str_val("Second");
+ ensure("1. isHead failed", LLStringUtil::isHead(str_val, "SecondLife Source") == true);
+ ensure("2. isHead failed", LLStringUtil::isHead(str_val, " SecondLife Source") == false);
+ std::string str_val2("");
+ ensure("3. isHead failed", LLStringUtil::isHead(str_val2, "") == false);
+ }
+
+ template<> template<>
+ void string_index_object_t::test<11>()
+ {
+ std::string str_val("Hello.\n\n Lindenlabs. \n This is \na simple test.\n");
+ std::string orig_str_val(str_val);
+ LLStringUtil::addCRLF(str_val);
+ ensure_equals("addCRLF failed", str_val, "Hello.\r\n\r\n Lindenlabs. \r\n This is \r\na simple test.\r\n");
+ LLStringUtil::removeCRLF(str_val);
+ ensure_equals("removeCRLF failed", str_val, orig_str_val);
+ }
+
+ template<> template<>
+ void string_index_object_t::test<12>()
+ {
+ std::string str_val("Hello.\n\n\t \t Lindenlabs. \t\t");
+ std::string orig_str_val(str_val);
+ LLStringUtil::replaceTabsWithSpaces(str_val, 1);
+ ensure_equals("replaceTabsWithSpaces failed", str_val, "Hello.\n\n Lindenlabs. ");
+ LLStringUtil::replaceTabsWithSpaces(orig_str_val, 0);
+ ensure_equals("replaceTabsWithSpaces failed for 0", orig_str_val, "Hello.\n\n Lindenlabs. ");
+
+ str_val = "\t\t\t\t";
+ LLStringUtil::replaceTabsWithSpaces(str_val, 0);
+ ensure_equals("replaceTabsWithSpaces failed for all tabs", str_val, "");
+ }
+
+ template<> template<>
+ void string_index_object_t::test<13>()
+ {
+ std::string str_val("Hello.\n\n\t\t\r\nLindenlabsX.");
+ LLStringUtil::replaceNonstandardASCII(str_val, 'X');
+ ensure_equals("replaceNonstandardASCII failed", str_val, "Hello.\n\nXXX\nLindenlabsX.");
+ }
+
+ template<> template<>
+ void string_index_object_t::test<14>()
+ {
+ std::string str_val("Hello.\n\t\r\nABCDEFGHIABABAB");
+ LLStringUtil::replaceChar(str_val, 'A', 'X');
+ ensure_equals("1: replaceChar failed", str_val, "Hello.\n\t\r\nXBCDEFGHIXBXBXB");
+ std::string str_val1("Hello.\n\t\r\nABCDEFGHIABABAB");
+ }
+
+ template<> template<>
+ void string_index_object_t::test<15>()
+ {
+ std::string str_val("Hello.\n\r\t");
+ ensure("containsNonprintable failed", LLStringUtil::containsNonprintable(str_val) == true);
+
+ str_val = "ABC ";
+ ensure("containsNonprintable failed", LLStringUtil::containsNonprintable(str_val) == false);
+ }
+
+ template<> template<>
+ void string_index_object_t::test<16>()
+ {
+ std::string str_val("Hello.\n\r\t Again!");
+ LLStringUtil::stripNonprintable(str_val);
+ ensure_equals("stripNonprintable failed", str_val, "Hello. Again!");
+
+ str_val = "\r\n\t\t";
+ LLStringUtil::stripNonprintable(str_val);
+ ensure_equals("stripNonprintable resulting in empty string failed", str_val, "");
+
+ str_val = "";
+ LLStringUtil::stripNonprintable(str_val);
+ ensure_equals("stripNonprintable of empty string resulting in empty string failed", str_val, "");
+ }
+
+ template<> template<>
+ void string_index_object_t::test<17>()
+ {
+ bool value;
+ std::string str_val("1");
+ ensure("convertToBOOL 1 failed", LLStringUtil::convertToBOOL(str_val, value) && value);
+ str_val = "T";
+ ensure("convertToBOOL T failed", LLStringUtil::convertToBOOL(str_val, value) && value);
+ str_val = "t";
+ ensure("convertToBOOL t failed", LLStringUtil::convertToBOOL(str_val, value) && value);
+ str_val = "TRUE";
+ ensure("convertToBOOL TRUE failed", LLStringUtil::convertToBOOL(str_val, value) && value);
+ str_val = "True";
+ ensure("convertToBOOL True failed", LLStringUtil::convertToBOOL(str_val, value) && value);
+ str_val = "true";
+ ensure("convertToBOOL true failed", LLStringUtil::convertToBOOL(str_val, value) && value);
+
+ str_val = "0";
+ ensure("convertToBOOL 0 failed", LLStringUtil::convertToBOOL(str_val, value) && !value);
+ str_val = "F";
+ ensure("convertToBOOL F failed", LLStringUtil::convertToBOOL(str_val, value) && !value);
+ str_val = "f";
+ ensure("convertToBOOL f failed", LLStringUtil::convertToBOOL(str_val, value) && !value);
+ str_val = "FALSE";
+ ensure("convertToBOOL FASLE failed", LLStringUtil::convertToBOOL(str_val, value) && !value);
+ str_val = "False";
+ ensure("convertToBOOL False failed", LLStringUtil::convertToBOOL(str_val, value) && !value);
+ str_val = "false";
+ ensure("convertToBOOL false failed", LLStringUtil::convertToBOOL(str_val, value) && !value);
+
+ str_val = "Tblah";
+ ensure("convertToBOOL false failed", !LLStringUtil::convertToBOOL(str_val, value));
+ }
+
+ template<> template<>
+ void string_index_object_t::test<18>()
+ {
+ U8 value;
+ std::string str_val("255");
+ ensure("1: convertToU8 failed", LLStringUtil::convertToU8(str_val, value) && value == 255);
+
+ str_val = "0";
+ ensure("2: convertToU8 failed", LLStringUtil::convertToU8(str_val, value) && value == 0);
+
+ str_val = "-1";
+ ensure("3: convertToU8 failed", !LLStringUtil::convertToU8(str_val, value));
+
+ str_val = "256"; // bigger than MAX_U8
+ ensure("4: convertToU8 failed", !LLStringUtil::convertToU8(str_val, value));
+ }
+
+ template<> template<>
+ void string_index_object_t::test<19>()
+ {
+ S8 value;
+ std::string str_val("127");
+ ensure("1: convertToS8 failed", LLStringUtil::convertToS8(str_val, value) && value == 127);
+
+ str_val = "0";
+ ensure("2: convertToS8 failed", LLStringUtil::convertToS8(str_val, value) && value == 0);
+
+ str_val = "-128";
+ ensure("3: convertToS8 failed", LLStringUtil::convertToS8(str_val, value) && value == -128);
+
+ str_val = "128"; // bigger than MAX_S8
+ ensure("4: convertToS8 failed", !LLStringUtil::convertToS8(str_val, value));
+
+ str_val = "-129";
+ ensure("5: convertToS8 failed", !LLStringUtil::convertToS8(str_val, value));
+ }
+
+ template<> template<>
+ void string_index_object_t::test<20>()
+ {
+ S16 value;
+ std::string str_val("32767");
+ ensure("1: convertToS16 failed", LLStringUtil::convertToS16(str_val, value) && value == 32767);
+
+ str_val = "0";
+ ensure("2: convertToS16 failed", LLStringUtil::convertToS16(str_val, value) && value == 0);
+
+ str_val = "-32768";
+ ensure("3: convertToS16 failed", LLStringUtil::convertToS16(str_val, value) && value == -32768);
+
+ str_val = "32768";
+ ensure("4: convertToS16 failed", !LLStringUtil::convertToS16(str_val, value));
+
+ str_val = "-32769";
+ ensure("5: convertToS16 failed", !LLStringUtil::convertToS16(str_val, value));
+ }
+
+ template<> template<>
+ void string_index_object_t::test<21>()
+ {
+ U16 value;
+ std::string str_val("65535"); //0xFFFF
+ ensure("1: convertToU16 failed", LLStringUtil::convertToU16(str_val, value) && value == 65535);
+
+ str_val = "0";
+ ensure("2: convertToU16 failed", LLStringUtil::convertToU16(str_val, value) && value == 0);
+
+ str_val = "-1";
+ ensure("3: convertToU16 failed", !LLStringUtil::convertToU16(str_val, value));
+
+ str_val = "65536";
+ ensure("4: convertToU16 failed", !LLStringUtil::convertToU16(str_val, value));
+ }
+
+ template<> template<>
+ void string_index_object_t::test<22>()
+ {
+ U32 value;
+ std::string str_val("4294967295"); //0xFFFFFFFF
+ ensure("1: convertToU32 failed", LLStringUtil::convertToU32(str_val, value) && value == 4294967295UL);
+
+ str_val = "0";
+ ensure("2: convertToU32 failed", LLStringUtil::convertToU32(str_val, value) && value == 0);
+
+ str_val = "4294967296";
+ ensure("3: convertToU32 failed", !LLStringUtil::convertToU32(str_val, value));
+ }
+
+ template<> template<>
+ void string_index_object_t::test<23>()
+ {
+ S32 value;
+ std::string str_val("2147483647"); //0x7FFFFFFF
+ ensure("1: convertToS32 failed", LLStringUtil::convertToS32(str_val, value) && value == 2147483647);
+
+ str_val = "0";
+ ensure("2: convertToS32 failed", LLStringUtil::convertToS32(str_val, value) && value == 0);
+
+ // Avoid "unary minus operator applied to unsigned type" warning on VC++. JC
+ S32 min_val = -2147483647 - 1;
+ str_val = "-2147483648";
+ ensure("3: convertToS32 failed", LLStringUtil::convertToS32(str_val, value) && value == min_val);
+
+ str_val = "2147483648";
+ ensure("4: convertToS32 failed", !LLStringUtil::convertToS32(str_val, value));
+
+ str_val = "-2147483649";
+ ensure("5: convertToS32 failed", !LLStringUtil::convertToS32(str_val, value));
+ }
+
+ template<> template<>
+ void string_index_object_t::test<24>()
+ {
+ F32 value;
+ std::string str_val("2147483647"); //0x7FFFFFFF
+ ensure("1: convertToF32 failed", LLStringUtil::convertToF32(str_val, value) && value == 2147483647);
+
+ str_val = "0";
+ ensure("2: convertToF32 failed", LLStringUtil::convertToF32(str_val, value) && value == 0);
+
+ /* Need to find max/min F32 values
+ str_val = "-2147483648";
+ ensure("3: convertToF32 failed", LLStringUtil::convertToF32(str_val, value) && value == -2147483648);
+
+ str_val = "2147483648";
+ ensure("4: convertToF32 failed", !LLStringUtil::convertToF32(str_val, value));
+
+ str_val = "-2147483649";
+ ensure("5: convertToF32 failed", !LLStringUtil::convertToF32(str_val, value));
+ */
+ }
+
+ template<> template<>
+ void string_index_object_t::test<25>()
+ {
+ F64 value;
+ std::string str_val("9223372036854775807"); //0x7FFFFFFFFFFFFFFF
+ ensure("1: convertToF64 failed", LLStringUtil::convertToF64(str_val, value) && value == 9223372036854775807LL);
+
+ str_val = "0";
+ ensure("2: convertToF64 failed", LLStringUtil::convertToF64(str_val, value) && value == 0.0F);
+
+ /* Need to find max/min F64 values
+ str_val = "-2147483648";
+ ensure("3: convertToF32 failed", LLStringUtil::convertToF32(str_val, value) && value == -2147483648);
+
+ str_val = "2147483648";
+ ensure("4: convertToF32 failed", !LLStringUtil::convertToF32(str_val, value));
+
+ str_val = "-2147483649";
+ ensure("5: convertToF32 failed", !LLStringUtil::convertToF32(str_val, value));
+ */
+ }
+
+ template<> template<>
+ void string_index_object_t::test<26>()
+ {
+ const char* str1 = NULL;
+ const char* str2 = NULL;
+
+ ensure("1: compareStrings failed", LLStringUtil::compareStrings(str1, str2) == 0);
+ str2 = "A";
+ ensure("2: compareStrings failed", LLStringUtil::compareStrings(str1, str2) > 0);
+ ensure("3: compareStrings failed", LLStringUtil::compareStrings(str2, str1) < 0);
+
+ str1 = "A is smaller than B";
+ str2 = "B is greater than A";
+ ensure("4: compareStrings failed", LLStringUtil::compareStrings(str1, str2) < 0);
+
+ str2 = "A is smaller than B";
+ ensure("5: compareStrings failed", LLStringUtil::compareStrings(str1, str2) == 0);
+ }
+
+ template<> template<>
+ void string_index_object_t::test<27>()
+ {
+ const char* str1 = NULL;
+ const char* str2 = NULL;
+
+ ensure("1: compareInsensitive failed", LLStringUtil::compareInsensitive(str1, str2) == 0);
+ str2 = "A";
+ ensure("2: compareInsensitive failed", LLStringUtil::compareInsensitive(str1, str2) > 0);
+ ensure("3: compareInsensitive failed", LLStringUtil::compareInsensitive(str2, str1) < 0);
+
+ str1 = "A is equal to a";
+ str2 = "a is EQUAL to A";
+ ensure("4: compareInsensitive failed", LLStringUtil::compareInsensitive(str1, str2) == 0);
+ }
+
+ template<> template<>
+ void string_index_object_t::test<28>()
+ {
+ std::string lhs_str("PROgraM12files");
+ std::string rhs_str("PROgram12Files");
+ ensure("compareDict 1 failed", LLStringUtil::compareDict(lhs_str, rhs_str) < 0);
+ ensure("precedesDict 1 failed", LLStringUtil::precedesDict(lhs_str, rhs_str) == true);
+
+ lhs_str = "PROgram12Files";
+ rhs_str = "PROgram12Files";
+ ensure("compareDict 2 failed", LLStringUtil::compareDict(lhs_str, rhs_str) == 0);
+ ensure("precedesDict 2 failed", LLStringUtil::precedesDict(lhs_str, rhs_str) == false);
+
+ lhs_str = "PROgram12Files";
+ rhs_str = "PROgRAM12FILES";
+ ensure("compareDict 3 failed", LLStringUtil::compareDict(lhs_str, rhs_str) > 0);
+ ensure("precedesDict 3 failed", LLStringUtil::precedesDict(lhs_str, rhs_str) == false);
+ }
+
+ template<> template<>
+ void string_index_object_t::test<29>()
+ {
+ char str1[] = "First String...";
+ char str2[100];
+
+ LLStringUtil::copy(str2, str1, 100);
+ ensure("LLStringUtil::copy with enough dest length failed", strcmp(str2, str1) == 0);
+ LLStringUtil::copy(str2, str1, sizeof("First"));
+ ensure("LLStringUtil::copy with less dest length failed", strcmp(str2, "First") == 0);
+ }
+
+ template<> template<>
+ void string_index_object_t::test<30>()
+ {
+ std::string str1 = "This is the sentence...";
+ std::string str2 = "This is the ";
+ std::string str3 = "first ";
+ std::string str4 = "This is the first sentence...";
+ std::string str5 = "This is the sentence...first ";
+ std::string dest;
+
+ dest = str1;
+ LLStringUtil::copyInto(dest, str3, str2.length());
+ ensure("LLStringUtil::copyInto insert failed", dest == str4);
+
+ dest = str1;
+ LLStringUtil::copyInto(dest, str3, dest.length());
+ ensure("LLStringUtil::copyInto append failed", dest == str5);
+ }
+
+ template<> template<>
+ void string_index_object_t::test<31>()
+ {
+ std::string stripped;
+
+ // Plain US ASCII text, including spaces and punctuation,
+ // should not be altered.
+ std::string simple_text = "Hello, world!";
+ stripped = LLStringFn::strip_invalid_xml(simple_text);
+ ensure("Simple text passed unchanged", stripped == simple_text);
+
+ // Control characters should be removed
+ // except for 0x09, 0x0a, 0x0d
+ std::string control_chars;
+ for (char c = 0x01; c < 0x20; c++)
+ {
+ control_chars.push_back(c);
+ }
+ std::string allowed_control_chars;
+ allowed_control_chars.push_back( (char)0x09 );
+ allowed_control_chars.push_back( (char)0x0a );
+ allowed_control_chars.push_back( (char)0x0d );
+
+ stripped = LLStringFn::strip_invalid_xml(control_chars);
+ ensure("Only tab, LF, CR control characters allowed",
+ stripped == allowed_control_chars);
+
+ // UTF-8 should be passed intact, including high byte
+ // characters. Try Francais (with C squiggle cedilla)
+ std::string french = "Fran";
+ french.push_back( (char)0xC3 );
+ french.push_back( (char)0xA7 );
+ french += "ais";
+ stripped = LLStringFn::strip_invalid_xml( french );
+ ensure("UTF-8 high byte text is allowed", french == stripped );
+ }
+
+ template<> template<>
+ void string_index_object_t::test<32>()
+ {
+ // Test LLStringUtil::format() string interpolation
+ LLStringUtil::format_map_t fmt_map;
+ std::string s;
+ int subcount;
+
+ fmt_map["[TRICK1]"] = "[A]";
+ fmt_map["[A]"] = "a";
+ fmt_map["[B]"] = "b";
+ fmt_map["[AAA]"] = "aaa";
+ fmt_map["[BBB]"] = "bbb";
+ fmt_map["[TRICK2]"] = "[A]";
+ fmt_map["[EXPLOIT]"] = "!!!!!!!!!!!![EXPLOIT]!!!!!!!!!!!!";
+ fmt_map["[KEYLONGER]"] = "short";
+ fmt_map["[KEYSHORTER]"] = "Am I not a long string?";
+ fmt_map["?"] = "?";
+ fmt_map["[DELETE]"] = "";
+ fmt_map["[]"] = "[]"; // doesn't do a substitution, but shouldn't crash either
+
+ for (LLStringUtil::format_map_t::const_iterator iter = fmt_map.begin(); iter != fmt_map.end(); ++iter)
+ {
+ // Test when source string is entirely one key
+ std::string s1 = (std::string)iter->first;
+ std::string s2 = (std::string)iter->second;
+ subcount = LLStringUtil::format(s1, fmt_map);
+ ensure_equals("LLStringUtil::format: Raw interpolation result", s1, s2);
+ if (s1 == "?" || s1 == "[]") // no interp expected
+ {
+ ensure_equals("LLStringUtil::format: Raw interpolation result count", 0, subcount);
+ }
+ else
+ {
+ ensure_equals("LLStringUtil::format: Raw interpolation result count", 1, subcount);
+ }
+ }
+
+ for (LLStringUtil::format_map_t::const_iterator iter = fmt_map.begin(); iter != fmt_map.end(); ++iter)
+ {
+ // Test when source string is one key, duplicated
+ std::string s1 = (std::string)iter->first;
+ std::string s2 = (std::string)iter->second;
+ s = s1 + s1 + s1 + s1;
+ subcount = LLStringUtil::format(s, fmt_map);
+ ensure_equals("LLStringUtil::format: Rawx4 interpolation result", s, s2 + s2 + s2 + s2);
+ if (s1 == "?" || s1 == "[]") // no interp expected
+ {
+ ensure_equals("LLStringUtil::format: Rawx4 interpolation result count", 0, subcount);
+ }
+ else
+ {
+ ensure_equals("LLStringUtil::format: Rawx4 interpolation result count", 4, subcount);
+ }
+ }
+
+ // Test when source string has no keys
+ std::string srcs = "!!!!!!!!!!!!!!!!";
+ s = srcs;
+ subcount = LLStringUtil::format(s, fmt_map);
+ ensure_equals("LLStringUtil::format: No key test result", s, srcs);
+ ensure_equals("LLStringUtil::format: No key test result count", 0, subcount);
+
+ // Test when source string has no keys and is empty
+ std::string srcs3;
+ s = srcs3;
+ subcount = LLStringUtil::format(s, fmt_map);
+ ensure("LLStringUtil::format: No key test3 result", s.empty());
+ ensure_equals("LLStringUtil::format: No key test3 result count", 0, subcount);
+
+ // Test a substitution where a key is substituted with blankness
+ std::string srcs2 = "[DELETE]";
+ s = srcs2;
+ subcount = LLStringUtil::format(s, fmt_map);
+ ensure("LLStringUtil::format: Delete key test2 result", s.empty());
+ ensure_equals("LLStringUtil::format: Delete key test2 result count", 1, subcount);
+
+ // Test an assorted substitution
+ std::string srcs4 = "[TRICK1][A][B][AAA][BBB][TRICK2][KEYLONGER][KEYSHORTER]?[DELETE]";
+ s = srcs4;
+ subcount = LLStringUtil::format(s, fmt_map);
+ ensure_equals("LLStringUtil::format: Assorted Test1 result", s, "[A]abaaabbb[A]shortAm I not a long string??");
+ ensure_equals("LLStringUtil::format: Assorted Test1 result count", 9, subcount);
+
+ // Test an assorted substitution
+ std::string srcs5 = "[DELETE]?[KEYSHORTER][KEYLONGER][TRICK2][BBB][AAA][B][A][TRICK1]";
+ s = srcs5;
+ subcount = LLStringUtil::format(s, fmt_map);
+ ensure_equals("LLStringUtil::format: Assorted Test2 result", s, "?Am I not a long string?short[A]bbbaaaba[A]");
+ ensure_equals("LLStringUtil::format: Assorted Test2 result count", 9, subcount);
+
+ // Test on nested brackets
+ std::string srcs6 = "[[TRICK1]][[A]][[B]][[AAA]][[BBB]][[TRICK2]][[KEYLONGER]][[KEYSHORTER]]?[[DELETE]]";
+ s = srcs6;
+ subcount = LLStringUtil::format(s, fmt_map);
+ ensure_equals("LLStringUtil::format: Assorted Test2 result", s, "[[A]][a][b][aaa][bbb][[A]][short][Am I not a long string?]?[]");
+ ensure_equals("LLStringUtil::format: Assorted Test2 result count", 9, subcount);
+
+
+ // Test an assorted substitution
+ std::string srcs8 = "foo[DELETE]bar?";
+ s = srcs8;
+ subcount = LLStringUtil::format(s, fmt_map);
+ ensure_equals("LLStringUtil::format: Assorted Test3 result", s, "foobar?");
+ ensure_equals("LLStringUtil::format: Assorted Test3 result count", 1, subcount);
+ }
+
+ template<> template<>
+ void string_index_object_t::test<33>()
+ {
+ // Test LLStringUtil::format() string interpolation
+ LLStringUtil::format_map_t blank_fmt_map;
+ std::string s;
+ int subcount;
+
+ // Test substituting out of a blank format_map
+ std::string srcs6 = "12345";
+ s = srcs6;
+ subcount = LLStringUtil::format(s, blank_fmt_map);
+ ensure_equals("LLStringUtil::format: Blankfmt Test1 result", s, "12345");
+ ensure_equals("LLStringUtil::format: Blankfmt Test1 result count", 0, subcount);
+
+ // Test substituting a blank string out of a blank format_map
+ std::string srcs7;
+ s = srcs7;
+ subcount = LLStringUtil::format(s, blank_fmt_map);
+ ensure("LLStringUtil::format: Blankfmt Test2 result", s.empty());
+ ensure_equals("LLStringUtil::format: Blankfmt Test2 result count", 0, subcount);
+ }
+
+ template<> template<>
+ void string_index_object_t::test<34>()
+ {
+ // Test that incorrect LLStringUtil::format() use does not explode.
+ LLStringUtil::format_map_t nasty_fmt_map;
+ std::string s;
+ int subcount;
+
+ nasty_fmt_map[""] = "never used"; // see, this is nasty.
+
+ // Test substituting out of a nasty format_map
+ std::string srcs6 = "12345";
+ s = srcs6;
+ subcount = LLStringUtil::format(s, nasty_fmt_map);
+ ensure_equals("LLStringUtil::format: Nastyfmt Test1 result", s, "12345");
+ ensure_equals("LLStringUtil::format: Nastyfmt Test1 result count", 0, subcount);
+
+ // Test substituting a blank string out of a nasty format_map
+ std::string srcs7;
+ s = srcs7;
+ subcount = LLStringUtil::format(s, nasty_fmt_map);
+ ensure("LLStringUtil::format: Nastyfmt Test2 result", s.empty());
+ ensure_equals("LLStringUtil::format: Nastyfmt Test2 result count", 0, subcount);
+ }
+
+ template<> template<>
+ void string_index_object_t::test<35>()
+ {
+ // Make sure startsWith works
+ std::string string("anybody in there?");
+ std::string substr("anybody");
+ ensure("startsWith works.", LLStringUtil::startsWith(string, substr));
+ }
+
+ template<> template<>
+ void string_index_object_t::test<36>()
+ {
+ // Make sure startsWith correctly fails
+ std::string string("anybody in there?");
+ std::string substr("there");
+ ensure("startsWith fails.", !LLStringUtil::startsWith(string, substr));
+ }
+
+ template<> template<>
+ void string_index_object_t::test<37>()
+ {
+ // startsWith fails on empty strings
+ std::string value("anybody in there?");
+ std::string empty;
+ ensure("empty string.", !LLStringUtil::startsWith(value, empty));
+ ensure("empty substr.", !LLStringUtil::startsWith(empty, value));
+ ensure("empty everything.", !LLStringUtil::startsWith(empty, empty));
+ }
+
+ template<> template<>
+ void string_index_object_t::test<38>()
+ {
+ // Make sure endsWith works correctly
+ std::string string("anybody in there?");
+ std::string substr("there?");
+ ensure("endsWith works.", LLStringUtil::endsWith(string, substr));
+ }
+
+ template<> template<>
+ void string_index_object_t::test<39>()
+ {
+ // Make sure endsWith correctly fails
+ std::string string("anybody in there?");
+ std::string substr("anybody");
+ ensure("endsWith fails.", !LLStringUtil::endsWith(string, substr));
+ substr = "there";
+ ensure("endsWith fails.", !LLStringUtil::endsWith(string, substr));
+ substr = "ther?";
+ ensure("endsWith fails.", !LLStringUtil::endsWith(string, substr));
+ }
+
+ template<> template<>
+ void string_index_object_t::test<40>()
+ {
+ // endsWith fails on empty strings
+ std::string value("anybody in there?");
+ std::string empty;
+ ensure("empty string.", !LLStringUtil::endsWith(value, empty));
+ ensure("empty substr.", !LLStringUtil::endsWith(empty, value));
+ ensure("empty everything.", !LLStringUtil::endsWith(empty, empty));
+ }
+
+ template<> template<>
+ void string_index_object_t::test<41>()
+ {
+ set_test_name("getTokens(\"delims\")");
+ ensure_equals("empty string", LLStringUtil::getTokens("", " "), StringVec());
+ ensure_equals("only delims",
+ LLStringUtil::getTokens(" \r\n ", " \r\n"), StringVec());
+ ensure_equals("sequence of delims",
+ LLStringUtil::getTokens(",,, one ,,,", ","), list_of("one"));
+ // nat considers this a dubious implementation side effect, but I'd
+ // hate to change it now...
+ ensure_equals("noncontiguous tokens",
+ LLStringUtil::getTokens(", ,, , one ,,,", ","), list_of("")("")("one"));
+ ensure_equals("space-padded tokens",
+ LLStringUtil::getTokens(", one , two ,", ","), list_of("one")("two"));
+ ensure_equals("no delims", LLStringUtil::getTokens("one", ","), list_of("one"));
+ }
+
+ // Shorthand for verifying that getTokens() behaves the same when you
+ // don't pass a string of escape characters, when you pass an empty string
+ // (different overloads), and when you pass a string of characters that
+ // aren't actually present.
+ void ensure_getTokens(const std::string& desc,
+ const std::string& string,
+ const std::string& drop_delims,
+ const std::string& keep_delims,
+ const std::string& quotes,
+ const std::vector<std::string>& expect)
+ {
+ ensure_equals(desc + " - no esc",
+ LLStringUtil::getTokens(string, drop_delims, keep_delims, quotes),
+ expect);
+ ensure_equals(desc + " - empty esc",
+ LLStringUtil::getTokens(string, drop_delims, keep_delims, quotes, ""),
+ expect);
+ ensure_equals(desc + " - unused esc",
+ LLStringUtil::getTokens(string, drop_delims, keep_delims, quotes, "!"),
+ expect);
+ }
+
+ void ensure_getTokens(const std::string& desc,
+ const std::string& string,
+ const std::string& drop_delims,
+ const std::string& keep_delims,
+ const std::vector<std::string>& expect)
+ {
+ ensure_getTokens(desc, string, drop_delims, keep_delims, "", expect);
+ }
+
+ template<> template<>
+ void string_index_object_t::test<42>()
+ {
+ set_test_name("getTokens(\"delims\", etc.)");
+ // Signatures to test in this method:
+ // getTokens(string, drop_delims, keep_delims [, quotes [, escapes]])
+ // If you omit keep_delims, you get the older function (test above).
+
+ // cases like the getTokens(string, delims) tests above
+ ensure_getTokens("empty string", "", " ", "", StringVec());
+ ensure_getTokens("only delims",
+ " \r\n ", " \r\n", "", StringVec());
+ ensure_getTokens("sequence of delims",
+ ",,, one ,,,", ", ", "", list_of("one"));
+ // Note contrast with the case in the previous method
+ ensure_getTokens("noncontiguous tokens",
+ ", ,, , one ,,,", ", ", "", list_of("one"));
+ ensure_getTokens("space-padded tokens",
+ ", one , two ,", ", ", "",
+ list_of("one")("two"));
+ ensure_getTokens("no delims", "one", ",", "", list_of("one"));
+
+ // drop_delims vs. keep_delims
+ ensure_getTokens("arithmetic",
+ " ab+def / xx* yy ", " ", "+-*/",
+ list_of("ab")("+")("def")("/")("xx")("*")("yy"));
+
+ // quotes
+ ensure_getTokens("no quotes",
+ "She said, \"Don't go.\"", " ", ",", "",
+ list_of("She")("said")(",")("\"Don't")("go.\""));
+ ensure_getTokens("quotes",
+ "She said, \"Don't go.\"", " ", ",", "\"",
+ list_of("She")("said")(",")("Don't go."));
+ ensure_getTokens("quotes and delims",
+ "run c:/'Documents and Settings'/someone", " ", "", "'",
+ list_of("run")("c:/Documents and Settings/someone"));
+ ensure_getTokens("unmatched quote",
+ "baby don't leave", " ", "", "'",
+ list_of("baby")("don't")("leave"));
+ ensure_getTokens("adjacent quoted",
+ "abc'def \"ghi'\"jkl' mno\"pqr", " ", "", "\"'",
+ list_of("abcdef \"ghijkl' mnopqr"));
+ ensure_getTokens("quoted empty string",
+ "--set SomeVar ''", " ", "", "'",
+ list_of("--set")("SomeVar")(""));
+
+ // escapes
+ // Don't use backslash as an escape for these tests -- you'll go nuts
+ // between the C++ string scanner and getTokens() escapes. Test with
+ // something else!
+ ensure_equals("escaped delims",
+ LLStringUtil::getTokens("^ a - dog^-gone^ phrase", " ", "-", "", "^"),
+ list_of(" a")("-")("dog-gone phrase"));
+ ensure_equals("escaped quotes",
+ LLStringUtil::getTokens("say: 'this isn^'t w^orking'.", " ", "", "'", "^"),
+ list_of("say:")("this isn't working."));
+ ensure_equals("escaped escape",
+ LLStringUtil::getTokens("want x^^2", " ", "", "", "^"),
+ list_of("want")("x^2"));
+ ensure_equals("escape at end",
+ LLStringUtil::getTokens("it's^ up there^", " ", "", "'", "^"),
+ list_of("it's up")("there^"));
+ }
+}
diff --git a/indra/llcommon/tests/lltrace_test.cpp b/indra/llcommon/tests/lltrace_test.cpp index 0a9d85ad00..8851f87b91 100644 --- a/indra/llcommon/tests/lltrace_test.cpp +++ b/indra/llcommon/tests/lltrace_test.cpp @@ -1,4 +1,4 @@ -/** +/** * @file llsingleton_test.cpp * @date 2011-08-11 * @brief Unit test for the LLSingleton class @@ -34,14 +34,14 @@ namespace LLUnits { - // using powers of 2 to allow strict floating point equality - LL_DECLARE_BASE_UNIT(Ounces, "oz"); - LL_DECLARE_DERIVED_UNIT(TallCup, "", Ounces, / 12); - LL_DECLARE_DERIVED_UNIT(GrandeCup, "", Ounces, / 16); - LL_DECLARE_DERIVED_UNIT(VentiCup, "", Ounces, / 20); - - LL_DECLARE_BASE_UNIT(Grams, "g"); - LL_DECLARE_DERIVED_UNIT(Milligrams, "mg", Grams, * 1000); + // using powers of 2 to allow strict floating point equality + LL_DECLARE_BASE_UNIT(Ounces, "oz"); + LL_DECLARE_DERIVED_UNIT(TallCup, "", Ounces, / 12); + LL_DECLARE_DERIVED_UNIT(GrandeCup, "", Ounces, / 16); + LL_DECLARE_DERIVED_UNIT(VentiCup, "", Ounces, / 20); + + LL_DECLARE_BASE_UNIT(Grams, "g"); + LL_DECLARE_DERIVED_UNIT(Milligrams, "mg", Grams, * 1000); } LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Ounces); @@ -54,89 +54,89 @@ LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Milligrams); namespace tut { - using namespace LLTrace; - struct trace - { - ThreadRecorder mRecorder; - }; - - typedef test_group<trace> trace_t; - typedef trace_t::object trace_object_t; - tut::trace_t tut_singleton("LLTrace"); - - static CountStatHandle<S32> sCupsOfCoffeeConsumed("coffeeconsumed", "Delicious cup of dark roast."); - static SampleStatHandle<F32Milligrams> sCaffeineLevelStat("caffeinelevel", "Coffee buzz quotient"); - static EventStatHandle<S32Ounces> sOuncesPerCup("cupsize", "Large, huge, or ginormous"); - - static F32 sCaffeineLevel(0.f); - const F32Milligrams sCaffeinePerOz(18.f); - - void drink_coffee(S32 num_cups, S32Ounces cup_size) - { - add(sCupsOfCoffeeConsumed, num_cups); - for (S32 i = 0; i < num_cups; i++) - { - record(sOuncesPerCup, cup_size); - } - - sCaffeineLevel += F32Ounces(num_cups * cup_size).value() * sCaffeinePerOz.value(); - sample(sCaffeineLevelStat, sCaffeineLevel); - } - - // basic data collection - template<> template<> - void trace_object_t::test<1>() - { - sample(sCaffeineLevelStat, sCaffeineLevel); - - Recording all_day; - Recording at_work; - Recording after_3pm; - - all_day.start(); - { - // warm up with one grande cup - drink_coffee(1, S32TallCup(1)); - - // go to work - at_work.start(); - { - // drink 3 tall cups, 1 after 3 pm - drink_coffee(2, S32GrandeCup(1)); - after_3pm.start(); - drink_coffee(1, S32GrandeCup(1)); - } - at_work.stop(); - drink_coffee(1, S32VentiCup(1)); - } - // don't need to stop recordings to get accurate values out of them - //after_3pm.stop(); - //all_day.stop(); - - ensure("count stats are counted when recording is active", - at_work.getSum(sCupsOfCoffeeConsumed) == 3 - && all_day.getSum(sCupsOfCoffeeConsumed) == 5 - && after_3pm.getSum(sCupsOfCoffeeConsumed) == 2); - ensure("measurement sums are counted when recording is active", - at_work.getSum(sOuncesPerCup) == S32Ounces(48) - && all_day.getSum(sOuncesPerCup) == S32Ounces(80) - && after_3pm.getSum(sOuncesPerCup) == S32Ounces(36)); - ensure("measurement min is specific to when recording is active", - at_work.getMin(sOuncesPerCup) == S32GrandeCup(1) - && all_day.getMin(sOuncesPerCup) == S32TallCup(1) - && after_3pm.getMin(sOuncesPerCup) == S32GrandeCup(1)); - ensure("measurement max is specific to when recording is active", - at_work.getMax(sOuncesPerCup) == S32GrandeCup(1) - && all_day.getMax(sOuncesPerCup) == S32VentiCup(1) - && after_3pm.getMax(sOuncesPerCup) == S32VentiCup(1)); - ensure("sample min is specific to when recording is active", - at_work.getMin(sCaffeineLevelStat) == sCaffeinePerOz * ((S32Ounces)S32TallCup(1)).value() - && all_day.getMin(sCaffeineLevelStat) == F32Milligrams(0.f) - && after_3pm.getMin(sCaffeineLevelStat) == sCaffeinePerOz * ((S32Ounces)S32TallCup(1) + (S32Ounces)S32GrandeCup(2)).value()); - ensure("sample max is specific to when recording is active", - at_work.getMax(sCaffeineLevelStat) == sCaffeinePerOz * ((S32Ounces)S32TallCup(1) + (S32Ounces)S32GrandeCup(3)).value() - && all_day.getMax(sCaffeineLevelStat) == sCaffeinePerOz * ((S32Ounces)S32TallCup(1) + (S32Ounces)S32GrandeCup(3) + (S32Ounces)S32VentiCup(1)).value() - && after_3pm.getMax(sCaffeineLevelStat) == sCaffeinePerOz * ((S32Ounces)S32TallCup(1) + (S32Ounces)S32GrandeCup(3) + (S32Ounces)S32VentiCup(1)).value()); - } + using namespace LLTrace; + struct trace + { + ThreadRecorder mRecorder; + }; + + typedef test_group<trace> trace_t; + typedef trace_t::object trace_object_t; + tut::trace_t tut_singleton("LLTrace"); + + static CountStatHandle<S32> sCupsOfCoffeeConsumed("coffeeconsumed", "Delicious cup of dark roast."); + static SampleStatHandle<F32Milligrams> sCaffeineLevelStat("caffeinelevel", "Coffee buzz quotient"); + static EventStatHandle<S32Ounces> sOuncesPerCup("cupsize", "Large, huge, or ginormous"); + + static F32 sCaffeineLevel(0.f); + const F32Milligrams sCaffeinePerOz(18.f); + + void drink_coffee(S32 num_cups, S32Ounces cup_size) + { + add(sCupsOfCoffeeConsumed, num_cups); + for (S32 i = 0; i < num_cups; i++) + { + record(sOuncesPerCup, cup_size); + } + + sCaffeineLevel += F32Ounces(num_cups * cup_size).value() * sCaffeinePerOz.value(); + sample(sCaffeineLevelStat, sCaffeineLevel); + } + + // basic data collection + template<> template<> + void trace_object_t::test<1>() + { + sample(sCaffeineLevelStat, sCaffeineLevel); + + Recording all_day; + Recording at_work; + Recording after_3pm; + + all_day.start(); + { + // warm up with one grande cup + drink_coffee(1, S32TallCup(1)); + + // go to work + at_work.start(); + { + // drink 3 tall cups, 1 after 3 pm + drink_coffee(2, S32GrandeCup(1)); + after_3pm.start(); + drink_coffee(1, S32GrandeCup(1)); + } + at_work.stop(); + drink_coffee(1, S32VentiCup(1)); + } + // don't need to stop recordings to get accurate values out of them + //after_3pm.stop(); + //all_day.stop(); + + ensure("count stats are counted when recording is active", + at_work.getSum(sCupsOfCoffeeConsumed) == 3 + && all_day.getSum(sCupsOfCoffeeConsumed) == 5 + && after_3pm.getSum(sCupsOfCoffeeConsumed) == 2); + ensure("measurement sums are counted when recording is active", + at_work.getSum(sOuncesPerCup) == S32Ounces(48) + && all_day.getSum(sOuncesPerCup) == S32Ounces(80) + && after_3pm.getSum(sOuncesPerCup) == S32Ounces(36)); + ensure("measurement min is specific to when recording is active", + at_work.getMin(sOuncesPerCup) == S32GrandeCup(1) + && all_day.getMin(sOuncesPerCup) == S32TallCup(1) + && after_3pm.getMin(sOuncesPerCup) == S32GrandeCup(1)); + ensure("measurement max is specific to when recording is active", + at_work.getMax(sOuncesPerCup) == S32GrandeCup(1) + && all_day.getMax(sOuncesPerCup) == S32VentiCup(1) + && after_3pm.getMax(sOuncesPerCup) == S32VentiCup(1)); + ensure("sample min is specific to when recording is active", + at_work.getMin(sCaffeineLevelStat) == sCaffeinePerOz * ((S32Ounces)S32TallCup(1)).value() + && all_day.getMin(sCaffeineLevelStat) == F32Milligrams(0.f) + && after_3pm.getMin(sCaffeineLevelStat) == sCaffeinePerOz * ((S32Ounces)S32TallCup(1) + (S32Ounces)S32GrandeCup(2)).value()); + ensure("sample max is specific to when recording is active", + at_work.getMax(sCaffeineLevelStat) == sCaffeinePerOz * ((S32Ounces)S32TallCup(1) + (S32Ounces)S32GrandeCup(3)).value() + && all_day.getMax(sCaffeineLevelStat) == sCaffeinePerOz * ((S32Ounces)S32TallCup(1) + (S32Ounces)S32GrandeCup(3) + (S32Ounces)S32VentiCup(1)).value() + && after_3pm.getMax(sCaffeineLevelStat) == sCaffeinePerOz * ((S32Ounces)S32TallCup(1) + (S32Ounces)S32GrandeCup(3) + (S32Ounces)S32VentiCup(1)).value()); + } } diff --git a/indra/llcommon/tests/lltreeiterators_test.cpp b/indra/llcommon/tests/lltreeiterators_test.cpp index b9c7a70c07..7a2adfd8ba 100644 --- a/indra/llcommon/tests/lltreeiterators_test.cpp +++ b/indra/llcommon/tests/lltreeiterators_test.cpp @@ -3,25 +3,25 @@ * @author Nat Goodspeed * @date 2008-08-20 * @brief Test of lltreeiterators.h - * + * * $LicenseInfo:firstyear=2008&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -1045,7 +1045,7 @@ bool LLTreeWalkIter_test(const std::string& itername, const std::string& nodenam { std::cout << itername << '<' << nodename << ">(NULL)\n"; return false; - } + } return true; } @@ -1107,7 +1107,7 @@ namespace tut TreeNodePtr tnroot(example_tree<TreeNode>()); TreeNodePtr tnB2b(get_B2b<TreeNode, TreeNode::child_iterator> (tnroot, boost::bind(&TreeNode::child_begin, _1))); - + std::string desc1("for (TreeNodePr : getRootRange<LLTreeIter::UP>(tnB2b))"); // std::cout << desc1 << "\n"; // Although we've commented out the output statement, ensure that the diff --git a/indra/llcommon/tests/llunits_test.cpp b/indra/llcommon/tests/llunits_test.cpp index 57cf9810af..49f2d3085a 100644 --- a/indra/llcommon/tests/llunits_test.cpp +++ b/indra/llcommon/tests/llunits_test.cpp @@ -1,4 +1,4 @@ -/** +/** * @file llsingleton_test.cpp * @date 2011-08-11 * @brief Unit test for the LLSingleton class @@ -32,10 +32,10 @@ namespace LLUnits { - // using powers of 2 to allow strict floating point equality - LL_DECLARE_BASE_UNIT(Quatloos, "Quat"); - LL_DECLARE_DERIVED_UNIT(Latinum, "Lat", Quatloos, / 4); - LL_DECLARE_DERIVED_UNIT(Solari, "Sol", Latinum, * 16); + // using powers of 2 to allow strict floating point equality + LL_DECLARE_BASE_UNIT(Quatloos, "Quat"); + LL_DECLARE_DERIVED_UNIT(Latinum, "Lat", Quatloos, / 4); + LL_DECLARE_DERIVED_UNIT(Solari, "Sol", Latinum, * 16); } LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Quatloos); @@ -44,9 +44,9 @@ LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Solari); namespace LLUnits { - LL_DECLARE_BASE_UNIT(Celcius, "c"); - LL_DECLARE_DERIVED_UNIT(Fahrenheit, "f", Celcius, * 9 / 5 + 32); - LL_DECLARE_DERIVED_UNIT(Kelvin, "k", Celcius, + 273.15f); + LL_DECLARE_BASE_UNIT(Celcius, "c"); + LL_DECLARE_DERIVED_UNIT(Fahrenheit, "f", Celcius, * 9 / 5 + 32); + LL_DECLARE_DERIVED_UNIT(Kelvin, "k", Celcius, + 273.15f); } LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Celcius); @@ -56,333 +56,333 @@ LL_DECLARE_UNIT_TYPEDEFS(LLUnits, Kelvin); namespace tut { - using namespace LLUnits; - struct units - { - }; - - typedef test_group<units> units_t; - typedef units_t::object units_object_t; - tut::units_t tut_singleton("LLUnit"); - - // storage type conversions - template<> template<> - void units_object_t::test<1>() - { - LLUnit<F32, Quatloos> float_quatloos; - ensure("default float unit is zero", float_quatloos == F32Quatloos(0.f)); - - LLUnit<F32, Quatloos> float_initialize_quatloos(1); - ensure("non-zero initialized unit", float_initialize_quatloos == F32Quatloos(1.f)); - - LLUnit<S32, Quatloos> int_quatloos; - ensure("default int unit is zero", int_quatloos == S32Quatloos(0)); - - int_quatloos = S32Quatloos(42); - ensure("int assignment is preserved", int_quatloos == S32Quatloos(42)); - float_quatloos = int_quatloos; - ensure("float assignment from int preserves value", float_quatloos == F32Quatloos(42.f)); - - int_quatloos = float_quatloos; - ensure("int assignment from float preserves value", int_quatloos == S32Quatloos(42)); - - float_quatloos = F32Quatloos(42.1f); - int_quatloos = float_quatloos; - ensure("int units truncate float units on assignment", int_quatloos == S32Quatloos(42)); - - LLUnit<U32, Quatloos> unsigned_int_quatloos(float_quatloos); - ensure("unsigned int can be initialized from signed int", unsigned_int_quatloos == S32Quatloos(42)); - - S32Solari int_solari(1); - - float_quatloos = int_solari; - ensure("fractional units are preserved in conversion from integer to float type", float_quatloos == F32Quatloos(0.25f)); - - int_quatloos = S32Quatloos(1); - F32Solari float_solari = int_quatloos; - ensure("can convert with fractional intermediates from integer to float type", float_solari == F32Solari(4.f)); - } - - // conversions to/from base unit - template<> template<> - void units_object_t::test<2>() - { - LLUnit<F32, Quatloos> quatloos(1.f); - LLUnit<F32, Latinum> latinum_bars(quatloos); - ensure("conversion between units is automatic via initialization", latinum_bars == F32Latinum(1.f / 4.f)); - - latinum_bars = S32Latinum(256); - quatloos = latinum_bars; - ensure("conversion between units is automatic via assignment, and bidirectional", quatloos == S32Quatloos(1024)); - - LLUnit<S32, Quatloos> single_quatloo(1); - LLUnit<F32, Latinum> quarter_latinum = single_quatloo; - ensure("division of integer unit preserves fractional values when converted to float unit", quarter_latinum == F32Latinum(0.25f)); - } - - // conversions across non-base units - template<> template<> - void units_object_t::test<3>() - { - LLUnit<F32, Quatloos> quatloos(1024); - LLUnit<F32, Solari> solari(quatloos); - ensure("conversions can work between indirectly related units: Quatloos -> Latinum -> Solari", solari == S32Solari(4096)); - - LLUnit<F32, Latinum> latinum_bars = solari; - ensure("Non base units can be converted between each other", latinum_bars == S32Latinum(256)); - } - - // math operations - template<> template<> - void units_object_t::test<4>() - { - // exercise math operations - LLUnit<F32, Quatloos> quatloos(1.f); - quatloos *= 4.f; - ensure(quatloos == S32Quatloos(4)); - quatloos = quatloos * 2; - ensure(quatloos == S32Quatloos(8)); - quatloos = 2.f * quatloos; - ensure(quatloos == S32Quatloos(16)); - - quatloos += F32Quatloos(4.f); - ensure(quatloos == S32Quatloos(20)); - quatloos += S32Quatloos(4); - ensure(quatloos == S32Quatloos(24)); - quatloos = quatloos + S32Quatloos(4); - ensure(quatloos == S32Quatloos(28)); - quatloos = S32Quatloos(4) + quatloos; - ensure(quatloos == S32Quatloos(32)); - quatloos += quatloos * 3; - ensure(quatloos == S32Quatloos(128)); - - quatloos -= quatloos / 4 * 3; - ensure(quatloos == S32Quatloos(32)); - quatloos = quatloos - S32Quatloos(8); - ensure(quatloos == S32Quatloos(24)); - quatloos -= S32Quatloos(4); - ensure(quatloos == S32Quatloos(20)); - quatloos -= F32Quatloos(4.f); - ensure(quatloos == S32Quatloos(16)); - - quatloos /= 2.f; - ensure(quatloos == S32Quatloos(8)); - quatloos = quatloos / 4; - ensure(quatloos == S32Quatloos(2)); - - F32 ratio = quatloos / LLUnit<F32, Quatloos>(2.f); - ensure(ratio == 1); - ratio = quatloos / LLUnit<F32, Solari>(8.f); - ensure(ratio == 1); - - quatloos += LLUnit<F32, Solari>(8.f); - ensure(quatloos == S32Quatloos(4)); - quatloos -= LLUnit<F32, Latinum>(1.f); - ensure(quatloos == S32Quatloos(0)); - } - - // comparison operators - template<> template<> - void units_object_t::test<5>() - { - LLUnit<S32, Quatloos> quatloos(1); - ensure("can perform less than comparison against same type", quatloos < S32Quatloos(2)); - ensure("can perform less than comparison against different storage type", quatloos < F32Quatloos(2.f)); - ensure("can perform less than comparison against different units", quatloos < S32Latinum(5)); - ensure("can perform less than comparison against different storage type and units", quatloos < F32Latinum(5.f)); - - ensure("can perform greater than comparison against same type", quatloos > S32Quatloos(0)); - ensure("can perform greater than comparison against different storage type", quatloos > F32Quatloos(0.f)); - ensure("can perform greater than comparison against different units", quatloos > S32Latinum(0)); - ensure("can perform greater than comparison against different storage type and units", quatloos > F32Latinum(0.f)); - - } - - bool accept_explicit_quatloos(S32Quatloos q) - { - return true; - } - - bool accept_implicit_quatloos(S32Quatloos q) - { - return true; - } - - // signature compatibility - template<> template<> - void units_object_t::test<6>() - { - S32Quatloos quatloos(1); - ensure("can pass unit values as argument", accept_explicit_quatloos(S32Quatloos(1))); - ensure("can pass unit values as argument", accept_explicit_quatloos(quatloos)); - } - - // implicit units - template<> template<> - void units_object_t::test<7>() - { - LLUnit<F32, Quatloos> quatloos; - LLUnitImplicit<F32, Quatloos> quatloos_implicit = quatloos + S32Quatloos(1); - ensure("can initialize implicit unit from explicit", quatloos_implicit == 1); - - quatloos = quatloos_implicit; - ensure("can assign implicit unit to explicit unit", quatloos == S32Quatloos(1)); - quatloos += quatloos_implicit; - ensure("can perform math operation using mixture of implicit and explicit units", quatloos == S32Quatloos(2)); - - // math operations on implicits - quatloos_implicit = 1; - ensure(quatloos_implicit == 1); - - quatloos_implicit += 2; - ensure(quatloos_implicit == 3); - - quatloos_implicit *= 2; - ensure(quatloos_implicit == 6); - - quatloos_implicit -= 1; - ensure(quatloos_implicit == 5); - - quatloos_implicit /= 5; - ensure(quatloos_implicit == 1); - - quatloos_implicit = quatloos_implicit + 3 + quatloos_implicit; - ensure(quatloos_implicit == 5); - - quatloos_implicit = 10 - quatloos_implicit - 1; - ensure(quatloos_implicit == 4); - - quatloos_implicit = 2 * quatloos_implicit * 2; - ensure(quatloos_implicit == 16); - - F32 one_half = quatloos_implicit / (quatloos_implicit * 2); - ensure(one_half == 0.5f); - - // implicit conversion to POD - F32 float_val = quatloos_implicit; - ensure("implicit units convert implicitly to regular values", float_val == 16); - - S32 int_val = quatloos_implicit; - ensure("implicit units convert implicitly to regular values", int_val == 16); - - // conversion of implicits - LLUnitImplicit<F32, Latinum> latinum_implicit(2); - ensure("implicit units of different types are comparable", latinum_implicit * 2 == quatloos_implicit); - - quatloos_implicit += F32Quatloos(10); - ensure("can add-assign explicit units", quatloos_implicit == 26); - - quatloos_implicit -= F32Quatloos(10); - ensure("can subtract-assign explicit units", quatloos_implicit == 16); - - // comparisons - ensure("can compare greater than implicit unit", quatloos_implicit > F32QuatloosImplicit(0.f)); - ensure("can compare greater than non-implicit unit", quatloos_implicit > F32Quatloos(0.f)); - ensure("can compare greater than or equal to implicit unit", quatloos_implicit >= F32QuatloosImplicit(0.f)); - ensure("can compare greater than or equal to non-implicit unit", quatloos_implicit >= F32Quatloos(0.f)); - ensure("can compare less than implicit unit", quatloos_implicit < F32QuatloosImplicit(20.f)); - ensure("can compare less than non-implicit unit", quatloos_implicit < F32Quatloos(20.f)); - ensure("can compare less than or equal to implicit unit", quatloos_implicit <= F32QuatloosImplicit(20.f)); - ensure("can compare less than or equal to non-implicit unit", quatloos_implicit <= F32Quatloos(20.f)); - } - - // precision tests - template<> template<> - void units_object_t::test<8>() - { - U32Bytes max_bytes(U32_MAX); - S32Megabytes mega_bytes = max_bytes; - ensure("max available precision is used when converting units", mega_bytes == (S32Megabytes)4095); - - mega_bytes = (S32Megabytes)-5 + (U32Megabytes)1; - ensure("can mix signed and unsigned in units addition", mega_bytes == (S32Megabytes)-4); - - mega_bytes = (U32Megabytes)5 + (S32Megabytes)-1; - ensure("can mix unsigned and signed in units addition", mega_bytes == (S32Megabytes)4); - } - - // default units - template<> template<> - void units_object_t::test<9>() - { - U32Gigabytes GB(1); - U32Megabytes MB(GB); - U32Kilobytes KB(GB); - U32Bytes B(GB); - - ensure("GB -> MB conversion", MB.value() == 1024); - ensure("GB -> KB conversion", KB.value() == 1024 * 1024); - ensure("GB -> B conversion", B.value() == 1024 * 1024 * 1024); - - KB = U32Kilobytes(1); - U32Kilobits Kb(KB); - U32Bits b(KB); - ensure("KB -> Kb conversion", Kb.value() == 8); - ensure("KB -> b conversion", b.value() == 8 * 1024); - - U32Days days(1); - U32Hours hours(days); - U32Minutes minutes(days); - U32Seconds seconds(days); - U32Milliseconds ms(days); - - ensure("days -> hours conversion", hours.value() == 24); - ensure("days -> minutes conversion", minutes.value() == 24 * 60); - ensure("days -> seconds conversion", seconds.value() == 24 * 60 * 60); - ensure("days -> ms conversion", ms.value() == 24 * 60 * 60 * 1000); - - U32Kilometers km(1); - U32Meters m(km); - U32Centimeters cm(km); - U32Millimeters mm(km); - - ensure("km -> m conversion", m.value() == 1000); - ensure("km -> cm conversion", cm.value() == 1000 * 100); - ensure("km -> mm conversion", mm.value() == 1000 * 1000); - - U32Gigahertz GHz(1); - U32Megahertz MHz(GHz); - U32Kilohertz KHz(GHz); - U32Hertz Hz(GHz); - - ensure("GHz -> MHz conversion", MHz.value() == 1000); - ensure("GHz -> KHz conversion", KHz.value() == 1000 * 1000); - ensure("GHz -> Hz conversion", Hz.value() == 1000 * 1000 * 1000); - - F32Radians rad(6.2831853071795f); - S32Degrees deg(rad); - ensure("radians -> degrees conversion", deg.value() == 360); - - F32Percent percent(50); - F32Ratio ratio(percent); - ensure("percent -> ratio conversion", ratio.value() == 0.5f); - - U32Kilotriangles ktris(1); - U32Triangles tris(ktris); - ensure("kilotriangles -> triangles conversion", tris.value() == 1000); - } - - bool value_near(F32 value, F32 target, F32 threshold) - { - return fabsf(value - target) < threshold; - } - - // linear transforms - template<> template<> - void units_object_t::test<10>() - { - F32Celcius float_celcius(100); - F32Fahrenheit float_fahrenheit(float_celcius); - ensure("floating point celcius -> fahrenheit conversion using linear transform", value_near(float_fahrenheit.value(), 212, 0.1f) ); - - float_celcius = float_fahrenheit; - ensure("floating point fahrenheit -> celcius conversion using linear transform (round trip)", value_near(float_celcius.value(), 100.f, 0.1f) ); - - S32Celcius int_celcius(100); - S32Fahrenheit int_fahrenheit(int_celcius); - ensure("integer celcius -> fahrenheit conversion using linear transform", int_fahrenheit.value() == 212); - - int_celcius = int_fahrenheit; - ensure("integer fahrenheit -> celcius conversion using linear transform (round trip)", int_celcius.value() == 100); - } + using namespace LLUnits; + struct units + { + }; + + typedef test_group<units> units_t; + typedef units_t::object units_object_t; + tut::units_t tut_singleton("LLUnit"); + + // storage type conversions + template<> template<> + void units_object_t::test<1>() + { + LLUnit<F32, Quatloos> float_quatloos; + ensure("default float unit is zero", float_quatloos == F32Quatloos(0.f)); + + LLUnit<F32, Quatloos> float_initialize_quatloos(1); + ensure("non-zero initialized unit", float_initialize_quatloos == F32Quatloos(1.f)); + + LLUnit<S32, Quatloos> int_quatloos; + ensure("default int unit is zero", int_quatloos == S32Quatloos(0)); + + int_quatloos = S32Quatloos(42); + ensure("int assignment is preserved", int_quatloos == S32Quatloos(42)); + float_quatloos = int_quatloos; + ensure("float assignment from int preserves value", float_quatloos == F32Quatloos(42.f)); + + int_quatloos = float_quatloos; + ensure("int assignment from float preserves value", int_quatloos == S32Quatloos(42)); + + float_quatloos = F32Quatloos(42.1f); + int_quatloos = float_quatloos; + ensure("int units truncate float units on assignment", int_quatloos == S32Quatloos(42)); + + LLUnit<U32, Quatloos> unsigned_int_quatloos(float_quatloos); + ensure("unsigned int can be initialized from signed int", unsigned_int_quatloos == S32Quatloos(42)); + + S32Solari int_solari(1); + + float_quatloos = int_solari; + ensure("fractional units are preserved in conversion from integer to float type", float_quatloos == F32Quatloos(0.25f)); + + int_quatloos = S32Quatloos(1); + F32Solari float_solari = int_quatloos; + ensure("can convert with fractional intermediates from integer to float type", float_solari == F32Solari(4.f)); + } + + // conversions to/from base unit + template<> template<> + void units_object_t::test<2>() + { + LLUnit<F32, Quatloos> quatloos(1.f); + LLUnit<F32, Latinum> latinum_bars(quatloos); + ensure("conversion between units is automatic via initialization", latinum_bars == F32Latinum(1.f / 4.f)); + + latinum_bars = S32Latinum(256); + quatloos = latinum_bars; + ensure("conversion between units is automatic via assignment, and bidirectional", quatloos == S32Quatloos(1024)); + + LLUnit<S32, Quatloos> single_quatloo(1); + LLUnit<F32, Latinum> quarter_latinum = single_quatloo; + ensure("division of integer unit preserves fractional values when converted to float unit", quarter_latinum == F32Latinum(0.25f)); + } + + // conversions across non-base units + template<> template<> + void units_object_t::test<3>() + { + LLUnit<F32, Quatloos> quatloos(1024); + LLUnit<F32, Solari> solari(quatloos); + ensure("conversions can work between indirectly related units: Quatloos -> Latinum -> Solari", solari == S32Solari(4096)); + + LLUnit<F32, Latinum> latinum_bars = solari; + ensure("Non base units can be converted between each other", latinum_bars == S32Latinum(256)); + } + + // math operations + template<> template<> + void units_object_t::test<4>() + { + // exercise math operations + LLUnit<F32, Quatloos> quatloos(1.f); + quatloos *= 4.f; + ensure(quatloos == S32Quatloos(4)); + quatloos = quatloos * 2; + ensure(quatloos == S32Quatloos(8)); + quatloos = 2.f * quatloos; + ensure(quatloos == S32Quatloos(16)); + + quatloos += F32Quatloos(4.f); + ensure(quatloos == S32Quatloos(20)); + quatloos += S32Quatloos(4); + ensure(quatloos == S32Quatloos(24)); + quatloos = quatloos + S32Quatloos(4); + ensure(quatloos == S32Quatloos(28)); + quatloos = S32Quatloos(4) + quatloos; + ensure(quatloos == S32Quatloos(32)); + quatloos += quatloos * 3; + ensure(quatloos == S32Quatloos(128)); + + quatloos -= quatloos / 4 * 3; + ensure(quatloos == S32Quatloos(32)); + quatloos = quatloos - S32Quatloos(8); + ensure(quatloos == S32Quatloos(24)); + quatloos -= S32Quatloos(4); + ensure(quatloos == S32Quatloos(20)); + quatloos -= F32Quatloos(4.f); + ensure(quatloos == S32Quatloos(16)); + + quatloos /= 2.f; + ensure(quatloos == S32Quatloos(8)); + quatloos = quatloos / 4; + ensure(quatloos == S32Quatloos(2)); + + F32 ratio = quatloos / LLUnit<F32, Quatloos>(2.f); + ensure(ratio == 1); + ratio = quatloos / LLUnit<F32, Solari>(8.f); + ensure(ratio == 1); + + quatloos += LLUnit<F32, Solari>(8.f); + ensure(quatloos == S32Quatloos(4)); + quatloos -= LLUnit<F32, Latinum>(1.f); + ensure(quatloos == S32Quatloos(0)); + } + + // comparison operators + template<> template<> + void units_object_t::test<5>() + { + LLUnit<S32, Quatloos> quatloos(1); + ensure("can perform less than comparison against same type", quatloos < S32Quatloos(2)); + ensure("can perform less than comparison against different storage type", quatloos < F32Quatloos(2.f)); + ensure("can perform less than comparison against different units", quatloos < S32Latinum(5)); + ensure("can perform less than comparison against different storage type and units", quatloos < F32Latinum(5.f)); + + ensure("can perform greater than comparison against same type", quatloos > S32Quatloos(0)); + ensure("can perform greater than comparison against different storage type", quatloos > F32Quatloos(0.f)); + ensure("can perform greater than comparison against different units", quatloos > S32Latinum(0)); + ensure("can perform greater than comparison against different storage type and units", quatloos > F32Latinum(0.f)); + + } + + bool accept_explicit_quatloos(S32Quatloos q) + { + return true; + } + + bool accept_implicit_quatloos(S32Quatloos q) + { + return true; + } + + // signature compatibility + template<> template<> + void units_object_t::test<6>() + { + S32Quatloos quatloos(1); + ensure("can pass unit values as argument", accept_explicit_quatloos(S32Quatloos(1))); + ensure("can pass unit values as argument", accept_explicit_quatloos(quatloos)); + } + + // implicit units + template<> template<> + void units_object_t::test<7>() + { + LLUnit<F32, Quatloos> quatloos; + LLUnitImplicit<F32, Quatloos> quatloos_implicit = quatloos + S32Quatloos(1); + ensure("can initialize implicit unit from explicit", quatloos_implicit == 1); + + quatloos = quatloos_implicit; + ensure("can assign implicit unit to explicit unit", quatloos == S32Quatloos(1)); + quatloos += quatloos_implicit; + ensure("can perform math operation using mixture of implicit and explicit units", quatloos == S32Quatloos(2)); + + // math operations on implicits + quatloos_implicit = 1; + ensure(quatloos_implicit == 1); + + quatloos_implicit += 2; + ensure(quatloos_implicit == 3); + + quatloos_implicit *= 2; + ensure(quatloos_implicit == 6); + + quatloos_implicit -= 1; + ensure(quatloos_implicit == 5); + + quatloos_implicit /= 5; + ensure(quatloos_implicit == 1); + + quatloos_implicit = quatloos_implicit + 3 + quatloos_implicit; + ensure(quatloos_implicit == 5); + + quatloos_implicit = 10 - quatloos_implicit - 1; + ensure(quatloos_implicit == 4); + + quatloos_implicit = 2 * quatloos_implicit * 2; + ensure(quatloos_implicit == 16); + + F32 one_half = quatloos_implicit / (quatloos_implicit * 2); + ensure(one_half == 0.5f); + + // implicit conversion to POD + F32 float_val = quatloos_implicit; + ensure("implicit units convert implicitly to regular values", float_val == 16); + + S32 int_val = quatloos_implicit; + ensure("implicit units convert implicitly to regular values", int_val == 16); + + // conversion of implicits + LLUnitImplicit<F32, Latinum> latinum_implicit(2); + ensure("implicit units of different types are comparable", latinum_implicit * 2 == quatloos_implicit); + + quatloos_implicit += F32Quatloos(10); + ensure("can add-assign explicit units", quatloos_implicit == 26); + + quatloos_implicit -= F32Quatloos(10); + ensure("can subtract-assign explicit units", quatloos_implicit == 16); + + // comparisons + ensure("can compare greater than implicit unit", quatloos_implicit > F32QuatloosImplicit(0.f)); + ensure("can compare greater than non-implicit unit", quatloos_implicit > F32Quatloos(0.f)); + ensure("can compare greater than or equal to implicit unit", quatloos_implicit >= F32QuatloosImplicit(0.f)); + ensure("can compare greater than or equal to non-implicit unit", quatloos_implicit >= F32Quatloos(0.f)); + ensure("can compare less than implicit unit", quatloos_implicit < F32QuatloosImplicit(20.f)); + ensure("can compare less than non-implicit unit", quatloos_implicit < F32Quatloos(20.f)); + ensure("can compare less than or equal to implicit unit", quatloos_implicit <= F32QuatloosImplicit(20.f)); + ensure("can compare less than or equal to non-implicit unit", quatloos_implicit <= F32Quatloos(20.f)); + } + + // precision tests + template<> template<> + void units_object_t::test<8>() + { + U32Bytes max_bytes(U32_MAX); + S32Megabytes mega_bytes = max_bytes; + ensure("max available precision is used when converting units", mega_bytes == (S32Megabytes)4095); + + mega_bytes = (S32Megabytes)-5 + (U32Megabytes)1; + ensure("can mix signed and unsigned in units addition", mega_bytes == (S32Megabytes)-4); + + mega_bytes = (U32Megabytes)5 + (S32Megabytes)-1; + ensure("can mix unsigned and signed in units addition", mega_bytes == (S32Megabytes)4); + } + + // default units + template<> template<> + void units_object_t::test<9>() + { + U32Gigabytes GB(1); + U32Megabytes MB(GB); + U32Kilobytes KB(GB); + U32Bytes B(GB); + + ensure("GB -> MB conversion", MB.value() == 1024); + ensure("GB -> KB conversion", KB.value() == 1024 * 1024); + ensure("GB -> B conversion", B.value() == 1024 * 1024 * 1024); + + KB = U32Kilobytes(1); + U32Kilobits Kb(KB); + U32Bits b(KB); + ensure("KB -> Kb conversion", Kb.value() == 8); + ensure("KB -> b conversion", b.value() == 8 * 1024); + + U32Days days(1); + U32Hours hours(days); + U32Minutes minutes(days); + U32Seconds seconds(days); + U32Milliseconds ms(days); + + ensure("days -> hours conversion", hours.value() == 24); + ensure("days -> minutes conversion", minutes.value() == 24 * 60); + ensure("days -> seconds conversion", seconds.value() == 24 * 60 * 60); + ensure("days -> ms conversion", ms.value() == 24 * 60 * 60 * 1000); + + U32Kilometers km(1); + U32Meters m(km); + U32Centimeters cm(km); + U32Millimeters mm(km); + + ensure("km -> m conversion", m.value() == 1000); + ensure("km -> cm conversion", cm.value() == 1000 * 100); + ensure("km -> mm conversion", mm.value() == 1000 * 1000); + + U32Gigahertz GHz(1); + U32Megahertz MHz(GHz); + U32Kilohertz KHz(GHz); + U32Hertz Hz(GHz); + + ensure("GHz -> MHz conversion", MHz.value() == 1000); + ensure("GHz -> KHz conversion", KHz.value() == 1000 * 1000); + ensure("GHz -> Hz conversion", Hz.value() == 1000 * 1000 * 1000); + + F32Radians rad(6.2831853071795f); + S32Degrees deg(rad); + ensure("radians -> degrees conversion", deg.value() == 360); + + F32Percent percent(50); + F32Ratio ratio(percent); + ensure("percent -> ratio conversion", ratio.value() == 0.5f); + + U32Kilotriangles ktris(1); + U32Triangles tris(ktris); + ensure("kilotriangles -> triangles conversion", tris.value() == 1000); + } + + bool value_near(F32 value, F32 target, F32 threshold) + { + return fabsf(value - target) < threshold; + } + + // linear transforms + template<> template<> + void units_object_t::test<10>() + { + F32Celcius float_celcius(100); + F32Fahrenheit float_fahrenheit(float_celcius); + ensure("floating point celcius -> fahrenheit conversion using linear transform", value_near(float_fahrenheit.value(), 212, 0.1f) ); + + float_celcius = float_fahrenheit; + ensure("floating point fahrenheit -> celcius conversion using linear transform (round trip)", value_near(float_celcius.value(), 100.f, 0.1f) ); + + S32Celcius int_celcius(100); + S32Fahrenheit int_fahrenheit(int_celcius); + ensure("integer celcius -> fahrenheit conversion using linear transform", int_fahrenheit.value() == 212); + + int_celcius = int_fahrenheit; + ensure("integer fahrenheit -> celcius conversion using linear transform (round trip)", int_celcius.value() == 100); + } } diff --git a/indra/llcommon/tests/lluri_test.cpp b/indra/llcommon/tests/lluri_test.cpp index 1a4c6641b9..b48dd3ef1b 100644 --- a/indra/llcommon/tests/lluri_test.cpp +++ b/indra/llcommon/tests/lluri_test.cpp @@ -6,21 +6,21 @@ * $LicenseInfo:firstyear=2006&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -34,359 +34,359 @@ namespace tut { - struct URITestData { - void checkParts(const LLURI& u, - const char* expectedScheme, - const char* expectedOpaque, - const char* expectedAuthority, - const char* expectedPath, - const char* expectedQuery = "") - { - ensure_equals("scheme", u.scheme(), expectedScheme); - ensure_equals("opaque", u.opaque(), expectedOpaque); - ensure_equals("authority", u.authority(), expectedAuthority); - ensure_equals("path", u.path(), expectedPath); - ensure_equals("query", u.query(), expectedQuery); - } - - void escapeRoundTrip(const std::string& uri_raw_1) - { - std::string uri_esc_1(LLURI::escape(uri_raw_1)); - std::string uri_raw_2(LLURI::unescape(uri_esc_1)); - ensure_equals("escape/unescape raw", uri_raw_2, uri_raw_1); - std::string uri_esc_2(LLURI::escape(uri_raw_2)); - ensure_equals("escape/unescape escaped", uri_esc_2, uri_esc_1); - } - }; - - typedef test_group<URITestData> URITestGroup; - typedef URITestGroup::object URITestObject; - - URITestGroup uriTestGroup("LLURI"); - - template<> template<> - void URITestObject::test<1>() - { - LLURI u("http://abc.com/def/ghi?x=37&y=hello"); - - ensure_equals("scheme", u.scheme(), "http"); - ensure_equals("authority", u.authority(), "abc.com"); - ensure_equals("path", u.path(), "/def/ghi"); - ensure_equals("query", u.query(), "x=37&y=hello"); - - ensure_equals("host name", u.hostName(), "abc.com"); - ensure_equals("host port", u.hostPort(), 80); - - LLSD query = u.queryMap(); - ensure_equals("query x", query["x"].asInteger(), 37); - ensure_equals("query y", query["y"].asString(), "hello"); - - query = LLURI::queryMap("x=22.23&y=https://lindenlab.com/"); - ensure_equals("query x", query["x"].asReal(), 22.23); - ensure_equals("query y", query["y"].asURI().asString(), "https://lindenlab.com/"); - } - - template<> template<> - void URITestObject::test<2>() - { - set_test_name("empty string"); - checkParts(LLURI(""), "", "", "", ""); - } - - template<> template<> - void URITestObject::test<3>() - { - set_test_name("no scheme"); - checkParts(LLURI("foo"), "", "foo", "", ""); - checkParts(LLURI("foo%3A"), "", "foo:", "", ""); - } - - template<> template<> - void URITestObject::test<4>() - { - set_test_name("scheme w/o paths"); - checkParts(LLURI("mailto:zero@ll.com"), - "mailto", "zero@ll.com", "", ""); - checkParts(LLURI("silly://abc/def?foo"), - "silly", "//abc/def?foo", "", ""); - } - - template<> template<> - void URITestObject::test<5>() - { - set_test_name("authority section"); - checkParts(LLURI("http:///"), - "http", "///", "", "/"); - - checkParts(LLURI("http://abc"), - "http", "//abc", "abc", ""); - - checkParts(LLURI("http://a%2Fb/cd"), - "http", "//a/b/cd", "a/b", "/cd"); - - checkParts(LLURI("http://host?"), - "http", "//host?", "host", ""); - } - - template<> template<> - void URITestObject::test<6>() - { - set_test_name("path section"); - checkParts(LLURI("http://host/a/b/"), - "http", "//host/a/b/", "host", "/a/b/"); - - checkParts(LLURI("http://host/a%3Fb/"), - "http", "//host/a?b/", "host", "/a?b/"); - - checkParts(LLURI("http://host/a:b/"), - "http", "//host/a:b/", "host", "/a:b/"); - } - - template<> template<> - void URITestObject::test<7>() - { - set_test_name("query string"); - checkParts(LLURI("http://host/?"), - "http", "//host/?", "host", "/", ""); - - checkParts(LLURI("http://host/?x"), - "http", "//host/?x", "host", "/", "x"); - - checkParts(LLURI("http://host/??"), - "http", "//host/??", "host", "/", "?"); - - checkParts(LLURI("http://host/?%3F"), - "http", "//host/??", "host", "/", "?"); - } - - template<> template<> - void URITestObject::test<8>() - { - LLSD path; - path.append("x"); - path.append("123"); - checkParts(LLURI::buildHTTP("host", path), - "http", "//host/x/123", "host", "/x/123"); - - LLSD query; - query["123"] = "12"; - query["abcd"] = "abc"; - checkParts(LLURI::buildHTTP("host", path, query), - "http", "//host/x/123?123=12&abcd=abc", - "host", "/x/123", "123=12&abcd=abc"); - - ensure_equals(LLURI::buildHTTP("host", "").asString(), - "http://host"); - ensure_equals(LLURI::buildHTTP("host", "/").asString(), - "http://host/"); - ensure_equals(LLURI::buildHTTP("host", "//").asString(), - "http://host/"); - ensure_equals(LLURI::buildHTTP("host", "dir name").asString(), - "http://host/dir%20name"); - ensure_equals(LLURI::buildHTTP("host", "dir name/").asString(), - "http://host/dir%20name/"); - ensure_equals(LLURI::buildHTTP("host", "/dir name").asString(), - "http://host/dir%20name"); - ensure_equals(LLURI::buildHTTP("host", "/dir name/").asString(), - "http://host/dir%20name/"); - ensure_equals(LLURI::buildHTTP("host", "dir name/subdir name").asString(), - "http://host/dir%20name/subdir%20name"); - ensure_equals(LLURI::buildHTTP("host", "dir name/subdir name/").asString(), - "http://host/dir%20name/subdir%20name/"); - ensure_equals(LLURI::buildHTTP("host", "/dir name/subdir name").asString(), - "http://host/dir%20name/subdir%20name"); - ensure_equals(LLURI::buildHTTP("host", "/dir name/subdir name/").asString(), - "http://host/dir%20name/subdir%20name/"); - ensure_equals(LLURI::buildHTTP("host", "//dir name//subdir name//").asString(), - "http://host/dir%20name/subdir%20name/"); - } - - template<> template<> - void URITestObject::test<9>() - { - set_test_name("test unescaped path components"); - LLSD path; - path.append("x@*//*$&^"); - path.append("123"); - checkParts(LLURI::buildHTTP("host", path), - "http", "//host/x@*//*$&^/123", "host", "/x@*//*$&^/123"); - } - - template<> template<> - void URITestObject::test<10>() - { - set_test_name("test unescaped query components"); - LLSD path; - path.append("x"); - path.append("123"); - LLSD query; - query["123"] = "?&*#//"; - query["**@&?//"] = "abc"; - checkParts(LLURI::buildHTTP("host", path, query), - "http", "//host/x/123?**@&?//=abc&123=?&*#//", - "host", "/x/123", "**@&?//=abc&123=?&*#//"); - } - - template<> template<> - void URITestObject::test<11>() - { - set_test_name("test unescaped host components"); - LLSD path; - path.append("x"); - path.append("123"); - LLSD query; - query["123"] = "12"; - query["abcd"] = "abc"; - checkParts(LLURI::buildHTTP("hi123*33--}{:portstuffs", path, query), - "http", "//hi123*33--}{:portstuffs/x/123?123=12&abcd=abc", - "hi123*33--}{:portstuffs", "/x/123", "123=12&abcd=abc"); - } - - template<> template<> - void URITestObject::test<12>() - { - set_test_name("test funky host_port values that are actually prefixes"); - - checkParts(LLURI::buildHTTP("http://example.com:8080", LLSD()), - "http", "//example.com:8080", - "example.com:8080", ""); - - checkParts(LLURI::buildHTTP("http://example.com:8080/", LLSD()), - "http", "//example.com:8080/", - "example.com:8080", "/"); - - checkParts(LLURI::buildHTTP("http://example.com:8080/a/b", LLSD()), - "http", "//example.com:8080/a/b", - "example.com:8080", "/a/b"); - } - - template<> template<> - void URITestObject::test<13>() - { - const std::string unreserved = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" - "0123456789" - "-._~"; - set_test_name("test escape"); - ensure_equals("escaping", LLURI::escape("abcdefg", "abcdef"), "abcdef%67"); - ensure_equals("escaping", LLURI::escape("|/&\\+-_!@", ""), "%7C%2F%26%5C%2B%2D%5F%21%40"); - ensure_equals("escaping as query variable", - LLURI::escape("http://10.0.1.4:12032/agent/god/agent-id/map/layer/?resume=http://station3.ll.com:12032/agent/203ad6df-b522-491d-ba48-4e24eb57aeff/send-postcard", unreserved + ":@!$'()*+,="), - "http:%2F%2F10.0.1.4:12032%2Fagent%2Fgod%2Fagent-id%2Fmap%2Flayer%2F%3Fresume=http:%2F%2Fstation3.ll.com:12032%2Fagent%2F203ad6df-b522-491d-ba48-4e24eb57aeff%2Fsend-postcard"); - // French cedilla (C with squiggle, like in the word Francais) is UTF-8 C3 A7 + struct URITestData { + void checkParts(const LLURI& u, + const char* expectedScheme, + const char* expectedOpaque, + const char* expectedAuthority, + const char* expectedPath, + const char* expectedQuery = "") + { + ensure_equals("scheme", u.scheme(), expectedScheme); + ensure_equals("opaque", u.opaque(), expectedOpaque); + ensure_equals("authority", u.authority(), expectedAuthority); + ensure_equals("path", u.path(), expectedPath); + ensure_equals("query", u.query(), expectedQuery); + } + + void escapeRoundTrip(const std::string& uri_raw_1) + { + std::string uri_esc_1(LLURI::escape(uri_raw_1)); + std::string uri_raw_2(LLURI::unescape(uri_esc_1)); + ensure_equals("escape/unescape raw", uri_raw_2, uri_raw_1); + std::string uri_esc_2(LLURI::escape(uri_raw_2)); + ensure_equals("escape/unescape escaped", uri_esc_2, uri_esc_1); + } + }; + + typedef test_group<URITestData> URITestGroup; + typedef URITestGroup::object URITestObject; + + URITestGroup uriTestGroup("LLURI"); + + template<> template<> + void URITestObject::test<1>() + { + LLURI u("http://abc.com/def/ghi?x=37&y=hello"); + + ensure_equals("scheme", u.scheme(), "http"); + ensure_equals("authority", u.authority(), "abc.com"); + ensure_equals("path", u.path(), "/def/ghi"); + ensure_equals("query", u.query(), "x=37&y=hello"); + + ensure_equals("host name", u.hostName(), "abc.com"); + ensure_equals("host port", u.hostPort(), 80); + + LLSD query = u.queryMap(); + ensure_equals("query x", query["x"].asInteger(), 37); + ensure_equals("query y", query["y"].asString(), "hello"); + + query = LLURI::queryMap("x=22.23&y=https://lindenlab.com/"); + ensure_equals("query x", query["x"].asReal(), 22.23); + ensure_equals("query y", query["y"].asURI().asString(), "https://lindenlab.com/"); + } + + template<> template<> + void URITestObject::test<2>() + { + set_test_name("empty string"); + checkParts(LLURI(""), "", "", "", ""); + } + + template<> template<> + void URITestObject::test<3>() + { + set_test_name("no scheme"); + checkParts(LLURI("foo"), "", "foo", "", ""); + checkParts(LLURI("foo%3A"), "", "foo:", "", ""); + } + + template<> template<> + void URITestObject::test<4>() + { + set_test_name("scheme w/o paths"); + checkParts(LLURI("mailto:zero@ll.com"), + "mailto", "zero@ll.com", "", ""); + checkParts(LLURI("silly://abc/def?foo"), + "silly", "//abc/def?foo", "", ""); + } + + template<> template<> + void URITestObject::test<5>() + { + set_test_name("authority section"); + checkParts(LLURI("http:///"), + "http", "///", "", "/"); + + checkParts(LLURI("http://abc"), + "http", "//abc", "abc", ""); + + checkParts(LLURI("http://a%2Fb/cd"), + "http", "//a/b/cd", "a/b", "/cd"); + + checkParts(LLURI("http://host?"), + "http", "//host?", "host", ""); + } + + template<> template<> + void URITestObject::test<6>() + { + set_test_name("path section"); + checkParts(LLURI("http://host/a/b/"), + "http", "//host/a/b/", "host", "/a/b/"); + + checkParts(LLURI("http://host/a%3Fb/"), + "http", "//host/a?b/", "host", "/a?b/"); + + checkParts(LLURI("http://host/a:b/"), + "http", "//host/a:b/", "host", "/a:b/"); + } + + template<> template<> + void URITestObject::test<7>() + { + set_test_name("query string"); + checkParts(LLURI("http://host/?"), + "http", "//host/?", "host", "/", ""); + + checkParts(LLURI("http://host/?x"), + "http", "//host/?x", "host", "/", "x"); + + checkParts(LLURI("http://host/??"), + "http", "//host/??", "host", "/", "?"); + + checkParts(LLURI("http://host/?%3F"), + "http", "//host/??", "host", "/", "?"); + } + + template<> template<> + void URITestObject::test<8>() + { + LLSD path; + path.append("x"); + path.append("123"); + checkParts(LLURI::buildHTTP("host", path), + "http", "//host/x/123", "host", "/x/123"); + + LLSD query; + query["123"] = "12"; + query["abcd"] = "abc"; + checkParts(LLURI::buildHTTP("host", path, query), + "http", "//host/x/123?123=12&abcd=abc", + "host", "/x/123", "123=12&abcd=abc"); + + ensure_equals(LLURI::buildHTTP("host", "").asString(), + "http://host"); + ensure_equals(LLURI::buildHTTP("host", "/").asString(), + "http://host/"); + ensure_equals(LLURI::buildHTTP("host", "//").asString(), + "http://host/"); + ensure_equals(LLURI::buildHTTP("host", "dir name").asString(), + "http://host/dir%20name"); + ensure_equals(LLURI::buildHTTP("host", "dir name/").asString(), + "http://host/dir%20name/"); + ensure_equals(LLURI::buildHTTP("host", "/dir name").asString(), + "http://host/dir%20name"); + ensure_equals(LLURI::buildHTTP("host", "/dir name/").asString(), + "http://host/dir%20name/"); + ensure_equals(LLURI::buildHTTP("host", "dir name/subdir name").asString(), + "http://host/dir%20name/subdir%20name"); + ensure_equals(LLURI::buildHTTP("host", "dir name/subdir name/").asString(), + "http://host/dir%20name/subdir%20name/"); + ensure_equals(LLURI::buildHTTP("host", "/dir name/subdir name").asString(), + "http://host/dir%20name/subdir%20name"); + ensure_equals(LLURI::buildHTTP("host", "/dir name/subdir name/").asString(), + "http://host/dir%20name/subdir%20name/"); + ensure_equals(LLURI::buildHTTP("host", "//dir name//subdir name//").asString(), + "http://host/dir%20name/subdir%20name/"); + } + + template<> template<> + void URITestObject::test<9>() + { + set_test_name("test unescaped path components"); + LLSD path; + path.append("x@*//*$&^"); + path.append("123"); + checkParts(LLURI::buildHTTP("host", path), + "http", "//host/x@*//*$&^/123", "host", "/x@*//*$&^/123"); + } + + template<> template<> + void URITestObject::test<10>() + { + set_test_name("test unescaped query components"); + LLSD path; + path.append("x"); + path.append("123"); + LLSD query; + query["123"] = "?&*#//"; + query["**@&?//"] = "abc"; + checkParts(LLURI::buildHTTP("host", path, query), + "http", "//host/x/123?**@&?//=abc&123=?&*#//", + "host", "/x/123", "**@&?//=abc&123=?&*#//"); + } + + template<> template<> + void URITestObject::test<11>() + { + set_test_name("test unescaped host components"); + LLSD path; + path.append("x"); + path.append("123"); + LLSD query; + query["123"] = "12"; + query["abcd"] = "abc"; + checkParts(LLURI::buildHTTP("hi123*33--}{:portstuffs", path, query), + "http", "//hi123*33--}{:portstuffs/x/123?123=12&abcd=abc", + "hi123*33--}{:portstuffs", "/x/123", "123=12&abcd=abc"); + } + + template<> template<> + void URITestObject::test<12>() + { + set_test_name("test funky host_port values that are actually prefixes"); + + checkParts(LLURI::buildHTTP("http://example.com:8080", LLSD()), + "http", "//example.com:8080", + "example.com:8080", ""); + + checkParts(LLURI::buildHTTP("http://example.com:8080/", LLSD()), + "http", "//example.com:8080/", + "example.com:8080", "/"); + + checkParts(LLURI::buildHTTP("http://example.com:8080/a/b", LLSD()), + "http", "//example.com:8080/a/b", + "example.com:8080", "/a/b"); + } + + template<> template<> + void URITestObject::test<13>() + { + const std::string unreserved = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "0123456789" + "-._~"; + set_test_name("test escape"); + ensure_equals("escaping", LLURI::escape("abcdefg", "abcdef"), "abcdef%67"); + ensure_equals("escaping", LLURI::escape("|/&\\+-_!@", ""), "%7C%2F%26%5C%2B%2D%5F%21%40"); + ensure_equals("escaping as query variable", + LLURI::escape("http://10.0.1.4:12032/agent/god/agent-id/map/layer/?resume=http://station3.ll.com:12032/agent/203ad6df-b522-491d-ba48-4e24eb57aeff/send-postcard", unreserved + ":@!$'()*+,="), + "http:%2F%2F10.0.1.4:12032%2Fagent%2Fgod%2Fagent-id%2Fmap%2Flayer%2F%3Fresume=http:%2F%2Fstation3.ll.com:12032%2Fagent%2F203ad6df-b522-491d-ba48-4e24eb57aeff%2Fsend-postcard"); + // French cedilla (C with squiggle, like in the word Francais) is UTF-8 C3 A7 #if LL_WINDOWS #pragma warning(disable: 4309) #endif - std::string cedilla; - cedilla.push_back( (char)0xC3 ); - cedilla.push_back( (char)0xA7 ); - ensure_equals("escape UTF8", LLURI::escape( cedilla, unreserved), "%C3%A7"); - } - - - template<> template<> - void URITestObject::test<14>() - { - set_test_name("make sure escape and unescape of empty strings return empty strings."); - std::string uri_esc(LLURI::escape("")); - ensure("escape string empty", uri_esc.empty()); - std::string uri_raw(LLURI::unescape("")); - ensure("unescape string empty", uri_raw.empty()); - } - - template<> template<> - void URITestObject::test<15>() - { - set_test_name("do some round-trip tests"); - escapeRoundTrip("http://secondlife.com"); - escapeRoundTrip("http://secondlife.com/url with spaces"); - escapeRoundTrip("http://bad[domain]name.com/"); - escapeRoundTrip("ftp://bill.gates@ms/micro$oft.com/c:\\autoexec.bat"); - escapeRoundTrip(""); - } - - template<> template<> - void URITestObject::test<16>() - { - set_test_name("Test the default escaping"); - // yes -- this mangles the url. This is expected behavior - std::string simple("http://secondlife.com"); - ensure_equals( - "simple http", - LLURI::escape(simple), - "http%3A%2F%2Fsecondlife.com"); - ensure_equals( - "needs escape", - LLURI::escape("http://get.secondlife.com/windows viewer"), - "http%3A%2F%2Fget.secondlife.com%2Fwindows%20viewer"); - } - - template<> template<> - void URITestObject::test<17>() - { - set_test_name("do some round-trip tests with very long strings."); - escapeRoundTrip("Welcome to Second Life.We hope you'll have a richly rewarding experience, filled with creativity, self expression and fun.The goals of the Community Standards are simple: treat each other with respect and without harassment, adhere to local standards as indicated by simulator ratings, and refrain from any hate activity which slurs a real-world individual or real-world community. Behavioral Guidelines - The Big Six"); - escapeRoundTrip( - "'asset_data':b(12100){'task_id':ucc706f2d-0b68-68f8-11a4-f1043ff35ca0}\n{\n\tname\tObject|\n\tpermissions 0\n\t{\n\t\tbase_mask\t7fffffff\n\t\towner_mask\t7fffffff\n\t\tgroup_mask\t00000000\n\t\teveryone_mask\t00000000\n\t\tnext_owner_mask\t7fffffff\n\t\tcreator_id\t13fd9595-a47b-4d64-a5fb-6da645f038e0\n\t\towner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tlast_owner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tgroup_id\t00000000-0000-0000-0000-000000000000\n\t}\n\tlocal_id\t217444921\n\ttotal_crc\t323\n\ttype\t2\n\ttask_valid\t2\n\ttravel_access\t13\n\tdisplayopts\t2\n\tdisplaytype\tv\n\tpos\t-0.368634403\t0.00781063363\t-0.569040775\n\toldpos\t150.117996\t25.8658009\t8.19664001\n\trotation\t-0.06293071806430816650390625\t-0.6995697021484375\t-0.7002241611480712890625\t0.1277817934751510620117188\n\tchildpos\t-0.00499999989\t-0.0359999985\t0.307999998\n\tchildrot\t-0.515492737293243408203125\t-0.46601200103759765625\t0.529055416584014892578125\t0.4870323240756988525390625\n\tscale" - "\t0.074629\t0.289956\t0.01\n\tsit_offset\t0\t0\t0\n\tcamera_eye_offset\t0\t0\t0\n\tcamera_at_offset\t0\t0\t0\n\tsit_quat\t0\t0\t0\t1\n\tsit_hint\t0\n\tstate\t160\n\tmaterial\t3\n\tsoundid\t00000000-0000-0000-0000-000000000000\n\tsoundgain\t0\n\tsoundradius\t0\n\tsoundflags\t0\n\ttextcolor\t0 0 0 1\n\tselected\t0\n\tselector\t00000000-0000-0000-0000-000000000000\n\tusephysics\t0\n\trotate_x\t1\n\trotate_y\t1\n\trotate_z\t1\n\tphantom\t0\n\tremote_script_access_pin\t0\n\tvolume_detect\t0\n\tblock_grabs\t0\n\tdie_at_edge\t0\n\treturn_at_edge\t0\n\ttemporary\t0\n\tsandbox\t0\n\tsandboxhome\t0\t0\t0\n\tshape 0\n\t{\n\t\tpath 0\n\t\t{\n\t\t\tcurve\t16\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\tscale_x\t1\n\t\t\tscale_y\t1\n\t\t\tshear_x\t0\n\t\t\tshear_y\t0\n\t\t\ttwist\t0\n\t\t\ttwist_begin\t0\n\t\t\tradius_offset\t0\n\t\t\ttaper_x\t0\n\t\t\ttaper_y\t0\n\t\t\trevolutions\t1\n\t\t\tskew\t0\n\t\t}\n\t\tprofile 0\n\t\t{\n\t\t\tcurve\t1\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\thollow\t0\n\t\t}\n\t}\n\tf" - "aces\t6\n\t{\n\t\timageid\tddde1ffc-678b-3cda-1748-513086bdf01b\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204" - "\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tddde1ffc-678b-3cda-1748-513086bdf01b\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t-1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\tps_next_crc\t1\n\tgpw_bias\t1\n\tip\t0\n\tcomplete\tTRUE\n\tdelay\t50000\n\tnextstart\t0\n\tbirthtime\t1061088050622956\n\treztime\t1094866329019785\n\tparceltime\t1133568981980596\n\ttax_rate\t1.00084\n\tscratchpad\t0\n\t{\n\t\n\t}\n\tsale_info\t0\n\t{\n\t\tsale_type\tnot\n\t\tsale_price\t10\n\t}\n\tcorrect_family_id\t00000000-0000-0000-0000-000000000000\n\thas_rezzed\t0\n\tpre_link_base_mask\t7fffffff\n\tlinked \tchild\n\tdefault_pay_price\t-2\t1\t5\t10\t20\n}\n{'task_id':u61fa7364-e151-0597-774c-523312dae31b}\n{\n\tname\tObject|\n\tpermissions 0\n\t{\n\t\tbase_mask\t7fffff" - "ff\n\t\towner_mask\t7fffffff\n\t\tgroup_mask\t00000000\n\t\teveryone_mask\t00000000\n\t\tnext_owner_mask\t7fffffff\n\t\tcreator_id\t13fd9595-a47b-4d64-a5fb-6da645f038e0\n\t\towner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tlast_owner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tgroup_id\t00000000-0000-0000-0000-000000000000\n\t}\n\tlocal_id\t217444922\n\ttotal_crc\t324\n\ttype\t2\n\ttask_valid\t2\n\ttravel_access\t13\n\tdisplayopts\t2\n\tdisplaytype\tv\n\tpos\t-0.367110789\t0.00780026987\t-0.566269755\n\toldpos\t150.115005\t25.8479004\t8.18669987\n\trotation\t0.47332942485809326171875\t-0.380102097988128662109375\t-0.5734078884124755859375\t0.550168216228485107421875\n\tchildpos\t-0.00499999989\t-0.0370000005\t0.305000007\n\tchildrot\t-0.736649334430694580078125\t-0.03042060509324073791503906\t-0.02784589119255542755126953\t0.67501628398895263671875\n\tscale\t0.074629\t0.289956\t0.01\n\tsit_offset\t0\t0\t0\n\tcamera_eye_offset\t0\t0\t0\n\tcamera_at_offset\t0\t0\t0\n\tsit_quat\t0\t" - "0\t0\t1\n\tsit_hint\t0\n\tstate\t160\n\tmaterial\t3\n\tsoundid\t00000000-0000-0000-0000-000000000000\n\tsoundgain\t0\n\tsoundradius\t0\n\tsoundflags\t0\n\ttextcolor\t0 0 0 1\n\tselected\t0\n\tselector\t00000000-0000-0000-0000-000000000000\n\tusephysics\t0\n\trotate_x\t1\n\trotate_y\t1\n\trotate_z\t1\n\tphantom\t0\n\tremote_script_access_pin\t0\n\tvolume_detect\t0\n\tblock_grabs\t0\n\tdie_at_edge\t0\n\treturn_at_edge\t0\n\ttemporary\t0\n\tsandbox\t0\n\tsandboxhome\t0\t0\t0\n\tshape 0\n\t{\n\t\tpath 0\n\t\t{\n\t\t\tcurve\t16\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\tscale_x\t1\n\t\t\tscale_y\t1\n\t\t\tshear_x\t0\n\t\t\tshear_y\t0\n\t\t\ttwist\t0\n\t\t\ttwist_begin\t0\n\t\t\tradius_offset\t0\n\t\t\ttaper_x\t0\n\t\t\ttaper_y\t0\n\t\t\trevolutions\t1\n\t\t\tskew\t0\n\t\t}\n\t\tprofile 0\n\t\t{\n\t\t\tcurve\t1\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\thollow\t0\n\t\t}\n\t}\n\tfaces\t6\n\t{\n\t\timageid\tddde1ffc-678b-3cda-1748-513086bdf01b\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t" - "\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t" - "\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tddde1ffc-678b-3cda-1748-513086bdf01b\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t-1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\tps_next_crc\t1\n\tgpw_bias\t1\n\tip\t0\n\tcomplete\tTRUE\n\tdelay\t50000\n\tnextstart\t0\n\tbirthtime\t1061087839248891\n\treztime\t1094866329020800\n\tparceltime\t1133568981981983\n\ttax_rate\t1.00084\n\tscratchpad\t0\n\t{\n\t\n\t}\n\tsale_info\t0\n\t{\n\t\tsale_type\tnot\n\t\tsale_price\t10\n\t}\n\tcorrect_family_id\t00000000-0000-0000-0000-000000000000\n\thas_rezzed\t0\n\tpre_link_base_mask\t7fffffff\n\tlinked \tchild\n\tdefault_pay_price\t-2\t1\t5\t10\t20\n}\n{'task_id':ub8d68643-7dd8-57af-0d24-8790032aed0c}\n{\n\tname\tObject|\n\tpermissions 0\n\t{\n\t\tbase_mask\t7fffffff\n\t\towner_mask\t7fffffff\n\t\tgroup_mask\t00000000\n\t\teveryone_mask\t00000000\n\t\tnext_owner_mask\t7fffffff\n\t\tcreat" - "or_id\t13fd9595-a47b-4d64-a5fb-6da645f038e0\n\t\towner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tlast_owner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tgroup_id\t00000000-0000-0000-0000-000000000000\n\t}\n\tlocal_id\t217444923\n\ttotal_crc\t235\n\ttype\t2\n\ttask_valid\t2\n\ttravel_access\t13\n\tdisplayopts\t2\n\tdisplaytype\tv\n\tpos\t-0.120029509\t-0.00284469454\t-0.0302077383\n\toldpos\t150.710999\t25.8584995\t8.19172001\n\trotation\t0.145459949970245361328125\t-0.1646589934825897216796875\t0.659558117389678955078125\t-0.718826770782470703125\n\tchildpos\t0\t-0.182999998\t-0.26699999\n\tchildrot\t0.991444766521453857421875\t3.271923924330621957778931e-05\t-0.0002416197530692443251609802\t0.1305266767740249633789062\n\tscale\t0.0382982\t0.205957\t0.368276\n\tsit_offset\t0\t0\t0\n\tcamera_eye_offset\t0\t0\t0\n\tcamera_at_offset\t0\t0\t0\n\tsit_quat\t0\t0\t0\t1\n\tsit_hint\t0\n\tstate\t160\n\tmaterial\t3\n\tsoundid\t00000000-0000-0000-0000-000000000000\n\tsoundgain\t0\n\tsoundra" - "dius\t0\n\tsoundflags\t0\n\ttextcolor\t0 0 0 1\n\tselected\t0\n\tselector\t00000000-0000-0000-0000-000000000000\n\tusephysics\t0\n\trotate_x\t1\n\trotate_y\t1\n\trotate_z\t1\n\tphantom\t0\n\tremote_script_access_pin\t0\n\tvolume_detect\t0\n\tblock_grabs\t0\n\tdie_at_edge\t0\n\treturn_at_edge\t0\n\ttemporary\t0\n\tsandbox\t0\n\tsandboxhome\t0\t0\t0\n\tshape 0\n\t{\n\t\tpath 0\n\t\t{\n\t\t\tcurve\t32\n\t\t\tbegin\t0.3\n\t\t\tend\t0.65\n\t\t\tscale_x\t1\n\t\t\tscale_y\t0.05\n\t\t\tshear_x\t0\n\t\t\tshear_y\t0\n\t\t\ttwist\t0\n\t\t\ttwist_begin\t0\n\t\t\tradius_offset\t0\n\t\t\ttaper_x\t0\n\t\t\ttaper_y\t0\n\t\t\trevolutions\t1\n\t\t\tskew\t0\n\t\t}\n\t\tprofile 0\n\t\t{\n\t\t\tcurve\t0\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\thollow\t0\n\t\t}\n\t}\n\tfaces\t3\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0" - "\n\t}\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\tps_next_crc\t1\n\tgpw_bias\t1\n\tip\t0\n\tcomplete\tTRUE\n\tdelay\t50000\n\tnextstart\t0\n\tbirthtime\t1061087534454174\n\treztime\t1094866329021741\n\tparceltime\t1133568981982889\n\ttax_rate\t1.00326\n\tscratchpad\t0\n\t{\n\t\n\t}\n\tsale_info\t0\n\t{\n\t\tsale_type\tnot\n\t\tsale_price\t10\n\t}\n\tcorrect_family_id\t00000000-0000-0000-0000-000000000000\n\thas_rezzed\t0\n\tpre_link_base_mask\t7fffffff\n\tlinked \tchild\n\tdefault_pay_price\t-2\t1\t5\t10\t20\n}\n{'task_id':ue4b19200-9d33-962f-c8c5-6f" - "25be3a3fd0}\n{\n\tname\tApotheosis_Immolaine_tail|\n\tpermissions 0\n\t{\n\t\tbase_mask\t7fffffff\n\t\towner_mask\t7fffffff\n\t\tgroup_mask\t00000000\n\t\teveryone_mask\t00000000\n\t\tnext_owner_mask\t7fffffff\n\t\tcreator_id\t13fd9595-a47b-4d64-a5fb-6da645f038e0\n\t\towner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tlast_owner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tgroup_id\t00000000-0000-0000-0000-000000000000\n\t}\n\tlocal_id\t217444924\n\ttotal_crc\t675\n\ttype\t1\n\ttask_valid\t2\n\ttravel_access\t13\n\tdisplayopts\t2\n\tdisplaytype\tv\n\tpos\t-0.34780401\t-0.00968400016\t-0.260098994\n\toldpos\t0\t0\t0\n\trotation\t0.73164522647857666015625\t-0.67541944980621337890625\t-0.07733880728483200073242188\t0.05022468417882919311523438\n\tvelocity\t0\t0\t0\n\tangvel\t0\t0\t0\n\tscale\t0.0382982\t0.32228\t0.383834\n\tsit_offset\t0\t0\t0\n\tcamera_eye_offset\t0\t0\t0\n\tcamera_at_offset\t0\t0\t0\n\tsit_quat\t0\t0\t0\t1\n\tsit_hint\t0\n\tstate\t160\n\tmaterial\t3\n\tsoundid\t00000" - "000-0000-0000-0000-000000000000\n\tsoundgain\t0\n\tsoundradius\t0\n\tsoundflags\t0\n\ttextcolor\t0 0 0 1\n\tselected\t0\n\tselector\t00000000-0000-0000-0000-000000000000\n\tusephysics\t0\n\trotate_x\t1\n\trotate_y\t1\n\trotate_z\t1\n\tphantom\t0\n\tremote_script_access_pin\t0\n\tvolume_detect\t0\n\tblock_grabs\t0\n\tdie_at_edge\t0\n\treturn_at_edge\t0\n\ttemporary\t0\n\tsandbox\t0\n\tsandboxhome\t0\t0\t0\n\tshape 0\n\t{\n\t\tpath 0\n\t\t{\n\t\t\tcurve\t32\n\t\t\tbegin\t0.3\n\t\t\tend\t0.65\n\t\t\tscale_x\t1\n\t\t\tscale_y\t0.05\n\t\t\tshear_x\t0\n\t\t\tshear_y\t0\n\t\t\ttwist\t0\n\t\t\ttwist_begin\t0\n\t\t\tradius_offset\t0\n\t\t\ttaper_x\t0\n\t\t\ttaper_y\t0\n\t\t\trevolutions\t1\n\t\t\tskew\t0\n\t\t}\n\t\tprofile 0\n\t\t{\n\t\t\tcurve\t0\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\thollow\t0\n\t\t}\n\t}\n\tfaces\t3\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1" - ".57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\tps_next_crc\t1\n\tgpw_bias\t1\n\tip\t0\n\tcomplete\tTRUE\n\tdelay\t50000\n\tnextstart\t0\n\tbirthtime\t1061087463950186\n\treztime\t1094866329022555\n\tparceltime\t1133568981984359\n\tdescription\t(No Description)|\n\ttax_rate\t1.01736\n\tnamevalue\tAttachPt U32 RW S 10\n\tnamevalue\tAttachmentOrientation VEC3 RW DS -3.110088, -0.182018, 1.493795\n\tnamevalue\tAttachmentOffset VEC3 RW DS -0.347804, -0.009684, -0.260099\n\tnamevalue\tAttachItemI" - "D STRING RW SV 20f36c3a-b44b-9bc7-87f3-018bfdfc8cda\n\tscratchpad\t0\n\t{\n\t\n\t}\n\tsale_info\t0\n\t{\n\t\tsale_type\tnot\n\t\tsale_price\t10\n\t}\n\torig_asset_id\t8747acbc-d391-1e59-69f1-41d06830e6c0\n\torig_item_id\t20f36c3a-b44b-9bc7-87f3-018bfdfc8cda\n\tfrom_task_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\tcorrect_family_id\t00000000-0000-0000-0000-000000000000\n\thas_rezzed\t0\n\tpre_link_base_mask\t7fffffff\n\tlinked \tlinked\n\tdefault_pay_price\t-2\t1\t5\t10\t20\n}\n"); - } - - - template<> template<> - void URITestObject::test<18>() - { - LLURI u("secondlife:///app/login?first_name=Testert4&last_name=Tester&web_login_key=test"); - // if secondlife is the scheme, LLURI should parse /app/login as path, with no authority - ensure_equals("scheme", u.scheme(), "secondlife"); - ensure_equals("authority", u.authority(), ""); - ensure_equals("path", u.path(), "/app/login"); - ensure_equals("pathmap", u.pathArray()[0].asString(), "app"); - ensure_equals("pathmap", u.pathArray()[1].asString(), "login"); - ensure_equals("query", u.query(), "first_name=Testert4&last_name=Tester&web_login_key=test"); - ensure_equals("query map element", u.queryMap()["last_name"].asString(), "Tester"); - - u = LLURI("secondlife://Da Boom/128/128/128"); - // if secondlife is the scheme, LLURI should parse /128/128/128 as path, with Da Boom as authority - ensure_equals("scheme", u.scheme(), "secondlife"); - ensure_equals("authority", u.authority(), "Da Boom"); - ensure_equals("path", u.path(), "/128/128/128"); - ensure_equals("pathmap", u.pathArray()[0].asString(), "128"); - ensure_equals("pathmap", u.pathArray()[1].asString(), "128"); - ensure_equals("pathmap", u.pathArray()[2].asString(), "128"); - ensure_equals("query", u.query(), ""); - } - - template<> template<> - void URITestObject::test<19>() - { - set_test_name("Parse about: schemes"); - LLURI u("about:blank?redirect-http-hack=secondlife%3A%2F%2F%2Fapp%2Flogin%3Ffirst_name%3DCallum%26last_name%3DLinden%26location%3Dspecify%26grid%3Dvaak%26region%3D%2FMorris%2F128%2F128%26web_login_key%3Defaa4795-c2aa-4c58-8966-763c27931e78"); - ensure_equals("scheme", u.scheme(), "about"); - ensure_equals("authority", u.authority(), ""); - ensure_equals("path", u.path(), "blank"); - ensure_equals("pathmap", u.pathArray()[0].asString(), "blank"); - ensure_equals("query", u.query(), "redirect-http-hack=secondlife:///app/login?first_name=Callum&last_name=Linden&location=specify&grid=vaak®ion=/Morris/128/128&web_login_key=efaa4795-c2aa-4c58-8966-763c27931e78"); - ensure_equals("query map element", u.queryMap()["redirect-http-hack"].asString(), "secondlife:///app/login?first_name=Callum&last_name=Linden&location=specify&grid=vaak®ion=/Morris/128/128&web_login_key=efaa4795-c2aa-4c58-8966-763c27931e78"); - } - - template<> template<> - void URITestObject::test<20>() - { + std::string cedilla; + cedilla.push_back( (char)0xC3 ); + cedilla.push_back( (char)0xA7 ); + ensure_equals("escape UTF8", LLURI::escape( cedilla, unreserved), "%C3%A7"); + } + + + template<> template<> + void URITestObject::test<14>() + { + set_test_name("make sure escape and unescape of empty strings return empty strings."); + std::string uri_esc(LLURI::escape("")); + ensure("escape string empty", uri_esc.empty()); + std::string uri_raw(LLURI::unescape("")); + ensure("unescape string empty", uri_raw.empty()); + } + + template<> template<> + void URITestObject::test<15>() + { + set_test_name("do some round-trip tests"); + escapeRoundTrip("http://secondlife.com"); + escapeRoundTrip("http://secondlife.com/url with spaces"); + escapeRoundTrip("http://bad[domain]name.com/"); + escapeRoundTrip("ftp://bill.gates@ms/micro$oft.com/c:\\autoexec.bat"); + escapeRoundTrip(""); + } + + template<> template<> + void URITestObject::test<16>() + { + set_test_name("Test the default escaping"); + // yes -- this mangles the url. This is expected behavior + std::string simple("http://secondlife.com"); + ensure_equals( + "simple http", + LLURI::escape(simple), + "http%3A%2F%2Fsecondlife.com"); + ensure_equals( + "needs escape", + LLURI::escape("http://get.secondlife.com/windows viewer"), + "http%3A%2F%2Fget.secondlife.com%2Fwindows%20viewer"); + } + + template<> template<> + void URITestObject::test<17>() + { + set_test_name("do some round-trip tests with very long strings."); + escapeRoundTrip("Welcome to Second Life.We hope you'll have a richly rewarding experience, filled with creativity, self expression and fun.The goals of the Community Standards are simple: treat each other with respect and without harassment, adhere to local standards as indicated by simulator ratings, and refrain from any hate activity which slurs a real-world individual or real-world community. Behavioral Guidelines - The Big Six"); + escapeRoundTrip( + "'asset_data':b(12100){'task_id':ucc706f2d-0b68-68f8-11a4-f1043ff35ca0}\n{\n\tname\tObject|\n\tpermissions 0\n\t{\n\t\tbase_mask\t7fffffff\n\t\towner_mask\t7fffffff\n\t\tgroup_mask\t00000000\n\t\teveryone_mask\t00000000\n\t\tnext_owner_mask\t7fffffff\n\t\tcreator_id\t13fd9595-a47b-4d64-a5fb-6da645f038e0\n\t\towner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tlast_owner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tgroup_id\t00000000-0000-0000-0000-000000000000\n\t}\n\tlocal_id\t217444921\n\ttotal_crc\t323\n\ttype\t2\n\ttask_valid\t2\n\ttravel_access\t13\n\tdisplayopts\t2\n\tdisplaytype\tv\n\tpos\t-0.368634403\t0.00781063363\t-0.569040775\n\toldpos\t150.117996\t25.8658009\t8.19664001\n\trotation\t-0.06293071806430816650390625\t-0.6995697021484375\t-0.7002241611480712890625\t0.1277817934751510620117188\n\tchildpos\t-0.00499999989\t-0.0359999985\t0.307999998\n\tchildrot\t-0.515492737293243408203125\t-0.46601200103759765625\t0.529055416584014892578125\t0.4870323240756988525390625\n\tscale" + "\t0.074629\t0.289956\t0.01\n\tsit_offset\t0\t0\t0\n\tcamera_eye_offset\t0\t0\t0\n\tcamera_at_offset\t0\t0\t0\n\tsit_quat\t0\t0\t0\t1\n\tsit_hint\t0\n\tstate\t160\n\tmaterial\t3\n\tsoundid\t00000000-0000-0000-0000-000000000000\n\tsoundgain\t0\n\tsoundradius\t0\n\tsoundflags\t0\n\ttextcolor\t0 0 0 1\n\tselected\t0\n\tselector\t00000000-0000-0000-0000-000000000000\n\tusephysics\t0\n\trotate_x\t1\n\trotate_y\t1\n\trotate_z\t1\n\tphantom\t0\n\tremote_script_access_pin\t0\n\tvolume_detect\t0\n\tblock_grabs\t0\n\tdie_at_edge\t0\n\treturn_at_edge\t0\n\ttemporary\t0\n\tsandbox\t0\n\tsandboxhome\t0\t0\t0\n\tshape 0\n\t{\n\t\tpath 0\n\t\t{\n\t\t\tcurve\t16\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\tscale_x\t1\n\t\t\tscale_y\t1\n\t\t\tshear_x\t0\n\t\t\tshear_y\t0\n\t\t\ttwist\t0\n\t\t\ttwist_begin\t0\n\t\t\tradius_offset\t0\n\t\t\ttaper_x\t0\n\t\t\ttaper_y\t0\n\t\t\trevolutions\t1\n\t\t\tskew\t0\n\t\t}\n\t\tprofile 0\n\t\t{\n\t\t\tcurve\t1\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\thollow\t0\n\t\t}\n\t}\n\tf" + "aces\t6\n\t{\n\t\timageid\tddde1ffc-678b-3cda-1748-513086bdf01b\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204" + "\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tddde1ffc-678b-3cda-1748-513086bdf01b\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t-1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\tps_next_crc\t1\n\tgpw_bias\t1\n\tip\t0\n\tcomplete\tTRUE\n\tdelay\t50000\n\tnextstart\t0\n\tbirthtime\t1061088050622956\n\treztime\t1094866329019785\n\tparceltime\t1133568981980596\n\ttax_rate\t1.00084\n\tscratchpad\t0\n\t{\n\t\n\t}\n\tsale_info\t0\n\t{\n\t\tsale_type\tnot\n\t\tsale_price\t10\n\t}\n\tcorrect_family_id\t00000000-0000-0000-0000-000000000000\n\thas_rezzed\t0\n\tpre_link_base_mask\t7fffffff\n\tlinked \tchild\n\tdefault_pay_price\t-2\t1\t5\t10\t20\n}\n{'task_id':u61fa7364-e151-0597-774c-523312dae31b}\n{\n\tname\tObject|\n\tpermissions 0\n\t{\n\t\tbase_mask\t7fffff" + "ff\n\t\towner_mask\t7fffffff\n\t\tgroup_mask\t00000000\n\t\teveryone_mask\t00000000\n\t\tnext_owner_mask\t7fffffff\n\t\tcreator_id\t13fd9595-a47b-4d64-a5fb-6da645f038e0\n\t\towner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tlast_owner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tgroup_id\t00000000-0000-0000-0000-000000000000\n\t}\n\tlocal_id\t217444922\n\ttotal_crc\t324\n\ttype\t2\n\ttask_valid\t2\n\ttravel_access\t13\n\tdisplayopts\t2\n\tdisplaytype\tv\n\tpos\t-0.367110789\t0.00780026987\t-0.566269755\n\toldpos\t150.115005\t25.8479004\t8.18669987\n\trotation\t0.47332942485809326171875\t-0.380102097988128662109375\t-0.5734078884124755859375\t0.550168216228485107421875\n\tchildpos\t-0.00499999989\t-0.0370000005\t0.305000007\n\tchildrot\t-0.736649334430694580078125\t-0.03042060509324073791503906\t-0.02784589119255542755126953\t0.67501628398895263671875\n\tscale\t0.074629\t0.289956\t0.01\n\tsit_offset\t0\t0\t0\n\tcamera_eye_offset\t0\t0\t0\n\tcamera_at_offset\t0\t0\t0\n\tsit_quat\t0\t" + "0\t0\t1\n\tsit_hint\t0\n\tstate\t160\n\tmaterial\t3\n\tsoundid\t00000000-0000-0000-0000-000000000000\n\tsoundgain\t0\n\tsoundradius\t0\n\tsoundflags\t0\n\ttextcolor\t0 0 0 1\n\tselected\t0\n\tselector\t00000000-0000-0000-0000-000000000000\n\tusephysics\t0\n\trotate_x\t1\n\trotate_y\t1\n\trotate_z\t1\n\tphantom\t0\n\tremote_script_access_pin\t0\n\tvolume_detect\t0\n\tblock_grabs\t0\n\tdie_at_edge\t0\n\treturn_at_edge\t0\n\ttemporary\t0\n\tsandbox\t0\n\tsandboxhome\t0\t0\t0\n\tshape 0\n\t{\n\t\tpath 0\n\t\t{\n\t\t\tcurve\t16\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\tscale_x\t1\n\t\t\tscale_y\t1\n\t\t\tshear_x\t0\n\t\t\tshear_y\t0\n\t\t\ttwist\t0\n\t\t\ttwist_begin\t0\n\t\t\tradius_offset\t0\n\t\t\ttaper_x\t0\n\t\t\ttaper_y\t0\n\t\t\trevolutions\t1\n\t\t\tskew\t0\n\t\t}\n\t\tprofile 0\n\t\t{\n\t\t\tcurve\t1\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\thollow\t0\n\t\t}\n\t}\n\tfaces\t6\n\t{\n\t\timageid\tddde1ffc-678b-3cda-1748-513086bdf01b\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t" + "\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tf54a0c32-3cd1-d49a-5b4f-7b792bebc204\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t" + "\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\tddde1ffc-678b-3cda-1748-513086bdf01b\n\t\tcolors\t0.937255 0.796078 0.494118 1\n\t\tscales\t1\n\t\tscalet\t-1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t0\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\tps_next_crc\t1\n\tgpw_bias\t1\n\tip\t0\n\tcomplete\tTRUE\n\tdelay\t50000\n\tnextstart\t0\n\tbirthtime\t1061087839248891\n\treztime\t1094866329020800\n\tparceltime\t1133568981981983\n\ttax_rate\t1.00084\n\tscratchpad\t0\n\t{\n\t\n\t}\n\tsale_info\t0\n\t{\n\t\tsale_type\tnot\n\t\tsale_price\t10\n\t}\n\tcorrect_family_id\t00000000-0000-0000-0000-000000000000\n\thas_rezzed\t0\n\tpre_link_base_mask\t7fffffff\n\tlinked \tchild\n\tdefault_pay_price\t-2\t1\t5\t10\t20\n}\n{'task_id':ub8d68643-7dd8-57af-0d24-8790032aed0c}\n{\n\tname\tObject|\n\tpermissions 0\n\t{\n\t\tbase_mask\t7fffffff\n\t\towner_mask\t7fffffff\n\t\tgroup_mask\t00000000\n\t\teveryone_mask\t00000000\n\t\tnext_owner_mask\t7fffffff\n\t\tcreat" + "or_id\t13fd9595-a47b-4d64-a5fb-6da645f038e0\n\t\towner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tlast_owner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tgroup_id\t00000000-0000-0000-0000-000000000000\n\t}\n\tlocal_id\t217444923\n\ttotal_crc\t235\n\ttype\t2\n\ttask_valid\t2\n\ttravel_access\t13\n\tdisplayopts\t2\n\tdisplaytype\tv\n\tpos\t-0.120029509\t-0.00284469454\t-0.0302077383\n\toldpos\t150.710999\t25.8584995\t8.19172001\n\trotation\t0.145459949970245361328125\t-0.1646589934825897216796875\t0.659558117389678955078125\t-0.718826770782470703125\n\tchildpos\t0\t-0.182999998\t-0.26699999\n\tchildrot\t0.991444766521453857421875\t3.271923924330621957778931e-05\t-0.0002416197530692443251609802\t0.1305266767740249633789062\n\tscale\t0.0382982\t0.205957\t0.368276\n\tsit_offset\t0\t0\t0\n\tcamera_eye_offset\t0\t0\t0\n\tcamera_at_offset\t0\t0\t0\n\tsit_quat\t0\t0\t0\t1\n\tsit_hint\t0\n\tstate\t160\n\tmaterial\t3\n\tsoundid\t00000000-0000-0000-0000-000000000000\n\tsoundgain\t0\n\tsoundra" + "dius\t0\n\tsoundflags\t0\n\ttextcolor\t0 0 0 1\n\tselected\t0\n\tselector\t00000000-0000-0000-0000-000000000000\n\tusephysics\t0\n\trotate_x\t1\n\trotate_y\t1\n\trotate_z\t1\n\tphantom\t0\n\tremote_script_access_pin\t0\n\tvolume_detect\t0\n\tblock_grabs\t0\n\tdie_at_edge\t0\n\treturn_at_edge\t0\n\ttemporary\t0\n\tsandbox\t0\n\tsandboxhome\t0\t0\t0\n\tshape 0\n\t{\n\t\tpath 0\n\t\t{\n\t\t\tcurve\t32\n\t\t\tbegin\t0.3\n\t\t\tend\t0.65\n\t\t\tscale_x\t1\n\t\t\tscale_y\t0.05\n\t\t\tshear_x\t0\n\t\t\tshear_y\t0\n\t\t\ttwist\t0\n\t\t\ttwist_begin\t0\n\t\t\tradius_offset\t0\n\t\t\ttaper_x\t0\n\t\t\ttaper_y\t0\n\t\t\trevolutions\t1\n\t\t\tskew\t0\n\t\t}\n\t\tprofile 0\n\t\t{\n\t\t\tcurve\t0\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\thollow\t0\n\t\t}\n\t}\n\tfaces\t3\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0" + "\n\t}\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\tps_next_crc\t1\n\tgpw_bias\t1\n\tip\t0\n\tcomplete\tTRUE\n\tdelay\t50000\n\tnextstart\t0\n\tbirthtime\t1061087534454174\n\treztime\t1094866329021741\n\tparceltime\t1133568981982889\n\ttax_rate\t1.00326\n\tscratchpad\t0\n\t{\n\t\n\t}\n\tsale_info\t0\n\t{\n\t\tsale_type\tnot\n\t\tsale_price\t10\n\t}\n\tcorrect_family_id\t00000000-0000-0000-0000-000000000000\n\thas_rezzed\t0\n\tpre_link_base_mask\t7fffffff\n\tlinked \tchild\n\tdefault_pay_price\t-2\t1\t5\t10\t20\n}\n{'task_id':ue4b19200-9d33-962f-c8c5-6f" + "25be3a3fd0}\n{\n\tname\tApotheosis_Immolaine_tail|\n\tpermissions 0\n\t{\n\t\tbase_mask\t7fffffff\n\t\towner_mask\t7fffffff\n\t\tgroup_mask\t00000000\n\t\teveryone_mask\t00000000\n\t\tnext_owner_mask\t7fffffff\n\t\tcreator_id\t13fd9595-a47b-4d64-a5fb-6da645f038e0\n\t\towner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tlast_owner_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\t\tgroup_id\t00000000-0000-0000-0000-000000000000\n\t}\n\tlocal_id\t217444924\n\ttotal_crc\t675\n\ttype\t1\n\ttask_valid\t2\n\ttravel_access\t13\n\tdisplayopts\t2\n\tdisplaytype\tv\n\tpos\t-0.34780401\t-0.00968400016\t-0.260098994\n\toldpos\t0\t0\t0\n\trotation\t0.73164522647857666015625\t-0.67541944980621337890625\t-0.07733880728483200073242188\t0.05022468417882919311523438\n\tvelocity\t0\t0\t0\n\tangvel\t0\t0\t0\n\tscale\t0.0382982\t0.32228\t0.383834\n\tsit_offset\t0\t0\t0\n\tcamera_eye_offset\t0\t0\t0\n\tcamera_at_offset\t0\t0\t0\n\tsit_quat\t0\t0\t0\t1\n\tsit_hint\t0\n\tstate\t160\n\tmaterial\t3\n\tsoundid\t00000" + "000-0000-0000-0000-000000000000\n\tsoundgain\t0\n\tsoundradius\t0\n\tsoundflags\t0\n\ttextcolor\t0 0 0 1\n\tselected\t0\n\tselector\t00000000-0000-0000-0000-000000000000\n\tusephysics\t0\n\trotate_x\t1\n\trotate_y\t1\n\trotate_z\t1\n\tphantom\t0\n\tremote_script_access_pin\t0\n\tvolume_detect\t0\n\tblock_grabs\t0\n\tdie_at_edge\t0\n\treturn_at_edge\t0\n\ttemporary\t0\n\tsandbox\t0\n\tsandboxhome\t0\t0\t0\n\tshape 0\n\t{\n\t\tpath 0\n\t\t{\n\t\t\tcurve\t32\n\t\t\tbegin\t0.3\n\t\t\tend\t0.65\n\t\t\tscale_x\t1\n\t\t\tscale_y\t0.05\n\t\t\tshear_x\t0\n\t\t\tshear_y\t0\n\t\t\ttwist\t0\n\t\t\ttwist_begin\t0\n\t\t\tradius_offset\t0\n\t\t\ttaper_x\t0\n\t\t\ttaper_y\t0\n\t\t\trevolutions\t1\n\t\t\tskew\t0\n\t\t}\n\t\tprofile 0\n\t\t{\n\t\t\tcurve\t0\n\t\t\tbegin\t0\n\t\t\tend\t1\n\t\t\thollow\t0\n\t\t}\n\t}\n\tfaces\t3\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1" + ".57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\t{\n\t\timageid\te7150bed-3e3e-c698-eb15-d17b178148af\n\t\tcolors\t0.843137 0.156863 0.156863 1\n\t\tscales\t15\n\t\tscalet\t1\n\t\toffsets\t0\n\t\toffsett\t0\n\t\timagerot\t-1.57084\n\t\tbump\t0\n\t\tfullbright\t0\n\t\tmedia_flags\t0\n\t}\n\tps_next_crc\t1\n\tgpw_bias\t1\n\tip\t0\n\tcomplete\tTRUE\n\tdelay\t50000\n\tnextstart\t0\n\tbirthtime\t1061087463950186\n\treztime\t1094866329022555\n\tparceltime\t1133568981984359\n\tdescription\t(No Description)|\n\ttax_rate\t1.01736\n\tnamevalue\tAttachPt U32 RW S 10\n\tnamevalue\tAttachmentOrientation VEC3 RW DS -3.110088, -0.182018, 1.493795\n\tnamevalue\tAttachmentOffset VEC3 RW DS -0.347804, -0.009684, -0.260099\n\tnamevalue\tAttachItemI" + "D STRING RW SV 20f36c3a-b44b-9bc7-87f3-018bfdfc8cda\n\tscratchpad\t0\n\t{\n\t\n\t}\n\tsale_info\t0\n\t{\n\t\tsale_type\tnot\n\t\tsale_price\t10\n\t}\n\torig_asset_id\t8747acbc-d391-1e59-69f1-41d06830e6c0\n\torig_item_id\t20f36c3a-b44b-9bc7-87f3-018bfdfc8cda\n\tfrom_task_id\t3c115e51-04f4-523c-9fa6-98aff1034730\n\tcorrect_family_id\t00000000-0000-0000-0000-000000000000\n\thas_rezzed\t0\n\tpre_link_base_mask\t7fffffff\n\tlinked \tlinked\n\tdefault_pay_price\t-2\t1\t5\t10\t20\n}\n"); + } + + + template<> template<> + void URITestObject::test<18>() + { + LLURI u("secondlife:///app/login?first_name=Testert4&last_name=Tester&web_login_key=test"); + // if secondlife is the scheme, LLURI should parse /app/login as path, with no authority + ensure_equals("scheme", u.scheme(), "secondlife"); + ensure_equals("authority", u.authority(), ""); + ensure_equals("path", u.path(), "/app/login"); + ensure_equals("pathmap", u.pathArray()[0].asString(), "app"); + ensure_equals("pathmap", u.pathArray()[1].asString(), "login"); + ensure_equals("query", u.query(), "first_name=Testert4&last_name=Tester&web_login_key=test"); + ensure_equals("query map element", u.queryMap()["last_name"].asString(), "Tester"); + + u = LLURI("secondlife://Da Boom/128/128/128"); + // if secondlife is the scheme, LLURI should parse /128/128/128 as path, with Da Boom as authority + ensure_equals("scheme", u.scheme(), "secondlife"); + ensure_equals("authority", u.authority(), "Da Boom"); + ensure_equals("path", u.path(), "/128/128/128"); + ensure_equals("pathmap", u.pathArray()[0].asString(), "128"); + ensure_equals("pathmap", u.pathArray()[1].asString(), "128"); + ensure_equals("pathmap", u.pathArray()[2].asString(), "128"); + ensure_equals("query", u.query(), ""); + } + + template<> template<> + void URITestObject::test<19>() + { + set_test_name("Parse about: schemes"); + LLURI u("about:blank?redirect-http-hack=secondlife%3A%2F%2F%2Fapp%2Flogin%3Ffirst_name%3DCallum%26last_name%3DLinden%26location%3Dspecify%26grid%3Dvaak%26region%3D%2FMorris%2F128%2F128%26web_login_key%3Defaa4795-c2aa-4c58-8966-763c27931e78"); + ensure_equals("scheme", u.scheme(), "about"); + ensure_equals("authority", u.authority(), ""); + ensure_equals("path", u.path(), "blank"); + ensure_equals("pathmap", u.pathArray()[0].asString(), "blank"); + ensure_equals("query", u.query(), "redirect-http-hack=secondlife:///app/login?first_name=Callum&last_name=Linden&location=specify&grid=vaak®ion=/Morris/128/128&web_login_key=efaa4795-c2aa-4c58-8966-763c27931e78"); + ensure_equals("query map element", u.queryMap()["redirect-http-hack"].asString(), "secondlife:///app/login?first_name=Callum&last_name=Linden&location=specify&grid=vaak®ion=/Morris/128/128&web_login_key=efaa4795-c2aa-4c58-8966-763c27931e78"); + } + + template<> template<> + void URITestObject::test<20>() + { set_test_name("escapePathAndData uri test"); // Basics scheme:[//authority]path[?query][#fragment] diff --git a/indra/llcommon/tests/stringize_test.cpp b/indra/llcommon/tests/stringize_test.cpp index 2a4ed44a67..a3ce7f8e3d 100644 --- a/indra/llcommon/tests/stringize_test.cpp +++ b/indra/llcommon/tests/stringize_test.cpp @@ -3,25 +3,25 @@ * @author Nat Goodspeed * @date 2008-09-12 * @brief Test of stringize.h - * + * * $LicenseInfo:firstyear=2008&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -78,7 +78,7 @@ namespace tut float f; double d; std::string abc; - std::wstring def; + std::wstring def; LLSD llsd; }; typedef test_group<stringize_data> stringize_group; diff --git a/indra/llcommon/tests/threadsafeschedule_test.cpp b/indra/llcommon/tests/threadsafeschedule_test.cpp index 8851590189..f2f17dd2e6 100644 --- a/indra/llcommon/tests/threadsafeschedule_test.cpp +++ b/indra/llcommon/tests/threadsafeschedule_test.cpp @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2021-10-04 * @brief Test for threadsafeschedule. - * + * * $LicenseInfo:firstyear=2021&license=viewerlgpl$ * Copyright (c) 2021, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/tests/tuple_test.cpp b/indra/llcommon/tests/tuple_test.cpp index af94e2086c..aff129753f 100644 --- a/indra/llcommon/tests/tuple_test.cpp +++ b/indra/llcommon/tests/tuple_test.cpp @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2021-10-04 * @brief Test for tuple. - * + * * $LicenseInfo:firstyear=2021&license=viewerlgpl$ * Copyright (c) 2021, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/tests/workqueue_test.cpp b/indra/llcommon/tests/workqueue_test.cpp index df16f4a46e..209b6b868c 100644 --- a/indra/llcommon/tests/workqueue_test.cpp +++ b/indra/llcommon/tests/workqueue_test.cpp @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2021-10-07 * @brief Test for workqueue. - * + * * $LicenseInfo:firstyear=2021&license=viewerlgpl$ * Copyright (c) 2021, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/tests/wrapllerrs.h b/indra/llcommon/tests/wrapllerrs.h index 6978c296b3..9cd2c69b11 100644 --- a/indra/llcommon/tests/wrapllerrs.h +++ b/indra/llcommon/tests/wrapllerrs.h @@ -3,25 +3,25 @@ * @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=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -117,9 +117,9 @@ class CaptureLogRecorder : public LLError::Recorder, public boost::noncopyable { public: CaptureLogRecorder() - : LLError::Recorder(), - boost::noncopyable(), - mMessages() + : LLError::Recorder(), + boost::noncopyable(), + mMessages() { } diff --git a/indra/llcommon/threadpool.h b/indra/llcommon/threadpool.h index 74056aea17..0eb1891754 100644 --- a/indra/llcommon/threadpool.h +++ b/indra/llcommon/threadpool.h @@ -4,7 +4,7 @@ * @date 2021-10-21 * @brief ThreadPool configures a WorkQueue along with a pool of threads to * service it. - * + * * $LicenseInfo:firstyear=2021&license=viewerlgpl$ * Copyright (c) 2021, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/threadpool_fwd.h b/indra/llcommon/threadpool_fwd.h index 1aa3c4a0e2..50e212ef05 100644 --- a/indra/llcommon/threadpool_fwd.h +++ b/indra/llcommon/threadpool_fwd.h @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2022-12-09 * @brief Forward declarations for ThreadPool et al. - * + * * $LicenseInfo:firstyear=2022&license=viewerlgpl$ * Copyright (c) 2022, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/threadsafeschedule.h b/indra/llcommon/threadsafeschedule.h index 92bc7da940..0c7f583819 100644 --- a/indra/llcommon/threadsafeschedule.h +++ b/indra/llcommon/threadsafeschedule.h @@ -4,7 +4,7 @@ * @date 2021-10-02 * @brief ThreadSafeSchedule is an ordered queue in which every item has an * associated timestamp. - * + * * $LicenseInfo:firstyear=2021&license=viewerlgpl$ * Copyright (c) 2021, Linden Research, Inc. * $/LicenseInfo$ @@ -18,7 +18,7 @@ #include "llthreadsafequeue.h" #include "tuple.h" #include <chrono> -#include <tuple> +#include <tuple> namespace LL { diff --git a/indra/llcommon/timer.h b/indra/llcommon/timer.h index 82d19c2d32..aaa0cf0775 100644 --- a/indra/llcommon/timer.h +++ b/indra/llcommon/timer.h @@ -1,25 +1,25 @@ -/** +/** * @file timer.h * @brief Legacy wrapper header. * * $LicenseInfo:firstyear=2000&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llcommon/tuple.h b/indra/llcommon/tuple.h index bfe7e3c2ba..21337650ef 100644 --- a/indra/llcommon/tuple.h +++ b/indra/llcommon/tuple.h @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2021-10-04 * @brief A couple tuple utilities - * + * * $LicenseInfo:firstyear=2021&license=viewerlgpl$ * Copyright (c) 2021, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/u64.cpp b/indra/llcommon/u64.cpp index 02c2c15d26..1d39d3c3e5 100644 --- a/indra/llcommon/u64.cpp +++ b/indra/llcommon/u64.cpp @@ -1,25 +1,25 @@ -/** +/** * @file u64.cpp * @brief Utilities to deal with U64s. * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -31,75 +31,75 @@ U64 str_to_U64(const std::string& str) { - U64 result = 0; - const char *aptr = strpbrk(str.c_str(),"0123456789"); + U64 result = 0; + const char *aptr = strpbrk(str.c_str(),"0123456789"); - if (!aptr) - { - LL_WARNS() << "str_to_U64: Bad string to U64 conversion attempt: format\n" << LL_ENDL; - } - else - { - while ((*aptr >= '0') && (*aptr <= '9')) - { - result = result*10 + (*aptr++ - '0'); - } - } - return (result); + if (!aptr) + { + LL_WARNS() << "str_to_U64: Bad string to U64 conversion attempt: format\n" << LL_ENDL; + } + else + { + while ((*aptr >= '0') && (*aptr <= '9')) + { + result = result*10 + (*aptr++ - '0'); + } + } + return (result); } -std::string U64_to_str(U64 value) +std::string U64_to_str(U64 value) { - std::string res; - U32 part1,part2,part3; - - part3 = (U32)(value % (U64)10000000); - - value /= 10000000; - part2 = (U32)(value % (U64)10000000); - - value /= 10000000; - part1 = (U32)(value % (U64)10000000); - - // three cases to avoid leading zeroes unless necessary - - if (part1) - { - res = llformat("%u%07u%07u",part1,part2,part3); - } - else if (part2) - { - res = llformat("%u%07u",part2,part3); - } - else - { - res = llformat("%u",part3); - } - return res; -} + std::string res; + U32 part1,part2,part3; + + part3 = (U32)(value % (U64)10000000); + + value /= 10000000; + part2 = (U32)(value % (U64)10000000); + + value /= 10000000; + part1 = (U32)(value % (U64)10000000); + + // three cases to avoid leading zeroes unless necessary + + if (part1) + { + res = llformat("%u%07u%07u",part1,part2,part3); + } + else if (part2) + { + res = llformat("%u%07u",part2,part3); + } + else + { + res = llformat("%u",part3); + } + return res; +} -char* U64_to_str(U64 value, char* result, S32 result_size) +char* U64_to_str(U64 value, char* result, S32 result_size) { - std::string res = U64_to_str(value); - LLStringUtil::copy(result, res.c_str(), result_size); - return result; + std::string res = U64_to_str(value); + LLStringUtil::copy(result, res.c_str(), result_size); + return result; } F64 U64_to_F64(const U64 value) { - S64 top_bits = (S64)(value >> 1); - F64 result = (F64)top_bits; - result *= 2.f; - result += (U32)(value & 0x01); - return result; + S64 top_bits = (S64)(value >> 1); + F64 result = (F64)top_bits; + result *= 2.f; + result += (U32)(value & 0x01); + return result; } -U64 llstrtou64(const char* str, char** end, S32 base) +U64 llstrtou64(const char* str, char** end, S32 base) { #ifdef LL_WINDOWS - return _strtoui64(str,end,base); + return _strtoui64(str,end,base); #else - return strtoull(str,end,base); + return strtoull(str,end,base); #endif } diff --git a/indra/llcommon/u64.h b/indra/llcommon/u64.h index 75c8a59136..d70477feb0 100644 --- a/indra/llcommon/u64.h +++ b/indra/llcommon/u64.h @@ -1,25 +1,25 @@ -/** +/** * @file u64.h * @brief Utilities to deal with U64s. * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. - * + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. - * + * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * + * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ diff --git a/indra/llcommon/workqueue.cpp b/indra/llcommon/workqueue.cpp index cf80ce0656..6066e74fb5 100644 --- a/indra/llcommon/workqueue.cpp +++ b/indra/llcommon/workqueue.cpp @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2021-10-06 * @brief Implementation for WorkQueue. - * + * * $LicenseInfo:firstyear=2021&license=viewerlgpl$ * Copyright (c) 2021, Linden Research, Inc. * $/LicenseInfo$ diff --git a/indra/llcommon/workqueue.h b/indra/llcommon/workqueue.h index ec0700a718..9d7bbfbf7a 100644 --- a/indra/llcommon/workqueue.h +++ b/indra/llcommon/workqueue.h @@ -3,7 +3,7 @@ * @author Nat Goodspeed * @date 2021-09-30 * @brief Queue used for inter-thread work passing. - * + * * $LicenseInfo:firstyear=2021&license=viewerlgpl$ * Copyright (c) 2021, Linden Research, Inc. * $/LicenseInfo$ |