Skip to content

Commit d009665

Browse files
authored
Merge pull request #254 from bbbgan/fix_parse_number
2 parents cc352a9 + 7d20997 commit d009665

File tree

8 files changed

+84
-41
lines changed

8 files changed

+84
-41
lines changed

iguana/detail/charconv.h

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,36 @@
33

44
#include "dragonbox_to_chars.h"
55
#include "fast_float.h"
6+
#include "iguana/define.h"
67
#include "itoa.hpp"
78

8-
99
namespace iguana {
1010
template <typename T>
1111
struct is_char_type
1212
: std::disjunction<std::is_same<T, char>, std::is_same<T, wchar_t>,
1313
std::is_same<T, char16_t>, std::is_same<T, char32_t>> {};
1414

1515
namespace detail {
16-
template <typename U>
16+
17+
// check_number==true: check if the string [first, last) is a legal number
18+
template <bool check_number = true, typename U>
1719
std::pair<const char *, std::errc> from_chars(const char *first,
18-
const char *last,
19-
U &value) noexcept {
20+
const char *last, U &value) {
2021
using T = std::decay_t<U>;
2122
if constexpr (std::is_floating_point_v<T>) {
2223
auto [p, ec] = fast_float::from_chars(first, last, value);
24+
if constexpr (check_number) {
25+
if (p != last || ec != std::errc{})
26+
IGUANA_UNLIKELY { throw std::runtime_error("Failed to parse number"); }
27+
}
2328
return {p, ec};
2429
}
2530
else {
2631
auto [p, ec] = std::from_chars(first, last, value);
32+
if constexpr (check_number) {
33+
if (p != last || ec != std::errc{})
34+
IGUANA_UNLIKELY { throw std::runtime_error("Failed to parse number"); }
35+
}
2736
return {p, ec};
2837
}
2938
}

iguana/json_reader.hpp

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,8 @@ IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) {
6868
if (size == 0)
6969
IGUANA_UNLIKELY { throw std::runtime_error("Failed to parse number"); }
7070
const auto start = &*it;
71-
auto [p, ec] = detail::from_chars(start, start + size, value);
72-
if (ec != std::errc{} || *p == '.')
71+
auto [p, ec] = detail::from_chars<false>(start, start + size, value);
72+
if (ec != std::errc{} || !can_follow_number(*p))
7373
IGUANA_UNLIKELY { throw std::runtime_error("Failed to parse number"); }
7474
it += (p - &*it);
7575
}
@@ -82,9 +82,7 @@ IGUANA_INLINE void from_json_impl(U &value, It &&it, It &&end) {
8282
buffer[i] = *it++;
8383
++i;
8484
}
85-
auto [p, ec] = detail::from_chars(buffer, buffer + i, value);
86-
if (ec != std::errc{})
87-
IGUANA_UNLIKELY { throw std::runtime_error("Failed to parse number"); }
85+
detail::from_chars(buffer, buffer + i, value);
8886
}
8987
}
9088

iguana/json_util.hpp

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,7 @@ class numeric_str {
1818
if (val_.empty())
1919
IGUANA_UNLIKELY { throw std::runtime_error("Failed to parse number"); }
2020
T res;
21-
auto [_, ec] =
22-
detail::from_chars(val_.data(), val_.data() + val_.size(), res);
23-
if (ec != std::errc{})
24-
IGUANA_UNLIKELY { throw std::runtime_error("Failed to parse number"); }
21+
detail::from_chars(val_.data(), val_.data() + val_.size(), res);
2522
return res;
2623
}
2724

@@ -214,4 +211,28 @@ IGUANA_INLINE bool is_numeric(char c) noexcept {
214211
return static_cast<bool>(is_num[static_cast<unsigned int>(c)]);
215212
}
216213

214+
// '\t' '\r' '\n' '"' '}' ']' ',' ' ' '\0'
215+
IGUANA_INLINE bool can_follow_number(char c) noexcept {
216+
static constexpr int can_follow_num[256] = {
217+
// 0 1 2 3 4 5 6 7 8 9 A B C D E F
218+
1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, // 0
219+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1
220+
1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, // 2
221+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3
222+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4
223+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, // 5
224+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 6
225+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, // 7
226+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8
227+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9
228+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A
229+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // B
230+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // C
231+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // D
232+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // E
233+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 // F
234+
};
235+
return static_cast<bool>(can_follow_num[static_cast<unsigned int>(c)]);
236+
}
237+
217238
} // namespace iguana

iguana/xml_reader.hpp

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
#include "detail/utf.hpp"
66
#include "xml_util.hpp"
77

8-
98
namespace iguana {
109
namespace detail {
1110

@@ -34,10 +33,7 @@ IGUANA_INLINE void parse_value(U &&value, It &&begin, It &&end) {
3433
else if constexpr (num_v<T>) {
3534
auto size = std::distance(begin, end);
3635
const auto start = &*begin;
37-
auto [p, ec] = detail::from_chars(start, start + size, value);
38-
if (ec != std::errc{})
39-
IGUANA_UNLIKELY
40-
throw std::runtime_error("Failed to parse number");
36+
detail::from_chars(start, start + size, value);
4137
}
4238
else if constexpr (char_v<T>) {
4339
if (static_cast<size_t>(std::distance(begin, end)) != 1)

iguana/yaml_reader.hpp

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
#include "detail/utf.hpp"
77
#include "yaml_util.hpp"
88

9-
109
namespace iguana {
1110

1211
template <typename T, typename It, std::enable_if_t<refletable_v<T>, int> = 0>
@@ -114,10 +113,7 @@ IGUANA_INLINE void parse_value(U &value, It &&value_begin, It &&value_end) {
114113
IGUANA_UNLIKELY { return; }
115114
auto size = std::distance(value_begin, value_end);
116115
const auto start = &*value_begin;
117-
auto [p, ec] = detail::from_chars(start, start + size, value);
118-
if (ec != std::errc{})
119-
IGUANA_UNLIKELY
120-
throw std::runtime_error("Failed to parse number");
116+
detail::from_chars(start, start + size, value);
121117
}
122118

123119
// string_view should be used for string with ' " ?

test/test.cpp

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -180,23 +180,22 @@ struct nest_t {
180180
};
181181
REFLECTION(nest_t, name, value, var, var2);
182182

183-
struct point_t1 {
184-
int x;
185-
int y;
186-
};
187-
REFLECTION(point_t1, x, y);
188-
189-
TEST_CASE("test double to int") {
190-
point_t p{1, 0.45};
191-
std::string s;
192-
iguana::to_json(p, s);
193-
std::cout << s << std::endl;
194-
point_t1 p2;
195-
CHECK_THROWS(iguana::from_json(p2, s));
196-
197-
point_t p3;
198-
iguana::from_json(p3, s);
199-
CHECK(p.y == p3.y);
183+
TEST_CASE("test throw while parsing an illegal number") {
184+
{
185+
std::string str{"[0,1.0]"};
186+
std::vector<int> test{};
187+
CHECK_THROWS(iguana::from_json(test, str.begin(), str.end()));
188+
}
189+
{
190+
std::string str{"1A"};
191+
int test{};
192+
CHECK_THROWS(iguana::from_json(test, str.begin(), str.end()));
193+
}
194+
{
195+
std::string str{"1.0"};
196+
int test{};
197+
CHECK_THROWS(iguana::from_json(test, str.begin(), str.end()));
198+
}
200199
}
201200

202201
TEST_CASE("test variant") {

test/test_xml.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -700,6 +700,31 @@ TEST_CASE("test smart_ptr") {
700700
validator(cont1);
701701
}
702702

703+
TEST_CASE("test throw while parsing an illegal number") {
704+
{
705+
std::string str = R"(
706+
<Contents_t>
707+
<vec>42A</vec>
708+
<vec_s>15</vec_s>
709+
<b>test</b>
710+
</Contents_t>
711+
)";
712+
Contents_t cont;
713+
CHECK_THROWS(iguana::from_xml(cont, str));
714+
}
715+
{
716+
std::string str = R"(
717+
<Contents_t>
718+
<vec>42</vec>
719+
<vec_s>15.7</vec_s>
720+
<b>test</b>
721+
</Contents_t>
722+
)";
723+
Contents_t cont;
724+
CHECK_THROWS(iguana::from_xml(cont, str));
725+
}
726+
}
727+
703728
struct next_obj_t {
704729
int x;
705730
int y;

test/unit_test.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,6 @@ TEST_CASE("test parse item seq container") {
206206
std::array<int, 3> test{};
207207
CHECK_THROWS(iguana::from_json(test, str.begin(), str.end()));
208208
}
209-
210209
{
211210
std::string str{"[0,1,2"};
212211
std::list<int> test{};

0 commit comments

Comments
 (0)