Skip to content

Commit

Permalink
Merge pull request #443 from biojppm/fix/442
Browse files Browse the repository at this point in the history
numbers: fix serialization with `+`, fix emit style heuristics
  • Loading branch information
biojppm committed Jun 21, 2024
2 parents 31d8205 + 8f0d631 commit ea81d4a
Show file tree
Hide file tree
Showing 8 changed files with 340 additions and 13 deletions.
4 changes: 4 additions & 0 deletions changelog/current.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## Fixes

- Fix [#442](https://github.com/biojppm/rapidyaml/issues/442) ([PR#443](https://github.com/biojppm/rapidyaml/pull/443)):
- Ensure leading `+` is accepted when deserializing numbers.
- Ensure numbers are not quoted by fixing the heuristics in `scalar_style_query_plain()` and `scalar_style_choose()`.
- Add quickstart sample for overflow detection (only of integral types).
- Parse engine: cleanup unused macros
2 changes: 1 addition & 1 deletion ext/c4core
28 changes: 28 additions & 0 deletions samples/quickstart.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2488,6 +2488,34 @@ void sample_fundamental_types()
tree["nan" ] >> f; CHECK(std::isnan(f));
tree["nan" ] >> d; CHECK(std::isnan(d));
C4_SUPPRESS_WARNING_GCC_CLANG_POP

// value overflow detection:
// (for integral types only)
{
// we will be detecting errors below, so we use this sample helper
ScopedErrorHandlerExample err = {};
ryml::Tree t(err.callbacks()); // instantiate with the error-detecting callbacks
// create a simple tree with an int value
ryml::parse_in_arena(R"({val: 258})", &t);
// by default, overflow is not detected:
uint8_t valu8 = 0;
int8_t vali8 = 0;
t["val"] >> valu8; CHECK(valu8 == 2); // not 257; it wrapped around
t["val"] >> vali8; CHECK(vali8 == 2); // not 257; it wrapped around
// ...but there are facilities to detect overflow
CHECK(ryml::overflows<uint8_t>(t["val"].val()));
CHECK(ryml::overflows<int8_t>(t["val"].val()));
CHECK( ! ryml::overflows<int16_t>(t["val"].val()));
// and there is a format helper
CHECK(err.check_error_occurs([&]{
auto checku8 = ryml::fmt::overflow_checked(valu8); // need to declare the wrapper type before using it with >>
t["val"] >> checku8; // this will cause an error
}));
CHECK(err.check_error_occurs([&]{
auto checki8 = ryml::fmt::overflow_checked(vali8); // need to declare the wrapper type before using it with >>
t["val"] >> checki8; // this will cause an error
}));
}
}


Expand Down
30 changes: 24 additions & 6 deletions src/c4/yml/node.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1604,31 +1604,49 @@ inline void write(NodeRef *n, T const& v)
n->set_val_serialized(v);
}

namespace detail {
// SFINAE overloads for skipping leading + which cannot be read by the charconv functions
template<class T>
C4_ALWAYS_INLINE auto read_skip_plus(csubstr val, T *v)
-> typename std::enable_if<std::is_arithmetic<T>::value, bool>::type
{
if(val.begins_with('+'))
val = val.sub(1);
return from_chars(val, v);
}
template<class T>
C4_ALWAYS_INLINE auto read_skip_plus(csubstr val, T *v)
-> typename std::enable_if< ! std::is_arithmetic<T>::value, bool>::type
{
return from_chars(val, v);
}
} // namespace detail

/** convert the val of a scalar node to a particular type, by
* forwarding its val to @ref from_chars<T>(). The full string is
* used.
* @return false if the conversion failed */
template<class T>
typename std::enable_if< ! std::is_floating_point<T>::value, bool>::type
inline read(NodeRef const& n, T *v)
inline auto read(NodeRef const& n, T *v)
-> typename std::enable_if< ! std::is_floating_point<T>::value, bool>::type
{
csubstr val = n.val();
if(val.empty())
return false;
return from_chars(val, v);
return detail::read_skip_plus(val, v);
}
/** convert the val of a scalar node to a particular type, by
* forwarding its val to @ref from_chars<T>(). The full string is
* used.
* @return false if the conversion failed */
template<class T>
typename std::enable_if< ! std::is_floating_point<T>::value, bool>::type
inline read(ConstNodeRef const& n, T *v)
inline auto read(ConstNodeRef const& n, T *v)
-> typename std::enable_if< ! std::is_floating_point<T>::value, bool>::type
{
csubstr val = n.val();
if(val.empty())
return false;
return from_chars(val, v);
return detail::read_skip_plus(val, v);
}

/** convert the val of a scalar node to a floating point type, by
Expand Down
4 changes: 4 additions & 0 deletions src/c4/yml/node_type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,10 @@ bool scalar_style_query_plain(csubstr s) noexcept
else if(s.sub(2).is_number())
return true;
}
else if(s.begins_with_any("0123456789.-+") && s.is_number())
{
return true;
}
return s != ':'
&& ( ! s.begins_with_any("-:?*&,'\"{}[]|>%#@`\r")) // @ and ` are reserved characters
&& ( ! s.ends_with_any(":#"))
Expand Down
4 changes: 4 additions & 0 deletions src/c4/yml/tree.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ bool from_chars_float(csubstr buf, T *C4_RESTRICT val)
{
return true;
}
else if(C4_UNLIKELY(buf.begins_with('+')))
{
return from_chars(buf.sub(1), val);
}
else if(C4_UNLIKELY(buf == ".nan" || buf == ".NaN" || buf == ".NAN"))
{
*val = std::numeric_limits<T>::quiet_NaN();
Expand Down
115 changes: 109 additions & 6 deletions test/test_node_type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,29 +129,132 @@ TEST(NodeType, type_str)
#undef teststr
}

TEST(NodeType, scalar_style_choose)
{
EXPECT_EQ(scalar_style_choose(" \n\t"), SCALAR_DQUO);
EXPECT_EQ(scalar_style_choose("01"), SCALAR_PLAIN);
}

TEST(NodeType, scalar_style_choose_json)
{
EXPECT_EQ(scalar_style_json_choose("true"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_json_choose("false"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_json_choose("null"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_json_choose("0.1"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_json_choose("-0.1"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_json_choose("+0.1"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_json_choose(".1"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_json_choose("+.1"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_json_choose("-.1"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_json_choose("01"), SCALAR_DQUO);
EXPECT_EQ(scalar_style_json_choose("foo"), SCALAR_DQUO);
EXPECT_EQ(scalar_style_json_choose("bar"), SCALAR_DQUO);
}

TEST(NodeType, scalar_style_choose)
{
EXPECT_EQ(scalar_style_choose(" \n\t"), SCALAR_DQUO);
EXPECT_EQ(scalar_style_choose("-.inf"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("-.INF"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("-.034"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("-.034x"), SCALAR_SQUO);
EXPECT_EQ(scalar_style_choose("2.35e-10"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("-2.35e-10"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("+2.35e-10"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("0.1"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("-0.1"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("+0.1"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("01"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("0x1"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("0o1"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("0b1"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("0x1"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("0o1"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("0b1"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("-01"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("-0x1"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("-0o1"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("-0b1"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("-0x1"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("-0o1"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("-0b1"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("+01"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("+0x1"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("+0o1"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("+0b1"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("+0x1"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("+0o1"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("+0b1"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("01"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("0X1"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("0O1"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("0B1"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("0X1"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("0O1"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("0B1"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("-01"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("-0X1"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("-0O1"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("-0B1"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("-0X1"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("-0O1"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("-0B1"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("+01"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("+0X1"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("+0O1"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("+0B1"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("+0X1"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("+0O1"), SCALAR_PLAIN);
EXPECT_EQ(scalar_style_choose("+0B1"), SCALAR_PLAIN);
}

TEST(NodeType, scalar_style_query_plain)
{
EXPECT_TRUE(scalar_style_query_plain("-.inf"));
EXPECT_TRUE(scalar_style_query_plain("-.INF"));
EXPECT_TRUE(scalar_style_query_plain("-.034"));
EXPECT_FALSE(scalar_style_query_plain("-.034x"));
EXPECT_TRUE(scalar_style_query_plain("2.35e-10"));
EXPECT_TRUE(scalar_style_query_plain("-2.35e-10"));
EXPECT_TRUE(scalar_style_query_plain("+2.35e-10"));
EXPECT_TRUE(scalar_style_query_plain("0.1"));
EXPECT_TRUE(scalar_style_query_plain("-0.1"));
EXPECT_TRUE(scalar_style_query_plain("+0.1"));
EXPECT_TRUE(scalar_style_query_plain("01"));
EXPECT_TRUE(scalar_style_query_plain("0x1"));
EXPECT_TRUE(scalar_style_query_plain("0o1"));
EXPECT_TRUE(scalar_style_query_plain("0b1"));
EXPECT_TRUE(scalar_style_query_plain("0x1"));
EXPECT_TRUE(scalar_style_query_plain("0o1"));
EXPECT_TRUE(scalar_style_query_plain("0b1"));
EXPECT_TRUE(scalar_style_query_plain("+01"));
EXPECT_TRUE(scalar_style_query_plain("+0x1"));
EXPECT_TRUE(scalar_style_query_plain("+0o1"));
EXPECT_TRUE(scalar_style_query_plain("+0b1"));
EXPECT_TRUE(scalar_style_query_plain("+0x1"));
EXPECT_TRUE(scalar_style_query_plain("+0o1"));
EXPECT_TRUE(scalar_style_query_plain("+0b1"));
EXPECT_TRUE(scalar_style_query_plain("-01"));
EXPECT_TRUE(scalar_style_query_plain("-0x1"));
EXPECT_TRUE(scalar_style_query_plain("-0o1"));
EXPECT_TRUE(scalar_style_query_plain("-0b1"));
EXPECT_TRUE(scalar_style_query_plain("-0x1"));
EXPECT_TRUE(scalar_style_query_plain("-0o1"));
EXPECT_TRUE(scalar_style_query_plain("-0b1"));
EXPECT_TRUE(scalar_style_query_plain("0X1"));
EXPECT_TRUE(scalar_style_query_plain("0O1"));
EXPECT_TRUE(scalar_style_query_plain("0B1"));
EXPECT_TRUE(scalar_style_query_plain("0X1"));
EXPECT_TRUE(scalar_style_query_plain("0O1"));
EXPECT_TRUE(scalar_style_query_plain("0B1"));
EXPECT_TRUE(scalar_style_query_plain("+01"));
EXPECT_TRUE(scalar_style_query_plain("+0X1"));
EXPECT_TRUE(scalar_style_query_plain("+0O1"));
EXPECT_TRUE(scalar_style_query_plain("+0B1"));
EXPECT_TRUE(scalar_style_query_plain("+0X1"));
EXPECT_TRUE(scalar_style_query_plain("+0O1"));
EXPECT_TRUE(scalar_style_query_plain("+0B1"));
EXPECT_TRUE(scalar_style_query_plain("-01"));
EXPECT_TRUE(scalar_style_query_plain("-0X1"));
EXPECT_TRUE(scalar_style_query_plain("-0O1"));
EXPECT_TRUE(scalar_style_query_plain("-0B1"));
EXPECT_TRUE(scalar_style_query_plain("-0X1"));
EXPECT_TRUE(scalar_style_query_plain("-0O1"));
EXPECT_TRUE(scalar_style_query_plain("-0B1"));
}


Expand Down
Loading

0 comments on commit ea81d4a

Please sign in to comment.