json: add locale independent parse and format functions

Use them in pw-dump.
Add some unit tests.

See #2223
This commit is contained in:
Wim Taymans 2022-03-20 21:04:33 +01:00
parent 947d15a60a
commit 93b5d440bc
3 changed files with 58 additions and 5 deletions

View file

@ -34,6 +34,7 @@ extern "C" {
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <locale.h>
#include <spa/utils/defs.h>
@ -237,9 +238,16 @@ static inline bool spa_json_is_null(const char *val, int len)
static inline int spa_json_parse_float(const char *val, int len, float *result)
{
char *end;
*result = strtof(val, &end);
static locale_t locale = NULL;
if (SPA_UNLIKELY(locale == NULL))
locale = newlocale(LC_ALL_MASK, "C", NULL);
if (locale != NULL)
*result = strtof_l(val, &end, locale);
else
*result = strtof(val, &end);
return len > 0 && end == val + len;
}
static inline bool spa_json_is_float(const char *val, int len)
{
float dummy;
@ -254,6 +262,16 @@ static inline int spa_json_get_float(struct spa_json *iter, float *res)
return spa_json_parse_float(value, len, res);
}
static inline char *spa_json_format_double(char *str, int size, const double val)
{
int i, l;
l = snprintf(str, size, "%f", val);
for (i = 0; i < l; i++)
if (str[i] == ',')
str[i] = '.';
return str;
}
/* int */
static inline int spa_json_parse_int(const char *val, int len, int *result)
{

View file

@ -278,13 +278,15 @@ static void put_int(struct data *d, const char *key, int64_t val)
static void put_double(struct data *d, const char *key, double val)
{
put_fmt(d, key, "%s%f%s", NUMBER, val, NORMAL);
char buf[128];
put_fmt(d, key, "%s%s%s", NUMBER,
spa_json_format_double(buf, sizeof(buf), val), NORMAL);
}
static void put_value(struct data *d, const char *key, const char *val)
{
int64_t li;
double dv;
float fv;
if (val == NULL)
put_literal(d, key, "null");
@ -292,8 +294,8 @@ static void put_value(struct data *d, const char *key, const char *val)
put_literal(d, key, val);
else if (spa_atoi64(val, &li, 10))
put_int(d, key, li);
else if (spa_atod(val, &dv))
put_double(d, key, dv);
else if (spa_json_parse_float(val, strlen(val), &fv))
put_double(d, key, fv);
else
put_string(d, key, val);
}

View file

@ -22,6 +22,8 @@
* DEALINGS IN THE SOFTWARE.
*/
#include <locale.h>
#include "pwtest.h"
#include <spa/utils/defs.h>
@ -225,8 +227,39 @@ PWTEST(json_overflow)
PWTEST(json_float)
{
struct {
const char *str;
double val;
} val[] = {
{ "0.0", 0.0f },
{ ".0", 0.0f },
{ ".0E0", 0.0E0f },
{ "1.0", 1.0f },
{ "1.011", 1.011f },
{ "176543.123456", 176543.123456f },
{ "-176543.123456", -176543.123456f },
{ "-5678.5432E10", -5678.5432E10f },
{ "-5678.5432e10", -5678.5432e10f },
{ "-5678.5432e-10", -5678.5432e-10f },
{ "5678.5432e+10", 5678.5432e+10f },
{ "00.000100", 00.000100f },
{ "-0.000100", -0.000100f },
};
float v;
unsigned i;
pwtest_int_eq(spa_json_parse_float("", 0, &v), 0);
setlocale(LC_NUMERIC, "C");
for (i = 0; i < SPA_N_ELEMENTS(val); i++) {
pwtest_int_gt(spa_json_parse_float(val[i].str, strlen(val[i].str), &v), 0);
pwtest_double_eq(v, val[i].val);
}
setlocale(LC_NUMERIC, "fr_FR");
for (i = 0; i < SPA_N_ELEMENTS(val); i++) {
pwtest_int_gt(spa_json_parse_float(val[i].str, strlen(val[i].str), &v), 0);
pwtest_double_eq(v, val[i].val);
}
return PWTEST_PASS;
}