diff options
Diffstat (limited to 'indra/llcommon')
-rw-r--r-- | indra/llcommon/llstring.cpp | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/indra/llcommon/llstring.cpp b/indra/llcommon/llstring.cpp index 614075498c..23b12bb98d 100644 --- a/indra/llcommon/llstring.cpp +++ b/indra/llcommon/llstring.cpp @@ -31,10 +31,168 @@ #include "llfasttimer.h" #include "llsd.h" #include <vector> +#include <sstream> #if LL_WINDOWS #include "llwin32headerslean.h" #include <winnls.h> // for WideCharToMultiByte + +// From https://stackoverflow.com/a/48716488: +// how to work around MSVC's broken implementation of std::basic_ostream<CHAR> +// (get C2941 otherwise) +// The problem is that the MSVC implementation doesn't generalize for +// arbitrary CHAR. +namespace std +{ + +// The std::numpunct<_Elem> template on which this specialization is based was +// copied 2024-09-11 from xlocnum header with versions: +// FRAMEWORK40VERSION="v4.0" +// FRAMEWORKVERSION="v4.0.30319" +// FRAMEWORKVERSION64="v4.0.30319" +// UCRTVERSION="10.0.22621.0" +// VCTOOLSVERSION="14.40.33807" +// VISUALSTUDIOVERSION="17.0" +// WINDOWSSDKLIBVERSION="10.0.22621.0" +// WINDOWSSDKVERSION="10.0.22621.0" +// Not sure which of the above versions tracks changes to xlocnum. +template <> +class numpunct<llwchar> : public locale::facet // facet for defining numeric punctuation text +{ +private: + friend _Tidy_guard<numpunct>; + +public: + using string_type = basic_string<llwchar, char_traits<llwchar>, allocator<llwchar>>; + using char_type = llwchar; + + static locale::id id; // unique facet id + + llwchar decimal_point() const { + return do_decimal_point(); + } + + llwchar thousands_sep() const { + return do_thousands_sep(); + } + + string grouping() const { + return do_grouping(); + } + + string_type falsename() const { + return do_falsename(); + } + + string_type truename() const { + return do_truename(); + } + + explicit numpunct(size_t _Refs = 0) : locale::facet(_Refs) { // construct from current locale + _BEGIN_LOCINFO(_Lobj) + _Init(_Lobj); + if (_Kseparator == 0) { + _Kseparator = // NB: differs from "C" locale + _Maklocchr(',', static_cast<llwchar*>(nullptr), _Lobj._Getcvt()); + } + _END_LOCINFO() + } + + numpunct(const _Locinfo& _Lobj, size_t _Refs = 0, bool _Isdef = false) : locale::facet(_Refs) { + _Init(_Lobj, _Isdef); + } + + static size_t _Getcat(const locale::facet** _Ppf = nullptr, const locale* _Ploc = nullptr) { + // return locale category mask and construct standard facet + if (_Ppf && !*_Ppf) { + *_Ppf = new numpunct<llwchar>(_Locinfo(_Ploc->_C_str()), 0, true); + } + return _X_NUMERIC; + } + +protected: + __CLR_OR_THIS_CALL ~numpunct() noexcept override { + _Tidy(); + } + + numpunct(const char* _Locname, size_t _Refs = 0, bool _Isdef = false) : locale::facet(_Refs) { + _BEGIN_LOCINFO(_Lobj(_Locname)) + _Init(_Lobj, _Isdef); + _END_LOCINFO() + } + + template <class _Elem2> + void _Getvals(_Elem2, const lconv* _Ptr, _Locinfo::_Cvtvec _Cvt) { // get values + _Dp = _Maklocchr(_Ptr->decimal_point[0], static_cast<_Elem2*>(nullptr), _Cvt); + _Kseparator = _Maklocchr(_Ptr->thousands_sep[0], static_cast<_Elem2*>(nullptr), _Cvt); + } + + void _Getvals(wchar_t, const lconv* _Ptr, _Locinfo::_Cvtvec) { // get values + _Dp = static_cast<llwchar>(_Ptr->_W_decimal_point[0]); + _Kseparator = static_cast<llwchar>(_Ptr->_W_thousands_sep[0]); + } + + void _Init(const _Locinfo& _Lobj, bool _Isdef = false) { // initialize from _Lobj + const lconv* _Ptr = _Lobj._Getlconv(); + _Locinfo::_Cvtvec _Cvt = _Lobj._Getcvt(); // conversion information + + _Grouping = nullptr; + _Falsename = nullptr; + _Truename = nullptr; + + _Tidy_guard<numpunct> _Guard{this}; + _Grouping = _Maklocstr(_Isdef ? "" : _Ptr->grouping, static_cast<char*>(nullptr), _Lobj._Getcvt()); + _Falsename = _Maklocstr(_Lobj._Getfalse(), static_cast<llwchar*>(nullptr), _Cvt); + _Truename = _Maklocstr(_Lobj._Gettrue(), static_cast<llwchar*>(nullptr), _Cvt); + _Guard._Target = nullptr; + + if (_Isdef) { // apply defaults for required facets + // _Grouping = _Maklocstr("", static_cast<char *>(nullptr), _Cvt); + _Dp = _Maklocchr('.', static_cast<llwchar*>(nullptr), _Cvt); + _Kseparator = _Maklocchr(',', static_cast<llwchar*>(nullptr), _Cvt); + } else { + _Getvals(llwchar{}, _Ptr, _Cvt); + } + } + + virtual llwchar __CLR_OR_THIS_CALL do_decimal_point() const { + return _Dp; + } + + virtual llwchar __CLR_OR_THIS_CALL do_thousands_sep() const { + return _Kseparator; + } + + virtual string __CLR_OR_THIS_CALL do_grouping() const { + return string{_Grouping}; + } + + virtual string_type __CLR_OR_THIS_CALL do_falsename() const { + return string_type{_Falsename}; + } + + virtual string_type __CLR_OR_THIS_CALL do_truename() const { + return string_type{_Truename}; + } + +private: + void _Tidy() noexcept { // free all storage + _CSTD free(const_cast<char*>(_Grouping)); + _CSTD free(const_cast<llwchar*>(_Falsename)); + _CSTD free(const_cast<llwchar*>(_Truename)); + } + + const char* _Grouping; // grouping string, "" for "C" locale + llwchar _Dp; // decimal point, '.' for "C" locale + llwchar _Kseparator; // thousands separator, '\0' for "C" locale + const llwchar* _Falsename; // name for false, "false" for "C" locale + const llwchar* _Truename; // name for true, "true" for "C" locale +}; + +locale::id numpunct<llwchar>::id; + +} // namespace std + #endif std::string ll_safe_string(const char* in) |