Skip to content

Commit fd8c1d8

Browse files
committed
Remove use of global locale in JSON parsing and serialization.
1 parent 3542f07 commit fd8c1d8

File tree

6 files changed

+38
-214
lines changed

6 files changed

+38
-214
lines changed

Release/include/cpprest/asyncrt_utils.h

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -230,35 +230,6 @@ namespace conversions
230230

231231
namespace details
232232
{
233-
/// <summary>
234-
/// Cross platform RAII container for setting thread local locale.
235-
/// </summary>
236-
class scoped_c_thread_locale
237-
{
238-
public:
239-
_ASYNCRTIMP scoped_c_thread_locale();
240-
_ASYNCRTIMP ~scoped_c_thread_locale();
241-
242-
#if !defined(ANDROID) && !defined(__ANDROID__) // CodePlex 269
243-
#ifdef _WIN32
244-
typedef _locale_t xplat_locale;
245-
#else
246-
typedef locale_t xplat_locale;
247-
#endif
248-
249-
static _ASYNCRTIMP xplat_locale __cdecl c_locale();
250-
#endif
251-
private:
252-
#ifdef _WIN32
253-
std::string m_prevLocale;
254-
int m_prevThreadSetting;
255-
#elif !(defined(ANDROID) || defined(__ANDROID__))
256-
locale_t m_prevLocale;
257-
#endif
258-
scoped_c_thread_locale(const scoped_c_thread_locale &);
259-
scoped_c_thread_locale & operator=(const scoped_c_thread_locale &);
260-
};
261-
262233
/// <summary>
263234
/// Our own implementation of alpha numeric instead of std::isalnum to avoid
264235
/// taking global lock for performance reasons.

Release/include/cpprest/json.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -562,9 +562,6 @@ namespace json
562562
/// <param name="stream">The stream that the JSON string representation should be written to.</param>
563563
inline void serialize(std::ostream& stream) const
564564
{
565-
#ifndef _WIN32
566-
utility::details::scoped_c_thread_locale locale;
567-
#endif
568565
stream << serialize();
569566
}
570567
#if !defined(_LIBCPP_VERSION)

Release/src/json/json_parsing.cpp

Lines changed: 30 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,6 @@ class JSON_Parser
126126

127127
web::json::value ParseValue(Token &first)
128128
{
129-
#ifndef _WIN32
130-
utility::details::scoped_c_thread_locale locale;
131-
#endif
132-
133129
return ParseValue_inner(first);
134130
}
135131

@@ -447,30 +443,6 @@ inline bool JSON_Parser::ParseInt64(char first, uint64_t& value)
447443
return true;
448444
}
449445

450-
#if defined(_WIN32)
451-
int print_llu(char* ptr, size_t n, uint64_t val64)
452-
{
453-
return _snprintf_s_l(ptr, n, _TRUNCATE, "%I64u", utility::details::scoped_c_thread_locale::c_locale(), val64);
454-
}
455-
double anystod(const char* str)
456-
{
457-
return _strtod_l(str, nullptr, utility::details::scoped_c_thread_locale::c_locale());
458-
}
459-
#else
460-
int __attribute__((__unused__)) print_llu(char* ptr, size_t n, unsigned long long val64)
461-
{
462-
return snprintf(ptr, n, "%llu", val64);
463-
}
464-
int __attribute__((__unused__)) print_llu(char* ptr, size_t n, unsigned long val64)
465-
{
466-
return snprintf(ptr, n, "%lu", val64);
467-
}
468-
double __attribute__((__unused__)) anystod(const char* str)
469-
{
470-
return strtod(str, nullptr);
471-
}
472-
#endif
473-
474446
bool JSON_Parser::CompleteNumberLiteral(char first, Token &token)
475447
{
476448
bool minus_sign;
@@ -528,13 +500,22 @@ bool JSON_Parser::CompleteNumberLiteral(char first, Token &token)
528500
return true;
529501
}
530502

531-
// Magic number 5 leaves room for decimal point, null terminator, etc (in most cases)
532-
::std::vector<char> buf(::std::numeric_limits<uint64_t>::digits10 + 5);
533-
int count = print_llu(buf.data(), buf.size(), val64);
503+
// digits10 is the number of digits _from text_ that are guaranteed to round-trip.
504+
// We must +1 to get the the amount of text needed to round trip any number
505+
// Also, +1 for null terminator
506+
std::array<char, ::std::numeric_limits<uint64_t>::digits10 + 2> longbuf;
507+
508+
#ifdef WIN32
509+
int count = _snprintf_s(longbuf.data(), longbuf.size(), _TRUNCATE, "%I64u", val64);
510+
#else
511+
int count = std::snprintf(longbuf.data(), longbuf.size(), "%llu", val64);
512+
#endif
534513
_ASSERTE(count >= 0);
535-
_ASSERTE((size_t)count < buf.size());
536-
// Resize to cut off the null terminator
537-
buf.resize(count);
514+
_ASSERTE((size_t)count < longbuf.size());
515+
516+
::std::stringstream buf;
517+
buf.imbue(std::locale::classic());
518+
buf.write(longbuf.data(), count);
538519

539520
bool decimal = false;
540521

@@ -543,7 +524,7 @@ bool JSON_Parser::CompleteNumberLiteral(char first, Token &token)
543524
// Digit encountered?
544525
if (ch >= '0' && ch <= '9')
545526
{
546-
buf.push_back(static_cast<char>(ch));
527+
buf.put(static_cast<char>(ch));
547528
NextCharacter();
548529
ch = PeekCharacter();
549530
}
@@ -555,7 +536,7 @@ bool JSON_Parser::CompleteNumberLiteral(char first, Token &token)
555536
return false;
556537

557538
decimal = true;
558-
buf.push_back(static_cast<char>(ch));
539+
buf.put('.');
559540

560541
NextCharacter();
561542
ch = PeekCharacter();
@@ -564,36 +545,30 @@ bool JSON_Parser::CompleteNumberLiteral(char first, Token &token)
564545
if (ch < '0' || ch > '9')
565546
return false;
566547

567-
buf.push_back(static_cast<char>(ch));
548+
buf.put(static_cast<char>(ch));
568549
NextCharacter();
569550
ch = PeekCharacter();
570551
}
571552

572553
// Exponent?
573554
else if (ch == 'E' || ch == 'e')
574555
{
575-
buf.push_back(static_cast<char>(ch));
556+
buf.put(static_cast<char>(ch));
576557
NextCharacter();
577558
ch = PeekCharacter();
578559

579560
// Check for the exponent sign
580-
if (ch == '+')
561+
if (ch == '+' || ch == '-')
581562
{
582-
buf.push_back(static_cast<char>(ch));
583-
NextCharacter();
584-
ch = PeekCharacter();
585-
}
586-
else if (ch == '-')
587-
{
588-
buf.push_back(static_cast<char>(ch));
563+
buf.put(static_cast<char>(ch));
589564
NextCharacter();
590565
ch = PeekCharacter();
591566
}
592567

593568
// First number of the exponent
594569
if (ch >= '0' && ch <= '9')
595570
{
596-
buf.push_back(static_cast<char>(ch));
571+
buf.put(static_cast<char>(ch));
597572
NextCharacter();
598573
ch = PeekCharacter();
599574
}
@@ -602,7 +577,7 @@ bool JSON_Parser::CompleteNumberLiteral(char first, Token &token)
602577
// The rest of the exponent
603578
while (ch >= '0' && ch <= '9')
604579
{
605-
buf.push_back(static_cast<char>(ch));
580+
buf.put(static_cast<char>(ch));
606581
NextCharacter();
607582
ch = PeekCharacter();
608583
}
@@ -616,9 +591,8 @@ bool JSON_Parser::CompleteNumberLiteral(char first, Token &token)
616591
break;
617592
}
618593
};
594+
buf >> token.double_val;
619595

620-
buf.push_back('\0');
621-
token.double_val = anystod(buf.data());
622596
if (minus_sign)
623597
{
624598
token.double_val = -token.double_val;
@@ -789,11 +763,12 @@ inline bool JSON_Parser::handle_unescape_char(Token &token)
789763
ec = json_error::malformed_string_literal;
790764
return 0;
791765
}
792-
#ifdef _WIN32
793-
const int isxdigitResult = _isxdigit_l(ch_int, utility::details::scoped_c_thread_locale::c_locale());
794-
#else
795-
const int isxdigitResult = isxdigit(ch_int);
796-
#endif
766+
767+
const bool isxdigitResult =
768+
(ch_int >= '0' && ch_int <= '9')
769+
|| (ch_int >= 'a' && ch_int <= 'f')
770+
|| (ch_int >= 'A' && ch_int <= 'F');
771+
797772
if (!isxdigitResult)
798773
{
799774
ec = json_error::malformed_string_literal;

Release/src/json/json_serialization.cpp

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
#include "stdafx.h"
2727
#include <stdio.h>
28+
#include <iomanip>
2829

2930
#ifndef _WIN32
3031
#define __STDC_FORMAT_MACROS
@@ -145,21 +146,12 @@ void web::json::details::_Number::serialize_impl(std::string& stream) const
145146
}
146147
else
147148
{
148-
// #digits + 2 to avoid loss + 1 for the sign + 1 for decimal point + 5 for exponent (e+xxx) + 1 for null terminator
149-
const size_t tempSize = std::numeric_limits<double>::digits10 + 10;
150-
char tempBuffer[tempSize];
151-
#ifdef _WIN32
152-
const auto numChars = _sprintf_s_l(
153-
tempBuffer,
154-
tempSize,
155-
"%.*g",
156-
utility::details::scoped_c_thread_locale::c_locale(),
157-
std::numeric_limits<double>::digits10 + 2,
158-
m_number.m_value);
159-
#else
160-
const auto numChars = snprintf(tempBuffer, tempSize, "%.*g", std::numeric_limits<double>::digits10 + 2, m_number.m_value);
161-
#endif
162-
stream.append(tempBuffer, numChars);
149+
std::stringstream sbuf;
150+
sbuf.imbue(std::locale::classic());
151+
sbuf.precision(std::numeric_limits<double>::max_digits10);
152+
sbuf << m_number.m_value;
153+
154+
stream.append(sbuf.str());
163155
}
164156
}
165157

@@ -172,10 +164,6 @@ const std::string& web::json::value::as_string() const
172164

173165
std::string json::value::serialize() const
174166
{
175-
#ifndef _WIN32
176-
utility::details::scoped_c_thread_locale locale;
177-
#endif
178-
179167
std::string ret;
180168
ret.reserve(serialize_size());
181169
serialize(ret);

Release/src/utilities/asyncrt_utils.cpp

Lines changed: 0 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -53,113 +53,6 @@ namespace utility
5353
namespace details
5454
{
5555

56-
#if !defined(ANDROID) && !defined(__ANDROID__)
57-
std::once_flag g_c_localeFlag;
58-
std::unique_ptr<scoped_c_thread_locale::xplat_locale, void(*)(scoped_c_thread_locale::xplat_locale *)> g_c_locale(nullptr, [](scoped_c_thread_locale::xplat_locale *){});
59-
scoped_c_thread_locale::xplat_locale scoped_c_thread_locale::c_locale()
60-
{
61-
std::call_once(g_c_localeFlag, [&]()
62-
{
63-
scoped_c_thread_locale::xplat_locale *clocale = new scoped_c_thread_locale::xplat_locale();
64-
#ifdef _WIN32
65-
*clocale = _create_locale(LC_ALL, "C");
66-
if (*clocale == nullptr)
67-
{
68-
throw std::runtime_error("Unable to create 'C' locale.");
69-
}
70-
auto deleter = [](scoped_c_thread_locale::xplat_locale *clocale)
71-
{
72-
_free_locale(*clocale);
73-
delete 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::xplat_locale *clocale)
82-
{
83-
freelocale(*clocale);
84-
delete clocale;
85-
};
86-
#endif
87-
g_c_locale = std::unique_ptr<scoped_c_thread_locale::xplat_locale, void(*)(scoped_c_thread_locale::xplat_locale *)>(clocale, deleter);
88-
});
89-
return *g_c_locale;
90-
}
91-
#endif
92-
93-
#ifdef _WIN32
94-
scoped_c_thread_locale::scoped_c_thread_locale()
95-
: m_prevLocale(), m_prevThreadSetting(-1)
96-
{
97-
char *prevLocale = setlocale(LC_ALL, nullptr);
98-
if (prevLocale == nullptr)
99-
{
100-
throw std::runtime_error("Unable to retrieve current locale.");
101-
}
102-
103-
if (std::strcmp(prevLocale, "C") != 0)
104-
{
105-
m_prevLocale = prevLocale;
106-
m_prevThreadSetting = _configthreadlocale(_ENABLE_PER_THREAD_LOCALE);
107-
if (m_prevThreadSetting == -1)
108-
{
109-
throw std::runtime_error("Unable to enable per thread locale.");
110-
}
111-
if (setlocale(LC_ALL, "C") == nullptr)
112-
{
113-
_configthreadlocale(m_prevThreadSetting);
114-
throw std::runtime_error("Unable to set locale");
115-
}
116-
}
117-
}
118-
119-
scoped_c_thread_locale::~scoped_c_thread_locale()
120-
{
121-
if (m_prevThreadSetting != -1)
122-
{
123-
setlocale(LC_ALL, m_prevLocale.c_str());
124-
_configthreadlocale(m_prevThreadSetting);
125-
}
126-
}
127-
#elif (defined(ANDROID) || defined(__ANDROID__))
128-
scoped_c_thread_locale::scoped_c_thread_locale() {}
129-
scoped_c_thread_locale::~scoped_c_thread_locale() {}
130-
#else
131-
scoped_c_thread_locale::scoped_c_thread_locale()
132-
: m_prevLocale(nullptr)
133-
{
134-
char *prevLocale = setlocale(LC_ALL, nullptr);
135-
if (prevLocale == nullptr)
136-
{
137-
throw std::runtime_error("Unable to retrieve current locale.");
138-
}
139-
140-
if (std::strcmp(prevLocale, "C") != 0)
141-
{
142-
m_prevLocale = uselocale(c_locale());
143-
if (m_prevLocale == nullptr)
144-
{
145-
throw std::runtime_error("Unable to set locale");
146-
}
147-
}
148-
}
149-
150-
scoped_c_thread_locale::~scoped_c_thread_locale()
151-
{
152-
if (m_prevLocale != nullptr)
153-
{
154-
uselocale(m_prevLocale);
155-
}
156-
}
157-
#endif
158-
}
159-
160-
namespace details
161-
{
162-
16356
const std::error_category & __cdecl platform_category()
16457
{
16558
#ifdef _WIN32

Release/tests/functional/json/parsing_tests.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -664,7 +664,7 @@ TEST(non_default_locale, "Ignore:Android", "Locale unsupported on Android")
664664
if(setlocale(LC_ALL, changedLocale.c_str()) != nullptr)
665665
{
666666
// string serialize
667-
std::string str("[true,false,-1.55,5,null,{\"abc\":5555}]");
667+
std::string str("[true,false,-1.125,5,null,{\"abc\":5555}]");
668668
json::value v = json::value::parse(str);
669669
VERIFY_ARE_EQUAL(changedLocale, setlocale(LC_ALL, nullptr));
670670
VERIFY_ARE_EQUAL(str, v.serialize());

0 commit comments

Comments
 (0)