summaryrefslogtreecommitdiff
path: root/indra/llcommon/llstring.h
diff options
context:
space:
mode:
authorJames Cook <james@lindenlab.com>2007-01-02 08:33:20 +0000
committerJames Cook <james@lindenlab.com>2007-01-02 08:33:20 +0000
commit420b91db29485df39fd6e724e782c449158811cb (patch)
treeb471a94563af914d3ed3edd3e856d21cb1b69945 /indra/llcommon/llstring.h
Print done when done.
Diffstat (limited to 'indra/llcommon/llstring.h')
-rw-r--r--indra/llcommon/llstring.h1281
1 files changed, 1281 insertions, 0 deletions
diff --git a/indra/llcommon/llstring.h b/indra/llcommon/llstring.h
new file mode 100644
index 0000000000..dca8ce4f3e
--- /dev/null
+++ b/indra/llcommon/llstring.h
@@ -0,0 +1,1281 @@
+/**
+ * @file llstring.h
+ * @brief String utility functions and LLString class.
+ *
+ * Copyright (c) 2001-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#ifndef LL_LLSTRING_H
+#define LL_LLSTRING_H
+
+#include "stdtypes.h"
+#include "llerror.h"
+#include <algorithm>
+#include <map>
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <math.h>
+#if LL_LINUX
+#include <wctype.h>
+#include <wchar.h>
+#endif
+
+const char LL_UNKNOWN_CHAR = '?';
+
+class LLVector3;
+class LLVector3d;
+class LLQuaternion;
+class LLUUID;
+class LLColor4;
+class LLColor4U;
+
+#if (LL_DARWIN || (LL_LINUX && __GNUC__ > 2))
+// Template specialization of char_traits for U16s. Only necessary on Mac for now (exists on Windows, unused/broken on Linux/gcc2.95)
+namespace std
+{
+template<>
+struct char_traits<U16>
+{
+ typedef U16 char_type;
+ typedef int int_type;
+ typedef streampos pos_type;
+ typedef streamoff off_type;
+ typedef mbstate_t state_type;
+
+ static void
+ assign(char_type& __c1, const char_type& __c2)
+ { __c1 = __c2; }
+
+ static bool
+ eq(const char_type& __c1, const char_type& __c2)
+ { return __c1 == __c2; }
+
+ static bool
+ lt(const char_type& __c1, const char_type& __c2)
+ { return __c1 < __c2; }
+
+ static int
+ compare(const char_type* __s1, const char_type* __s2, size_t __n)
+ { return memcmp(__s1, __s2, __n * sizeof(char_type)); }
+
+ static size_t
+ length(const char_type* __s)
+ {
+ const char_type *cur_char = __s;
+ while (*cur_char != 0)
+ {
+ ++cur_char;
+ }
+ return cur_char - __s;
+ }
+
+ static const char_type*
+ find(const char_type* __s, size_t __n, const char_type& __a)
+ { return static_cast<const char_type*>(memchr(__s, __a, __n * sizeof(char_type))); }
+
+ static char_type*
+ move(char_type* __s1, const char_type* __s2, size_t __n)
+ { return static_cast<char_type*>(memmove(__s1, __s2, __n * sizeof(char_type))); }
+
+ static char_type*
+ copy(char_type* __s1, const char_type* __s2, size_t __n)
+ { return static_cast<char_type*>(memcpy(__s1, __s2, __n * sizeof(char_type))); }
+
+ static char_type*
+ assign(char_type* __s, size_t __n, char_type __a)
+ {
+ // This isn't right.
+ //return static_cast<char_type*>(memset(__s, __a, __n * sizeof(char_type)));
+
+ // I don't think there's a standard 'memset' for 16-bit values.
+ // Do this the old-fashioned way.
+
+ size_t __i;
+ for(__i = 0; __i < __n; __i++)
+ {
+ __s[__i] = __a;
+ }
+ return __s;
+ }
+
+ static char_type
+ to_char_type(const int_type& __c)
+ { return static_cast<char_type>(__c); }
+
+ static int_type
+ to_int_type(const char_type& __c)
+ { return static_cast<int_type>(__c); }
+
+ static bool
+ eq_int_type(const int_type& __c1, const int_type& __c2)
+ { return __c1 == __c2; }
+
+ static int_type
+ eof() { return static_cast<int_type>(EOF); }
+
+ static int_type
+ not_eof(const int_type& __c)
+ { return (__c == eof()) ? 0 : __c; }
+ };
+};
+#endif
+
+class LLStringOps
+{
+public:
+ static char toUpper(char elem) { return toupper(elem); }
+ static llwchar toUpper(llwchar elem) { return towupper(elem); }
+
+ static char toLower(char elem) { return tolower(elem); }
+ static llwchar toLower(llwchar elem) { return towlower(elem); }
+
+ static BOOL isSpace(char elem) { return isspace(elem) != 0; }
+ static BOOL isSpace(llwchar elem) { return iswspace(elem) != 0; }
+
+ static BOOL isUpper(char elem) { return isupper(elem) != 0; }
+ static BOOL isUpper(llwchar elem) { return iswupper(elem) != 0; }
+
+ static BOOL isLower(char elem) { return islower(elem) != 0; }
+ static BOOL isLower(llwchar elem) { return iswlower(elem) != 0; }
+
+ static S32 collate(const char* a, const char* b) { return strcoll(a, b); }
+ static S32 collate(const llwchar* a, const llwchar* b);
+
+ static BOOL isDigit(char a) { return isdigit(a) != 0; }
+ static BOOL isDigit(llwchar a) { return iswdigit(a) != 0; }
+};
+
+//RN: I used a templated base class instead of a pure interface class to minimize code duplication
+// but it might be worthwhile to just go with two implementations (LLString and LLWString) of
+// an interface class, unless we can think of a good reason to have a std::basic_string polymorphic base
+
+//****************************************************************
+// NOTA BENE: do *NOT* dynamically allocate memory inside of LLStringBase as the {*()^#%*)#%W^*)#%*)STL implentation
+// of basic_string doesn't provide a virtual destructor. If we need to allocate resources specific to LLString
+// then we should either customize std::basic_string to linden::basic_string or change LLString to be a wrapper
+// that contains an instance of std::basic_string. Similarly, overriding methods defined in std::basic_string will *not*
+// be called in a polymorphic manner (passing an instance of basic_string to a particular function)
+//****************************************************************
+
+template <class T>
+class LLStringBase : public std::basic_string<T>
+{
+public:
+ typedef typename std::basic_string<T>::size_type size_type;
+
+ // naming convention follows those set for LLUUID
+// static LLStringBase null; // deprecated for std::string compliance
+// static LLStringBase zero_length; // deprecated for std::string compliance
+
+
+ // standard constructors
+ LLStringBase() : std::basic_string<T>() {}
+ LLStringBase(const LLStringBase& s): std::basic_string<T>(s) {}
+ LLStringBase(const std::basic_string<T>& s) : std::basic_string<T>(s) {}
+ LLStringBase(const std::basic_string<T>& s, size_type pos, size_type n = std::basic_string<T>::npos)
+ : std::basic_string<T>(s, pos, n) {}
+ LLStringBase(size_type count, const T& c) : std::basic_string<T>() { assign(count, c);}
+ // custom constructors
+ LLStringBase(const T* s);
+ LLStringBase(const T* s, size_type n);
+ LLStringBase(const T* s, size_type pos, size_type n );
+
+#if LL_LINUX
+ void clear() { assign(null); }
+
+ LLStringBase<T>& assign(const T* s);
+ LLStringBase<T>& assign(const T* s, size_type n);
+ LLStringBase<T>& assign(const LLStringBase& s);
+ LLStringBase<T>& assign(size_type n, const T& c);
+ LLStringBase<T>& assign(const T* a, const T* b);
+ LLStringBase<T>& assign(typename LLStringBase<T>::iterator &it1, typename LLStringBase<T>::iterator &it2);
+ LLStringBase<T>& assign(typename LLStringBase<T>::const_iterator &it1, typename LLStringBase<T>::const_iterator &it2);
+
+ // workaround for bug in gcc2 STL headers.
+ #if ((__GNUC__ <= 2) && (!defined _STLPORT_VERSION))
+ const T* c_str () const
+ {
+ if (length () == 0)
+ {
+ static const T zero = 0;
+ return &zero;
+ }
+
+ //terminate ();
+ { string_char_traits<T>::assign(const_cast<T*>(data())[length()], string_char_traits<T>::eos()); }
+
+ return data ();
+ }
+ #endif
+#endif
+
+ bool operator==(const T* _Right) const { return _Right ? (std::basic_string<T>::compare(_Right) == 0) : this->empty(); }
+
+public:
+ /////////////////////////////////////////////////////////////////////////////////////////
+ // Static Utility functions that operate on std::strings
+
+ static LLStringBase null;
+
+ typedef std::map<std::string, std::string> format_map_t;
+ static S32 format(std::basic_string<T>& s, const format_map_t& fmt_map);
+
+ static BOOL isValidIndex(const std::basic_string<T>& string, size_type i)
+ {
+ return !string.empty() && (0 <= i) && (i <= string.size());
+ }
+
+ static void trimHead(std::basic_string<T>& string);
+ static void trimTail(std::basic_string<T>& string);
+ static void trim(std::basic_string<T>& string) { trimHead(string); trimTail(string); }
+ static void truncate(std::basic_string<T>& string, size_type count);
+
+ static void toUpper(std::basic_string<T>& string);
+ static void toLower(std::basic_string<T>& string);
+
+ // True if this is the head of s.
+ static BOOL isHead( const std::basic_string<T>& string, const T* s );
+
+ static void addCRLF(std::basic_string<T>& string);
+ static void removeCRLF(std::basic_string<T>& string);
+
+ static void replaceTabsWithSpaces( std::basic_string<T>& string, size_type spaces_per_tab );
+ static void replaceNonstandardASCII( std::basic_string<T>& string, T replacement );
+ static void replaceChar( std::basic_string<T>& string, T target, T replacement );
+
+ static BOOL containsNonprintable(const std::basic_string<T>& string);
+ static void stripNonprintable(std::basic_string<T>& string);
+
+ /**
+ * @brief Unsafe way to make ascii characters. You should probably
+ * only call this when interacting with the host operating system.
+ * The 1 byte LLString does not work correctly.
+ * The 2 and 4 byte LLString probably work, so LLWString::_makeASCII
+ * should work.
+ */
+ static void _makeASCII(std::basic_string<T>& string);
+
+ static BOOL read(std::basic_string<T>& string, const char* filename); /*Flawfinder: ignore*/
+ static BOOL write(std::basic_string<T>& string, const char* filename);
+
+ // Conversion to other data types
+ static BOOL convertToBOOL(const std::basic_string<T>& string, BOOL& value);
+ static BOOL convertToU8(const std::basic_string<T>& string, U8& value);
+ static BOOL convertToS8(const std::basic_string<T>& string, S8& value);
+ static BOOL convertToS16(const std::basic_string<T>& string, S16& value);
+ static BOOL convertToU16(const std::basic_string<T>& string, U16& value);
+ static BOOL convertToU32(const std::basic_string<T>& string, U32& value);
+ static BOOL convertToS32(const std::basic_string<T>& string, S32& value);
+ static BOOL convertToF32(const std::basic_string<T>& string, F32& value);
+ static BOOL convertToF64(const std::basic_string<T>& string, F64& value);
+
+ /////////////////////////////////////////////////////////////////////////////////////////
+ // Utility functions for working with char*'s and strings
+
+ // Like strcmp but also handles empty strings. Uses
+ // current locale.
+ static S32 compareStrings(const T* lhs, const T* rhs);
+
+ // case insensitive version of above. Uses current locale on
+ // Win32, and falls back to a non-locale aware comparison on
+ // Linux.
+ static S32 compareInsensitive(const T* lhs, const T* rhs);
+
+ // Case sensitive comparison with good handling of numbers. Does not use current locale.
+ // a.k.a. strdictcmp()
+ static S32 compareDict(const std::basic_string<T>& a, const std::basic_string<T>& b);
+
+ // Puts compareDict() in a form appropriate for LL container classes to use for sorting.
+ static BOOL precedesDict( const std::basic_string<T>& a, const std::basic_string<T>& b );
+
+ // A replacement for strncpy.
+ // If the dst buffer is dst_size bytes long or more, ensures that dst is null terminated and holds
+ // up to dst_size-1 characters of src.
+ static void copy(T* dst, const T* src, size_type dst_size);
+
+ // Copies src into dst at a given offset.
+ static void copyInto(std::basic_string<T>& dst, const std::basic_string<T>& src, size_type offset);
+
+#ifdef _DEBUG
+ static void testHarness();
+#endif
+
+};
+
+template<class T> LLStringBase<T> LLStringBase<T>::null;
+
+typedef LLStringBase<char> LLString;
+typedef LLStringBase<llwchar> LLWString;
+
+struct LLDictionaryLess
+{
+public:
+ bool operator()(const std::string& a, const std::string& b)
+ {
+ return (LLString::precedesDict(a, b) ? true : false);
+ }
+};
+
+
+/**
+ * Simple support functions
+ */
+
+/**
+ * @breif chop off the trailing characters in a string.
+ *
+ * This function works on bytes rather than glyphs, so this will
+ * incorrectly truncate non-single byte strings.
+ * Use utf8str_truncate() for utf8 strings
+ * @return a copy of in string minus the trailing count characters.
+ */
+inline std::string chop_tail_copy(
+ const std::string& in,
+ std::string::size_type count)
+{
+ return std::string(in, 0, in.length() - count);
+}
+
+/**
+ * @brief Return a string constructed from in without crashing if the
+ * pointer is NULL.
+ */
+std::string ll_safe_string(const char* in);
+
+/**
+ * @brief This translates a nybble stored as a hex value from 0-f back
+ * to a nybble in the low order bits of the return byte.
+ */
+U8 hex_as_nybble(char hex);
+
+
+/**
+ * Unicode support
+ */
+
+// Make the incoming string a utf8 string. Replaces any unknown glyph
+// with the UNKOWN_CHARACTER. Once any unknown glph is found, the rest
+// of the data may not be recovered.
+std::string rawstr_to_utf8(const std::string& raw);
+
+//
+// We should never use UTF16 except when communicating with Win32!
+//
+typedef std::basic_string<U16> llutf16string;
+
+LLWString utf16str_to_wstring(const llutf16string &utf16str, S32 len);
+LLWString utf16str_to_wstring(const llutf16string &utf16str);
+
+llutf16string wstring_to_utf16str(const LLWString &utf32str, S32 len);
+llutf16string wstring_to_utf16str(const LLWString &utf32str);
+
+llutf16string utf8str_to_utf16str ( const LLString& utf8str, S32 len);
+llutf16string utf8str_to_utf16str ( const LLString& utf8str );
+
+LLWString utf8str_to_wstring(const std::string &utf8str, S32 len);
+LLWString utf8str_to_wstring(const std::string &utf8str);
+// Same function, better name. JC
+inline LLWString utf8string_to_wstring(const std::string& utf8_string) { return utf8str_to_wstring(utf8_string); }
+
+// Special hack for llfilepicker.cpp:
+S32 utf16chars_to_utf8chars(const U16* inchars, char* outchars, S32* nchars8 = 0);
+S32 utf16chars_to_wchar(const U16* inchars, llwchar* outchar);
+S32 wchar_to_utf8chars(llwchar inchar, char* outchars);
+
+//
+std::string wstring_to_utf8str(const LLWString &utf32str, S32 len);
+std::string wstring_to_utf8str(const LLWString &utf32str);
+
+std::string utf16str_to_utf8str(const llutf16string &utf16str, S32 len);
+std::string utf16str_to_utf8str(const llutf16string &utf16str);
+
+// Length of this UTF32 string in bytes when transformed to UTF8
+S32 wstring_utf8_length(const LLWString& wstr);
+
+// Length in bytes of this wide char in a UTF8 string
+S32 wchar_utf8_length(const llwchar wc);
+
+std::string utf8str_tolower(const std::string& utf8str);
+
+/**
+ * @brief Properly truncate a utf8 string to a maximum byte count.
+ *
+ * The returned string may be less than max_len if the truncation
+ * happens in the middle of a glyph. If max_len is longer than the
+ * string passed in, the return value == utf8str.
+ * @param utf8str A valid utf8 string to truncate.
+ * @param max_len The maximum number of bytes in the returne
+ * @return Returns a valid utf8 string with byte count <= max_len.
+ */
+std::string utf8str_truncate(const std::string& utf8str, const S32 max_len);
+
+std::string utf8str_trim(const std::string& utf8str);
+
+S32 utf8str_compare_insensitive(
+ const std::string& lhs,
+ const std::string& rhs);
+
+/**
+ * @brief Replace all occurences of target_char with replace_char
+ *
+ * @param utf8str A utf8 string to process.
+ * @param target_char The wchar to be replaced
+ * @param replace_char The wchar which is written on replace
+ */
+std::string utf8str_substChar(
+ const std::string& utf8str,
+ const llwchar target_char,
+ const llwchar replace_char);
+
+std::string utf8str_makeASCII(const std::string& utf8str);
+
+// Hack - used for evil notecards.
+std::string mbcsstring_makeASCII(const std::string& str);
+
+template <class T>
+std::ostream& operator<<(std::ostream &s, const LLStringBase<T> &str)
+{
+ s << ((std::basic_string<T>)str);
+ return s;
+}
+
+std::ostream& operator<<(std::ostream &s, const LLWString &wstr);
+
+
+/**
+ * Many of the 'strip' and 'replace' methods of LLStringBase need
+ * specialization to work with the signed char type.
+ * Sadly, it is not possible (AFAIK) to specialize a single method of
+ * a template class.
+ * That stuff should go here.
+ */
+namespace LLStringFn
+{
+ /**
+ * @brief Replace all non-printable characters with replacement in
+ * string.
+ *
+ * @param [in,out] string the to modify. out value is the string
+ * with zero non-printable characters.
+ * @param The replacement character. use LL_UNKNOWN_CHAR if unsure.
+ */
+ void replace_nonprintable(
+ std::basic_string<char>& string,
+ char replacement);
+
+ /**
+ * @brief Replace all non-printable characters with replacement in
+ * a wide string.
+ *
+ * @param [in,out] string the to modify. out value is the string
+ * with zero non-printable characters.
+ * @param The replacement character. use LL_UNKNOWN_CHAR if unsure.
+ */
+ void replace_nonprintable(
+ std::basic_string<llwchar>& string,
+ llwchar replacement);
+
+ /**
+ * @brief Replace all non-printable characters and pipe characters
+ * with replacement in a string.
+ *
+ * @param [in,out] the string to modify. out value is the string
+ * with zero non-printable characters and zero pipe characters.
+ * @param The replacement character. use LL_UNKNOWN_CHAR if unsure.
+ */
+ void replace_nonprintable_and_pipe(std::basic_string<char>& str,
+ char replacement);
+
+ /**
+ * @brief Replace all non-printable characters and pipe characters
+ * with replacement in a wide string.
+ *
+ * @param [in,out] the string to modify. out value is the string
+ * with zero non-printable characters and zero pipe characters.
+ * @param The replacement wide character. use LL_UNKNOWN_CHAR if unsure.
+ */
+ void replace_nonprintable_and_pipe(std::basic_string<llwchar>& str,
+ llwchar replacement);
+}
+
+////////////////////////////////////////////////////////////
+
+// static
+template<class T>
+S32 LLStringBase<T>::format(std::basic_string<T>& s, const format_map_t& fmt_map)
+{
+ typedef typename std::basic_string<T>::size_type string_size_type_t;
+ S32 res = 0;
+ for (format_map_t::const_iterator iter = fmt_map.begin(); iter != fmt_map.end(); ++iter)
+ {
+ U32 fmtlen = iter->first.size();
+ string_size_type_t n = 0;
+ while (1)
+ {
+ n = s.find(iter->first, n);
+ if (n == std::basic_string<T>::npos)
+ {
+ break;
+ }
+ s.erase(n, fmtlen);
+ s.insert(n, iter->second);
+ n += fmtlen;
+ ++res;
+ }
+ }
+ return res;
+}
+
+// static
+template<class T>
+S32 LLStringBase<T>::compareStrings(const T* lhs, const T* rhs)
+{
+ S32 result;
+ if( lhs == rhs )
+ {
+ result = 0;
+ }
+ else
+ if ( !lhs || !lhs[0] )
+ {
+ result = ((!rhs || !rhs[0]) ? 0 : 1);
+ }
+ else
+ if ( !rhs || !rhs[0])
+ {
+ result = -1;
+ }
+ else
+ {
+ result = LLStringOps::collate(lhs, rhs);
+ }
+ return result;
+}
+
+// static
+template<class T>
+S32 LLStringBase<T>::compareInsensitive(const T* lhs, const T* rhs )
+{
+ S32 result;
+ if( lhs == rhs )
+ {
+ result = 0;
+ }
+ else
+ if ( !lhs || !lhs[0] )
+ {
+ result = ((!rhs || !rhs[0]) ? 0 : 1);
+ }
+ else
+ if ( !rhs || !rhs[0] )
+ {
+ result = -1;
+ }
+ else
+ {
+ LLStringBase<T> lhs_string(lhs);
+ LLStringBase<T> rhs_string(rhs);
+ LLStringBase<T>::toUpper(lhs_string);
+ LLStringBase<T>::toUpper(rhs_string);
+ result = LLStringOps::collate(lhs_string.c_str(), rhs_string.c_str());
+ }
+ return result;
+}
+
+
+// Case sensitive comparison with good handling of numbers. Does not use current locale.
+// a.k.a. strdictcmp()
+
+//static
+template<class T>
+S32 LLStringBase<T>::compareDict(const std::basic_string<T>& astr, const std::basic_string<T>& bstr)
+{
+ const T* a = astr.c_str();
+ const T* b = bstr.c_str();
+ T ca, cb;
+ S32 ai, bi, cnt = 0;
+ S32 bias = 0;
+
+ ca = *(a++);
+ cb = *(b++);
+ while( ca && cb ){
+ if( bias==0 ){
+ if( LLStringOps::isUpper(ca) ){ ca = LLStringOps::toLower(ca); bias--; }
+ if( LLStringOps::isUpper(cb) ){ cb = LLStringOps::toLower(cb); bias++; }
+ }else{
+ if( LLStringOps::isUpper(ca) ){ ca = LLStringOps::toLower(ca); }
+ if( LLStringOps::isUpper(cb) ){ cb = LLStringOps::toLower(cb); }
+ }
+ if( LLStringOps::isDigit(ca) ){
+ if( cnt-->0 ){
+ if( cb!=ca ) break;
+ }else{
+ if( !LLStringOps::isDigit(cb) ) break;
+ for(ai=0; LLStringOps::isDigit(a[ai]); ai++);
+ for(bi=0; LLStringOps::isDigit(b[bi]); bi++);
+ if( ai<bi ){ ca=0; break; }
+ if( bi<ai ){ cb=0; break; }
+ if( ca!=cb ) break;
+ cnt = ai;
+ }
+ }else if( ca!=cb ){ break;
+ }
+ ca = *(a++);
+ cb = *(b++);
+ }
+ if( ca==cb ) ca += bias;
+ return ca-cb;
+}
+
+// Puts compareDict() in a form appropriate for LL container classes to use for sorting.
+// static
+template<class T>
+BOOL LLStringBase<T>::precedesDict( const std::basic_string<T>& a, const std::basic_string<T>& b )
+{
+ if( a.size() && b.size() )
+ {
+ return (LLStringBase<T>::compareDict(a.c_str(), b.c_str()) < 0);
+ }
+ else
+ {
+ return (!b.empty());
+ }
+}
+
+// Constructors
+template<class T>
+LLStringBase<T>::LLStringBase(const T* s ) : std::basic_string<T>()
+{
+ if (s) assign(s);
+}
+
+template<class T>
+LLStringBase<T>::LLStringBase(const T* s, size_type n ) : std::basic_string<T>()
+{
+ if (s) assign(s, n);
+}
+
+// Init from a substring
+template<class T>
+LLStringBase<T>::LLStringBase(const T* s, size_type pos, size_type n ) : std::basic_string<T>()
+{
+ if( s )
+ {
+ assign(s + pos, n);
+ }
+ else
+ {
+ assign(LLStringBase<T>::null);
+ }
+}
+
+#if LL_LINUX
+template<class T>
+LLStringBase<T>& LLStringBase<T>::assign(const T* s)
+{
+ if (s)
+ {
+ std::basic_string<T>::assign(s);
+ }
+ else
+ {
+ assign(LLStringBase<T>::null);
+ }
+ return *this;
+}
+
+template<class T>
+LLStringBase<T>& LLStringBase<T>::assign(const T* s, size_type n)
+{
+ if (s)
+ {
+ std::basic_string<T>::assign(s, n);
+ }
+ else
+ {
+ assign(LLStringBase<T>::null);
+ }
+ return *this;
+}
+
+template<class T>
+LLStringBase<T>& LLStringBase<T>::assign(const LLStringBase<T>& s)
+{
+ std::basic_string<T>::assign(s);
+ return *this;
+}
+
+template<class T>
+LLStringBase<T>& LLStringBase<T>::assign(size_type n, const T& c)
+{
+ std::basic_string<T>::assign(n, c);
+ return *this;
+}
+
+template<class T>
+LLStringBase<T>& LLStringBase<T>::assign(const T* a, const T* b)
+{
+ if (a > b)
+ assign(LLStringBase<T>::null);
+ else
+ assign(a, (size_type) (b-a));
+ return *this;
+}
+
+template<class T>
+LLStringBase<T>& LLStringBase<T>::assign(typename LLStringBase<T>::iterator &it1, typename LLStringBase<T>::iterator &it2)
+{
+ assign(LLStringBase<T>::null);
+ while(it1 != it2)
+ *this += *it1++;
+ return *this;
+}
+
+template<class T>
+LLStringBase<T>& LLStringBase<T>::assign(typename LLStringBase<T>::const_iterator &it1, typename LLStringBase<T>::const_iterator &it2)
+{
+ assign(LLStringBase<T>::null);
+ while(it1 != it2)
+ *this += *it1++;
+ return *this;
+}
+#endif
+
+//static
+template<class T>
+void LLStringBase<T>::toUpper(std::basic_string<T>& string)
+{
+ if( !string.empty() )
+ {
+ std::transform(
+ string.begin(),
+ string.end(),
+ string.begin(),
+ (T(*)(T)) &LLStringOps::toUpper);
+ }
+}
+
+//static
+template<class T>
+void LLStringBase<T>::toLower(std::basic_string<T>& string)
+{
+ if( !string.empty() )
+ {
+ std::transform(
+ string.begin(),
+ string.end(),
+ string.begin(),
+ (T(*)(T)) &LLStringOps::toLower);
+ }
+}
+
+//static
+template<class T>
+void LLStringBase<T>::trimHead(std::basic_string<T>& string)
+{
+ if( !string.empty() )
+ {
+ size_type i = 0;
+ while( i < string.length() && LLStringOps::isSpace( string[i] ) )
+ {
+ i++;
+ }
+ string.erase(0, i);
+ }
+}
+
+//static
+template<class T>
+void LLStringBase<T>::trimTail(std::basic_string<T>& string)
+{
+ if( string.size() )
+ {
+ size_type len = string.length();
+ size_type i = len;
+ while( i > 0 && LLStringOps::isSpace( string[i-1] ) )
+ {
+ i--;
+ }
+
+ string.erase( i, len - i );
+ }
+}
+
+
+// Replace line feeds with carriage return-line feed pairs.
+//static
+template<class T>
+void LLStringBase<T>::addCRLF(std::basic_string<T>& string)
+{
+ const T LF = 10;
+ const T CR = 13;
+
+ // Count the number of line feeds
+ size_type count = 0;
+ size_type len = string.size();
+ size_type i;
+ for( i = 0; i < len; i++ )
+ {
+ if( string[i] == LF )
+ {
+ count++;
+ }
+ }
+
+ // Insert a carriage return before each line feed
+ if( count )
+ {
+ size_type size = len + count;
+ T *t = new T[size];
+ size_type j = 0;
+ for( i = 0; i < len; ++i )
+ {
+ if( string[i] == LF )
+ {
+ t[j] = CR;
+ ++j;
+ }
+ t[j] = string[i];
+ ++j;
+ }
+
+ string.assign(t, size);
+ }
+}
+
+// Remove all carriage returns
+//static
+template<class T>
+void LLStringBase<T>::removeCRLF(std::basic_string<T>& string)
+{
+ const T CR = 13;
+
+ size_type cr_count = 0;
+ size_type len = string.size();
+ size_type i;
+ for( i = 0; i < len - cr_count; i++ )
+ {
+ if( string[i+cr_count] == CR )
+ {
+ cr_count++;
+ }
+
+ string[i] = string[i+cr_count];
+ }
+ string.erase(i, cr_count);
+}
+
+//static
+template<class T>
+void LLStringBase<T>::replaceChar( std::basic_string<T>& string, T target, T replacement )
+{
+ size_type found_pos = 0;
+ for (found_pos = string.find(target, found_pos);
+ found_pos != std::basic_string<T>::npos;
+ found_pos = string.find(target, found_pos))
+ {
+ string[found_pos] = replacement;
+ }
+}
+
+//static
+template<class T>
+void LLStringBase<T>::replaceNonstandardASCII( std::basic_string<T>& string, T replacement )
+{
+ const char LF = 10;
+ const S8 MIN = 32;
+// const S8 MAX = 127;
+
+ size_type len = string.size();
+ for( size_type i = 0; i < len; i++ )
+ {
+ // No need to test MAX < mText[i] because we treat mText[i] as a signed char,
+ // which has a max value of 127.
+ if( ( S8(string[i]) < MIN ) && (string[i] != LF) )
+ {
+ string[i] = replacement;
+ }
+ }
+}
+
+//static
+template<class T>
+void LLStringBase<T>::replaceTabsWithSpaces( std::basic_string<T>& string, size_type spaces_per_tab )
+{
+ llassert( spaces_per_tab >= 0 );
+
+ const T TAB = '\t';
+ const T SPACE = ' ';
+
+ LLStringBase<T> out_str;
+ // Replace tabs with spaces
+ for (size_type i = 0; i < string.length(); i++)
+ {
+ if (string[i] == TAB)
+ {
+ for (size_type j = 0; j < spaces_per_tab; j++)
+ out_str += SPACE;
+ }
+ else
+ {
+ out_str += string[i];
+ }
+ }
+ string = out_str;
+}
+
+//static
+template<class T>
+BOOL LLStringBase<T>::containsNonprintable(const std::basic_string<T>& string)
+{
+ const char MIN = 32;
+ BOOL rv = FALSE;
+ for (size_type i = 0; i < string.size(); i++)
+ {
+ if(string[i] < MIN)
+ {
+ rv = TRUE;
+ break;
+ }
+ }
+ return rv;
+}
+
+//static
+template<class T>
+void LLStringBase<T>::stripNonprintable(std::basic_string<T>& string)
+{
+ const char MIN = 32;
+ size_type j = 0;
+ if (string.empty())
+ {
+ return;
+ }
+ char* c_string = new char[string.size() + 1];
+ if(c_string == NULL)
+ {
+ return;
+ }
+ strcpy(c_string, string.c_str()); /*Flawfinder: ignore*/
+ char* write_head = &c_string[0];
+ for (size_type i = 0; i < string.size(); i++)
+ {
+ char* read_head = &string[i];
+ write_head = &c_string[j];
+ if(!(*read_head < MIN))
+ {
+ *write_head = *read_head;
+ ++j;
+ }
+ }
+ c_string[j]= '\0';
+ string = c_string;
+ delete []c_string;
+}
+
+template<class T>
+void LLStringBase<T>::_makeASCII(std::basic_string<T>& string)
+{
+ // Replace non-ASCII chars with LL_UNKNOWN_CHAR
+ for (size_type i = 0; i < string.length(); i++)
+ {
+ if (string[i] > 0x7f)
+ {
+ string[i] = LL_UNKNOWN_CHAR;
+ }
+ }
+}
+
+// static
+template<class T>
+void LLStringBase<T>::copy( T* dst, const T* src, size_type dst_size )
+{
+ if( dst_size > 0 )
+ {
+ size_type min_len = 0;
+ if( src )
+ {
+ min_len = llmin( dst_size - 1, strlen( src ) ); /* Flawfinder: ignore */
+ memcpy(dst, src, min_len * sizeof(T)); /* Flawfinder: ignore */
+ }
+ dst[min_len] = '\0';
+ }
+}
+
+// static
+template<class T>
+void LLStringBase<T>::copyInto(std::basic_string<T>& dst, const std::basic_string<T>& src, size_type offset)
+{
+ llassert( offset <= dst.length() );
+
+ // special case - append to end of string and avoid expensive (when strings are large) string manipulations
+ if ( offset == dst.length() )
+ {
+ dst += src;
+ }
+ else
+ {
+ LLWString tail = dst.substr(offset);
+
+ dst = dst.substr(0, offset);
+ dst += src;
+ dst += tail;
+ };
+}
+
+// True if this is the head of s.
+//static
+template<class T>
+BOOL LLStringBase<T>::isHead( const std::basic_string<T>& string, const T* s )
+{
+ if( string.empty() )
+ {
+ // Early exit
+ return FALSE;
+ }
+ else
+ {
+ return (strncmp( s, string.c_str(), string.size() ) == 0);
+ }
+}
+
+//static
+template<class T>
+BOOL LLStringBase<T>::read(std::basic_string<T>& string, const char* filename) /*Flawfinder: ignore*/
+{
+#ifdef LL_LINUX
+ printf("STUBBED: LLStringBase<T>::read at %s:%d\n", __FILE__, __LINE__);
+#else
+ llifstream ifs(filename, llifstream::binary);
+ if (!ifs.is_open())
+ {
+ llinfos << "Unable to open file" << filename << llendl;
+ return FALSE;
+ }
+
+ std::basic_ostringstream<T> oss;
+
+ oss << ifs.rdbuf();
+
+ string = oss.str();
+
+ ifs.close();
+#endif
+ return TRUE;
+}
+
+//static
+template<class T>
+BOOL LLStringBase<T>::write(std::basic_string<T>& string, const char* filename)
+{
+#ifdef LL_LINUX
+ printf("STUBBED: LLStringBase<T>::write at %s:%d\n", __FILE__, __LINE__);
+#else
+ llofstream ofs(filename, llofstream::binary);
+ if (!ofs.is_open())
+ {
+ llinfos << "Unable to open file" << filename << llendl;
+ return FALSE;
+ }
+
+ ofs << string;
+
+ ofs.close();
+#endif
+ return TRUE;
+}
+
+template<class T>
+BOOL LLStringBase<T>::convertToBOOL(const std::basic_string<T>& string, BOOL& value)
+{
+ if( string.empty() )
+ {
+ return FALSE;
+ }
+
+ LLStringBase<T> temp( string );
+ trim(temp);
+ if(
+ (temp == "1") ||
+ (temp == "T") ||
+ (temp == "t") ||
+ (temp == "TRUE") ||
+ (temp == "true") ||
+ (temp == "True") )
+ {
+ value = TRUE;
+ return TRUE;
+ }
+ else
+ if(
+ (temp == "0") ||
+ (temp == "F") ||
+ (temp == "f") ||
+ (temp == "FALSE") ||
+ (temp == "false") ||
+ (temp == "False") )
+ {
+ value = FALSE;
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+template<class T>
+BOOL LLStringBase<T>::convertToU8(const std::basic_string<T>& string, U8& value)
+{
+ S32 value32 = 0;
+ BOOL success = convertToS32(string, value32);
+ if( success && (U8_MIN <= value32) && (value32 <= U8_MAX) )
+ {
+ value = (U8) value32;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+template<class T>
+BOOL LLStringBase<T>::convertToS8(const std::basic_string<T>& string, S8& value)
+{
+ S32 value32 = 0;
+ BOOL success = convertToS32(string, value32);
+ if( success && (S8_MIN <= value32) && (value32 <= S8_MAX) )
+ {
+ value = (S8) value32;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+template<class T>
+BOOL LLStringBase<T>::convertToS16(const std::basic_string<T>& string, S16& value)
+{
+ S32 value32 = 0;
+ BOOL success = convertToS32(string, value32);
+ if( success && (S16_MIN <= value32) && (value32 <= S16_MAX) )
+ {
+ value = (S16) value32;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+template<class T>
+BOOL LLStringBase<T>::convertToU16(const std::basic_string<T>& string, U16& value)
+{
+ S32 value32 = 0;
+ BOOL success = convertToS32(string, value32);
+ if( success && (U16_MIN <= value32) && (value32 <= U16_MAX) )
+ {
+ value = (U16) value32;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+template<class T>
+BOOL LLStringBase<T>::convertToU32(const std::basic_string<T>& string, U32& value)
+{
+ if( string.empty() )
+ {
+ return FALSE;
+ }
+
+ LLStringBase<T> temp( string );
+ trim(temp);
+ U32 v;
+ std::basic_istringstream<T> i_stream((std::basic_string<T>)temp);
+ if(i_stream >> v)
+ {
+ //TODO: figure out overflow reporting here
+ //if( ULONG_MAX == v )
+ //{
+ // // Underflow or overflow
+ // return FALSE;
+ //}
+
+ value = v;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+template<class T>
+BOOL LLStringBase<T>::convertToS32(const std::basic_string<T>& string, S32& value)
+{
+ if( string.empty() )
+ {
+ return FALSE;
+ }
+
+ LLStringBase<T> temp( string );
+ trim(temp);
+ S32 v;
+ std::basic_istringstream<T> i_stream((std::basic_string<T>)temp);
+ if(i_stream >> v)
+ {
+ //TODO: figure out overflow and underflow reporting here
+ //if((LONG_MAX == v) || (LONG_MIN == v))
+ //{
+ // // Underflow or overflow
+ // return FALSE;
+ //}
+
+ value = v;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+template<class T>
+BOOL LLStringBase<T>::convertToF32(const std::basic_string<T>& string, F32& value)
+{
+ F64 value64 = 0.0;
+ BOOL success = convertToF64(string, value64);
+ if( success && (-F32_MAX <= value64) && (value64 <= F32_MAX) )
+ {
+ value = (F32) value64;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+template<class T>
+BOOL LLStringBase<T>::convertToF64(const std::basic_string<T>& string, F64& value)
+{
+ if( string.empty() )
+ {
+ return FALSE;
+ }
+
+ LLStringBase<T> temp( string );
+ trim(temp);
+ F64 v;
+ std::basic_istringstream<T> i_stream((std::basic_string<T>)temp);
+ if(i_stream >> v)
+ {
+ //TODO: figure out overflow and underflow reporting here
+ //if( ((-HUGE_VAL == v) || (HUGE_VAL == v))) )
+ //{
+ // // Underflow or overflow
+ // return FALSE;
+ //}
+
+ value = v;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+template<class T>
+void LLStringBase<T>::truncate(std::basic_string<T>& string, size_type count)
+{
+ size_type cur_size = string.size();
+ string.resize(count < cur_size ? count : cur_size);
+}
+
+#endif // LL_STRING_H