Skip to content

Commit 814caa4

Browse files
committed
Making json locale usage more performance optimal for Windows and Linux.
1 parent fc69c73 commit 814caa4

File tree

5 files changed

+111
-35
lines changed

5 files changed

+111
-35
lines changed

Release/include/cpprest/asyncrt_utils.h

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ namespace pplx = Concurrency;
4141
#include <cstdint>
4242
#include <system_error>
4343
#include <random>
44+
#include <locale.h>
4445

4546
#if !defined(_MS_WINDOWS) || (_MSC_VER >= 1700)
4647
#include <chrono>
@@ -163,21 +164,28 @@ namespace details
163164
/// <summary>
164165
/// Cross platform RAII container for setting thread local locale.
165166
/// </summary>
166-
class scoped_thread_locale
167+
class scoped_c_thread_locale
167168
{
168169
public:
169-
_ASYNCRTIMP scoped_thread_locale(const char * locale);
170-
_ASYNCRTIMP ~scoped_thread_locale();
170+
_ASYNCRTIMP scoped_c_thread_locale();
171+
_ASYNCRTIMP ~scoped_c_thread_locale();
172+
173+
#ifdef _MS_WINDOWS
174+
typedef _locale_t locale_t;
175+
#else
176+
typedef locale_t locale_t;
177+
#endif
178+
179+
static _ASYNCRTIMP locale_t __cdecl c_locale();
171180
private:
172181
#ifdef _MS_WINDOWS
173182
std::string m_prevLocale;
174183
int m_prevThreadSetting;
175184
#else
176-
locale_t m_prevLocale;
177-
locale_t m_newLocale;
185+
locale_t m_prevLocale;
178186
#endif
179-
scoped_thread_locale(const scoped_thread_locale &);
180-
scoped_thread_locale & operator=(const scoped_thread_locale &);
187+
scoped_c_thread_locale(const scoped_c_thread_locale &);
188+
scoped_c_thread_locale & operator=(const scoped_c_thread_locale &);
181189
};
182190

183191
/// <summary>

Release/src/json/json.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -364,8 +364,10 @@ bool web::json::details::_Object::has_field(const utility::string_t &key) const
364364
}
365365

366366
utility::string_t json::value::to_string() const
367-
{
368-
utility::details::scoped_thread_locale locale("C");
367+
{
368+
#ifndef _MS_WINDOWS
369+
utility::details::scoped_c_thread_locale locale;
370+
#endif
369371
return m_value->to_string();
370372
}
371373

Release/src/json/json_parsing.cpp

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,10 @@ class JSON_Parser
127127

128128
web::json::value ParseValue(typename JSON_Parser<CharType>::Token &first)
129129
{
130-
utility::details::scoped_thread_locale locale("C");
130+
#ifndef _MS_WINDOWS
131+
utility::details::scoped_c_thread_locale locale;
132+
#endif
133+
131134
auto _value = _ParseValue(first);
132135
#ifdef ENABLE_JSON_VALUE_VISUALIZER
133136
auto type = _value->type();
@@ -375,12 +378,12 @@ namespace
375378
#ifdef _MS_WINDOWS
376379
static int print_llu(char* ptr, size_t n, uint64_t val64)
377380
{
378-
return _snprintf_s(ptr, n, _TRUNCATE, "%I64u", val64);
381+
return _snprintf_s(ptr, n, _TRUNCATE, "%I64u", val64, utility::details::scoped_c_thread_locale::c_locale());
379382
}
380383

381384
static int print_llu(wchar_t* ptr, size_t n, uint64_t val64)
382385
{
383-
return _snwprintf_s(ptr, n, _TRUNCATE, L"%I64u", val64);
386+
return _snwprintf_s(ptr, n, _TRUNCATE, L"%I64u", val64, utility::details::scoped_c_thread_locale::c_locale());
384387
}
385388
#else
386389
static int print_llu(char* ptr, size_t n, unsigned long long val64)
@@ -393,8 +396,22 @@ namespace
393396
}
394397
#endif
395398

396-
static double anystod(const char* str) { return strtod(str, nullptr); }
397-
static double anystod(const wchar_t* str) { return wcstod(str, nullptr); }
399+
static double anystod(const char* str)
400+
{
401+
#ifdef _MS_WINDOWS
402+
return _strtod_l(str, nullptr, utility::details::scoped_c_thread_locale::c_locale());
403+
#else
404+
return strtod(str, nullptr);
405+
#endif
406+
}
407+
static double anystod(const wchar_t* str)
408+
{
409+
#ifdef _MS_WINDOWS
410+
return _wcstod_l(str, nullptr, utility::details::scoped_c_thread_locale::c_locale());
411+
#else
412+
return wcstod(str, nullptr);
413+
#endif
414+
}
398415
}
399416

400417
template <typename CharType>
@@ -710,7 +727,12 @@ inline bool JSON_Parser<CharType>::handle_unescape_char(Token &token)
710727
for (int i = 0; i < 4; ++i)
711728
{
712729
ch = NextCharacter();
713-
if (!isxdigit((unsigned char) (ch)))
730+
#ifdef _MS_WINDOWS
731+
const int isxdigitResult = _isxdigit_l(static_cast<unsigned char>(ch), utility::details::scoped_c_thread_locale::c_locale());
732+
#else
733+
const int isxdigitResult = isxdigit(static_cast<unsigned char>(ch));
734+
#endif
735+
if (!isxdigitResult)
714736
return false;
715737

716738
int val = _hexval[ch];

Release/src/json/json_serialization.cpp

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@ using namespace utility::conversions;
4141
#ifdef _MS_WINDOWS
4242
void web::json::value::serialize(std::ostream& stream) const
4343
{
44-
utility::details::scoped_thread_locale locale("C");
44+
#ifndef _MS_WINDOWS
45+
utility::details::scoped_c_thread_locale locale;
46+
#endif
4547

4648
// This has better performance than writing directly to stream.
4749
std::string str;
@@ -56,7 +58,9 @@ void web::json::value::format(std::basic_string<wchar_t> &string) const
5658

5759
void web::json::value::serialize(utility::ostream_t &stream) const
5860
{
59-
utility::details::scoped_thread_locale locale("C");
61+
#ifndef _MS_WINDOWS
62+
utility::details::scoped_c_thread_locale locale;
63+
#endif
6064

6165
// This has better performance than writing directly to stream.
6266
utility::string_t str;
@@ -173,7 +177,13 @@ void web::json::details::_Number::format(std::basic_string<char>& stream) const
173177
const size_t tempSize = std::numeric_limits<double>::digits10 + 10;
174178
char tempBuffer[tempSize];
175179
#ifdef _MS_WINDOWS
176-
const auto numChars = sprintf_s(tempBuffer, tempSize, "%.*g", std::numeric_limits<double>::digits10 + 2, m_number.m_value);
180+
const auto numChars = _sprintf_s_l(
181+
tempBuffer,
182+
tempSize,
183+
"%.*g",
184+
utility::details::scoped_c_thread_locale::c_locale(),
185+
std::numeric_limits<double>::digits10 + 2,
186+
m_number.m_value);
177187
#else
178188
const auto numChars = snprintf(tempBuffer, tempSize, "%.*g", std::numeric_limits<double>::digits10 + 2, m_number.m_value);
179189
#endif
@@ -219,7 +229,13 @@ void web::json::details::_Number::format(std::basic_string<wchar_t>& stream) con
219229
// #digits + 2 to avoid loss + 1 for the sign + 1 for decimal point + 5 for exponent (e+xxx) + 1 for null terminator
220230
const size_t tempSize = std::numeric_limits<double>::digits10 + 10;
221231
wchar_t tempBuffer[tempSize];
222-
const int numChars = swprintf_s(tempBuffer, tempSize, L"%.*g", std::numeric_limits<double>::digits10 + 2, m_number.m_value);
232+
const int numChars = _swprintf_s_l(
233+
tempBuffer,
234+
tempSize,
235+
L"%.*g",
236+
utility::details::scoped_c_thread_locale::c_locale(),
237+
std::numeric_limits<double>::digits10 + 2,
238+
m_number.m_value);
223239
stream.append(tempBuffer, numChars);
224240
}
225241
}
@@ -238,6 +254,8 @@ utility::string_t web::json::value::as_string() const
238254

239255
utility::string_t json::value::serialize() const
240256
{
241-
utility::details::scoped_thread_locale locale("C");
257+
#ifndef _MS_WINDOWS
258+
utility::details::scoped_c_thread_locale locale;
259+
#endif
242260
return m_value->to_string();
243261
}

Release/src/utilities/asyncrt_utils.cpp

Lines changed: 41 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,41 @@ namespace utility
5555
namespace details
5656
{
5757

58+
std::once_flag g_c_localeFlag;
59+
std::unique_ptr<scoped_c_thread_locale::locale_t, void(*)(scoped_c_thread_locale::locale_t *)> g_c_locale(nullptr, [](scoped_c_thread_locale::locale_t *){});
60+
scoped_c_thread_locale::locale_t scoped_c_thread_locale::c_locale()
61+
{
62+
std::call_once(g_c_localeFlag, [&]()
63+
{
64+
scoped_c_thread_locale::locale_t *clocale = new scoped_c_thread_locale::locale_t();
5865
#ifdef _MS_WINDOWS
59-
scoped_thread_locale::scoped_thread_locale(const char * locale)
66+
*clocale = _create_locale(LC_ALL, "C");
67+
if (clocale == nullptr)
68+
{
69+
throw std::runtime_error("Unable to create 'C' locale.");
70+
}
71+
auto deleter = [](scoped_c_thread_locale::locale_t *clocale)
72+
{
73+
_free_locale(*clocale);
74+
};
75+
#else
76+
*clocale = newlocale(LC_ALL, "C", nullptr);
77+
if (clocale == nullptr)
78+
{
79+
throw std::runtime_error("Unable to create 'C' locale.");
80+
}
81+
auto deleter = [](scoped_c_thread_locale::locale_t *clocale)
82+
{
83+
freelocale(clocale);
84+
};
85+
#endif
86+
g_c_locale = std::unique_ptr<scoped_c_thread_locale::locale_t, void(*)(scoped_c_thread_locale::locale_t *)>(clocale, deleter);
87+
});
88+
return *g_c_locale;
89+
}
90+
91+
#ifdef _MS_WINDOWS
92+
scoped_c_thread_locale::scoped_c_thread_locale()
6093
: m_prevLocale(), m_prevThreadSetting(-1)
6194
{
6295
char *prevLocale = setlocale(LC_ALL, nullptr);
@@ -65,23 +98,23 @@ scoped_thread_locale::scoped_thread_locale(const char * locale)
6598
throw std::runtime_error("Unable to retrieve current locale.");
6699
}
67100

68-
if(std::strcmp(prevLocale, locale) != 0)
101+
if(std::strcmp(prevLocale, "C") != 0)
69102
{
70103
m_prevLocale = prevLocale;
71104
m_prevThreadSetting = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
72105
if (m_prevThreadSetting == -1)
73106
{
74107
throw std::runtime_error("Unable to enable per thread locale.");
75108
}
76-
if (setlocale(LC_ALL, locale) == nullptr)
109+
if (setlocale(LC_ALL, "C") == nullptr)
77110
{
78111
_configthreadlocale(m_prevThreadSetting);
79112
throw std::runtime_error("Unable to set locale");
80113
}
81114
}
82115
}
83116

84-
scoped_thread_locale::~scoped_thread_locale()
117+
scoped_c_thread_locale::~scoped_c_thread_locale()
85118
{
86119
if(m_prevThreadSetting != -1)
87120
{
@@ -90,7 +123,7 @@ scoped_thread_locale::~scoped_thread_locale()
90123
}
91124
}
92125
#else
93-
scoped_thread_locale::scoped_thread_locale(const char * locale)
126+
scoped_c_thread_locale::scoped_c_thread_locale()
94127
: m_prevLocale(nullptr), m_newLocale(nullptr)
95128
{
96129
char * prevLocale = setlocale(LC_ALL, nullptr);
@@ -99,28 +132,21 @@ scoped_thread_locale::scoped_thread_locale(const char * locale)
99132
throw std::runtime_error("Unable to retrieve current locale.");
100133
}
101134

102-
if(std::strcmp(prevLocale, locale) != 0)
135+
if(std::strcmp(prevLocale, "C") != 0)
103136
{
104-
m_newLocale = newlocale(LC_ALL, locale, nullptr);
105-
if(m_newLocale == nullptr)
106-
{
107-
throw std::runtime_error("Unable to create new locale.");
108-
}
109-
m_prevLocale = uselocale(m_newLocale);
137+
m_prevLocale = uselocale(c_locale());
110138
if(m_prevLocale == nullptr)
111139
{
112-
freelocale(m_newLocale);
113140
throw std::runtime_error("Unable to set locale");
114141
}
115142
}
116143
}
117144

118-
scoped_thread_locale::~scoped_thread_locale()
145+
scoped_c_thread_locale::~scoped_c_thread_locale()
119146
{
120147
if(m_prevLocale != nullptr)
121148
{
122149
uselocale(m_prevLocale);
123-
freelocale(m_newLocale);
124150
}
125151
}
126152
#endif

0 commit comments

Comments
 (0)