Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
J
json
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Chen Yisong
json
Commits
6408402a
Unverified
Commit
6408402a
authored
Feb 16, 2017
by
Niels Lohmann
Browse files
Options
Browse Files
Download
Plain Diff
🔀
merge #378 (for #362 and #454)
parents
057b1e60
94906107
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
346 additions
and
81 deletions
+346
-81
json.hpp
src/json.hpp
+159
-36
json.hpp.re2c
src/json.hpp.re2c
+159
-36
unit-regression.cpp
test/src/unit-regression.cpp
+28
-9
No files found.
src/json.hpp
View file @
6408402a
...
@@ -2646,14 +2646,6 @@ class basic_json
...
@@ -2646,14 +2646,6 @@ class basic_json
string_t
dump
(
const
int
indent
=
-
1
)
const
string_t
dump
(
const
int
indent
=
-
1
)
const
{
{
std
::
stringstream
ss
;
std
::
stringstream
ss
;
// fix locale problems
ss
.
imbue
(
std
::
locale
::
classic
());
// 6, 15 or 16 digits of precision allows round-trip IEEE 754
// string->float->string, string->double->string or string->long
// double->string; to be safe, we read this value from
// std::numeric_limits<number_float_t>::digits10
ss
.
precision
(
std
::
numeric_limits
<
double
>::
digits10
);
if
(
indent
>=
0
)
if
(
indent
>=
0
)
{
{
...
@@ -6214,10 +6206,6 @@ class basic_json
...
@@ -6214,10 +6206,6 @@ class basic_json
`std::setw(4)` on @a o sets the indentation level to `4` and the
`std::setw(4)` on @a o sets the indentation level to `4` and the
serialization result is the same as calling `dump(4)`.
serialization result is the same as calling `dump(4)`.
@note During serialization, the locale and the precision of the output
stream @a o are changed. The original values are restored when the
function returns.
@param[in,out] o stream to serialize to
@param[in,out] o stream to serialize to
@param[in] j JSON value to serialize
@param[in] j JSON value to serialize
...
@@ -6239,22 +6227,9 @@ class basic_json
...
@@ -6239,22 +6227,9 @@ class basic_json
// reset width to 0 for subsequent calls to this stream
// reset width to 0 for subsequent calls to this stream
o
.
width
(
0
);
o
.
width
(
0
);
// fix locale problems
const
auto
old_locale
=
o
.
imbue
(
std
::
locale
::
classic
());
// set precision
// 6, 15 or 16 digits of precision allows round-trip IEEE 754
// string->float->string, string->double->string or string->long
// double->string; to be safe, we read this value from
// std::numeric_limits<number_float_t>::digits10
const
auto
old_precision
=
o
.
precision
(
std
::
numeric_limits
<
double
>::
digits10
);
// do the actual serialization
// do the actual serialization
j
.
dump
(
o
,
pretty_print
,
static_cast
<
unsigned
int
>
(
indentation
));
j
.
dump
(
o
,
pretty_print
,
static_cast
<
unsigned
int
>
(
indentation
));
// reset locale and precision
o
.
imbue
(
old_locale
);
o
.
precision
(
old_precision
);
return
o
;
return
o
;
}
}
...
@@ -8244,6 +8219,162 @@ class basic_json
...
@@ -8244,6 +8219,162 @@ class basic_json
return
result
;
return
result
;
}
}
/*!
@brief locale-independent serialization for built-in arithmetic types
*/
struct
numtostr
{
public
:
template
<
typename
T
>
numtostr
(
T
value
)
{
x_write
(
value
,
std
::
is_integral
<
T
>
());
}
operator
const
char
*
()
const
{
return
m_buf
.
data
();
}
const
char
*
c_str
()
const
{
return
m_buf
.
data
();
}
private
:
static
constexpr
size_t
s_capacity
=
30
;
std
::
array
<
char
,
s_capacity
+
2
>
m_buf
{{}};
// +2 for leading '-'
// and trailing '\0'
template
<
typename
T
>
void
x_write
(
T
x
,
std
::
true_type
)
{
static_assert
(
std
::
numeric_limits
<
T
>::
digits10
<=
s_capacity
,
""
);
const
bool
is_neg
=
x
<
0
;
size_t
i
=
0
;
while
(
x
and
i
<
s_capacity
)
{
const
auto
digit
=
std
::
labs
(
static_cast
<
long
>
(
x
%
10
));
m_buf
[
i
++
]
=
static_cast
<
char
>
(
'0'
+
digit
);
x
/=
10
;
}
assert
(
i
<
s_capacity
);
if
(
i
==
0
)
{
m_buf
[
i
++
]
=
'0'
;
}
if
(
is_neg
)
{
m_buf
[
i
++
]
=
'-'
;
}
std
::
reverse
(
m_buf
.
begin
(),
m_buf
.
begin
()
+
i
);
}
template
<
typename
T
>
void
x_write
(
T
x
,
std
::
false_type
)
{
if
(
x
==
0
)
{
std
::
strcpy
(
m_buf
.
data
(),
std
::
signbit
(
x
)
?
"-0.0"
:
"0.0"
);
return
;
}
static
constexpr
auto
d
=
std
::
numeric_limits
<
number_float_t
>::
digits10
;
static_assert
(
d
==
6
or
d
==
15
or
d
==
16
or
d
==
17
,
""
);
static
constexpr
auto
fmt
=
d
==
6
?
"%.7g"
:
d
==
15
?
"%.16g"
:
d
==
16
?
"%.17g"
:
d
==
17
?
"%.18g"
:
"%.19g"
;
// I'm not sure why we need to +1 the precision,
// but without it there's a unit-test that fails
// that asserts precision of the output
snprintf
(
m_buf
.
data
(),
m_buf
.
size
(),
fmt
,
x
);
#if 0
// C locales and C++ locales are similar but
// different.
//
// If working with C++ streams we'd've used
// these, but for C formatting functions we
// have to use C locales (setlocale / localeconv),
// rather than C++ locales (std::locale installed
// by std::locale::global()).
const std::locale loc;
const char thousands_sep =
std::use_facet< std::numpunct<char>>(
loc).thousands_sep();
const char decimal_point =
std::use_facet< std::numpunct<char>>(
loc).decimal_point();
#else
const
auto
loc
=
localeconv
();
assert
(
loc
!=
nullptr
);
const
char
thousands_sep
=
!
loc
->
thousands_sep
?
'\0'
:
loc
->
thousands_sep
[
0
];
const
char
decimal_point
=
!
loc
->
decimal_point
?
'\0'
:
loc
->
decimal_point
[
0
];
#endif
// erase thousands separator
if
(
thousands_sep
)
{
auto
end
=
std
::
remove
(
m_buf
.
begin
(),
m_buf
.
end
(),
thousands_sep
);
std
::
fill
(
end
,
m_buf
.
end
(),
'\0'
);
}
// convert decimal point to '.'
if
(
decimal_point
and
decimal_point
!=
'.'
)
{
for
(
auto
&
c
:
m_buf
)
{
if
(
c
==
decimal_point
)
{
c
=
'.'
;
break
;
}
}
}
// determine if need to apperd ".0"
auto
data_end
=
m_buf
.
begin
()
+
strlen
(
m_buf
.
data
());
const
bool
value_is_int_like
=
std
::
find_if
(
m_buf
.
begin
(),
data_end
,
[](
const
char
c
)
{
return
c
==
'.'
or
c
==
'e'
or
c
==
'E'
;
})
==
data_end
;
assert
(
data_end
+
2
<
m_buf
.
end
());
if
(
value_is_int_like
)
{
strcat
(
m_buf
.
data
(),
".0"
);
}
}
};
/*!
/*!
@brief internal implementation of the serialization function
@brief internal implementation of the serialization function
...
@@ -8363,27 +8494,19 @@ class basic_json
...
@@ -8363,27 +8494,19 @@ class basic_json
case
value_t
:
:
number_integer
:
case
value_t
:
:
number_integer
:
{
{
o
<<
m_value
.
number_integer
;
o
<<
numtostr
(
m_value
.
number_integer
).
c_str
()
;
return
;
return
;
}
}
case
value_t
:
:
number_unsigned
:
case
value_t
:
:
number_unsigned
:
{
{
o
<<
m_value
.
number_unsigned
;
o
<<
numtostr
(
m_value
.
number_unsigned
).
c_str
()
;
return
;
return
;
}
}
case
value_t
:
:
number_float
:
case
value_t
:
:
number_float
:
{
{
if
(
m_value
.
number_float
==
0
)
o
<<
numtostr
(
m_value
.
number_float
).
c_str
();
{
// special case for zero to get "0.0"/"-0.0"
o
<<
(
std
::
signbit
(
m_value
.
number_float
)
?
"-0.0"
:
"0.0"
);
}
else
{
o
<<
m_value
.
number_float
;
}
return
;
return
;
}
}
...
...
src/json.hpp.re2c
View file @
6408402a
...
@@ -2646,14 +2646,6 @@ class basic_json
...
@@ -2646,14 +2646,6 @@ class basic_json
string_t dump(const int indent = -1) const
string_t dump(const int indent = -1) const
{
{
std::stringstream ss;
std::stringstream ss;
// fix locale problems
ss.imbue(std::locale::classic());
// 6, 15 or 16 digits of precision allows round-trip IEEE 754
// string->float->string, string->double->string or string->long
// double->string; to be safe, we read this value from
// std::numeric_limits<number_float_t>::digits10
ss.precision(std::numeric_limits<double>::digits10);
if (indent >= 0)
if (indent >= 0)
{
{
...
@@ -6214,10 +6206,6 @@ class basic_json
...
@@ -6214,10 +6206,6 @@ class basic_json
`std::setw(4)` on @a o sets the indentation level to `4` and the
`std::setw(4)` on @a o sets the indentation level to `4` and the
serialization result is the same as calling `dump(4)`.
serialization result is the same as calling `dump(4)`.
@note During serialization, the locale and the precision of the output
stream @a o are changed. The original values are restored when the
function returns.
@param[in,out] o stream to serialize to
@param[in,out] o stream to serialize to
@param[in] j JSON value to serialize
@param[in] j JSON value to serialize
...
@@ -6239,22 +6227,9 @@ class basic_json
...
@@ -6239,22 +6227,9 @@ class basic_json
// reset width to 0 for subsequent calls to this stream
// reset width to 0 for subsequent calls to this stream
o.width(0);
o.width(0);
// fix locale problems
const auto old_locale = o.imbue(std::locale::classic());
// set precision
// 6, 15 or 16 digits of precision allows round-trip IEEE 754
// string->float->string, string->double->string or string->long
// double->string; to be safe, we read this value from
// std::numeric_limits<number_float_t>::digits10
const auto old_precision = o.precision(std::numeric_limits<double>::digits10);
// do the actual serialization
// do the actual serialization
j.dump(o, pretty_print, static_cast<unsigned int>(indentation));
j.dump(o, pretty_print, static_cast<unsigned int>(indentation));
// reset locale and precision
o.imbue(old_locale);
o.precision(old_precision);
return o;
return o;
}
}
...
@@ -8244,6 +8219,162 @@ class basic_json
...
@@ -8244,6 +8219,162 @@ class basic_json
return result;
return result;
}
}
/*!
@brief locale-independent serialization for built-in arithmetic types
*/
struct numtostr
{
public:
template<typename T>
numtostr(T value)
{
x_write(value, std::is_integral<T>());
}
operator const char* () const
{
return m_buf.data();
}
const char* c_str() const
{
return m_buf.data();
}
private:
static constexpr size_t s_capacity = 30;
std::array < char, s_capacity + 2 > m_buf{{}}; // +2 for leading '-'
// and trailing '\0'
template<typename T>
void x_write(T x, std::true_type)
{
static_assert(std::numeric_limits<T>::digits10 <= s_capacity, "");
const bool is_neg = x < 0;
size_t i = 0;
while (x and i < s_capacity)
{
const auto digit = std::labs(static_cast<long>(x % 10));
m_buf[i++] = static_cast<char>('0' + digit);
x /= 10;
}
assert(i < s_capacity);
if (i == 0)
{
m_buf[i++] = '0';
}
if (is_neg)
{
m_buf[i++] = '-';
}
std::reverse(m_buf.begin(), m_buf.begin() + i);
}
template<typename T>
void x_write(T x, std::false_type)
{
if (x == 0)
{
std::strcpy(m_buf.data(),
std::signbit(x) ? "-0.0" : "0.0");
return;
}
static constexpr auto d =
std::numeric_limits<number_float_t>::digits10;
static_assert(d == 6 or d == 15 or d == 16 or d == 17, "");
static constexpr auto fmt = d == 6 ? "%.7g"
: d == 15 ? "%.16g"
: d == 16 ? "%.17g"
: d == 17 ? "%.18g"
: "%.19g";
// I'm not sure why we need to +1 the precision,
// but without it there's a unit-test that fails
// that asserts precision of the output
snprintf(m_buf.data(), m_buf.size(), fmt, x);
#if 0
// C locales and C++ locales are similar but
// different.
//
// If working with C++ streams we'd've used
// these, but for C formatting functions we
// have to use C locales (setlocale / localeconv),
// rather than C++ locales (std::locale installed
// by std::locale::global()).
const std::locale loc;
const char thousands_sep =
std::use_facet< std::numpunct<char>>(
loc).thousands_sep();
const char decimal_point =
std::use_facet< std::numpunct<char>>(
loc).decimal_point();
#else
const auto loc = localeconv();
assert(loc != nullptr);
const char thousands_sep = !loc->thousands_sep ? '\0'
: loc->thousands_sep[0];
const char decimal_point = !loc->decimal_point ? '\0'
: loc->decimal_point[0];
#endif
// erase thousands separator
if (thousands_sep)
{
auto end = std::remove(m_buf.begin(),
m_buf.end(),
thousands_sep);
std::fill(end, m_buf.end(), '\0');
}
// convert decimal point to '.'
if (decimal_point and decimal_point != '.')
{
for (auto& c : m_buf)
{
if (c == decimal_point)
{
c = '.';
break;
}
}
}
// determine if need to apperd ".0"
auto data_end = m_buf.begin() + strlen(m_buf.data());
const bool value_is_int_like =
std::find_if(m_buf.begin(), data_end,
[](const char c)
{
return c == '.'
or c == 'e'
or c == 'E';
})
== data_end;
assert(data_end + 2 < m_buf.end());
if (value_is_int_like)
{
strcat(m_buf.data(), ".0");
}
}
};
/*!
/*!
@brief internal implementation of the serialization function
@brief internal implementation of the serialization function
...
@@ -8363,27 +8494,19 @@ class basic_json
...
@@ -8363,27 +8494,19 @@ class basic_json
case value_t::number_integer:
case value_t::number_integer:
{
{
o <<
m_value.number_integer
;
o <<
numtostr(m_value.number_integer).c_str()
;
return;
return;
}
}
case value_t::number_unsigned:
case value_t::number_unsigned:
{
{
o <<
m_value.number_unsigned
;
o <<
numtostr(m_value.number_unsigned).c_str()
;
return;
return;
}
}
case value_t::number_float:
case value_t::number_float:
{
{
if (m_value.number_float == 0)
o << numtostr(m_value.number_float).c_str();
{
// special case for zero to get "0.0"/"-0.0"
o << (std::signbit(m_value.number_float) ? "-0.0" : "0.0");
}
else
{
o << m_value.number_float;
}
return;
return;
}
}
...
...
test/src/unit-regression.cpp
View file @
6408402a
...
@@ -402,15 +402,35 @@ TEST_CASE("regression tests")
...
@@ -402,15 +402,35 @@ TEST_CASE("regression tests")
//issue #230
//issue #230
//CHECK(j2b.dump() == "23.42");
//CHECK(j2b.dump() == "23.42");
CHECK
(
j3a
.
dump
()
==
"10000"
);
CHECK
(
j3a
.
dump
()
==
"10000
.0
"
);
CHECK
(
j3b
.
dump
()
==
"10000"
);
CHECK
(
j3b
.
dump
()
==
"10000
.0
"
);
CHECK
(
j3c
.
dump
()
==
"10000"
);
CHECK
(
j3c
.
dump
()
==
"10000
.0
"
);
//CHECK(j3b.dump() == "1E04"); // roundtrip error
//CHECK(j3b.dump() == "1E04"); // roundtrip error
//CHECK(j3c.dump() == "1e04"); // roundtrip error
//CHECK(j3c.dump() == "1e04"); // roundtrip error
std
::
locale
::
global
(
orig_locale
);
std
::
locale
::
global
(
orig_locale
);
}
}
SECTION
(
"issue #378 - locale-independent num-to-str"
)
{
setlocale
(
LC_NUMERIC
,
"de_DE.UTF-8"
);
// Verify that snprintf uses special decimal and grouping characters.
// Disabled, because can't trigger locale-specific behavior in AppVeyor
#ifndef _MSC_VER
{
std
::
array
<
char
,
64
>
buf
;
std
::
snprintf
(
buf
.
data
(),
buf
.
size
(),
"%.2f"
,
12345.67
);
CHECK
(
strcmp
(
buf
.
data
(),
"12345,67"
)
==
0
);
}
#endif
// verify that dumped correctly with '.' and no grouping
const
json
j1
=
12345.67
;
CHECK
(
json
(
12345.67
).
dump
()
==
"12345.67"
);
setlocale
(
LC_NUMERIC
,
"C"
);
}
SECTION
(
"issue #379 - locale-independent str-to-num"
)
SECTION
(
"issue #379 - locale-independent str-to-num"
)
{
{
setlocale
(
LC_NUMERIC
,
"de_DE.UTF-8"
);
setlocale
(
LC_NUMERIC
,
"de_DE.UTF-8"
);
...
@@ -434,7 +454,6 @@ TEST_CASE("regression tests")
...
@@ -434,7 +454,6 @@ TEST_CASE("regression tests")
CHECK
(
json
::
parse
(
"1.000000000000000000000000000000000000000000000000000000000000000000000000"
).
get
<
double
>
()
==
1.0
);
CHECK
(
json
::
parse
(
"1.000000000000000000000000000000000000000000000000000000000000000000000000"
).
get
<
double
>
()
==
1.0
);
}
}
SECTION
(
"issue #233 - Can't use basic_json::iterator as a base iterator for std::move_iterator"
)
SECTION
(
"issue #233 - Can't use basic_json::iterator as a base iterator for std::move_iterator"
)
{
{
json
source
=
{
"a"
,
"b"
,
"c"
};
json
source
=
{
"a"
,
"b"
,
"c"
};
...
@@ -749,9 +768,9 @@ TEST_CASE("regression tests")
...
@@ -749,9 +768,9 @@ TEST_CASE("regression tests")
CHECK_THROWS_AS
(
json
::
parse
(
vec
),
std
::
invalid_argument
);
CHECK_THROWS_AS
(
json
::
parse
(
vec
),
std
::
invalid_argument
);
}
}
//
SECTION("issue #454 - doubles are printed as integers")
SECTION
(
"issue #454 - doubles are printed as integers"
)
//
{
{
//
json j = R"({"bool_value":true,"double_value":2.0,"int_value":10,"level1":{"list_value":[3,"hi",false],"tmp":5.0},"string_value":"hello"})"_json;
json
j
=
R"({"bool_value":true,"double_value":2.0,"int_value":10,"level1":{"list_value":[3,"hi",false],"tmp":5.0},"string_value":"hello"})"
_json
;
// CHECK(j["double_value"].is_number_integer
());
CHECK
(
j
[
"double_value"
].
is_number_float
());
//
}
}
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment