summaryrefslogtreecommitdiff
path: root/indra/llcommon
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llcommon')
-rw-r--r--indra/llcommon/CMakeLists.txt51
-rw-r--r--indra/llcommon/StackWalker.cpp4
-rw-r--r--indra/llcommon/classic_callback.h4
-rw-r--r--indra/llcommon/ctype_workaround.h54
-rw-r--r--indra/llcommon/fix_macros.h21
-rw-r--r--indra/llcommon/fsyspath.h4
-rw-r--r--[-rwxr-xr-x]indra/llcommon/hexdump.h0
-rw-r--r--indra/llcommon/llapp.cpp55
-rw-r--r--indra/llcommon/llapr.cpp222
-rw-r--r--indra/llcommon/llapr.h19
-rw-r--r--indra/llcommon/llatomic.cpp2
-rw-r--r--indra/llcommon/llbase32.cpp238
-rw-r--r--indra/llcommon/llbase32.h37
-rw-r--r--indra/llcommon/llcallbacklist.cpp2
-rw-r--r--indra/llcommon/llcommon.cpp54
-rw-r--r--indra/llcommon/llcond.h2
-rw-r--r--indra/llcommon/llcoros.h2
-rw-r--r--indra/llcommon/llcrc.cpp2
-rw-r--r--indra/llcommon/llcrc.h2
-rw-r--r--indra/llcommon/lldate.cpp4
-rw-r--r--indra/llcommon/lldeadmantimer.cpp1
-rw-r--r--indra/llcommon/lldefs.h55
-rw-r--r--indra/llcommon/llerror.cpp61
-rw-r--r--indra/llcommon/llerror.h7
-rw-r--r--indra/llcommon/llfasttimer.cpp11
-rw-r--r--indra/llcommon/llfile.cpp1117
-rw-r--r--indra/llcommon/llfile.h811
-rw-r--r--indra/llcommon/llfindlocale.cpp4
-rw-r--r--indra/llcommon/llfixedbuffer.h2
-rw-r--r--indra/llcommon/llinstancetracker.h6
-rw-r--r--indra/llcommon/llkeybind.cpp9
-rw-r--r--indra/llcommon/llkeybind.h1
-rw-r--r--indra/llcommon/llmd5.cpp6
-rw-r--r--indra/llcommon/llmemory.cpp3
-rw-r--r--indra/llcommon/llmemory.h46
-rw-r--r--indra/llcommon/llmutex.h2
-rw-r--r--indra/llcommon/llpreprocessor.h50
-rw-r--r--indra/llcommon/llprocessor.cpp69
-rw-r--r--indra/llcommon/llprocinfo.cpp1
-rw-r--r--indra/llcommon/llprofiler.cpp2
-rw-r--r--indra/llcommon/llprofiler.h9
-rw-r--r--indra/llcommon/llprofilercategories.h9
-rw-r--r--indra/llcommon/llrand.cpp25
-rw-r--r--indra/llcommon/llsd.cpp5
-rw-r--r--indra/llcommon/llsdjson.cpp2
-rw-r--r--indra/llcommon/llsdserialize.cpp5
-rw-r--r--indra/llcommon/llsdutil.cpp4
-rw-r--r--indra/llcommon/llsingleton.h2
-rw-r--r--indra/llcommon/llstl.h10
-rw-r--r--indra/llcommon/llstreamqueue.cpp24
-rw-r--r--indra/llcommon/llstreamqueue.h240
-rw-r--r--indra/llcommon/llsys.cpp136
-rw-r--r--indra/llcommon/llthread.cpp46
-rw-r--r--indra/llcommon/llthread.h8
-rw-r--r--indra/llcommon/llthreadsafequeue.h2
-rw-r--r--indra/llcommon/lltracerecording.cpp9
-rw-r--r--indra/llcommon/lluuid.cpp46
-rw-r--r--indra/llcommon/lluuid.h2
-rw-r--r--indra/llcommon/lockstatic.h2
-rw-r--r--indra/llcommon/mutex.h22
-rw-r--r--indra/llcommon/stdtypes.h24
-rw-r--r--indra/llcommon/tests/commonmisc_test.cpp4
-rw-r--r--indra/llcommon/tests/llallocator_heap_profile_test.cpp144
-rw-r--r--indra/llcommon/tests/llallocator_test.cpp80
-rw-r--r--indra/llcommon/tests/llapp_test.cpp162
-rw-r--r--indra/llcommon/tests/lldependencies_test.cpp12
-rw-r--r--indra/llcommon/tests/lldoubledispatch_test.cpp240
-rw-r--r--indra/llcommon/tests/llerror_test.cpp4
-rw-r--r--indra/llcommon/tests/llevents_test.cpp506
-rw-r--r--indra/llcommon/tests/llfile_test.cpp299
-rw-r--r--indra/llcommon/tests/llhttpdate_test.cpp162
-rw-r--r--indra/llcommon/tests/lllazy_test.cpp239
-rw-r--r--indra/llcommon/tests/llleap_test.cpp20
-rw-r--r--indra/llcommon/tests/llmemtype_test.cpp117
-rw-r--r--indra/llcommon/tests/llprocess_test.cpp56
-rw-r--r--indra/llcommon/tests/llsd_test.cpp830
-rw-r--r--indra/llcommon/tests/llsdserialize_test.cpp18
-rw-r--r--indra/llcommon/tests/llsdutil_test.cpp434
-rw-r--r--indra/llcommon/tests/llstreamqueue_test.cpp194
-rw-r--r--indra/llcommon/tests/llstreamtools_test.cpp883
-rw-r--r--indra/llcommon/tests/llstring_test.cpp4
-rw-r--r--indra/llcommon/threadpool.cpp1
-rw-r--r--indra/llcommon/timer.h26
83 files changed, 5522 insertions, 2588 deletions
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt
index 32226a62e6..bff3c66924 100644
--- a/indra/llcommon/CMakeLists.txt
+++ b/indra/llcommon/CMakeLists.txt
@@ -22,7 +22,6 @@ set(llcommon_SOURCE_FILES
llapr.cpp
llassettype.cpp
llatomic.cpp
- llbase32.cpp
llbase64.cpp
llbitpack.cpp
llcallbacklist.cpp
@@ -69,7 +68,7 @@ set(llcommon_SOURCE_FILES
llmortician.cpp
llmutex.cpp
llpointer.cpp
- llptrto.cpp
+ llptrto.cpp
llpredicate.cpp
llprocess.cpp
llprocessor.cpp
@@ -86,7 +85,6 @@ set(llcommon_SOURCE_FILES
llsdutil.cpp
llsingleton.cpp
llstacktrace.cpp
- llstreamqueue.cpp
llstreamtools.cpp
llstring.cpp
llstringtable.cpp
@@ -110,7 +108,7 @@ set(llcommon_SOURCE_FILES
workqueue.cpp
StackWalker.cpp
)
-
+
set(llcommon_HEADER_FILES
CMakeLists.txt
@@ -119,8 +117,6 @@ set(llcommon_HEADER_FILES
chrono.h
classic_callback.h
commoncontrol.h
- ctype_workaround.h
- fix_macros.h
fsyspath.h
function_types.h
indra_constants.h
@@ -131,7 +127,6 @@ set(llcommon_HEADER_FILES
llapr.h
llassettype.h
llatomic.h
- llbase32.h
llbase64.h
llbitpack.h
llboost.h
@@ -219,7 +214,6 @@ set(llcommon_HEADER_FILES
llsingleton.h
llstacktrace.h
llstl.h
- llstreamqueue.h
llstreamtools.h
llstrider.h
llstring.h
@@ -252,20 +246,33 @@ set(llcommon_HEADER_FILES
threadpool.h
threadpool_fwd.h
threadsafeschedule.h
- timer.h
tuple.h
u64.h
workqueue.h
StackWalker.h
)
-
+
if (DARWIN)
list(APPEND llcommon_HEADER_FILES llsys_objc.h)
list(APPEND llcommon_SOURCE_FILES llsys_objc.mm)
+
+ set_source_files_properties(
+ llsys_objc.mm
+ PROPERTIES
+ SKIP_PRECOMPILE_HEADERS TRUE
+ )
endif (DARWIN)
if (USE_TRACY)
list(APPEND llcommon_SOURCE_FILES llprofiler.cpp)
+
+ # Skip precompiled headers on these files due to special compilation needs
+ set_source_files_properties(
+ llfasttimer.cpp
+ llprofiler.cpp
+ PROPERTIES
+ SKIP_PRECOMPILE_HEADERS TRUE
+ )
endif ()
list(APPEND llcommon_SOURCE_FILES ${llcommon_HEADER_FILES})
@@ -283,8 +290,12 @@ target_link_libraries(
ll::sse2neon
)
-target_include_directories(llcommon INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
-target_include_directories(llcommon PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
+target_include_directories(llcommon PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
+target_include_directories(llcommon PRIVATE ${CMAKE_SOURCE_DIR}/llmath)
+
+if (USE_PRECOMPILED_HEADERS)
+ target_precompile_headers(llcommon REUSE_FROM llprecompiled)
+endif ()
add_dependencies(llcommon stage_third_party_libs)
@@ -302,16 +313,21 @@ if (LL_TESTS)
LL_ADD_INTEGRATION_TEST(classic_callback "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(commonmisc "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lazyeventapi "" "${test_libs}")
+ LL_ADD_INTEGRATION_TEST(llapp "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llbase64 "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llcond "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lldate "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lldeadmantimer "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lldependencies "" "${test_libs}")
+ LL_ADD_INTEGRATION_TEST(lldoubledispatch "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llerror "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lleventcoro "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lleventdispatcher "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lleventfilter "" "${test_libs}")
+ LL_ADD_INTEGRATION_TEST(llevents "" "${test_libs}")
+ LL_ADD_INTEGRATION_TEST(llfile "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llframetimer "" "${test_libs}")
+ LL_ADD_INTEGRATION_TEST(llhttpdate "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llheteromap "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llinstancetracker "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llleap "" "${test_libs}")
@@ -321,9 +337,11 @@ if (LL_TESTS)
LL_ADD_INTEGRATION_TEST(llprocessor "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llprocinfo "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llrand "" "${test_libs}")
+ LL_ADD_INTEGRATION_TEST(llsd "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llsdserialize "" "${test_libs}")
+ LL_ADD_INTEGRATION_TEST(llsdutil "" "llcommon;llmath")
LL_ADD_INTEGRATION_TEST(llsingleton "" "${test_libs}")
- LL_ADD_INTEGRATION_TEST(llstreamqueue "" "${test_libs}")
+ LL_ADD_INTEGRATION_TEST(llstreamtools "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llstring "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lltrace "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lltreeiterators "" "${test_libs}")
@@ -339,4 +357,11 @@ if (LL_TESTS)
## throwing and catching exceptions.
##LL_ADD_INTEGRATION_TEST(llexception "" "${test_libs}")
+ # Skip precompiled header for tests that need special handling
+ set_source_files_properties(
+ tests/llsd_test.cpp
+ PROPERTIES
+ SKIP_PRECOMPILE_HEADERS TRUE
+ )
+
endif (LL_TESTS)
diff --git a/indra/llcommon/StackWalker.cpp b/indra/llcommon/StackWalker.cpp
index e9ae1723fb..a281fe83f6 100644
--- a/indra/llcommon/StackWalker.cpp
+++ b/indra/llcommon/StackWalker.cpp
@@ -785,6 +785,8 @@ private:
case 8: //SymVirtual:
szSymType = "Virtual";
break;
+ case NumSymTypes:
+ break;
}
}
LPCSTR pdbName = Module.LoadedImageName;
@@ -902,7 +904,7 @@ bool StackWalker::LoadModules()
SetLastError(ERROR_DLL_INIT_FAILED);
return false;
}
- if (m_modulesLoaded != FALSE)
+ if (m_modulesLoaded)
return true;
// Build the sym-path:
diff --git a/indra/llcommon/classic_callback.h b/indra/llcommon/classic_callback.h
index 009c25d67c..8a6ac7f7ff 100644
--- a/indra/llcommon/classic_callback.h
+++ b/indra/llcommon/classic_callback.h
@@ -184,8 +184,8 @@ protected:
template <typename SIGNATURE, typename USERDATA=void*, typename CALLABLE=void(*)()>
auto makeClassicCallback(CALLABLE&& callable)
{
- return std::move(ClassicCallback<SIGNATURE, USERDATA, CALLABLE>
- (std::forward<CALLABLE>(callable)));
+ return ClassicCallback<SIGNATURE, USERDATA, CALLABLE>
+ (std::forward<CALLABLE>(callable));
}
/*****************************************************************************
diff --git a/indra/llcommon/ctype_workaround.h b/indra/llcommon/ctype_workaround.h
deleted file mode 100644
index 89a47fe3db..0000000000
--- a/indra/llcommon/ctype_workaround.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/**
- * @file ctype_workaround.h
- * @brief The workaround is to create some legacy symbols that point
- * to the correct symbols, which avoids link errors.
- *
- * $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 _CTYPE_WORKAROUND_H_
-#define _CTYPE_WORKAROUND_H_
-
-/**
- * the CTYPE_WORKAROUND is needed for linux dev stations that don't
- * have the broken libc6 packages needed by our out-of-date static
- * libs (such as libcrypto and libcurl).
- *
- * -- Leviathan 20060113
-*/
-
-#include <cctype>
-
-__const unsigned short int *__ctype_b;
-__const __int32_t *__ctype_tolower;
-__const __int32_t *__ctype_toupper;
-
-// 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());
-}
-
-#endif
-
diff --git a/indra/llcommon/fix_macros.h b/indra/llcommon/fix_macros.h
deleted file mode 100644
index ed6c26a371..0000000000
--- a/indra/llcommon/fix_macros.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/**
- * @file fix_macros.h
- * @author Nat Goodspeed
- * @date 2012-11-16
- * @brief The Mac system headers seem to #define macros with obnoxiously
- * 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$
- */
-
-// DON'T use an #include guard: every time we encounter this header, #undef
-// these macros all over again.
-
-// who injects MACROS with such generic names?! Grr.
-#ifdef check
-#undef check
-#endif
diff --git a/indra/llcommon/fsyspath.h b/indra/llcommon/fsyspath.h
index 2c900c02a7..237d99ae86 100644
--- a/indra/llcommon/fsyspath.h
+++ b/indra/llcommon/fsyspath.h
@@ -57,7 +57,7 @@ class fsyspath: public std::filesystem::path
public:
// default
- fsyspath() {}
+ fsyspath() = default;
// construct from UTF-8 encoded string
fsyspath(const std::string& path): fsyspath(std::string_view(path)) {}
fsyspath(const char* path): fsyspath(std::string_view(path)) {}
@@ -66,8 +66,10 @@ public:
{}
// construct from existing path
fsyspath(const super& path): super(path) {}
+ fsyspath(super&& path) : super(std::move(path)) {}
fsyspath& operator=(const super& p) { super::operator=(p); return *this; }
+ fsyspath& operator=(super&& p) { super::operator=(std::move(p)); return *this; }
fsyspath& operator=(const std::string& p) { return (*this) = std::string_view(p); }
fsyspath& operator=(const char* p) { return (*this) = std::string_view(p); }
fsyspath& operator=(std::string_view p)
diff --git a/indra/llcommon/hexdump.h b/indra/llcommon/hexdump.h
index ab5ba2b16d..ab5ba2b16d 100755..100644
--- a/indra/llcommon/hexdump.h
+++ b/indra/llcommon/hexdump.h
diff --git a/indra/llcommon/llapp.cpp b/indra/llcommon/llapp.cpp
index f92bb98ba6..02d6da989a 100644
--- a/indra/llcommon/llapp.cpp
+++ b/indra/llcommon/llapp.cpp
@@ -37,7 +37,7 @@
#endif
#include "llcommon.h"
-#include "llapr.h"
+
#include "llerrorcontrol.h"
#include "llframetimer.h"
#include "lllivefile.h"
@@ -53,19 +53,13 @@
//
// Signal handling
#ifndef LL_WINDOWS
+#include "apr_signal.h"
+
# 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;
@@ -721,47 +715,4 @@ void default_unix_signal_handler(int signum, siginfo_t *info, void *)
}
}
-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/llapr.cpp b/indra/llcommon/llapr.cpp
index 06eb4c389f..e1d80c5dea 100644
--- a/indra/llcommon/llapr.cpp
+++ b/indra/llcommon/llapr.cpp
@@ -230,7 +230,6 @@ bool LLVolatileAPRPool::isFull()
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];
apr_strerror(status, buf, sizeof(buf));
@@ -260,7 +259,6 @@ bool _ll_apr_warn_status(apr_status_t status, const char* file, int line)
#else
LL_WARNS("APR") << "APR: " << file << ":" << line << " " << buf << " (0x" << std::hex << status << std::dec << ")" << LL_ENDL;
#endif
-#endif
return true;
}
@@ -552,226 +550,6 @@ S32 LLAPRFile::seek(apr_file_t* file_handle, apr_seek_where_t where, S32 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;
- if (APR_STATUS_IS_ENOSPC(s))
- {
- LLApp::notifyOutOfDiskSpace();
- }
- 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 11e474b5dd..8795a0f2c4 100644
--- a/indra/llcommon/llapr.h
+++ b/indra/llcommon/llapr.h
@@ -35,12 +35,8 @@
#include "llwin32headers.h"
#include "apr_thread_proc.h"
-#include "apr_getopt.h"
-#include "apr_signal.h"
-#include "llstring.h"
-
-#include "mutex.h"
+#include <mutex>
struct apr_dso_handle_t;
/**
@@ -184,20 +180,7 @@ 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/llatomic.cpp b/indra/llcommon/llatomic.cpp
index d200bb0406..cff5e01225 100644
--- a/indra/llcommon/llatomic.cpp
+++ b/indra/llcommon/llatomic.cpp
@@ -23,6 +23,8 @@
* $/LicenseInfo$
*/
+#include "linden_common.h"
+
#include "llatomic.h"
//============================================================================
diff --git a/indra/llcommon/llbase32.cpp b/indra/llcommon/llbase32.cpp
deleted file mode 100644
index 979114fe86..0000000000
--- a/indra/llcommon/llbase32.cpp
+++ /dev/null
@@ -1,238 +0,0 @@
-/**
- * @file llbase32.cpp
- * @brief base32 encoding that returns a std::string
- * @author James Cook
- *
- * Based on code from bitter
- * http://ghostwhitecrab.com/bitter/
- *
- * Some parts of this file are:
- * $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$
- */
-
-/**
- * Copyright (c) 2006 Christian Biere <christianbiere@gmx.de>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. 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.
- * 3. Neither the name of the authors 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 AUTHORS 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 REGENTS 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.
- */
-
-#include "linden_common.h"
-
-#include "llbase32.h"
-
-#include <string>
-
-// bitter - base32.c starts here
-
-/*
- * See RFC 3548 for details about Base 32 encoding:
- * http://www.faqs.org/rfcs/rfc3548.html
- */
-
-static const char base32_alphabet[32] = {
- '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', '2', '3', '4', '5', '6', '7'
-};
-
-size_t
-base32_encode(char *dst, size_t size, const void *data, size_t len)
-{
- size_t i = 0;
- const U8 *p = (const U8*)data;
- const char *end = &dst[size];
- char *q = dst;
-
- do {
- size_t j, k;
- U8 x[5];
- char s[8];
-
- switch (len - i) {
- case 4: k = 7; break;
- case 3: k = 5; break;
- case 2: k = 3; break;
- case 1: k = 2; break;
- default:
- k = 8;
- }
-
- for (j = 0; j < 5; j++)
- x[j] = i < len ? p[i++] : 0;
-
-/*
- +-------+-----------+--------+
- | target| source | source |
- | byte | bits | byte |
- +-------+-----------+--------+
- | 0 | 7 6 5 4 3 | 0 |
- | 1 | 2 1 0 7 6 | 0-1 |
- | 2 | 5 4 3 2 1 | 1 |
- | 3 | 0 7 6 5 4 | 1-2 |
- | 4 | 3 2 1 0 7 | 2-3 |
- | 5 | 6 5 4 3 2 | 3 |
- | 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;
- s[3] = ((x[1] & 0x01) << 4) | (x[2] >> 4);
- s[4] = ((x[2] & 0x0f) << 1) | (x[3] >> 7);
- s[5] = (x[3] >> 2) & 0x1f;
- s[6] = ((x[3] & 0x03) << 3) | (x[4] >> 5);
- s[7] = x[4] & 0x1f;
-
- for (j = 0; j < k && q != end; j++)
- *q++ = base32_alphabet[(U8) s[j]];
-
- } while (i < len);
-
- return q - dst;
-}
-
-/* *TODO: Implement base32 encode.
-
-static inline int
-ascii_toupper(int c)
-{
- return c >= 97 && c <= 122 ? c - 32 : c;
-}
-
-static inline int
-ascii_tolower(int c)
-{
- return c >= 65 && c <= 90 ? c + 32 : c;
-}
-
-
-static char base32_map[(unsigned char) -1];
-
-size_t
-base32_decode(char *dst, size_t size, const void *data, size_t len)
-{
- const char *end = &dst[size];
- const unsigned char *p = data;
- char *q = dst;
- size_t i;
- unsigned max_pad = 3;
-
- 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];
- size_t j;
-
- c = p[i];
- if ('=' == c) {
- max_pad--;
- c = 0;
- } else {
- c = base32_map[c];
- if ((unsigned char) -1 == c) {
- return -1;
- }
- }
-
- j = i % LL_ARRAY_SIZE(s);
- s[j] = c;
-
- if (7 == j) {
- char b[5];
-
- b[0] = ((s[0] << 3) & 0xf8) | ((s[1] >> 2) & 0x07);
- b[1] = ((s[1] & 0x03) << 6) | ((s[2] & 0x1f) << 1) | ((s[3] >> 4) & 1);
- b[2] = ((s[3] & 0x0f) << 4) | ((s[4] >> 1) & 0x0f);
- b[3] = ((s[4] & 1) << 7) | ((s[5] & 0x1f) << 2) | ((s[6] >> 3) & 0x03);
- b[4] = ((s[6] & 0x07) << 5) | (s[7] & 0x1f);
-
- for (j = 0; j < LL_ARRAY_SIZE(b); j++) {
- if (q != end)
- *q = b[j];
- q++;
- }
- }
- }
-
- return q - dst;
-}
-*/
-
-
-// The following is
-// Copyright (c) 2007-$CurrentYear$, Linden Research, Inc.
-// 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;
-}
diff --git a/indra/llcommon/llbase32.h b/indra/llcommon/llbase32.h
deleted file mode 100644
index 8cfbd7ef53..0000000000
--- a/indra/llcommon/llbase32.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/**
- * @file llbase32.h
- * @brief base32 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 LLBASE32_H
-#define LLBASE32_H
-
-class LL_COMMON_API LLBase32
-{
-public:
- static std::string encode(const U8* input, size_t input_size);
-};
-
-#endif
diff --git a/indra/llcommon/llcallbacklist.cpp b/indra/llcommon/llcallbacklist.cpp
index 3d5d30bd90..8fb7823f04 100644
--- a/indra/llcommon/llcallbacklist.cpp
+++ b/indra/llcommon/llcallbacklist.cpp
@@ -24,6 +24,8 @@
* $/LicenseInfo$
*/
+#include "linden_common.h"
+
#include "llcallbacklist.h"
#include "lleventtimer.h"
#include "llerrorlegacy.h"
diff --git a/indra/llcommon/llcommon.cpp b/indra/llcommon/llcommon.cpp
index 7a22eaf203..bf8801c46b 100644
--- a/indra/llcommon/llcommon.cpp
+++ b/indra/llcommon/llcommon.cpp
@@ -47,30 +47,13 @@ void* ll_tracy_new(size_t size)
return ptr;
}
-void* ll_tracy_aligned_new(size_t size, size_t alignment)
-{
- void* ptr = ll_aligned_malloc_fallback(size, alignment);
- if (!ptr)
- {
- throw std::bad_alloc();
- }
- LL_PROFILE_ALLOC(ptr, size);
- return ptr;
-}
-
void ll_tracy_delete(void* ptr)
{
LL_PROFILE_FREE(ptr);
(free)(ptr);
}
-void ll_tracy_aligned_delete(void* ptr)
-{
- LL_PROFILE_FREE(ptr);
- ll_aligned_free_fallback(ptr);
-}
-
-void* operator new(size_t size)
+void* operator new(std::size_t size)
{
return ll_tracy_new(size);
}
@@ -80,24 +63,41 @@ void* operator new[](std::size_t count)
return ll_tracy_new(count);
}
-void* operator new(size_t size, std::align_val_t align)
+void operator delete(void *ptr) noexcept
{
- return ll_tracy_aligned_new(size, (size_t)align);
+ ll_tracy_delete(ptr);
}
-void* operator new[](std::size_t count, std::align_val_t align)
+void operator delete[](void* ptr) noexcept
{
- return ll_tracy_aligned_new(count, (size_t)align);
+ ll_tracy_delete(ptr);
}
-void operator delete(void *ptr) noexcept
+#if !defined(LL_LINUX)
+
+void* ll_tracy_aligned_new(size_t size, size_t alignment)
{
- ll_tracy_delete(ptr);
+ void* ptr = ll_aligned_malloc_fallback(size, alignment);
+ if (!ptr)
+ {
+ throw std::bad_alloc();
+ }
+ return ptr;
}
-void operator delete[](void* ptr) noexcept
+void ll_tracy_aligned_delete(void* ptr)
{
- ll_tracy_delete(ptr);
+ ll_aligned_free_fallback(ptr);
+}
+
+void* operator new(size_t size, std::align_val_t align)
+{
+ return ll_tracy_aligned_new(size, (size_t)align);
+}
+
+void* operator new[](std::size_t count, std::align_val_t align)
+{
+ return ll_tracy_aligned_new(count, (size_t)align);
}
void operator delete(void *ptr, std::align_val_t align) noexcept
@@ -110,6 +110,8 @@ void operator delete[](void* ptr, std::align_val_t align) noexcept
ll_tracy_aligned_delete(ptr);
}
+#endif
+
#endif // TRACY_ENABLE && !LL_PROFILER_ENABLE_TRACY_OPENGL
//static
diff --git a/indra/llcommon/llcond.h b/indra/llcommon/llcond.h
index 2df1719941..b72ea33dea 100644
--- a/indra/llcommon/llcond.h
+++ b/indra/llcommon/llcond.h
@@ -17,7 +17,7 @@
#include "llunits.h"
#include "llcoros.h"
#include LLCOROS_MUTEX_HEADER
-#include "mutex.h"
+#include <mutex>
#include <chrono>
/**
diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h
index 9df52b6ed5..602c65b9ba 100644
--- a/indra/llcommon/llcoros.h
+++ b/indra/llcommon/llcoros.h
@@ -34,10 +34,10 @@
#include <boost/fiber/future/future.hpp>
#include <boost/fiber/future/promise.hpp>
#include <boost/fiber/recursive_mutex.hpp>
-#include "mutex.h"
#include "llsingleton.h"
#include "llinstancetracker.h"
#include <functional>
+#include <mutex>
#include <string>
#include <exception>
#include <queue>
diff --git a/indra/llcommon/llcrc.cpp b/indra/llcommon/llcrc.cpp
index d79d06e2a2..33bf01a29b 100644
--- a/indra/llcommon/llcrc.cpp
+++ b/indra/llcommon/llcrc.cpp
@@ -166,7 +166,7 @@ void LLCRC::update(const std::string& filename)
return;
}
- FILE* fp = LLFile::fopen(filename, "rb"); /* Flawfinder: ignore */
+ FILE* fp = LLFile::fopen(filename, LLFILE_MODE("rb")); /* Flawfinder: ignore */
if (fp)
{
diff --git a/indra/llcommon/llcrc.h b/indra/llcommon/llcrc.h
index a3bde47780..2ea5fbc3b0 100644
--- a/indra/llcommon/llcrc.h
+++ b/indra/llcommon/llcrc.h
@@ -36,7 +36,7 @@
// example (don't try this at work kids):
//
// LLCRC crc;
-// FILE* fp = LLFile::fopen(filename,"rb");
+// FILE* fp = LLFile::fopen(filename,LLFILE_MODE("rb"));
// while(!feof(fp)) {
// crc.update(fgetc(fp));
// }
diff --git a/indra/llcommon/lldate.cpp b/indra/llcommon/lldate.cpp
index 5545bb71af..7f65c0d715 100644
--- a/indra/llcommon/lldate.cpp
+++ b/indra/llcommon/lldate.cpp
@@ -134,11 +134,7 @@ void LLDate::toStream(std::ostream& s) const
}
s << std::dec << std::setfill('0');
-#if( LL_WINDOWS || __GNUC__ > 2)
s << std::right;
-#else
- 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)
diff --git a/indra/llcommon/lldeadmantimer.cpp b/indra/llcommon/lldeadmantimer.cpp
index f9c14d7c24..45c01eb84a 100644
--- a/indra/llcommon/lldeadmantimer.cpp
+++ b/indra/llcommon/lldeadmantimer.cpp
@@ -25,6 +25,7 @@
* $/LicenseInfo$
*/
+#include "linden_common.h"
#include "lldeadmantimer.h"
diff --git a/indra/llcommon/lldefs.h b/indra/llcommon/lldefs.h
index 232987da14..0481e45351 100644
--- a/indra/llcommon/lldefs.h
+++ b/indra/llcommon/lldefs.h
@@ -28,6 +28,7 @@
#define LL_LLDEFS_H
#include "stdtypes.h"
+#include <cassert>
#include <type_traits>
// Often used array indices
@@ -169,6 +170,38 @@ constexpr U32 MAXADDRSTR = 17; // 123.567.901.345 = 15 chars + \0 +
// llclampb(a) // clamps a to [0 .. 255]
//
+// llless(d0, d1) safely compares d0 < d1 even if one is signed and the other
+// is unsigned. A simple (d0 < d1) expression converts the signed operand to
+// unsigned before comparing. If the signed operand is negative, that flips
+// the negative value to a huge positive value, producing the wrong answer!
+// llless() specifically addresses that case.
+template <typename T0, typename T1>
+constexpr bool llless(T0 d0, T1 d1)
+{
+ if constexpr (std::is_signed_v<T0> && ! std::is_signed_v<T1>)
+ {
+ // T0 signed, T1 unsigned: negative d0 is less than any unsigned d1
+ if (d0 < 0)
+ return true;
+ // both are non-negative: explicitly cast to avoid C4018
+ return std::make_unsigned_t<T0>(d0) < d1;
+ }
+ else if constexpr (! std::is_signed_v<T0> && std::is_signed_v<T1>)
+ {
+ // T0 unsigned, T1 signed: any unsigned d0 is greater than negative d1
+ if (d1 < 0)
+ return false;
+ // both are non-negative: explicitly cast to avoid C4018
+ return d0 < std::make_unsigned_t<T1>(d1);
+ }
+ else
+ {
+ // both T0 and T1 are signed, or both are unsigned:
+ // straightforward comparison works
+ return d0 < d1;
+ }
+}
+
// recursion tail
template <typename T>
constexpr auto llmax(T data)
@@ -180,7 +213,7 @@ template <typename T0, typename T1, typename... Ts>
constexpr auto llmax(T0 d0, T1 d1, Ts... rest)
{
auto maxrest = llmax(d1, rest...);
- return (d0 > maxrest)? d0 : maxrest;
+ return llless(maxrest, d0)? d0 : maxrest;
}
// recursion tail
@@ -194,12 +227,28 @@ template <typename T0, typename T1, typename... Ts>
constexpr auto llmin(T0 d0, T1 d1, Ts... rest)
{
auto minrest = llmin(d1, rest...);
- return (d0 < minrest) ? d0 : minrest;
+ return llless(d0, minrest) ? d0 : minrest;
}
template <typename A, typename MIN, typename MAX>
constexpr A llclamp(A a, MIN minval, MAX maxval)
{
+ // The only troublesome case is if A is unsigned and either minval or
+ // maxval is both signed and negative. Casting a negative number to
+ // unsigned flips it to a huge positive number, making this llclamp() call
+ // ineffective.
+ if constexpr (! std::is_signed_v<A>)
+ {
+ if constexpr (std::is_signed_v<MIN>)
+ {
+ assert(minval >= 0);
+ }
+ if constexpr (std::is_signed_v<MAX>)
+ {
+ assert(maxval >= 0);
+ }
+ }
+
A aminval{ static_cast<A>(minval) }, amaxval{ static_cast<A>(maxval) };
if ( a < aminval )
{
@@ -225,7 +274,7 @@ constexpr LLDATATYPE llclampb(LLDATATYPE a)
}
template <class LLDATATYPE>
-inline void llswap(LLDATATYPE& lhs, LLDATATYPE& rhs)
+constexpr void llswap(LLDATATYPE& lhs, LLDATATYPE& rhs)
{
std::swap(lhs, rhs);
}
diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp
index 6f5e57c3de..78101e21af 100644
--- a/indra/llcommon/llerror.cpp
+++ b/indra/llcommon/llerror.cpp
@@ -133,6 +133,7 @@ namespace {
RecordToFile(const std::string& filename):
mName(filename)
{
+ showMultiline(true);
mFile.open(filename.c_str(), std::ios_base::out | std::ios_base::app);
if (!mFile)
{
@@ -344,6 +345,51 @@ namespace {
}
};
#endif
+
+#if LL_PROFILER_CONFIGURATION >= LL_PROFILER_CONFIG_TRACY
+ class RecordToTracy : public LLError::Recorder
+ {
+ public:
+ RecordToTracy()
+ {
+ this->showMultiline(true);
+ this->showTags(false);
+ this->showLocation(false);
+ }
+
+ virtual bool enabled() override { return LLError::getEnabledLogTypesMask() & 0x12; }
+
+ virtual void recordMessage(LLError::ELevel level, const std::string& message) override
+ {
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_LOGGING;
+ switch (level)
+ {
+ case LLError::LEVEL_DEBUG:
+ {
+ TracyMessageC(message.c_str(), message.size(), tracy::Color::Turquoise);
+ break;
+ }
+ default:
+ case LLError::LEVEL_NONE:
+ case LLError::LEVEL_INFO:
+ {
+ TracyMessageC(message.c_str(), message.size(), tracy::Color::White);
+ break;
+ }
+ case LLError::LEVEL_WARN:
+ {
+ TracyMessageC(message.c_str(), message.size(), tracy::Color::Yellow);
+ break;
+ }
+ case LLError::LEVEL_ERROR:
+ {
+ TracyMessageC(message.c_str(), message.size(), tracy::Color::Red);
+ break;
+ }
+ }
+ }
+ };
+#endif
}
@@ -435,13 +481,9 @@ namespace
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";
+ if (!LLFile::isfile(file))
+ {
+ file = app_dir + "/logcontrol.xml";
}
return * new LogControlFile(file);
// NB: This instance is never freed
@@ -759,6 +801,11 @@ namespace
LLError::addRecorder(recordToWinDebug);
#endif
+#if LL_PROFILER_CONFIGURATION >= LL_PROFILER_CONFIG_TRACY
+ LLError::RecorderPtr recordToTracy(new RecordToTracy());
+ LLError::addRecorder(recordToTracy);
+#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
diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h
index 0083865cf7..a06c299c8d 100644
--- a/indra/llcommon/llerror.h
+++ b/indra/llcommon/llerror.h
@@ -91,13 +91,8 @@ const int LL_ERR_NOERR = 0;
#define llverify(func) do {if (func) {}} while(0)
#endif
-#ifdef LL_WINDOWS
#define LL_STATIC_ASSERT(func, msg) static_assert(func, msg)
-#define LL_BAD_TEMPLATE_INSTANTIATION(type, msg) static_assert(false, msg)
-#else
-#define LL_STATIC_ASSERT(func, msg) BOOST_STATIC_ASSERT(func)
-#define LL_BAD_TEMPLATE_INSTANTIATION(type, msg) BOOST_STATIC_ASSERT(sizeof(type) != 0 && false);
-#endif
+#define LL_BAD_TEMPLATE_INSTANTIATION(type, msg) static_assert(sizeof(type) != 0 && false, msg)
/** Error Logging Facility
diff --git a/indra/llcommon/llfasttimer.cpp b/indra/llcommon/llfasttimer.cpp
index 1c5fe9d2f5..c5a7a0e56b 100644
--- a/indra/llcommon/llfasttimer.cpp
+++ b/indra/llcommon/llfasttimer.cpp
@@ -387,6 +387,11 @@ void BlockTimer::logStats()
}
+#if defined(LL_GNUC) && GCC_VERSION >= 130000
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wnonnull"
+#endif
+
//static
void BlockTimer::dumpCurTimes()
{
@@ -418,8 +423,12 @@ void BlockTimer::dumpCurTimes()
<< num_calls << " calls";
LL_INFOS() << out_str.str() << LL_ENDL;
+ }
}
-}
+
+#if defined(LL_GNUC) && GCC_VERSION >= 130000
+# pragma GCC diagnostic push
+#endif
//static
void BlockTimer::writeLog(std::ostream& os)
diff --git a/indra/llcommon/llfile.cpp b/indra/llcommon/llfile.cpp
index f752e31563..114df29f6e 100644
--- a/indra/llcommon/llfile.cpp
+++ b/indra/llcommon/llfile.cpp
@@ -29,22 +29,17 @@
#include "linden_common.h"
#include "llfile.h"
-#include "llstring.h"
#include "llerror.h"
#include "stringize.h"
#if LL_WINDOWS
-#include "llwin32headers.h"
-#include <vector>
+#include <fcntl.h>
#else
#include <errno.h>
+#include <sys/file.h>
#endif
-using namespace std;
-
-static std::string empty;
-
-// Many of the methods below use OS-level functions that mess with errno. Wrap
+// Some of the methods below use OS-level functions that mess with errno. Wrap
// variants of strerror() to report errors.
#if LL_WINDOWS
@@ -79,6 +74,7 @@ static errentry const errtable[]
{ ERROR_CURRENT_DIRECTORY, EACCES }, // 16
{ ERROR_NOT_SAME_DEVICE, EXDEV }, // 17
{ ERROR_NO_MORE_FILES, ENOENT }, // 18
+ { ERROR_SHARING_VIOLATION, EACCES }, // 32
{ ERROR_LOCK_VIOLATION, EACCES }, // 33
{ ERROR_BAD_NETPATH, ENOENT }, // 53
{ ERROR_NETWORK_ACCESS_DENIED, EACCES }, // 65
@@ -109,22 +105,25 @@ static errentry const errtable[]
{ ERROR_NOT_ENOUGH_QUOTA, ENOMEM } // 1816
};
-static int set_errno_from_oserror(unsigned long oserr)
+static int get_errno_from_oserror(int oserr)
{
if (!oserr)
return 0;
// Check the table for the Windows OS error code
- for (const struct errentry &entry : errtable)
+ for (const struct errentry& entry : errtable)
{
if (oserr == entry.oserr)
{
- _set_errno(entry.errcode);
- return -1;
+ return entry.errcode;
}
}
+ return EINVAL;
+}
- _set_errno(EINVAL);
+static int set_errno_from_oserror(unsigned long oserr)
+{
+ _set_errno(get_errno_from_oserror(oserr));
return -1;
}
@@ -136,90 +135,8 @@ std::string strerr(int errn)
return buffer;
}
-inline bool is_slash(wchar_t const c)
-{
- return c == L'\\' || c == L'/';
-}
-
-static std::wstring utf8path_to_wstring(const std::string& utf8path)
-{
- if (utf8path.size() >= MAX_PATH)
- {
- // By prepending "\\?\" to a path, Windows widechar file APIs will not fail on long path names
- std::wstring utf16path = L"\\\\?\\" + ll_convert<std::wstring>(utf8path);
- // We need to make sure that the path does not contain forward slashes as above
- // prefix does bypass the path normalization that replaces slashes with backslashes
- // before passing the path to kernel mode APIs
- std::replace(utf16path.begin(), utf16path.end(), L'/', L'\\');
- return utf16path;
- }
- return ll_convert<std::wstring>(utf8path);
-}
-
-static unsigned short get_fileattr(const std::wstring& utf16path, bool dontFollowSymLink = false)
-{
- unsigned long flags = FILE_FLAG_BACKUP_SEMANTICS;
- if (dontFollowSymLink)
- {
- flags |= FILE_FLAG_OPEN_REPARSE_POINT;
- }
- HANDLE file_handle = CreateFileW(utf16path.c_str(), FILE_READ_ATTRIBUTES,
- FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
- nullptr, OPEN_EXISTING, flags, nullptr);
- if (file_handle != INVALID_HANDLE_VALUE)
- {
- FILE_ATTRIBUTE_TAG_INFO attribute_info;
- if (GetFileInformationByHandleEx(file_handle, FileAttributeTagInfo, &attribute_info, sizeof(attribute_info)))
- {
- // A volume path alone (only drive letter) is not recognized as directory while it technically is
- bool is_directory = (attribute_info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) ||
- (iswalpha(utf16path[0]) && utf16path[1] == ':' &&
- (!utf16path[2] || (is_slash(utf16path[2]) && !utf16path[3])));
- unsigned short st_mode = is_directory ? S_IFDIR :
- (attribute_info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ? S_IFLNK : S_IFREG);
- st_mode |= (attribute_info.FileAttributes & FILE_ATTRIBUTE_READONLY) ? S_IREAD : S_IREAD | S_IWRITE;
- // we do not try to guess executable flag
-
- // propagate user bits to group/other fields:
- st_mode |= (st_mode & 0700) >> 3;
- st_mode |= (st_mode & 0700) >> 6;
-
- CloseHandle(file_handle);
- return st_mode;
- }
- // Retrieve last error before calling CloseHandle()
- DWORD last_error = GetLastError();
- CloseHandle(file_handle);
- set_errno_from_oserror(last_error);
- }
- else
- {
- set_errno_from_oserror(GetLastError());
- }
-
- // If CreateFileW approach failed (e.g., exFAT), try the simpler GetFileAttributesW()
- // GetFileAttributesW() always follows symlinks, so we skip this fallback when dontFollowSymLink is true.
- if (!dontFollowSymLink)
- {
- DWORD attributes = GetFileAttributesW(utf16path.c_str());
- if (attributes != INVALID_FILE_ATTRIBUTES)
- {
- bool is_directory = (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
- unsigned short st_mode = is_directory ? S_IFDIR : S_IFREG;
- st_mode |= (attributes & FILE_ATTRIBUTE_READONLY) ? S_IREAD : S_IREAD | S_IWRITE;
-
- // propagate user bits to group/other fields:
- st_mode |= (st_mode & 0700) >> 3;
- st_mode |= (st_mode & 0700) >> 6;
-
- return st_mode;
- }
- set_errno_from_oserror(GetLastError());
- }
- return 0;
-}
-
#else
+
// On Posix we want to call strerror_r(), but alarmingly, there are two
// different variants. The one that returns int always populates the passed
// buffer (except in case of error), whereas the other one always returns a
@@ -266,9 +183,50 @@ std::string strerr(int errn)
return message_from(errn, buffer, sizeof(buffer),
strerror_r(errn, buffer, sizeof(buffer)));
}
+
#endif // ! LL_WINDOWS
-static int warnif(const std::string& desc, const std::string& filename, int rc, int accept = 0)
+#if LL_WINDOWS && 0 // turn on to debug file-locking problems
+#define PROCESS_LOCKING_CHECK 1
+static void find_locking_process(const std::string& filename)
+{
+ // Only do any of this stuff (before LL_ENDL) if it will be logged.
+ LL_DEBUGS("LLFile") << "";
+ // wrong way
+ std::string TEMP = LLFile::tmpdir();
+ if (TEMP.empty())
+ {
+ 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
+
+static int warnif(std::string_view desc, const std::filesystem::path& filename, int rc, int suppress_warning = 0)
{
if (rc < 0)
{
@@ -277,322 +235,840 @@ static int warnif(const std::string& desc, const std::string& filename, int rc,
// 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)
+ // EEXIST. Don't log a warning if caller explicitly says this errno is okay.
+ if (errn != suppress_warning)
{
- LL_WARNS("LLFile") << "Couldn't " << desc << " '" << filename
- << "' (errno " << errn << "): " << strerr(errn) << LL_ENDL;
+ 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 PROCESS_LOCKING_CHECK
// 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
+ 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;
+ find_locking_process(filename);
}
-#endif // LL_WINDOWS hack to identify processes holding file open
+#endif
}
return rc;
}
-// static
-int LLFile::mkdir(const std::string& dirname, int perms)
+static int warnif(std::string_view desc, const std::filesystem::path& filename, const std::error_code& ec, int suppress_warning = 0)
{
- // 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 (ec)
+ {
+ // get Posix errno from the std::error_code so we can compare it to the suppress_warning parameter
+ // to see when a caller wants us to not generate a warning for a particular error code
+#if LL_WINDOWS
+ int errn = get_errno_from_oserror(ec.value());
+#else
+ int errn = ec.value();
+#endif
+ // For certain operations, a particular errno value might be acceptable
+ // Don't warn if caller explicitly says this errno is okay.
+ if (errn != suppress_warning)
+ {
+ LL_WARNS("LLFile") << "Couldn't " << desc << " '" << filename << "' (errno " << errn << "): " << ec.message() << LL_ENDL;
+ }
+#if PROCESS_LOCKING_CHECK
+ // Try to detect locked files by other processes
+ if (ec.value() == ERROR_SHARING_VIOLATION || ec.value() == ERROR_LOCK_VIOLATION)
+ {
+ find_locking_process(filename);
+ }
+#endif
+ return -1;
+ }
+ return 0;
+}
+
#if LL_WINDOWS
- // permissions are ignored on Windows
- int rc = 0;
- std::wstring utf16dirname = utf8path_to_wstring(dirname);
- if (!CreateDirectoryW(utf16dirname.c_str(), nullptr))
+
+inline int set_ec_from_system_error(std::error_code& ec, DWORD error)
+{
+ ec.assign(error, std::system_category());
+ return -1;
+}
+
+static int set_ec_from_system_error(std::error_code& ec)
+{
+ return set_ec_from_system_error(ec, GetLastError());
+}
+
+inline int set_ec_to_parameter_error(std::error_code& ec)
+{
+ return set_ec_from_system_error(ec, ERROR_INVALID_PARAMETER);
+}
+
+inline int set_ec_to_outofmemory_error(std::error_code& ec)
+{
+ return set_ec_from_system_error(ec, ERROR_NOT_ENOUGH_MEMORY);
+}
+
+inline DWORD decode_access_mode(std::ios_base::openmode omode)
+{
+ switch (omode & (LLFile::in | LLFile::out))
+ {
+ case LLFile::in:
+ return GENERIC_READ;
+ case LLFile::out:
+ return GENERIC_WRITE;
+ case static_cast<std::ios_base::openmode>(LLFile::in | LLFile::out):
+ return GENERIC_READ | GENERIC_WRITE;
+ }
+ if (omode & LLFile::app)
+ {
+ return GENERIC_WRITE;
+ }
+ return 0;
+}
+
+inline DWORD decode_open_create_flags(std::ios_base::openmode omode)
+{
+ if (omode & LLFile::noreplace)
+ {
+ return CREATE_NEW; // create if it does not exist, otherwise fail
+ }
+ if (omode & LLFile::trunc)
{
- // Only treat other errors than an already existing file as a real error
- unsigned long oserr = GetLastError();
- if (oserr != ERROR_ALREADY_EXISTS)
+ if (!(omode & LLFile::out))
{
- rc = set_errno_from_oserror(oserr);
+ return TRUNCATE_EXISTING; // open and truncate if it exists, otherwise fail
}
+ return CREATE_ALWAYS; // open and truncate if it exists, otherwise create it
+ }
+ if (!(omode & LLFile::out))
+ {
+ return OPEN_EXISTING; // open if it exists, otherwise fail
+ }
+ // LLFile::app or (LLFile::out and (!LLFile::trunc or !LLFile::noreplace))
+ return OPEN_ALWAYS; // open if it exists, otherwise create it
+}
+
+inline DWORD decode_share_mode(int omode)
+{
+ if (omode & LLFile::exclusive)
+ {
+ return 0; // allow no other access
+ }
+ if (omode & LLFile::shared)
+ {
+ return FILE_SHARE_READ; // allow read access
+ }
+ return FILE_SHARE_READ | FILE_SHARE_WRITE; // allow read and write access to others
+}
+
+inline DWORD decode_attributes(std::ios_base::openmode omode, int perm)
+{
+ return (perm & S_IWRITE) ? FILE_ATTRIBUTE_NORMAL : FILE_ATTRIBUTE_READONLY;
+}
+
+// Under Windows the values for the std::ios_base::seekdir constants match the according FILE_BEGIN
+// and other constants but we do a programmatic translation for now to be sure
+static DWORD seek_mode_from_dir(std::ios_base::seekdir seekdir)
+{
+ switch (seekdir)
+ {
+ case LLFile::beg:
+ return FILE_BEGIN;
+ case LLFile::cur:
+ return FILE_CURRENT;
+ case LLFile::end:
+ return FILE_END;
}
+ return FILE_BEGIN;
+}
+
#else
- int rc = ::mkdir(dirname.c_str(), (mode_t)perms);
- if (rc < 0 && errno == EEXIST)
+
+inline int set_ec_from_system_error(std::error_code& ec, int error)
+{
+ ec.assign(error, std::system_category());
+ return -1;
+}
+
+static int set_ec_from_system_error(std::error_code& ec)
+{
+ return set_ec_from_system_error(ec, errno);
+}
+
+inline int set_ec_to_parameter_error(std::error_code& ec)
+{
+ return set_ec_from_system_error(ec, EINVAL);
+}
+
+inline int set_ec_to_outofmemory_error(std::error_code& ec)
+{
+ return set_ec_from_system_error(ec, ENOMEM);
+}
+
+inline int decode_access_mode(std::ios_base::openmode omode)
+{
+ if (omode & LLFile::out)
{
- // this is not the error you want, move along
- return 0;
+ if (omode & LLFile::in)
+ {
+ return O_RDWR;
+ }
+ return O_WRONLY;
+ }
+ return O_RDONLY;
+
+ /*switch (omode & (LLFile::in | LLFile::out))
+ {
+ case LLFile::out:
+ return O_WRONLY;
+ case static_cast<std::ios_base::openmode>(LLFile::in | LLFile::out):
+ return O_RDWR;
+ }
+ return O_RDONLY;*/
+}
+
+inline int decode_open_mode(std::ios_base::openmode omode)
+{
+ int flags = O_CREAT | decode_access_mode(omode);
+ if (omode & LLFile::app)
+ {
+ flags |= O_APPEND;
}
+ if (omode & LLFile::trunc)
+ {
+ flags |= O_TRUNC;
+ }
+ if (omode & LLFile::binary)
+ {
+ // Not a thing under *nix
+ }
+ if (omode & LLFile::noreplace)
+ {
+ flags |= O_EXCL;
+ }
+ return flags;
+}
+
+inline int decode_lock_mode(std::ios_base::openmode omode)
+{
+ int lmode = omode & LLFile::noblock ? LOCK_NB : 0;
+ if (omode & LLFile::lock_mask)
+ {
+ if (omode & LLFile::exclusive)
+ {
+ return lmode | LOCK_EX;
+ }
+ return lmode | LOCK_SH;
+ }
+ return lmode | LOCK_UN;
+}
+
+// Under Linux and Mac the values for the std::ios_base::seekdir constants match the according SEEK_SET
+// and other constants but we do a programmatic translation for now to be sure
+inline int seek_mode_from_dir(std::ios_base::seekdir seekdir)
+{
+ switch (seekdir)
+ {
+ case LLFile::beg:
+ return SEEK_SET;
+ case LLFile::cur:
+ return SEEK_CUR;
+ case LLFile::end:
+ return SEEK_END;
+ }
+ return SEEK_SET;
+}
+
#endif
- // anything else might be a problem
- return warnif("mkdir", dirname, rc);
+
+inline int clear_error(std::error_code& ec)
+{
+ ec.clear();
+ return 0;
}
-// static
-int LLFile::rmdir(const std::string& dirname, int suppress_error)
+inline bool are_open_mode_flags_invalid(std::ios_base::openmode omode)
+{
+ // at least one of input or output needs to be specified
+ if (!(omode & (LLFile::in | LLFile::out)))
+ {
+ return true;
+ }
+ // output must be possible for any of the extra options
+ if (!(omode & LLFile::out) && (omode & (LLFile::trunc | LLFile::app | LLFile::noreplace)))
+ {
+ return true;
+ }
+ // invalid combination, mutually exclusive
+ if ((omode & LLFile::app) && (omode & (LLFile::trunc | LLFile::noreplace)))
+ {
+ return true;
+ }
+ return false;
+}
+
+//----------------------------------------------------------------------------------------
+// class member functions
+//----------------------------------------------------------------------------------------
+int LLFile::open(const std::filesystem::path& file_path, std::ios_base::openmode omode, std::error_code& ec, int perm)
{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE;
+ close(ec);
+ if (are_open_mode_flags_invalid(omode))
+ {
+ return set_ec_to_parameter_error(ec);
+ }
#if LL_WINDOWS
- std::wstring utf16dirname = utf8path_to_wstring(dirname);
- int rc = _wrmdir(utf16dirname.c_str());
+ DWORD access = decode_access_mode(omode),
+ share = decode_share_mode(omode),
+ create = decode_open_create_flags(omode),
+ attributes = decode_attributes(omode, perm);
+
+ mHandle = CreateFileW(file_path.native().c_str(), access, share, nullptr, create, attributes, nullptr);
+ // The dwShareMode = share parameter takes care of locking the file for other processes if indicated,
+ // no need to do anything else for file locking here
#else
- int rc = ::rmdir(dirname.c_str());
+ int oflags = decode_open_mode(omode);
+ int lmode = omode & LLFile::lock_mask;
+ mHandle = ::open(file_path.native().c_str(), oflags, perm);
+ if (mHandle != InvalidHandle && lmode && lock(lmode | LLFile::noblock, ec) != 0)
+ {
+ close();
+ return -1;
+ }
#endif
- return warnif("rmdir", dirname, rc, suppress_error);
+ if (mHandle == InvalidHandle)
+ {
+ return set_ec_from_system_error(ec);
+ }
+
+ if (omode & LLFile::ate && seek(0, LLFile::end, ec) != 0)
+ {
+ close();
+ return -1;
+ }
+ mOpen = omode;
+ return clear_error(ec);
}
-// static
-LLFILE* LLFile::fopen(const std::string& filename, const char* mode)
+S64 LLFile::size(std::error_code& ec)
{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE;
#if LL_WINDOWS
- std::wstring utf16filename = utf8path_to_wstring(filename);
- std::wstring utf16mode = ll_convert<std::wstring>(std::string(mode));
- return _wfopen(utf16filename.c_str(), utf16mode.c_str());
+ LARGE_INTEGER value = { 0 };
+ if (GetFileSizeEx(mHandle, &value))
+ {
+ clear_error(ec);
+ return value.QuadPart;
+ }
#else
- return ::fopen(filename.c_str(),mode);
+ struct stat statval;
+ if (fstat(mHandle, &statval) == 0)
+ {
+ clear_error(ec);
+ return statval.st_size;
+ }
#endif
+ set_ec_from_system_error(ec);
+ return 0;
}
-// static
-int LLFile::close(LLFILE * file)
+S64 LLFile::tell(std::error_code& ec)
{
- int ret_value = 0;
- if (file)
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE;
+#if LL_WINDOWS
+ LARGE_INTEGER value = { 0 };
+ if (SetFilePointerEx(mHandle, value, &value, FILE_CURRENT))
{
- ret_value = fclose(file);
+ clear_error(ec);
+ return value.QuadPart;
}
- return ret_value;
+#else
+ off_t offset = lseek(mHandle, 0, SEEK_CUR);
+ if (offset != -1)
+ {
+ clear_error(ec);
+ return offset;
+ }
+#endif
+ return set_ec_from_system_error(ec);
}
-// static
-std::string LLFile::getContents(const std::string& filename)
+int LLFile::seek(S64 pos, std::error_code& ec)
{
- LLFILE* fp = LLFile::fopen(filename, "rb");
- if (fp)
+ return seek(pos, LLFile::beg, ec);
+}
+
+int LLFile::seek(S64 offset, std::ios_base::seekdir dir, std::error_code& ec)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE;
+ S64 newOffset = 0;
+#if LL_WINDOWS
+ DWORD seekdir = seek_mode_from_dir(dir);
+ LARGE_INTEGER value;
+ value.QuadPart = offset;
+ if (SetFilePointerEx(mHandle, value, (PLARGE_INTEGER)&newOffset, seekdir))
+#else
+ newOffset = lseek(mHandle, offset, seek_mode_from_dir(dir));
+ if (newOffset != -1)
+#endif
{
- fseek(fp, 0, SEEK_END);
- U32 length = ftell(fp);
- fseek(fp, 0, SEEK_SET);
+ return clear_error(ec);
+ }
+ return set_ec_from_system_error(ec);
+}
- std::vector<char> buffer(length);
- size_t nread = fread(buffer.data(), 1, length, fp);
- fclose(fp);
+#if LL_WINDOWS
+inline DWORD next_buffer_size(S64 nbytes)
+{
+ return nbytes > 0x80000000 ? 0x80000000 : (DWORD)nbytes;
+}
+#endif
- return std::string(buffer.data(), nread);
+S64 LLFile::read(void* buffer, S64 nbytes, std::error_code& ec)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE;
+ if (nbytes == 0)
+ {
+ // Nothing to do
+ return clear_error(ec);
}
+ else if (!buffer || nbytes < 0)
+ {
+ return set_ec_to_parameter_error(ec);
+ }
+#if LL_WINDOWS
+ S64 totalBytes = 0;
+ char *ptr = (char*)buffer;
+ DWORD bytesRead, bytesToRead = next_buffer_size(nbytes);
- return LLStringUtil::null;
+ // Read in chunks to support >4GB which the S64 nbytes value makes possible
+ while (ReadFile(mHandle, ptr, bytesToRead, &bytesRead, nullptr))
+ {
+ totalBytes += bytesRead;
+ if (nbytes <= totalBytes || // requested amount read
+ bytesRead < bytesToRead) // ReadFile encountered eof
+ {
+ clear_error(ec);
+ return totalBytes;
+ }
+ ptr += bytesRead;
+ bytesToRead = next_buffer_size(nbytes - totalBytes);
+ }
+#else
+ ssize_t bytesRead = ::read(mHandle, buffer, nbytes);
+ if (bytesRead != -1)
+ {
+ clear_error(ec);
+ return bytesRead;
+ }
+#endif
+ return set_ec_from_system_error(ec);
}
-// static
-int LLFile::remove(const std::string& filename, int suppress_error)
+S64 LLFile::write(const void* buffer, S64 nbytes, std::error_code& ec)
{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE;
+ if (nbytes == 0)
+ {
+ // Nothing to do here
+ return clear_error(ec);
+ }
+ else if (!buffer || nbytes < 0)
+ {
+ return set_ec_to_parameter_error(ec);
+ }
#if LL_WINDOWS
- // Posix remove() works on both files and directories although on Windows
- // remove() and its wide char variant _wremove() only removes files just
- // as its siblings unlink() and _wunlink().
- // If we really only want to support files we should instead use
- // unlink() in the non-Windows part below too
- int rc = -1;
- std::wstring utf16filename = utf8path_to_wstring(filename);
- unsigned short st_mode = get_fileattr(utf16filename);
- if (S_ISDIR(st_mode))
+ // If this was opened in append mode, we emulate it on Windows
+ if (mOpen & LLFile::app && seek(0, LLFile::end, ec) != 0)
{
- rc = _wrmdir(utf16filename.c_str());
+ return -1;
}
- else if (S_ISREG(st_mode))
+
+ S64 totalBytes = 0;
+ char* ptr = (char*)buffer;
+ DWORD bytesWritten, bytesToWrite = next_buffer_size(nbytes);
+
+ // Write in chunks to support >4GB which the S64 nbytes value makes possible
+ while (WriteFile(mHandle, ptr, bytesToWrite, &bytesWritten, nullptr))
{
- rc = _wunlink(utf16filename.c_str());
+ totalBytes += bytesWritten;
+ if (nbytes <= totalBytes)
+ {
+ clear_error(ec);
+ return totalBytes;
+ }
+ ptr += bytesWritten;
+ bytesToWrite = next_buffer_size(nbytes - totalBytes);
+ }
+#else
+ ssize_t bytesWritten = ::write(mHandle, buffer, nbytes);
+ if (bytesWritten != -1)
+ {
+ clear_error(ec);
+ return bytesWritten;
+ }
+#endif
+ return set_ec_from_system_error(ec);
+}
+
+S64 LLFile::printf(const char* fmt, ...)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE;
+ va_list args1;
+ va_start(args1, fmt);
+ va_list args2;
+ va_copy(args2, args1);
+ int length = vsnprintf(nullptr, 0, fmt, args1);
+ va_end(args1);
+ if (length < 0)
+ {
+ va_end(args2);
+ return -1;
}
- else if (st_mode)
+ void* buffer = malloc(length + 1);
+ if (!buffer)
{
- // it is something else than a file or directory
- // this should not really happen as long as we do not allow for symlink
- // detection in the optional parameter to get_fileattr()
- rc = set_errno_from_oserror(ERROR_INVALID_PARAMETER);
+ va_end(args2);
+ return -1;
+ }
+ length = vsnprintf((char*)buffer, length + 1, fmt, args2);
+ va_end(args2);
+ std::error_code ec;
+ S64 written = write(buffer, length, ec);
+ free(buffer);
+ return written;
+}
+
+int LLFile::lock(int mode, std::error_code& ec)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE;
+#if LL_WINDOWS
+ if (!(mode & LLFile::lock_mask))
+ {
+ if (UnlockFile(mHandle, 0, 0, MAXDWORD, MAXDWORD))
+ {
+ return clear_error(ec);
+ }
}
else
{
- // get_fileattr() failed and already set errno, preserve it for correct error reporting
+ OVERLAPPED overlapped = { 0 };
+ DWORD flags = (mode & LLFile::noblock) ? LOCKFILE_FAIL_IMMEDIATELY : 0;
+ if (mode & LLFile::exclusive)
+ {
+ flags |= LOCKFILE_EXCLUSIVE_LOCK;
+ }
+ // We lock the maximum range, since flock only supports locking the entire file too
+ if (LockFileEx(mHandle, flags, 0, MAXDWORD, MAXDWORD, &overlapped))
+ {
+ return clear_error(ec);
+ }
}
#else
- int rc = ::remove(filename.c_str());
+ if (flock(mHandle, decode_lock_mode(static_cast<std::ios_base::openmode>(mode))) == 0)
+ {
+ return clear_error(ec);
+ }
#endif
- return warnif("remove", filename, rc, suppress_error);
+ return set_ec_from_system_error(ec);
+}
+
+int LLFile::close(std::error_code& ec)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE;
+ if (mHandle != InvalidHandle)
+ {
+ llfile_handle_t handle = InvalidHandle;
+ std::swap(handle, mHandle);
+#if LL_WINDOWS
+ if (!CloseHandle(handle))
+#else
+ if (::close(handle))
+#endif
+ {
+ return set_ec_from_system_error(ec);
+ }
+ }
+ return clear_error(ec);
+}
+
+int LLFile::close()
+{
+ std::error_code ec;
+ return close(ec);
}
+//----------------------------------------------------------------------------------------
+// static member functions
+//----------------------------------------------------------------------------------------
+
// static
-int LLFile::rename(const std::string& filename, const std::string& newname, int suppress_error)
+LLFILE* LLFile::fopen(const std::filesystem::path& file_path, const fopen_flags_t* mode, int lmode)
{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE;
+ LLFILE* file;
#if LL_WINDOWS
- // Posix rename() will gladly overwrite a file at newname if it exists, the Windows
- // rename(), respectively _wrename(), will bark on that. Instead call directly the Windows
- // API MoveFileEx() and use its flags to specify that overwrite is allowed.
- std::wstring utf16filename = utf8path_to_wstring(filename);
- std::wstring utf16newname = utf8path_to_wstring(newname);
- int rc = 0;
- if (!MoveFileExW(utf16filename.c_str(), utf16newname.c_str(), MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED))
+ int shflag = _SH_DENYNO;
+ switch (lmode)
{
- rc = set_errno_from_oserror(GetLastError());
+ case LLFile::exclusive:
+ shflag = _SH_DENYRW;
+ break;
+ case LLFile::shared:
+ shflag = _SH_DENYWR;
+ break;
}
+ file = _wfsopen(file_path.native().c_str(), mode, shflag);
#else
- int rc = ::rename(filename.c_str(),newname.c_str());
+ file = ::fopen(file_path.native().c_str(), mode);
+ if (file && (lmode & (LLFile::lock_mask)))
+ {
+ // Rather fail on a sharing conflict than block
+ if (flock(fileno(file), decode_lock_mode(static_cast<std::ios_base::openmode>(lmode | LLFile::noblock))))
+ {
+ ::fclose(file);
+ file = nullptr;
+ }
+ }
#endif
- return warnif(STRINGIZE("rename to '" << newname << "' from"), filename, rc, suppress_error);
+ return file;
}
-// Make this a define rather than using magic numbers multiple times in the code
-#define LLFILE_COPY_BUFFER_SIZE 16384
+// static
+int LLFile::close(LLFILE* file)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE;
+ int ret_value = 0;
+ if (file)
+ {
+ ret_value = ::fclose(file);
+ }
+ return ret_value;
+}
// static
-bool LLFile::copy(const std::string& from, const std::string& to)
+std::string LLFile::getContents(const std::filesystem::path& file_path, std::error_code& ec)
{
- bool copied = false;
- LLFILE* in = LLFile::fopen(from, "rb");
- if (in)
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE;
+ std::string buffer;
+ LLFile file(file_path, LLFile::in | LLFile::binary, ec);
+ if (file)
{
- LLFILE* out = LLFile::fopen(to, "wb");
- if (out)
+ S64 length = file.size(ec);
+ if (!ec && length > 0)
{
- char buf[LLFILE_COPY_BUFFER_SIZE];
- size_t readbytes;
- bool write_ok = true;
- while (write_ok && (readbytes = fread(buf, 1, LLFILE_COPY_BUFFER_SIZE, in)))
- {
- if (fwrite(buf, 1, readbytes, out) != readbytes)
- {
- LL_WARNS("LLFile") << "Short write" << LL_ENDL;
- write_ok = false;
- }
- }
- if ( write_ok )
+ buffer = std::string(length, 0);
+ file.read(&buffer[0], length, ec);
+ if (ec)
{
- copied = true;
+ buffer.clear();
}
- fclose(out);
}
- fclose(in);
}
- return copied;
+ return buffer;
}
// static
-int LLFile::stat(const std::string& filename, llstat* filestatus, int suppress_error)
+int LLFile::mkdir(const std::filesystem::path& file_path)
{
-#if LL_WINDOWS
- std::wstring utf16filename = utf8path_to_wstring(filename);
- int rc = _wstat64(utf16filename.c_str(), filestatus);
-#else
- int rc = ::stat(filename.c_str(), filestatus);
-#endif
- return warnif("stat", filename, rc, suppress_error);
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE;
+ std::error_code ec;
+ // 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.
+ std::filesystem::create_directory(file_path, ec);
+ // The return value is only true if the directory was actually created.
+ // But if it already existed, ec still indicates success.
+ return warnif("mkdir", file_path, ec);
}
// static
-unsigned short LLFile::getattr(const std::string& filename, bool dontFollowSymLink, int suppress_error)
+int LLFile::remove(const std::filesystem::path& file_path, int suppress_warning)
{
-#if LL_WINDOWS
- // _wstat64() is a bit heavyweight on Windows, use a more lightweight API
- // to just get the attributes
- int rc = -1;
- std::wstring utf16filename = utf8path_to_wstring(filename);
- unsigned short st_mode = get_fileattr(utf16filename, dontFollowSymLink);
- if (st_mode)
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE;
+ std::error_code ec;
+ std::filesystem::remove(file_path, ec);
+ return warnif("remove", file_path, ec, suppress_warning);
+}
+
+// static
+int LLFile::rename(const std::filesystem::path& file_path, const std::filesystem::path& new_path, int suppress_warning)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE;
+ std::error_code ec;
+ std::filesystem::rename(file_path, new_path, ec);
+ return warnif("rename", file_path, ec, suppress_warning);
+}
+
+// static
+S64 LLFile::read(const std::filesystem::path& file_path, void* buf, S64 offset, S64 nbytes, std::error_code& ec)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE;
+ // if number of bytes is 0 or less there is nothing to do here
+ if (nbytes <= 0)
{
- return st_mode;
+ clear_error(ec);
+ return 0;
}
-#else
- llstat filestatus;
- int rc = dontFollowSymLink ? ::lstat(filename.c_str(), &filestatus) : ::stat(filename.c_str(), &filestatus);
- if (rc == 0)
+
+ if (!buf || offset < 0)
{
- return filestatus.st_mode;
+ set_ec_to_parameter_error(ec);
}
-#endif
- warnif("getattr", filename, rc, suppress_error);
- return 0;
+ else
+ {
+ std::ios_base::openmode omode = LLFile::in | LLFile::binary;
+
+ LLFile file(file_path, omode, ec);
+ if (!ec && (bool)file)
+ {
+ if (offset > 0)
+ {
+ file.seek(offset, ec);
+ }
+ // else (offset == 0) file was just opened and should already be at 0.
+ if (!ec)
+ {
+ S64 bytes_read = file.read(buf, nbytes, ec);
+ if (!ec)
+ {
+ return bytes_read;
+ }
+ }
+ }
+ }
+ return warnif("read from file failed", file_path, ec);
}
// static
-bool LLFile::isdir(const std::string& filename)
+S64 LLFile::write(const std::filesystem::path& file_path, const void* buf, S64 offset, S64 nbytes, std::error_code& ec)
{
- return S_ISDIR(getattr(filename));
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE;
+ // if number of bytes is 0 or less there is nothing to do here
+ if (nbytes <= 0)
+ {
+ clear_error(ec);
+ return 0;
+ }
+
+ if (!buf)
+ {
+ set_ec_to_parameter_error(ec);
+ }
+ else
+ {
+ std::ios_base::openmode omode = LLFile::out | LLFile::binary;
+ if (offset < 0)
+ {
+ omode |= LLFile::app;
+ }
+
+ LLFile file(file_path, omode, ec);
+ if (!ec && (bool)file)
+ {
+ if (offset > 0)
+ {
+ file.seek(offset, ec);
+ }
+ // else (offset == 0) we are not appending, file was just opened and should already be at 0.
+ if (!ec)
+ {
+ S64 bytes_written = file.write(buf, nbytes, ec);
+ if (!ec)
+ {
+ return bytes_written;
+ }
+ }
+ }
+ }
+ return warnif("write to file failed", file_path, ec);
}
// static
-bool LLFile::isfile(const std::string& filename)
+bool LLFile::copy(const std::filesystem::path& source_path, const std::filesystem::path& target_path, std::filesystem::copy_options options, std::error_code& ec)
{
- return S_ISREG(getattr(filename));
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE;
+ bool copied = std::filesystem::copy_file(source_path, target_path, options, ec);
+ if (!copied)
+ {
+ warnif(STRINGIZE("copy failed, to '" << target_path << "' from"), source_path, ec);
+ }
+ return copied;
}
// static
-bool LLFile::islink(const std::string& filename)
+int LLFile::stat(const std::filesystem::path& file_path, llstat* filestatus, const char *fname, int suppress_warning)
{
- return S_ISLNK(getattr(filename, true));
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE;
+#if LL_WINDOWS
+ int rc = _wstat64(file_path.native().c_str(), filestatus);
+#else
+ int rc = ::stat(file_path.native().c_str(), filestatus);
+#endif
+ return warnif(fname ? fname : "stat", file_path, rc, suppress_warning);
}
// static
-const char *LLFile::tmpdir()
+S64 LLFile::size(const std::filesystem::path& file_path, int suppress_warning)
{
- static std::string utf8path;
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE;
+ std::error_code ec;
+ std::intmax_t size = static_cast<std::intmax_t>(std::filesystem::file_size(file_path, ec));
+ if (ec)
+ {
+ warnif("size", file_path, ec, suppress_warning);
+ return 0;
+ }
+ return size;
+}
- if (utf8path.empty())
+// static
+std::filesystem::file_status LLFile::getStatus(const std::filesystem::path& file_path, bool dontFollowSymLink, int suppress_warning)
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE;
+ std::error_code ec;
+ std::filesystem::file_status status;
+ if (dontFollowSymLink)
{
- char sep;
-#if LL_WINDOWS
- sep = '\\';
+ status = std::filesystem::symlink_status(file_path, ec);
+ }
+ else
+ {
+ status = std::filesystem::status(file_path, ec);
+ }
+ warnif("getStatus()", file_path, ec, suppress_warning);
+ return status;
+}
- std::vector<wchar_t> utf16path(MAX_PATH + 1);
- GetTempPathW(static_cast<DWORD>(utf16path.size()), &utf16path[0]);
- utf8path = ll_convert_wide_to_string(&utf16path[0]);
+// static
+const std::string& LLFile::tmpdir()
+{
+ LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE;
+ static std::string temppath;
+ if (temppath.empty())
+ {
+#if LL_WINDOWS
+ temppath = ll_convert<std::string>(std::filesystem::temp_directory_path().native());
+ char sep = '\\';
#else
- sep = '/';
-
- utf8path = LLStringUtil::getenv("TMPDIR", "/tmp/");
+ temppath = std::filesystem::temp_directory_path().string();
+ char sep = '/';
#endif
- if (utf8path[utf8path.size() - 1] != sep)
+ if (temppath[temppath.size() - 1] != sep)
{
- utf8path += sep;
+ temppath += sep;
}
}
- return utf8path.c_str();
+
+ return temppath;
}
#if LL_WINDOWS
/************** input file stream ********************************/
-llifstream::llifstream() {}
+// explicit
+llifstream::llifstream(const char* _Filename, ios_base::openmode _Mode) :
+ std::ifstream(ll_convert<std::wstring>(_Filename).c_str(), _Mode | ios_base::in)
+{
+}
// explicit
llifstream::llifstream(const std::string& _Filename, ios_base::openmode _Mode):
@@ -601,17 +1077,37 @@ llifstream::llifstream(const std::string& _Filename, ios_base::openmode _Mode):
{
}
+// explicit
+llifstream::llifstream(const std::filesystem::path& _Filepath, ios_base::openmode _Mode) :
+ std::ifstream(_Filepath, _Mode | ios_base::in)
+{
+}
+
+void llifstream::open(const char* _Filename, ios_base::openmode _Mode)
+{
+ std::ifstream::open(ll_convert<std::wstring>(_Filename).c_str(),
+ _Mode | ios_base::in);
+}
+
void llifstream::open(const std::string& _Filename, ios_base::openmode _Mode)
{
std::ifstream::open(ll_convert<std::wstring>(_Filename).c_str(),
_Mode | ios_base::in);
}
+void llifstream::open(const std::filesystem::path& _Filepath, ios_base::openmode _Mode)
+{
+ std::ifstream::open(_Filepath,
+ _Mode | ios_base::in);
+}
/************** output file stream ********************************/
-
-llofstream::llofstream() {}
+// explicit
+llofstream::llofstream(const char* _Filename, ios_base::openmode _Mode) :
+ std::ofstream(ll_convert<std::wstring>(_Filename).c_str(), _Mode | ios_base::out)
+{
+}
// explicit
llofstream::llofstream(const std::string& _Filename, ios_base::openmode _Mode):
@@ -620,36 +1116,27 @@ llofstream::llofstream(const std::string& _Filename, ios_base::openmode _Mode):
{
}
-void llofstream::open(const std::string& _Filename, ios_base::openmode _Mode)
+// explicit
+llofstream::llofstream(const std::filesystem::path& _Filepath, ios_base::openmode _Mode) :
+ std::ofstream(_Filepath, _Mode | ios_base::out)
{
- std::ofstream::open(ll_convert<std::wstring>( _Filename ).c_str(),
- _Mode | ios_base::out);
}
-/************** helper functions ********************************/
+void llofstream::open(const char* _Filename, ios_base::openmode _Mode)
+{
+ std::ofstream::open(ll_convert<std::wstring>(_Filename).c_str(), _Mode | ios_base::out);
+}
-std::streamsize llifstream_size(llifstream& ifstr)
+void llofstream::open(const std::string& _Filename, ios_base::openmode _Mode)
{
- 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::ofstream::open(ll_convert<std::wstring>( _Filename ).c_str(),
+ _Mode | ios_base::out);
}
-std::streamsize llofstream_size(llofstream& ofstr)
+void llofstream::open(const std::filesystem::path& _Filepath, ios_base::openmode _Mode)
{
- 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;
+ std::ofstream::open(_Filepath,
+ _Mode | ios_base::out);
}
#endif // LL_WINDOWS
diff --git a/indra/llcommon/llfile.h b/indra/llcommon/llfile.h
index 04a2946ac4..8de3f5c9b4 100644
--- a/indra/llcommon/llfile.h
+++ b/indra/llcommon/llfile.h
@@ -35,133 +35,712 @@
* Attempts to mostly mirror the POSIX style IO functions.
*/
-typedef FILE LLFILE;
+#include "stdtypes.h"
#include <fstream>
+#include <filesystem>
#include <sys/stat.h>
#if LL_WINDOWS
+#include <windows.h>
// The Windows version of stat function and stat data structure are called _stat64
// We use _stat64 here to support 64-bit st_size and time_t values
-typedef struct _stat64 llstat;
+typedef struct _stat64 llstat;
#else
-typedef struct stat llstat;
#include <sys/types.h>
+typedef struct stat llstat;
#endif
-#ifndef S_ISREG
-# define S_ISREG(x) (((x) & S_IFMT) == S_IFREG)
-#endif
-
-#ifndef S_ISDIR
-# define S_ISDIR(x) (((x) & S_IFMT) == S_IFDIR)
-#endif
+typedef FILE LLFILE;
-// Windows C runtime library does not define this and does not support symlink detection in the
-// stat functions but we do in our getattr() function
-#ifndef S_IFLNK
-#define S_IFLNK 0xA000 /* symlink */
-#endif
+#include "fsyspath.h"
+#include "llstring.h" // safe char* -> std::string conversion
-#ifndef S_ISLNK
-#define S_ISLNK(x) (((x) & S_IFMT) == S_IFLNK)
+#if LL_WINDOWS
+#define LLFILE_MODE(flags) L##flags
+#else
+#define LLFILE_MODE(flags) flags
#endif
-#include "llstring.h" // safe char* -> std::string conversion
-
-/// LLFile is a class of static functions operating on paths
+/// This class provides a selection of functions to operate on files through names and
+/// a class implementation to represent a file for reading and writing to it
/// All the functions with a path string input take UTF8 path/filenames
-class LL_COMMON_API LLFile
+///
+/// @nosubgrouping
+///
+class LLFile
{
public:
+ // ================================================================================
+ /// @name Constants
+ ///
+ ///@{
+ /** These can be passed to the omode parameter of LLFile::open() and its constructor
+
+ This is similar to the openmode flags for std:fstream but not exactly the same
+ std::fstream open() does not allow to open a file for writing without either
+ forcing the file to be truncated on open or all write operations being always
+ appended to the end of the file or failing to open when the file does not exist.
+ But to allow implementing the LLAPRFile::writeEx() functionality we need to be
+ able to write at a random position to the file without truncating it on open.
+
+ any other combinations than listed here are not allowed and will cause an error
+
+ bin in out trunc app noreplace File exists File doesn't exist
+ ----------------------------------------------------------------------------------
+ - + - - - - Open at begin Failure to open
+ + + - - - - " "
+ - - + - - - " Create new
+ + - + - - - " "
+ - + + - - - " "
+ + + + - - - " "
+ ----------------------------------------------------------------------------------
+ - - + + - - Destroy contents Create new
+ + - + + - - " "
+ - + + + - - " "
+ + + + + - - " "
+ ----------------------------------------------------------------------------------
+ - - + x - + Failure to open Create new
+ + - + x - + " "
+ - + + x - + " "
+ + + + x - + " "
+ ----------------------------------------------------------------------------------
+ - - + - + - Write to end Create new
+ + - + - + - " "
+ - + + - + - " "
+ + + + - + - " "
+ ----------------------------------------------------------------------------------
+ */
+ static const std::ios_base::openmode app = static_cast<std::ios_base::openmode>(1 << 1); // append to end
+ static const std::ios_base::openmode ate = static_cast<std::ios_base::openmode>(1 << 2); // initialize to end
+ static const std::ios_base::openmode binary = static_cast<std::ios_base::openmode>(1 << 3); // binary mode
+ static const std::ios_base::openmode in = static_cast<std::ios_base::openmode>(1 << 4); // for reading
+ static const std::ios_base::openmode out = static_cast<std::ios_base::openmode>(1 << 5); // for writing // for writing and reading
+ static const std::ios_base::openmode trunc = static_cast<std::ios_base::openmode>(1 << 6); // truncate on open
+ static const std::ios_base::openmode noreplace = static_cast<std::ios_base::openmode>(1 << 7); // no replace if it exists
+
+ /// Additional optional flags to omode in open() and lmode in fopen() or lock()
+ /// to indicate which sort of lock if any to attempt to get
+ ///
+ /// NOTE: there is a fundamental difference between platforms.
+ /// On Windows this lock is mandatory as it is part of the API to open a file handle and other
+ /// processes can not avoid it. If a file was opened denying other processes read and/or write
+ /// access, trying to open the same file in another process with that access will fail.
+ /// On Mac and Linux it is only an advisory lock implemented through the flock() system call.
+ /// This means that any other application needs to also attempt to at least acquire a shared
+ /// lock on the file in order to notice that the file is actually already locked. It can
+ /// therefore not be used to prevent random other applications from accessing the file, but it
+ /// works for other viewer processes when they use either the LLFile::open() or LLFile::fopen()
+ /// functions with the appropriate lock flags to open a file.
+ static const std::ios_base::openmode exclusive = static_cast<std::ios_base::openmode>(1 << 16);
+ static const std::ios_base::openmode shared = static_cast<std::ios_base::openmode>(1 << 17);
+
+ /// Additional lmode flag to indicate to rather fail instead of blocking when trying
+ /// to acquire a lock with LLFile::lock()
+ static const std::ios_base::openmode noblock = static_cast<std::ios_base::openmode>(1 << 18);
+
+ /// The mask value for the lock mask bits
+ static const std::ios_base::openmode lock_mask = static_cast<std::ios_base::openmode>(exclusive | shared);
+
+ /// One of these can be passed to the dir parameter of LLFile::seek()
+ static const std::ios_base::seekdir beg = std::ios_base::beg;
+ static const std::ios_base::seekdir cur = std::ios_base::cur;
+ static const std::ios_base::seekdir end = std::ios_base::end;
+ ///@}
+
+ // ================================================================================
+ /// @name constructor/deconstructor
+ ///
+ ///@{
+ /// default constructor
+ LLFile() : mHandle(InvalidHandle) {}
+
+ /// no copy constructor
+ LLFile(const LLFile&) = delete;
+
+ /// move constructor
+ LLFile(LLFile&& other) noexcept
+ {
+ mHandle = other.mHandle;
+ other.mHandle = InvalidHandle;
+ }
+
+ /// constructor opening the file
+ explicit LLFile(const char* filename, std::ios_base::openmode omode, std::error_code& ec, int perm = 0666) :
+ mHandle(InvalidHandle)
+ {
+ open(filename, omode, ec, perm);
+ }
+
+ explicit LLFile(const std::string& filename, std::ios_base::openmode omode, std::error_code& ec, int perm = 0666) :
+ mHandle(InvalidHandle)
+ {
+ open(filename, omode, ec, perm);
+ }
+
+ explicit LLFile(const std::filesystem::path& file_path, std::ios_base::openmode omode, std::error_code& ec, int perm = 0666) :
+ mHandle(InvalidHandle)
+ {
+ open(file_path, omode, ec, perm);
+ }
+
+ /// destructor always attempts to close the file
+ ~LLFile() { close(); }
+ ///@}
+
+ // ================================================================================
+ /// @name operators
+ ///
+ ///@{
+ /// copy assignment deleted
+ LLFile& operator=(const LLFile&) = delete;
+
+ /// move assignment
+ LLFile& operator=(LLFile&& other) noexcept
+ {
+ close();
+ std::swap(mHandle, other.mHandle);
+ return *this;
+ }
+
+ // detect whether the wrapped file descriptor/handle is open or not
+ explicit operator bool() const { return (mHandle != InvalidHandle); }
+ bool operator!() { return (mHandle == InvalidHandle); }
+ ///@}
+
+ /// ================================================================================
+ /// @name class member methods
+ ///
+ /// These methods provide read and write support as well as additional functionality to query the size of
+ /// the file, change the position of the current file pointer or query it.
+ ///
+ /// Most of these functions take as one of their parameters a std::error_code object which can be used to
+ /// determine in more detail what error occurred if required
+ ///@{
+
+ /// Open a file with the specific open mode flags
+ inline int open(const char* filename, std::ios_base::openmode omode, std::error_code& ec, int perm = 0666)
+ {
+ std::filesystem::path file_path = fsyspath(filename);
+ return open(file_path, omode, ec, perm);
+ }
+ inline int open(const std::string& filename, std::ios_base::openmode omode, std::error_code& ec, int perm = 0666)
+ {
+ std::filesystem::path file_path = fsyspath(filename);
+ return open(file_path, omode, ec, perm);
+ }
+ int open(const std::filesystem::path& filename, std::ios_base::openmode omode, std::error_code& ec, int perm = 0666);
+ ///< @returns 0 on success, -1 on failure
+
+ /// Determine the size of the opened file
+ S64 size(std::error_code& ec);
+ ///< @returns the number of bytes in the file or 0 on failure
+
+ /// Query the position of the current file pointer in the file
+ S64 tell(std::error_code& ec);
+ ///< @returns the absolute offset of the file pointer in bytes relative to the start of the file or -1 on failure
+
+ /// Move the file pointer to the specified absolute position relative to the start of the file
+ int seek(S64 pos, std::error_code& ec);
+ ///< @returns 0 on success, -1 on failure
+
+ /// Move the file pointer to the specified position relative to dir
+ int seek(S64 offset, std::ios_base::seekdir dir, std::error_code& ec);
+ ///< @returns 0 on success, -1 on failure
+
+ /// Read the specified number of bytes into the buffer starting at the current file pointer
+ S64 read(void* buffer, S64 nbytes, std::error_code& ec);
+ ///< If the file ends before the requested amount of bytes could be read, the function succeeds and
+ /// returns the bytes up to the end of the file. The return value indicates the number of actually
+ /// read bytes and can be therefore smaller than the requested amount.
+ /// @returns the number of bytes read from the file or -1 on failure
+
+ /// Write the specified number of bytes to the file starting at the current file pointer
+ S64 write(const void* buffer, S64 nbytes, std::error_code& ec);
+ ///< @returns the number of bytes written to the file or -1 on failure
+
+ /// Write into the file starting at the current file pointer using printf style format and
+ /// additional optional parameters as specified in the fmt string
+ S64 printf(const char* fmt, ...);
+ ///< @returns the number of bytes written to the file or -1 on failure
+
+ /// Attempt to acquire or release a lock on the file
+ int lock(int lmode, std::error_code& ec);
+ ///< lmode can be one of LLFile::exclusive or LLFile::shared to acquire the according lock
+ /// or 0 to give up an earlier acquired lock. Adding LLFile::noblock together with one of
+ /// the lock requests will cause the function to fail if the lock can not be acquired,
+ /// otherwise the function will block until the lock can be acquired.
+ /// @returns 0 on success, -1 on failure
+
+ /// close the file explicitly
+ int close(std::error_code& ec);
+ ///< @returns 0 on success, -1 on failure
+
+ /// Convenience function to close the file without additional parameters
+ int close();
+ ///< @returns 0 on success, -1 on failure
+ ///@}
+
+ /// ================================================================================
+ /// @name static member functions
+ ///
+ /// These functions are static and operate with UTF8 filenames as one of their parameters.
+ ///
+ ///@{
/// open a file with the specified access mode
- static LLFILE* fopen(const std::string& filename, const char* accessmode); /* Flawfinder: ignore */
+ ///
+#if LL_WINDOWS
+ using fopen_flags_t = wchar_t;
+#else
+ using fopen_flags_t = char;
+#endif
+ inline static LLFILE* fopen(const char* filename, const fopen_flags_t* accessmode, int lmode = 0)
+ {
+ std::filesystem::path file_path = fsyspath(filename);
+ return LLFile::fopen(file_path, accessmode, lmode);
+ }
+ inline static LLFILE* fopen(const std::string& filename, const fopen_flags_t* accessmode, int lmode = 0)
+ {
+ std::filesystem::path file_path = fsyspath(filename);
+ return LLFile::fopen(file_path, accessmode, lmode);
+ }
+ static LLFILE* fopen(const std::filesystem::path& file_path, const fopen_flags_t* accessmode, int lmode = 0);
///< 'accessmode' follows the rules of the Posix fopen() mode parameter
- /// "r" open the file for reading only and positions the stream at the beginning
- /// "r+" open the file for reading and writing and positions the stream at the beginning
- /// "w" open the file for reading and writing and truncate it to zero length
- /// "w+" open or create the file for reading and writing and truncate to zero length if it existed
- /// "a" open the file for reading and writing and position the stream at the end of the file
- /// "a+" open or create the file for reading and writing and position the stream at the end of the file
+ /// "r" open the file for reading only and positions the stream at the beginning
+ /// "r+" open the file for reading and writing and positions the stream at the beginning
+ /// "w" open the file for reading and writing and truncate it to zero length
+ /// "w+" open or create the file for reading and writing and truncate to zero length if it existed
+ /// "a" open the file for reading and writing and before every write position the stream at the end of the file
+ /// "a+" open or create the file for reading and writing and before every write position the stream at the end of the file
+ ///
+ /// in addition to these values, "b" can be appended to indicate binary stream access, but on Linux and Mac
+ /// this is strictly for compatibility and has no effect. On Windows this makes the file functions not
+ /// try to translate line endings. Windows also allows to append "t" to indicate text mode. If neither
+ /// "b" or "t" is defined, Windows uses the value set by _fmode which by default is _O_TEXT.
+ /// This means that it is always a good idea to append "b" specifically for binary file access to
+ /// avoid corruption of the binary consistency of the data stream when reading or writing
+ /// Other characters in 'accessmode', while possible on some platforms (Windows), will usually
+ /// cause an error on other platforms as fopen will verify this parameter
///
- /// in addition to these values, "b" can be appended to indicate binary stream access, but on Linux and Mac
- /// this is strictly for compatibility and has no effect. On Windows this makes the file functions not
- /// try to translate line endings. Windows also allows to append "t" to indicate text mode. If neither
- /// "b" or "t" is defined, Windows uses the value set by _fmode which by default is _O_TEXT.
- /// This means that it is always a good idea to append "b" specifically for binary file access to
- /// avoid corruption of the binary consistency of the data stream when reading or writing
- /// Other characters in 'accessmode' will usually cause an error as fopen will verify this parameter
- /// @returns a valid LLFILE* pointer on success or NULL on failure
+ /// lmode is optional and allows to lock the file for other processes either as a shared lock or an
+ /// exclusive lock. If the requested lock conflicts with an already existing lock, the open fails.
+ /// Pass either LLFIle::exclusive or LLFile::shared to this parameter if you want to prevent other
+ /// processes from reading (exclusive lock) or writing (shared lock) to the file. It will always use
+ /// LLFile::noblock, meaning the open will immediately fail if it conflicts with an existing lock on the
+ /// file.
+ ///
+ /// @returns a valid LLFILE* pointer on success that can be passed to the fread() and fwrite() functions
+ /// and some other f<something> functions in the Standard C library that accept a FILE* as parameter
+ /// or NULL on failure
+ /// Close a file handle opened with fopen() above
static int close(LLFILE * file);
-
- /// retrieve the content of a file into a string
- static std::string getContents(const std::string& filename);
- ///< @returns the content of the file or an empty string on failure
+ ///< @returns 0 on success and -1 on failure.
/// create a directory
- 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 not an error.
- /// @returns 0 on success and -1 on failure.
-
- //// remove a directory
- static int rmdir(const std::string& filename, int suppress_error = 0);
- ///< pass ENOENT in the optional 'suppress_error' parameter
- /// if you don't want a warning in the log when the directory does not exist
+ inline static int mkdir(const char* dirname)
+ {
+ std::filesystem::path dir_path = fsyspath(dirname);
+ return mkdir(dir_path);
+ }
+ inline static int mkdir(const std::string& dirname)
+ {
+ std::filesystem::path dir_path = fsyspath(dirname);
+ return mkdir(dir_path);
+ }
+ static int mkdir(const std::filesystem::path& dirname);
+ ///< mkdir() considers "directory already exists" to be not an error.
/// @returns 0 on success and -1 on failure.
/// remove a file or directory
- static int remove(const std::string& filename, int suppress_error = 0);
- ///< pass ENOENT in the optional 'suppress_error' parameter
- /// if you don't want a warning in the log when the directory does not exist
+ inline static int remove(const char* filename, int suppress_warning = 0)
+ {
+ std::filesystem::path file_path = fsyspath(filename);
+ return remove(file_path, suppress_warning);
+ }
+ inline static int remove(const std::string& filename, int suppress_warning = 0)
+ {
+ std::filesystem::path file_path = fsyspath(filename);
+ return remove(file_path, suppress_warning);
+ }
+ static int remove(const std::filesystem::path& file_path, int suppress_warning = 0);
+ ///< pass an errno value (e.g., ENOENT) in the optional 'suppress_warning' parameter if you want to
+ /// suppress a warning in the log when the failure matches that errno (e.g., suppress warning if
+ /// the file or directory does not exist)
/// @returns 0 on success and -1 on failure.
/// rename a file
- static int rename(const std::string& filename, const std::string& newname, int suppress_error = 0);
+ inline static int rename(const char* filename, const char* newname, int suppress_warning = 0)
+ {
+ std::filesystem::path file_path = fsyspath(filename);
+ std::filesystem::path new_path = fsyspath(newname);
+ return rename(file_path, new_path, suppress_warning);
+ }
+ inline static int rename(const std::string& filename, const char* newname, int suppress_warning = 0)
+ {
+ std::filesystem::path file_path = fsyspath(filename);
+ std::filesystem::path new_path = fsyspath(newname);
+ return rename(file_path, new_path, suppress_warning);
+ }
+ inline static int rename(const char* filename, const std::string& newname, int suppress_warning = 0)
+ {
+ std::filesystem::path file_path = fsyspath(filename);
+ std::filesystem::path new_path = fsyspath(newname);
+ return rename(file_path, new_path, suppress_warning);
+ }
+ inline static int rename(const std::string& filename, const std::string& newname, int suppress_warning = 0)
+ {
+ std::filesystem::path file_path = fsyspath(filename);
+ std::filesystem::path new_path = fsyspath(newname);
+ return rename(file_path, new_path, suppress_warning);
+ }
+ static int rename(const std::filesystem::path& file_path, const std::filesystem::path& new_path, int suppress_warning = 0);
///< it will silently overwrite newname if it exists without returning an error
/// Posix guarantees that if newname already exists, then there will be no moment
/// in which for other processes newname does not exist. There is no such guarantee
- /// under Windows at this time. It may do it in the same way but the used Windows API
- /// does not make such guarantees.
+ /// under Windows at this time. It may do it in the same way but the used Windows
+ /// APIs do not make such guarantees.
/// @returns 0 on success and -1 on failure.
+ /// copy the contents of the file from 'source' to 'target'
+ inline static bool copy(const char* source, const char* target)
+ {
+ std::error_code ec;
+ return copy(source, target, std::filesystem::copy_options::overwrite_existing, ec);
+ }
+ inline static bool copy(const char* source, const std::string& target)
+ {
+ std::error_code ec;
+ return copy(source, target, std::filesystem::copy_options::overwrite_existing, ec);
+ }
+ inline static bool copy(const std::string& source, const char* target)
+ {
+ std::error_code ec;
+ return copy(source, target, std::filesystem::copy_options::overwrite_existing, ec);
+ }
+ inline static bool copy(const std::string& source, const std::string& target)
+ {
+ std::error_code ec;
+ return copy(source, target, std::filesystem::copy_options::overwrite_existing, ec);
+ }
+ inline static bool copy(const std::filesystem::path& source_path, const std::filesystem::path& target_path)
+ {
+ std::error_code ec;
+ return copy(source_path, target_path, std::filesystem::copy_options::overwrite_existing, ec);
+ }
+ ///< Copies the contents of the file 'source' to the file 'target', overwriting 'target' if it already
+ /// existed.
+ /// This is a convenience function that implements the previous behavior of silently overwriting an
+ /// already existing target file. Consider using the function below if you desire a different
+ /// behavior when the target file already exists
+ /// @returns true on success and false on failure.
+
+ /// copy the contents of the file from 'from' to 'to'
+ inline static bool copy(const char* source, const char* target, std::filesystem::copy_options options, std::error_code& ec)
+ {
+ std::filesystem::path source_path = fsyspath(source);
+ std::filesystem::path target_path = fsyspath(target);
+ return copy(source_path, target_path, options, ec);
+ }
+ inline static bool copy(const char* source, const std::string& target, std::filesystem::copy_options options, std::error_code& ec)
+ {
+ std::filesystem::path source_path = fsyspath(source);
+ std::filesystem::path target_path = fsyspath(target);
+ return copy(source_path, target_path, options, ec);
+ }
+ inline static bool copy(const std::string& source, const char* target, std::filesystem::copy_options options, std::error_code& ec)
+ {
+ std::filesystem::path source_path = fsyspath(source);
+ std::filesystem::path target_path = fsyspath(target);
+ return copy(source_path, target_path, options, ec);
+ }
+ inline static bool copy(const std::string& source, const std::string& target, std::filesystem::copy_options options, std::error_code& ec)
+ {
+ std::filesystem::path source_path = fsyspath(source);
+ std::filesystem::path target_path = fsyspath(target);
+ return copy(source_path, target_path, options, ec);
+ }
+ static bool copy(const std::filesystem::path& source_path, const std::filesystem::path& target_path, std::filesystem::copy_options options, std::error_code& ec);
+ ///< Copies the contents of the file 'source' to the file 'target'. The options parameter allows to
+ /// specify what should happen if the "target" file already exists:
+ /// std::filesystem::copy_options::none - return an error in ec and fail
+ /// std::filesystem::copy_options::skip_existing - skip the operation and do not overwrite file
+ /// std::filesystem::copy_options::overwrite_existing - overwrite the file
+ /// std::filesystem::copy_options::update_existing - overwrite the file only if it is older than the file being copied
+ /// @returns true on success and false on failure.
+
+ /// retrieve the content of a file into a string
+ inline static std::string getContents(const char* filename)
+ {
+ std::error_code ec;
+ return getContents(filename, ec);
+ }
+ inline static std::string getContents(const std::string& filename)
+ {
+ std::error_code ec;
+ return getContents(filename, ec);
+ }
+ inline static std::string getContents(const std::filesystem::path& file_path)
+ {
+ std::error_code ec;
+ return getContents(file_path, ec);
+ }
+ inline static std::string getContents(const char* filename, std::error_code& ec)
+ {
+ std::filesystem::path file_path = fsyspath(filename);
+ return getContents(file_path, ec);
+ }
+ inline static std::string getContents(const std::string& filename, std::error_code& ec)
+ {
+ std::filesystem::path file_path = fsyspath(filename);
+ return getContents(file_path, ec);
+ }
+ static std::string getContents(const std::filesystem::path& file_path, std::error_code& ec);
+ ///< @returns the entire content of the file as std::string or an empty string on failure
+
+ /// read nBytes from the file into the buffer, starting at offset in the file
+ inline static S64 read(const char* filename, void* buf, S64 offset, S64 nbytes)
+ {
+ std::error_code ec;
+ return read(filename, buf, offset, nbytes, ec);
+ }
+ inline static S64 read(const std::string& filename, void* buf, S64 offset, S64 nbytes)
+ {
+ std::error_code ec;
+ return read(filename, buf, offset, nbytes, ec);
+ }
+ inline static S64 read(const std::filesystem::path& file_path, void* buf, S64 offset, S64 nbytes)
+ {
+ std::error_code ec;
+ return read(file_path, buf, offset, nbytes, ec);
+ }
+ inline static S64 read(const char* filename, void* buf, S64 offset, S64 nbytes, std::error_code& ec)
+ {
+ std::filesystem::path file_path = fsyspath(filename);
+ return read(file_path, buf, offset, nbytes, ec);
+ }
+ inline static S64 read(const std::string& filename, void* buf, S64 offset, S64 nbytes, std::error_code& ec)
+ {
+ std::filesystem::path file_path = fsyspath(filename);
+ return read(file_path, buf, offset, nbytes, ec);
+ }
+ static S64 read(const std::filesystem::path& filename, void* buf, S64 offset, S64 nbytes, std::error_code& ec);
+ ///< @returns bytes read on success, or -1 on failure
- /// copy the contents of file from 'from' to 'to' filename
- static bool copy(const std::string& from, const std::string& to);
- ///< @returns true on success and false on failure.
+ /// write nBytes from the buffer into the file, starting at offset in the file
+ inline static S64 write(const std::string& filename, const void* buf, S64 offset, S64 nbytes)
+ {
+ std::error_code ec;
+ std::filesystem::path file_path = fsyspath(filename);
+ return write(file_path, buf, offset, nbytes, ec);
+ }
+ inline static S64 write(const std::filesystem::path& filename, const void* buf, S64 offset, S64 nbytes)
+ {
+ std::error_code ec;
+ std::filesystem::path file_path = fsyspath(filename);
+ return write(file_path, buf, offset, nbytes, ec);
+ }
+ inline static S64 write(const std::string& filename, const void* buf, S64 offset, S64 nbytes, std::error_code& ec)
+ {
+ std::filesystem::path file_path = fsyspath(filename);
+ return write(file_path, buf, offset, nbytes, ec);
+ }
+ static S64 write(const std::filesystem::path& filename, const void* buf, S64 offset, S64 nbytes, std::error_code& ec);
+ ///< If a negative offset is provided, the file is opened in append mode and the
+ /// write will be appended to the end of the file.
+ /// @returns bytes written on success, or -1 on failure
/// return the file stat structure for filename
- static int stat(const std::string& filename, llstat* file_status, int suppress_error = ENOENT);
+ inline static int stat(const char* filename, llstat* file_status, const char* operation = nullptr, int suppress_warning = ENOENT)
+ {
+ std::filesystem::path file_path = fsyspath(filename);
+ return stat(file_path, file_status, operation, suppress_warning);
+ }
+ inline static int stat(const std::string& filename, llstat* file_status, const char* operation = nullptr, int suppress_warning = ENOENT)
+ {
+ std::filesystem::path file_path = fsyspath(filename);
+ return stat(file_path, file_status, operation, suppress_warning);
+ }
+ static int stat(const std::filesystem::path& file_path, llstat* file_status, const char *operation = nullptr, int suppress_warning = ENOENT);
///< for compatibility with existing uses of LL_File::stat() we use ENOENT as default in the
- /// optional 'suppress_error' parameter to avoid spamming the log with warnings when the API
+ /// optional 'suppress_warning' parameter to avoid spamming the log with warnings when the API
/// is used to detect if a file exists
/// @returns 0 on success and -1 on failure.
- /// get the file or directory attributes for filename
- static unsigned short getattr(const std::string& filename, bool dontFollowSymLink = false, int suppress_error = ENOENT);
- ///< a more lightweight function on Windows to stat, that just returns the file attribute flags
- /// dontFollowSymLinks set to true returns the attributes of the symlink if it is one, rather than resolving it
- /// we pass by default ENOENT in the optional 'suppress_error' parameter to not spam the log with
- /// warnings when the file or directory does not exist
- /// @returns 0 on failure and a st_mode value with either S_IFDIR or S_IFREG set otherwise
- /// together with the three access bits which under Windows only the write bit is relevant.
+ /// get the std::filesystem::file_status for filename
+ inline static std::filesystem::file_status getStatus(const char* filename, bool dontFollowSymLink = false, int suppress_warning = ENOENT)
+ {
+ std::filesystem::path file_path = fsyspath(filename);
+ return getStatus(file_path, dontFollowSymLink, suppress_warning);
+ }
+ inline static std::filesystem::file_status getStatus(const std::string& filename, bool dontFollowSymLink = false, int suppress_warning = ENOENT)
+ {
+ std::filesystem::path file_path = fsyspath(filename);
+ return getStatus(file_path, dontFollowSymLink, suppress_warning);
+ }
+ static std::filesystem::file_status getStatus(const std::filesystem::path& file_path, bool dontFollowSymLink = false, int suppress_warning = ENOENT);
+ ///< dontFollowSymLinks set to true returns the std::filesystem::file_status of the symlink if it
+ /// is one, rather than resolving it. We pass by default ENOENT in the optional 'suppress_warning'
+ /// parameter to not spam the log with warnings when the file or directory does not exist
+ /// @returns a std::filesystem::file_status value that can be passed to the appropriate std::filesystem::exists()
+ /// and other APIs accepting a file_status.
+
+ /// get the size of a file in bytes
+ inline static S64 size(const char* filename, int suppress_warning = ENOENT)
+ {
+ std::filesystem::path file_path = fsyspath(filename);
+ return size(file_path, suppress_warning);
+ }
+ inline static S64 size(const std::string& filename, int suppress_warning = ENOENT)
+ {
+ std::filesystem::path file_path = fsyspath(filename);
+ return size(file_path, suppress_warning);
+ }
+ static S64 size(const std::filesystem::path& file_path, int suppress_warning = ENOENT);
+ ///< we pass by default ENOENT in the optional 'suppress_warning' parameter to not spam
+ /// the log with warnings when the file does not exist
+ /// @returns the file size on success or 0 on failure.
+
+ /// check if filename is an existing file or directory
+ inline static bool exists(const char* filename)
+ {
+ std::filesystem::file_status status = getStatus(filename);
+ return std::filesystem::exists(status);
+ }
+ inline static bool exists(const std::string& filename)
+ {
+ std::filesystem::file_status status = getStatus(filename);
+ return std::filesystem::exists(status);
+ }
+ inline static bool exists(const std::filesystem::path& file_path)
+ {
+ std::filesystem::file_status status = getStatus(file_path);
+ return std::filesystem::exists(status);
+ }
+ ///< @returns true if the path is for an existing file or directory
/// check if filename is an existing directory
- static bool isdir(const std::string& filename);
+ inline static bool isdir(const char* filename)
+ {
+ std::filesystem::file_status status = getStatus(filename);
+ return std::filesystem::is_directory(status);
+ }
+ inline static bool isdir(const std::string& filename)
+ {
+ std::filesystem::file_status status = getStatus(filename);
+ return std::filesystem::is_directory(status);
+ }
+ inline static bool isdir(const std::filesystem::path& file_path)
+ {
+ std::filesystem::file_status status = getStatus(file_path);
+ return std::filesystem::is_directory(status);
+ }
///< @returns true if the path is for an existing directory
/// check if filename is an existing file
- static bool isfile(const std::string& filename);
+ inline static bool isfile(const char* filename)
+ {
+ std::filesystem::file_status status = getStatus(filename);
+ return std::filesystem::is_regular_file(status);
+ }
+ inline static bool isfile(const std::string& filename)
+ {
+ std::filesystem::file_status status = getStatus(filename);
+ return std::filesystem::is_regular_file(status);
+ }
+ inline static bool isfile(const std::filesystem::path& file_path)
+ {
+ std::filesystem::file_status status = getStatus(file_path);
+ return std::filesystem::is_regular_file(status);
+ }
///< @returns true if the path is for an existing file
/// check if filename is a symlink
- static bool islink(const std::string& filename);
+ inline static bool islink(const char* filename)
+ {
+ std::filesystem::file_status status = getStatus(filename, true);
+ return std::filesystem::is_symlink(status);
+ }
+ inline static bool islink(const std::string& filename)
+ {
+ std::filesystem::file_status status = getStatus(filename, true);
+ return std::filesystem::is_symlink(status);
+ }
+ inline static bool islink(const std::filesystem::path& file_path)
+ {
+ std::filesystem::file_status status = getStatus(file_path, true);
+ return std::filesystem::is_symlink(status);
+ }
///< @returns true if the path is pointing at a symlink
/// return a path to the temporary directory on the system
- static const char * tmpdir();
+ static const std::string& tmpdir();
+
+private:
+#if LL_WINDOWS
+ typedef HANDLE llfile_handle_t;
+ const llfile_handle_t InvalidHandle = INVALID_HANDLE_VALUE;
+ llfile_handle_t mHandle = INVALID_HANDLE_VALUE; // The file handle/descriptor
+#else
+ typedef int llfile_handle_t;
+ const llfile_handle_t InvalidHandle = -1;
+ llfile_handle_t mHandle = -1; // The file handle/descriptor
+#endif
+
+ std::ios_base::openmode mOpen{}; // Used to emulate std::ios_base::app under Windows
+};
+
+#if LL_WINDOWS
+/**
+ * @brief Controlling input for files.
+ *
+ * This class supports reading from named files, using the inherited
+ * functions from std::ifstream. The only added value is that our constructor
+ * Does The Right Thing when passed a non-ASCII pathname. Sadly, that isn't
+ * true of Microsoft's std::ifstream.
+ */
+class llifstream : public std::ifstream
+{
+ // 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).
+ */
+ llifstream() = default;
+
+ /**
+ * @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 char* _Filename,
+ ios_base::openmode _Mode = ios_base::in);
+ explicit llifstream(const std::string& _Filename,
+ ios_base::openmode _Mode = ios_base::in);
+ explicit llifstream(const std::filesystem::path& _Filepath,
+ 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.
+ */
+ void open(const char* _Filename,
+ ios_base::openmode _Mode = ios_base::in);
+ void open(const std::string& _Filename,
+ ios_base::openmode _Mode = ios_base::in);
+ void open(const std::filesystem::path& _Filepath,
+ ios_base::openmode _Mode = ios_base::in);
};
/// RAII class
@@ -169,22 +748,19 @@ class LLUniqueFile
{
public:
// empty
- LLUniqueFile(): mFileHandle(nullptr) {}
+ LLUniqueFile() = default;
// wrap (e.g.) result of LLFile::fopen()
- LLUniqueFile(LLFILE* f): mFileHandle(f) {}
+ LLUniqueFile(LLFILE* f) : mFileHandle(f) {}
// no copy
LLUniqueFile(const LLUniqueFile&) = delete;
// move construction
LLUniqueFile(LLUniqueFile&& other) noexcept
{
- mFileHandle = other.mFileHandle;
+ mFileHandle = other.mFileHandle;
other.mFileHandle = nullptr;
}
// The point of LLUniqueFile is to close on destruction.
- ~LLUniqueFile()
- {
- close();
- }
+ ~LLUniqueFile() { close(); }
// simple assignment
LLUniqueFile& operator=(LLFILE* f)
@@ -209,7 +785,7 @@ public:
if (mFileHandle)
{
// in case close() throws, set mFileHandle null FIRST
- LLFILE* h{nullptr};
+ LLFILE* h{ nullptr };
std::swap(h, mFileHandle);
LLFile::close(h);
}
@@ -217,62 +793,16 @@ public:
// detect whether the wrapped LLFILE is open or not
explicit operator bool() const { return bool(mFileHandle); }
- bool operator!() { return ! mFileHandle; }
+ bool operator!() { return !mFileHandle; }
// LLUniqueFile should be usable for any operation that accepts LLFILE*
// (or FILE* for that matter)
operator LLFILE*() const { return mFileHandle; }
private:
- LLFILE* mFileHandle;
-};
-
-#if LL_WINDOWS
-/**
- * @brief Controlling input for files.
- *
- * This class supports reading from named files, using the inherited
- * functions from std::ifstream. The only added value is that our constructor
- * 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
-{
- // 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).
- */
- llifstream();
-
- /**
- * @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,
- 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.
- */
- void open(const std::string& _Filename,
- ios_base::openmode _Mode = ios_base::in);
+ LLFILE* mFileHandle = nullptr;
};
-
/**
* @brief Controlling output for files.
*
@@ -281,7 +811,7 @@ 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 llofstream : public std::ofstream
{
public:
// Constructors:
@@ -292,7 +822,7 @@ class LL_COMMON_API llofstream : public std::ofstream
* @c &sb to the base class initializer. Does not open any files
* (you haven't given it a filename to open).
*/
- llofstream();
+ llofstream() = default;
/**
* @brief Create an output file stream.
@@ -301,8 +831,12 @@ class LL_COMMON_API llofstream : public std::ofstream
*
* @c ios_base::out is automatically included in @a mode.
*/
+ explicit llofstream(const char* _Filename,
+ ios_base::openmode _Mode = ios_base::out|ios_base::trunc);
explicit llofstream(const std::string& _Filename,
ios_base::openmode _Mode = ios_base::out|ios_base::trunc);
+ explicit llofstream(const std::filesystem::path& _Filepath,
+ ios_base::openmode _Mode = ios_base::out|ios_base::trunc);
/**
* @brief Opens an external file.
@@ -311,21 +845,14 @@ class LL_COMMON_API llofstream : public std::ofstream
*
* @c ios_base::out is automatically included in @a mode.
*/
+ void open(const char* _Filename,
+ ios_base::openmode _Mode = ios_base::out|ios_base::trunc);
void open(const std::string& _Filename,
ios_base::openmode _Mode = ios_base::out|ios_base::trunc);
+ void open(const std::filesystem::path& _Filepath,
+ ios_base::openmode _Mode = ios_base::out|ios_base::trunc);
};
-
-/**
- * @brief filesize helpers.
- *
- * The file size helpers are not considered particularly efficient,
- * and should only be used for config files and the like -- not in a
- * loop.
- */
-std::streamsize LL_COMMON_API llifstream_size(llifstream& fstr);
-std::streamsize LL_COMMON_API llofstream_size(llofstream& fstr);
-
#else // ! LL_WINDOWS
// on non-windows, llifstream and llofstream are just mapped directly to the std:: equivalents
diff --git a/indra/llcommon/llfindlocale.cpp b/indra/llcommon/llfindlocale.cpp
index b4bcc80ac4..d606a6fdb9 100644
--- a/indra/llcommon/llfindlocale.cpp
+++ b/indra/llcommon/llfindlocale.cpp
@@ -134,7 +134,7 @@ accumulate_locstring(const char *str, FL_Locale *l) {
return 0;
}
-
+#ifndef LL_WINDOWS
static int
accumulate_env(const char *name, FL_Locale *l) {
char *env;
@@ -148,7 +148,7 @@ accumulate_env(const char *name, FL_Locale *l) {
free(lang); free(country); free(variant);
return 0;
}
-
+#endif
static void
canonise_fl(FL_Locale *l) {
diff --git a/indra/llcommon/llfixedbuffer.h b/indra/llcommon/llfixedbuffer.h
index 1234d2014f..d2cd660c18 100644
--- a/indra/llcommon/llfixedbuffer.h
+++ b/indra/llcommon/llfixedbuffer.h
@@ -27,7 +27,7 @@
#ifndef LL_LLFIXEDBUFFER_H
#define LL_LLFIXEDBUFFER_H
-#include "timer.h"
+#include "lltimer.h"
#include <deque>
#include <string>
#include "llstring.h"
diff --git a/indra/llcommon/llinstancetracker.h b/indra/llcommon/llinstancetracker.h
index 92b26354a1..734365767d 100644
--- a/indra/llcommon/llinstancetracker.h
+++ b/indra/llcommon/llinstancetracker.h
@@ -28,6 +28,8 @@
#ifndef LL_LLINSTANCETRACKER_H
#define LL_LLINSTANCETRACKER_H
+#include "llpreprocessor.h"
+
#include <map>
#include <set>
#include <vector>
@@ -35,7 +37,7 @@
#include <memory>
#include <type_traits>
-#include "mutex.h"
+#include <mutex>
#include <boost/iterator/transform_iterator.hpp>
#include <boost/iterator/indirect_iterator.hpp>
@@ -244,6 +246,7 @@ public:
}
protected:
+ LL_UBSAN_SUPRESS_VPTR
LLInstanceTracker(const KEY& key)
{
// We do not intend to manage the lifespan of this object with
@@ -482,6 +485,7 @@ public:
using key_snapshot_of = instance_snapshot_of<SUBCLASS>;
protected:
+ LL_UBSAN_SUPRESS_VPTR
LLInstanceTracker()
{
// Since we do not intend for this shared_ptr to manage lifespan, give
diff --git a/indra/llcommon/llkeybind.cpp b/indra/llcommon/llkeybind.cpp
index e36c1d0a4c..abb0df0b87 100644
--- a/indra/llcommon/llkeybind.cpp
+++ b/indra/llcommon/llkeybind.cpp
@@ -114,15 +114,6 @@ void LLKeyData::reset()
mIgnoreMasks = false;
}
-LLKeyData& LLKeyData::operator=(const LLKeyData& rhs)
-{
- mMouse = rhs.mMouse;
- mKey = rhs.mKey;
- mMask = rhs.mMask;
- mIgnoreMasks = rhs.mIgnoreMasks;
- return *this;
-}
-
bool LLKeyData::operator==(const LLKeyData& rhs)
{
if (mMouse != rhs.mMouse) return false;
diff --git a/indra/llcommon/llkeybind.h b/indra/llcommon/llkeybind.h
index 1bbb2fadb5..0a9d4600cc 100644
--- a/indra/llcommon/llkeybind.h
+++ b/indra/llcommon/llkeybind.h
@@ -43,7 +43,6 @@ public:
bool isEmpty() const;
bool empty() const { return isEmpty(); };
void reset();
- LLKeyData& operator=(const LLKeyData& rhs);
bool operator==(const LLKeyData& rhs);
bool operator!=(const LLKeyData& rhs);
diff --git a/indra/llcommon/llmd5.cpp b/indra/llcommon/llmd5.cpp
index c8ca586e7f..b05fd2e21b 100644
--- a/indra/llcommon/llmd5.cpp
+++ b/indra/llcommon/llmd5.cpp
@@ -255,13 +255,15 @@ void LLMD5::raw_digest(unsigned char* s) const
memcpy(s, digest, 16); /* Flawfinder: ignore */
}
-#if LL_DARWIN
+#if LL_CLANG
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif
void LLMD5::hex_digest(char* s) const
{
+ if(!s) return;
+
if (!finalized)
{
std::cerr << "LLMD5::hex_digest: Can't get digest if you haven't "
@@ -278,7 +280,7 @@ void LLMD5::hex_digest(char* s) const
s[32] = '\0';
}
-#if LL_DARWIN
+#if LL_CLANG
#pragma clang diagnostic pop
#endif
diff --git a/indra/llcommon/llmemory.cpp b/indra/llcommon/llmemory.cpp
index ba48319a16..9617e30f0b 100644
--- a/indra/llcommon/llmemory.cpp
+++ b/indra/llcommon/llmemory.cpp
@@ -146,9 +146,6 @@ void LLMemory::updateMemoryInfo()
LL_WARNS() << "task_info failed" << LL_ENDL;
}
#elif defined(LL_LINUX)
- // Use sysinfo() to get the total physical memory.
- struct sysinfo info;
- sysinfo(&info);
sAllocatedMemInKB = U32Kilobytes::convert(U64Bytes(LLMemory::getCurrentRSS())); // represents the RAM allocated by this process only (in line with the windows implementation)
#else
//not valid for other systems for now.
diff --git a/indra/llcommon/llmemory.h b/indra/llcommon/llmemory.h
index adc556d180..a25387d321 100644
--- a/indra/llcommon/llmemory.h
+++ b/indra/llcommon/llmemory.h
@@ -48,6 +48,9 @@ class LLMutex ;
#define LL_ALIGN_OF __align_of__
#endif
+#if defined(LL_X86_64) || defined(LL_ARM64)
+#define LL_DEFAULT_HEAP_ALIGN 16
+#else
#if LL_WINDOWS
#define LL_DEFAULT_HEAP_ALIGN 8
#elif LL_DARWIN
@@ -55,7 +58,7 @@ class LLMutex ;
#elif LL_LINUX
#define LL_DEFAULT_HEAP_ALIGN 8
#endif
-
+#endif
LL_COMMON_API void ll_assert_aligned_func(uintptr_t ptr,U32 alignment);
@@ -89,22 +92,6 @@ template <typename T> T* LL_NEXT_ALIGNED_ADDRESS_64(T* address)
(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) \
@@ -127,7 +114,6 @@ public: \
ll_aligned_free_16(ptr); \
}
-
//------------------------------------------------------------------------------------------------
//------------------------------------------------------------------------------------------------
// for enable buffer overrun detection predefine LL_DEBUG_BUFFER_OVERRUN in current library
@@ -143,6 +129,10 @@ public: \
LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY;
#if defined(LL_WINDOWS)
void* ret = _aligned_malloc(size, align);
+ #elif defined(LL_LINUX)
+ void *ret;
+ if (0 != posix_memalign(&ret, align, size))
+ return nullptr;
#else
char* aligned = NULL;
void* mem = malloc( size + (align - 1) + sizeof(void*) );
@@ -165,6 +155,8 @@ public: \
LL_PROFILE_FREE(ptr);
#if defined(LL_WINDOWS)
_aligned_free(ptr);
+ #elif defined(LL_LINUX)
+ free(ptr);
#else
if (ptr)
{
@@ -179,10 +171,10 @@ public: \
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)
+#if LL_DEFAULT_HEAP_ALIGN == 16
+ void* ret = malloc(size); // default osx and 64-bit malloc is 16 byte aligned.
+#elif 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))
@@ -196,10 +188,10 @@ inline void ll_aligned_free_16(void *p)
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY;
LL_PROFILE_FREE(p);
-#if defined(LL_WINDOWS)
+#if LL_DEFAULT_HEAP_ALIGN == 16
+ free(p);
+#elif defined(LL_WINDOWS)
_aligned_free(p);
-#elif defined(LL_DARWIN)
- return free(p);
#else
free(p); // posix_memalign() is compatible with heap deallocator
#endif
@@ -209,10 +201,10 @@ inline void* ll_aligned_realloc_16(void* ptr, size_t size, size_t old_size) // r
{
LL_PROFILE_ZONE_SCOPED_CATEGORY_MEMORY;
LL_PROFILE_FREE(ptr);
-#if defined(LL_WINDOWS)
+#if LL_DEFAULT_HEAP_ALIGN == 16
+ void* ret = realloc(ptr,size); // default osx and 64bit malloc is 16 byte aligned.
+#elif 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);
diff --git a/indra/llcommon/llmutex.h b/indra/llcommon/llmutex.h
index f3615a1270..bc3d8fed06 100644
--- a/indra/llcommon/llmutex.h
+++ b/indra/llcommon/llmutex.h
@@ -30,7 +30,7 @@
#include "stdtypes.h"
#include "llthread.h"
-#include "mutex.h"
+#include <mutex>
#include <shared_mutex>
#include <unordered_map>
#include <condition_variable>
diff --git a/indra/llcommon/llpreprocessor.h b/indra/llcommon/llpreprocessor.h
index b2a9734548..104d3cc186 100644
--- a/indra/llcommon/llpreprocessor.h
+++ b/indra/llcommon/llpreprocessor.h
@@ -61,35 +61,48 @@
# define LL_UNLIKELY(EXPR) (EXPR)
#endif
-
// Figure out differences between compilers
-#if defined(__GNUC__)
+#if defined(__MSVC_VER__) || defined(_MSC_VER)
+ #ifndef LL_MSVC
+ #define LL_MSVC 1
+ #endif
+
+ // Clang CL is MSVC-like but also supports its own macros
+ #if defined(__clang__)
+ #ifndef LL_CLANG
+ #define LL_CLANG 1
+ #endif
+ #endif
+#elif defined(__clang__)
+ #define CLANG_VERSION (__clang_major__ * 10000 \
+ + __clang_minor__ * 100 \
+ + __clang_patchlevel__)
+ #ifndef LL_CLANG
+ #define LL_CLANG 1
+ #endif
+#elif defined(__GNUC__)
#define GCC_VERSION (__GNUC__ * 10000 \
+ __GNUC_MINOR__ * 100 \
+ __GNUC_PATCHLEVEL__)
#ifndef LL_GNUC
#define LL_GNUC 1
#endif
-#elif defined(__MSVC_VER__) || defined(_MSC_VER)
- #ifndef LL_MSVC
- #define LL_MSVC 1
- #endif
#endif
// Set up CPU architecture defines
#if LL_MSVC && defined(_M_ARM64)
# define LL_ARM64 1
-#elif LL_GNUC && (defined(__arm64__) || defined(__aarch64__))
+#elif (LL_GNUC || LL_CLANG) && (defined(__arm64__) || defined(__aarch64__))
# define LL_ARM64 1
#elif LL_MSVC && _M_X64
# define LL_X86_64 1
# define LL_X86 1
#elif LL_MSVC && _M_IX86
# define LL_X86 1
-#elif LL_GNUC && ( defined(__amd64__) || defined(__x86_64__) )
+#elif (LL_GNUC || LL_CLANG) && ( defined(__amd64__) || defined(__x86_64__) )
# define LL_X86_64 1
# define LL_X86 1
-#elif LL_GNUC && ( defined(__i386__) )
+#elif (LL_GNUC || LL_CLANG) && ( defined(__i386__) )
# define LL_X86 1
#endif
@@ -106,16 +119,6 @@
#endif
-// Although thread_local is now a standard storage class, we can't just
-// #define LL_THREAD_LOCAL as thread_local because the *usage* is different.
-// We'll have to take the time to change LL_THREAD_LOCAL declarations by hand.
-#if LL_WINDOWS
-# define LL_THREAD_LOCAL __declspec(thread)
-#else
-# define LL_THREAD_LOCAL __thread
-#endif
-
-
#if defined(LL_WINDOWS)
#define BOOST_REGEX_NO_LIB 1
#define CURL_STATICLIB 1
@@ -140,7 +143,7 @@
#define LL_DLLIMPORT
#endif // LL_WINDOWS
-#if __clang__ || ! defined(LL_WINDOWS)
+#if LL_CLANG || ! defined(LL_WINDOWS)
// Only on Windows, and only with the Microsoft compiler (vs. clang) is
// wchar_t potentially not a distinct type.
#define LL_WCHAR_T_NATIVE 1
@@ -201,6 +204,13 @@
#define LL_PRETTY_FUNCTION __PRETTY_FUNCTION__
#endif
+// vptr warning supression funtionality for undefined behavior sanitizer
+#if LL_CLANG || LL_GNUC
+# define LL_UBSAN_SUPRESS_VPTR __attribute__((no_sanitize("vptr")))
+#else
+# define LL_UBSAN_SUPRESS_VPTR
+#endif
+
#if LL_ARM64
#define GLM_FORCE_NEON 1
#else
diff --git a/indra/llcommon/llprocessor.cpp b/indra/llcommon/llprocessor.cpp
index 0778b123ea..9d0ef45bee 100644
--- a/indra/llcommon/llprocessor.cpp
+++ b/indra/llcommon/llprocessor.cpp
@@ -783,6 +783,20 @@ private:
};
#elif LL_LINUX
+
+// *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]);}
+
+#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);} \
+ }
+
const char CPUINFO_FILE[] = "/proc/cpuinfo";
class LLProcessorInfoLinuxImpl : public LLProcessorInfoImpl
@@ -794,12 +808,36 @@ public:
}
virtual ~LLProcessorInfoLinuxImpl() {}
+
private:
+ F64 getCPUMaxMHZ()
+ {
+ // Nicky: We just look into cpu0. In theory we could iterate over all cores
+ // "/sys/devices/system/cpu/cpu*/cpufreq/cpuinfo_max_freq"
+ // But those should not fluctuate that much?
+ llifstream fIn{ "/sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq" };
+
+ if( !fIn.is_open() )
+ return 0.0;
+
+ std::string strLine;
+ fIn >> strLine;
+ if( strLine.empty() )
+ return 0.0l;
+
+ F64 mhz {};
+ if( !LLStringUtil::convertToF64(strLine, mhz ) )
+ return 0.0;
+
+ mhz = mhz / 1000.0;
+ return mhz;
+ }
+
void get_proc_cpuinfo()
{
std::map< std::string, std::string > cpuinfo;
- LLFILE* cpuinfo_fp = LLFile::fopen(CPUINFO_FILE, "rb");
+ LLFILE* cpuinfo_fp = LLFile::fopen(CPUINFO_FILE, LLFILE_MODE("rb"));
if(cpuinfo_fp)
{
char line[MAX_STRING];
@@ -830,24 +868,17 @@ private:
}
# 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]);}
-
-#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 mhzFromSys = getCPUMaxMHZ();
+ F64 mhzFromProc {};
+ if( !LLStringUtil::convertToF64(cpuinfo["cpu mhz"], mhzFromProc ) )
+ mhzFromProc = 0.0;
+ if (mhzFromSys > 1.0 && mhzFromSys > mhzFromProc )
+ {
+ setInfo( eFrequency, mhzFromSys );
}
-
- F64 mhz;
- if (LLStringUtil::convertToF64(cpuinfo["cpu mhz"], mhz)
- && 200.0 < mhz && mhz < 10000.0)
+ else if ( 200.0 < mhzFromProc && mhzFromProc < 10000.0)
{
- setInfo(eFrequency,(F64)(mhz));
+ setInfo(eFrequency,(F64)(mhzFromProc));
}
LLPI_SET_INFO_STRING(eBrandName, "model name");
@@ -857,7 +888,7 @@ private:
LLPI_SET_INFO_INT(eModel, "model");
- S32 family;
+ S32 family{};
if (!cpuinfo["cpu family"].empty()
&& LLStringUtil::convertToS32(cpuinfo["cpu family"], family))
{
@@ -923,7 +954,7 @@ private:
std::ostringstream s;
// *NOTE:Mani - This is for linux only.
- LLFILE* cpuinfo = LLFile::fopen(CPUINFO_FILE, "rb");
+ LLFILE* cpuinfo = LLFile::fopen(CPUINFO_FILE, LLFILE_MODE("rb"));
if(cpuinfo)
{
char line[MAX_STRING];
diff --git a/indra/llcommon/llprocinfo.cpp b/indra/llcommon/llprocinfo.cpp
index 69be00e14a..9fe4af2103 100644
--- a/indra/llcommon/llprocinfo.cpp
+++ b/indra/llcommon/llprocinfo.cpp
@@ -25,6 +25,7 @@
* $/LicenseInfo$
*/
+#include "linden_common.h"
#include "llprocinfo.h"
diff --git a/indra/llcommon/llprofiler.cpp b/indra/llcommon/llprofiler.cpp
index bdddabf977..b2ac7723d4 100644
--- a/indra/llcommon/llprofiler.cpp
+++ b/indra/llcommon/llprofiler.cpp
@@ -25,6 +25,6 @@
* $/LicenseInfo$
*/
-#include "linden_common.h"
+#include "llpreprocessor.h"
#include "TracyClient.cpp"
diff --git a/indra/llcommon/llprofiler.h b/indra/llcommon/llprofiler.h
index 5fb32d6280..7c77d94475 100644
--- a/indra/llcommon/llprofiler.h
+++ b/indra/llcommon/llprofiler.h
@@ -76,7 +76,16 @@
#if defined(LL_PROFILER_CONFIGURATION) && (LL_PROFILER_CONFIGURATION > LL_PROFILER_CONFIG_NONE)
#if LL_PROFILER_CONFIGURATION == LL_PROFILER_CONFIG_TRACY || LL_PROFILER_CONFIGURATION == LL_PROFILER_CONFIG_TRACY_FAST_TIMER
+ #include "llpreprocessor.h"
+
+#if defined(LL_GNUC) && GCC_VERSION >= 130000
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wnonnull"
+#endif
#include "tracy/Tracy.hpp"
+#if defined(LL_GNUC) && GCC_VERSION >= 130000
+# pragma GCC diagnostic push
+#endif
// Enable RenderDoc labeling
//#define LL_PROFILER_ENABLE_RENDER_DOC 0
diff --git a/indra/llcommon/llprofilercategories.h b/indra/llcommon/llprofilercategories.h
index 1c4f0f5624..261fdf14b7 100644
--- a/indra/llcommon/llprofilercategories.h
+++ b/indra/llcommon/llprofilercategories.h
@@ -48,6 +48,7 @@
#define LL_PROFILER_CATEGORY_ENABLE_DRAWPOOL 1
#define LL_PROFILER_CATEGORY_ENABLE_ENVIRONMENT 1
#define LL_PROFILER_CATEGORY_ENABLE_FACE 1
+//#define LL_PROFILER_CATEGORY_ENABLE_FILE 1
#define LL_PROFILER_CATEGORY_ENABLE_LLSD 1
#define LL_PROFILER_CATEGORY_ENABLE_LOGGING 1
#define LL_PROFILER_CATEGORY_ENABLE_MATERIAL 1
@@ -126,6 +127,14 @@
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_FACE
#endif
+#if LL_PROFILER_CATEGORY_ENABLE_FILE
+#define LL_PROFILE_ZONE_NAMED_CATEGORY_FILE LL_PROFILE_ZONE_NAMED
+#define LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE LL_PROFILE_ZONE_SCOPED
+#else
+#define LL_PROFILE_ZONE_NAMED_CATEGORY_FILE(name)
+#define LL_PROFILE_ZONE_SCOPED_CATEGORY_FILE
+#endif
+
#if LL_PROFILER_CATEGORY_ENABLE_LLSD
#define LL_PROFILE_ZONE_NAMED_CATEGORY_LLSD LL_PROFILE_ZONE_NAMED
#define LL_PROFILE_ZONE_SCOPED_CATEGORY_LLSD LL_PROFILE_ZONE_SCOPED
diff --git a/indra/llcommon/llrand.cpp b/indra/llcommon/llrand.cpp
index 2c51e6f07f..513613f543 100644
--- a/indra/llcommon/llrand.cpp
+++ b/indra/llcommon/llrand.cpp
@@ -58,9 +58,26 @@
* to restore uniform distribution.
*/
-// gRandomGenerator is a stateful static object, which is therefore not
+// pRandomGenerator is a stateful static object, which is therefore not
// inherently thread-safe.
-static thread_local LLRandLagFib2281 gRandomGenerator(LLUUID::getRandomSeed());
+//We use a pointer to not construct a huge object in the TLS space, sadly this is necessary
+// due to libcef.so on Linux being compiled with TLS model initial-exec (resulting in
+// FLAG STATIC_TLS, see readelf libcef.so). CEFs own TLS objects + LLRandLagFib2281 then will exhaust the
+// available TLS space, causing media failure.
+
+static thread_local std::unique_ptr< LLRandLagFib2281 > pRandomGenerator = nullptr;
+
+namespace {
+ F64 ll_internal_get_rand()
+ {
+ if( !pRandomGenerator )
+ {
+ pRandomGenerator.reset(new LLRandLagFib2281(LLUUID::getRandomSeed( ) ));
+ }
+
+ return(*pRandomGenerator)();
+ }
+}
// no default implementation, only specific F64 and F32 specializations
template <typename REAL>
@@ -73,7 +90,7 @@ inline F64 ll_internal_random<F64>()
// 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() };
+ F64 rv{ ll_internal_get_rand() };
if(!((rv >= 0.0) && (rv < 1.0))) return fmod(rv, 1.0);
return rv;
}
@@ -85,7 +102,7 @@ inline F32 ll_internal_random<F32>()
// 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<F64>(gRandomGenerator()) };
+ F32 rv{ narrow<F64>(ll_internal_get_rand()) };
if(!((rv >= 0.0f) && (rv < 1.0f))) return fmodf(rv, 1.0f);
return rv;
}
diff --git a/indra/llcommon/llsd.cpp b/indra/llcommon/llsd.cpp
index ce39bc7af3..ad2ce231fa 100644
--- a/indra/llcommon/llsd.cpp
+++ b/indra/llcommon/llsd.cpp
@@ -32,7 +32,6 @@
#include "llbase64.h"
#include "llerror.h"
-#include "../llmath/llmath.h"
#include "llformat.h"
#include "llsdserialize.h"
#include "stringize.h"
@@ -297,10 +296,10 @@ namespace
};
LLSD::Boolean ImplReal::asBoolean() const
- { return !llisnan(mValue) && mValue != 0.0; }
+ { return !std::isnan(mValue) && mValue != 0.0; }
LLSD::Integer ImplReal::asInteger() const
- { return !llisnan(mValue) ? (LLSD::Integer)mValue : 0; }
+ { return !std::isnan(mValue) ? (LLSD::Integer)mValue : 0; }
LLSD::String ImplReal::asString() const
{ return llformat("%lg", mValue); }
diff --git a/indra/llcommon/llsdjson.cpp b/indra/llcommon/llsdjson.cpp
index bb806f32eb..acd4bafb85 100644
--- a/indra/llcommon/llsdjson.cpp
+++ b/indra/llcommon/llsdjson.cpp
@@ -26,14 +26,12 @@
// Must turn on conditional declarations in header file so definitions end up
// with proper linkage.
-#define LLSD_DEBUG_INFO
#include "linden_common.h"
#include "llsdjson.h"
#include "llsdutil.h"
#include "llerror.h"
-#include "../llmath/llmath.h"
#include <boost/json/src.hpp>
diff --git a/indra/llcommon/llsdserialize.cpp b/indra/llcommon/llsdserialize.cpp
index ee7f0b01cc..7fe1f9cdd4 100644
--- a/indra/llcommon/llsdserialize.cpp
+++ b/indra/llcommon/llsdserialize.cpp
@@ -1421,11 +1421,7 @@ S32 LLSDNotationFormatter::format_impl(const LLSD& data, std::ostream& ostr,
case LLSD::TypeBoolean:
if(mBoolAlpha ||
-#if( LL_WINDOWS || __GNUC__ > 2)
(ostr.flags() & std::ios::boolalpha)
-#else
- (ostr.flags() & 0x0100)
-#endif
)
{
ostr << (data.asBoolean()
@@ -2391,6 +2387,7 @@ U8* unzip_llsdNavMesh( bool& valid, size_t& outsize, std::istream& is, S32 size
{
case Z_NEED_DICT:
ret = Z_DATA_ERROR;
+ [[fallthrough]];
case Z_DATA_ERROR:
case Z_MEM_ERROR:
inflateEnd(&strm);
diff --git a/indra/llcommon/llsdutil.cpp b/indra/llcommon/llsdutil.cpp
index dbd89118c9..fd01484128 100644
--- a/indra/llcommon/llsdutil.cpp
+++ b/indra/llcommon/llsdutil.cpp
@@ -503,7 +503,7 @@ bool filter_llsd_with_template(
* Helpers for llsd_matches()
*****************************************************************************/
// raw data used for LLSD::Type lookup
-struct Data
+struct LLSDData
{
LLSD::Type type;
const char* name;
@@ -534,7 +534,7 @@ public:
{
LL_PROFILE_ZONE_SCOPED;
- for (const Data *di(boost::begin(typedata)), *dend(boost::end(typedata)); di != dend; ++di)
+ for (const LLSDData*di(boost::begin(typedata)), *dend(boost::end(typedata)); di != dend; ++di)
{
mMap[di->type] = di->name;
}
diff --git a/indra/llcommon/llsingleton.h b/indra/llcommon/llsingleton.h
index e6989211ae..ebef2489ce 100644
--- a/indra/llcommon/llsingleton.h
+++ b/indra/llcommon/llsingleton.h
@@ -30,7 +30,7 @@
#include <typeinfo>
#include <unordered_set>
#include <vector>
-#include "mutex.h"
+#include <mutex>
#include "lockstatic.h"
#include "llthread.h" // on_main_thread()
#include "llmainthreadtask.h"
diff --git a/indra/llcommon/llstl.h b/indra/llcommon/llstl.h
index 7a1c7caf82..550725cbc8 100644
--- a/indra/llcommon/llstl.h
+++ b/indra/llcommon/llstl.h
@@ -519,18 +519,8 @@ llbind2nd(const _Operation& __oper, const _Tp& __x)
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
}
/**
diff --git a/indra/llcommon/llstreamqueue.cpp b/indra/llcommon/llstreamqueue.cpp
deleted file mode 100644
index 981d913749..0000000000
--- a/indra/llcommon/llstreamqueue.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-/**
- * @file llstreamqueue.cpp
- * @author Nat Goodspeed
- * @date 2012-01-05
- * @brief Implementation for llstreamqueue.
- *
- * $LicenseInfo:firstyear=2012&license=viewerlgpl$
- * Copyright (c) 2012, Linden Research, Inc.
- * $/LicenseInfo$
- */
-
-// Precompiled header
-#include "linden_common.h"
-// associated header
-#include "llstreamqueue.h"
-// STL headers
-// std headers
-// external library headers
-// other Linden headers
-
-// As of this writing, llstreamqueue.h is entirely template-based, therefore
-// we don't strictly need a corresponding .cpp file. However, our CMake test
-// macro assumes one. Here it is.
-bool llstreamqueue_cpp_ignored = true;
diff --git a/indra/llcommon/llstreamqueue.h b/indra/llcommon/llstreamqueue.h
deleted file mode 100644
index 01689457dd..0000000000
--- a/indra/llcommon/llstreamqueue.h
+++ /dev/null
@@ -1,240 +0,0 @@
-/**
- * @file llstreamqueue.h
- * @author Nat Goodspeed
- * @date 2012-01-04
- * @brief Definition of LLStreamQueue
- *
- * $LicenseInfo:firstyear=2012&license=viewerlgpl$
- * Copyright (c) 2012, Linden Research, Inc.
- * $/LicenseInfo$
- */
-
-#if ! defined(LL_LLSTREAMQUEUE_H)
-#define LL_LLSTREAMQUEUE_H
-
-#include <string>
-#include <list>
-#include <iosfwd> // std::streamsize
-#include <boost/iostreams/categories.hpp>
-
-/**
- * This class is a growable buffer between a producer and consumer. It serves
- * as a queue usable with Boost.Iostreams -- hence, a "stream queue."
- *
- * This is especially useful for buffering nonblocking I/O. For instance, we
- * want application logic to be able to serialize LLSD to a std::ostream. We
- * may write more data than the destination pipe can handle all at once, but
- * it's imperative NOT to block the application-level serialization call. So
- * we buffer it instead. Successive frames can try nonblocking writes to the
- * destination pipe until all buffered data has been sent.
- *
- * Similarly, we want application logic be able to deserialize LLSD from a
- * std::istream. Again, we must not block that deserialize call waiting for
- * more data to arrive from the input pipe! Instead we build up a buffer over
- * a number of frames, using successive nonblocking reads, until we have
- * "enough" data to be able to present it through a std::istream.
- *
- * @note The use cases for this class overlap somewhat with those for the
- * LLIOPipe/LLPumpIO hierarchies, and indeed we considered using those. This
- * class has two virtues over the older machinery:
- *
- * # It's vastly simpler -- way fewer concepts. It's not clear to me whether
- * there were ever LLIOPipe/etc. use cases that demanded all the fanciness
- * rolled in, or whether they were simply overdesigned. In any case, no
- * remaining Lindens will admit to familiarity with those classes -- and
- * they're sufficiently obtuse that it would take considerable learning
- * curve to figure out how to use them properly. The bottom line is that
- * current management is not keen on any more engineers climbing that curve.
- * # This class is designed around available components such as std::string,
- * std::list, Boost.Iostreams. There's less proprietary code.
- */
-template <typename Ch>
-class LLGenericStreamQueue
-{
-public:
- LLGenericStreamQueue():
- mSize(0),
- mClosed(false)
- {}
-
- /**
- * Boost.Iostreams Source Device facade for use with other Boost.Iostreams
- * functionality. LLGenericStreamQueue doesn't quite fit any of the Boost
- * 1.48 Iostreams concepts; instead it behaves as both a Sink and a
- * Source. This is its Source facade.
- */
- struct Source
- {
- typedef Ch char_type;
- typedef boost::iostreams::source_tag category;
-
- /// Bind the underlying LLGenericStreamQueue
- Source(LLGenericStreamQueue& sq):
- mStreamQueue(sq)
- {}
-
- // Read up to n characters from the underlying data source into the
- // buffer s, returning the number of characters read; return -1 to
- // indicate EOF
- std::streamsize read(Ch* s, std::streamsize n)
- {
- return mStreamQueue.read(s, n);
- }
-
- LLGenericStreamQueue& mStreamQueue;
- };
-
- /**
- * Boost.Iostreams Sink Device facade for use with other Boost.Iostreams
- * functionality. LLGenericStreamQueue doesn't quite fit any of the Boost
- * 1.48 Iostreams concepts; instead it behaves as both a Sink and a
- * Source. This is its Sink facade.
- */
- struct Sink
- {
- typedef Ch char_type;
- typedef boost::iostreams::sink_tag category;
-
- /// Bind the underlying LLGenericStreamQueue
- Sink(LLGenericStreamQueue& sq):
- mStreamQueue(sq)
- {}
-
- /// Write up to n characters from the buffer s to the output sequence,
- /// returning the number of characters written
- std::streamsize write(const Ch* s, std::streamsize n)
- {
- return mStreamQueue.write(s, n);
- }
-
- /// Send EOF to consumer
- void close()
- {
- mStreamQueue.close();
- }
-
- LLGenericStreamQueue& mStreamQueue;
- };
-
- /// Present Boost.Iostreams Source facade
- Source asSource() { return Source(*this); }
- /// Present Boost.Iostreams Sink facade
- Sink asSink() { return Sink(*this); }
-
- /// append data to buffer
- std::streamsize write(const Ch* s, std::streamsize n)
- {
- // Unclear how often we might be asked to write 0 bytes -- perhaps a
- // naive caller responding to an unready nonblocking read. But if we
- // do get such a call, don't add a completely empty BufferList entry.
- if (n == 0)
- return n;
- // We could implement this using a single std::string object, a la
- // ostringstream. But the trouble with appending to a string is that
- // you might have to recopy all previous contents to grow its size. If
- // we want this to scale to large data volumes, better to allocate
- // individual pieces.
- mBuffer.push_back(string(s, n));
- mSize += n;
- return n;
- }
-
- /**
- * Inform this LLGenericStreamQueue that no further data are forthcoming.
- * For our purposes, close() is strictly a producer-side operation;
- * there's little point in closing the consumer side.
- */
- void close()
- {
- mClosed = true;
- }
-
- /// consume data from buffer
- std::streamsize read(Ch* s, std::streamsize n)
- {
- // read() is actually a convenience method for peek() followed by
- // skip().
- std::streamsize got(peek(s, n));
- // We can only skip() as many characters as we can peek(); ignore
- // skip() return here.
- skip(n);
- return got;
- }
-
- /// Retrieve data from buffer without consuming. Like read(), return -1 on
- /// EOF.
- std::streamsize peek(Ch* s, std::streamsize n) const;
-
- /// Consume data from buffer without retrieving. Unlike read() and peek(),
- /// at EOF we simply skip 0 characters.
- std::streamsize skip(std::streamsize n);
-
- /// How many characters do we currently have buffered?
- std::streamsize size() const
- {
- return mSize;
- }
-
-private:
- typedef std::basic_string<Ch> string;
- typedef std::list<string> BufferList;
- BufferList mBuffer;
- std::streamsize mSize;
- bool mClosed;
-};
-
-template <typename Ch>
-std::streamsize LLGenericStreamQueue<Ch>::peek(Ch* s, std::streamsize n) const
-{
- // Here we may have to build up 'n' characters from an arbitrary
- // number of individual BufferList entries.
- typename BufferList::const_iterator bli(mBuffer.begin()), blend(mBuffer.end());
- // Indicate EOF if producer has closed the pipe AND we've exhausted
- // all previously-buffered data.
- if (mClosed && bli == blend)
- {
- return -1;
- }
- // Here either producer hasn't yet closed, or we haven't yet exhausted
- // remaining data.
- std::streamsize needed(n), got(0);
- // Loop until either we run out of BufferList entries or we've
- // completely satisfied the request.
- for ( ; bli != blend && needed; ++bli)
- {
- std::streamsize chunk(std::min(needed, std::streamsize(bli->length())));
- std::copy(bli->begin(), bli->begin() + chunk, s);
- needed -= chunk;
- s += chunk;
- got += chunk;
- }
- return got;
-}
-
-template <typename Ch>
-std::streamsize LLGenericStreamQueue<Ch>::skip(std::streamsize n)
-{
- typename BufferList::iterator bli(mBuffer.begin()), blend(mBuffer.end());
- std::streamsize toskip(n), skipped(0);
- while (bli != blend && (size_t)toskip >= bli->length())
- {
- std::streamsize chunk(bli->length());
- typename BufferList::iterator zap(bli++);
- mBuffer.erase(zap);
- mSize -= chunk;
- toskip -= chunk;
- skipped += chunk;
- }
- if (bli != blend && toskip)
- {
- bli->erase(bli->begin(), bli->begin() + toskip);
- mSize -= toskip;
- skipped += toskip;
- }
- return skipped;
-}
-
-typedef LLGenericStreamQueue<char> LLStreamQueue;
-typedef LLGenericStreamQueue<wchar_t> LLWStreamQueue;
-
-#endif /* ! defined(LL_LLSTREAMQUEUE_H) */
diff --git a/indra/llcommon/llsys.cpp b/indra/llcommon/llsys.cpp
index 803bab393c..5ef042b5a7 100644
--- a/indra/llcommon/llsys.cpp
+++ b/indra/llcommon/llsys.cpp
@@ -506,57 +506,46 @@ 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 */
+namespace {
- size_t nbytes = fread(buff, 1, STATUS_SIZE-1, status_filep);
- buff[nbytes] = '\0';
+ U32 readFromProcStat( std::string entryName )
+ {
+ U32 val{};
+#if LL_LINUX
+ constexpr U32 STATUS_SIZE = 2048;
- // All these guys return numbers in KB
- char *memp = strstr(buff, "VmSize:");
- if (memp)
+ LLFILE* status_filep = LLFile::fopen("/proc/self/status", LLFILE_MODE("rb"));
+ if (status_filep)
{
- numRead += sscanf(memp, "%*s %u", &virtual_size);
+ 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, entryName.c_str());
+ if (memp)
+ {
+ (void) sscanf(memp, "%*s %u", &val);
+ }
+ fclose(status_filep);
}
- fclose(status_filep);
- }
#endif
- return virtual_size;
+ return val;
+ }
+
}
//static
-U32 LLOSInfo::getProcessResidentSizeKB()
+U32 LLOSInfo::getProcessVirtualSizeKB()
{
- 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';
+ return readFromProcStat( "VmSize:" );
+}
- // 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
+U32 LLOSInfo::getProcessResidentSizeKB()
+{
+ return readFromProcStat( "VmRSS:" );
}
//static
@@ -721,7 +710,7 @@ public:
void add(const LLSD::String& name, const T& value,
typename std::enable_if_t<std::is_integral_v<T> >* = 0)
{
- mStats[name] = LLSD::Integer(value);
+ mStats[name] = LLSD::Integer(llmin<T>(value, S32_MAX));
}
// Store every floating-point type as LLSD::Real.
@@ -830,57 +819,8 @@ void LLMemoryInfo::getAvailableMemoryKB(U32Kilobytes& avail_mem_kb)
}
#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)
- LLSD statsMap(loadStatsMap());
-
- avail_mem_kb = (U32Kilobytes)statsMap["MemFree"].asInteger();
+ U64 phys = U64(getpagesize()) * U64(get_avphys_pages());
+ avail_mem_kb = U64Bytes(phys);
#else
//do not know how to collect available memory info for other systems.
//leave it blank here for now.
@@ -1080,7 +1020,7 @@ LLSD LLMemoryInfo::loadStatsMap()
}
#elif LL_LINUX
- std::ifstream meminfo(MEMINFO_FILE);
+ llifstream meminfo(MEMINFO_FILE);
if (meminfo.is_open())
{
// MemTotal: 4108424 kB
@@ -1120,9 +1060,10 @@ LLSD LLMemoryInfo::loadStatsMap()
LLSD::String key(matched[1].first, matched[1].second);
LLSD::String value_str(matched[2].first, matched[2].second);
LLSD::Integer value(0);
+ S64 intval = 0;
try
{
- value = boost::lexical_cast<LLSD::Integer>(value_str);
+ intval = llclamp(boost::lexical_cast<S64>(value_str), S32_MIN, S32_MAX);
}
catch (const boost::bad_lexical_cast&)
{
@@ -1131,6 +1072,7 @@ LLSD LLMemoryInfo::loadStatsMap()
<< line << LL_ENDL;
continue;
}
+ value = LLSD::Integer(intval);
// Store this statistic.
stats.add(key, value);
}
@@ -1331,7 +1273,7 @@ bool gunzip_file(const std::string& srcfile, const std::string& dstfile)
src = gzopen(srcfile.c_str(), "rb");
#endif
if (! src) goto err;
- dst = LLFile::fopen(tmpfile, "wb"); /* Flawfinder: ignore */
+ dst = LLFile::fopen(tmpfile, LLFILE_MODE("wb")); /* Flawfinder: ignore */
if (! dst) goto err;
do
{
@@ -1372,7 +1314,7 @@ bool gzip_file(const std::string& srcfile, const std::string& dstfile)
#endif
if (! dst) goto err;
- src = LLFile::fopen(srcfile, "rb"); /* Flawfinder: ignore */
+ src = LLFile::fopen(srcfile, LLFILE_MODE("rb")); /* Flawfinder: ignore */
if (! src) goto err;
while ((bytes = (S32)fread(buffer, sizeof(U8), COMPRESS_BUFFER_SIZE, src)) > 0)
diff --git a/indra/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp
index 4f15626108..277edcb6a2 100644
--- a/indra/llcommon/llthread.cpp
+++ b/indra/llcommon/llthread.cpp
@@ -42,6 +42,10 @@
#include <sched.h>
#endif
+#if LL_DARWIN || LL_LINUX
+#include <pthread.h>
+#endif
+
#ifdef LL_WINDOWS
@@ -56,25 +60,32 @@ typedef struct tagTHREADNAME_INFO
DWORD dwFlags; // Reserved for future use, must be zero.
} THREADNAME_INFO;
#pragma pack(pop)
+#endif
-void set_thread_name( DWORD dwThreadID, const char* threadName)
+void set_thread_name(const char* threadName)
{
+#if LL_WINDOWS
THREADNAME_INFO info;
- info.dwType = 0x1000;
- info.szName = threadName;
- info.dwThreadID = dwThreadID;
- info.dwFlags = 0;
+ info.dwType = 0x1000;
+ info.szName = threadName;
+ info.dwThreadID = GetCurrentThreadId();
+ info.dwFlags = 0;
__try
{
- ::RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(DWORD), (ULONG_PTR*)&info );
+ ::RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(DWORD), (ULONG_PTR*)&info);
}
- __except(EXCEPTION_CONTINUE_EXECUTION)
+ __except (EXCEPTION_CONTINUE_EXECUTION)
{
}
-}
+#elif LL_DARWIN
+ std::string truncated_name(std::string_view(threadName).substr(0, 15));
+ pthread_setname_np(truncated_name.c_str());
+#elif LL_LINUX
+ std::string truncated_name(std::string_view(threadName).substr(0, 15));
+ pthread_setname_np(pthread_self(), truncated_name.c_str());
#endif
-
+}
//----------------------------------------------------------------------------
// Usage:
@@ -148,27 +159,12 @@ LL_COMMON_API bool assert_main_thread()
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
-
+ set_thread_name(mName.c_str());
LL_PROFILER_SET_THREAD_NAME( mName.c_str() );
// this is the first point at which we're actually running in the new thread
diff --git a/indra/llcommon/llthread.h b/indra/llcommon/llthread.h
index 8794ac93aa..b97d479abc 100644
--- a/indra/llcommon/llthread.h
+++ b/indra/llcommon/llthread.h
@@ -28,10 +28,11 @@
#define LL_LLTHREAD_H
#include "llapr.h"
-#include "boost/intrusive_ptr.hpp"
#include "llrefcount.h"
#include <thread>
+extern void set_thread_name(const char* threadName);
+
namespace LLTrace
{
class ThreadRecorder;
@@ -86,11 +87,6 @@ public:
id_t getID() const { return mID; }
- // Called by threads *not* created via LLThread to register some
- // internal state used by LLMutex. You must call this once early
- // in the running thread to prevent collisions with the main thread.
- static void registerThreadID();
-
private:
bool mPaused;
std::thread::native_handle_type mNativeHandle; // for termination in case of issues
diff --git a/indra/llcommon/llthreadsafequeue.h b/indra/llcommon/llthreadsafequeue.h
index 1a1d06a6fd..2a18b4a438 100644
--- a/indra/llcommon/llthreadsafequeue.h
+++ b/indra/llcommon/llthreadsafequeue.h
@@ -32,7 +32,7 @@
#include <boost/fiber/timed_mutex.hpp>
#include LLCOROS_CONDVAR_HEADER
#include "llexception.h"
-#include "mutex.h"
+#include <mutex>
#include <chrono>
#include <queue>
#include <string>
diff --git a/indra/llcommon/lltracerecording.cpp b/indra/llcommon/lltracerecording.cpp
index c23adca7e8..f010f0e961 100644
--- a/indra/llcommon/lltracerecording.cpp
+++ b/indra/llcommon/lltracerecording.cpp
@@ -32,11 +32,6 @@
#include "lltracethreadrecorder.h"
#include "llthread.h"
-inline F64 lerp(F64 a, F64 b, F64 u)
-{
- return a + ((b - a) * u);
-}
-
namespace LLTrace
{
@@ -302,7 +297,7 @@ F64 Recording::getMean( const StatType<SampleAccumulator>& stat )
{
t = (F64)active_accumulator->getSampleCount() / (F64)div;
}
- return lerp(accumulator.getMean(), active_accumulator->getMean(), t);
+ return std::lerp(accumulator.getMean(), active_accumulator->getMean(), t);
}
else
{
@@ -388,7 +383,7 @@ F64 Recording::getMean( const StatType<EventAccumulator>& stat )
{
t = (F64)active_accumulator->getSampleCount() / (F64)div;
}
- return lerp(accumulator.getMean(), active_accumulator->getMean(), t);
+ return std::lerp(accumulator.getMean(), active_accumulator->getMean(), t);
}
else
{
diff --git a/indra/llcommon/lluuid.cpp b/indra/llcommon/lluuid.cpp
index 4aae90626e..867744a29d 100644
--- a/indra/llcommon/lluuid.cpp
+++ b/indra/llcommon/lluuid.cpp
@@ -151,6 +151,48 @@ U32 janky_fast_random_seeded_bytes(U32 seed, U32 val)
}
#endif
+void LLUUID::to_chars(char* out) const
+{
+ snprintf(out, UUID_STR_LENGTH, "%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]));
+}
+
+void LLUUID::to_wchars(wchar_t* out) const
+{
+ swprintf(out, UUID_STR_LENGTH, L"%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]));
+}
+
// Common to all UUID implementations
void LLUUID::toString(std::string& out) const
{
@@ -405,8 +447,8 @@ LLUUID LLUUID::combine(const LLUUID& other) const
std::ostream& operator<<(std::ostream& s, const LLUUID& uuid)
{
- std::string uuid_str;
- uuid.toString(uuid_str);
+ char uuid_str[UUID_STR_LENGTH];
+ uuid.to_chars(uuid_str);
s << uuid_str;
return s;
}
diff --git a/indra/llcommon/lluuid.h b/indra/llcommon/lluuid.h
index f91aadccc0..122cf414ca 100644
--- a/indra/llcommon/lluuid.h
+++ b/indra/llcommon/lluuid.h
@@ -104,6 +104,8 @@ public:
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 to_chars(char* out) const;
+ void to_wchars(wchar_t* out) const;
void toString(std::string& out) const;
void toCompressedString(std::string& out) const;
diff --git a/indra/llcommon/lockstatic.h b/indra/llcommon/lockstatic.h
index 7cc9b7eec0..db4c7af0ec 100644
--- a/indra/llcommon/lockstatic.h
+++ b/indra/llcommon/lockstatic.h
@@ -13,7 +13,7 @@
#if ! defined(LL_LOCKSTATIC_H)
#define LL_LOCKSTATIC_H
-#include "mutex.h" // std::unique_lock
+#include <mutex> // std::unique_lock
namespace llthread
{
diff --git a/indra/llcommon/mutex.h b/indra/llcommon/mutex.h
deleted file mode 100644
index 82e46315e2..0000000000
--- a/indra/llcommon/mutex.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/**
- * @file mutex.h
- * @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$
- */
-
-#if LL_WINDOWS
-#pragma warning (push)
-#pragma warning (disable:4265)
-#endif
-// warning C4265: 'std::_Pad' : class has virtual functions, but destructor is not virtual
-
-#include <mutex>
-
-#if LL_WINDOWS
-#pragma warning (pop)
-#endif
diff --git a/indra/llcommon/stdtypes.h b/indra/llcommon/stdtypes.h
index f1e4c2bc78..6b98a6bc49 100644
--- a/indra/llcommon/stdtypes.h
+++ b/indra/llcommon/stdtypes.h
@@ -38,6 +38,11 @@ typedef signed short S16;
typedef unsigned short U16;
typedef signed int S32;
typedef unsigned int U32;
+typedef long long int S64;
+typedef long long unsigned int U64;
+
+#define S64L(a) (a##LL)
+#define U64L(a) (a##ULL)
// to express an index that might go negative
// (ssize_t is provided by SOME compilers, don't collide)
@@ -62,21 +67,6 @@ typedef wchar_t llwchar;
#define LLWCHAR_IS_WCHAR_T 1
#endif
-#if LL_WINDOWS
-typedef signed __int64 S64;
-// probably should be 'hyper' or similiar
-#define S64L(a) (a)
-typedef unsigned __int64 U64;
-#define U64L(a) (a)
-#else
-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)
-#endif
-#endif
-
typedef float F32;
typedef double F64;
@@ -121,10 +111,6 @@ typedef U8 LLPCode;
#define LL_ARRAY_SIZE( _kArray ) ( sizeof( (_kArray) ) / sizeof( _kArray[0] ) )
-#if LL_LINUX && __GNUC__ <= 2
-typedef int intptr_t;
-#endif
-
/*****************************************************************************
* Narrowing
*****************************************************************************/
diff --git a/indra/llcommon/tests/commonmisc_test.cpp b/indra/llcommon/tests/commonmisc_test.cpp
index b1a284225e..80be26dfeb 100644
--- a/indra/llcommon/tests/commonmisc_test.cpp
+++ b/indra/llcommon/tests/commonmisc_test.cpp
@@ -503,7 +503,7 @@ namespace tut
result_str = (const char*) result;
ensure_equals("U64_to_str converted 1.4", val_str, result_str);
- val = U64L(-1); // 0xFFFFFFFFFFFFFFFF == 18446744073709551615
+ val = U64(-1); // 0xFFFFFFFFFFFFFFFF == 18446744073709551615
val_str = "18446744073709551615";
U64_to_str(val, result, sizeof(result));
result_str = (const char*) result;
@@ -619,7 +619,7 @@ namespace tut
val = U64_to_F64(U64L(2));
ensure_equals("U64_to_F64 converted 3.6", val, result);
- result = U64L(0x7FFFFFFFFFFFFFFF) * 1.0L; // 0x7FFFFFFFFFFFFFFF
+ result = F64(0x7FFFFFFFFFFFFFFF) * 1.0; // 0x7FFFFFFFFFFFFFFF
val = U64_to_F64(U64L(0x7FFFFFFFFFFFFFFF));
ensure_equals("U64_to_F64 converted 3.7", val, result);
}
diff --git a/indra/llcommon/tests/llallocator_heap_profile_test.cpp b/indra/llcommon/tests/llallocator_heap_profile_test.cpp
deleted file mode 100644
index da8b54ffc0..0000000000
--- a/indra/llcommon/tests/llallocator_heap_profile_test.cpp
+++ /dev/null
@@ -1,144 +0,0 @@
-/**
- * @file llallocator_heap_profile_test.cpp
- * @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$
- */
-
-#include "../llallocator_heap_profile.h"
-#include "../test/lltut.h"
-
-namespace tut
-{
- struct llallocator_heap_profile_data
- {
- LLAllocatorHeapProfile prof;
-
- static char const * const sample_win_profile;
- // *TODO - get test output from mac/linux tcmalloc
- static char const * const sample_mac_profile;
- static char const * const sample_lin_profile;
-
- static char const * const crash_testcase;
- };
- typedef test_group<llallocator_heap_profile_data> factory;
- typedef factory::object object;
-}
-namespace
-{
- tut::factory llallocator_heap_profile_test_factory("LLAllocatorHeapProfile");
-}
-
-namespace tut
-{
- template<> template<>
- void object::test<1>()
- {
- prof.parse(sample_win_profile);
-
- ensure_equals("count lines", prof.mLines.size() , 5);
- ensure_equals("alloc counts", prof.mLines[0].mLiveCount, 2131854U);
- ensure_equals("alloc counts", prof.mLines[0].mLiveSize, 2245710106ULL);
- ensure_equals("alloc counts", prof.mLines[0].mTotalCount, 14069198U);
- ensure_equals("alloc counts", prof.mLines[0].mTotalSize, 4295177308ULL);
- ensure_equals("count markers", prof.mLines[0].mTrace.size(), 0);
- ensure_equals("count markers", prof.mLines[1].mTrace.size(), 0);
- ensure_equals("count markers", prof.mLines[2].mTrace.size(), 4);
- ensure_equals("count markers", prof.mLines[3].mTrace.size(), 6);
- ensure_equals("count markers", prof.mLines[4].mTrace.size(), 7);
-
- //prof.dump(std::cout);
- }
-
- template<> template<>
- void object::test<2>()
- {
- prof.parse(crash_testcase);
-
- ensure_equals("count lines", prof.mLines.size(), 2);
- ensure_equals("alloc counts", prof.mLines[0].mLiveCount, 3U);
- ensure_equals("alloc counts", prof.mLines[0].mLiveSize, 1049652ULL);
- ensure_equals("alloc counts", prof.mLines[0].mTotalCount, 8U);
- ensure_equals("alloc counts", prof.mLines[0].mTotalSize, 1049748ULL);
- ensure_equals("count markers", prof.mLines[0].mTrace.size(), 0);
- ensure_equals("count markers", prof.mLines[1].mTrace.size(), 0);
-
- //prof.dump(std::cout);
- }
-
- template<> template<>
- void object::test<3>()
- {
- // test that we don't crash on edge case data
- prof.parse("");
- ensure("emtpy on error", prof.mLines.empty());
-
- prof.parse("heap profile:");
- ensure("emtpy on error", prof.mLines.empty());
- }
-
-char const * const llallocator_heap_profile_data::sample_win_profile =
-"heap profile: 2131854: 2245710106 [14069198: 4295177308] @\n"
-"308592: 1073398388 [966564: 1280998739] @\n"
-"462651: 375969538 [1177377: 753561247] @ 2 3 6 1\n"
-"314744: 206611283 [2008722: 570934755] @ 2 3 3 7 21 32\n"
-"277152: 82862770 [621961: 168503640] @ 2 3 3 7 21 32 87\n"
-"\n"
-"MAPPED_LIBRARIES:\n"
-"00400000-02681000 r-xp 00000000 00:00 0 c:\\proj\\tcmalloc-eval-9\\indra\\build-vc80\\newview\\RelWithDebInfo\\secondlife-bin.exe\n"
-"77280000-773a7000 r-xp 00000000 00:00 0 C:\\Windows\\system32\\ntdll.dll\n"
-"76df0000-76ecb000 r-xp 00000000 00:00 0 C:\\Windows\\system32\\kernel32.dll\n"
-"76000000-76073000 r-xp 00000000 00:00 0 C:\\Windows\\system32\\comdlg32.dll\n"
-"75ee0000-75f8a000 r-xp 00000000 00:00 0 C:\\Windows\\system32\\msvcrt.dll\n"
-"76c30000-76c88000 r-xp 00000000 00:00 0 C:\\Windows\\system32\\SHLWAPI.dll\n"
-"75f90000-75fdb000 r-xp 00000000 00:00 0 C:\\Windows\\system32\\GDI32.dll\n"
-"77420000-774bd000 r-xp 00000000 00:00 0 C:\\Windows\\system32\\USER32.dll\n"
-"75e10000-75ed6000 r-xp 00000000 00:00 0 C:\\Windows\\system32\\ADVAPI32.dll\n"
-"75b00000-75bc2000 r-xp 00000000 00:00 0 C:\\Windows\\system32\\RPCRT4.dll\n"
-"72ca0000-72d25000 r-xp 00000000 00:00 0 C:\\Windows\\WinSxS\\x86_microsoft.windows.common-controls_6595b64144ccf1df_5.82.6001.18000_none_886786f450a74a05\\COMCTL32.dll\n"
-"76120000-76c30000 r-xp 00000000 00:00 0 C:\\Windows\\system32\\SHELL32.dll\n"
-"71ce0000-71d13000 r-xp 00000000 00:00 0 C:\\Windows\\system32\\DINPUT8.dll\n";
-
-
-char const * const llallocator_heap_profile_data::crash_testcase =
-"heap profile: 3: 1049652 [ 8: 1049748] @\n"
-" 3: 1049652 [ 8: 1049748] @\n"
-"\n"
-"MAPPED_LIBRARIES:\n"
-"00400000-004d5000 r-xp 00000000 00:00 0 c:\\code\\linden\\tcmalloc\\indra\\build-vc80\\llcommon\\RelWithDebInfo\\llallocator_test.exe\n"
-"7c900000-7c9af000 r-xp 00000000 00:00 0 C:\\WINDOWS\\system32\\ntdll.dll\n"
-"7c800000-7c8f6000 r-xp 00000000 00:00 0 C:\\WINDOWS\\system32\\kernel32.dll\n"
-"77dd0000-77e6b000 r-xp 00000000 00:00 0 C:\\WINDOWS\\system32\\ADVAPI32.dll\n"
-"77e70000-77f02000 r-xp 00000000 00:00 0 C:\\WINDOWS\\system32\\RPCRT4.dll\n"
-"77fe0000-77ff1000 r-xp 00000000 00:00 0 C:\\WINDOWS\\system32\\Secur32.dll\n"
-"71ab0000-71ac7000 r-xp 00000000 00:00 0 C:\\WINDOWS\\system32\\WS2_32.dll\n"
-"77c10000-77c68000 r-xp 00000000 00:00 0 C:\\WINDOWS\\system32\\msvcrt.dll\n"
-"71aa0000-71aa8000 r-xp 00000000 00:00 0 C:\\WINDOWS\\system32\\WS2HELP.dll\n"
-"76bf0000-76bfb000 r-xp 00000000 00:00 0 C:\\WINDOWS\\system32\\PSAPI.DLL\n"
-"5b860000-5b8b5000 r-xp 00000000 00:00 0 C:\\WINDOWS\\system32\\NETAPI32.dll\n"
-"10000000-10041000 r-xp 00000000 00:00 0 c:\\code\\linden\\tcmalloc\\indra\\build-vc80\\llcommon\\RelWithDebInfo\\libtcmalloc_minimal.dll\n"
-"7c420000-7c4a7000 r-xp 00000000 00:00 0 C:\\WINDOWS\\WinSxS\\x86_Microsoft.VC80.CRT_1fc8b3b9a1e18e3b_8.0.50727.1433_x-ww_5cf844d2\\MSVCP80.dll\n"
-"78130000-781cb000 r-xp 00000000 00:00 0 C:\\WINDOWS\\WinSxS\\x86_Microsoft.VC80.CRT_1fc8b3b9a1e18e3b_8.0.50727.1433_x-ww_5cf844d2\\MSVCR80.dll\n";
-
-}
diff --git a/indra/llcommon/tests/llallocator_test.cpp b/indra/llcommon/tests/llallocator_test.cpp
deleted file mode 100644
index a7573641c7..0000000000
--- a/indra/llcommon/tests/llallocator_test.cpp
+++ /dev/null
@@ -1,80 +0,0 @@
-/**
- * @file llallocator_test.cpp
- * @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$
- */
-
-#include "../llallocator.h"
-#include "../test/lltut.h"
-
-namespace tut
-{
- struct llallocator_data
- {
- LLAllocator llallocator;
- };
- typedef test_group<llallocator_data> factory;
- typedef factory::object object;
-}
-namespace
-{
- tut::factory llallocator_test_factory("LLAllocator");
-}
-
-namespace tut
-{
- template<> template<>
- void object::test<1>()
- {
- llallocator.setProfilingEnabled(false);
- ensure("Profiler disable", !llallocator.isProfiling());
- }
-
-#if LL_USE_TCMALLOC
- template<> template<>
- void object::test<2>()
- {
- llallocator.setProfilingEnabled(true);
- ensure("Profiler enable", llallocator.isProfiling());
- }
-
- template <> template <>
- void object::test<3>()
- {
- llallocator.setProfilingEnabled(true);
-
- char * test_alloc = new char[1024];
-
- llallocator.getProfile();
-
- delete [] test_alloc;
-
- llallocator.getProfile();
-
- // *NOTE - this test isn't ensuring anything right now other than no
- // exceptions are thrown.
- }
-#endif // LL_USE_TCMALLOC
-};
diff --git a/indra/llcommon/tests/llapp_test.cpp b/indra/llcommon/tests/llapp_test.cpp
new file mode 100644
index 0000000000..114af6edbf
--- /dev/null
+++ b/indra/llcommon/tests/llapp_test.cpp
@@ -0,0 +1,162 @@
+/**
+ * @file llapp_tut.cpp
+ * @author Phoenix
+ * @date 2006-09-12
+ *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2006-2011, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * 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 <tut/tut.hpp>
+
+#include "linden_common.h"
+#include "llapp.h"
+#include "lltut.h"
+
+
+namespace tut
+{
+ struct application
+ {
+ class LLTestApp : public LLApp
+ {
+ public:
+ virtual bool init() { return true; }
+ virtual bool cleanup() { return true; }
+ virtual bool frame() { return true; }
+ };
+ LLTestApp* mApp;
+ application()
+ {
+ mApp = new LLTestApp;
+ }
+ ~application()
+ {
+ delete mApp;
+ }
+ };
+
+ typedef test_group<application> application_t;
+ typedef application_t::object application_object_t;
+ tut::application_t tut_application("application");
+
+ template<> template<>
+ void application_object_t::test<1>()
+ {
+ LLSD defaults;
+ defaults["template"] = "../../../scripts/messages/message_template.msg";
+ defaults["configdir"] = ".";
+ defaults["datadir"] = "data";
+ mApp->setOptionData(LLApp::PRIORITY_DEFAULT, defaults);
+
+ LLSD datadir_sd = mApp->getOption("datadir");
+ ensure_equals("data type", datadir_sd.type(), LLSD::TypeString);
+ ensure_equals(
+ "data value", datadir_sd.asString(), std::string("data"));
+ }
+
+ template<> template<>
+ void application_object_t::test<2>()
+ {
+ const int ARGC = 13;
+ const char* ARGV[ARGC] =
+ {
+ "", // argv[0] is usually the application name
+ "-crashcount",
+ "2",
+ "-space",
+ "spaceserver.grid.lindenlab.com",
+ "-db_host",
+ "localhost",
+ "--allowlslhttprequests",
+ "-asset-uri",
+ "http://test.lindenlab.com/assets",
+ "-data",
+ "127.0.0.1",
+ "--smtp"
+ };
+ bool ok = mApp->parseCommandOptions(ARGC, const_cast<char**>(ARGV));
+ ensure("command line parsed", ok);
+ ensure_equals(
+ "crashcount", mApp->getOption("crashcount").asInteger(), 2);
+ ensure_equals(
+ "space",
+ mApp->getOption("space").asString(),
+ std::string("spaceserver.grid.lindenlab.com"));
+ ensure_equals(
+ "db_host",
+ mApp->getOption("db_host").asString(),
+ std::string("localhost"));
+ ensure("allowlshlttprequests", mApp->getOption("smtp"));
+ ensure_equals(
+ "asset-uri",
+ mApp->getOption("asset-uri").asString(),
+ std::string("http://test.lindenlab.com/assets"));
+ ensure_equals(
+ "data",
+ mApp->getOption("data").asString(),
+ std::string("127.0.0.1"));
+ ensure("smtp", mApp->getOption("smtp"));
+ }
+
+ template<> template<>
+ void application_object_t::test<3>()
+ {
+ const int ARGC = 4;
+ const char* ARGV[ARGC] =
+ {
+ "", // argv[0] is usually the application name
+ "crashcount",
+ "2",
+ "--space"
+ };
+ bool ok = mApp->parseCommandOptions(ARGC, const_cast<char**>(ARGV));
+ ensure("command line parse failure", !ok);
+ }
+
+ template<> template<>
+ void application_object_t::test<4>()
+ {
+ const int ARGC = 4;
+ const char* ARGV[ARGC] =
+ {
+ "", // argv[0] is usually the application name
+ "--crashcount",
+ "2",
+ "space"
+ };
+ bool ok = mApp->parseCommandOptions(ARGC, const_cast<char**>(ARGV));
+ ensure("command line parse failure", !ok);
+ }
+
+
+ template<> template<>
+ void application_object_t::test<5>()
+ {
+ LLSD options;
+ options["boolean-test"] = true;
+ mApp->setOptionData(LLApp::PRIORITY_GENERAL_CONFIGURATION, options);
+ ensure("bool set", mApp->getOption("boolean-test").asBoolean());
+ options["boolean-test"] = false;
+ mApp->setOptionData(LLApp::PRIORITY_RUNTIME_OVERRIDE, options);
+ ensure("bool unset", !mApp->getOption("boolean-test").asBoolean());
+ }
+}
diff --git a/indra/llcommon/tests/lldependencies_test.cpp b/indra/llcommon/tests/lldependencies_test.cpp
index 2fea92e3b7..fd554005a3 100644
--- a/indra/llcommon/tests/lldependencies_test.cpp
+++ b/indra/llcommon/tests/lldependencies_test.cpp
@@ -228,19 +228,7 @@ namespace tut
StringList keys(StringList{ "The", "brown", "dog.", "fox", "jumps", "lazy", "over", "quick", "the", "yellow" });
ensure_equals(instance_from_range<StringList>(deps.get_key_range()), keys);
-#if (! defined(__GNUC__)) || (__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ > 3)
- // This is the succinct way, works on modern compilers
ensure_equals(instance_from_range<StringList>(make_transform_range(deps.get_range(), extract_key)), keys);
-#else // gcc 3.3
- StringDeps::range got_range(deps.get_range());
- StringDeps::iterator kni = got_range.begin(), knend = got_range.end();
- StringList::iterator ki = keys.begin(), kend = keys.end();
- for ( ; kni != knend && ki != kend; ++kni, ++ki)
- {
- ensure_equals(kni->first, *ki);
- }
- ensure("get_range() returns proper length", kni == knend && ki == kend);
-#endif // gcc 3.3
// blow off get_node_range() because they're all LLDependenciesEmpty instances
}
diff --git a/indra/llcommon/tests/lldoubledispatch_test.cpp b/indra/llcommon/tests/lldoubledispatch_test.cpp
new file mode 100644
index 0000000000..79c4edc058
--- /dev/null
+++ b/indra/llcommon/tests/lldoubledispatch_test.cpp
@@ -0,0 +1,240 @@
+/**
+ * @file lldoubledispatch_tut.cpp
+ * @author Nat Goodspeed
+ * @date 2008-11-13
+ * @brief Test for lldoubledispatch.h
+ *
+ * This program tests the DoubleDispatch class, using a variation on the example
+ * from Scott Meyers' "More Effective C++", Item 31.
+ *
+ * $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$
+ */
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+#include "lldoubledispatch.h"
+// STL headers
+// std headers
+#include <iostream>
+#include <memory>
+#include <string>
+#include <typeinfo>
+// external library headers
+// other Linden headers
+#include "lltut.h"
+
+
+/*---------------------------- Class hierarchy -----------------------------*/
+// All objects are GameObjects.
+class GameObject
+{
+public:
+ GameObject(const std::string& name): mName(name) {}
+ virtual ~GameObject() {}
+ virtual std::string stringize() { return std::string(typeid(*this).name()) + " " + mName; }
+
+protected:
+ std::string mName;
+};
+
+// SpaceStation, Asteroid and SpaceShip are peer GameObjects.
+struct SpaceStation: public GameObject
+{
+ SpaceStation(const std::string& name): GameObject(name) {}
+ // Only a dummy SpaceStation is constructed without a name
+ SpaceStation(): GameObject("dummy") {}
+};
+
+struct Asteroid: public GameObject
+{
+ Asteroid(const std::string& name): GameObject(name) {}
+ Asteroid(): GameObject("dummy") {}
+};
+
+struct SpaceShip: public GameObject
+{
+ SpaceShip(const std::string& name): GameObject(name) {}
+ SpaceShip(): GameObject("dummy") {}
+};
+
+// SpaceShip is specialized further into CommercialShip and MilitaryShip.
+struct CommercialShip: public SpaceShip
+{
+ CommercialShip(const std::string& name): SpaceShip(name) {}
+ CommercialShip(): SpaceShip("dummy") {}
+};
+
+struct MilitaryShip: public SpaceShip
+{
+ MilitaryShip(const std::string& name): SpaceShip(name) {}
+ MilitaryShip(): SpaceShip("dummy") {}
+};
+
+/*-------------------------- Collision functions ---------------------------*/
+// This mechanism permits us to overcome a limitation of Meyers' approach: we
+// can declare the parameter types exactly as we want, rather than having to
+// make them all GameObject& parameters.
+std::string shipAsteroid(SpaceShip& ship, Asteroid& rock)
+{
+// std::cout << rock.stringize() << " has pulverized " << ship.stringize() << std::endl;
+ return "shipAsteroid";
+}
+
+std::string militaryShipAsteroid(MilitaryShip& ship, Asteroid& rock)
+{
+// std::cout << rock.stringize() << " has severely damaged " << ship.stringize() << std::endl;
+ return "militaryShipAsteroid";
+}
+
+std::string shipStation(SpaceShip& ship, SpaceStation& dock)
+{
+// std::cout << ship.stringize() << " has docked at " << dock.stringize() << std::endl;
+ return "shipStation";
+}
+
+std::string asteroidStation(Asteroid& rock, SpaceStation& dock)
+{
+// std::cout << rock.stringize() << " has damaged " << dock.stringize() << std::endl;
+ return "asteroidStation";
+}
+
+/*------------------------------- Test code --------------------------------*/
+namespace tut
+{
+ struct dispatch_data
+ {
+ dispatch_data():
+ home(new SpaceStation("Terra Station")),
+ obstacle(new Asteroid("Ganymede")),
+ tug(new CommercialShip("Pilotfish")),
+ patrol(new MilitaryShip("Enterprise"))
+ {}
+
+ // Instantiate and populate the DoubleDispatch object.
+ typedef LLDoubleDispatch<std::string, GameObject> DD;
+ DD dispatcher;
+
+ // Instantiate a few GameObjects. Make sure we refer to them
+ // polymorphically, and don't let them leak.
+ std::unique_ptr<GameObject> home;
+ std::unique_ptr<GameObject> obstacle;
+ std::unique_ptr<GameObject> tug;
+ std::unique_ptr<GameObject> patrol;
+
+ // prototype objects
+ Asteroid dummyAsteroid;
+ SpaceShip dummyShip;
+ MilitaryShip dummyMilitary;
+ CommercialShip dummyCommercial;
+ SpaceStation dummyStation;
+ };
+ typedef test_group<dispatch_data> dispatch_group;
+ typedef dispatch_group::object dispatch_object;
+ tut::dispatch_group ddgr("double dispatch");
+
+ template<> template<>
+ void dispatch_object::test<1>()
+ {
+ // Describe param types using explicit DD::Type objects
+ // (order-sensitive add() variant)
+ dispatcher.add(DD::Type<SpaceShip>(), DD::Type<Asteroid>(), shipAsteroid, true);
+ // naive adding, won't work
+ dispatcher.add(DD::Type<MilitaryShip>(), DD::Type<Asteroid>(), militaryShipAsteroid, true);
+ dispatcher.add(DD::Type<SpaceShip>(), DD::Type<SpaceStation>(), shipStation, true);
+ dispatcher.add(DD::Type<Asteroid>(), DD::Type<SpaceStation>(), asteroidStation, true);
+
+ // Try colliding them.
+ ensure_equals(dispatcher(*home, *tug), // reverse params, SpaceShip subclass
+ "shipStation");
+ ensure_equals(dispatcher(*patrol, *home), // forward params, SpaceShip subclass
+ "shipStation");
+ ensure_equals(dispatcher(*obstacle, *home), // forward params
+ "asteroidStation");
+ ensure_equals(dispatcher(*home, *obstacle), // reverse params
+ "asteroidStation");
+ ensure_equals(dispatcher(*tug, *obstacle), // forward params, SpaceShip subclass
+ "shipAsteroid");
+ ensure_equals(dispatcher(*obstacle, *patrol), // reverse params, SpaceShip subclass
+ // won't use militaryShipAsteroid() because it was added
+ // in wrong order
+ "shipAsteroid");
+ }
+
+ template<> template<>
+ void dispatch_object::test<2>()
+ {
+ // Describe param types using explicit DD::Type objects
+ // (order-sensitive add() variant)
+ // adding in correct order
+ dispatcher.add(DD::Type<MilitaryShip>(), DD::Type<Asteroid>(), militaryShipAsteroid, true);
+ dispatcher.add(DD::Type<SpaceShip>(), DD::Type<Asteroid>(), shipAsteroid, true);
+ dispatcher.add(DD::Type<SpaceShip>(), DD::Type<SpaceStation>(), shipStation, true);
+ dispatcher.add(DD::Type<Asteroid>(), DD::Type<SpaceStation>(), asteroidStation, true);
+
+ ensure_equals(dispatcher(*patrol, *obstacle), "militaryShipAsteroid");
+ ensure_equals(dispatcher(*tug, *obstacle), "shipAsteroid");
+ }
+
+ template<> template<>
+ void dispatch_object::test<3>()
+ {
+ // Describe param types with actual prototype instances
+ // (order-insensitive add() variant)
+ dispatcher.add(dummyMilitary, dummyAsteroid, militaryShipAsteroid);
+ dispatcher.add(dummyShip, dummyAsteroid, shipAsteroid);
+ dispatcher.add(dummyShip, dummyStation, shipStation);
+ dispatcher.add(dummyAsteroid, dummyStation, asteroidStation);
+
+ ensure_equals(dispatcher(*patrol, *obstacle), "militaryShipAsteroid");
+ ensure_equals(dispatcher(*tug, *obstacle), "shipAsteroid");
+ ensure_equals(dispatcher(*obstacle, *patrol), "");
+ }
+
+ template<> template<>
+ void dispatch_object::test<4>()
+ {
+ // Describe param types with actual prototype instances
+ // (order-insensitive add() variant)
+ dispatcher.add(dummyShip, dummyAsteroid, shipAsteroid);
+ // Even if we add the militaryShipAsteroid in the wrong order, it
+ // should still work.
+ dispatcher.add(dummyMilitary, dummyAsteroid, militaryShipAsteroid);
+ dispatcher.add(dummyShip, dummyStation, shipStation);
+ dispatcher.add(dummyAsteroid, dummyStation, asteroidStation);
+
+ ensure_equals(dispatcher(*patrol, *obstacle), "militaryShipAsteroid");
+ ensure_equals(dispatcher(*tug, *obstacle), "shipAsteroid");
+ }
+
+ template<> template<>
+ void dispatch_object::test<5>()
+ {
+ dispatcher.add<SpaceShip, Asteroid>(shipAsteroid);
+ dispatcher.add<MilitaryShip, Asteroid>(militaryShipAsteroid);
+ dispatcher.add<SpaceShip, SpaceStation>(shipStation);
+ dispatcher.add<Asteroid, SpaceStation>(asteroidStation);
+
+ ensure_equals(dispatcher(*patrol, *obstacle), "militaryShipAsteroid");
+ ensure_equals(dispatcher(*tug, *obstacle), "shipAsteroid");
+ }
+} // namespace tut
diff --git a/indra/llcommon/tests/llerror_test.cpp b/indra/llcommon/tests/llerror_test.cpp
index 3ec429530c..653f64f1b6 100644
--- a/indra/llcommon/tests/llerror_test.cpp
+++ b/indra/llcommon/tests/llerror_test.cpp
@@ -59,8 +59,10 @@ static const char* FieldName[] =
namespace
{
-#ifdef __clang__
+#if defined(LL_CLANG)
# pragma clang diagnostic ignored "-Wunused-function"
+#elif defined(LL_GNUC)
+# pragma GCC diagnostic ignored "-Wunused-function"
#endif
void test_that_error_h_includes_enough_things_to_compile_a_message()
{
diff --git a/indra/llcommon/tests/llevents_test.cpp b/indra/llcommon/tests/llevents_test.cpp
new file mode 100644
index 0000000000..c5d5dcb9d4
--- /dev/null
+++ b/indra/llcommon/tests/llevents_test.cpp
@@ -0,0 +1,506 @@
+/**
+ * @file llevents_tut.cpp
+ * @author Nat Goodspeed
+ * @date 2008-09-12
+ * @brief Test of llevents.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$
+ */
+
+#if LL_WINDOWS
+#pragma warning (disable : 4675) // "resolved by ADL" -- just as I want!
+#endif
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+// UGLY HACK! We want to verify state internal to the classes without
+// providing public accessors.
+#define testable public
+#include "llevents.h"
+#undef testable
+// STL headers
+// std headers
+#include <iostream>
+#include <typeinfo>
+// external library headers
+#include <boost/bind.hpp>
+// other Linden headers
+#include "tests/listener.h" // must PRECEDE lltut.h
+#include "lltut.h"
+#include "catch_and_store_what_in.h"
+#include "stringize.h"
+
+template<typename T>
+T make(const T& value)
+{
+ return value;
+}
+
+/*****************************************************************************
+ * tut test group
+ *****************************************************************************/
+namespace tut
+{
+struct events_data
+{
+ events_data() :
+ pumps(LLEventPumps::instance()),
+ listener0("first"),
+ listener1("second")
+ {
+ }
+ LLEventPumps& pumps;
+ Listener listener0;
+ Listener listener1;
+
+ void check_listener(const std::string& desc, const Listener& listener, LLSD::Integer got)
+ {
+ ensure_equals(STRINGIZE(listener << ' ' << desc),
+ listener.getLastEvent().asInteger(), got);
+ }
+};
+typedef test_group<events_data> events_group;
+typedef events_group::object events_object;
+tut::events_group evgr("events");
+
+template<> template<>
+void events_object::test<1>()
+{
+ set_test_name("basic operations");
+ // Having to modify this to track the statically-
+ // constructed pumps in other TUT modules in this giant monolithic test
+ // executable isn't such a hot idea.
+ // ensure_equals("initial pump", pumps.mPumpMap.size(), 1);
+ size_t initial_pumps(pumps.mPumpMap.size());
+ LLEventPump& per_frame(pumps.obtain("per-frame"));
+ ensure_equals("first explicit pump", pumps.mPumpMap.size(), initial_pumps + 1);
+ // Verify that per_frame was instantiated as an LLEventStream.
+ ensure("LLEventStream leaf class", dynamic_cast<LLEventStream*> (&per_frame));
+ ensure("enabled", per_frame.enabled());
+ // Trivial test, but posting an event to an EventPump with no
+ // listeners should not blow up. The test is relevant because defining
+ // a boost::signal with a non-void return signature, using the default
+ // combiner, blows up if there are no listeners. This is because the
+ // default combiner is defined to return the value returned by the
+ // last listener, which is meaningless if there were no listeners.
+ per_frame.post(0);
+ LLBoundListener connection = listener0.listenTo(per_frame);
+ ensure("connected", connection.connected());
+ ensure("not blocked", !connection.blocked());
+ per_frame.post(1);
+ check_listener("received", listener0, 1);
+ { // block the connection
+ LLEventPump::Blocker block(connection);
+ ensure("blocked", connection.blocked());
+ per_frame.post(2);
+ check_listener("not updated", listener0, 1);
+ } // unblock
+ ensure("unblocked", !connection.blocked());
+ per_frame.post(3);
+ check_listener("unblocked", listener0, 3);
+ LLBoundListener sameConnection = per_frame.getListener(listener0.getName());
+ ensure("still connected", sameConnection.connected());
+ ensure("still not blocked", !sameConnection.blocked());
+ { // block it again
+ LLEventPump::Blocker block(sameConnection);
+ ensure("re-blocked", sameConnection.blocked());
+ per_frame.post(4);
+ check_listener("re-blocked", listener0, 3);
+ } // unblock
+ std::string threw = catch_what<LLEventPump::DupListenerName>(
+ [&per_frame, this](){
+ // NOTE: boost::bind() saves its arguments by VALUE! If you pass
+ // an object instance rather than a pointer, you'll end up binding
+ // to an internal copy of that instance! Use boost::ref() to
+ // capture a reference instead.
+ per_frame.listen(listener0.getName(), // note bug, dup name
+ boost::bind(&Listener::call, boost::ref(listener1), _1));
+ });
+ ensure_equals(threw,
+ std::string("DupListenerName: "
+ "Attempt to register duplicate listener name '") +
+ listener0.getName() + "' on " + typeid(per_frame).name() +
+ " '" + per_frame.getName() + "'");
+ // do it right this time
+ listener1.listenTo(per_frame);
+ per_frame.post(5);
+ check_listener("got", listener0, 5);
+ check_listener("got", listener1, 5);
+ per_frame.enable(false);
+ per_frame.post(6);
+ check_listener("didn't get", listener0, 5);
+ check_listener("didn't get", listener1, 5);
+ per_frame.enable();
+ per_frame.post(7);
+ check_listener("got", listener0, 7);
+ check_listener("got", listener1, 7);
+ per_frame.stopListening(listener0.getName());
+ ensure("disconnected 0", ! connection.connected());
+ ensure("disconnected 1", ! sameConnection.connected());
+ per_frame.post(8);
+ check_listener("disconnected", listener0, 7);
+ check_listener("still connected", listener1, 8);
+ per_frame.stopListening(listener1.getName());
+ per_frame.post(9);
+ check_listener("disconnected", listener1, 8);
+}
+
+template<> template<>
+void events_object::test<2>()
+{
+ set_test_name("callstop() returning true");
+ LLEventPump& per_frame(pumps.obtain("per-frame"));
+ listener0.reset(0);
+ listener1.reset(0);
+ LLBoundListener bound0 = listener0.listenTo(per_frame, &Listener::callstop);
+ LLBoundListener bound1 = listener1.listenTo(per_frame, &Listener::call,
+ // after listener0
+ make<LLEventPump::NameList>(LLEventPump::NameList{ listener0.getName() }));
+ ensure("enabled", per_frame.enabled());
+ ensure("connected 0", bound0.connected());
+ ensure("unblocked 0", !bound0.blocked());
+ ensure("connected 1", bound1.connected());
+ ensure("unblocked 1", !bound1.blocked());
+ per_frame.post(1);
+ check_listener("got", listener0, 1);
+ // Because listener0.callstop() returns true, control never reaches listener1.call().
+ check_listener("got", listener1, 0);
+}
+
+bool chainEvents(Listener& someListener, const LLSD& event)
+{
+ // Make this call so we can watch for side effects for test purposes.
+ someListener.call(event);
+ // This function represents a recursive event chain -- or some other
+ // scenario in which an event handler raises additional events.
+ int value = event.asInteger();
+ if (value)
+ {
+ LLEventPumps::instance().obtain("login").post(value - 1);
+ }
+ return false;
+}
+
+template<> template<>
+void events_object::test<3>()
+{
+ set_test_name("explicitly-instantiated LLEventStream");
+ // Explicitly instantiate an LLEventStream, and verify that it
+ // self-registers with LLEventPumps
+ size_t registered = pumps.mPumpMap.size();
+ size_t owned = pumps.mOurPumps.size();
+ LLEventPump* localInstance;
+ {
+ LLEventStream myEventStream("stream");
+ localInstance = &myEventStream;
+ LLEventPump& stream(pumps.obtain("stream"));
+ ensure("found named LLEventStream instance", &stream == localInstance);
+ ensure_equals("registered new instance", pumps.mPumpMap.size(), registered + 1);
+ ensure_equals("explicit instance not owned", pumps.mOurPumps.size(), owned);
+ } // destroy myEventStream -- should unregister
+ ensure_equals("destroyed instance unregistered", pumps.mPumpMap.size(), registered);
+ ensure_equals("destroyed instance not owned", pumps.mOurPumps.size(), owned);
+ LLEventPump& stream(pumps.obtain("stream"));
+ ensure("new LLEventStream instance", &stream != localInstance);
+ ensure_equals("obtain()ed instance registered", pumps.mPumpMap.size(), registered + 1);
+ ensure_equals("obtain()ed instance owned", pumps.mOurPumps.size(), owned + 1);
+}
+
+template<> template<>
+void events_object::test<4>()
+{
+ set_test_name("stopListening()");
+ LLEventPump& login(pumps.obtain("login"));
+ listener0.listenTo(login);
+ login.stopListening(listener0.getName());
+ // should not throw because stopListening() should have removed name
+ listener0.listenTo(login, &Listener::callstop);
+ LLBoundListener wrong = login.getListener("bogus");
+ ensure("bogus connection disconnected", !wrong.connected());
+ ensure("bogus connection blocked", wrong.blocked());
+}
+
+template<> template<>
+void events_object::test<5>()
+{
+ set_test_name("chaining LLEventPump instances");
+ LLEventPump& upstream(pumps.obtain("upstream"));
+ // One potentially-useful construct is to chain LLEventPumps together.
+ // Among other things, this allows you to turn subsets of listeners on
+ // and off in groups.
+ LLEventPump& filter0(pumps.obtain("filter0"));
+ LLEventPump& filter1(pumps.obtain("filter1"));
+ upstream.listen(filter0.getName(), boost::bind(&LLEventPump::post, boost::ref(filter0), _1));
+ upstream.listen(filter1.getName(), boost::bind(&LLEventPump::post, boost::ref(filter1), _1));
+ listener0.listenTo(filter0);
+ listener1.listenTo(filter1);
+ listener0.reset(0);
+ listener1.reset(0);
+ upstream.post(1);
+ check_listener("got unfiltered", listener0, 1);
+ check_listener("got unfiltered", listener1, 1);
+ filter0.enable(false);
+ upstream.post(2);
+ check_listener("didn't get filtered", listener0, 1);
+ check_listener("got filtered", listener1, 2);
+}
+
+template<> template<>
+void events_object::test<6>()
+{
+ set_test_name("listener dependency order");
+ typedef LLEventPump::NameList NameList;
+ LLEventPump& button(pumps.obtain("button"));
+ Collect collector;
+ button.listen("Mary",
+ boost::bind(&Collect::add, boost::ref(collector), "Mary", _1),
+ // state that "Mary" must come after "checked"
+ make<NameList>(NameList{ "checked" }));
+ button.listen("checked",
+ boost::bind(&Collect::add, boost::ref(collector), "checked", _1),
+ // "checked" must come after "spot"
+ make<NameList>(NameList{ "spot" }));
+ button.listen("spot",
+ boost::bind(&Collect::add, boost::ref(collector), "spot", _1));
+ button.post(1);
+ ensure_equals(collector.result, make<StringVec>(StringVec{ "spot", "checked", "Mary" }));
+ collector.clear();
+ button.stopListening("Mary");
+ button.listen("Mary",
+ boost::bind(&Collect::add, boost::ref(collector), "Mary", _1),
+ LLEventPump::empty, // no after dependencies
+ // now "Mary" must come before "spot"
+ make<NameList>(NameList{ "spot" }));
+ button.post(2);
+ ensure_equals(collector.result, make<StringVec>(StringVec{ "Mary", "spot", "checked" }));
+ collector.clear();
+ button.stopListening("spot");
+ std::string threw = catch_what<LLEventPump::Cycle>(
+ [&button, &collector](){
+ button.listen("spot",
+ boost::bind(&Collect::add, boost::ref(collector), "spot", _1),
+ // after "Mary" and "checked" -- whoops!
+ make<NameList>(NameList{ "Mary", "checked" }));
+ });
+ // Obviously the specific wording of the exception text can
+ // change; go ahead and change the test to match.
+ // Establish that it contains:
+ // - the name and runtime type of the LLEventPump
+ ensure_contains("LLEventPump type", threw, typeid(button).name());
+ ensure_contains("LLEventPump name", threw, "'button'");
+ // - the name of the new listener that caused the problem
+ ensure_contains("new listener name", threw, "'spot'");
+ // - a synopsis of the problematic dependencies.
+ ensure_contains("cyclic dependencies", threw,
+ "\"Mary\" -> before (\"spot\")");
+ ensure_contains("cyclic dependencies", threw,
+ "after (\"spot\") -> \"checked\"");
+ ensure_contains("cyclic dependencies", threw,
+ "after (\"Mary\", \"checked\") -> \"spot\"");
+ button.listen("yellow",
+ boost::bind(&Collect::add, boost::ref(collector), "yellow", _1), make<NameList>(NameList{ "checked" }));
+ button.listen("shoelaces",
+ boost::bind(&Collect::add, boost::ref(collector), "shoelaces", _1), make<NameList>(NameList{ "checked" }));
+ button.post(3);
+ ensure_equals(collector.result, make<StringVec>(StringVec{ "Mary", "checked", "yellow", "shoelaces" }));
+ collector.clear();
+ threw = catch_what<LLEventPump::OrderChange>(
+ [&button, &collector](){
+ button.listen("of",
+ boost::bind(&Collect::add, boost::ref(collector), "of", _1),
+ make<NameList>(NameList{ "shoelaces" }),
+ make<NameList>(NameList{ "yellow" }));
+ });
+ // Same remarks about the specific wording of the exception. Just
+ // ensure that it contains enough information to clarify the
+ // problem and what must be done to resolve it.
+ ensure_contains("LLEventPump type", threw, typeid(button).name());
+ ensure_contains("LLEventPump name", threw, "'button'");
+ ensure_contains("new listener name", threw, "'of'");
+ ensure_contains("prev listener name", threw, "'yellow'");
+ // std::cout << "Thrown Exception: " << threw << std::endl;
+ ensure_contains("old order", threw, "was: Mary, checked, yellow, shoelaces");
+ ensure_contains("new order", threw, "now: Mary, checked, shoelaces, of, yellow");
+ button.post(4);
+ ensure_equals(collector.result, make<StringVec>(StringVec{ "Mary", "checked", "yellow", "shoelaces" }));
+}
+
+template<> template<>
+void events_object::test<7>()
+{
+ set_test_name("tweaked and untweaked LLEventPump instance names");
+ { // nested scope
+ // Hand-instantiate an LLEventStream...
+ LLEventStream bob("bob");
+ std::string threw = catch_what<LLEventPump::DupPumpName>(
+ [](){
+ // then another with a duplicate name.
+ LLEventStream bob2("bob");
+ });
+ ensure("Caught DupPumpName", !threw.empty());
+ } // delete first 'bob'
+ LLEventStream bob("bob"); // should work, previous one unregistered
+ LLEventStream bob1("bob", true);// allowed to tweak name
+ ensure_equals("tweaked LLEventStream name", bob1.getName(), "bob1");
+ std::vector<std::shared_ptr<LLEventStream> > streams;
+ for (int i = 2; i <= 10; ++i)
+ {
+ streams.push_back(std::shared_ptr<LLEventStream>(new LLEventStream("bob", true)));
+ }
+ ensure_equals("last tweaked LLEventStream name", streams.back()->getName(), "bob10");
+}
+
+// Define a function that accepts an LLListenerOrPumpName
+void eventSource(const LLListenerOrPumpName& listener)
+{
+ // Pretend that some time has elapsed. Call listener immediately.
+ listener(17);
+}
+
+template<> template<>
+void events_object::test<8>()
+{
+ set_test_name("LLListenerOrPumpName");
+ // Passing a boost::bind() expression to LLListenerOrPumpName
+ listener0.reset(0);
+ eventSource(boost::bind(&Listener::call, boost::ref(listener0), _1));
+ check_listener("got by listener", listener0, 17);
+ // Passing a string LLEventPump name to LLListenerOrPumpName
+ listener0.reset(0);
+ LLEventStream random("random");
+ listener0.listenTo(random);
+ eventSource("random");
+ check_listener("got by pump name", listener0, 17);
+ std::string threw = catch_what<LLListenerOrPumpName::Empty>(
+ [](){
+ LLListenerOrPumpName empty;
+ empty(17);
+ });
+
+ ensure("threw Empty", !threw.empty());
+}
+
+class TempListener: public Listener
+{
+public:
+ TempListener(const std::string& name, bool& liveFlag) :
+ Listener(name), mLiveFlag(liveFlag)
+ {
+ mLiveFlag = true;
+ }
+
+ virtual ~TempListener()
+ {
+ mLiveFlag = false;
+ }
+
+private:
+ bool& mLiveFlag;
+};
+
+template<> template<>
+void events_object::test<9>()
+{
+ set_test_name("listen(boost::bind(...TempListener...))");
+ // listen() can't do anything about a plain TempListener instance:
+ // it's not managed with shared_ptr, nor is it an LLEventTrackable subclass
+ bool live = false;
+ LLEventPump& heaptest(pumps.obtain("heaptest"));
+ LLBoundListener connection;
+ {
+ TempListener tempListener("temp", live);
+ ensure("TempListener constructed", live);
+ connection = heaptest.listen(tempListener.getName(),
+ boost::bind(&Listener::call,
+ boost::ref(tempListener),
+ _1));
+ heaptest.post(1);
+ check_listener("received", tempListener, 1);
+ } // presumably this will make newListener go away?
+ // verify that
+ ensure("TempListener destroyed", !live);
+ // This is the case against which we can't defend. Don't even try to
+ // post to heaptest -- that would engage Undefined Behavior.
+ // Cautiously inspect connection...
+ ensure("misleadingly connected", connection.connected());
+ // then disconnect by hand.
+ heaptest.stopListening("temp");
+}
+
+class TempTrackableListener: public TempListener, public LLEventTrackable
+{
+public:
+ TempTrackableListener(const std::string& name, bool& liveFlag):
+ TempListener(name, liveFlag)
+ {}
+};
+
+template<> template<>
+void events_object::test<10>()
+{
+ set_test_name("listen(boost::bind(...TempTrackableListener ref...))");
+ bool live = false;
+ LLEventPump& heaptest(pumps.obtain("heaptest"));
+ LLBoundListener connection;
+ {
+ TempTrackableListener tempListener("temp", live);
+ ensure("TempTrackableListener constructed", live);
+ connection = heaptest.listen(tempListener.getName(),
+ boost::bind(&TempTrackableListener::call,
+ boost::ref(tempListener), _1));
+ heaptest.post(1);
+ check_listener("received", tempListener, 1);
+ } // presumably this will make tempListener go away?
+ // verify that
+ ensure("TempTrackableListener destroyed", ! live);
+ ensure("implicit disconnect", ! connection.connected());
+ // now just make sure we don't blow up trying to access a freed object!
+ heaptest.post(2);
+}
+
+template<> template<>
+void events_object::test<11>()
+{
+ set_test_name("listen(boost::bind(...TempTrackableListener pointer...))");
+ bool live = false;
+ LLEventPump& heaptest(pumps.obtain("heaptest"));
+ LLBoundListener connection;
+ {
+ TempTrackableListener* newListener(new TempTrackableListener("temp", live));
+ ensure("TempTrackableListener constructed", live);
+ connection = heaptest.listen(newListener->getName(),
+ boost::bind(&TempTrackableListener::call,
+ newListener, _1));
+ heaptest.post(1);
+ check_listener("received", *newListener, 1);
+ // explicitly destroy newListener
+ delete newListener;
+ }
+ // verify that
+ ensure("TempTrackableListener destroyed", ! live);
+ ensure("implicit disconnect", ! connection.connected());
+ // now just make sure we don't blow up trying to access a freed object!
+ heaptest.post(2);
+}
+
+} // namespace tut
diff --git a/indra/llcommon/tests/llfile_test.cpp b/indra/llcommon/tests/llfile_test.cpp
new file mode 100644
index 0000000000..ab4616cfb2
--- /dev/null
+++ b/indra/llcommon/tests/llfile_test.cpp
@@ -0,0 +1,299 @@
+/**
+ * @file llfile_test.cpp
+ * @author Frederick Martian
+ * @date 2025-11
+ * @brief LLFile test cases.
+ *
+ * $LicenseInfo:firstyear=2025&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2025, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * 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 <tut/tut.hpp>
+#include "lltut.h"
+#include "linden_common.h"
+#include "llfile.h"
+
+namespace tut
+{
+ static void clear_entire_dir(const std::filesystem::path& dir_path)
+ {
+ std::error_code ec;
+ std::filesystem::remove_all(dir_path, ec);
+ }
+
+ static std::filesystem::path append_filename(const std::filesystem::path& dir, const std::u8string& element)
+ {
+ std::filesystem::path path = dir;
+ return path.append(element);
+ }
+
+ static std::filesystem::path get_testdir(const std::filesystem::path& tempdir)
+ {
+ return append_filename(tempdir, std::u8string(u8"test_dir"));
+ }
+
+ static std::filesystem::path get_testdir_unicode(const std::filesystem::path& tempdir)
+ {
+ // Example Unicode directory name: "test_ユニコード_dir"
+ return append_filename(tempdir, std::u8string(u8"test_\xE3\x83\xA6\xE3\x83\x8B\xE3\x82\xB3\xE3\x83\xBC\xE3\x83\x89_dir"));
+ }
+
+ struct llfile_test
+ {
+ std::filesystem::path tempdir = LLFile::tmpdir();
+ std::filesystem::path testdir = get_testdir(tempdir);
+ };
+ typedef test_group<llfile_test> llfile_test_t;
+ typedef llfile_test_t::object llfile_test_object_t;
+ tut::llfile_test_t tut_llfile_test("llfile_test");
+
+ template<> template<>
+ void llfile_test_object_t::test<1>()
+ {
+ // Test creating directories and files and deleting them and checking if the
+ // relevant status functions work as expected
+ ensure("LLFile::tmpdir() empty", !tempdir.empty());
+ ensure("LLFile::tmpdir() doesn't exist", LLFile::exists(tempdir));
+ ensure("LLFile::tmpdir() is not a directory", LLFile::isdir(tempdir));
+ ensure("LLFile::tmpdir() should not be a file", !LLFile::isfile(tempdir));
+
+ // Make sure there is nothing left from a previous test run
+ clear_entire_dir(testdir);
+ ensure("llfile_test should not exist anymore", !LLFile::exists(testdir));
+
+ int rc = LLFile::mkdir(testdir);
+ ensure("LLFile::mkdir() failed", rc == 0);
+ ensure("llfile_test should be a directory", LLFile::isdir(testdir));
+ rc = LLFile::mkdir(testdir);
+ ensure("LLFile::mkdir() should not fail when the directory already exists", rc == 0);
+
+ std::filesystem::path testfile1 = testdir;
+ testfile1.append("llfile_test.dat");
+ ensure("llfile_test1.dat should not yet exist", !LLFile::exists(testfile1));
+
+ const char* testdata = "testdata";
+ S64 bytes = LLFile::write(testfile1, testdata, 0, sizeof(testdata));
+ ensure("LLFile::write() did not write correctly", bytes == sizeof(testdata));
+
+ rc = LLFile::remove(testfile1);
+ ensure("LLFile::remove() for file test_file.dat", rc == 0);
+ ensure("llfile_test.dat should not exist anymore", !LLFile::exists(testfile1));
+ ensure("llfile_test.dat should not be a file", !LLFile::isfile(testfile1));
+ ensure("llfile_test.dat should not be a directory", !LLFile::isdir(testfile1));
+ ensure("llfile_test.dat should not be a symlink", !LLFile::islink(testfile1));
+
+ rc = LLFile::remove(testdir);
+ ensure("LLFile::remove() for directory llfile_test failed", rc == 0);
+ ensure("llfile_test should not exist anymore", !LLFile::exists(testdir));
+ }
+
+ template<> template<>
+ void llfile_test_object_t::test<2>()
+ {
+ // High level static file IO functions to read and write data files
+ LLFile::mkdir(testdir);
+ ensure("llfile_test should exist", LLFile::isdir(testdir));
+
+ std::filesystem::path testfile1 = testdir;
+ testfile1.append("llfile_test.dat");
+
+ std::string testdata1("testdata");
+ std::string testdata2("datateststuff");
+ std::time_t current = time(nullptr);
+ S64 bytes = LLFile::write(testfile1, testdata1.c_str(), 0, testdata1.length());
+ ensure("LLFile::write() did not write correctly", bytes == testdata1.length());
+ ensure("llfile_test.dat should exist", LLFile::exists(testfile1));
+ ensure("llfile_test.dat should be a file", LLFile::isfile(testfile1));
+ ensure("llfile_test.dat should not be a directory", !LLFile::isdir(testfile1));
+
+ bytes = LLFile::size(testfile1);
+ ensure("LLFile::size() did not return the correct size", bytes == testdata1.length());
+
+ std::string data = LLFile::getContents(testfile1);
+ ensure("LLFile::getContents() did not return the correct size data", data.length() == testdata1.length());
+ ensure_memory_matches("LLFile::getContents() did not read correct data", testdata1.c_str(), (U32)testdata1.length(), data.c_str(), (U32)data.length());
+
+ char buffer[1024];
+ bytes = LLFile::read(testfile1, buffer, 0, testdata1.length());
+ ensure("LLFile:read() did not return the correct size", bytes == testdata1.length());
+ ensure_memory_matches("LLFile::read() did not read correct data", testdata1.c_str(), (U32)bytes, buffer, (U32)bytes);
+
+ // What if we try to read more data than there is in the file?
+ bytes = LLFile::read(testfile1, buffer, 0, bytes + 10);
+ ensure("LLFile:read() did not correctly stop on eof", bytes == testdata1.length());
+ ensure_memory_matches("LLFile::read() did not read correct data", testdata1.c_str(), (U32)bytes, buffer, (U32)bytes);
+
+ // Let's append more data
+ bytes = LLFile::write(testfile1, testdata2.c_str(), -1, testdata2.length());
+ ensure("LLFile::write() did not write correctly", bytes == testdata2.length());
+
+ bytes = LLFile::size(testfile1);
+ ensure("LLFile::size() did not return the correct size", bytes == testdata1.length() + testdata2.length());
+ bytes = LLFile::read(testfile1, buffer, 0, bytes);
+ ensure("LLFile:read() did not read correct number of bytes", bytes == testdata1.length() + testdata2.length());
+ ensure_memory_matches("LLFile:read() did not read correct testdata1", testdata1.c_str(), (U32)testdata1.length(), buffer, (U32)testdata1.length());
+ ensure_memory_matches("LLFile:read() did not read correct testdata2", testdata2.c_str(), (U32)testdata2.length(), buffer + testdata1.length(), (U32)testdata2.length());
+ }
+
+ template<> template<>
+ void llfile_test_object_t::test<3>()
+ {
+ const size_t numints = 1024;
+
+ // Testing the LLFile class implementation
+ std::filesystem::path testfile = testdir;
+ testfile.append("llfile_test.bin");
+
+ int data[numints];
+ for (int &t : data)
+ {
+ t = rand();
+ }
+
+ std::error_code ec;
+ LLFile fileout(testfile, LLFile::out, ec);
+ ensure("LLFile constructor did not open correctly", (bool)fileout);
+ ensure("error_code from LLFile constructor should not indicate an error", !ec);
+ if (fileout)
+ {
+ S64 length = fileout.size(ec);
+ ensure("freshly created file should be empty", length == 0);
+ ensure("error_code from LLFile::size() should not indicate an error", !ec);
+ S64 bytes = fileout.write(data, sizeof(data), ec);
+ ensure("LLFile::write() did not write correctly", bytes == sizeof(data));
+ ensure("error_code from LLFile::write() should not indicate an error", !ec);
+ bytes = fileout.write(data, sizeof(data), ec);
+ ensure("LLFile::write() did not write correctly", bytes == sizeof(data));
+ ensure("error_code from LLFile::write() should not indicate an error", !ec);
+ bytes = fileout.size(ec);
+ ensure("LLFile::size() returned wrong size", bytes == 2 * sizeof(data));
+ ensure("error_code from LLFile::size() should not indicate an error", !ec);
+ fileout.close();
+ }
+
+ LLFile filein(testfile, LLFile::in, ec);
+ ensure("LLFile constructor did not open correctly", (bool)filein);
+ ensure("error_code from LLFile constructor should not indicate an error", !ec);
+ if (filein)
+ {
+ S64 length = filein.size(ec);
+ ensure("LLFile::size() returned wrong size", length == 2 * sizeof(data));
+ ensure("error_code from LLFile::size() should not indicate an error", !ec);
+ char* buffer = (char*)malloc(length);
+ S64 bytes = filein.read(buffer, length, ec);
+ ensure("LLFile::read() did not read correctly", bytes == length);
+ ensure("error_code from LLFile::read() should not indicate an error", !ec);
+ ensure_memory_matches("LLFile:read() did not read correct data1", data, (U32)sizeof(data), buffer, (U32)sizeof(data));
+ ensure_memory_matches("LLFile:read() did not read correct data2", data, (U32)sizeof(data), buffer + sizeof(data), (U32)sizeof(data));
+ S64 offset = filein.tell(ec);
+ ensure("LLFile::tell() returned a bad offset", offset == length);
+ ensure("error_code from LLFile::read() should not indicate an error", !ec);
+ offset = sizeof(data) / 2;
+ int rc = filein.seek(offset, ec);
+ ensure("LLFile::seek() indicated an error", rc == 0);
+ ensure("error_code from LLFile::seek() should not indicate an error", !ec);
+ bytes = filein.read(buffer, 2 * sizeof(data), ec);
+ ensure("LLFile::read() did not read correctly", bytes == sizeof(data) + offset);
+ ensure("error_code from LLFile::read() should not indicate an error", !ec);
+ ensure_memory_matches("LLFile:read() did not read correct data3", (char*)data + offset, (U32)offset, buffer, (U32)offset);
+ ensure_memory_matches("LLFile:read() did not read correct data4", (char*)data, (U32)sizeof(data), buffer + offset, (U32)sizeof(data));
+ filein.close();
+
+ free(buffer);
+ }
+ }
+
+ template<> template<>
+ void llfile_test_object_t::test<4>()
+ {
+ // Testing the LLFile class implementation with wrong paths and parameters
+ std::filesystem::path testfile = testdir;
+ testfile.append("llfile_test.bin");
+
+ std::error_code ec;
+ LLFile file(testfile, LLFile::out | LLFile::noreplace, ec);
+ ensure("LLFile constructor should not have opened the already existing file", !file);
+ ensure("error_code from LLFile constructor should indicate an error", (bool)ec);
+
+ LLFile::remove(testfile);
+ file = LLFile(testfile, LLFile::out | LLFile::app | LLFile::trunc, ec);
+ ensure("LLFile constructor should not have opened the file with conflicting flags", !file);
+ ensure("error_code from LLFile constructor should indicate an error", (bool)ec);
+
+ file = LLFile(testfile, LLFile::out | LLFile::app | LLFile::noreplace, ec);
+ ensure("LLFile constructor should not have opened the file with conflicting flags", !file);
+ ensure("error_code from LLFile constructor should indicate an error", (bool)ec);
+
+ testfile = testdir;
+ testfile.append("llfile_test");
+ testfile.append("llfile_test.bin");
+
+ file = LLFile(testfile, LLFile::in, ec);
+ ensure("LLFile constructor should not have been able to open the file in the non-existing directory", !file);
+ ensure("error_code from LLFile constructor should indicate an error", (bool)ec);
+ }
+
+ template<> template<>
+ void llfile_test_object_t::test<5>()
+ {
+ // Test file and directory operations with Unicode paths and filenames
+ std::filesystem::path testdir_unicode = get_testdir_unicode(tempdir);
+
+ // Unicode filename: "ファイル_テスト.bin" (means "file_test.bin" in Japanese)
+ std::string unicode_filename = "\xE3\x83\x95\xE3\x82\xA1\xE3\x82\xA4\xE3\x83\xAB_\xE3\x83\x86\xE3\x82\xB9\xE3\x83\x88.bin";
+ std::filesystem::path testfile_unicode = testdir_unicode;
+ testfile_unicode.append(unicode_filename);
+
+ // Clean up any previous test artifacts
+ clear_entire_dir(testdir_unicode);
+ ensure("Unicode test directory should not exist", !LLFile::exists(testdir_unicode));
+
+ // Create the Unicode directory
+ int rc = LLFile::mkdir(testdir_unicode);
+ ensure("LLFile::mkdir() failed for Unicode directory", rc == 0);
+ ensure("Unicode test directory should exist", LLFile::isdir(testdir_unicode));
+
+
+ ensure("Unicode test file should not exist", !LLFile::exists(testfile_unicode));
+
+ // Write to the Unicode file
+ const char* testdata = "unicode_testdata";
+ S64 bytes = LLFile::write(testfile_unicode, testdata, 0, strlen(testdata));
+ ensure("Unicode test file should exist", LLFile::exists(testfile_unicode));
+ ensure("Unicode test file should be a file", LLFile::isfile(testfile_unicode));
+ ensure("LLFile::write() did not write correctly to Unicode file", bytes == (S64)strlen(testdata));
+
+ // Read back the data
+ char buffer[64] = {};
+ bytes = LLFile::read(testfile_unicode, buffer, 0, sizeof(buffer));
+ ensure("LLFile::read() did not read correctly from Unicode file", bytes == (S64)strlen(testdata));
+ ensure_memory_matches("LLFile::read() did not read correct Unicode data", testdata, (U32)bytes, buffer, (U32)bytes);
+
+ // Remove the file and directory
+ rc = LLFile::remove(testfile_unicode);
+ ensure("LLFile::remove() failed for Unicode file", rc == 0);
+ ensure("Unicode test file should not exist after removal", !LLFile::exists(testfile_unicode));
+
+ rc = LLFile::remove(testdir_unicode);
+ ensure("LLFile::remove() failed for Unicode directory", rc == 0);
+ ensure("Unicode test directory should not exist after removal", !LLFile::exists(testdir_unicode));
+ }
+} // namespace tut
diff --git a/indra/llcommon/tests/llhttpdate_test.cpp b/indra/llcommon/tests/llhttpdate_test.cpp
new file mode 100644
index 0000000000..b580b09a9f
--- /dev/null
+++ b/indra/llcommon/tests/llhttpdate_test.cpp
@@ -0,0 +1,162 @@
+/**
+ * @file llhttpdate_tut.cpp
+ * @author Kartic Krishnamurthy
+ * @date Wednesday, 18 Jul 2007 17:00:00 GMT :)
+ *
+ * $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 "lltut.h"
+
+#include "lldate.h"
+#include "llcommon.h"
+#include "llframetimer.h"
+
+#include <time.h>
+#include <locale.h>
+
+namespace tut
+{
+ struct httpdate_data
+ {
+ httpdate_data()
+ {
+ }
+ ~httpdate_data()
+ {
+ }
+ LLDate some_date;
+ };
+ typedef test_group<httpdate_data> httpdate_test;
+ typedef httpdate_test::object httpdate_object;
+ tut::httpdate_test httpdate("httpdate");
+
+ template<> template<>
+ void httpdate_object::test<1>()
+ {
+
+ static std::string epoch_expected = "Thursday, 01 Jan 1970 00:00:00 GMT" ;
+ ensure("Check Epoch in RFC 1123", ( epoch_expected == some_date.asRFC1123()));
+ }
+
+ template<> template<>
+ void httpdate_object::test<2>()
+ {
+
+ static std::string expected = "Wednesday, 18 Jul 2007 22:17:24 GMT" ;
+ some_date = LLDate(1184797044.037586);
+ ensure("Check some timestamp in RFC 1123", ( expected == some_date.asRFC1123()));
+ }
+
+ // This test of course most generic.. runs off current time
+ template<> template<>
+ void httpdate_object::test<3>()
+ {
+
+ //F64 sometime = LLFrameTimer::getTotalSeconds();
+ time_t sometime;
+ time(&sometime);
+ some_date = LLDate((F64) sometime);
+ struct tm *result;
+ char expected[255];
+ std::string actual;
+
+ result = gmtime(&sometime);
+ /*
+ std::cout << " seconds: "<< result->tm_sec
+ << ", minutes: " << result->tm_min
+ << ", hours: " << result->tm_hour
+ << ", day of the month: " << result->tm_mday
+ << ", month: " << result->tm_mon
+ << ", year: " << result->tm_year
+ << ", day of the week: " << result->tm_wday
+ << ", day in the year: " << result->tm_yday
+ << ", DST: " << result->tm_isdst << std::endl;
+ */
+ strftime(expected, 255, "%A, %d %b %Y %H:%M:%S GMT", result);
+ actual = some_date.asRFC1123();
+ // probably not a good idea to use strcmp but this is just a unit test
+ ensure("Current time in RFC 1123", (strcmp(expected, actual.c_str()) == 0));
+ }
+
+ void test_date_string(const std::string &locale, struct tm *t,
+ const std::string &fmt, const std::string &expected)
+ {
+ std::string result = LLDate::toHTTPDateString(t, fmt);
+ LLStringUtil::toLower(result);
+ std::string label = std::string("toHTTPDateString - ") + locale;
+ ensure_equals(label.c_str(), result, expected);
+ }
+
+ template<> template<>
+ void httpdate_object::test<4>()
+ {
+ // test localization of http dates
+ const char *en_locale = "en_US.UTF-8";
+ const char *fr_locale = "fr_FR.UTF-8";
+
+ std::string prev_locale = LLStringUtil::getLocale();
+ std::string prev_clocale = std::string(setlocale(LC_TIME, NULL));
+ time_t test_time = 1252374030; // 8 Sep 2009 01:40:01
+ struct tm *t = gmtime(&test_time);
+
+ setlocale(LC_TIME, en_locale);
+ if (strcmp(setlocale(LC_TIME, NULL), en_locale) != 0)
+ {
+ setlocale(LC_TIME, prev_clocale.c_str());
+ skip("Cannot set English locale");
+ }
+
+ LLStringUtil::setLocale(en_locale);
+ test_date_string(en_locale, t, "%d %B %Y - %H:%M", "08 september 2009 - 01:40");
+ test_date_string(en_locale, t, "%H", "01");
+ test_date_string(en_locale, t, "%M", "40");
+ test_date_string(en_locale, t, "%I", "01");
+ test_date_string(en_locale, t, "%d", "08");
+ test_date_string(en_locale, t, "%Y", "2009");
+ test_date_string(en_locale, t, "%p", "am");
+ test_date_string(en_locale, t, "%A", "tuesday");
+ test_date_string(en_locale, t, "%B", "september");
+
+ setlocale(LC_TIME, fr_locale);
+ if (strcmp(setlocale(LC_TIME, NULL), fr_locale) != 0)
+ {
+ LLStringUtil::setLocale(prev_locale);
+ setlocale(LC_TIME, prev_clocale.c_str());
+ skip("Cannot set French locale");
+ }
+
+ LLStringUtil::setLocale(fr_locale);
+ test_date_string(fr_locale, t, "%d %B %Y - %H:%M", "08 septembre 2009 - 01:40");
+ test_date_string(fr_locale, t, "%H", "01");
+ test_date_string(fr_locale, t, "%M", "40");
+ test_date_string(fr_locale, t, "%I", "01");
+ test_date_string(fr_locale, t, "%d", "08");
+ test_date_string(fr_locale, t, "%Y", "2009");
+ test_date_string(fr_locale, t, "%A", "mardi");
+ test_date_string(fr_locale, t, "%B", "septembre");
+
+ LLStringUtil::setLocale(prev_locale);
+ setlocale(LC_TIME, prev_clocale.c_str());
+ }
+}
diff --git a/indra/llcommon/tests/lllazy_test.cpp b/indra/llcommon/tests/lllazy_test.cpp
deleted file mode 100644
index 923fe952a9..0000000000
--- a/indra/llcommon/tests/lllazy_test.cpp
+++ /dev/null
@@ -1,239 +0,0 @@
-/**
- * @file lllazy_test.cpp
- * @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$
- */
-
-// Precompiled header
-#include "linden_common.h"
-// associated header
-#include "lllazy.h"
-// STL headers
-#include <iostream>
-// std headers
-// external library headers
-#include <boost/lambda/construct.hpp>
-#include <boost/lambda/bind.hpp>
-// other Linden headers
-#include "../test/lltut.h"
-#include "../test/catch_and_store_what_in.h"
-
-namespace bll = boost::lambda;
-
-/*****************************************************************************
-* Test classes
-*****************************************************************************/
-
-// Let's say that because of its many external dependencies, YuckyFoo is very
-// hard to instantiate in a test harness.
-class YuckyFoo
-{
-public:
- virtual ~YuckyFoo() {}
- virtual std::string whoami() const { return "YuckyFoo"; }
-};
-
-// Let's further suppose that YuckyBar is another hard-to-instantiate class.
-class YuckyBar
-{
-public:
- YuckyBar(const std::string& which):
- mWhich(which)
- {}
- virtual ~YuckyBar() {}
-
- virtual std::string identity() const { return std::string("YuckyBar(") + mWhich + ")"; }
-
-private:
- const std::string mWhich;
-};
-
-// Pretend that this class would be tough to test because, up until we started
-// trying to test it, it contained instances of both YuckyFoo and YuckyBar.
-// Now we've refactored so it contains LLLazy<YuckyFoo> and LLLazy<YuckyBar>.
-// More than that, it contains them by virtue of deriving from
-// LLLazyBase<YuckyFoo> and LLLazyBase<YuckyBar>.
-// We postulate two different LLLazyBases because, with only one, you need not
-// specify *which* get()/set() method you're talking about. That's a simpler
-// case.
-class NeedsTesting: public LLLazyBase<YuckyFoo>, public LLLazyBase<YuckyBar>
-{
-public:
- NeedsTesting():
- // mYuckyBar("RealYuckyBar")
- LLLazyBase<YuckyBar>(bll::bind(bll::new_ptr<YuckyBar>(), "RealYuckyBar"))
- {}
- virtual ~NeedsTesting() {}
-
- virtual std::string describe() const
- {
- return std::string("NeedsTesting(") + getLazy<YuckyFoo>(this).whoami() + ", " +
- getLazy<YuckyBar>(this).identity() + ")";
- }
-
-private:
- // These instance members were moved to LLLazyBases:
- // YuckyFoo mYuckyFoo;
- // YuckyBar mYuckyBar;
-};
-
-// Fake up a test YuckyFoo class
-class TestFoo: public YuckyFoo
-{
-public:
- virtual std::string whoami() const { return "TestFoo"; }
-};
-
-// and a test YuckyBar
-class TestBar: public YuckyBar
-{
-public:
- TestBar(const std::string& which): YuckyBar(which) {}
- virtual std::string identity() const
- {
- return std::string("TestBar(") + YuckyBar::identity() + ")";
- }
-};
-
-// So here's a test subclass of NeedsTesting that uses TestFoo and TestBar
-// instead of YuckyFoo and YuckyBar.
-class TestNeedsTesting: public NeedsTesting
-{
-public:
- TestNeedsTesting()
- {
- // Exercise setLazy(T*)
- setLazy<YuckyFoo>(this, new TestFoo());
- // Exercise setLazy(Factory)
- setLazy<YuckyBar>(this, bll::bind(bll::new_ptr<TestBar>(), "TestYuckyBar"));
- }
-
- virtual std::string describe() const
- {
- return std::string("TestNeedsTesting(") + NeedsTesting::describe() + ")";
- }
-
- void toolate()
- {
- setLazy<YuckyFoo>(this, new TestFoo());
- }
-};
-
-// This class tests having an explicit LLLazy<T> instance as a named member,
-// rather than deriving from LLLazyBase<T>.
-class LazyMember
-{
-public:
- YuckyFoo& getYuckyFoo() { return *mYuckyFoo; }
- std::string whoisit() const { return mYuckyFoo->whoami(); }
-
-protected:
- LLLazy<YuckyFoo> mYuckyFoo;
-};
-
-// This is a test subclass of the above, dynamically replacing the
-// LLLazy<YuckyFoo> member.
-class TestLazyMember: public LazyMember
-{
-public:
- // use factory setter
- TestLazyMember()
- {
- mYuckyFoo.set(bll::new_ptr<TestFoo>());
- }
-
- // use instance setter
- TestLazyMember(YuckyFoo* instance)
- {
- mYuckyFoo.set(instance);
- }
-};
-
-/*****************************************************************************
-* TUT
-*****************************************************************************/
-namespace tut
-{
- struct lllazy_data
- {
- };
- typedef test_group<lllazy_data> lllazy_group;
- typedef lllazy_group::object lllazy_object;
- lllazy_group lllazygrp("lllazy");
-
- template<> template<>
- void lllazy_object::test<1>()
- {
- // Instantiate an official one, just because we can
- NeedsTesting nt;
- // and a test one
- TestNeedsTesting tnt;
-// std::cout << nt.describe() << '\n';
- ensure_equals(nt.describe(), "NeedsTesting(YuckyFoo, YuckyBar(RealYuckyBar))");
-// std::cout << tnt.describe() << '\n';
- ensure_equals(tnt.describe(),
- "TestNeedsTesting(NeedsTesting(TestFoo, TestBar(YuckyBar(TestYuckyBar))))");
- }
-
- template<> template<>
- void lllazy_object::test<2>()
- {
- TestNeedsTesting tnt;
- std::string threw = catch_what<LLLazyCommon::InstanceChange>([&tnt](){
- tnt.toolate();
- });
- ensure_contains("InstanceChange exception", threw, "replace LLLazy instance");
- }
-
- template<> template<>
- void lllazy_object::test<3>()
- {
- {
- LazyMember lm;
- // operator*() on-demand instantiation
- ensure_equals(lm.getYuckyFoo().whoami(), "YuckyFoo");
- }
- {
- LazyMember lm;
- // operator->() on-demand instantiation
- ensure_equals(lm.whoisit(), "YuckyFoo");
- }
- }
-
- template<> template<>
- void lllazy_object::test<4>()
- {
- {
- // factory setter
- TestLazyMember tlm;
- ensure_equals(tlm.whoisit(), "TestFoo");
- }
- {
- // instance setter
- TestLazyMember tlm(new TestFoo());
- ensure_equals(tlm.whoisit(), "TestFoo");
- }
- }
-} // namespace tut
diff --git a/indra/llcommon/tests/llleap_test.cpp b/indra/llcommon/tests/llleap_test.cpp
index fa48bcdefd..ae20e7ed27 100644
--- a/indra/llcommon/tests/llleap_test.cpp
+++ b/indra/llcommon/tests/llleap_test.cpp
@@ -190,7 +190,7 @@ namespace tut
// computation, so I don't mind calling it twice.) Then take the
// basename.
reader_module(LLProcess::basename(
- reader.getName().substr(0, reader.getName().length()-3))),
+ reader.getPath().string().substr(0, reader.getPath().string().length()-3))),
PYTHON(LLStringUtil::getenv("PYTHON"))
{
ensure("Set PYTHON to interpreter pathname", !PYTHON.empty());
@@ -212,9 +212,9 @@ namespace tut
"time.sleep(1)\n");
LLLeapVector instances;
instances.push_back(LLLeap::create(get_test_name(),
- StringVec{PYTHON, script.getName()})->getWeak());
+ StringVec{PYTHON, script.getPath().string()})->getWeak());
instances.push_back(LLLeap::create(get_test_name(),
- StringVec{PYTHON, script.getName()})->getWeak());
+ StringVec{PYTHON, script.getPath().string()})->getWeak());
// In this case we're simply establishing that two LLLeap instances
// can coexist without throwing exceptions or bombing in any other
// way. Wait for them to terminate.
@@ -229,7 +229,7 @@ namespace tut
"import sys\n"
"sys.stderr.write('''Hello from Python!\n"
"note partial line''')\n");
- StringVec vcommand{ PYTHON, script.getName() };
+ StringVec vcommand{ PYTHON, script.getPath().string() };
CaptureLog log(LLError::LEVEL_INFO);
waitfor(LLLeap::create(get_test_name(), vcommand));
log.messageWith("Hello from Python!");
@@ -244,7 +244,7 @@ namespace tut
"print('Hello from Python!')\n");
CaptureLog log(LLError::LEVEL_WARN);
waitfor(LLLeap::create(get_test_name(),
- StringVec{PYTHON, script.getName()}));
+ StringVec{PYTHON, script.getPath().string()}));
ensure_contains("error log line",
log.messageWith("invalid protocol"), "Hello from Python!");
}
@@ -259,7 +259,7 @@ namespace tut
"sys.stdout.write('Hello from Python!')\n");
CaptureLog log(LLError::LEVEL_WARN);
waitfor(LLLeap::create(get_test_name(),
- StringVec{PYTHON, script.getName()}));
+ StringVec{PYTHON, script.getPath().string()}));
ensure_contains("error log line",
log.messageWith("Discarding"), "Hello from Python!");
}
@@ -273,7 +273,7 @@ namespace tut
"sys.stdout.write('5a2:something')\n");
CaptureLog log(LLError::LEVEL_WARN);
waitfor(LLLeap::create(get_test_name(),
- StringVec{PYTHON, script.getName()}));
+ StringVec{PYTHON, script.getPath().string()}));
ensure_contains("error log line",
log.messageWith("invalid protocol"), "5a2:");
}
@@ -386,7 +386,7 @@ namespace tut
" else 'bad: ' + str(resp)\n"
"send(pump='" << result.getName() << "', data=result)\n";});
waitfor(LLLeap::create(get_test_name(),
- StringVec{PYTHON, script.getName()}));
+ StringVec{PYTHON, script.getPath().string()}));
result.ensure();
}
@@ -445,7 +445,7 @@ namespace tut
" result = 'expected reqid=%s in %s' % (i, resp)\n"
" break\n"
"send(pump='" << result.getName() << "', data=result)\n";});
- waitfor(LLLeap::create(get_test_name(), StringVec{PYTHON, script.getName()}),
+ waitfor(LLLeap::create(get_test_name(), StringVec{PYTHON, script.getPath().string()}),
300); // needs more realtime than most tests
result.ensure();
}
@@ -512,7 +512,7 @@ namespace tut
" (start, large[start:end], echoed[start:end]))\n"
"sys.exit(1)\n";});
waitfor(LLLeap::create(test_name,
- StringVec{PYTHON, script.getName(), stringize(size)}),
+ StringVec{PYTHON, script.getPath().string(), stringize(size)}),
180); // try a longer timeout
result.ensure();
}
diff --git a/indra/llcommon/tests/llmemtype_test.cpp b/indra/llcommon/tests/llmemtype_test.cpp
deleted file mode 100644
index 2d64d342ae..0000000000
--- a/indra/llcommon/tests/llmemtype_test.cpp
+++ /dev/null
@@ -1,117 +0,0 @@
-/**
- * @file llmemtype_test.cpp
- * @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$
- */
-
-#include "../llmemtype.h"
-#include "../test/lltut.h"
-#include "../llallocator.h"
-
-
-#include <stack>
-
-std::stack<S32> memTypeStack;
-
-void LLAllocator::pushMemType(S32 i)
-{
- memTypeStack.push(i);
-}
-
-S32 LLAllocator::popMemType(void)
-{
- S32 ret = memTypeStack.top();
- memTypeStack.pop();
- return ret;
-}
-
-namespace tut
-{
- struct llmemtype_data
- {
- };
-
- typedef test_group<llmemtype_data> factory;
- typedef factory::object object;
-}
-namespace
-{
- tut::factory llmemtype_test_factory("LLMemType");
-}
-
-namespace tut
-{
- template<> template<>
- void object::test<1>()
- {
- 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");
-
- std::string test_name4 = LLMemType::getNameFromID(-1);
- ensure_equals("Invalid name", test_name4, "INVALID");
- }
-
-};
diff --git a/indra/llcommon/tests/llprocess_test.cpp b/indra/llcommon/tests/llprocess_test.cpp
index 00dcd52d5d..5df43e497f 100644
--- a/indra/llcommon/tests/llprocess_test.cpp
+++ b/indra/llcommon/tests/llprocess_test.cpp
@@ -89,14 +89,14 @@ static void aprchk_(const char* call, apr_status_t rv, apr_status_t expected=APR
* @param desc Optional description of the file for error message;
* defaults to "in <pathname>"
*/
-static std::string readfile(const std::string& pathname, const std::string& desc="")
+static std::string readfile(const std::filesystem::path& pathname, const std::string& desc="")
{
std::string use_desc(desc);
if (use_desc.empty())
{
- use_desc = "in " + pathname;
+ use_desc = "in " + pathname.string();
}
- std::ifstream inf(pathname.c_str());
+ llifstream inf(pathname.c_str());
std::string output;
if (!std::getline(inf, output))
{
@@ -183,7 +183,7 @@ struct PythonProcessLauncher
mParams.desc = desc + " script";
mParams.executable = PYTHON;
- mParams.args.add(mScript.getName());
+ mParams.args.add(mScript.getPath().string());
}
/// Launch Python script; verify that it launched
@@ -203,7 +203,7 @@ struct PythonProcessLauncher
const char* APR_LOG = getenv("APR_LOG");
if (APR_LOG && *APR_LOG)
{
- std::ifstream inf(APR_LOG);
+ llifstream inf(APR_LOG);
if (! inf.is_open())
{
LL_WARNS() << "Couldn't open '" << APR_LOG << "'" << LL_ENDL;
@@ -251,11 +251,11 @@ struct PythonProcessLauncher
{
NamedTempFile out("out", ""); // placeholder
// pass name of this temporary file to the script
- mParams.args.add(out.getName());
+ mParams.args.add(out.getPath().string());
run();
// assuming the script wrote to that file, read it
std::string desc = "from " + mDesc + " script";
- return readfile(out.getName(), desc);
+ return readfile(out.getPath(), desc);
}
LLProcess::Params mParams;
@@ -498,23 +498,23 @@ public:
NamedTempDir():
mPath(NamedTempFile::temp_path()),
- mCreated(boost::filesystem::create_directories(mPath))
+ mCreated(std::filesystem::create_directories(mPath))
{
- mPath = boost::filesystem::canonical(mPath);
+ mPath = std::filesystem::canonical(mPath);
}
~NamedTempDir()
{
if (mCreated)
{
- boost::filesystem::remove_all(mPath);
+ std::filesystem::remove_all(mPath);
}
}
std::string getName() const { return mPath.string(); }
private:
- boost::filesystem::path mPath;
+ std::filesystem::path mPath;
bool mCreated;
};
@@ -674,7 +674,7 @@ namespace tut
#endif
// Have to have a named copy of this std::string so its c_str() value
// will persist.
- std::string scriptname(script.getName());
+ std::string scriptname(script.getPath().string());
argv.push_back(scriptname.c_str());
argv.push_back(NULL);
@@ -953,14 +953,14 @@ namespace tut
"with open(sys.argv[1], 'w') as f:\n"
" f.write('bad')\n");
NamedTempFile out("out", "not started");
- py.mParams.args.add(out.getName());
+ py.mParams.args.add(out.getPath().string());
py.launch();
// Wait for the script to wake up and do its first write
int i = 0, timeout = 60;
for ( ; i < timeout; ++i)
{
yield();
- if (readfile(out.getName(), "from kill() script") == "ok")
+ if (readfile(out.getPath(), "from kill() script") == "ok")
break;
}
// If we broke this loop because of the counter, something's wrong
@@ -979,7 +979,7 @@ namespace tut
// If kill() failed, the script would have woken up on its own and
// overwritten the file with 'bad'. But if kill() succeeded, it should
// not have had that chance.
- ensure_equals(get_test_name() + " script output", readfile(out.getName()), "ok");
+ ensure_equals(get_test_name() + " script output", readfile(out.getPath()), "ok");
}
template<> template<>
@@ -999,7 +999,7 @@ namespace tut
"# if caller hasn't managed to kill by now, bad\n"
"with open(sys.argv[1], 'w') as f:\n"
" f.write('bad')\n");
- py.mParams.args.add(out.getName());
+ py.mParams.args.add(out.getPath().string());
py.launch();
// Capture handle for later
phandle = py.mPy->getProcessHandle();
@@ -1008,7 +1008,7 @@ namespace tut
for ( ; i < timeout; ++i)
{
yield();
- if (readfile(out.getName(), "from kill() script") == "ok")
+ if (readfile(out.getPath(), "from kill() script") == "ok")
break;
}
// If we broke this loop because of the counter, something's wrong
@@ -1021,7 +1021,7 @@ namespace tut
// If kill() failed, the script would have woken up on its own and
// overwritten the file with 'bad'. But if kill() succeeded, it should
// not have had that chance.
- ensure_equals(get_test_name() + " script output", readfile(out.getName()), "ok");
+ ensure_equals(get_test_name() + " script output", readfile(out.getPath()), "ok");
}
template<> template<>
@@ -1051,8 +1051,8 @@ namespace tut
"# okay, saw 'go', write 'ack'\n"
"with open(sys.argv[1], 'w') as f:\n"
" f.write('ack')\n");
- py.mParams.args.add(from.getName());
- py.mParams.args.add(to.getName());
+ py.mParams.args.add(from.getPath().string());
+ py.mParams.args.add(to.getPath().string());
py.mParams.autokill = false;
py.launch();
// Capture handle for later
@@ -1062,7 +1062,7 @@ namespace tut
for ( ; i < timeout; ++i)
{
yield();
- if (readfile(from.getName(), "from autokill script") == "ok")
+ if (readfile(from.getPath(), "from autokill script") == "ok")
break;
}
// If we broke this loop because of the counter, something's wrong
@@ -1074,14 +1074,14 @@ namespace tut
// How do we know it's not terminated? By making it respond to
// a specific stimulus in a specific way.
{
- std::ofstream outf(to.getName().c_str());
+ llofstream outf(to.getPath());
outf << "go";
} // flush and close.
// now wait for the script to terminate... one way or another.
waitfor(phandle, "autokill script");
// If the LLProcess destructor implicitly called kill(), the
// script could not have written 'ack' as we expect.
- ensure_equals(get_test_name() + " script output", readfile(from.getName()), "ack");
+ ensure_equals(get_test_name() + " script output", readfile(from.getPath()), "ack");
}
template<> template<>
@@ -1113,8 +1113,8 @@ namespace tut
"# okay, saw 'go', write 'ack'\n"
"with open(sys.argv[1], 'w') as f:\n"
" f.write('ack')\n");
- py.mParams.args.add(from.getName());
- py.mParams.args.add(to.getName());
+ py.mParams.args.add(from.getPath().string());
+ py.mParams.args.add(to.getPath().string());
py.mParams.autokill = true;
py.mParams.attached = false;
py.launch();
@@ -1125,7 +1125,7 @@ namespace tut
for ( ; i < timeout; ++i)
{
yield();
- if (readfile(from.getName(), "from autokill script") == "ok")
+ if (readfile(from.getPath(), "from autokill script") == "ok")
break;
}
// If we broke this loop because of the counter, something's wrong
@@ -1137,14 +1137,14 @@ namespace tut
// How do we know it's not terminated? By making it respond to
// a specific stimulus in a specific way.
{
- std::ofstream outf(to.getName().c_str());
+ llofstream outf(to.getPath());
outf << "go";
} // flush and close.
// now wait for the script to terminate... one way or another.
waitfor(phandle, "autokill script");
// If the LLProcess destructor implicitly called kill(), the
// script could not have written 'ack' as we expect.
- ensure_equals(get_test_name() + " script output", readfile(from.getName()), "ack");
+ ensure_equals(get_test_name() + " script output", readfile(from.getPath()), "ack");
}
template<> template<>
diff --git a/indra/llcommon/tests/llsd_test.cpp b/indra/llcommon/tests/llsd_test.cpp
new file mode 100644
index 0000000000..9cc93bcd14
--- /dev/null
+++ b/indra/llcommon/tests/llsd_test.cpp
@@ -0,0 +1,830 @@
+/**
+ * @file llsd_new_tut.cpp
+ * @date February 2006
+ * @brief LLSD unit tests
+ *
+ * $LicenseInfo:firstyear=2006&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2006-2011, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#define LLSD_DEBUG_INFO
+#include "linden_common.h"
+#include "lltut.h"
+#include "llsd.h"
+
+#include "llsdtraits.h"
+#include "llstring.h"
+
+using std::fpclassify;
+
+namespace tut
+{
+ class SDCleanupCheck
+ {
+ private:
+ U32 mOutstandingAtStart;
+ public:
+ SDCleanupCheck() : mOutstandingAtStart(llsd::outstandingCount()) { }
+ ~SDCleanupCheck()
+ {
+ ensure_equals("SDCleanupCheck",
+ llsd::outstandingCount(), mOutstandingAtStart);
+ }
+ };
+
+ class SDAllocationCheck : public SDCleanupCheck
+ {
+ private:
+ std::string mMessage;
+ U32 mExpectedAllocations;
+ U32 mAllocationAtStart;
+ public:
+ SDAllocationCheck(const std::string& message, int expectedAllocations)
+ : mMessage(message),
+ mExpectedAllocations(expectedAllocations),
+ mAllocationAtStart(llsd::allocationCount())
+ { }
+ ~SDAllocationCheck()
+ {
+ ensure_equals(mMessage + " SDAllocationCheck",
+ llsd::allocationCount() - mAllocationAtStart,
+ mExpectedAllocations);
+ }
+ };
+
+ struct SDTestData {
+ template<class T>
+ static void ensureTypeAndValue(const char* msg, const LLSD& actual,
+ T expectedValue)
+ {
+ LLSDTraits<T> traits;
+
+ std::string s(msg);
+
+ ensure( s + " type", traits.checkType(actual));
+ ensure_equals( s + " value", traits.get(actual), expectedValue);
+ }
+
+ template<class T>
+ static void ensureTypeAndRefValue(const char* msg, const LLSD& actual,
+ const T& expectedValue)
+ {
+ LLSDTraits<const T&> traits;
+
+ std::string s(msg);
+
+ ensure( s + " type", traits.checkType(actual));
+ ensure_equals( s + " value", traits.get(actual), expectedValue);
+ }
+ };
+
+ typedef test_group<SDTestData> SDTestGroup;
+ typedef SDTestGroup::object SDTestObject;
+
+ SDTestGroup sdTestGroup("LLSD(new)");
+
+ // template<> template<>
+ // void SDTestObject::test<1>()
+ // // construction and test of undefined
+ // {
+ // SDCleanupCheck check;
+
+ // LLSD u;
+ // ensure("is undefined", u.isUndefined());
+ // }
+
+ template<> template<>
+ void SDTestObject::test<2>()
+ // setting and fetching scalar types
+ {
+ SDCleanupCheck check;
+
+ LLSD v;
+
+ v = true; ensureTypeAndValue("set true", v, true);
+ v = false; ensureTypeAndValue("set false", v, false);
+ v = true; ensureTypeAndValue("set true again", v, true);
+
+ v = 42; ensureTypeAndValue("set to 42", v, 42);
+ v = 0; ensureTypeAndValue("set to zero", v, 0);
+ v = -12345; ensureTypeAndValue("set to neg", v, -12345);
+ v = 2000000000; ensureTypeAndValue("set to big", v, 2000000000);
+
+ v = 3.14159265359;
+ ensureTypeAndValue("set to pi", v, 3.14159265359);
+ ensure_not_equals("isn't float", v.asReal(),
+ (float)3.14159265359);
+ v = 6.7e256; ensureTypeAndValue("set to big", v, 6.7e256);
+
+ LLUUID nullUUID;
+ LLUUID newUUID;
+ newUUID.generate();
+
+ v = nullUUID; ensureTypeAndValue("set to null UUID", v, nullUUID);
+ v = newUUID; ensureTypeAndValue("set to new UUID", v, newUUID);
+ v = nullUUID; ensureTypeAndValue("set to null again", v, nullUUID);
+
+ // strings must be tested with two types of string objects
+ std::string s = "now is the time";
+ const char* cs = "for all good zorks";
+
+ v = s; ensureTypeAndValue("set to std::string", v, s);
+ v = cs; ensureTypeAndValue("set to const char*", v, cs);
+
+ LLDate epoch;
+ LLDate aDay("2001-10-22T10:11:12.00Z");
+
+ v = epoch; ensureTypeAndValue("set to epoch", v, epoch);
+ v = aDay; ensureTypeAndValue("set to a day", v, aDay);
+
+ LLURI path("http://slurl.com/secondlife/Ambleside/57/104/26/");
+
+ v = path; ensureTypeAndValue("set to a uri", v, path);
+
+ const char source[] = "once in a blue moon";
+ std::vector<U8> data;
+ copy(&source[0], &source[sizeof(source)], back_inserter(data));
+
+ v = data; ensureTypeAndRefValue("set to data", v, data);
+
+ v.clear();
+ ensure("reset to undefined", v.type() == LLSD::TypeUndefined);
+ }
+
+ template<> template<>
+ void SDTestObject::test<3>()
+ // construction via scalar values
+ // tests both constructor and initialize forms
+ {
+ SDCleanupCheck check;
+
+ LLSD b1(true); ensureTypeAndValue("construct boolean", b1, true);
+ LLSD b2 = true; ensureTypeAndValue("initialize boolean", b2, true);
+ LLSD i1(42); ensureTypeAndValue("construct int", i1, 42);
+ LLSD i2 =42; ensureTypeAndValue("initialize int", i2, 42);
+ LLSD d1(1.2); ensureTypeAndValue("construct double", d1, 1.2);
+ LLSD d2 = 1.2; ensureTypeAndValue("initialize double", d2, 1.2);
+
+ LLUUID newUUID;
+ newUUID.generate();
+ LLSD u1(newUUID);
+ ensureTypeAndValue("construct UUID", u1, newUUID);
+ LLSD u2 = newUUID;
+ ensureTypeAndValue("initialize UUID", u2, newUUID);
+
+ LLSD ss1(std::string("abc"));
+ ensureTypeAndValue("construct std::string", ss1, "abc");
+ LLSD ss2 = std::string("abc");
+ ensureTypeAndValue("initialize std::string",ss2, "abc");
+ LLSD sl1(std::string("def"));
+ ensureTypeAndValue("construct std::string", sl1, "def");
+ LLSD sl2 = std::string("def");
+ ensureTypeAndValue("initialize std::string", sl2, "def");
+ LLSD sc1("ghi");
+ ensureTypeAndValue("construct const char*", sc1, "ghi");
+ LLSD sc2 = "ghi";
+ ensureTypeAndValue("initialize const char*",sc2, "ghi");
+
+ LLDate aDay("2001-10-22T10:11:12.00Z");
+ LLSD t1(aDay); ensureTypeAndValue("construct LLDate", t1, aDay);
+ LLSD t2 = aDay; ensureTypeAndValue("initialize LLDate", t2, aDay);
+
+ LLURI path("http://slurl.com/secondlife/Ambleside/57/104/26/");
+ LLSD p1(path); ensureTypeAndValue("construct LLURI", p1, path);
+ LLSD p2 = path; ensureTypeAndValue("initialize LLURI", p2, path);
+
+ const char source[] = "once in a blue moon";
+ std::vector<U8> data;
+ copy(&source[0], &source[sizeof(source)], back_inserter(data));
+ LLSD x1(data); ensureTypeAndRefValue("construct vector<U8>", x1, data);
+ LLSD x2 = data; ensureTypeAndRefValue("initialize vector<U8>", x2, data);
+ }
+
+ void checkConversions(const char* msg, const LLSD& v,
+ LLSD::Boolean eBoolean, LLSD::Integer eInteger,
+ LLSD::Real eReal, const LLSD::String& eString)
+ {
+ std::string s(msg);
+
+ ensure_equals(s+" to bool", v.asBoolean(), eBoolean);
+ ensure_equals(s+" to int", v.asInteger(), eInteger);
+ if (eReal == eReal)
+ {
+ ensure_equals(s+" to real", v.asReal(), eReal);
+ ensure_equals(s+" to string", v.asString(), eString);
+ }
+ else
+ {
+ int left = fpclassify(v.asReal());
+ int right = fpclassify(eReal);
+
+ ensure_equals(s+" to real", left, right);
+ // ensure_equals(s+" to string", v.asString(), eString);
+ // I've commented this check out, since there doesn't
+ // seem to be uniform string representation for NaN on
+ // all platforms. For example, on my Ubuntu 8.10 laptop
+ // with libc 2.11.1, sqrt(-1.0) will return '-nan', not
+ // 'nan'.
+ }
+ }
+
+ template<> template<>
+ void SDTestObject::test<4>()
+ // conversion between undefined and basic scalar types:
+ // boolean, integer, real and string
+ {
+ SDCleanupCheck check;
+
+ LLSD v; checkConversions("untitled", v, false, 0, 0.0, "");
+
+ v = false; checkConversions("false", v, false, 0, 0.0, "");
+ v = true; checkConversions("true", v, true, 1, 1.0, "true");
+
+ v = 0; checkConversions("zero", v, false, 0, 0.0, "0");
+ v = 1; checkConversions("one", v, true, 1, 1.0, "1");
+ v = -33; checkConversions("neg33", v, true, -33, -33.0, "-33");
+
+ v = 0.0; checkConversions("0.0", v, false, 0, 0.0, "0");
+ v = 0.5; checkConversions("point5", v, true, 0, 0.5, "0.5");
+ v = 0.9; checkConversions("point9", v, true, 0, 0.9, "0.9");
+ v = -3.9; checkConversions("neg3dot9", v, true, -3, -3.9, "-3.9");
+ // Get rid of NaN test. First, some libraries don't reliably return
+ // NaN for sqrt(-1.0) -- meaning that I don't even know how to
+ // portably, reliably produce a NaN value. Second, we observe failures
+ // on different platforms in the asString() test. But LLSD's
+ // ImplReal::asString() does not itself recognize NaN! It merely
+ // passes the value through to llformat(), which passes it through to
+ // the library vsnprintf(). That is, even when we do produce NaN,
+ // we're not testing any LLSD code: we're testing the local library's
+ // vsnprintf() function, which (empirically) produces idiosyncratic
+ // results. This is just not a good test case.
+// v = sqrt(-1.0); checkConversions("NaN", v, false, 0, sqrt(-1.0), "nan");
+
+ v = ""; checkConversions("empty", v, false, 0, 0.0, "");
+ v = "0"; checkConversions("digit0", v, true, 0, 0.0, "0");
+ v = "10"; checkConversions("digit10", v, true, 10, 10.0, "10");
+ v = "-2.345"; checkConversions("decdigits", v,
+ true, -2, -2.345, "-2.345");
+ v = "apple"; checkConversions("apple", v, true, 0, 0.0, "apple");
+ v = "33bob"; checkConversions("digialpha", v, true, 0, 0.0, "33bob");
+ v = " "; checkConversions("space", v, true, 0, 0.0, " ");
+ v = "\n"; checkConversions("newline", v, true, 0, 0.0, "\n");
+ }
+
+ template<class T>
+ void checkRoundTrip(const std::string& msg, const LLSD& actual,
+ const char* sExpected, T vExpected)
+ {
+ std::string str = actual.asString();
+
+ if (sExpected) {
+ ensure_equals(msg + " string", str, sExpected);
+ }
+
+ LLSD u(str);
+ LLSDTraits<T> traits;
+
+ ensure_equals(msg + " value", traits.get(u), vExpected);
+ }
+
+
+ template<> template<>
+ void SDTestObject::test<5>()
+ // conversion of String to and from UUID, Date and URI.
+ {
+ SDCleanupCheck check;
+
+ LLSD v;
+
+ LLUUID nullUUID;
+ LLUUID someUUID;
+ someUUID.generate();
+
+ v = nullUUID; checkRoundTrip("null uuid", v,
+ "00000000-0000-0000-0000-000000000000", nullUUID);
+ v = someUUID; checkRoundTrip("random uuid", v, 0, someUUID);
+
+ LLDate epoch;
+ LLDate beta("2003-04-30T04:00:00Z");
+ LLDate oneOh("2003-06-23T04:00:00Z");
+
+ v = epoch; checkRoundTrip("epoch date", v, 0, epoch);
+ v = beta; checkRoundTrip("beta date", v,
+ "2003-04-30T04:00:00Z", beta);
+ v = oneOh; checkRoundTrip("1.0 date", v,
+ "2003-06-23T04:00:00Z", oneOh);
+
+ LLURI empty;
+ LLURI path("http://slurl.com/secondlife/Ambleside/57/104/26/");
+ LLURI mail("mailto:zero.linden@secondlife.com");
+
+ v = empty; checkRoundTrip("empty URI", v, 0, empty);
+ v = path; checkRoundTrip("path URI", v,
+ "http://slurl.com/secondlife/Ambleside/57/104/26/",
+ path);
+ v = mail; checkRoundTrip("mail URI", v,
+ "mailto:zero.linden@secondlife.com", mail);
+ }
+
+ template<> template<>
+ void SDTestObject::test<6>()
+ // copy construction and assignment
+ // checking for shared values after constr. or assignment
+ // checking in both the same type and change of type case
+ {
+ SDCleanupCheck check;
+
+ {
+ LLSD v = 42;
+
+ LLSD w0(v);
+ ensureTypeAndValue("int constr.", w0, 42);
+
+ LLSD w1(v);
+ w1 = 13;
+ ensureTypeAndValue("int constr. change case 1", w1, 13);
+ ensureTypeAndValue("int constr. change case 2", v, 42);
+
+ LLSD w2(v);
+ v = 7;
+ ensureTypeAndValue("int constr. change case 3", w2, 42);
+ ensureTypeAndValue("int constr. change case 4", v, 7);
+ }
+
+ {
+ LLSD v = 42;
+
+ LLSD w1(v);
+ w1 = "bob";
+ ensureTypeAndValue("string constr. change case 1", w1, "bob");
+ ensureTypeAndValue("string constr. change case 2", v, 42);
+
+ LLSD w2(v);
+ v = "amy";
+ ensureTypeAndValue("string constr. change case 3", w2, 42);
+ ensureTypeAndValue("string constr. change case 4", v, "amy");
+ }
+
+ {
+ LLSD v = 42;
+
+ LLSD w0;
+ w0 = v;
+ ensureTypeAndValue("int assign", w0, 42);
+
+ LLSD w1;
+ w1 = v;
+ w1 = 13;
+ ensureTypeAndValue("int assign change case 1", w1, 13);
+ ensureTypeAndValue("int assign change case 2", v, 42);
+
+ LLSD w2;
+ w2 = v;
+ v = 7;
+ ensureTypeAndValue("int assign change case 3", w2, 42);
+ ensureTypeAndValue("int assign change case 4", v, 7);
+ }
+
+ {
+ LLSD v = 42;
+
+ LLSD w1;
+ w1 = v;
+ w1 = "bob";
+ ensureTypeAndValue("string assign change case 1", w1, "bob");
+ ensureTypeAndValue("string assign change case 2", v, 42);
+
+ LLSD w2;
+ w2 = v;
+ v = "amy";
+ ensureTypeAndValue("string assign change case 3", w2, 42);
+ ensureTypeAndValue("string assign change case 4", v, "amy");
+ }
+ }
+
+
+ template<> template<>
+ void SDTestObject::test<7>()
+ // Test assignment and casting to various scalar types. These
+ // assignments should invoke the right conversion without it being
+ // mentioned explicitly. The few exceptions are marked SAD.
+ {
+ SDCleanupCheck check;
+
+ LLSD v(" 42.375");
+
+ bool b = false;
+ b = v; ensure_equals("assign to bool", b, true);
+ b = (bool)v; ensure_equals("cast to bool", b, true);
+
+ int i = 99;
+ i = v; ensure_equals("assign to int", i, 42);
+ i = (int)v; ensure_equals("cast to int", i, 42);
+
+ double d = 3.14159;
+ d = v; ensure_equals("assign to double", d, 42.375);
+ d = (double)v; ensure_equals("cast to double", d, 42.375);
+
+ std::string s = "yo";
+// SAD s = v; ensure_equals("assign to string", s, " 42.375");
+ s = (std::string)v; ensure_equals("cast to string", s, " 42.375");
+
+ std::string uuidStr = "b1e50c2b-b627-4d23-8a86-a65d97b6319b";
+ v = uuidStr;
+ LLUUID u;
+ u = v;
+ ensure_equals("assign to LLUUID", u, LLUUID(uuidStr));
+// SAD u = (LLUUID)v;
+// ensure_equals("cast to LLUUID", u, LLUUID(uuidStr));
+
+ std::string dateStr = "2005-10-24T15:00:00Z";
+ v = dateStr;
+ LLDate date;
+ date = v;
+ ensure_equals("assign to LLDate", date.asString(), dateStr);
+// SAD date = (LLDate)v;
+// ensure_equals("cast to LLDate", date.asString(), dateStr);
+
+ std::string uriStr = "http://secondlife.com";
+ v = uriStr;
+ LLURI uri;
+ uri = v;
+ ensure_equals("assign to LLURI", uri.asString(), uriStr);
+// SAD uri = (LLURI)v;
+// ensure_equals("cast to LLURI", uri.asString(), uriStr);
+ }
+
+ template<> template<>
+ void SDTestObject::test<8>()
+ // Test construction of various scalar types from LLSD.
+ // Test both construction and initialization forms.
+ // These should invoke the right conversion without it being
+ // mentioned explicitly. The few exceptions are marked SAD.
+ {
+ SDCleanupCheck check;
+
+ LLSD v(" 42.375");
+
+ bool b1(v); ensure_equals("contruct bool", b1, true);
+ bool b2 = v; ensure_equals("initialize bool", b2, true);
+
+ int i1(v); ensure_equals("contruct int", i1, 42);
+ int i2 = v; ensure_equals("initialize int", i2, 42);
+
+ double d1(v); ensure_equals("contruct double", d1, 42.375);
+ double d2 = v; ensure_equals("initialize double", d2, 42.375);
+
+ std::string s1(v);
+ std::string s2 = v;
+ ensure_equals("contruct string", s1, " 42.375");
+ ensure_equals("initialize string", s2, " 42.375");
+
+ std::string t1(v);
+ std::string t2 = v.asString(); // SAD
+ ensure_equals("contruct std::string", t1, " 42.375");
+ ensure_equals("initialize std::string", t2, " 42.375");
+
+ std::string uuidStr = "b1e50c2b-b627-4d23-8a86-a65d97b6319b";
+ v = uuidStr;
+ LLUUID uuid1(v.asUUID()); // SAD
+ LLUUID uuid2 = v;
+ ensure_equals("contruct LLUUID", uuid1, LLUUID(uuidStr));
+ ensure_equals("initialize LLUUID", uuid2, LLUUID(uuidStr));
+
+ std::string dateStr = "2005-10-24T15:00:00Z";
+ v = dateStr;
+ LLDate date1(v.asDate()); // SAD
+ LLDate date2 = v;
+ ensure_equals("contruct LLDate", date1.asString(), dateStr);
+ ensure_equals("initialize LLDate", date2.asString(), dateStr);
+
+ std::string uriStr = "http://secondlife.com";
+ v = uriStr;
+ LLURI uri1(v.asURI()); // SAD
+ LLURI uri2 = v;
+ ensure_equals("contruct LLURI", uri1.asString(), uriStr);
+ ensure_equals("initialize LLURI", uri2.asString(), uriStr);
+ }
+
+
+ template<> template<>
+ void SDTestObject::test<9>()
+ // test to make sure v is interpreted as a bool in a various
+ // scenarios.
+ {
+ SDCleanupCheck check;
+
+ LLSD v = "0";
+ // magic value that is interpreted as boolean true, but integer false!
+
+ ensure_equals("trinary operator bool", (v ? true : false), true);
+ ensure_equals("convert to int, then bool",
+ ((int)v ? true : false), false);
+
+ if(v)
+ {
+ ensure("if converted to bool", true);
+ }
+ else
+ {
+ fail("bool did not convert to a bool in if statement.");
+ }
+
+ if(!v)
+ {
+ fail("bool did not convert to a bool in negated if statement.");
+ }
+ }
+
+ template<> template<>
+ void SDTestObject::test<10>()
+ // map operations
+ {
+ SDCleanupCheck check;
+
+ LLSD v;
+ ensure("undefined has no members", !v.has("amy"));
+ ensure("undefined get() is undefined", v.get("bob").isUndefined());
+
+ v = LLSD::emptyMap();
+ ensure("empty map is a map", v.isMap());
+ ensure("empty map has no members", !v.has("cam"));
+ ensure("empty map get() is undefined", v.get("don").isUndefined());
+
+ v.clear();
+ v.insert("eli", 43);
+ ensure("insert converts to map", v.isMap());
+ ensure("inserted key is present", v.has("eli"));
+ ensureTypeAndValue("inserted value", v.get("eli"), 43);
+
+ v.insert("fra", false);
+ ensure("first key still present", v.has("eli"));
+ ensure("second key is present", v.has("fra"));
+ ensureTypeAndValue("first value", v.get("eli"), 43);
+ ensureTypeAndValue("second value", v.get("fra"), false);
+
+ v.erase("eli");
+ ensure("first key now gone", !v.has("eli"));
+ ensure("second key still present", v.has("fra"));
+ ensure("first value gone", v.get("eli").isUndefined());
+ ensureTypeAndValue("second value sill there", v.get("fra"), false);
+
+ v.erase("fra");
+ ensure("second key now gone", !v.has("fra"));
+ ensure("second value gone", v.get("fra").isUndefined());
+
+ v["gil"] = (std::string)"good morning";
+ ensure("third key present", v.has("gil"));
+ ensureTypeAndValue("third key value", v.get("gil"), "good morning");
+
+ const LLSD& cv = v; // FIX ME IF POSSIBLE
+ ensure("missing key", cv["ham"].isUndefined());
+ ensure("key not present", !v.has("ham"));
+
+ LLSD w = 43;
+ const LLSD& cw = w; // FIX ME IF POSSIBLE
+ int i = cw["ian"];
+ ensureTypeAndValue("other missing value", i, 0);
+ ensure("other missing key", !w.has("ian"));
+ ensure("no conversion", w.isInteger());
+
+ LLSD x;
+ x = v;
+ ensure("copy map type", x.isMap());
+ ensureTypeAndValue("copy map value gil", x.get("gil"), "good morning");
+ }
+
+
+ template<> template<>
+ void SDTestObject::test<11>()
+ // array operations
+ {
+ SDCleanupCheck check;
+
+ LLSD v;
+ ensure_equals("undefined has no size", v.size(), 0);
+ ensure("undefined get() is undefined", v.get(0).isUndefined());
+
+ v = LLSD::emptyArray();
+ ensure("empty array is an array", v.isArray());
+ ensure_equals("empty array has no size", v.size(), 0);
+ ensure("empty map get() is undefined", v.get(0).isUndefined());
+
+ v.clear();
+ v.append(88);
+ v.append("noodle");
+ v.append(true);
+ ensure_equals("appened array size", v.size(), 3);
+ ensure("append array is an array", v.isArray());
+ ensureTypeAndValue("append 0", v[0], 88);
+ ensureTypeAndValue("append 1", v[1], "noodle");
+ ensureTypeAndValue("append 2", v[2], true);
+
+ v.insert(0, 77);
+ v.insert(2, "soba");
+ v.insert(4, false);
+ ensure_equals("inserted array size", v.size(), 6);
+ ensureTypeAndValue("post insert 0", v[0], 77);
+ ensureTypeAndValue("post insert 1", v[1], 88);
+ ensureTypeAndValue("post insert 2", v[2], "soba");
+ ensureTypeAndValue("post insert 3", v[3], "noodle");
+ ensureTypeAndValue("post insert 4", v[4], false);
+ ensureTypeAndValue("post insert 5", v[5], true);
+
+ ensureTypeAndValue("get 1", v.get(1), 88);
+ v.set(1, "hot");
+ ensureTypeAndValue("set 1", v.get(1), "hot");
+
+ v.erase(3);
+ ensure_equals("post erase array size", v.size(), 5);
+ ensureTypeAndValue("post erase 0", v[0], 77);
+ ensureTypeAndValue("post erase 1", v[1], "hot");
+ ensureTypeAndValue("post erase 2", v[2], "soba");
+ ensureTypeAndValue("post erase 3", v[3], false);
+ ensureTypeAndValue("post erase 4", v[4], true);
+
+ v.append(34);
+ ensure_equals("size after append", v.size(), 6);
+ ensureTypeAndValue("post append 5", v[5], 34);
+
+ LLSD w;
+ w = v;
+ ensure("copy array type", w.isArray());
+ ensure_equals("copy array size", w.size(), 6);
+ ensureTypeAndValue("copy array 0", w[0], 77);
+ ensureTypeAndValue("copy array 1", w[1], "hot");
+ ensureTypeAndValue("copy array 2", w[2], "soba");
+ ensureTypeAndValue("copy array 3", w[3], false);
+ ensureTypeAndValue("copy array 4", w[4], true);
+ ensureTypeAndValue("copy array 5", w[5], 34);
+ }
+
+
+ template<> template<>
+ void SDTestObject::test<12>()
+ // no sharing
+ {
+ SDCleanupCheck check;
+
+ LLSD a = 99;
+ LLSD b = a;
+ a = 34;
+ ensureTypeAndValue("top level original changed", a, 34);
+ ensureTypeAndValue("top level copy unaltered", b, 99);
+ b = a;
+ b = 66;
+ ensureTypeAndValue("top level original unaltered", a, 34);
+ ensureTypeAndValue("top level copy changed", b, 66);
+
+ a[0] = "uno";
+ a[1] = 99;
+ a[2] = 1.414;
+ b = a;
+ a[1] = 34;
+ ensureTypeAndValue("array member original changed", a[1], 34);
+ ensureTypeAndValue("array member copy unaltered", b[1], 99);
+ b = a;
+ b[1] = 66;
+ ensureTypeAndValue("array member original unaltered", a[1], 34);
+ ensureTypeAndValue("array member copy changed", b[1], 66);
+
+ a["alpha"] = "uno";
+ a["beta"] = 99;
+ a["gamma"] = 1.414;
+ b = a;
+ a["beta"] = 34;
+ ensureTypeAndValue("map member original changed", a["beta"], 34);
+ ensureTypeAndValue("map member copy unaltered", b["beta"], 99);
+ b = a;
+ b["beta"] = 66;
+ ensureTypeAndValue("map member original unaltered", a["beta"], 34);
+ ensureTypeAndValue("map member copy changed", b["beta"], 66);
+ }
+
+ template<> template<>
+ void SDTestObject::test<13>()
+ // sharing implementation
+ {
+ SDCleanupCheck check;
+
+ {
+ SDAllocationCheck check("copy construct undefinded", 0);
+ LLSD v;
+ LLSD w = v;
+ }
+
+ {
+ SDAllocationCheck check("assign undefined", 0);
+ LLSD v;
+ LLSD w;
+ w = v;
+ }
+
+ {
+ SDAllocationCheck check("assign integer value", 1);
+ LLSD v = 45;
+ v = 33;
+ v = 0;
+ }
+
+ {
+ SDAllocationCheck check("copy construct integer", 1);
+ LLSD v = 45;
+ LLSD w = v;
+ }
+
+ {
+ SDAllocationCheck check("assign integer", 1);
+ LLSD v = 45;
+ LLSD w;
+ w = v;
+ }
+
+ {
+ SDAllocationCheck check("avoids extra clone", 2);
+ LLSD v = 45;
+ LLSD w = v;
+ w = "nice day";
+ }
+
+ {
+ SDAllocationCheck check("shared values test for threaded work", 9);
+
+ //U32 start_llsd_count = LLSD::outstandingCount();
+
+ LLSD m = LLSD::emptyMap();
+
+ m["one"] = 1;
+ m["two"] = 2;
+ m["one_copy"] = m["one"]; // 3 (m, "one" and "two")
+
+ m["undef_one"] = LLSD();
+ m["undef_two"] = LLSD();
+ m["undef_one_copy"] = m["undef_one"];
+
+ { // Ensure first_array gets freed to avoid counting it
+ LLSD first_array = LLSD::emptyArray();
+ first_array.append(1.0f);
+ first_array.append(2.0f);
+ first_array.append(3.0f); // 7
+
+ m["array"] = first_array;
+ m["array_clone"] = first_array;
+ m["array_copy"] = m["array"]; // 7
+ }
+
+ m["string_one"] = "string one value";
+ m["string_two"] = "string two value";
+ m["string_one_copy"] = m["string_one"]; // 9
+
+ //U32 llsd_object_count = LLSD::outstandingCount();
+ //std::cout << "Using " << (llsd_object_count - start_llsd_count) << " LLSD objects" << std::endl;
+
+ //m.dumpStats();
+ }
+ }
+
+ template<> template<>
+ void SDTestObject::test<14>()
+ // make sure that assignment of char* NULL in a string does not crash.
+ {
+ LLSD v;
+ v = (const char*)NULL;
+ ensure("type is a string", v.isString());
+ }
+
+ /* TO DO:
+ conversion of undefined to UUID, Date, URI and Binary
+ conversion of undefined to map and array
+ test map operations
+ test array operations
+ test array extension
+
+ test copying and assign maps and arrays (clone)
+ test iteration over map
+ test iteration over array
+ test iteration over scalar
+
+ test empty map and empty array are indeed shared
+ test serializations
+ */
+}
+
diff --git a/indra/llcommon/tests/llsdserialize_test.cpp b/indra/llcommon/tests/llsdserialize_test.cpp
index f52b3fec71..71096523a9 100644
--- a/indra/llcommon/tests/llsdserialize_test.cpp
+++ b/indra/llcommon/tests/llsdserialize_test.cpp
@@ -52,7 +52,7 @@ typedef U32 uint32_t;
#include "llformat.h"
#include "llmemorystream.h"
-#include "../test/hexdump.h"
+#include "hexdump.h"
#include "../test/lltut.h"
#include "../test/namedtempfile.h"
#include "stringize.h"
@@ -1860,7 +1860,7 @@ namespace tut
#if LL_WINDOWS
std::string q("\"");
std::string qPYTHON(q + PYTHON + q);
- std::string qscript(q + scriptfile.getName() + q);
+ std::string qscript(q + scriptfile.getPath().string() + q);
int rc = (int)_spawnl(_P_WAIT, PYTHON.c_str(), qPYTHON.c_str(), qscript.c_str(),
std::forward<ARGS>(args)..., NULL);
if (rc == -1)
@@ -1877,7 +1877,7 @@ namespace tut
#else // LL_DARWIN, LL_LINUX
LLProcess::Params params;
params.executable = PYTHON;
- params.args.add(scriptfile.getName());
+ params.args.add(scriptfile.getPath().string());
for (const std::string& arg : StringVec{ std::forward<ARGS>(args)... })
{
params.args.add(arg);
@@ -1973,12 +1973,12 @@ namespace tut
int bufflen{ static_cast<int>(buffstr.length()) };
out.write(reinterpret_cast<const char*>(&bufflen), sizeof(bufflen));
LL_DEBUGS() << "Wrote length: "
- << hexdump(reinterpret_cast<const char*>(&bufflen),
+ << LL::hexdump(reinterpret_cast<const char*>(&bufflen),
sizeof(bufflen))
<< LL_ENDL;
out.write(buffstr.c_str(), buffstr.length());
LL_DEBUGS() << "Wrote data: "
- << hexmix(buffstr.c_str(), buffstr.length())
+ << LL::hexmix(buffstr.c_str(), buffstr.length())
<< LL_ENDL;
}
}
@@ -2054,8 +2054,8 @@ namespace tut
" yield frombytes\n"
<< pydata <<
// Don't forget raw-string syntax for Windows pathnames.
- "debug = open(r'" << debug.getName() << "', 'w')\n"
- "verify(parse_each(open(r'" << file.getName() << "', 'rb')))\n";});
+ "debug = open(r'" << debug.getPath().string() << "', 'w')\n"
+ "verify(parse_each(open(r'" << file.getPath().string() << "', 'rb')))\n";});
}
catch (const failure&)
{
@@ -2163,13 +2163,13 @@ namespace tut
"]\n"
// Don't forget raw-string syntax for Windows pathnames.
// N.B. Using 'print' implicitly adds newlines.
- "with open(r'" << file.getName() << "', 'wb') as f:\n"
+ "with open(r'" << (const char*)file.getPath().u8string().c_str() << "', 'wb') as f:\n"
" for item in DATA:\n"
" serialized = llsd." << pyformatter << "(item)\n"
" f.write(lenformat.pack(len(serialized)))\n"
" f.write(serialized)\n";});
- std::ifstream inf(file.getName().c_str());
+ llifstream inf(file.getPath());
LLSD item;
try
{
diff --git a/indra/llcommon/tests/llsdutil_test.cpp b/indra/llcommon/tests/llsdutil_test.cpp
new file mode 100644
index 0000000000..4b25a02f30
--- /dev/null
+++ b/indra/llcommon/tests/llsdutil_test.cpp
@@ -0,0 +1,434 @@
+/**
+ * @file llsdutil_tut.cpp
+ * @author Adroit
+ * @date 2007-02
+ * @brief LLSD conversion routines test cases.
+ *
+ * $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 "lltut.h"
+#include "m4math.h"
+#include "v2math.h"
+#include "v2math.h"
+#include "v3color.h"
+#include "v3math.h"
+#include "v3dmath.h"
+#include "v4coloru.h"
+#include "v4math.h"
+#include "llquaternion.h"
+#include "llsdutil.h"
+#include "llsdutil_math.h"
+#include "stringize.h"
+#include <set>
+#include <boost/range.hpp>
+
+namespace tut
+{
+ struct llsdutil_data
+ {
+ void test_matches(const std::string& proto_key, const LLSD& possibles,
+ const char** begin, const char** end)
+ {
+ std::set<std::string> succeed(begin, end);
+ LLSD prototype(possibles[proto_key]);
+ for (LLSD::map_const_iterator pi(possibles.beginMap()), pend(possibles.endMap());
+ pi != pend; ++pi)
+ {
+ std::string match(llsd_matches(prototype, pi->second));
+ std::set<std::string>::const_iterator found = succeed.find(pi->first);
+ if (found != succeed.end())
+ {
+ // This test is supposed to succeed. Comparing to the
+ // empty string ensures that if the test fails, it will
+ // display the string received so we can tell what failed.
+ ensure_equals("match", match, "");
+ }
+ else
+ {
+ // This test is supposed to fail. If we get a false match,
+ // the string 'match' will be empty, which doesn't tell us
+ // much about which case went awry. So construct a more
+ // detailed description string.
+ ensure(proto_key + " shouldn't match " + pi->first, ! match.empty());
+ }
+ }
+ }
+ };
+ typedef test_group<llsdutil_data> llsdutil_test;;
+ typedef llsdutil_test::object llsdutil_object;
+ tut::llsdutil_test tutil("llsdutil");
+
+ template<> template<>
+ void llsdutil_object::test<1>()
+ {
+ LLSD sd;
+ U64 valueIn , valueOut;
+ valueIn = U64L(0xFEDCBA9876543210);
+ sd = ll_sd_from_U64(valueIn);
+ valueOut = ll_U64_from_sd(sd);
+ ensure_equals("U64 valueIn->sd->valueOut", valueIn, valueOut);
+ }
+
+ template<> template<>
+ void llsdutil_object::test<2>()
+ {
+ LLSD sd;
+ U32 valueIn, valueOut;
+ valueIn = 0x87654321;
+ sd = ll_sd_from_U32(valueIn);
+ valueOut = ll_U32_from_sd(sd);
+ ensure_equals("U32 valueIn->sd->valueOut", valueIn, valueOut);
+ }
+
+ template<> template<>
+ void llsdutil_object::test<3>()
+ {
+ U32 valueIn, valueOut;
+ valueIn = 0x87654321;
+ LLSD sd;
+ sd = ll_sd_from_ipaddr(valueIn);
+ valueOut = ll_ipaddr_from_sd(sd);
+ ensure_equals("valueIn->sd->valueOut", valueIn, valueOut);
+ }
+
+ template<> template<>
+ void llsdutil_object::test<4>()
+ {
+ LLSD sd;
+ LLVector3 vec1(-1.0, 2.0, -3.0);
+ sd = ll_sd_from_vector3(vec1);
+ LLVector3 vec2 = ll_vector3_from_sd(sd);
+ ensure_equals("vector3 -> sd -> vector3: 1", vec1, vec2);
+
+ LLVector3 vec3(sd);
+ ensure_equals("vector3 -> sd -> vector3: 2", vec1, vec3);
+
+ sd.clear();
+ vec1.setVec(0., 0., 0.);
+ sd = ll_sd_from_vector3(vec1);
+ vec2 = ll_vector3_from_sd(sd);
+ ensure_equals("vector3 -> sd -> vector3: 3", vec1, vec2);
+ sd.clear();
+ }
+
+ template<> template<>
+ void llsdutil_object::test<5>()
+ {
+ LLSD sd;
+ LLVector3d vec1((F64)(U64L(0xFEDCBA9876543210) << 2), -1., 0);
+ sd = ll_sd_from_vector3d(vec1);
+ LLVector3d vec2 = ll_vector3d_from_sd(sd);
+ ensure_equals("vector3d -> sd -> vector3d: 1", vec1, vec2);
+
+ LLVector3d vec3(sd);
+ ensure_equals("vector3d -> sd -> vector3d : 2", vec1, vec3);
+ }
+
+ template<> template<>
+ void llsdutil_object::test<6>()
+ {
+ LLSD sd;
+ LLVector2 vec((F32) -3., (F32) 4.2);
+ sd = ll_sd_from_vector2(vec);
+ LLVector2 vec1 = ll_vector2_from_sd(sd);
+ ensure_equals("vector2 -> sd -> vector2", vec, vec1);
+
+ LLSD sd2 = ll_sd_from_vector2(vec1);
+ ensure_equals("sd -> vector2 -> sd: 2", sd, sd2);
+ }
+
+ template<> template<>
+ void llsdutil_object::test<7>()
+ {
+ LLSD sd;
+ LLQuaternion quat((F32) 1., (F32) -0.98, (F32) 2.3, (F32) 0xffff);
+ sd = ll_sd_from_quaternion(quat);
+ LLQuaternion quat1 = ll_quaternion_from_sd(sd);
+ ensure_equals("LLQuaternion -> sd -> LLQuaternion", quat, quat1);
+
+ LLSD sd2 = ll_sd_from_quaternion(quat1);
+ ensure_equals("sd -> LLQuaternion -> sd ", sd, sd2);
+ }
+
+ template<> template<>
+ void llsdutil_object::test<8>()
+ {
+ LLSD sd;
+ LLColor4 c(1.0f, 2.2f, 4.0f, 7.f);
+ sd = ll_sd_from_color4(c);
+ LLColor4 c1 =ll_color4_from_sd(sd);
+ ensure_equals("LLColor4 -> sd -> LLColor4", c, c1);
+
+ LLSD sd1 = ll_sd_from_color4(c1);
+ ensure_equals("sd -> LLColor4 -> sd", sd, sd1);
+ }
+
+ template<> template<>
+ void llsdutil_object::test<9>()
+ {
+ set_test_name("llsd_matches");
+
+ // for this test, construct a map of all possible LLSD types
+ LLSD map;
+ map.insert("empty", LLSD());
+ map.insert("Boolean", LLSD::Boolean());
+ map.insert("Integer", LLSD::Integer(0));
+ map.insert("Real", LLSD::Real(0.0));
+ map.insert("String", LLSD::String("bah"));
+ map.insert("NumString", LLSD::String("1"));
+ map.insert("UUID", LLSD::UUID());
+ map.insert("Date", LLSD::Date());
+ map.insert("URI", LLSD::URI());
+ map.insert("Binary", LLSD::Binary());
+ map.insert("Map", LLSD().with("foo", LLSD()));
+ // Only an empty array can be constructed on the fly
+ LLSD array;
+ array.append(LLSD());
+ map.insert("Array", array);
+
+ // These iterators are declared outside our various for loops to avoid
+ // fatal MSVC warning: "I used to be broken, but I'm all better now!"
+ LLSD::map_const_iterator mi, mend(map.endMap());
+
+ /*-------------------------- llsd_matches --------------------------*/
+
+ // empty prototype matches anything
+ for (mi = map.beginMap(); mi != mend; ++mi)
+ {
+ ensure_equals(std::string("empty matches ") + mi->first, llsd_matches(LLSD(), mi->second), "");
+ }
+
+ LLSD proto_array, data_array;
+ for (int i = 0; i < 3; ++i)
+ {
+ proto_array.append(LLSD());
+ data_array.append(LLSD());
+ }
+
+ // prototype array matches only array
+ for (mi = map.beginMap(); mi != mend; ++mi)
+ {
+ ensure(std::string("array doesn't match ") + mi->first,
+ ! llsd_matches(proto_array, mi->second).empty());
+ }
+
+ // data array must be at least as long as prototype array
+ proto_array.append(LLSD());
+ ensure_equals("data array too short", llsd_matches(proto_array, data_array),
+ "Array size 4 required instead of Array size 3");
+ data_array.append(LLSD());
+ ensure_equals("data array just right", llsd_matches(proto_array, data_array), "");
+ data_array.append(LLSD());
+ ensure_equals("data array longer", llsd_matches(proto_array, data_array), "");
+
+ // array element matching
+ data_array[0] = LLSD::String();
+ ensure_equals("undefined prototype array entry", llsd_matches(proto_array, data_array), "");
+ proto_array[0] = LLSD::Binary();
+ ensure_equals("scalar prototype array entry", llsd_matches(proto_array, data_array),
+ "[0]: Binary required instead of String");
+ data_array[0] = LLSD::Binary();
+ ensure_equals("matching prototype array entry", llsd_matches(proto_array, data_array), "");
+
+ // build a coupla maps
+ LLSD proto_map, data_map;
+ data_map["got"] = LLSD();
+ data_map["found"] = LLSD();
+ for (LLSD::map_const_iterator dmi(data_map.beginMap()), dmend(data_map.endMap());
+ dmi != dmend; ++dmi)
+ {
+ proto_map[dmi->first] = dmi->second;
+ }
+ proto_map["foo"] = LLSD();
+ proto_map["bar"] = LLSD();
+
+ // prototype map matches only map
+ for (mi = map.beginMap(); mi != mend; ++mi)
+ {
+ ensure(std::string("map doesn't match ") + mi->first,
+ ! llsd_matches(proto_map, mi->second).empty());
+ }
+
+ // data map must contain all keys in prototype map
+ std::string error(llsd_matches(proto_map, data_map));
+ ensure_contains("missing keys", error, "missing keys");
+ ensure_contains("missing foo", error, "foo");
+ ensure_contains("missing bar", error, "bar");
+ ensure_does_not_contain("found found", error, "found");
+ ensure_does_not_contain("got got", error, "got");
+ data_map["bar"] = LLSD();
+ error = llsd_matches(proto_map, data_map);
+ ensure_contains("missing foo", error, "foo");
+ ensure_does_not_contain("got bar", error, "bar");
+ data_map["foo"] = LLSD();
+ ensure_equals("data map just right", llsd_matches(proto_map, data_map), "");
+ data_map["extra"] = LLSD();
+ ensure_equals("data map with extra", llsd_matches(proto_map, data_map), "");
+
+ // map element matching
+ data_map["foo"] = LLSD::String();
+ ensure_equals("undefined prototype map entry", llsd_matches(proto_map, data_map), "");
+ proto_map["foo"] = LLSD::Binary();
+ ensure_equals("scalar prototype map entry", llsd_matches(proto_map, data_map),
+ "['foo']: Binary required instead of String");
+ data_map["foo"] = LLSD::Binary();
+ ensure_equals("matching prototype map entry", llsd_matches(proto_map, data_map), "");
+
+ // String
+ {
+ static const char* matches[] = { "String", "NumString", "Boolean", "Integer",
+ "Real", "UUID", "Date", "URI" };
+ test_matches("String", map, boost::begin(matches), boost::end(matches));
+ }
+
+ // Boolean, Integer, Real
+ static const char* numerics[] = { "Boolean", "Integer", "Real" };
+ for (const char **ni = boost::begin(numerics), **nend = boost::end(numerics);
+ ni != nend; ++ni)
+ {
+ static const char* matches[] = { "Boolean", "Integer", "Real", "String", "NumString" };
+ test_matches(*ni, map, boost::begin(matches), boost::end(matches));
+ }
+
+ // UUID
+ {
+ static const char* matches[] = { "UUID", "String", "NumString" };
+ test_matches("UUID", map, boost::begin(matches), boost::end(matches));
+ }
+
+ // Date
+ {
+ static const char* matches[] = { "Date", "String", "NumString" };
+ test_matches("Date", map, boost::begin(matches), boost::end(matches));
+ }
+
+ // URI
+ {
+ static const char* matches[] = { "URI", "String", "NumString" };
+ test_matches("URI", map, boost::begin(matches), boost::end(matches));
+ }
+
+ // Binary
+ {
+ static const char* matches[] = { "Binary" };
+ test_matches("Binary", map, boost::begin(matches), boost::end(matches));
+ }
+
+ /*-------------------------- llsd_equals ---------------------------*/
+
+ // Cross-product of each LLSD type with every other
+ for (LLSD::map_const_iterator lmi(map.beginMap()), lmend(map.endMap());
+ lmi != lmend; ++lmi)
+ {
+ for (LLSD::map_const_iterator rmi(map.beginMap()), rmend(map.endMap());
+ rmi != rmend; ++rmi)
+ {
+ // Name this test based on the map keys naming the types of
+ // interest, e.g "String::Integer".
+ // We expect the values (xmi->second) to be equal if and only
+ // if the type names (xmi->first) are equal.
+ ensure(STRINGIZE(lmi->first << "::" << rmi->first),
+ bool(lmi->first == rmi->first) ==
+ bool(llsd_equals(lmi->second, rmi->second)));
+ }
+ }
+
+ // Array cases
+ LLSD rarray;
+ rarray.append(1.0);
+ rarray.append(2);
+ rarray.append("3");
+ LLSD larray(rarray);
+ ensure("llsd_equals(equal arrays)", llsd_equals(larray, rarray));
+ rarray[2] = "4";
+ ensure("llsd_equals(different [2])", ! llsd_equals(larray, rarray));
+ rarray = larray;
+ rarray.append(LLSD::Date());
+ ensure("llsd_equals(longer right array)", ! llsd_equals(larray, rarray));
+ rarray = larray;
+ rarray.erase(2);
+ ensure("llsd_equals(shorter right array)", ! llsd_equals(larray, rarray));
+
+ // Map cases
+ LLSD rmap;
+ rmap["San Francisco"] = 65;
+ rmap["Phoenix"] = 92;
+ rmap["Boston"] = 77;
+ LLSD lmap(rmap);
+ ensure("llsd_equals(equal maps)", llsd_equals(lmap, rmap));
+ rmap["Boston"] = 80;
+ ensure("llsd_equals(different [\"Boston\"])", ! llsd_equals(lmap, rmap));
+ rmap = lmap;
+ rmap["Atlanta"] = 95;
+ ensure("llsd_equals(superset right map)", ! llsd_equals(lmap, rmap));
+ rmap = lmap;
+ lmap["Seattle"] = 72;
+ ensure("llsd_equals(superset left map)", ! llsd_equals(lmap, rmap));
+ }
+
+ template<> template<>
+ void llsdutil_object::test<10>()
+ {
+ set_test_name("llsd_hashing");
+
+ {
+ LLSD data_s1 = LLSD::String("The quick brown aardvark jumped over the lazy lemming.");
+ LLSD data_s2 = LLSD::String("The quick brown aardvark jumped over the lazy lemming.");
+
+ ensure("hash: Identical string hashes match.", boost::hash<LLSD>{}(data_s1) == boost::hash<LLSD>{}(data_s2));
+ }
+ {
+ LLSD data_r1 = LLSD::Real(3.0f);
+ LLSD data_i1 = LLSD::Integer(3);
+ ensure("hash: equivalent values but different types do not match.", boost::hash<LLSD>{}(data_r1) != boost::hash<LLSD>{}(data_i1));
+ }
+ {
+ LLSD data_a1 = llsd::array("A", "B", "C");
+ LLSD data_a2 = llsd::array("A", "B", "C");
+
+ ensure("hash: identical arrays produce identical results", boost::hash<LLSD>{}(data_a1) == boost::hash<LLSD>{}(data_a2));
+
+ data_a2.append(llsd::array(1, 2));
+
+ ensure("hash: changing the array changes the hash.", boost::hash<LLSD>{}(data_a1) != boost::hash<LLSD>{}(data_a2));
+
+ data_a1.append(llsd::array(1, 2));
+ ensure("hash: identical arrays produce identical results with nested arrays", boost::hash<LLSD>{}(data_a1) == boost::hash<LLSD>{}(data_a2));
+ }
+ {
+ LLSD data_m1 = LLSDMap("key1", LLSD::Real(3.0))("key2", "value2")("key3", llsd::array(1, 2, 3));
+ LLSD data_m2 = LLSDMap("key1", LLSD::Real(3.0))("key2", "value2")("key3", llsd::array(1, 2, 3));
+
+ ensure("hash: identical maps produce identical results", boost::hash<LLSD>{}(data_m1) == boost::hash<LLSD>{}(data_m2));
+
+ LLSD data_m3 = LLSDMap("key1", LLSD::Real(5.0))("key2", "value2")("key3", llsd::array(1, 2, 3));
+ ensure("hash: Different values in the map produce different hashes.", boost::hash<LLSD>{}(data_m1) != boost::hash<LLSD>{}(data_m3));
+
+ LLSD data_m4 = LLSDMap("keyA", LLSD::Real(3.0))("key2", "value2")("key3", llsd::array(1, 2, 3));
+ ensure("hash: Different keys in the map produce different hashes.", boost::hash<LLSD>{}(data_m1) != boost::hash<LLSD>{}(data_m4));
+
+ }
+ }
+
+}
diff --git a/indra/llcommon/tests/llstreamqueue_test.cpp b/indra/llcommon/tests/llstreamqueue_test.cpp
deleted file mode 100644
index 82b451119e..0000000000
--- a/indra/llcommon/tests/llstreamqueue_test.cpp
+++ /dev/null
@@ -1,194 +0,0 @@
-/**
- * @file llstreamqueue_test.cpp
- * @author Nat Goodspeed
- * @date 2012-01-05
- * @brief Test for llstreamqueue.
- *
- * $LicenseInfo:firstyear=2012&license=viewerlgpl$
- * Copyright (c) 2012, Linden Research, Inc.
- * $/LicenseInfo$
- */
-
-// Precompiled header
-#include "linden_common.h"
-// associated header
-#include "llstreamqueue.h"
-// STL headers
-#include <vector>
-// other Linden headers
-#include "../test/lltut.h"
-#include "stringize.h"
-
-/*****************************************************************************
-* TUT
-*****************************************************************************/
-namespace tut
-{
- struct llstreamqueue_data
- {
- llstreamqueue_data():
- // we want a buffer with actual bytes in it, not an empty vector
- buffer(10)
- {}
- // As LLStreamQueue is merely a typedef for
- // LLGenericStreamQueue<char>, and no logic in LLGenericStreamQueue is
- // specific to the <char> instantiation, we're comfortable for now
- // testing only the narrow-char version.
- LLStreamQueue strq;
- // buffer for use in multiple tests
- std::vector<char> buffer;
- };
- typedef test_group<llstreamqueue_data> llstreamqueue_group;
- typedef llstreamqueue_group::object object;
- llstreamqueue_group llstreamqueuegrp("llstreamqueue");
-
- template<> template<>
- void object::test<1>()
- {
- set_test_name("empty LLStreamQueue");
- ensure_equals("brand-new LLStreamQueue isn't empty",
- strq.size(), 0);
- ensure_equals("brand-new LLStreamQueue returns data",
- strq.asSource().read(&buffer[0], buffer.size()), 0);
- strq.asSink().close();
- ensure_equals("closed empty LLStreamQueue not at EOF",
- strq.asSource().read(&buffer[0], buffer.size()), -1);
- }
-
- template<> template<>
- void object::test<2>()
- {
- set_test_name("one internal block, one buffer");
- LLStreamQueue::Sink sink(strq.asSink());
- ensure_equals("write(\"\")", sink.write("", 0), 0);
- ensure_equals("0 write should leave LLStreamQueue empty (size())",
- strq.size(), 0);
- ensure_equals("0 write should leave LLStreamQueue empty (peek())",
- strq.peek(&buffer[0], buffer.size()), 0);
- // The meaning of "atomic" is that it must be smaller than our buffer.
- std::string atomic("atomic");
- ensure("test data exceeds buffer", atomic.length() < buffer.size());
- ensure_equals(STRINGIZE("write(\"" << atomic << "\")"),
- sink.write(&atomic[0], atomic.length()), atomic.length());
- ensure_equals("size() after write()", strq.size(), atomic.length());
- size_t peeklen(strq.peek(&buffer[0], buffer.size()));
- ensure_equals(STRINGIZE("peek(\"" << atomic << "\")"),
- peeklen, atomic.length());
- ensure_equals(STRINGIZE("peek(\"" << atomic << "\") result"),
- std::string(buffer.begin(), buffer.begin() + peeklen), atomic);
- ensure_equals("size() after peek()", strq.size(), atomic.length());
- // peek() should not consume. Use a different buffer to prove it isn't
- // just leftover data from the first peek().
- std::vector<char> again(buffer.size());
- peeklen = size_t(strq.peek(&again[0], again.size()));
- ensure_equals(STRINGIZE("peek(\"" << atomic << "\") again"),
- peeklen, atomic.length());
- ensure_equals(STRINGIZE("peek(\"" << atomic << "\") again result"),
- std::string(again.begin(), again.begin() + peeklen), atomic);
- // now consume.
- std::vector<char> third(buffer.size());
- size_t readlen(strq.read(&third[0], third.size()));
- ensure_equals(STRINGIZE("read(\"" << atomic << "\")"),
- readlen, atomic.length());
- ensure_equals(STRINGIZE("read(\"" << atomic << "\") result"),
- std::string(third.begin(), third.begin() + readlen), atomic);
- ensure_equals("peek() after read()", strq.peek(&buffer[0], buffer.size()), 0);
- ensure_equals("size() after read()", strq.size(), 0);
- }
-
- template<> template<>
- void object::test<3>()
- {
- set_test_name("basic skip()");
- std::string lovecraft("lovecraft");
- ensure("test data exceeds buffer", lovecraft.length() < buffer.size());
- ensure_equals(STRINGIZE("write(\"" << lovecraft << "\")"),
- strq.write(&lovecraft[0], lovecraft.length()), lovecraft.length());
- size_t peeklen(strq.peek(&buffer[0], buffer.size()));
- ensure_equals(STRINGIZE("peek(\"" << lovecraft << "\")"),
- peeklen, lovecraft.length());
- ensure_equals(STRINGIZE("peek(\"" << lovecraft << "\") result"),
- std::string(buffer.begin(), buffer.begin() + peeklen), lovecraft);
- std::streamsize skip1(4);
- ensure_equals(STRINGIZE("skip(" << skip1 << ")"), strq.skip(skip1), skip1);
- ensure_equals("size() after skip()", strq.size(), lovecraft.length() - skip1);
- size_t readlen(strq.read(&buffer[0], buffer.size()));
- ensure_equals(STRINGIZE("read(\"" << lovecraft.substr(skip1) << "\")"),
- readlen, lovecraft.length() - skip1);
- ensure_equals(STRINGIZE("read(\"" << lovecraft.substr(skip1) << "\") result"),
- std::string(buffer.begin(), buffer.begin() + readlen),
- lovecraft.substr(skip1));
- ensure_equals("unconsumed", strq.read(&buffer[0], buffer.size()), 0);
- }
-
- template<> template<>
- void object::test<4>()
- {
- set_test_name("skip() multiple blocks");
- std::string blocks[] = { "books of ", "H.P. ", "Lovecraft" };
- std::streamsize total(blocks[0].length() + blocks[1].length() + blocks[2].length());
- std::streamsize leave(5); // len("craft") above
- std::streamsize skip(total - leave);
- std::streamsize written(0);
- for (const std::string& block : blocks)
- {
- written += strq.write(&block[0], block.length());
- ensure_equals("size() after write()", strq.size(), written);
- }
- std::streamsize skiplen(strq.skip(skip));
- ensure_equals(STRINGIZE("skip(" << skip << ")"), skiplen, skip);
- ensure_equals("size() after skip()", strq.size(), leave);
- size_t readlen(strq.read(&buffer[0], buffer.size()));
- ensure_equals("read(\"craft\")", readlen, leave);
- ensure_equals("read(\"craft\") result",
- std::string(buffer.begin(), buffer.begin() + readlen), "craft");
- }
-
- template<> template<>
- void object::test<5>()
- {
- set_test_name("concatenate blocks");
- std::string blocks[] = { "abcd", "efghij", "klmnopqrs" };
- for (const std::string& block : blocks)
- {
- strq.write(&block[0], block.length());
- }
- std::vector<char> longbuffer(30);
- std::streamsize readlen(strq.read(&longbuffer[0], longbuffer.size()));
- ensure_equals("read() multiple blocks",
- readlen, blocks[0].length() + blocks[1].length() + blocks[2].length());
- ensure_equals("read() multiple blocks result",
- std::string(longbuffer.begin(), longbuffer.begin() + readlen),
- blocks[0] + blocks[1] + blocks[2]);
- }
-
- template<> template<>
- void object::test<6>()
- {
- set_test_name("split blocks");
- std::string blocks[] = { "abcdefghijklm", "nopqrstuvwxyz" };
- for (const std::string& block : blocks)
- {
- strq.write(&block[0], block.length());
- }
- strq.close();
- // We've already verified what strq.size() should be at this point;
- // see above test named "skip() multiple blocks"
- std::streamsize chksize(strq.size());
- std::streamsize readlen(strq.read(&buffer[0], buffer.size()));
- ensure_equals("read() 0", readlen, buffer.size());
- ensure_equals("read() 0 result", std::string(buffer.begin(), buffer.end()), "abcdefghij");
- chksize -= readlen;
- ensure_equals("size() after read() 0", strq.size(), chksize);
- readlen = strq.read(&buffer[0], buffer.size());
- ensure_equals("read() 1", readlen, buffer.size());
- ensure_equals("read() 1 result", std::string(buffer.begin(), buffer.end()), "klmnopqrst");
- chksize -= readlen;
- ensure_equals("size() after read() 1", strq.size(), chksize);
- readlen = strq.read(&buffer[0], buffer.size());
- ensure_equals("read() 2", readlen, chksize);
- ensure_equals("read() 2 result",
- std::string(buffer.begin(), buffer.begin() + readlen), "uvwxyz");
- ensure_equals("read() 3", strq.read(&buffer[0], buffer.size()), -1);
- }
-} // namespace tut
diff --git a/indra/llcommon/tests/llstreamtools_test.cpp b/indra/llcommon/tests/llstreamtools_test.cpp
new file mode 100644
index 0000000000..970afb3b02
--- /dev/null
+++ b/indra/llcommon/tests/llstreamtools_test.cpp
@@ -0,0 +1,883 @@
+/**
+ * @file llstreamtools_tut.cpp
+ * @author Adroit
+ * @date February 2007
+ * @brief llstreamtools test cases.
+ *
+ * $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 <tut/tut.hpp>
+
+#include "linden_common.h"
+#include "llstreamtools.h"
+#include "lltut.h"
+
+
+namespace tut
+{
+ struct streamtools_data
+ {
+ };
+ typedef test_group<streamtools_data> streamtools_test;
+ typedef streamtools_test::object streamtools_object;
+ tut::streamtools_test streamtools_testcase("streamtools");
+
+ //test cases for skip_whitespace()
+ template<> template<>
+ void streamtools_object::test<1>()
+ {
+ char arr[255];
+ std::string str;
+ std::string expected_result;
+ std::string actual_result;
+ std::istringstream is;
+
+ is.str(str = "");
+ ensure("skip_whitespace: empty string", (false == skip_whitespace(is)));
+
+ is.clear();
+ is.str(str = " SecondLife is a 3D World");
+ skip_whitespace(is);
+ is.get(arr, 255, '\0');
+ expected_result = "SecondLife is a 3D World";
+ ensure_equals("skip_whitespace: space", arr, expected_result);
+
+ is.clear();
+ is.str(str = "\t \tSecondLife is a 3D World");
+ skip_whitespace(is);
+ is.get(arr, 255, '\0');
+ expected_result = "SecondLife is a 3D World";
+ ensure_equals("skip_whitespace: space and tabs", arr, expected_result);
+
+ is.clear();
+ is.str(str = "\t \tSecondLife is a 3D World ");
+ skip_whitespace(is);
+ is.get(arr, 255, '\0');
+ expected_result = "SecondLife is a 3D World ";
+ ensure_equals("skip_whitespace: space at end", arr, expected_result);
+
+ is.clear();
+ is.str(str = "\t \r\nSecondLife is a 3D World");
+ skip_whitespace(is);
+ is.get(arr, 255, '\0');
+ expected_result = "\r\nSecondLife is a 3D World";
+ ensure_equals("skip_whitespace: space at end", arr, expected_result);
+ }
+
+ //testcases for skip_emptyspaces()
+ template<> template<>
+ void streamtools_object::test<2>()
+ {
+ char arr[255];
+ std::string str;
+ std::string expected_result;
+ std::string actual_result;
+ std::istringstream is;
+ bool ret;
+
+ is.clear();
+ is.str(str = " \tSecondLife is a 3D World.\n");
+ skip_emptyspace(is);
+ is.get(arr, 255, '\0');
+ expected_result = "SecondLife is a 3D World.\n";
+ ensure_equals("skip_emptyspace: space and tabs", arr, expected_result);
+
+ is.clear();
+ is.str(str = " \t\r\n \r SecondLife is a 3D World.\n");
+ skip_emptyspace(is);
+ is.get(arr, 255, '\0');
+ expected_result = "SecondLife is a 3D World.\n";
+ ensure_equals("skip_emptyspace: space, tabs, carriage return, newline", arr, expected_result);
+
+ is.clear();
+ is.str(str = "");
+ ret = skip_emptyspace(is);
+ is.get(arr, 255, '\0');
+ ensure("skip_emptyspace: empty string", ret == false);
+
+ is.clear();
+ is.str(str = " \r\n \t ");
+ ret = skip_emptyspace(is);
+ is.get(arr, 255, '\0');
+ ensure("skip_emptyspace: space newline empty", ret == false);
+ }
+
+ //testcases for skip_comments_and_emptyspace()
+ template<> template<>
+ void streamtools_object::test<3>()
+ {
+ char arr[255];
+ std::string str;
+ std::string expected_result;
+ std::string actual_result;
+ std::istringstream is;
+ bool ret;
+
+ is.clear();
+ is.str(str = " \t\r\n \r SecondLife is a 3D World.\n");
+ skip_comments_and_emptyspace(is);
+ is.get(arr, 255, '\0');
+ expected_result = "SecondLife is a 3D World.\n";
+ ensure_equals("skip_comments_and_emptyspace: space, tabs, carriage return, newline", arr, expected_result);
+
+ is.clear();
+ is.str(str = "# \r\n SecondLife is a 3D World.");
+ skip_comments_and_emptyspace(is);
+ is.get(arr, 255, '\0');
+ expected_result = "SecondLife is a 3D World.";
+ ensure_equals("skip_comments_and_emptyspace: skip comment - 1", arr, expected_result);
+
+ is.clear();
+ is.str(str = "# \r\n # SecondLife is a 3D World. ##");
+ ensure("should not be good()", ! skip_comments_and_emptyspace(is));
+ ensure("should be at eof()", is.eof());
+ // don't get(): given bad state, we can't rely on results
+
+ is.clear();
+ is.str(str = " \r\n SecondLife is a 3D World. ##");
+ skip_comments_and_emptyspace(is);
+ is.get(arr, 255, '\0');
+ expected_result = "SecondLife is a 3D World. ##";
+ ensure_equals("skip_comments_and_emptyspace: skip comment - 3", arr, expected_result);
+
+ is.clear();
+ is.str(str = "");
+ ret = skip_comments_and_emptyspace(is);
+ ensure("skip_comments_and_emptyspace: empty string", ! ret);
+
+ is.clear();
+ is.str(str = " \r\n \t # SecondLife is a 3D World");
+ ret = skip_comments_and_emptyspace(is);
+ ensure("skip_comments_and_emptyspace: space newline comment empty", ! ret);
+ }
+
+ //testcases for skip_line()
+ template<> template<>
+ void streamtools_object::test<4>()
+ {
+ char arr[255];
+ std::string str;
+ std::string expected_result;
+ std::string actual_result;
+ std::istringstream is;
+ bool ret;
+
+ is.clear();
+ is.str(str = "SecondLife is a 3D World.\n\n It provides an opportunity to the site \nuser to perform real life activities in virtual world.");
+ skip_line(is);
+ is.get(arr, 255, '\0');
+ expected_result = "\n It provides an opportunity to the site \nuser to perform real life activities in virtual world.";
+ ensure_equals("skip_line: 1 newline", arr, expected_result);
+
+ is.clear();
+ is.str(expected_result);
+ skip_line(is);
+ is.get(arr, 255, '\0');
+ expected_result = " It provides an opportunity to the site \nuser to perform real life activities in virtual world.";
+ ensure_equals("skip_line: 2 newline", arr, expected_result);
+
+ is.clear();
+ is.str(expected_result);
+ skip_line(is);
+ is.get(arr, 255, '\0');
+ expected_result = "user to perform real life activities in virtual world.";
+ ensure_equals("skip_line: 3 newline", arr, expected_result);
+
+ is.clear();
+ is.str(str = "");
+ ret = skip_line(is);
+ ensure("skip_line: empty string", ret == false);
+ }
+
+
+ // testcases for skip_to_next_word()
+ template<> template<>
+ void streamtools_object::test<5>()
+ {
+ char arr[255];
+ std::string str;
+ std::string expected_result;
+ std::string actual_result;
+ std::istringstream is;
+ bool ret;
+
+ is.clear();
+ is.str(str = "SecondLife is a 3D_World.\n\n It-provides an opportunity to the site \nuser to perform real life activities in virtual world.");
+ skip_to_next_word(is); // get past SecondLife
+ is.get(arr, 255, '\0');
+ expected_result = "is a 3D_World.\n\n It-provides an opportunity to the site \nuser to perform real life activities in virtual world.";
+ ensure_equals("skip_to_next_word: 1", arr, expected_result);
+
+ is.clear();
+ is.str(expected_result);
+ skip_to_next_word(is); // get past is
+ skip_to_next_word(is); // get past a
+ skip_to_next_word(is); // get past 3D_World.\n\n
+ is.get(arr, 255, '\0');
+ expected_result = "It-provides an opportunity to the site \nuser to perform real life activities in virtual world.";
+ ensure_equals("skip_to_next_word: get past .\n\n 2", arr, expected_result);
+
+ is.clear();
+ is.str(expected_result);
+ skip_to_next_word(is); // get past It-
+ expected_result = "provides an opportunity to the site \nuser to perform real life activities in virtual world.";
+ is.get(arr, 255, '\0');
+ ensure_equals("skip_to_next_word: get past -", arr, expected_result);
+
+ is.clear();
+ is.str(str = "");
+ ret = skip_to_next_word(is);
+ ensure("skip_line: empty string", ret == false);
+
+ is.clear();
+ is.str(str = " \r\n\r\n");
+ ret = skip_to_next_word(is);
+ ensure("skip_line: space new lines", ret == false);
+ }
+
+
+ //testcases for skip_to_end_of_next_keyword()
+ template<> template<>
+ void streamtools_object::test<6>()
+ {
+ char arr[255];
+ std::string str;
+ std::string expected_result;
+ std::string actual_result;
+ std::istringstream is;
+ bool ret;
+
+ is.clear();
+ is.str(str = "FIRSTKEY followed by second delimiter\nSECONDKEY\t SecondValue followed by third delimiter \nSECONDKEY\nFOURTHKEY FOURTHVALUEis a 3DWorld.");
+ ret = skip_to_end_of_next_keyword("FIRSTKEY", is);
+ is.get(arr, 255, '\0');
+ expected_result = " followed by second delimiter\nSECONDKEY\t SecondValue followed by third delimiter \nSECONDKEY\nFOURTHKEY FOURTHVALUEis a 3DWorld.";
+ ensure_equals("skip_to_end_of_next_keyword: 1", arr, expected_result);
+
+ is.clear();
+ is.str(expected_result);
+ ret = skip_to_end_of_next_keyword("SECONDKEY", is);
+ is.get(arr, 255, '\0');
+ expected_result = "\t SecondValue followed by third delimiter \nSECONDKEY\nFOURTHKEY FOURTHVALUEis a 3DWorld.";
+ ensure_equals("skip_to_end_of_next_keyword: 2", arr, expected_result);
+
+ is.clear();
+ is.str(expected_result);
+ ret = skip_to_end_of_next_keyword("SECONDKEY", is);
+ is.get(arr, 255, '\0');
+ expected_result = "\nFOURTHKEY FOURTHVALUEis a 3DWorld.";
+ ensure_equals("skip_to_end_of_next_keyword: 3", arr, expected_result);
+
+ is.clear();
+ is.str(expected_result);
+ ret = skip_to_end_of_next_keyword("FOURTHKEY", is);
+ is.get(arr, 255, '\0');
+ expected_result = " FOURTHVALUEis a 3DWorld.";
+ ensure_equals("skip_to_end_of_next_keyword: 4", arr, expected_result);
+
+ is.clear();
+ is.str(str = "{should be skipped as newline/space/tab does not follow but this one should be picked\n { Does it?\n");
+ ret = skip_to_end_of_next_keyword("{", is);
+ is.get(arr, 255, '\0');
+ expected_result = " Does it?\n";
+ ensure_equals("skip_to_end_of_next_keyword: multiple delim matches on same line", arr, expected_result);
+
+ is.clear();
+ is.str(str = "Delim { could not be found at start");
+ ret = skip_to_end_of_next_keyword("{", is);
+ ensure("skip_to_end_of_next_keyword: delim should not be present", ret == false);
+
+ is.clear();
+ is.str(str = "Empty Delim");
+ ret = skip_to_end_of_next_keyword("", is);
+ ensure("skip_to_end_of_next_keyword: empty delim should not be valid", ret == false);
+
+ is.clear();
+ is.str(str = "");
+ ret = skip_to_end_of_next_keyword("}", is);
+ ensure("skip_to_end_of_next_keyword: empty string", ret == false);
+ }
+
+ //testcases for get_word(std::string& output_string, std::istream& input_stream)
+ template<> template<>
+ void streamtools_object::test<7>()
+ {
+ std::string str;
+ std::string expected_result;
+ std::string actual_result;
+ std::istringstream is;
+ bool ret;
+
+ is.clear();
+ is.str(str = " First Second \t \r \n Third Fourth-ShouldThisBePartOfFourth Fifth\n");
+ actual_result = "";
+ ret = get_word(actual_result, is);
+ expected_result = "First";
+ ensure_equals("get_word: 1", actual_result, expected_result);
+
+ actual_result = "";
+ ret = get_word(actual_result, is);
+ expected_result = "Second";
+ ensure_equals("get_word: 2", actual_result, expected_result);
+
+ actual_result = "";
+ ret = get_word(actual_result, is);
+ expected_result = "Third";
+ ensure_equals("get_word: 3", actual_result, expected_result);
+
+ // the current implementation of get_word seems inconsistent with
+ // skip_to_next_word. skip_to_next_word treats any character other
+ // than alhpa-numeric and '_' as a delimter, while get_word()
+ // treats only isspace() (i.e. space, form-feed('\f'), newline ('\n'),
+ // carriage return ('\r'), horizontal tab ('\t'), and vertical tab ('\v')
+ // as delimiters
+ actual_result = "";
+ ret = get_word(actual_result, is); // will copy Fourth-ShouldThisBePartOfFourth
+ expected_result = "Fourth-ShouldThisBePartOfFourth"; // as per current impl.
+ ensure_equals("get_word: 4", actual_result, expected_result);
+
+ actual_result = "";
+ ret = get_word(actual_result, is); // will copy Fifth
+ expected_result = "Fifth"; // as per current impl.
+ ensure_equals("get_word: 5", actual_result, expected_result);
+
+ is.clear();
+ is.str(str = " \t \r \n ");
+ actual_result = "";
+ ret = get_word(actual_result, is);
+ ensure("get_word: empty all spaces, newline tabs", ret == false);
+
+ is.clear();
+ is.str(str = "");
+ actual_result = "";
+ ret = get_word(actual_result, is);
+ ensure("get_word: empty string", ret == false);
+ }
+
+ // testcase for get_word and skip_to_next_word compatibility
+ template<> template<>
+ void streamtools_object::test<8>()
+ {
+ std::string str;
+ std::string expected_result;
+ std::string actual_result;
+ std::istringstream is;
+
+ is.clear();
+ is.str(str = " First Second \t \r \n Third Fourth-ShouldThisBePartOfFourth Fifth\n");
+ actual_result = "";
+ get_word(actual_result, is); // First
+ actual_result = "";
+ get_word(actual_result, is); // Second
+ actual_result = "";
+ get_word(actual_result, is); // Third
+
+ // the current implementation of get_word seems inconsistent with
+ // skip_to_next_word. skip_to_next_word treats any character other
+ // than alhpa-numeric and '_' as a delimter, while get_word()
+ // treats only isspace() (i.e. space, form-feed('\f'), newline ('\n'),
+ // carriage return ('\r'), horizontal tab ('\t'), and vertical tab ('\v')
+ // as delimiters
+ actual_result = "";
+ get_word(actual_result, is); // will copy Fourth-ShouldThisBePartOfFourth
+
+ actual_result = "";
+ get_word(actual_result, is); // will copy Fifth
+
+ is.clear();
+ is.str(str = " First Second \t \r \n Third Fourth_ShouldThisBePartOfFourth Fifth\n");
+ skip_to_next_word(is); // should now point to First
+ skip_to_next_word(is); // should now point to Second
+ skip_to_next_word(is); // should now point to Third
+ skip_to_next_word(is); // should now point to Fourth
+ skip_to_next_word(is); // should now point to ShouldThisBePartOfFourth
+ expected_result = "";
+ // will copy ShouldThisBePartOfFourth, the fifth word,
+ // while using get_word above five times result in getting "Fifth"
+ get_word(expected_result, is);
+ ensure_equals("get_word: skip_to_next_word compatibility", actual_result, expected_result);
+ }
+
+ //testcases for get_word(std::string& output_string, std::istream& input_stream, int n)
+ template<> template<>
+ void streamtools_object::test<9>()
+ {
+ std::string str;
+ std::string expected_result;
+ std::string actual_result;
+ std::istringstream is;
+ bool ret;
+
+ is.clear();
+ is.str(str = " First Second \t \r \n Third Fourth-ShouldThisBePartOfFourth Fifth\n");
+ actual_result = "";
+ ret = get_word(actual_result, is, 255);
+ expected_result = "First";
+ ensure_equals("get_word: 1", actual_result, expected_result);
+
+ actual_result = "";
+ ret = get_word(actual_result, is, 4);
+ expected_result = "Seco"; // should be cut short
+ ensure_equals("get_word: 2", actual_result, expected_result);
+
+ actual_result = "";
+ ret = get_word(actual_result, is, 255);
+ expected_result = "nd"; // get remainder of Second
+ ensure_equals("get_word: 3", actual_result, expected_result);
+
+ actual_result = "";
+ ret = get_word(actual_result, is, 0); // 0 size string
+ expected_result = ""; // get remainder of Second
+ ensure_equals("get_word: 0 sized output", actual_result, expected_result);
+
+ actual_result = "";
+ ret = get_word(actual_result, is, 255);
+ expected_result = "Third";
+ ensure_equals("get_word: 4", actual_result, expected_result);
+
+ is.clear();
+ is.str(str = " \t \r \n ");
+ actual_result = "";
+ ret = get_word(actual_result, is, 255);
+ ensure("get_word: empty all spaces, newline tabs", ret == false);
+
+ is.clear();
+ is.str(str = "");
+ actual_result = "";
+ ret = get_word(actual_result, is, 255);
+ ensure("get_word: empty string", ret == false);
+ }
+
+ //test cases for get_line(std::string& output_string, std::istream& input_stream)
+ template<> template<>
+ void streamtools_object::test<10>()
+ {
+ std::string str;
+ std::string expected_result;
+ std::string actual_result;
+ std::istringstream is;
+
+ is.clear();
+ is.str(str = "First Second \t \r\n Third Fourth-ShouldThisBePartOfFourth IsThisFifth\n");
+ actual_result = "";
+ get_line(actual_result, is);
+ expected_result = "First Second \t \r\n";
+ ensure_equals("get_line: 1", actual_result, expected_result);
+
+ actual_result = "";
+ get_line(actual_result, is);
+ expected_result = " Third Fourth-ShouldThisBePartOfFourth IsThisFifth\n";
+ ensure_equals("get_line: 2", actual_result, expected_result);
+
+ is.clear();
+ is.str(str = "\nFirst Line.\n\nSecond Line.\n");
+ actual_result = "";
+ get_line(actual_result, is);
+ expected_result = "\n";
+ ensure_equals("get_line: First char as newline", actual_result, expected_result);
+
+ actual_result = "";
+ get_line(actual_result, is);
+ expected_result = "First Line.\n";
+ ensure_equals("get_line: 3", actual_result, expected_result);
+
+ actual_result = "";
+ get_line(actual_result, is);
+ expected_result = "\n";
+ ensure_equals("get_line: 4", actual_result, expected_result);
+
+ actual_result = "";
+ get_line(actual_result, is);
+ expected_result = "Second Line.\n";
+ ensure_equals("get_line: 5", actual_result, expected_result);
+ }
+
+ //test cases for get_line(std::string& output_string, std::istream& input_stream)
+ template<> template<>
+ void streamtools_object::test<11>()
+ {
+ std::string str;
+ std::string expected_result;
+ std::string actual_result;
+ std::istringstream is;
+ bool ret;
+
+ is.clear();
+ is.str(str = "One Line only with no newline");
+ actual_result = "";
+ ret = get_line(actual_result, is);
+ expected_result = "One Line only with no newline";
+ ensure_equals("get_line: No newline", actual_result, expected_result);
+ ensure_equals("return value is good state of stream", ret, is.good());
+ }
+
+ //test cases for get_line(std::string& output_string, std::istream& input_stream)
+ template<> template<>
+ void streamtools_object::test<12>()
+ {
+ std::string str;
+ std::string expected_result;
+ std::string actual_result;
+ std::istringstream is;
+
+ // need to be check if this test case is wrong or the implementation is wrong.
+ is.clear();
+ is.str(str = "Should not skip lone \r.\r\n");
+ actual_result = "";
+ get_line(actual_result, is);
+ expected_result = "Should not skip lone \r.\r\n";
+ ensure_equals("get_line: carriage return skipped even though not followed by newline", actual_result, expected_result);
+ }
+
+ //test cases for get_line(std::string& output_string, std::istream& input_stream)
+ template<> template<>
+ void streamtools_object::test<13>()
+ {
+ std::string str;
+ std::string expected_result;
+ std::string actual_result;
+ std::istringstream is;
+
+ is.clear();
+ is.str(str = "\n");
+ actual_result = "";
+ get_line(actual_result, is);
+ expected_result = "\n";
+ ensure_equals("get_line: Just newline", actual_result, expected_result);
+ }
+
+
+ //testcases for get_line(std::string& output_string, std::istream& input_stream, int n)
+ template<> template<>
+ void streamtools_object::test<14>()
+ {
+ std::string str;
+ std::string expected_result;
+ std::string actual_result;
+ std::istringstream is;
+
+ is.clear();
+ is.str(str = "First Line.\nSecond Line.\n");
+ actual_result = "";
+ get_line(actual_result, is, 255);
+ expected_result = "First Line.\n";
+ ensure_equals("get_line: Basic Operation", actual_result, expected_result);
+
+ actual_result = "";
+ get_line(actual_result, is, sizeof("Second")-1);
+ expected_result = "Second\n";
+ ensure_equals("get_line: Insufficient length 1", actual_result, expected_result);
+
+ actual_result = "";
+ get_line(actual_result, is, 255);
+ expected_result = " Line.\n";
+ ensure_equals("get_line: Remainder after earlier insufficient length", actual_result, expected_result);
+
+ is.clear();
+ is.str(str = "One Line only with no newline with limited length");
+ actual_result = "";
+ get_line(actual_result, is, sizeof("One Line only with no newline with limited length")-1);
+ expected_result = "One Line only with no newline with limited length\n";
+ ensure_equals("get_line: No newline with limited length", actual_result, expected_result);
+
+ is.clear();
+ is.str(str = "One Line only with no newline");
+ actual_result = "";
+ get_line(actual_result, is, 255);
+ expected_result = "One Line only with no newline";
+ ensure_equals("get_line: No newline", actual_result, expected_result);
+ }
+
+ //testcases for get_line(std::string& output_string, std::istream& input_stream, int n)
+ template<> template<>
+ void streamtools_object::test<15>()
+ {
+ std::string str;
+ std::string expected_result;
+ std::string actual_result;
+ std::istringstream is;
+ bool ret;
+
+ is.clear();
+ is.str(str = "One Line only with no newline");
+ actual_result = "";
+ ret = get_line(actual_result, is, 255);
+ expected_result = "One Line only with no newline";
+ ensure_equals("get_line: No newline", actual_result, expected_result);
+ ensure_equals("return value is good state of stream", ret, is.good());
+ }
+
+ //testcases for remove_last_char()
+ template<> template<>
+ void streamtools_object::test<16>()
+ {
+ std::string str;
+ std::string expected_result;
+ bool ret;
+
+ str = "SecondLife is a 3D World";
+
+ ret = remove_last_char('d',str);
+ expected_result = "SecondLife is a 3D Worl";
+ ensure_equals("remove_last_char: should remove last char", str, expected_result);
+
+ str = "SecondLife is a 3D World";
+ ret = remove_last_char('W',str);
+ expected_result = "SecondLife is a 3D World";
+ ensure_equals("remove_last_char: should not remove as it is not last char", str, expected_result);
+ ensure("remove_last_char: should return false", ret == false);
+
+ str = "SecondLife is a 3D World\n";
+ ret = remove_last_char('\n',str);
+ expected_result = "SecondLife is a 3D World";
+ ensure_equals("remove_last_char: should remove last newline", str, expected_result);
+ ensure("remove_last_char: should remove newline and return true", ret == true);
+ }
+
+
+ //testcases for unescaped_string()
+ template<> template<>
+ void streamtools_object::test<17>()
+ {
+ std::string str;
+ std::string expected_result;
+
+ str = "SecondLife is a 3D world \\n";
+ unescape_string(str);
+ expected_result = "SecondLife is a 3D world \n";
+ ensure_equals("unescape_string: newline", str, expected_result);
+
+ str = "SecondLife is a 3D world \\\\t \\n";
+ unescape_string(str);
+ expected_result = "SecondLife is a 3D world \\t \n";
+ ensure_equals("unescape_string: backslash and newline", str, expected_result);
+
+ str = "SecondLife is a 3D world \\ ";
+ unescape_string(str);
+ expected_result = "SecondLife is a 3D world \\ ";
+ ensure_equals("unescape_string: insufficient to unescape", str, expected_result);
+
+ str = "SecondLife is a 3D world \\n \\n \\n \\\\\\n";
+ unescape_string(str);
+ expected_result = "SecondLife is a 3D world \n \n \n \\\n";
+ ensure_equals("unescape_string: multipel newline and backslash", str, expected_result);
+
+ str = "SecondLife is a 3D world \\t";
+ unescape_string(str);
+ expected_result = "SecondLife is a 3D world \\t";
+ ensure_equals("unescape_string: leaves tab as is", str, expected_result);
+
+ str = "\\n";
+ unescape_string(str);
+ expected_result = "\n";
+ ensure_equals("unescape_string: only a newline", str, expected_result);
+ }
+
+ //testcases for escape_string()
+ template<> template<>
+ void streamtools_object::test<18>()
+ {
+ std::string str;
+ std::string expected_result;
+
+ str = "SecondLife is a 3D world \n";
+ escape_string(str);
+ expected_result = "SecondLife is a 3D world \\n";
+ ensure_equals("escape_string: newline", str, expected_result);
+
+ str = "SecondLife is a 3D world \\t \n";
+ escape_string(str);
+ expected_result = "SecondLife is a 3D world \\\\t \\n";
+ ensure_equals("escape_string: backslash and newline", str, expected_result);
+
+ str = "SecondLife is a 3D world \n \n \n \\\n";
+ escape_string(str);
+ expected_result = "SecondLife is a 3D world \\n \\n \\n \\\\\\n";
+ ensure_equals("escape_string: multipel newline and backslash", str, expected_result);
+
+ str = "SecondLife is a 3D world \t";
+ escape_string(str);
+ expected_result = "SecondLife is a 3D world \t";
+ ensure_equals("unescape_string: leaves tab as is", str, expected_result);
+
+ str = "\n";
+ escape_string(str);
+ expected_result = "\\n";
+ ensure_equals("unescape_string: only a newline", str, expected_result);
+
+ // serialization/deserialization escape->unescape
+ str = "SecondLife is a 3D world \n \n \n \\\n";
+ escape_string(str);
+ unescape_string(str);
+ expected_result = "SecondLife is a 3D world \n \n \n \\\n";
+ ensure_equals("escape_string: should preserve with escape/unescape", str, expected_result);
+
+ // serialization/deserialization unescape->escape
+ str = "SecondLife is a 3D world \\n \\n \\n \\\\";
+ unescape_string(str);
+ escape_string(str);
+ expected_result = "SecondLife is a 3D world \\n \\n \\n \\\\";
+ ensure_equals("escape_string: should preserve with unescape/escape", str, expected_result);
+ }
+
+ // testcases for replace_newlines_with_whitespace()
+ template<> template<>
+ void streamtools_object::test<19>()
+ {
+ std::string str;
+ std::string expected_result;
+
+ str = "SecondLife is a 3D \n\nworld\n";
+ replace_newlines_with_whitespace(str);
+ expected_result = "SecondLife is a 3D world ";
+ ensure_equals("replace_newlines_with_whitespace: replace all newline", str, expected_result);
+
+ str = "\nSecondLife is a 3D world\n";
+ replace_newlines_with_whitespace(str);
+ expected_result = " SecondLife is a 3D world ";
+ ensure_equals("replace_newlines_with_whitespace: begin and newline", str, expected_result);
+
+ str = "SecondLife is a 3D world\r\t";
+ replace_newlines_with_whitespace(str);
+ expected_result = "SecondLife is a 3D world\r\t";
+ ensure_equals("replace_newlines_with_whitespace: should only replace newline", str, expected_result);
+
+ str = "";
+ replace_newlines_with_whitespace(str);
+ expected_result = "";
+ ensure_equals("replace_newlines_with_whitespace: empty string", str, expected_result);
+ }
+
+ //testcases for remove_double_quotes()
+ template<> template<>
+ void streamtools_object::test<20>()
+ {
+ std::string str;
+ std::string expected_result;
+
+ str = "SecondLife is a \"\"3D world";
+ remove_double_quotes(str);
+ expected_result = "SecondLife is a 3D world";
+ ensure_equals("remove_double_quotes: replace empty doube quotes", str, expected_result);
+
+ str = "SecondLife is a \"3D world";
+ remove_double_quotes(str);
+ expected_result = "SecondLife is a 3D world";
+ ensure_equals("remove_double_quotes: keep as is it matching quote not found", str, expected_result);
+ }
+
+ // testcases for get_brace_count()
+ template<> template<>
+ void streamtools_object::test<21>()
+ {
+ }
+
+ //testcases for get_keyword_and_value()
+ template<> template<>
+ void streamtools_object::test<22>()
+ {
+ std::string s = "SecondLife is a 3D World";
+ std::string keyword;
+ std::string value;
+ get_keyword_and_value(keyword, value, s);
+ ensure("get_keyword_and_value: Unable to get Keyword and Value", ((keyword == "SecondLife") && (value == "is a 3D World")));
+
+ s = "SecondLife";
+ get_keyword_and_value(keyword, value, s);
+ ensure("get_keyword_and_value: value should be empty", ((keyword == "SecondLife") && (value == "")));
+
+ s = "SecondLife \t is cool! \n";
+ get_keyword_and_value(keyword, value, s);
+ ensure("get_keyword_and_value: remove space before value but not after", ((keyword == "SecondLife") && (value == "is cool! ")));
+ }
+
+ //testcases for get_keyword_and_value()
+ template<> template<>
+ void streamtools_object::test<23>()
+ {
+ std::string s;
+ std::string keyword = "SOME PRIOR KEYWORD";
+ std::string value = "SOME PRIOR VALUE";
+
+ s = "SecondLife\n";
+ get_keyword_and_value(keyword, value, s);
+ ensure("get_keyword_and_value: terminated with newline. value should be empty", ((keyword == "SecondLife") && (value == "")));
+ }
+
+ //testcases for get_keyword_and_value()
+ template<> template<>
+ void streamtools_object::test<24>()
+ {
+ std::string s;
+ std::string keyword = "SOME PRIOR KEYWORD";
+ std::string value = "SOME PRIOR VALUE";
+
+ s = "";
+ get_keyword_and_value(keyword, value, s);
+ ensure("get_keyword_and_value: empty string. keyword value should empty", ((keyword == "") && (value == "")));
+ }
+
+ //testcase for fullread()
+ template<> template<>
+ void streamtools_object::test<25>()
+ {
+ std::string str = "First Line.\nSecond Line\n";
+ std::istringstream is(str);
+ char buf[255] = {0};
+
+ fullread(is, buf, 255);
+ ensure_memory_matches("fullread: read with newlines", (void*) buf, static_cast<U32>(str.size())-1, (void*) str.c_str(), static_cast<U32>(str.size())-1);
+
+ is.clear();
+ is.str(str = "First Line.\nSecond Line\n");
+ memset(buf, 0, 255);
+
+ char expected_string[] = "First Line.\nSecond";
+ int len = sizeof(expected_string)-1;
+ fullread(is, buf, len);
+ ensure_memory_matches("fullread: read with newlines", (void*) buf, len, (void*) &expected_string, len);
+ }
+
+
+// testcases for operator >>
+
+ template<> template<>
+ void streamtools_object::test<26>()
+ {
+ char arr[255];
+ std::string str;
+ std::string toCheck = "SecondLife" ;
+ std::string expected_result;
+ std::istringstream stream("SecondLife is a 3D World");
+ stream >> toCheck.c_str();
+ stream.get(arr, 255, '\0');
+ expected_result = " is a 3D World";
+ ensure_equals("istream << operator", arr, expected_result);
+
+ stream.clear();
+ stream.str(str = "SecondLife is a 3D World");
+ toCheck = "is";
+ stream >> toCheck.c_str();
+ ensure("istream << operator should have failed", stream.good() == false);
+ }
+}
diff --git a/indra/llcommon/tests/llstring_test.cpp b/indra/llcommon/tests/llstring_test.cpp
index 7393e0087c..3c9d8071ac 100644
--- a/indra/llcommon/tests/llstring_test.cpp
+++ b/indra/llcommon/tests/llstring_test.cpp
@@ -374,7 +374,7 @@ namespace tut
{
F32 value;
std::string str_val("2147483647"); //0x7FFFFFFF
- ensure("1: convertToF32 failed", LLStringUtil::convertToF32(str_val, value) && value == 2147483647);
+ ensure("1: convertToF32 failed", LLStringUtil::convertToF32(str_val, value) && value == 2147483647.f);
str_val = "0";
ensure("2: convertToF32 failed", LLStringUtil::convertToF32(str_val, value) && value == 0);
@@ -396,7 +396,7 @@ namespace tut
{
F64 value;
std::string str_val("9223372036854775807"); //0x7FFFFFFFFFFFFFFF
- ensure("1: convertToF64 failed", LLStringUtil::convertToF64(str_val, value) && value == 9223372036854775807LL);
+ ensure("1: convertToF64 failed", LLStringUtil::convertToF64(str_val, value) && value == 9223372036854775807.0);
str_val = "0";
ensure("2: convertToF64 failed", LLStringUtil::convertToF64(str_val, value) && value == 0.0F);
diff --git a/indra/llcommon/threadpool.cpp b/indra/llcommon/threadpool.cpp
index 451e60c083..6adbdffba8 100644
--- a/indra/llcommon/threadpool.cpp
+++ b/indra/llcommon/threadpool.cpp
@@ -78,6 +78,7 @@ void LL::ThreadPoolBase::start()
std::string tname{ stringize(mName, ':', (i+1), '/', mThreadCount) };
mThreads.emplace_back(tname, [this, tname]()
{
+ set_thread_name(tname.c_str());
LL_PROFILER_SET_THREAD_NAME(tname.c_str());
run(tname);
});
diff --git a/indra/llcommon/timer.h b/indra/llcommon/timer.h
deleted file mode 100644
index aaa0cf0775..0000000000
--- a/indra/llcommon/timer.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/**
- * @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$
- */
-#include "lltimer.h"