Skip to content

Commit

Permalink
Merge pull request #677 from akheron/ditch-localeconv
Browse files Browse the repository at this point in the history
Use sprintf() to determine locale's decimal point
  • Loading branch information
akheron authored Mar 8, 2024
2 parents 9b9b5e8 + 2d1c132 commit 842708a
Show file tree
Hide file tree
Showing 7 changed files with 29 additions and 48 deletions.
11 changes: 11 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
Version 2.14.1
==============

Work in progress

* Fixes:

- Fix thread safety of encoding and decoding when `uselocale` or `newlocale`
is used to switch locales inside the threads (#674, #675, #677. Thanks to
Bruno Haible the report and help with fixing.)

Version 2.14
============

Expand Down
11 changes: 0 additions & 11 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -215,18 +215,7 @@ if (NOT DEFINED JSON_INT_T)
endif ()
endif ()


# If locale.h and localeconv() are available, define to 1, otherwise to 0.
check_include_files (locale.h HAVE_LOCALE_H)
check_function_exists (localeconv HAVE_LOCALECONV)

if (HAVE_LOCALECONV AND HAVE_LOCALE_H)
set (JSON_HAVE_LOCALECONV 1)
else ()
set (JSON_HAVE_LOCALECONV 0)
endif()

# check if we have setlocale
check_function_exists(setlocale HAVE_SETLOCALE)

# Check what the inline keyword is.
Expand Down
4 changes: 0 additions & 4 deletions android/jansson_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,6 @@
otherwise to 0. */
#define JSON_INTEGER_IS_LONG_LONG 1

/* If locale.h and localeconv() are available, define to 1,
otherwise to 0. */
#define JSON_HAVE_LOCALECONV 0

/* Maximum recursion depth for parsing JSON input.
This limits the depth of e.g. array-within-array constructions. */
#define JSON_PARSER_MAX_DEPTH 2048
Expand Down
3 changes: 0 additions & 3 deletions cmake/jansson_config.h.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,6 @@
#define JSON_INTEGER_FORMAT @JSON_INTEGER_FORMAT@


/* If locale.h and localeconv() are available, define to 1, otherwise to 0. */
#define JSON_HAVE_LOCALECONV @JSON_HAVE_LOCALECONV@

/* If __atomic builtins are available they will be used to manage
reference counts of json_t. */
#define JSON_HAVE_ATOMIC_BUILTINS @JSON_HAVE_ATOMIC_BUILTINS@
Expand Down
8 changes: 1 addition & 7 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ esac
AC_SUBST([json_inline])

# Checks for library functions.
AC_CHECK_FUNCS([close getpid gettimeofday localeconv open read sched_yield strtoll])
AC_CHECK_FUNCS([close getpid gettimeofday open read setlocale sched_yield strtoll])

AC_MSG_CHECKING([for gcc __sync builtins])
have_sync_builtins=no
Expand Down Expand Up @@ -74,12 +74,6 @@ case "$ac_cv_type_long_long_int$ac_cv_func_strtoll" in
esac
AC_SUBST([json_have_long_long])

case "$ac_cv_header_locale_h$ac_cv_func_localeconv" in
yesyes) json_have_localeconv=1;;
*) json_have_localeconv=0;;
esac
AC_SUBST([json_have_localeconv])

# Features
AC_ARG_ENABLE([urandom],
[AS_HELP_STRING([--disable-urandom],
Expand Down
4 changes: 0 additions & 4 deletions src/jansson_config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,6 @@
otherwise to 0. */
#define JSON_INTEGER_IS_LONG_LONG @json_have_long_long@

/* If locale.h and localeconv() are available, define to 1,
otherwise to 0. */
#define JSON_HAVE_LOCALECONV @json_have_localeconv@

/* If __atomic builtins are available they will be used to manage
reference counts of json_t. */
#define JSON_HAVE_ATOMIC_BUILTINS @json_have_atomic_builtins@
Expand Down
36 changes: 17 additions & 19 deletions src/strconv.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,57 +11,57 @@
#include <jansson_private_config.h>
#endif

#if JSON_HAVE_LOCALECONV
#include <locale.h>

/*
- This code assumes that the decimal separator is exactly one
character.
- If setlocale() is called by another thread between the call to
localeconv() and the call to sprintf() or strtod(), the result may
be wrong. setlocale() is not thread-safe and should not be used
this way. Multi-threaded programs should use uselocale() instead.
get_decimal_point() and the call to sprintf() or strtod(), the
result may be wrong. setlocale() is not thread-safe and should
not be used this way. Multi-threaded programs should use
uselocale() instead.
*/
static char get_decimal_point() {
char buf[3];
sprintf(buf, "%#.0f", 1.0); // "1." in the current locale
return buf[1];
}

static void to_locale(strbuffer_t *strbuffer) {
const char *point;
char point;
char *pos;

point = localeconv()->decimal_point;
if (*point == '.') {
point = get_decimal_point();
if (point == '.') {
/* No conversion needed */
return;
}

pos = strchr(strbuffer->value, '.');
if (pos)
*pos = *point;
*pos = point;
}

static void from_locale(char *buffer) {
const char *point;
char point;
char *pos;

point = localeconv()->decimal_point;
if (*point == '.') {
point = get_decimal_point();
if (point == '.') {
/* No conversion needed */
return;
}

pos = strchr(buffer, *point);
pos = strchr(buffer, point);
if (pos)
*pos = '.';
}
#endif

int jsonp_strtod(strbuffer_t *strbuffer, double *out) {
double value;
char *end;

#if JSON_HAVE_LOCALECONV
to_locale(strbuffer);
#endif

errno = 0;
value = strtod(strbuffer->value, &end);
Expand Down Expand Up @@ -92,9 +92,7 @@ int jsonp_dtostr(char *buffer, size_t size, double value, int precision) {
if (length >= size)
return -1;

#if JSON_HAVE_LOCALECONV
from_locale(buffer);
#endif

/* Make sure there's a dot or 'e' in the output. Otherwise
a real is converted to an integer when decoding */
Expand Down

0 comments on commit 842708a

Please sign in to comment.