3rdparty: Update rapidyaml to v0.9.0

This commit is contained in:
JordanTheToaster
2025-06-29 06:24:22 +01:00
committed by Ty
parent a9526b7cc1
commit 377930d004
54 changed files with 16621 additions and 9402 deletions

View File

@@ -34,15 +34,26 @@ add_library(pcsx2-rapidyaml
include/c4/yml/detail/stack.hpp
include/c4/yml/emit.def.hpp
include/c4/yml/emit.hpp
include/c4/yml/event_handler_stack.hpp
include/c4/yml/event_handler_tree.hpp
include/c4/yml/filter_processor.hpp
include/c4/yml/fwd.hpp
include/c4/yml/export.hpp
include/c4/yml/node.hpp
include/c4/yml/node_type.hpp
include/c4/yml/parse.hpp
include/c4/yml/parse_engine.def.hpp
include/c4/yml/parse_engine.hpp
include/c4/yml/parser_state.hpp
include/c4/yml/reference_resolver.hpp
include/c4/yml/preprocess.hpp
include/c4/yml/std/map.hpp
include/c4/yml/std/std.hpp
include/c4/yml/std/string.hpp
include/c4/yml/std/vector.hpp
include/c4/yml/tag.hpp
include/c4/yml/tree.hpp
include/c4/yml/version.hpp
include/c4/yml/writer.hpp
include/c4/yml/yml.hpp
include/ryml.hpp
@@ -58,6 +69,10 @@ add_library(pcsx2-rapidyaml
src/c4/yml/parse.cpp
src/c4/yml/preprocess.cpp
src/c4/yml/tree.cpp
src/c4/yml/node_type.cpp
src/c4/yml/reference_resolver.cpp
src/c4/yml/tag.cpp
src/c4/yml/version.cpp
)
target_include_directories(pcsx2-rapidyaml PRIVATE

View File

@@ -18,6 +18,8 @@ template<class T> struct is_blob_type<blob_<T>> : std::integral_constant<bool, t
template<class T> struct is_blob_value_type : std::integral_constant<bool, (std::is_fundamental<T>::value || std::is_trivially_copyable<T>::value)> {};
} // namespace
// NOLINTBEGIN(cppcoreguidelines-special-member-functions,hicpp-special-member-functions)
template<class T>
struct blob_
{
@@ -37,23 +39,25 @@ public:
C4_ALWAYS_INLINE blob_& operator=(blob_ && that) noexcept = default;
C4_ALWAYS_INLINE blob_& operator=(blob_ const& that) noexcept = default;
template<class U, class=typename std::enable_if<std::is_const<T>::value && std::is_same<typename std::add_const<U>::type, T>::value, U>::type> C4_ALWAYS_INLINE blob_(blob_<U> const& that) noexcept : buf(that.buf), len(that.len) {}
template<class U, class=typename std::enable_if<std::is_const<T>::value && std::is_same<typename std::add_const<U>::type, T>::value, U>::type> C4_ALWAYS_INLINE blob_(blob_<U> && that) noexcept : buf(that.buf), len(that.len) {}
template<class U, class=typename std::enable_if<std::is_const<T>::value && std::is_same<typename std::add_const<U>::type, T>::value, U>::type> C4_ALWAYS_INLINE blob_& operator=(blob_<U> && that) noexcept { buf = that.buf; len = that.len; }
template<class U, class=typename std::enable_if<std::is_const<T>::value && std::is_same<typename std::add_const<U>::type, T>::value, U>::type> C4_ALWAYS_INLINE blob_& operator=(blob_<U> const& that) noexcept { buf = that.buf; len = that.len; }
template<class U, class=typename std::enable_if<std::is_const<T>::value && std::is_same<typename std::add_const<U>::type, T>::value, U>::type> C4_ALWAYS_INLINE blob_(blob_<U> const& that) noexcept : buf(that.buf), len(that.len) {} // NOLINT
template<class U, class=typename std::enable_if<std::is_const<T>::value && std::is_same<typename std::add_const<U>::type, T>::value, U>::type> C4_ALWAYS_INLINE blob_(blob_<U> && that) noexcept : buf(that.buf), len(that.len) {} // NOLINT
template<class U, class=typename std::enable_if<std::is_const<T>::value && std::is_same<typename std::add_const<U>::type, T>::value, U>::type> C4_ALWAYS_INLINE blob_& operator=(blob_<U> && that) noexcept { buf = that.buf; len = that.len; } // NOLINT
template<class U, class=typename std::enable_if<std::is_const<T>::value && std::is_same<typename std::add_const<U>::type, T>::value, U>::type> C4_ALWAYS_INLINE blob_& operator=(blob_<U> const& that) noexcept { buf = that.buf; len = that.len; } // NOLINT
C4_ALWAYS_INLINE blob_(void *ptr, size_t n) noexcept : buf(reinterpret_cast<T*>(ptr)), len(n) {}
C4_ALWAYS_INLINE blob_(void const *ptr, size_t n) noexcept : buf(reinterpret_cast<T*>(ptr)), len(n) {}
C4_ALWAYS_INLINE blob_(void *ptr, size_t n) noexcept : buf(reinterpret_cast<T*>(ptr)), len(n) {} // NOLINT
C4_ALWAYS_INLINE blob_(void const *ptr, size_t n) noexcept : buf(reinterpret_cast<T*>(ptr)), len(n) {} // NOLINT
#define _C4_REQUIRE_BLOBTYPE(ty) class=typename std::enable_if<((!detail::is_blob_type<ty>::value) && (detail::is_blob_value_type<ty>::value)), T>::type
template<class U, _C4_REQUIRE_BLOBTYPE(U)> C4_ALWAYS_INLINE blob_(U &var) noexcept : buf(reinterpret_cast<T*>(&var)), len(sizeof(U)) {}
template<class U, _C4_REQUIRE_BLOBTYPE(U)> C4_ALWAYS_INLINE blob_(U *ptr, size_t n) noexcept : buf(reinterpret_cast<T*>(ptr)), len(sizeof(U) * n) { C4_ASSERT(is_aligned(ptr)); }
template<class U, _C4_REQUIRE_BLOBTYPE(U)> C4_ALWAYS_INLINE blob_& operator= (U &var) noexcept { buf = reinterpret_cast<T*>(&var); len = sizeof(U); return *this; }
template<class U, size_t N, _C4_REQUIRE_BLOBTYPE(U)> C4_ALWAYS_INLINE blob_(U (&arr)[N]) noexcept : buf(reinterpret_cast<T*>(arr)), len(sizeof(U) * N) {}
template<class U, size_t N, _C4_REQUIRE_BLOBTYPE(U)> C4_ALWAYS_INLINE blob_& operator= (U (&arr)[N]) noexcept { buf = reinterpret_cast<T*>(arr); len = sizeof(U) * N; return *this; }
template<class U, _C4_REQUIRE_BLOBTYPE(U)> C4_ALWAYS_INLINE blob_(U &var) noexcept : buf(reinterpret_cast<T*>(&var)), len(sizeof(U)) {} // NOLINT
template<class U, _C4_REQUIRE_BLOBTYPE(U)> C4_ALWAYS_INLINE blob_(U *ptr, size_t n) noexcept : buf(reinterpret_cast<T*>(ptr)), len(sizeof(U) * n) { C4_ASSERT(is_aligned(ptr)); } // NOLINT
template<class U, _C4_REQUIRE_BLOBTYPE(U)> C4_ALWAYS_INLINE blob_& operator= (U &var) noexcept { buf = reinterpret_cast<T*>(&var); len = sizeof(U); return *this; } // NOLINT
template<class U, size_t N, _C4_REQUIRE_BLOBTYPE(U)> C4_ALWAYS_INLINE blob_(U (&arr)[N]) noexcept : buf(reinterpret_cast<T*>(arr)), len(sizeof(U) * N) {} // NOLINT
template<class U, size_t N, _C4_REQUIRE_BLOBTYPE(U)> C4_ALWAYS_INLINE blob_& operator= (U (&arr)[N]) noexcept { buf = reinterpret_cast<T*>(arr); len = sizeof(U) * N; return *this; } // NOLINT
#undef _C4_REQUIRE_BLOBTYPE
};
// NOLINTEND(cppcoreguidelines-special-member-functions,hicpp-special-member-functions)
/** an immutable binary blob */
using cblob = blob_<cbyte>;
/** a mutable binary blob */

View File

@@ -55,14 +55,7 @@
# define C4CORE_HAVE_FAST_FLOAT 1
# endif
# if C4CORE_HAVE_FAST_FLOAT
C4_SUPPRESS_WARNING_GCC_WITH_PUSH("-Wsign-conversion")
C4_SUPPRESS_WARNING_GCC("-Warray-bounds")
# if defined(__GNUC__) && __GNUC__ >= 5
C4_SUPPRESS_WARNING_GCC("-Wshift-count-overflow")
# endif
//# include "c4/ext/fast_float.hpp"
#include "fast_float/fast_float.h"
C4_SUPPRESS_WARNING_GCC_POP
# endif
#elif (C4_CPP >= 17)
# define C4CORE_HAVE_FAST_FLOAT 0
@@ -102,15 +95,13 @@
#endif
#if defined(_MSC_VER)
#if defined(_MSC_VER) && !defined(__clang__)
# pragma warning(push)
# pragma warning(disable: 4996) // snprintf/scanf: this function or variable may be unsafe
# if C4_MSVC_VERSION != C4_MSVC_VERSION_2017
# pragma warning(disable: 4800) //'int': forcing value to bool 'true' or 'false' (performance warning)
# endif
#endif
#if defined(__clang__)
#elif defined(__clang__)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare"
# pragma clang diagnostic ignored "-Wformat-nonliteral"
@@ -136,6 +127,7 @@
#define C4_NO_UBSAN_IOVRFLW
#endif
// NOLINTBEGIN(hicpp-signed-bitwise)
namespace c4 {
@@ -144,11 +136,11 @@ namespace c4 {
* Lightweight, very fast generic type-safe wrappers for converting
* individual values to/from strings. These are the main generic
* functions:
* - @ref doc_to_chars and its alias @ref doc_xtoa: implemented by calling @ref itoa()/@ref utoa()/@ref ftoa()/@ref dtoa() (or generically @ref xtoa())
* - @ref doc_from_chars and its alias @ref doc_atox: implemented by calling @ref atoi()/@ref atou()/@ref atof()/@ref atod() (or generically @ref atox())
* - @ref doc_to_chars and its alias @ref xtoa(): implemented by calling @ref itoa() / @ref utoa() / @ref ftoa() / @ref dtoa() (or generically @ref xtoa())
* - @ref doc_from_chars and its alias @ref atox(): implemented by calling @ref atoi() / @ref atou() / @ref atof() / @ref atod() (or generically @ref atox())
* - @ref to_chars_sub()
* - @ref from_chars_first()
* - @ref xtoa()/@ref atox() are implemented in terms @ref write_dec()/@ref read_dec() et al (see @ref doc_write/@ref doc_read())
* - @ref xtoa() and @ref atox() are implemented in terms of @ref write_dec() / @ref read_dec() et al (see @ref doc_write / @ref doc_read())
*
* And also some modest brag is in order: these functions are really
* fast: faster even than C++17 `std::to_chars()` and
@@ -241,7 +233,7 @@ struct is_fixed_length
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
#ifdef _MSC_VER
#if defined(_MSC_VER) && !defined(__clang__)
# pragma warning(push)
#elif defined(__clang__)
# pragma clang diagnostic push
@@ -377,7 +369,7 @@ template<> struct charconv_digits_<4u, false> // uint32_t
static constexpr csubstr max_value_dec() noexcept { return csubstr("4294967295"); }
static constexpr bool is_oct_overflow(csubstr str) noexcept { return !((str.len < 11) || (str.len == 11 && str[0] <= '3')); }
};
template<> struct charconv_digits_<8u, true> // int32_t
template<> struct charconv_digits_<8u, true> // int64_t
{
enum : size_t {
maxdigits_bin = 1 + 2 + 64, // len=67: -9223372036854775808 -0b1000000000000000000000000000000000000000000000000000000000000000
@@ -396,7 +388,7 @@ template<> struct charconv_digits_<8u, true> // int32_t
static constexpr csubstr max_value_dec() noexcept { return csubstr("9223372036854775807"); }
static constexpr bool is_oct_overflow(csubstr str) noexcept { return !((str.len < 22)); }
};
template<> struct charconv_digits_<8u, false>
template<> struct charconv_digits_<8u, false> // uint64_t
{
enum : size_t {
maxdigits_bin = 2 + 64, // len=66: 18446744073709551615 0b1111111111111111111111111111111111111111111111111111111111111111
@@ -545,24 +537,25 @@ C4_CONSTEXPR14 C4_ALWAYS_INLINE unsigned digits_oct(T v_) noexcept
// TODO: is there a better way?
C4_STATIC_ASSERT(std::is_integral<T>::value);
C4_ASSERT(v_ >= 0);
using U = typename
std::conditional<sizeof(T) <= sizeof(unsigned),
unsigned,
typename std::make_unsigned<T>::type>::type;
U v = (U) v_; // safe because we require v_ >= 0
unsigned __n = 1;
const unsigned __b2 = 64u;
const unsigned __b3 = __b2 * 8u;
const unsigned long __b4 = __b3 * 8u;
using U = typename std::conditional<sizeof(T) <= sizeof(unsigned),
unsigned,
typename std::make_unsigned<T>::type>::type;
U v = (U) v_; // safe because we require v_ >= 0 // NOLINT
uint32_t __n = 1;
enum : U {
__b2 = 64u,
__b3 = 64u * 8u,
__b4 = 64u * 8u * 8u,
};
while(true)
{
if(v < 8u)
return __n;
if(v < __b2)
else if(v < __b2)
return __n + 1;
if(v < __b3)
else if(v < __b3)
return __n + 2;
if(v < __b4)
else if(v < __b4)
return __n + 3;
v /= (U) __b4;
__n += 4;
@@ -617,7 +610,7 @@ void write_dec_unchecked(substr buf, T v, unsigned digits_v) noexcept
{
T quo = v;
quo /= T(100);
const auto num = (v - quo * T(100)) << 1u;
const auto num = (v - quo * T(100)) << 1u; // NOLINT
v = quo;
buf.str[--digits_v] = detail::digits0099[num + 1];
buf.str[--digits_v] = detail::digits0099[num];
@@ -782,15 +775,18 @@ template<class T, NumberWriter<T> writer>
size_t write_num_digits(substr buf, T v, size_t num_digits) noexcept
{
C4_STATIC_ASSERT(std::is_integral<T>::value);
size_t ret = writer(buf, v);
const size_t ret = writer(buf, v);
if(ret >= num_digits)
return ret;
else if(ret >= buf.len || num_digits > buf.len)
return num_digits;
C4_ASSERT(num_digits >= ret);
size_t delta = static_cast<size_t>(num_digits - ret);
memmove(buf.str + delta, buf.str, ret);
memset(buf.str, '0', delta);
const size_t delta = static_cast<size_t>(num_digits - ret); // NOLINT
C4_ASSERT(ret + delta <= buf.len);
if(ret)
memmove(buf.str + delta, buf.str, ret);
if(delta)
memset(buf.str, '0', delta);
return num_digits;
}
} // namespace detail
@@ -985,7 +981,9 @@ C4_SUPPRESS_WARNING_GCC_WITH_PUSH("-Wswitch-default")
namespace detail {
inline size_t _itoa2buf(substr buf, size_t pos, csubstr val) noexcept
{
C4_ASSERT(pos < buf.len);
C4_ASSERT(pos + val.len <= buf.len);
C4_ASSERT(val.len > 0);
memcpy(buf.str + pos, val.str, val.len);
return pos + val.len;
}
@@ -1013,7 +1011,7 @@ C4_NO_INLINE size_t _itoa2buf(substr buf, I radix) noexcept
size_t pos = 0;
if(C4_LIKELY(buf.len > 0))
buf.str[pos++] = '-';
switch(radix)
switch(radix) // NOLINT(hicpp-multiway-paths-covered)
{
case I(10):
if(C4_UNLIKELY(buf.len < digits_type::maxdigits_dec))
@@ -1052,7 +1050,7 @@ C4_NO_INLINE size_t _itoa2buf(substr buf, I radix, size_t num_digits) noexcept
size_t needed_digits = 0;
if(C4_LIKELY(buf.len > 0))
buf.str[pos++] = '-';
switch(radix)
switch(radix) // NOLINT(hicpp-multiway-paths-covered)
{
case I(10):
// add 1 to account for -
@@ -1117,7 +1115,7 @@ C4_ALWAYS_INLINE size_t itoa(substr buf, T v) noexcept
}
// when T is the min value (eg i8: -128), negating it
// will overflow, so treat the min as a special case
else if(C4_LIKELY(v != std::numeric_limits<T>::min()))
if(C4_LIKELY(v != std::numeric_limits<T>::min()))
{
v = -v;
unsigned digits = digits_dec(v);
@@ -1160,7 +1158,7 @@ C4_ALWAYS_INLINE size_t itoa(substr buf, T v, T radix) noexcept
++pos;
}
unsigned digits = 0;
switch(radix)
switch(radix) // NOLINT(hicpp-multiway-paths-covered)
{
case T(10):
digits = digits_dec(v);
@@ -1237,7 +1235,7 @@ C4_ALWAYS_INLINE size_t itoa(substr buf, T v, T radix, size_t num_digits) noexce
++pos;
}
unsigned total_digits = 0;
switch(radix)
switch(radix) // NOLINT(hicpp-multiway-paths-covered)
{
case T(10):
total_digits = digits_dec(v);
@@ -1322,7 +1320,7 @@ C4_ALWAYS_INLINE size_t utoa(substr buf, T v, T radix) noexcept
C4_STATIC_ASSERT(std::is_unsigned<T>::value);
C4_ASSERT(radix == 10 || radix == 16 || radix == 2 || radix == 8);
unsigned digits = 0;
switch(radix)
switch(radix) // NOLINT(hicpp-multiway-paths-covered)
{
case T(10):
digits = digits_dec(v);
@@ -1377,7 +1375,7 @@ C4_ALWAYS_INLINE size_t utoa(substr buf, T v, T radix, size_t num_digits) noexce
C4_STATIC_ASSERT(std::is_unsigned<T>::value);
C4_ASSERT(radix == 10 || radix == 16 || radix == 2 || radix == 8);
unsigned total_digits = 0;
switch(radix)
switch(radix) // NOLINT(hicpp-multiway-paths-covered)
{
case T(10):
total_digits = digits_dec(v);
@@ -1601,7 +1599,7 @@ C4_ALWAYS_INLINE size_t atou_first(csubstr str, T *v)
/** @} */
#ifdef _MSC_VER
#if defined(_MSC_VER) && !defined(__clang__)
# pragma warning(pop)
#elif defined(__clang__)
# pragma clang diagnostic pop
@@ -1618,19 +1616,16 @@ C4_ALWAYS_INLINE size_t atou_first(csubstr str, T *v)
namespace detail {
inline bool check_overflow(csubstr str, csubstr limit) noexcept
{
if(str.len == limit.len)
{
for(size_t i = 0; i < limit.len; ++i)
{
if(str[i] < limit[i])
return false;
else if(str[i] > limit[i])
return true;
}
return false;
}
else
if(str.len != limit.len)
return str.len > limit.len;
for(size_t i = 0; i < limit.len; ++i)
{
if(str[i] < limit[i])
return false;
else if(str[i] > limit[i])
return true;
}
return false;
}
} // namespace detail
/** @endcond */
@@ -1714,7 +1709,7 @@ auto overflows(csubstr str) noexcept
* @see doc_overflow_checked for format specifiers to enforce no-overflow reads
*/
template<class T>
auto overflows(csubstr str)
auto overflows(csubstr str) noexcept
-> typename std::enable_if<std::is_signed<T>::value, bool>::type
{
C4_STATIC_ASSERT(std::is_integral<T>::value);
@@ -1762,7 +1757,9 @@ auto overflows(csubstr str)
}
}
else
{
return detail::check_overflow(str.sub(1), detail::charconv_digits<T>::min_value_dec());
}
}
else if(str.str[0] == '0')
{
@@ -1805,7 +1802,9 @@ auto overflows(csubstr str)
}
}
else
{
return detail::check_overflow(str, detail::charconv_digits<T>::max_value_dec());
}
}
/** @} */
@@ -1989,7 +1988,7 @@ C4_ALWAYS_INLINE bool scan_rhex(csubstr s, T *C4_RESTRICT val) noexcept
else if(c == 'p' || c == 'P')
{
++pos;
goto power; // no mantissa given, jump to power
goto power; // no mantissa given, jump to power // NOLINT
}
else
{
@@ -1999,7 +1998,7 @@ C4_ALWAYS_INLINE bool scan_rhex(csubstr s, T *C4_RESTRICT val) noexcept
// mantissa
{
// 0.0625 == 1/16 == value of first digit after the comma
for(T digit = T(0.0625); pos < s.len; ++pos, digit /= T(16))
for(T digit = T(0.0625); pos < s.len; ++pos, digit /= T(16)) // NOLINT
{
const char c = s.str[pos];
if(c >= '0' && c <= '9')
@@ -2011,7 +2010,7 @@ C4_ALWAYS_INLINE bool scan_rhex(csubstr s, T *C4_RESTRICT val) noexcept
else if(c == 'p' || c == 'P')
{
++pos;
goto power; // mantissa finished, jump to power
goto power; // mantissa finished, jump to power // NOLINT
}
else
{
@@ -2172,6 +2171,7 @@ inline size_t atof_first(csubstr str, float * C4_RESTRICT v) noexcept
*/
C4_ALWAYS_INLINE bool atod(csubstr str, double * C4_RESTRICT v) noexcept
{
C4_ASSERT(str.len > 0);
C4_ASSERT(str.triml(" \r\t\n").len == str.len);
#if C4CORE_HAVE_FAST_FLOAT
// fastfloat cannot parse hexadecimal floats
@@ -2228,8 +2228,8 @@ inline size_t atod_first(csubstr str, double * C4_RESTRICT v) noexcept
/** @cond dev */
// on some platforms, (unsigned) int and (unsigned) long
// are not any of the fixed length types above
#define _C4_IF_NOT_FIXED_LENGTH_I(T, ty) C4_ALWAYS_INLINE typename std::enable_if<std:: is_signed<T>::value && !is_fixed_length<T>::value_i, ty>
#define _C4_IF_NOT_FIXED_LENGTH_U(T, ty) C4_ALWAYS_INLINE typename std::enable_if<std::is_unsigned<T>::value && !is_fixed_length<T>::value_u, ty>
#define _C4_IF_NOT_FIXED_LENGTH_I(T, ty) typename std::enable_if<std:: is_signed<T>::value && !is_fixed_length<T>::value_i, ty>
#define _C4_IF_NOT_FIXED_LENGTH_U(T, ty) typename std::enable_if<std::is_unsigned<T>::value && !is_fixed_length<T>::value_u, ty>
/** @endcond*/
@@ -2271,8 +2271,8 @@ C4_ALWAYS_INLINE size_t xtoa(substr s, int64_t v, int64_t radix, size_t num_di
C4_ALWAYS_INLINE size_t xtoa(substr s, float v, int precision, RealFormat_e formatting=FTOA_FLEX) noexcept { return ftoa(s, v, precision, formatting); }
C4_ALWAYS_INLINE size_t xtoa(substr s, double v, int precision, RealFormat_e formatting=FTOA_FLEX) noexcept { return dtoa(s, v, precision, formatting); }
template <class T> _C4_IF_NOT_FIXED_LENGTH_I(T, size_t)::type xtoa(substr buf, T v) noexcept { return itoa(buf, v); }
template <class T> _C4_IF_NOT_FIXED_LENGTH_U(T, size_t)::type xtoa(substr buf, T v) noexcept { return write_dec(buf, v); }
template <class T> C4_ALWAYS_INLINE auto xtoa(substr buf, T v) noexcept -> _C4_IF_NOT_FIXED_LENGTH_I(T, size_t)::type { return itoa(buf, v); }
template <class T> C4_ALWAYS_INLINE auto xtoa(substr buf, T v) noexcept -> _C4_IF_NOT_FIXED_LENGTH_U(T, size_t)::type { return write_dec(buf, v); }
template <class T>
C4_ALWAYS_INLINE size_t xtoa(substr s, T *v) noexcept { return itoa(s, (intptr_t)v, (intptr_t)16); }
@@ -2296,8 +2296,8 @@ C4_ALWAYS_INLINE bool atox(csubstr s, int64_t *C4_RESTRICT v) noexcept { return
C4_ALWAYS_INLINE bool atox(csubstr s, float *C4_RESTRICT v) noexcept { return atof(s, v); }
C4_ALWAYS_INLINE bool atox(csubstr s, double *C4_RESTRICT v) noexcept { return atod(s, v); }
template <class T> _C4_IF_NOT_FIXED_LENGTH_I(T, bool )::type atox(csubstr buf, T *C4_RESTRICT v) noexcept { return atoi(buf, v); }
template <class T> _C4_IF_NOT_FIXED_LENGTH_U(T, bool )::type atox(csubstr buf, T *C4_RESTRICT v) noexcept { return atou(buf, v); }
template <class T> C4_ALWAYS_INLINE auto atox(csubstr buf, T *C4_RESTRICT v) noexcept -> _C4_IF_NOT_FIXED_LENGTH_I(T, bool)::type { return atoi(buf, v); }
template <class T> C4_ALWAYS_INLINE auto atox(csubstr buf, T *C4_RESTRICT v) noexcept -> _C4_IF_NOT_FIXED_LENGTH_U(T, bool)::type { return atou(buf, v); }
template <class T>
C4_ALWAYS_INLINE bool atox(csubstr s, T **v) noexcept { intptr_t tmp; bool ret = atox(s, &tmp); if(ret) { *v = (T*)tmp; } return ret; }
@@ -2336,8 +2336,8 @@ C4_ALWAYS_INLINE size_t to_chars(substr buf, int64_t v) noexcept { return itoa(
C4_ALWAYS_INLINE size_t to_chars(substr buf, float v) noexcept { return ftoa(buf, v); }
C4_ALWAYS_INLINE size_t to_chars(substr buf, double v) noexcept { return dtoa(buf, v); }
template <class T> _C4_IF_NOT_FIXED_LENGTH_I(T, size_t)::type to_chars(substr buf, T v) noexcept { return itoa(buf, v); }
template <class T> _C4_IF_NOT_FIXED_LENGTH_U(T, size_t)::type to_chars(substr buf, T v) noexcept { return write_dec(buf, v); }
template <class T> C4_ALWAYS_INLINE auto to_chars(substr buf, T v) noexcept -> _C4_IF_NOT_FIXED_LENGTH_I(T, size_t)::type { return itoa(buf, v); }
template <class T> C4_ALWAYS_INLINE auto to_chars(substr buf, T v) noexcept -> _C4_IF_NOT_FIXED_LENGTH_U(T, size_t)::type { return write_dec(buf, v); }
template <class T>
C4_ALWAYS_INLINE size_t to_chars(substr s, T *v) noexcept { return itoa(s, (intptr_t)v, (intptr_t)16); }
@@ -2371,8 +2371,8 @@ C4_ALWAYS_INLINE bool from_chars(csubstr buf, int64_t *C4_RESTRICT v) noexcept
C4_ALWAYS_INLINE bool from_chars(csubstr buf, float *C4_RESTRICT v) noexcept { return atof(buf, v); }
C4_ALWAYS_INLINE bool from_chars(csubstr buf, double *C4_RESTRICT v) noexcept { return atod(buf, v); }
template <class T> _C4_IF_NOT_FIXED_LENGTH_I(T, bool )::type from_chars(csubstr buf, T *C4_RESTRICT v) noexcept { return atoi(buf, v); }
template <class T> _C4_IF_NOT_FIXED_LENGTH_U(T, bool )::type from_chars(csubstr buf, T *C4_RESTRICT v) noexcept { return atou(buf, v); }
template <class T> C4_ALWAYS_INLINE auto from_chars(csubstr buf, T *C4_RESTRICT v) noexcept -> _C4_IF_NOT_FIXED_LENGTH_I(T, bool)::type { return atoi(buf, v); }
template <class T> C4_ALWAYS_INLINE auto from_chars(csubstr buf, T *C4_RESTRICT v) noexcept -> _C4_IF_NOT_FIXED_LENGTH_U(T, bool)::type { return atou(buf, v); }
template <class T>
C4_ALWAYS_INLINE bool from_chars(csubstr buf, T **v) noexcept { intptr_t tmp; bool ret = from_chars(buf, &tmp); if(ret) { *v = (T*)tmp; } return ret; }
@@ -2399,8 +2399,8 @@ C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, int64_t *C4_RESTRICT v) n
C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, float *C4_RESTRICT v) noexcept { return atof_first(buf, v); }
C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, double *C4_RESTRICT v) noexcept { return atod_first(buf, v); }
template <class T> _C4_IF_NOT_FIXED_LENGTH_I(T, size_t)::type from_chars_first(csubstr buf, T *C4_RESTRICT v) noexcept { return atoi_first(buf, v); }
template <class T> _C4_IF_NOT_FIXED_LENGTH_U(T, size_t)::type from_chars_first(csubstr buf, T *C4_RESTRICT v) noexcept { return atou_first(buf, v); }
template <class T> C4_ALWAYS_INLINE auto from_chars_first(csubstr buf, T *C4_RESTRICT v) noexcept -> _C4_IF_NOT_FIXED_LENGTH_I(T, size_t)::type { return atoi_first(buf, v); }
template <class T> C4_ALWAYS_INLINE auto from_chars_first(csubstr buf, T *C4_RESTRICT v) noexcept -> _C4_IF_NOT_FIXED_LENGTH_U(T, size_t)::type { return atou_first(buf, v); }
template <class T>
C4_ALWAYS_INLINE size_t from_chars_first(csubstr buf, T **v) noexcept { intptr_t tmp; bool ret = from_chars_first(buf, &tmp); if(ret) { *v = (T*)tmp; } return ret; }
@@ -2657,11 +2657,11 @@ inline size_t to_chars(substr buf, const char * C4_RESTRICT v) noexcept
} // namespace c4
#ifdef _MSC_VER
# pragma warning(pop)
#endif
// NOLINTEND(hicpp-signed-bitwise)
#if defined(__clang__)
#if defined(_MSC_VER) && !defined(__clang__)
# pragma warning(pop)
#elif defined(__clang__)
# pragma clang diagnostic pop
#elif defined(__GNUC__)
# pragma GCC diagnostic pop

View File

@@ -24,7 +24,7 @@
/** @see http://sourceforge.net/p/predef/wiki/Compilers/ for a list of compiler identifier macros */
/** @see https://msdn.microsoft.com/en-us/library/b0084kay.aspx for VS2013 predefined macros */
#if defined(_MSC_VER) && !defined(__clang__)
#if defined(_MSC_VER)
# define C4_MSVC
# define C4_MSVC_VERSION_2022 17
# define C4_MSVC_VERSION_2019 16
@@ -93,6 +93,9 @@
# define C4_CLANG_VERSION __apple_build_version__
# endif
# elif defined(__GNUC__)
# ifdef __MINGW32__
# define C4_MINGW
# endif
# define C4_GCC
# if defined(__GNUC_PATCHLEVEL__)
# define C4_GCC_VERSION C4_VERSION_ENCODED(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)

View File

@@ -7,7 +7,7 @@
// see also https://sourceforge.net/p/predef/wiki/Architectures/
// see also https://sourceforge.net/p/predef/wiki/Endianness/
// see also https://github.com/googlesamples/android-ndk/blob/android-mk/hello-jni/jni/hello-jni.c
// see http://code.qt.io/cgit/qt/qtbase.git/tree/src/corelib/global/qprocessordetection.h
// see also http://code.qt.io/cgit/qt/qtbase.git/tree/src/corelib/global/qprocessordetection.h
#ifdef __ORDER_LITTLE_ENDIAN__
# define _C4EL __ORDER_LITTLE_ENDIAN__
@@ -22,7 +22,11 @@
#endif
// mixed byte order (eg, PowerPC or ia64)
#define _C4EM 1111
#define _C4EM 1111 // NOLINT
// NOTE: to find defined macros in a platform,
// g++ <flags> -dM -E - </dev/null | sort
#if defined(__x86_64) || defined(__x86_64__) || defined(__amd64) || defined(_M_X64)
# define C4_CPU_X86_64
@@ -60,11 +64,15 @@
|| defined(__ARM_ARCH_6M__) || defined(__ARM_ARCH_6KZ__) \
|| (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 6)
# define C4_CPU_ARMV6
# elif defined(__ARM_ARCH_5TEJ__) \
# elif (defined(__ARM_ARCH) && __ARM_ARCH == 5) \
|| defined(__ARM_ARCH_5TEJ__) \
|| defined(__ARM_ARCH_5TE__) \
|| defined(__ARM_ARCH_5T__) \
|| (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 5)
# define C4_CPU_ARMV5
# elif defined(__ARM_ARCH_4T__) \
# elif (defined(__ARM_ARCH) && __ARM_ARCH == 4) \
|| defined(__ARM_ARCH_4T__) \
|| defined(__ARM_ARCH_4__) \
|| (defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM >= 4)
# define C4_CPU_ARMV4
# else
@@ -145,6 +153,44 @@
# endif
# define C4_BYTE_ORDER _C4EL
#elif defined(__mips__) || defined(_mips) || defined(mips)
# if defined(__mips)
# if __mips == 64
# define C4_CPU_MIPS64
# define C4_WORDSIZE 8
# elif __mips == 32
# define C4_CPU_MIPS32
# define C4_WORDSIZE 4
# endif
# elif defined(__arch64__) || (defined(__SIZE_WIDTH__) && __SIZE_WIDTH__ == 64) || (defined(__LP64__) && __LP64__)
# define C4_CPU_MIPS64
# define C4_WORDSIZE 8
# elif defined(__arch32__) || (defined(__SIZE_WIDTH__) && __SIZE_WIDTH__ == 32) || (defined(__LP32__) && __LP32__)
# define C4_CPU_MIPS32
# define C4_WORDSIZE 4
# else
# error "unknown mips architecture"
# endif
# if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
# define C4_BYTE_ORDER _C4EB
# elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
# define C4_BYTE_ORDER _C4EL
# else
# error "unknown mips endianness"
# endif
#elif defined(__sparc__) || defined(__sparc) || defined(sparc)
# if defined(__arch64__) || (defined(__SIZE_WIDTH__) && __SIZE_WIDTH__ == 64) || (defined(__LP64__) && __LP64__)
# define C4_CPU_SPARC64
# define C4_WORDSIZE 8
# elif defined(__arch32__) || (defined(__SIZE_WIDTH__) && __SIZE_WIDTH__ == 32) || (defined(__LP32__) && __LP32__)
# define C4_CPU_SPARC32
# define C4_WORDSIZE 4
# else
# error "unknown sparc architecture"
# endif
# define C4_BYTE_ORDER _C4EB
#elif defined(SWIG)
# error "please define CPU architecture macros when compiling with swig"

File diff suppressed because it is too large Load Diff

View File

@@ -63,6 +63,7 @@ struct fail_type__ {};
#else
# ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wundef"
# if !defined(__APPLE_CC__)
# if __clang_major__ >= 10
# pragma clang diagnostic ignored "-Wgnu-inline-cpp-without-extern" // debugbreak/debugbreak.h:50:16: error: 'gnu_inline' attribute without 'extern' in C++ treated as externally available, this changed in Clang 10 [-Werror,-Wgnu-inline-cpp-without-extern]
@@ -73,12 +74,15 @@ struct fail_type__ {};
# endif
# endif
# elif defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wundef"
# endif
# include <c4/ext/debugbreak/debugbreak.h>
# define C4_DEBUG_BREAK() if(c4::is_debugger_attached()) { ::debug_break(); }
# ifdef __clang__
# pragma clang diagnostic pop
# elif defined(__GNUC__)
# pragma GCC diagnostic pop
# endif
#endif
@@ -114,17 +118,17 @@ namespace c4 {
typedef enum : uint32_t {
/** when an error happens and the debugger is attached, call C4_DEBUG_BREAK().
* Without effect otherwise. */
ON_ERROR_DEBUGBREAK = 0x01 << 0,
ON_ERROR_DEBUGBREAK = 0x01u << 0u,
/** when an error happens log a message. */
ON_ERROR_LOG = 0x01 << 1,
ON_ERROR_LOG = 0x01u << 1u,
/** when an error happens invoke a callback if it was set with
* set_error_callback(). */
ON_ERROR_CALLBACK = 0x01 << 2,
ON_ERROR_CALLBACK = 0x01u << 2u,
/** when an error happens call std::terminate(). */
ON_ERROR_ABORT = 0x01 << 3,
ON_ERROR_ABORT = 0x01u << 3u,
/** when an error happens and exceptions are enabled throw an exception.
* Without effect otherwise. */
ON_ERROR_THROW = 0x01 << 4,
ON_ERROR_THROW = 0x01u << 4u,
/** the default flags. */
ON_ERROR_DEFAULTS = ON_ERROR_DEBUGBREAK|ON_ERROR_LOG|ON_ERROR_CALLBACK|ON_ERROR_ABORT
} ErrorFlags_e;
@@ -140,7 +144,7 @@ C4CORE_EXPORT error_callback_type get_error_callback();
//-----------------------------------------------------------------------------
/** RAII class controling the error settings inside a scope. */
struct ScopedErrorSettings
struct ScopedErrorSettings // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions)
{
error_flags m_flags;
error_callback_type m_callback;
@@ -177,7 +181,8 @@ struct ScopedErrorSettings
/** source location */
struct srcloc;
C4CORE_EXPORT void handle_error(srcloc s, const char *fmt, ...);
// watchout: for VS the [[noreturn]] needs to come before other annotations like C4CORE_EXPORT
[[noreturn]] C4CORE_EXPORT void handle_error(srcloc s, const char *fmt, ...);
C4CORE_EXPORT void handle_warning(srcloc s, const char *fmt, ...);

View File

@@ -8,7 +8,7 @@
#include "c4/blob.hpp"
#ifdef _MSC_VER
#if defined(_MSC_VER) && !defined(__clang__)
# pragma warning(push)
# if C4_MSVC_VERSION != C4_MSVC_VERSION_2017
# pragma warning(disable: 4800) // forcing value to bool 'true' or 'false' (performance warning)
@@ -20,6 +20,7 @@
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wuseless-cast"
#endif
// NOLINTBEGIN(cppcoreguidelines-pro-type-reinterpret-cast,*avoid-goto*)
/** @defgroup doc_format_utils Format utilities
*
@@ -323,7 +324,7 @@ to_chars(substr buf, fmt::integral_padded_<T> fmt)
return utoa(buf, fmt.val, fmt.radix, fmt.num_digits);
}
/** read an format an integer unsigned type
/** read an integer type, detecting overflow (returns false on overflow)
* @ingroup doc_from_chars */
template<class T>
C4_ALWAYS_INLINE bool from_chars(csubstr s, fmt::overflow_checked_<T> wrapper)
@@ -332,6 +333,15 @@ C4_ALWAYS_INLINE bool from_chars(csubstr s, fmt::overflow_checked_<T> wrapper)
return atox(s, wrapper.val);
return false;
}
/** read an integer type, detecting overflow (returns false on overflow)
* @ingroup doc_from_chars */
template<class T>
C4_ALWAYS_INLINE bool from_chars(csubstr s, fmt::overflow_checked_<T> *wrapper)
{
if(C4_LIKELY(!overflows<T>(s)))
return atox(s, wrapper->val);
return false;
}
//-----------------------------------------------------------------------------
@@ -967,7 +977,6 @@ inline CharOwningContainer catseprs(Sep const& C4_RESTRICT sep, Args const& C4_R
* the result. The buffer is appended to.
*
* @return a csubstr of the appended part
* @ingroup formatting_functions
* @ingroup doc_catsep */
template<class CharOwningContainer, class Sep, class... Args>
inline csubstr catseprs_append(CharOwningContainer * C4_RESTRICT cont, Sep const& C4_RESTRICT sep, Args const& C4_RESTRICT ...args)
@@ -1019,7 +1028,6 @@ inline CharOwningContainer formatrs(csubstr fmt, Args const& C4_RESTRICT ...args
* arguments, resizing the container as needed to contain the
* result. The buffer is appended to.
* @return the region newly appended to the original container
* @ingroup formatting_functions
* @ingroup doc_format */
template<class CharOwningContainer, class... Args>
inline csubstr formatrs_append(CharOwningContainer * C4_RESTRICT cont, csubstr fmt, Args const& C4_RESTRICT ...args)
@@ -1038,6 +1046,7 @@ retry:
} // namespace c4
// NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast,*avoid-goto*)
#ifdef _MSC_VER
# pragma warning(pop)
#elif defined(__clang__)

View File

@@ -333,7 +333,7 @@ namespace detail {
#ifdef __GNUC__
# define C4_DONT_OPTIMIZE(var) c4::detail::dont_optimize(var)
template< class T >
C4_ALWAYS_INLINE void dont_optimize(T const& value) { asm volatile("" : : "g"(value) : "memory"); }
C4_ALWAYS_INLINE void dont_optimize(T const& value) { asm volatile("" : : "g"(value) : "memory"); } // NOLINT
#else
# define C4_DONT_OPTIMIZE(var) c4::detail::use_char_pointer(reinterpret_cast< const char* >(&var))
void use_char_pointer(char const volatile*);

View File

@@ -25,6 +25,8 @@
/** @file memory_util.hpp Some memory utilities. */
// NOLINTBEGIN(google-runtime-int)
namespace c4 {
C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wold-style-cast")
@@ -696,8 +698,8 @@ struct tight_pair<First, Second, tpc_same_empty> : public First
C4_ALWAYS_INLINE C4_CONSTEXPR14 First & first () { return static_cast<First &>(*this); }
C4_ALWAYS_INLINE C4_CONSTEXPR14 First const& first () const { return static_cast<First const&>(*this); }
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second & second() { return reinterpret_cast<Second &>(*this); }
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second const& second() const { return reinterpret_cast<Second const&>(*this); }
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second & second() { return reinterpret_cast<Second &>(*this); } // NOLINT
C4_ALWAYS_INLINE C4_CONSTEXPR14 Second const& second() const { return reinterpret_cast<Second const&>(*this); } // NOLINT
};
template<class First, class Second>
@@ -775,4 +777,6 @@ C4_SUPPRESS_WARNING_GCC_CLANG_POP
} // namespace c4
// NOLINTEND(google-runtime-int)
#endif /* _C4_MEMORY_UTIL_HPP_ */

View File

@@ -67,12 +67,9 @@ static inline void _do_reverse(C *C4_RESTRICT first, C *C4_RESTRICT last)
* @see a [quickstart
* sample](https://rapidyaml.readthedocs.io/latest/doxygen/group__doc__quickstart.html#ga43e253da0692c13967019446809c1113)
* in rapidyaml's documentation.
*
* @see @ref substr and @ref to_substr()
* @see @ref csubstr and @ref to_csubstr()
*/
template<class C>
struct C4CORE_EXPORT basic_substring
struct C4CORE_EXPORT basic_substring // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions)
{
public:
@@ -530,11 +527,11 @@ public:
/** @name Lookup methods */
/** @{ */
inline size_t find(const C c, size_t start_pos=0) const
size_t find(const C c, size_t start_pos=0) const
{
return first_of(c, start_pos);
}
inline size_t find(ro_substr pattern, size_t start_pos=0) const
size_t find(ro_substr pattern, size_t start_pos=0) const
{
C4_ASSERT(start_pos == npos || (start_pos >= 0 && start_pos <= len));
if(len < pattern.len) return npos;
@@ -561,7 +558,7 @@ public:
public:
/** count the number of occurrences of c */
inline size_t count(const C c, size_t pos=0) const
size_t count(const C c, size_t pos=0) const
{
C4_ASSERT(pos >= 0 && pos <= len);
size_t num = 0;
@@ -575,7 +572,7 @@ public:
}
/** count the number of occurrences of s */
inline size_t count(ro_substr c, size_t pos=0) const
size_t count(ro_substr c, size_t pos=0) const
{
C4_ASSERT(pos >= 0 && pos <= len);
size_t num = 0;
@@ -589,14 +586,14 @@ public:
}
/** get the substr consisting of the first occurrence of @p c after @p pos, or an empty substr if none occurs */
inline basic_substring select(const C c, size_t pos=0) const
basic_substring select(const C c, size_t pos=0) const
{
pos = find(c, pos);
return pos != npos ? sub(pos, 1) : basic_substring();
}
/** get the substr consisting of the first occurrence of @p pattern after @p pos, or an empty substr if none occurs */
inline basic_substring select(ro_substr pattern, size_t pos=0) const
basic_substring select(ro_substr pattern, size_t pos=0) const
{
pos = find(pattern, pos);
return pos != npos ? sub(pos, pattern.len) : basic_substring();
@@ -608,7 +605,7 @@ public:
{
size_t which;
size_t pos;
inline operator bool() const { return which != NONE && pos != npos; }
operator bool() const { return which != NONE && pos != npos; }
};
first_of_any_result first_of_any(ro_substr s0, ro_substr s1) const
@@ -1307,12 +1304,12 @@ public:
else if(c == '.')
{
++pos;
goto fractional_part_dec;
goto fractional_part_dec; // NOLINT
}
else if(c == 'e' || c == 'E')
{
++pos;
goto power_part_dec;
goto power_part_dec; // NOLINT
}
else if(_is_delim_char(c))
{
@@ -1341,7 +1338,7 @@ public:
else if(c == 'e' || c == 'E')
{
++pos;
goto power_part_dec;
goto power_part_dec; // NOLINT
}
else if(_is_delim_char(c))
{
@@ -1394,12 +1391,12 @@ public:
else if(c == '.')
{
++pos;
goto fractional_part_hex;
goto fractional_part_hex; // NOLINT
}
else if(c == 'p' || c == 'P')
{
++pos;
goto power_part_hex;
goto power_part_hex; // NOLINT
}
else if(_is_delim_char(c))
{
@@ -1428,7 +1425,7 @@ public:
else if(c == 'p' || c == 'P')
{
++pos;
goto power_part_hex;
goto power_part_hex; // NOLINT
}
else if(_is_delim_char(c))
{
@@ -1484,12 +1481,12 @@ public:
else if(c == '.')
{
++pos;
goto fractional_part_bin;
goto fractional_part_bin; // NOLINT
}
else if(c == 'p' || c == 'P')
{
++pos;
goto power_part_bin;
goto power_part_bin; // NOLINT
}
else if(_is_delim_char(c))
{
@@ -1518,7 +1515,7 @@ public:
else if(c == 'p' || c == 'P')
{
++pos;
goto power_part_bin;
goto power_part_bin; // NOLINT
}
else if(_is_delim_char(c))
{
@@ -1574,12 +1571,12 @@ public:
else if(c == '.')
{
++pos;
goto fractional_part_oct;
goto fractional_part_oct; // NOLINT
}
else if(c == 'p' || c == 'P')
{
++pos;
goto power_part_oct;
goto power_part_oct; // NOLINT
}
else if(_is_delim_char(c))
{
@@ -1608,7 +1605,7 @@ public:
else if(c == 'p' || c == 'P')
{
++pos;
goto power_part_oct;
goto power_part_oct; // NOLINT
}
else if(_is_delim_char(c))
{
@@ -1714,7 +1711,7 @@ private:
}
split_iterator_impl& operator++ () { _tick(); return *this; }
split_iterator_impl operator++ (int) { split_iterator_impl it = *this; _tick(); return it; }
split_iterator_impl operator++ (int) { split_iterator_impl it = *this; _tick(); return it; } // NOLINT
basic_substring& operator* () { return m_str; }
basic_substring* operator-> () { return &m_str; }
@@ -2069,7 +2066,7 @@ public:
C4_REQUIRE_RW(basic_substring) erase_range(size_t first, size_t last)
{
C4_ASSERT(first <= last);
return erase(first, static_cast<size_t>(last-first));
return erase(first, static_cast<size_t>(last-first)); // NOLINT
}
/** erase a part of the string.

View File

@@ -53,7 +53,7 @@ szconv(SizeIn sz) noexcept
template<class SizeOut, class SizeIn>
C4_ALWAYS_INLINE
typename std::enable_if<is_narrower_size<SizeOut, SizeIn>::value, SizeOut>::type
szconv(SizeIn sz) C4_NOEXCEPT_X
szconv(SizeIn sz)
{
C4_XASSERT(sz >= 0);
C4_XASSERT_MSG((SizeIn)sz <= (SizeIn)std::numeric_limits<SizeOut>::max(), "size conversion overflow: in=%zu", (size_t)sz);

View File

@@ -17,6 +17,8 @@
/** @defgroup types Type utilities */
// NOLINTBEGIN(bugprone-macro-parentheses)
namespace c4 {
/** @defgroup intrinsic_types Intrinsic types
@@ -114,17 +116,17 @@ using fastcref = typename std::conditional<c4::cref_uses_val<T>::value, T, T con
//--------------------------------------------------
/** Just what its name says. Useful sometimes as a default empty policy class. */
struct EmptyStruct
struct EmptyStruct // NOLINT
{
template<class... T> EmptyStruct(T && ...){}
template<class... T> EmptyStruct(T && ...){} // NOLINT
};
/** Just what its name says. Useful sometimes as a default policy class to
* be inherited from. */
struct EmptyStructVirtual
struct EmptyStructVirtual // NOLINT
{
virtual ~EmptyStructVirtual() = default;
template<class... T> EmptyStructVirtual(T && ...){}
template<class... T> EmptyStructVirtual(T && ...){} // NOLINT
};
@@ -159,7 +161,7 @@ struct Padded : public T
using T::T;
using T::operator=;
Padded(T const& val) : T(val) {}
Padded(T && val) : T(val) {}
Padded(T && val) : T(std::forward<T>(val)) {} // NOLINT
char ___c4padspace___[BytesToPadAtEnd];
};
#pragma pack(pop)
@@ -170,7 +172,7 @@ struct Padded<T, 0> : public T
using T::T;
using T::operator=;
Padded(T const& val) : T(val) {}
Padded(T && val) : T(val) {}
Padded(T && val) : T(std::forward<T>(val)) {} // NOLINT
};
/** make T have a size which is at least Min bytes */
@@ -500,4 +502,6 @@ using index_sequence_for = make_index_sequence<sizeof...(_Tp)>;
} // namespace c4
// NOLINTEND(bugprone-macro-parentheses)
#endif /* _C4_TYPES_HPP_ */

View File

@@ -6,10 +6,67 @@
#include <stddef.h>
#include <stdint.h>
/** @file utf.hpp utilities for UTF and Byte Order Mark */
namespace c4 {
substr decode_code_point(substr out, csubstr code_point);
size_t decode_code_point(uint8_t *C4_RESTRICT buf, size_t buflen, const uint32_t code);
/** @defgroup doc_utf UTF utilities
* @{ */
/** skip the Byte Order Mark, or get the full string if there is Byte Order Mark.
* @see Implements the Byte Order Marks as described in https://en.wikipedia.org/wiki/Byte_order_mark#Byte-order_marks_by_encoding */
C4CORE_EXPORT substr skip_bom(substr s);
/** skip the Byte Order Mark, or get the full string if there is Byte Order Mark
* @see Implements the Byte Order Marks as described in https://en.wikipedia.org/wiki/Byte_order_mark#Byte-order_marks_by_encoding */
C4CORE_EXPORT csubstr skip_bom(csubstr s);
/** get the Byte Order Mark, or an empty string if there is no Byte Order Mark
* @see Implements the Byte Order Marks as described in https://en.wikipedia.org/wiki/Byte_order_mark#Byte-order_marks_by_encoding */
C4CORE_EXPORT substr get_bom(substr s);
/** get the Byte Order Mark, or an empty string if there is no Byte Order Mark
* @see Implements the Byte Order Marks as described in https://en.wikipedia.org/wiki/Byte_order_mark#Byte-order_marks_by_encoding */
C4CORE_EXPORT csubstr get_bom(csubstr s);
/** return the position of the first character not belonging to the
* Byte Order Mark, or 0 if there is no Byte Order Mark.
* @see Implements the Byte Order Marks as described in https://en.wikipedia.org/wiki/Byte_order_mark#Byte-order_marks_by_encoding */
C4CORE_EXPORT size_t first_non_bom(csubstr s);
/** decode the given @p code_point, writing into the output string in
* @p out.
*
* @param out the output string. must have at least 4 bytes (this is
* asserted), and must not have a null string.
*
* @param code_point: must have length in ]0,8], and must not begin
* with any of `U+`,`\\x`,`\\u,`\\U`,`0` (asserted)
*
* @return the part of @p out that was written, which will always be
* at most 4 bytes.
*/
C4CORE_EXPORT substr decode_code_point(substr out, csubstr code_point);
/** decode the given @p code point, writing into the output string @p
* buf, of size @p buflen
*
* @param buf the output string. must have at least 4 bytes (this is
* asserted), and must not be null
*
* @param buflen the length of the output string. must be at least 4
*
* @param code: the code point must have length in ]0,8], and must not begin
* with any of `U+`,`\\x`,`\\u,`\\U`,`0` (asserted)
*
* @return the part of @p out that was written, which will always be
* at most 4 bytes.
*/
size_t decode_code_point(uint8_t *C4_RESTRICT buf, size_t buflen, uint32_t code);
/** @} */
} // namespace c4

View File

@@ -5,8 +5,50 @@
#include <cstddef>
#include <c4/substr.hpp>
#include <c4/charconv.hpp>
#include <c4/dump.hpp>
#include <c4/yml/export.hpp>
#if defined(C4_MSVC) || defined(C4_MINGW)
#include <malloc.h>
#else
#include <alloca.h>
#endif
//-----------------------------------------------------------------------------
#ifndef RYML_ERRMSG_SIZE
/// size for the error message buffer
#define RYML_ERRMSG_SIZE (1024)
#endif
#ifndef RYML_LOGBUF_SIZE
/// size for the buffer used to format individual values to string
/// while preparing an error message. This is only used for formatting
/// individual values in the message; final messages will be larger
/// than this value (see @ref RYML_ERRMSG_SIZE). This is also used for
/// the detailed debug log messages when RYML_DBG is defined.
#define RYML_LOGBUF_SIZE (256)
#endif
#ifndef RYML_LOGBUF_SIZE_MAX
/// size for the fallback larger log buffer. When @ref
/// RYML_LOGBUF_SIZE is not large enough to convert a value to string,
/// then temporary stack memory is allocated up to
/// RYML_LOGBUF_SIZE_MAX. This limit is in place to prevent a stack
/// overflow. If the printed value requires more than
/// RYML_LOGBUF_SIZE_MAX, the value is silently skipped.
#define RYML_LOGBUF_SIZE_MAX (1024)
#endif
#ifndef RYML_LOCATIONS_SMALL_THRESHOLD
/// threshold at which a location search will revert from linear to
/// binary search.
#define RYML_LOCATIONS_SMALL_THRESHOLD (30)
#endif
//-----------------------------------------------------------------------------
// Specify groups to have a predefined topic order in doxygen:
@@ -83,6 +125,11 @@
*
*/
/** @defgroup doc_ref_utils Anchor/Reference utilities
*
* @see sample::sample_anchors_and_aliases
* */
/** @defgroup doc_tag_utils Tag utilities
* @see sample::sample_tags
*/
@@ -134,11 +181,13 @@
# define RYML_ASSERT(cond) RYML_CHECK(cond)
# define RYML_ASSERT_MSG(cond, msg) RYML_CHECK_MSG(cond, msg)
# define _RYML_CB_ASSERT(cb, cond) _RYML_CB_CHECK((cb), (cond))
# define _RYML_CB_ASSERT_(cb, cond, loc) _RYML_CB_CHECK((cb), (cond), (loc))
# define RYML_NOEXCEPT
#else
# define RYML_ASSERT(cond)
# define RYML_ASSERT_MSG(cond, msg)
# define _RYML_CB_ASSERT(cb, cond)
# define _RYML_CB_ASSERT_(cb, cond, loc)
# define RYML_NOEXCEPT noexcept
#endif
@@ -148,7 +197,7 @@
do { \
if(C4_UNLIKELY(!(cond))) \
{ \
RYML_DEBUG_BREAK() \
RYML_DEBUG_BREAK(); \
c4::yml::error("check failed: " #cond, c4::yml::Location(__FILE__, __LINE__, 0)); \
C4_UNREACHABLE_AFTER_ERR(); \
} \
@@ -159,7 +208,7 @@
{ \
if(C4_UNLIKELY(!(cond))) \
{ \
RYML_DEBUG_BREAK() \
RYML_DEBUG_BREAK(); \
c4::yml::error(msg ": check failed: " #cond, c4::yml::Location(__FILE__, __LINE__, 0)); \
C4_UNREACHABLE_AFTER_ERR(); \
} \
@@ -167,17 +216,16 @@
#if defined(RYML_DBG) && !defined(NDEBUG) && !defined(C4_NO_DEBUG_BREAK)
# define RYML_DEBUG_BREAK() \
{ \
do { \
if(c4::get_error_flags() & c4::ON_ERROR_DEBUGBREAK) \
{ \
C4_DEBUG_BREAK(); \
} \
}
} while(0)
#else
# define RYML_DEBUG_BREAK()
#endif
/** @endcond */
@@ -190,11 +238,33 @@ namespace yml {
C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wold-style-cast")
enum : size_t {
/** a null position */
npos = size_t(-1),
#ifndef RYML_ID_TYPE
/** The type of a node id in the YAML tree. In the future, the default
* will likely change to int32_t, which was observed to be faster.
* @see id_type */
#define RYML_ID_TYPE size_t
#endif
/** The type of a node id in the YAML tree; to override the default
* type, define the macro @ref RYML_ID_TYPE to a suitable integer
* type. */
using id_type = RYML_ID_TYPE;
static_assert(std::is_integral<id_type>::value, "id_type must be an integer type");
C4_SUPPRESS_WARNING_GCC_WITH_PUSH("-Wuseless-cast")
enum : id_type {
/** an index to none */
NONE = size_t(-1)
NONE = id_type(-1),
};
C4_SUPPRESS_WARNING_GCC_CLANG_POP
enum : size_t {
/** a null string position */
npos = size_t(-1)
};
@@ -212,28 +282,40 @@ struct RYML_EXPORT LineCol
//! column
size_t col;
LineCol() : offset(), line(), col() {}
LineCol() = default;
//! construct from line and column
LineCol(size_t l, size_t c) : offset(0), line(l), col(c) {}
//! construct from offset, line and column
LineCol(size_t o, size_t l, size_t c) : offset(o), line(l), col(c) {}
};
static_assert(std::is_trivial<LineCol>::value, "LineCol not trivial");
static_assert(std::is_standard_layout<LineCol>::value, "Location not trivial");
//! a source file position
struct RYML_EXPORT Location : public LineCol
struct RYML_EXPORT Location
{
//! number of bytes from the beginning of the source buffer
size_t offset;
//! line
size_t line;
//! column
size_t col;
//! file name
csubstr name;
operator bool () const { return !name.empty() || line != 0 || offset != 0; }
operator bool () const { return !name.empty() || line != 0 || offset != 0 || col != 0; }
operator LineCol const& () const { return reinterpret_cast<LineCol const&>(*this); } // NOLINT
Location() : LineCol(), name() {}
Location( size_t l, size_t c) : LineCol{ l, c}, name( ) {}
Location( csubstr n, size_t l, size_t c) : LineCol{ l, c}, name(n) {}
Location( csubstr n, size_t b, size_t l, size_t c) : LineCol{b, l, c}, name(n) {}
Location(const char *n, size_t l, size_t c) : LineCol{ l, c}, name(to_csubstr(n)) {}
Location(const char *n, size_t b, size_t l, size_t c) : LineCol{b, l, c}, name(to_csubstr(n)) {}
Location() = default;
Location( size_t l, size_t c) : offset( ), line(l), col(c), name( ) {}
Location( size_t b, size_t l, size_t c) : offset(b), line(l), col(c), name( ) {}
Location( csubstr n, size_t l, size_t c) : offset( ), line(l), col(c), name(n) {}
Location( csubstr n, size_t b, size_t l, size_t c) : offset(b), line(l), col(c), name(n) {}
Location(const char *n, size_t l, size_t c) : offset( ), line(l), col(c), name(to_csubstr(n)) {}
Location(const char *n, size_t b, size_t l, size_t c) : offset(b), line(l), col(c), name(to_csubstr(n)) {}
};
static_assert(std::is_standard_layout<Location>::value, "Location not trivial");
//-----------------------------------------------------------------------------
@@ -300,7 +382,7 @@ struct RYML_EXPORT Callbacks
/** Construct an object with the default callbacks. If
* @ref RYML_NO_DEFAULT_CALLBACKS is defined, the object will have null
* members.*/
Callbacks();
Callbacks() noexcept;
/** Construct an object with the given callbacks.
*
@@ -338,6 +420,20 @@ struct RYML_EXPORT Callbacks
/** @} */
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
typedef enum {
NOBOM,
UTF8,
UTF16LE,
UTF16BE,
UTF32LE,
UTF32BE,
} Encoding_e;
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
@@ -364,30 +460,30 @@ template<size_t N>
}
#define _RYML_CB_ERR(cb, msg_literal) \
_RYML_CB_ERR_(cb, msg_literal, c4::yml::Location(__FILE__, 0, __LINE__, 0))
#define _RYML_CB_CHECK(cb, cond) \
_RYML_CB_CHECK_(cb, cond, c4::yml::Location(__FILE__, 0, __LINE__, 0))
#define _RYML_CB_ERR_(cb, msg_literal, loc) \
do \
{ \
const char msg[] = msg_literal; \
RYML_DEBUG_BREAK() \
c4::yml::error((cb), \
msg, sizeof(msg), \
c4::yml::Location(__FILE__, 0, __LINE__, 0)); \
RYML_DEBUG_BREAK(); \
c4::yml::error((cb), msg, sizeof(msg)-1, loc); \
C4_UNREACHABLE_AFTER_ERR(); \
} while(0)
#define _RYML_CB_CHECK(cb, cond) \
#define _RYML_CB_CHECK_(cb, cond, loc) \
do \
{ \
if(!(cond)) \
if(C4_UNLIKELY(!(cond))) \
{ \
const char msg[] = "check failed: " #cond; \
RYML_DEBUG_BREAK() \
c4::yml::error((cb), \
msg, sizeof(msg), \
c4::yml::Location(__FILE__, 0, __LINE__, 0)); \
RYML_DEBUG_BREAK(); \
c4::yml::error((cb), msg, sizeof(msg)-1, loc); \
C4_UNREACHABLE_AFTER_ERR(); \
} \
} while(0)
#define _RYML_CB_ALLOC_HINT(cb, T, num, hint) (T*) (cb).m_allocate((num) * sizeof(T), (hint), (cb).m_user_data)
#define _RYML_CB_ALLOC(cb, T, num) _RYML_CB_ALLOC_HINT((cb), (T), (num), nullptr)
#define _RYML_CB_ALLOC(cb, T, num) _RYML_CB_ALLOC_HINT((cb), T, (num), nullptr)
#define _RYML_CB_FREE(cb, buf, T, num) \
do { \
(cb).m_free((buf), (num) * sizeof(T), (cb).m_user_data); \
@@ -395,11 +491,54 @@ do \
} while(0)
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
typedef enum {
BLOCK_LITERAL, //!< keep newlines (|)
BLOCK_FOLD //!< replace newline with single space (>)
} BlockStyle_e;
typedef enum {
CHOMP_CLIP, //!< single newline at end (default)
CHOMP_STRIP, //!< no newline at end (-)
CHOMP_KEEP //!< all newlines from end (+)
} BlockChomp_e;
/** Abstracts the fact that a scalar filter result may not fit in the
* intended memory. */
struct FilterResult
{
C4_ALWAYS_INLINE bool valid() const noexcept { return str.str != nullptr; }
C4_ALWAYS_INLINE size_t required_len() const noexcept { return str.len; }
C4_ALWAYS_INLINE csubstr get() const { RYML_ASSERT(valid()); return str; }
csubstr str;
};
/** Abstracts the fact that a scalar filter result may not fit in the
* intended memory. */
struct FilterResultExtending
{
C4_ALWAYS_INLINE bool valid() const noexcept { return str.str != nullptr; }
C4_ALWAYS_INLINE size_t required_len() const noexcept { return reqlen; }
C4_ALWAYS_INLINE csubstr get() const { RYML_ASSERT(valid()); return str; }
csubstr str;
size_t reqlen;
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
namespace detail {
// is there a better way to do this?
template<int8_t signedval, uint8_t unsignedval>
struct _charconstant_t
: public std::conditional<std::is_signed<char>::value,
std::integral_constant<int8_t, signedval>,
std::integral_constant<int8_t, static_cast<int8_t>(unsignedval)>,
std::integral_constant<uint8_t, unsignedval>>::type
{};
#define _RYML_CHCONST(signedval, unsignedval) ::c4::yml::detail::_charconstant_t<INT8_C(signedval), UINT8_C(unsignedval)>::value
@@ -411,10 +550,11 @@ struct _SubstrWriter
{
substr buf;
size_t pos;
_SubstrWriter(substr buf_, size_t pos_=0) : buf(buf_), pos(pos_) {}
_SubstrWriter(substr buf_, size_t pos_=0) : buf(buf_), pos(pos_) { C4_ASSERT(buf.str); }
void append(csubstr s)
{
C4_ASSERT(!s.overlaps(buf));
C4_ASSERT(s.str || !s.len);
if(s.len && pos + s.len <= buf.len)
{
C4_ASSERT(s.str);
@@ -424,12 +564,14 @@ struct _SubstrWriter
}
void append(char c)
{
C4_ASSERT(buf.str);
if(pos < buf.len)
buf.str[pos] = c;
++pos;
}
void append_n(char c, size_t numtimes)
{
C4_ASSERT(buf.str);
if(numtimes && pos + numtimes < buf.len)
memset(buf.str + pos, c, numtimes);
pos += numtimes;
@@ -439,15 +581,77 @@ struct _SubstrWriter
//! get the part written so far
csubstr curr() const { return pos <= buf.len ? buf.first(pos) : buf; }
//! get the part that is still free to write to (the remainder)
substr rem() { return pos < buf.len ? buf.sub(pos) : buf.last(0); }
substr rem() const { return pos < buf.len ? buf.sub(pos) : buf.last(0); }
size_t advance(size_t more) { pos += more; return pos; }
};
} // namespace detail
namespace detail {
// dumpfn is a function abstracting prints to terminal (or to string).
template<class DumpFn, class ...Args>
C4_NO_INLINE void _dump(DumpFn &&dumpfn, csubstr fmt, Args&& ...args)
{
DumpResults results;
// try writing everything:
{
// buffer for converting individual arguments. it is defined
// in a child scope to free it in case the buffer is too small
// for any of the arguments.
char writebuf[RYML_LOGBUF_SIZE];
results = format_dump_resume(std::forward<DumpFn>(dumpfn), writebuf, fmt, std::forward<Args>(args)...);
}
// if any of the arguments failed to fit the buffer, allocate a
// larger buffer (up to a limit) and resume writing.
//
// results.bufsize is set to the size of the largest element
// serialized. Eg int(1) will require 1 byte.
if(C4_UNLIKELY(results.bufsize > RYML_LOGBUF_SIZE))
{
const size_t bufsize = results.bufsize <= RYML_LOGBUF_SIZE_MAX ? results.bufsize : RYML_LOGBUF_SIZE_MAX;
#ifdef C4_MSVC
substr largerbuf = {static_cast<char*>(_alloca(bufsize)), bufsize};
#else
substr largerbuf = {static_cast<char*>(alloca(bufsize)), bufsize};
#endif
results = format_dump_resume(std::forward<DumpFn>(dumpfn), results, largerbuf, fmt, std::forward<Args>(args)...);
}
}
template<class ...Args>
C4_NORETURN C4_NO_INLINE void _report_err(Callbacks const& C4_RESTRICT callbacks, csubstr fmt, Args const& C4_RESTRICT ...args)
{
char errmsg[RYML_ERRMSG_SIZE] = {0};
detail::_SubstrWriter writer(errmsg);
auto dumpfn = [&writer](csubstr s){ writer.append(s); };
_dump(dumpfn, fmt, args...);
writer.append('\n');
const size_t len = writer.pos < RYML_ERRMSG_SIZE ? writer.pos : RYML_ERRMSG_SIZE;
callbacks.m_error(errmsg, len, {}, callbacks.m_user_data);
C4_UNREACHABLE_AFTER_ERR();
}
} // namespace detail
inline csubstr _c4prc(const char &C4_RESTRICT c) // pass by reference!
{
switch(c)
{
case '\n': return csubstr("\\n");
case '\t': return csubstr("\\t");
case '\0': return csubstr("\\0");
case '\r': return csubstr("\\r");
case '\f': return csubstr("\\f");
case '\b': return csubstr("\\b");
case '\v': return csubstr("\\v");
case '\a': return csubstr("\\a");
default: return csubstr(&c, 1);
}
}
/// @endcond
C4_SUPPRESS_WARNING_GCC_CLANG_POP
C4_SUPPRESS_WARNING_GCC_POP
} // namespace yml
} // namespace c4

View File

@@ -17,7 +17,7 @@ namespace c4 {
namespace yml {
void check_invariants(Tree const& t, size_t node=NONE);
void check_invariants(Tree const& t, id_type node=NONE);
void check_free_list(Tree const& t);
void check_arena(Tree const& t);
@@ -26,16 +26,16 @@ void check_arena(Tree const& t);
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline void check_invariants(Tree const& t, size_t node)
inline void check_invariants(Tree const& t, id_type node)
{
if(node == NONE)
{
if(t.size() == 0) return;
if(t.empty()) return;
node = t.root_id();
}
auto const& n = *t._p(node);
#ifdef RYML_DBG
NodeData const& n = *t._p(node);
#if defined(RYML_DBG) && 0
if(n.m_first_child != NONE || n.m_last_child != NONE)
{
printf("check(%zu): fc=%zu lc=%zu\n", node, n.m_first_child, n.m_last_child);
@@ -100,10 +100,10 @@ inline void check_invariants(Tree const& t, size_t node)
C4_CHECK(t._p(n.m_next_sibling)->m_next_sibling != node);
}
size_t count = 0;
for(size_t i = n.m_first_child; i != NONE; i = t.next_sibling(i))
id_type count = 0;
for(id_type i = n.m_first_child; i != NONE; i = t.next_sibling(i))
{
#ifdef RYML_DBG
#if defined(RYML_DBG) && 0
printf("check(%zu): descend to child[%zu]=%zu\n", node, count, i);
#endif
auto const& ch = *t._p(i);
@@ -131,7 +131,7 @@ inline void check_invariants(Tree const& t, size_t node)
check_arena(t);
}
for(size_t i = t.first_child(node); i != NONE; i = t.next_sibling(i))
for(id_type i = t.first_child(node); i != NONE; i = t.next_sibling(i))
{
check_invariants(t, i);
}
@@ -159,8 +159,8 @@ inline void check_free_list(Tree const& t)
//C4_CHECK(head.m_prev_sibling == NONE);
//C4_CHECK(tail.m_next_sibling == NONE);
size_t count = 0;
for(size_t i = t.m_free_head, prev = NONE; i != NONE; i = t._p(i)->m_next_sibling)
id_type count = 0;
for(id_type i = t.m_free_head, prev = NONE; i != NONE; i = t._p(i)->m_next_sibling)
{
auto const& elm = *t._p(i);
if(&elm != &head)

View File

@@ -4,135 +4,144 @@
#ifndef _C4_YML_COMMON_HPP_
#include "../common.hpp"
#endif
#ifdef RYML_DBG
#include <cstdio>
#endif
//-----------------------------------------------------------------------------
// some debugging scaffolds
#if defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable: 4068/*unknown pragma*/)
#endif
// NOLINTBEGIN(*)
C4_SUPPRESS_WARNING_GCC_CLANG_PUSH
C4_SUPPRESS_WARNING_MSVC_PUSH
C4_SUPPRESS_WARNING_MSVC(4068/*unknown pragma*/)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
//#pragma GCC diagnostic ignored "-Wpragma-system-header-outside-header"
#pragma GCC system_header
C4_SUPPRESS_WARNING_GCC("-Wunknown-pragmas")
C4_SUPPRESS_WARNING_CLANG("-Wgnu-zero-variadic-macro-arguments")
// NOLINTEND(*)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Werror"
#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
// some debugging scaffolds
#ifdef RYML_DBG
#include <c4/dump.hpp>
namespace c4 {
inline void _dbg_dumper(csubstr s) { fwrite(s.str, 1, s.len, stdout); };
template<class ...Args>
void _dbg_printf(c4::csubstr fmt, Args&& ...args)
{
static char writebuf[256];
auto results = c4::format_dump_resume<&_dbg_dumper>(writebuf, fmt, std::forward<Args>(args)...);
// resume writing if the results failed to fit the buffer
if(C4_UNLIKELY(results.bufsize > sizeof(writebuf))) // bufsize will be that of the largest element serialized. Eg int(1), will require 1 byte.
{
results = format_dump_resume<&_dbg_dumper>(results, writebuf, fmt, std::forward<Args>(args)...);
if(C4_UNLIKELY(results.bufsize > sizeof(writebuf)))
{
results = format_dump_resume<&_dbg_dumper>(results, writebuf, fmt, std::forward<Args>(args)...);
}
}
}
} // namespace c4
# define _c4dbgt(fmt, ...) this->_dbg ("{}:{}: " fmt , __FILE__, __LINE__, ## __VA_ARGS__)
# define _c4dbgpf(fmt, ...) _dbg_printf("{}:{}: " fmt "\n", __FILE__, __LINE__, ## __VA_ARGS__)
# define _c4dbgp(msg) _dbg_printf("{}:{}: " msg "\n", __FILE__, __LINE__ )
# define _c4dbgq(msg) _dbg_printf(msg "\n")
#ifndef RYML_DBG
# define _c4err(fmt, ...) \
do { if(c4::is_debugger_attached()) { C4_DEBUG_BREAK(); } \
this->_err("ERROR:\n" "{}:{}: " fmt, __FILE__, __LINE__, ## __VA_ARGS__); } while(0)
#else
this->_err("ERROR: " fmt, ## __VA_ARGS__)
# define _c4dbgt(fmt, ...)
# define _c4dbgpf(fmt, ...)
# define _c4dbgpf_(fmt, ...)
# define _c4dbgp(msg)
# define _c4dbgp_(msg)
# define _c4dbgq(msg)
# define _c4presc(...)
# define _c4prscalar(msg, scalar, keep_newlines)
#else
# define _c4err(fmt, ...) \
do { if(c4::is_debugger_attached()) { C4_DEBUG_BREAK(); } \
this->_err("ERROR: " fmt, ## __VA_ARGS__); } while(0)
#endif
do { RYML_DEBUG_BREAK(); this->_err("ERROR:\n" "{}:{}: " fmt, __FILE__, __LINE__, ## __VA_ARGS__); } while(0)
# define _c4dbgt(fmt, ...) do { if(_dbg_enabled()) { \
this->_dbg ("{}:{}: " fmt , __FILE__, __LINE__, ## __VA_ARGS__); } } while(0)
# define _c4dbgpf(fmt, ...) _dbg_printf("{}:{}: " fmt "\n", __FILE__, __LINE__, ## __VA_ARGS__)
# define _c4dbgpf_(fmt, ...) _dbg_printf("{}:{}: " fmt , __FILE__, __LINE__, ## __VA_ARGS__)
# define _c4dbgp(msg) _dbg_printf("{}:{}: " msg "\n", __FILE__, __LINE__ )
# define _c4dbgp_(msg) _dbg_printf("{}:{}: " msg , __FILE__, __LINE__ )
# define _c4dbgq(msg) _dbg_printf(msg "\n")
# define _c4presc(...) do { if(_dbg_enabled()) __c4presc(__VA_ARGS__); } while(0)
# define _c4prscalar(msg, scalar, keep_newlines) \
do { \
_c4dbgpf_("{}: [{}]~~~", msg, scalar.len); \
if(_dbg_enabled()) { \
__c4presc((scalar).str, (scalar).len, (keep_newlines)); \
} \
_c4dbgq("~~~"); \
} while(0)
#endif // RYML_DBG
#define _c4prsp(sp) sp
#define _c4presc(s) __c4presc(s.str, s.len)
inline c4::csubstr _c4prc(const char &C4_RESTRICT c)
//-----------------------------------------------------------------------------
#ifdef RYML_DBG
#include <c4/dump.hpp>
namespace c4 {
inline bool& _dbg_enabled() { static bool enabled = true; return enabled; }
inline void _dbg_set_enabled(bool yes) { _dbg_enabled() = yes; }
inline void _dbg_dumper(csubstr s)
{
switch(c)
if(s.str)
fwrite(s.str, 1, s.len, stdout);
}
inline substr _dbg_buf() noexcept
{
static char writebuf[2048];
return substr{writebuf, sizeof(writebuf)}; // g++-5 has trouble with return writebuf;
}
template<class ...Args>
C4_NO_INLINE void _dbg_printf(c4::csubstr fmt, Args const& ...args)
{
if(_dbg_enabled())
{
case '\n': return c4::csubstr("\\n");
case '\t': return c4::csubstr("\\t");
case '\0': return c4::csubstr("\\0");
case '\r': return c4::csubstr("\\r");
case '\f': return c4::csubstr("\\f");
case '\b': return c4::csubstr("\\b");
case '\v': return c4::csubstr("\\v");
case '\a': return c4::csubstr("\\a");
default: return c4::csubstr(&c, 1);
substr buf = _dbg_buf();
const size_t needed_size = c4::format_dump(&_dbg_dumper, buf, fmt, args...);
C4_CHECK(needed_size <= buf.len);
}
}
inline void __c4presc(const char *s, size_t len)
inline C4_NO_INLINE void __c4presc(const char *s, size_t len, bool keep_newlines=false)
{
RYML_ASSERT(s || !len);
size_t prev = 0;
for(size_t i = 0; i < len; ++i)
{
switch(s[i])
{
case '\n' : if(i > prev) { fwrite(s+prev, 1, i-prev, stdout); } putchar('\\'); putchar('n'); putchar('\n'); prev = i+1; break;
case '\t' : if(i > prev) { fwrite(s+prev, 1, i-prev, stdout); } putchar('\\'); putchar('t'); prev = i+1; break;
case '\0' : if(i > prev) { fwrite(s+prev, 1, i-prev, stdout); } putchar('\\'); putchar('0'); prev = i+1; break;
case '\r' : if(i > prev) { fwrite(s+prev, 1, i-prev, stdout); } putchar('\\'); putchar('r'); prev = i+1; break;
case '\f' : if(i > prev) { fwrite(s+prev, 1, i-prev, stdout); } putchar('\\'); putchar('f'); prev = i+1; break;
case '\b' : if(i > prev) { fwrite(s+prev, 1, i-prev, stdout); } putchar('\\'); putchar('b'); prev = i+1; break;
case '\v' : if(i > prev) { fwrite(s+prev, 1, i-prev, stdout); } putchar('\\'); putchar('v'); prev = i+1; break;
case '\a' : if(i > prev) { fwrite(s+prev, 1, i-prev, stdout); } putchar('\\'); putchar('a'); prev = i+1; break;
case '\x1b': if(i > prev) { fwrite(s+prev, 1, i-prev, stdout); } putchar('\\'); putchar('e'); prev = i+1; break;
case '\n' : _dbg_printf("{}{}{}", csubstr(s+prev, i-prev), csubstr("\\n"), csubstr(keep_newlines ? "\n":"")); prev = i+1; break;
case '\t' : _dbg_printf("{}{}", csubstr(s+prev, i-prev), csubstr("\\t")); prev = i+1; break;
case '\0' : _dbg_printf("{}{}", csubstr(s+prev, i-prev), csubstr("\\0")); prev = i+1; break;
case '\r' : _dbg_printf("{}{}", csubstr(s+prev, i-prev), csubstr("\\r")); prev = i+1; break;
case '\f' : _dbg_printf("{}{}", csubstr(s+prev, i-prev), csubstr("\\f")); prev = i+1; break;
case '\b' : _dbg_printf("{}{}", csubstr(s+prev, i-prev), csubstr("\\b")); prev = i+1; break;
case '\v' : _dbg_printf("{}{}", csubstr(s+prev, i-prev), csubstr("\\v")); prev = i+1; break;
case '\a' : _dbg_printf("{}{}", csubstr(s+prev, i-prev), csubstr("\\a")); prev = i+1; break;
case '\x1b': _dbg_printf("{}{}", csubstr(s+prev, i-prev), csubstr("\\x1b")); prev = i+1; break;
case -0x3e/*0xc2u*/:
if(i+1 < len)
{
if(s[i+1] == -0x60/*0xa0u*/)
{
if(i > prev) { fwrite(s+prev, 1, i-prev, stdout); } putchar('\\'); putchar('_'); prev = i+2; ++i;
_dbg_printf("{}{}", csubstr(s+prev, i-prev), csubstr("\\_")); prev = i+1;
}
else if(s[i+1] == -0x7b/*0x85u*/)
{
if(i > prev) { fwrite(s+prev, 1, i-prev, stdout); } putchar('\\'); putchar('N'); prev = i+2; ++i;
_dbg_printf("{}{}", csubstr(s+prev, i-prev), csubstr("\\N")); prev = i+1;
}
break;
}
break;
case -0x1e/*0xe2u*/:
if(i+2 < len && s[i+1] == -0x80/*0x80u*/)
{
if(s[i+2] == -0x58/*0xa8u*/)
{
if(i > prev) { fwrite(s+prev, 1, i-prev, stdout); } putchar('\\'); putchar('L'); prev = i+3; i += 2;
_dbg_printf("{}{}", csubstr(s+prev, i-prev), csubstr("\\L")); prev = i+1;
}
else if(s[i+2] == -0x57/*0xa9u*/)
{
if(i > prev) { fwrite(s+prev, 1, i-prev, stdout); } putchar('\\'); putchar('P'); prev = i+3; i += 2;
_dbg_printf("{}{}", csubstr(s+prev, i-prev), csubstr("\\P")); prev = i+1;
}
break;
}
break;
}
}
if(len > prev)
fwrite(s + prev, 1, len - prev, stdout);
_dbg_printf("{}", csubstr(s+prev, len-prev));
}
inline void __c4presc(csubstr s, bool keep_newlines=false)
{
__c4presc(s.str, s.len, keep_newlines);
}
} // namespace c4
#pragma clang diagnostic pop
#pragma GCC diagnostic pop
#if defined(_MSC_VER)
# pragma warning(pop)
#endif
#endif // RYML_DBG
C4_SUPPRESS_WARNING_GCC_CLANG_POP
C4_SUPPRESS_WARNING_MSVC_POP
#endif /* _C4_YML_DETAIL_PARSER_DBG_HPP_ */

View File

@@ -4,20 +4,76 @@
#include "c4/yml/tree.hpp"
#include "c4/yml/node.hpp"
#ifdef RYML_DBG
#define _c4dbg_tree(...) print_tree(__VA_ARGS__)
#define _c4dbg_node(...) print_tree(__VA_ARGS__)
#else
#define _c4dbg_tree(...)
#define _c4dbg_node(...)
#endif
namespace c4 {
namespace yml {
C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wold-style-cast")
C4_SUPPRESS_WARNING_GCC("-Wuseless-cast")
inline size_t print_node(Tree const& p, size_t node, int level, size_t count, bool print_children)
inline const char* _container_style_code(Tree const& p, id_type node)
{
printf("[%zd]%*s[%zd] %p", count, (2*level), "", node, (void const*)p.get(node));
if(p.is_container(node))
{
if(p._p(node)->m_type & (FLOW_SL|FLOW_ML))
{
return "[FLOW]";
}
if(p._p(node)->m_type & (BLOCK))
{
return "[BLCK]";
}
}
return "";
}
inline char _scalar_code(NodeType masked)
{
if(masked & (KEY_LITERAL|VAL_LITERAL))
return '|';
if(masked & (KEY_FOLDED|VAL_FOLDED))
return '>';
if(masked & (KEY_SQUO|VAL_SQUO))
return '\'';
if(masked & (KEY_DQUO|VAL_DQUO))
return '"';
if(masked & (KEY_PLAIN|VAL_PLAIN))
return '~';
return '@';
}
inline char _scalar_code_key(NodeType t)
{
return _scalar_code(t & KEY_STYLE);
}
inline char _scalar_code_val(NodeType t)
{
return _scalar_code(t & VAL_STYLE);
}
inline char _scalar_code_key(Tree const& p, id_type node)
{
return _scalar_code_key(p._p(node)->m_type);
}
inline char _scalar_code_val(Tree const& p, id_type node)
{
return _scalar_code_key(p._p(node)->m_type);
}
inline id_type print_node(Tree const& p, id_type node, int level, id_type count, bool print_children)
{
printf("[%zu]%*s[%zu] %p", (size_t)count, (2*level), "", (size_t)node, (void const*)p.get(node));
if(p.is_root(node))
{
printf(" [ROOT]");
}
printf(" %s:", p.type_str(node));
char typebuf[128];
csubstr typestr = p.type(node).type_str(typebuf);
RYML_CHECK(typestr.str);
printf(" %.*s", (int)typestr.len, typestr.str);
if(p.has_key(node))
{
if(p.has_key_anchor(node))
@@ -28,65 +84,47 @@ inline size_t print_node(Tree const& p, size_t node, int level, size_t count, bo
if(p.has_key_tag(node))
{
csubstr kt = p.key_tag(node);
csubstr k = p.key(node);
printf(" %.*s '%.*s'", (int)kt.len, kt.str, (int)k.len, k.str);
}
else
{
csubstr k = p.key(node);
printf(" '%.*s'", (int)k.len, k.str);
}
}
else
{
RYML_ASSERT( ! p.has_key_tag(node));
}
if(p.has_val(node))
{
if(p.has_val_tag(node))
{
csubstr vt = p.val_tag(node);
csubstr v = p.val(node);
printf(" %.*s '%.*s'", (int)vt.len, vt.str, (int)v.len, v.str);
}
else
{
csubstr v = p.val(node);
printf(" '%.*s'", (int)v.len, v.str);
}
}
else
{
if(p.has_val_tag(node))
{
csubstr vt = p.val_tag(node);
printf(" %.*s", (int)vt.len, vt.str);
printf(" <%.*s>", (int)kt.len, kt.str);
}
const char code = _scalar_code_key(p, node);
csubstr k = p.key(node);
printf(" %c%.*s%c :", code, (int)k.len, k.str, code);
}
if(p.has_val_anchor(node))
{
auto &a = p.val_anchor(node);
printf(" valanchor='&%.*s'", (int)a.len, a.str);
csubstr a = p.val_anchor(node);
printf(" &%.*s'", (int)a.len, a.str);
}
printf(" (%zd sibs)", p.num_siblings(node));
if(p.has_val_tag(node))
{
csubstr vt = p.val_tag(node);
printf(" <%.*s>", (int)vt.len, vt.str);
}
if(p.has_val(node))
{
const char code = _scalar_code_val(p, node);
csubstr v = p.val(node);
printf(" %c%.*s%c", code, (int)v.len, v.str, code);
}
printf(" (%zu sibs)", (size_t)p.num_siblings(node));
++count;
if(p.is_container(node))
if(!p.is_container(node))
{
printf(" %zd children:\n", p.num_children(node));
printf("\n");
}
else
{
printf(" (%zu children)\n", (size_t)p.num_children(node));
if(print_children)
{
for(size_t i = p.first_child(node); i != NONE; i = p.next_sibling(i))
for(id_type i = p.first_child(node); i != NONE; i = p.next_sibling(i))
{
count = print_node(p, i, level+1, count, print_children);
}
}
}
else
{
printf("\n");
}
return count;
}
@@ -106,21 +144,37 @@ inline void print_node(ConstNodeRef const& p, int level=0)
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline size_t print_tree(Tree const& p, size_t node=NONE)
inline id_type print_tree(const char *message, Tree const& p, id_type node=NONE)
{
printf("--------------------------------------\n");
size_t ret = 0;
if(message != nullptr)
printf("%s:\n", message);
id_type ret = 0;
if(!p.empty())
{
if(node == NONE)
node = p.root_id();
ret = print_node(p, node, 0, 0, true);
}
printf("#nodes=%zd vs #printed=%zd\n", p.size(), ret);
printf("#nodes=%zu vs #printed=%zu\n", (size_t)p.size(), (size_t)ret);
printf("--------------------------------------\n");
return ret;
}
inline id_type print_tree(Tree const& p, id_type node=NONE)
{
return print_tree(nullptr, p, node);
}
inline void print_tree(ConstNodeRef const& p, int level)
{
print_node(p, level);
for(ConstNodeRef ch : p.children())
{
print_tree(ch, level+1);
}
}
C4_SUPPRESS_WARNING_GCC_CLANG_POP
} /* namespace yml */

View File

@@ -18,22 +18,27 @@ C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wold-style-cast")
namespace detail {
/** A lightweight contiguous stack with SSO. This avoids a dependency on std. */
template<class T, size_t N=16>
/** A lightweight contiguous stack with Small Storage
* Optimization. This is required because std::vector can throw
* exceptions, and we don't want to enforce any particular error
* mechanism. */
template<class T, id_type N=16>
class stack
{
static_assert(std::is_trivially_copyable<T>::value, "T must be trivially copyable");
static_assert(std::is_trivially_destructible<T>::value, "T must be trivially destructible");
enum : size_t { sso_size = N };
public:
enum : id_type { sso_size = N };
public:
T m_buf[N];
T * m_stack;
size_t m_size;
size_t m_capacity;
Callbacks m_callbacks;
T m_buf[size_t(N)];
T *C4_RESTRICT m_stack;
id_type m_size;
id_type m_capacity;
Callbacks m_callbacks;
public:
@@ -64,9 +69,12 @@ public:
stack& operator= (stack const& that) RYML_NOEXCEPT
{
_cb(that.m_callbacks);
resize(that.m_size);
_cp(&that);
if(&that != this)
{
_cb(that.m_callbacks);
resize(that.m_size);
_cp(&that);
}
return *this;
}
@@ -79,29 +87,29 @@ public:
public:
size_t size() const { return m_size; }
size_t empty() const { return m_size == 0; }
size_t capacity() const { return m_capacity; }
id_type size() const { return m_size; }
id_type empty() const { return m_size == 0; }
id_type capacity() const { return m_capacity; }
void clear()
{
m_size = 0;
}
void resize(size_t sz)
void resize(id_type sz)
{
reserve(sz);
m_size = sz;
}
void reserve(size_t sz);
void reserve(id_type sz);
void push(T const& C4_RESTRICT n)
{
RYML_ASSERT((const char*)&n + sizeof(T) < (const char*)m_stack || &n > m_stack + m_capacity);
_RYML_CB_ASSERT(m_callbacks, (const char*)&n + sizeof(T) < (const char*)m_stack || &n > m_stack + m_capacity);
if(m_size == m_capacity)
{
size_t cap = m_capacity == 0 ? N : 2 * m_capacity;
id_type cap = m_capacity == 0 ? N : 2 * m_capacity;
reserve(cap);
}
m_stack[m_size] = n;
@@ -110,10 +118,10 @@ public:
void push_top()
{
RYML_ASSERT(m_size > 0);
_RYML_CB_ASSERT(m_callbacks, m_size > 0);
if(m_size == m_capacity)
{
size_t cap = m_capacity == 0 ? N : 2 * m_capacity;
id_type cap = m_capacity == 0 ? N : 2 * m_capacity;
reserve(cap);
}
m_stack[m_size] = m_stack[m_size - 1];
@@ -122,25 +130,25 @@ public:
T const& C4_RESTRICT pop()
{
RYML_ASSERT(m_size > 0);
_RYML_CB_ASSERT(m_callbacks, m_size > 0);
--m_size;
return m_stack[m_size];
}
C4_ALWAYS_INLINE T const& C4_RESTRICT top() const { RYML_ASSERT(m_size > 0); return m_stack[m_size - 1]; }
C4_ALWAYS_INLINE T & C4_RESTRICT top() { RYML_ASSERT(m_size > 0); return m_stack[m_size - 1]; }
C4_ALWAYS_INLINE T const& C4_RESTRICT top() const { _RYML_CB_ASSERT(m_callbacks, m_size > 0); return m_stack[m_size - 1]; }
C4_ALWAYS_INLINE T & C4_RESTRICT top() { _RYML_CB_ASSERT(m_callbacks, m_size > 0); return m_stack[m_size - 1]; }
C4_ALWAYS_INLINE T const& C4_RESTRICT bottom() const { RYML_ASSERT(m_size > 0); return m_stack[0]; }
C4_ALWAYS_INLINE T & C4_RESTRICT bottom() { RYML_ASSERT(m_size > 0); return m_stack[0]; }
C4_ALWAYS_INLINE T const& C4_RESTRICT bottom() const { _RYML_CB_ASSERT(m_callbacks, m_size > 0); return m_stack[0]; }
C4_ALWAYS_INLINE T & C4_RESTRICT bottom() { _RYML_CB_ASSERT(m_callbacks, m_size > 0); return m_stack[0]; }
C4_ALWAYS_INLINE T const& C4_RESTRICT top(size_t i) const { RYML_ASSERT(i < m_size); return m_stack[m_size - 1 - i]; }
C4_ALWAYS_INLINE T & C4_RESTRICT top(size_t i) { RYML_ASSERT(i < m_size); return m_stack[m_size - 1 - i]; }
C4_ALWAYS_INLINE T const& C4_RESTRICT top(id_type i) const { _RYML_CB_ASSERT(m_callbacks, i < m_size); return m_stack[m_size - 1 - i]; }
C4_ALWAYS_INLINE T & C4_RESTRICT top(id_type i) { _RYML_CB_ASSERT(m_callbacks, i < m_size); return m_stack[m_size - 1 - i]; }
C4_ALWAYS_INLINE T const& C4_RESTRICT bottom(size_t i) const { RYML_ASSERT(i < m_size); return m_stack[i]; }
C4_ALWAYS_INLINE T & C4_RESTRICT bottom(size_t i) { RYML_ASSERT(i < m_size); return m_stack[i]; }
C4_ALWAYS_INLINE T const& C4_RESTRICT bottom(id_type i) const { _RYML_CB_ASSERT(m_callbacks, i < m_size); return m_stack[i]; }
C4_ALWAYS_INLINE T & C4_RESTRICT bottom(id_type i) { _RYML_CB_ASSERT(m_callbacks, i < m_size); return m_stack[i]; }
C4_ALWAYS_INLINE T const& C4_RESTRICT operator[](size_t i) const { RYML_ASSERT(i < m_size); return m_stack[i]; }
C4_ALWAYS_INLINE T & C4_RESTRICT operator[](size_t i) { RYML_ASSERT(i < m_size); return m_stack[i]; }
C4_ALWAYS_INLINE T const& C4_RESTRICT operator[](id_type i) const { _RYML_CB_ASSERT(m_callbacks, i < m_size); return m_stack[i]; }
C4_ALWAYS_INLINE T & C4_RESTRICT operator[](id_type i) { _RYML_CB_ASSERT(m_callbacks, i < m_size); return m_stack[i]; }
public:
@@ -154,10 +162,12 @@ public:
const_iterator end () const { return (const_iterator)m_stack + m_size; }
public:
void _free();
void _cp(stack const* C4_RESTRICT that);
void _mv(stack * that);
void _cb(Callbacks const& cb);
};
@@ -165,8 +175,8 @@ public:
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
template<class T, size_t N>
void stack<T, N>::reserve(size_t sz)
template<class T, id_type N>
void stack<T, N>::reserve(id_type sz)
{
if(sz <= m_size)
return;
@@ -176,11 +186,12 @@ void stack<T, N>::reserve(size_t sz)
m_capacity = N;
return;
}
T *buf = (T*) m_callbacks.m_allocate(sz * sizeof(T), m_stack, m_callbacks.m_user_data);
memcpy(buf, m_stack, m_size * sizeof(T));
T *buf = (T*) m_callbacks.m_allocate((size_t)sz * sizeof(T), m_stack, m_callbacks.m_user_data);
_RYML_CB_ASSERT(m_callbacks, ((uintptr_t)buf % alignof(T)) == 0u);
memcpy(buf, m_stack, (size_t)m_size * sizeof(T));
if(m_stack != m_buf)
{
m_callbacks.m_free(m_stack, m_capacity * sizeof(T), m_callbacks.m_user_data);
m_callbacks.m_free(m_stack, (size_t)m_capacity * sizeof(T), m_callbacks.m_user_data);
}
m_stack = buf;
m_capacity = sz;
@@ -189,38 +200,38 @@ void stack<T, N>::reserve(size_t sz)
//-----------------------------------------------------------------------------
template<class T, size_t N>
template<class T, id_type N>
void stack<T, N>::_free()
{
RYML_ASSERT(m_stack != nullptr); // this structure cannot be memset() to zero
_RYML_CB_ASSERT(m_callbacks, m_stack != nullptr); // this structure cannot be memset() to zero
if(m_stack != m_buf)
{
m_callbacks.m_free(m_stack, m_capacity * sizeof(T), m_callbacks.m_user_data);
m_callbacks.m_free(m_stack, (size_t)m_capacity * sizeof(T), m_callbacks.m_user_data);
m_stack = m_buf;
m_size = N;
m_capacity = N;
}
else
{
RYML_ASSERT(m_capacity == N);
_RYML_CB_ASSERT(m_callbacks, m_capacity == N);
}
}
//-----------------------------------------------------------------------------
template<class T, size_t N>
template<class T, id_type N>
void stack<T, N>::_cp(stack const* C4_RESTRICT that)
{
if(that->m_stack != that->m_buf)
{
RYML_ASSERT(that->m_capacity > N);
RYML_ASSERT(that->m_size <= that->m_capacity);
_RYML_CB_ASSERT(m_callbacks, that->m_capacity > N);
_RYML_CB_ASSERT(m_callbacks, that->m_size <= that->m_capacity);
}
else
{
RYML_ASSERT(that->m_capacity <= N);
RYML_ASSERT(that->m_size <= that->m_capacity);
_RYML_CB_ASSERT(m_callbacks, that->m_capacity <= N);
_RYML_CB_ASSERT(m_callbacks, that->m_size <= that->m_capacity);
}
memcpy(m_stack, that->m_stack, that->m_size * sizeof(T));
m_size = that->m_size;
@@ -231,19 +242,19 @@ void stack<T, N>::_cp(stack const* C4_RESTRICT that)
//-----------------------------------------------------------------------------
template<class T, size_t N>
template<class T, id_type N>
void stack<T, N>::_mv(stack * that)
{
if(that->m_stack != that->m_buf)
{
RYML_ASSERT(that->m_capacity > N);
RYML_ASSERT(that->m_size <= that->m_capacity);
_RYML_CB_ASSERT(m_callbacks, that->m_capacity > N);
_RYML_CB_ASSERT(m_callbacks, that->m_size <= that->m_capacity);
m_stack = that->m_stack;
}
else
{
RYML_ASSERT(that->m_capacity <= N);
RYML_ASSERT(that->m_size <= that->m_capacity);
_RYML_CB_ASSERT(m_callbacks, that->m_capacity <= N);
_RYML_CB_ASSERT(m_callbacks, that->m_size <= that->m_capacity);
memcpy(m_buf, that->m_buf, that->m_size * sizeof(T));
m_stack = m_buf;
}
@@ -251,7 +262,7 @@ void stack<T, N>::_mv(stack * that)
m_capacity = that->m_capacity;
m_callbacks = that->m_callbacks;
// make sure no deallocation happens on destruction
RYML_ASSERT(that->m_stack != m_buf);
_RYML_CB_ASSERT(m_callbacks, that->m_stack != m_buf);
that->m_stack = that->m_buf;
that->m_capacity = N;
that->m_size = 0;
@@ -260,7 +271,7 @@ void stack<T, N>::_mv(stack * that)
//-----------------------------------------------------------------------------
template<class T, size_t N>
template<class T, id_type N>
void stack<T, N>::_cb(Callbacks const& cb)
{
if(cb != m_callbacks)

File diff suppressed because it is too large Load Diff

View File

@@ -15,13 +15,8 @@
#include "./node.hpp"
#endif
#ifdef emit
#error "emit is defined, likely from a Qt include. This will cause a compilation error. See https://github.com/biojppm/rapidyaml/issues/120"
#endif
#define RYML_DEPRECATE_EMIT \
RYML_DEPRECATED("use emit_yaml() instead. See https://github.com/biojppm/rapidyaml/issues/120")
#define RYML_DEPRECATE_EMITRS \
RYML_DEPRECATED("use emitrs_yaml() instead. See https://github.com/biojppm/rapidyaml/issues/120")
C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wold-style-cast")
//-----------------------------------------------------------------------------
@@ -43,6 +38,15 @@ using EmitterOStream = Emitter<WriterOStream<OStream>>;
using EmitterFile = Emitter<WriterFile>;
using EmitterBuf = Emitter<WriterBuf>;
namespace detail {
inline bool is_set_(ConstNodeRef n) { return n.tree() && (n.id() != NONE); }
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** Specifies the type of content to emit */
typedef enum {
EMIT_YAML = 0, ///< emit YAML
@@ -50,6 +54,59 @@ typedef enum {
} EmitType_e;
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** A lightweight object containing options to be used when emitting. */
struct EmitOptions
{
typedef enum : uint32_t {
DEFAULT_FLAGS = 0u,
JSON_ERR_ON_TAG = 1u << 0u,
JSON_ERR_ON_ANCHOR = 1u << 1u,
_JSON_ERR_MASK = JSON_ERR_ON_TAG|JSON_ERR_ON_ANCHOR,
} EmitOptionFlags_e;
public:
/** @name option flags
*
* @{ */
C4_ALWAYS_INLINE EmitOptionFlags_e json_error_flags() const noexcept { return m_option_flags; }
EmitOptions& json_error_flags(EmitOptionFlags_e d) noexcept { m_option_flags = (EmitOptionFlags_e)(d & _JSON_ERR_MASK); return *this; }
/** @} */
public:
/** @name max depth for the emitted tree
*
* This makes the emitter fail when emitting trees exceeding the
* max_depth.
*
* @{ */
C4_ALWAYS_INLINE id_type max_depth() const noexcept { return m_max_depth; }
EmitOptions& max_depth(id_type d) noexcept { m_max_depth = d; return *this; }
static constexpr const id_type max_depth_default = 64;
/** @} */
public:
bool operator== (const EmitOptions& that) const noexcept
{
return m_max_depth == that.m_max_depth &&
m_option_flags == that.m_option_flags;
}
private:
/** @cond dev */
id_type m_max_depth{max_depth_default};
EmitOptionFlags_e m_option_flags{DEFAULT_FLAGS};
/** @endcond */
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
@@ -61,12 +118,21 @@ class Emitter : public Writer
{
public:
/** Construct the emitter and its internal Writer state. Every
* parameter is forwarded to the constructor of the writer. */
/** Construct the emitter and its internal Writer state, using default emit options.
* @param args arguments to be forwarded to the constructor of the writer.
* */
template<class ...Args>
Emitter(Args &&...args) : Writer(std::forward<Args>(args)...), m_tree() {}
/** emit!
Emitter(Args &&...args) : Writer(std::forward<Args>(args)...), m_tree(), m_opts(), m_flow(false) {}
/** Construct the emitter and its internal Writer state.
*
* @param opts EmitOptions
* @param args arguments to be forwarded to the constructor of the writer.
* */
template<class ...Args>
Emitter(EmitOptions const& opts, Args &&...args) : Writer(std::forward<Args>(args)...), m_tree(), m_opts(opts), m_flow(false) {}
/** emit!
*
* When writing to a buffer, returns a substr of the emitted YAML.
* If the given buffer has insufficient space, the returned substr
@@ -83,36 +149,63 @@ public:
* @param error_on_excess when true, an error is raised when the
* output buffer is too small for the emitted YAML/JSON
* */
substr emit_as(EmitType_e type, Tree const& t, size_t id, bool error_on_excess);
substr emit_as(EmitType_e type, Tree const& t, id_type id, bool error_on_excess);
/** emit starting at the root node */
substr emit_as(EmitType_e type, Tree const& t, bool error_on_excess=true);
/** emit the given node */
substr emit_as(EmitType_e type, ConstNodeRef const& n, bool error_on_excess=true);
substr emit_as(EmitType_e type, Tree const& t, bool error_on_excess=true)
{
if(t.empty())
return {};
return this->emit_as(type, t, t.root_id(), error_on_excess);
}
/** emit starting at the given node */
substr emit_as(EmitType_e type, ConstNodeRef const& n, bool error_on_excess=true)
{
if(!detail::is_set_(n))
return {};
_RYML_CB_CHECK(n.tree()->callbacks(), n.readable());
return this->emit_as(type, *n.tree(), n.id(), error_on_excess);
}
public:
/** get the emit options for this object */
EmitOptions const& options() const noexcept { return m_opts; }
/** set the max depth for emitted trees (to prevent a stack overflow) */
void max_depth(id_type max_depth) noexcept { m_opts.max_depth(max_depth); }
/** get the max depth for emitted trees (to prevent a stack overflow) */
id_type max_depth() const noexcept { return m_opts.max_depth(); }
private:
Tree const* C4_RESTRICT m_tree;
void _emit_yaml(size_t id);
void _do_visit_flow_sl(size_t id, size_t ilevel=0);
void _do_visit_flow_ml(size_t id, size_t ilevel=0, size_t do_indent=1);
void _do_visit_block(size_t id, size_t ilevel=0, size_t do_indent=1);
void _do_visit_block_container(size_t id, size_t next_level, size_t do_indent);
void _do_visit_json(size_t id);
EmitOptions m_opts;
bool m_flow;
private:
void _write(NodeScalar const& C4_RESTRICT sc, NodeType flags, size_t level);
void _emit_yaml(id_type id);
void _do_visit_flow_sl(id_type id, id_type depth, id_type ilevel=0);
void _do_visit_flow_ml(id_type id, id_type depth, id_type ilevel=0, id_type do_indent=1);
void _do_visit_block(id_type id, id_type depth, id_type ilevel=0, id_type do_indent=1);
void _do_visit_block_container(id_type id, id_type depth, id_type next_level, bool do_indent);
void _do_visit_json(id_type id, id_type depth);
private:
void _write(NodeScalar const& C4_RESTRICT sc, NodeType flags, id_type level);
void _write_json(NodeScalar const& C4_RESTRICT sc, NodeType flags);
void _write_doc(size_t id);
void _write_scalar(csubstr s, bool was_quoted);
void _write_scalar_json(csubstr s, bool as_key, bool was_quoted);
void _write_scalar_literal(csubstr s, size_t level, bool as_key, bool explicit_indentation=false);
void _write_scalar_folded(csubstr s, size_t level, bool as_key);
void _write_scalar_squo(csubstr s, size_t level);
void _write_scalar_dquo(csubstr s, size_t level);
void _write_scalar_plain(csubstr s, size_t level);
void _write_doc(id_type id);
void _write_scalar_json_dquo(csubstr s);
void _write_scalar_literal(csubstr s, id_type level, bool as_key);
void _write_scalar_folded(csubstr s, id_type level, bool as_key);
void _write_scalar_squo(csubstr s, id_type level);
void _write_scalar_dquo(csubstr s, id_type level);
void _write_scalar_plain(csubstr s, id_type level);
size_t _write_escaped_newlines(csubstr s, size_t i);
size_t _write_indented_block(csubstr s, size_t i, id_type level);
void _write_tag(csubstr tag)
{
@@ -122,18 +215,28 @@ private:
}
enum : type_bits {
_keysc = (KEY|KEYREF|KEYANCH|KEYQUO|_WIP_KEY_STYLE) | ~(VAL|VALREF|VALANCH|VALQUO|_WIP_VAL_STYLE),
_valsc = ~(KEY|KEYREF|KEYANCH|KEYQUO|_WIP_KEY_STYLE) | (VAL|VALREF|VALANCH|VALQUO|_WIP_VAL_STYLE),
_keysc = (KEY|KEYREF|KEYANCH|KEYQUO|KEY_STYLE) | ~(VAL|VALREF|VALANCH|VALQUO|VAL_STYLE) | CONTAINER_STYLE,
_valsc = ~(KEY|KEYREF|KEYANCH|KEYQUO|KEY_STYLE) | (VAL|VALREF|VALANCH|VALQUO|VAL_STYLE) | CONTAINER_STYLE,
_keysc_json = (KEY) | ~(VAL),
_valsc_json = ~(KEY) | (VAL),
};
C4_ALWAYS_INLINE void _writek(size_t id, size_t level) { _write(m_tree->keysc(id), m_tree->_p(id)->m_type.type & ~_valsc, level); }
C4_ALWAYS_INLINE void _writev(size_t id, size_t level) { _write(m_tree->valsc(id), m_tree->_p(id)->m_type.type & ~_keysc, level); }
C4_ALWAYS_INLINE void _writek(id_type id, id_type level) { _write(m_tree->keysc(id), (m_tree->_p(id)->m_type.type & ~_valsc), level); }
C4_ALWAYS_INLINE void _writev(id_type id, id_type level) { _write(m_tree->valsc(id), (m_tree->_p(id)->m_type.type & ~_keysc), level); }
C4_ALWAYS_INLINE void _writek_json(size_t id) { _write_json(m_tree->keysc(id), m_tree->_p(id)->m_type.type & ~(VAL)); }
C4_ALWAYS_INLINE void _writev_json(size_t id) { _write_json(m_tree->valsc(id), m_tree->_p(id)->m_type.type & ~(KEY)); }
C4_ALWAYS_INLINE void _writek_json(id_type id) { _write_json(m_tree->keysc(id), m_tree->_p(id)->m_type.type & ~(VAL)); }
C4_ALWAYS_INLINE void _writev_json(id_type id) { _write_json(m_tree->valsc(id), m_tree->_p(id)->m_type.type & ~(KEY)); }
void _indent(id_type level, bool enabled)
{
if(enabled)
this->Writer::_do_write(' ', 2u * (size_t)level);
}
void _indent(id_type level)
{
if(!m_flow)
this->Writer::_do_write(' ', 2u * (size_t)level);
}
};
@@ -147,33 +250,59 @@ private:
*/
/** emit YAML to the given file. A null file defaults to stdout.
* Return the number of bytes written. */
inline size_t emit_yaml(Tree const& t, size_t id, FILE *f)
// emit from tree and node id -----------------------
/** (1) emit YAML to the given file, starting at the given node. A null
* file defaults to stdout. Return the number of bytes written. */
inline size_t emit_yaml(Tree const& t, id_type id, EmitOptions const& opts, FILE *f)
{
EmitterFile em(opts, f);
return em.emit_as(EMIT_YAML, t, id, /*error_on_excess*/true).len;
}
/** (2) like (1), but use default emit options */
inline size_t emit_yaml(Tree const& t, id_type id, FILE *f)
{
EmitterFile em(f);
return em.emit_as(EMIT_YAML, t, id, /*error_on_excess*/true).len;
}
/** emit JSON to the given file. A null file defaults to stdout.
* Return the number of bytes written. */
inline size_t emit_json(Tree const& t, size_t id, FILE *f)
/** (1) emit JSON to the given file, starting at the given node. A null
* file defaults to stdout. Return the number of bytes written. */
inline size_t emit_json(Tree const& t, id_type id, EmitOptions const& opts, FILE *f)
{
EmitterFile em(opts, f);
return em.emit_as(EMIT_JSON, t, id, /*error_on_excess*/true).len;
}
/** (2) like (1), but use default emit options */
inline size_t emit_json(Tree const& t, id_type id, FILE *f)
{
EmitterFile em(f);
return em.emit_as(EMIT_JSON, t, id, /*error_on_excess*/true).len;
}
/** emit YAML to the given file. A null file defaults to stdout.
* Return the number of bytes written.
* @overload */
// emit from root -------------------------
/** (1) emit YAML to the given file, starting at the root node. A null file defaults to stdout.
* Return the number of bytes written. */
inline size_t emit_yaml(Tree const& t, EmitOptions const& opts, FILE *f=nullptr)
{
EmitterFile em(opts, f);
return em.emit_as(EMIT_YAML, t, /*error_on_excess*/true).len;
}
/** (2) like (1), but use default emit options */
inline size_t emit_yaml(Tree const& t, FILE *f=nullptr)
{
EmitterFile em(f);
return em.emit_as(EMIT_YAML, t, /*error_on_excess*/true).len;
}
/** emit JSON to the given file. A null file defaults to stdout.
* Return the number of bytes written.
* @overload */
/** (1) emit JSON to the given file. A null file defaults to stdout.
* Return the number of bytes written. */
inline size_t emit_json(Tree const& t, EmitOptions const& opts, FILE *f=nullptr)
{
EmitterFile em(opts, f);
return em.emit_as(EMIT_JSON, t, /*error_on_excess*/true).len;
}
/** (2) like (1), but use default emit options */
inline size_t emit_json(Tree const& t, FILE *f=nullptr)
{
EmitterFile em(f);
@@ -181,19 +310,39 @@ inline size_t emit_json(Tree const& t, FILE *f=nullptr)
}
/** emit YAML to the given file. A null file defaults to stdout.
* Return the number of bytes written.
* @overload */
// emit from ConstNodeRef ------------------------
/** (1) emit YAML to the given file. A null file defaults to stdout.
* Return the number of bytes written. */
inline size_t emit_yaml(ConstNodeRef const& r, EmitOptions const& opts, FILE *f=nullptr)
{
if(!detail::is_set_(r))
return {};
EmitterFile em(opts, f);
return em.emit_as(EMIT_YAML, r, /*error_on_excess*/true).len;
}
/** (2) like (1), but use default emit options */
inline size_t emit_yaml(ConstNodeRef const& r, FILE *f=nullptr)
{
if(!detail::is_set_(r))
return {};
EmitterFile em(f);
return em.emit_as(EMIT_YAML, r, /*error_on_excess*/true).len;
}
/** emit JSON to the given file. A null file defaults to stdout.
* Return the number of bytes written.
* @overload */
/** (1) emit JSON to the given file. A null file defaults to stdout.
* Return the number of bytes written. */
inline size_t emit_json(ConstNodeRef const& r, EmitOptions const& opts, FILE *f=nullptr)
{
if(!detail::is_set_(r))
return {};
EmitterFile em(opts, f);
return em.emit_as(EMIT_JSON, r, /*error_on_excess*/true).len;
}
/** (2) like (1), but use default emit options */
inline size_t emit_json(ConstNodeRef const& r, FILE *f=nullptr)
{
if(!detail::is_set_(r))
return {};
EmitterFile em(f);
return em.emit_as(EMIT_JSON, r, /*error_on_excess*/true).len;
}
@@ -208,24 +357,6 @@ inline size_t emit_json(ConstNodeRef const& r, FILE *f=nullptr)
* @{
*/
/** mark a tree or node to be emitted as json when using @ref operator<< . For example:
*
* ```cpp
* Tree t = parse_in_arena("{foo: bar}");
* std::cout << t; // emits YAML
* std::cout << as_json(t); // emits JSON
* ```
*
* @see @ref operator<< */
struct as_json
{
Tree const* tree;
size_t node;
as_json(Tree const& t) : tree(&t), node(t.empty() ? NONE : t.root_id()) {}
as_json(Tree const& t, size_t id) : tree(&t), node(id) {}
as_json(ConstNodeRef const& n) : tree(n.tree()), node(n.id()) {}
};
/** emit YAML to an STL-like ostream */
template<class OStream>
inline OStream& operator<< (OStream& s, Tree const& t)
@@ -240,20 +371,77 @@ inline OStream& operator<< (OStream& s, Tree const& t)
template<class OStream>
inline OStream& operator<< (OStream& s, ConstNodeRef const& n)
{
if(!detail::is_set_(n))
return s;
EmitterOStream<OStream> em(s);
em.emit_as(EMIT_YAML, n);
return s;
}
/** mark a tree or node to be emitted as yaml when using @ref
* operator<<, with options. For example:
*
* ```cpp
* Tree t = parse_in_arena("{foo: bar}");
* std::cout << t; // emits YAML
* std::cout << as_yaml(t); // emits YAML, same as above
* std::cout << as_yaml(t, EmitOptions().max_depth(10)); // emits JSON with a max tree depth
* ```
*
* @see @ref operator<< */
struct as_json
{
Tree const* tree;
size_t node;
EmitOptions options;
as_json(Tree const& t, EmitOptions const& opts={}) : tree(&t), node(t.empty() ? NONE : t.root_id()), options(opts) {}
as_json(Tree const& t, size_t id, EmitOptions const& opts={}) : tree(&t), node(id), options(opts) {}
as_json(ConstNodeRef const& n, EmitOptions const& opts={}) : tree(n.tree()), node(n.id()), options(opts) {}
};
/** mark a tree or node to be emitted as yaml when using @ref
* operator<< . For example:
*
* ```cpp
* Tree t = parse_in_arena("{foo: bar}");
* std::cout << t; // emits YAML
* std::cout << as_json(t); // emits JSON
* std::cout << as_json(t, EmitOptions().max_depth(10)); // emits JSON with a max tree depth
* ```
*
* @see @ref operator<< */
struct as_yaml
{
Tree const* tree;
size_t node;
EmitOptions options;
as_yaml(Tree const& t, EmitOptions const& opts={}) : tree(&t), node(t.empty() ? NONE : t.root_id()), options(opts) {}
as_yaml(Tree const& t, size_t id, EmitOptions const& opts={}) : tree(&t), node(id), options(opts) {}
as_yaml(ConstNodeRef const& n, EmitOptions const& opts={}) : tree(n.tree()), node(n.id()), options(opts) {}
};
/** emit json to an STL-like stream */
template<class OStream>
inline OStream& operator<< (OStream& s, as_json const& j)
{
EmitterOStream<OStream> em(s);
if(!j.tree || j.node == NONE)
return s;
EmitterOStream<OStream> em(j.options, s);
em.emit_as(EMIT_JSON, *j.tree, j.node, true);
return s;
}
/** emit yaml to an STL-like stream */
template<class OStream>
inline OStream& operator<< (OStream& s, as_yaml const& y)
{
if(!y.tree || y.node == NONE)
return s;
EmitterOStream<OStream> em(y.options, s);
em.emit_as(EMIT_YAML, *y.tree, y.node, true);
return s;
}
/** @} */
@@ -264,35 +452,85 @@ inline OStream& operator<< (OStream& s, as_json const& j)
* @{
*/
/** emit YAML to the given buffer. Return a substr trimmed to the emitted YAML.
// emit from tree and node id -----------------------
/** (1) emit YAML to the given buffer. Return a substr trimmed to the emitted YAML.
* @param t the tree to emit.
* @param id the node where to start emitting.
* @param opts emit options.
* @param buf the output buffer.
* @param error_on_excess Raise an error if the space in the buffer is insufficient.
* @overload */
inline substr emit_yaml(Tree const& t, size_t id, substr buf, bool error_on_excess=true)
* @return a substr trimmed to the result in the output buffer. If the buffer is
* insufficient (when error_on_excess is false), the string pointer of the
* result will be set to null, and the length will report the required buffer size. */
inline substr emit_yaml(Tree const& t, id_type id, EmitOptions const& opts, substr buf, bool error_on_excess=true)
{
EmitterBuf em(opts, buf);
return em.emit_as(EMIT_YAML, t, id, error_on_excess);
}
/** (2) like (1), but use default emit options */
inline substr emit_yaml(Tree const& t, id_type id, substr buf, bool error_on_excess=true)
{
EmitterBuf em(buf);
return em.emit_as(EMIT_YAML, t, id, error_on_excess);
}
/** emit JSON to the given buffer. Return a substr trimmed to the emitted JSON.
/** (1) emit JSON to the given buffer. Return a substr trimmed to the emitted JSON.
* @param t the tree to emit.
* @param id the node where to start emitting.
* @param opts emit options.
* @param buf the output buffer.
* @param error_on_excess Raise an error if the space in the buffer is insufficient.
* @overload */
inline substr emit_json(Tree const& t, size_t id, substr buf, bool error_on_excess=true)
* @return a substr trimmed to the result in the output buffer. If the buffer is
* insufficient (when error_on_excess is false), the string pointer of the
* result will be set to null, and the length will report the required buffer size. */
inline substr emit_json(Tree const& t, id_type id, EmitOptions const& opts, substr buf, bool error_on_excess=true)
{
EmitterBuf em(opts, buf);
return em.emit_as(EMIT_JSON, t, id, error_on_excess);
}
/** (2) like (1), but use default emit options */
inline substr emit_json(Tree const& t, id_type id, substr buf, bool error_on_excess=true)
{
EmitterBuf em(buf);
return em.emit_as(EMIT_JSON, t, id, error_on_excess);
}
/** emit YAML to the given buffer. Return a substr trimmed to the emitted YAML.
// emit from root -------------------------
/** (1) emit YAML to the given buffer. Return a substr trimmed to the emitted YAML.
* @param t the tree; will be emitted from the root node.
* @param opts emit options.
* @param buf the output buffer.
* @param error_on_excess Raise an error if the space in the buffer is insufficient.
* @overload */
* @return a substr trimmed to the result in the output buffer. If the buffer is
* insufficient (when error_on_excess is false), the string pointer of the
* result will be set to null, and the length will report the required buffer size. */
inline substr emit_yaml(Tree const& t, EmitOptions const& opts, substr buf, bool error_on_excess=true)
{
EmitterBuf em(opts, buf);
return em.emit_as(EMIT_YAML, t, error_on_excess);
}
/** (2) like (1), but use default emit options */
inline substr emit_yaml(Tree const& t, substr buf, bool error_on_excess=true)
{
EmitterBuf em(buf);
return em.emit_as(EMIT_YAML, t, error_on_excess);
}
/** emit JSON to the given buffer. Return a substr trimmed to the emitted JSON.
/** (1) emit JSON to the given buffer. Return a substr trimmed to the emitted JSON.
* @param t the tree; will be emitted from the root node.
* @param opts emit options.
* @param buf the output buffer.
* @param error_on_excess Raise an error if the space in the buffer is insufficient.
* @overload */
* @return a substr trimmed to the result in the output buffer. If the buffer is
* insufficient (when error_on_excess is false), the string pointer of the
* result will be set to null, and the length will report the required buffer size. */
inline substr emit_json(Tree const& t, EmitOptions const& opts, substr buf, bool error_on_excess=true)
{
EmitterBuf em(opts, buf);
return em.emit_as(EMIT_JSON, t, error_on_excess);
}
/** (2) like (1), but use default emit options */
inline substr emit_json(Tree const& t, substr buf, bool error_on_excess=true)
{
EmitterBuf em(buf);
@@ -300,21 +538,51 @@ inline substr emit_json(Tree const& t, substr buf, bool error_on_excess=true)
}
/** emit YAML to the given buffer. Return a substr trimmed to the emitted YAML.
// emit from ConstNodeRef ------------------------
/** (1) emit YAML to the given buffer. Return a substr trimmed to the emitted YAML.
* @param r the starting node.
* @param buf the output buffer.
* @param opts emit options.
* @param error_on_excess Raise an error if the space in the buffer is insufficient.
* @overload
*/
* @return a substr trimmed to the result in the output buffer. If the buffer is
* insufficient (when error_on_excess is false), the string pointer of the
* result will be set to null, and the length will report the required buffer size. */
inline substr emit_yaml(ConstNodeRef const& r, EmitOptions const& opts, substr buf, bool error_on_excess=true)
{
if(!detail::is_set_(r))
return {};
EmitterBuf em(opts, buf);
return em.emit_as(EMIT_YAML, r, error_on_excess);
}
/** (2) like (1), but use default emit options */
inline substr emit_yaml(ConstNodeRef const& r, substr buf, bool error_on_excess=true)
{
if(!detail::is_set_(r))
return {};
EmitterBuf em(buf);
return em.emit_as(EMIT_YAML, r, error_on_excess);
}
/** emit JSON to the given buffer. Return a substr trimmed to the emitted JSON.
/** (1) emit JSON to the given buffer. Return a substr trimmed to the emitted JSON.
* @param r the starting node.
* @param buf the output buffer.
* @param opts emit options.
* @param error_on_excess Raise an error if the space in the buffer is insufficient.
* @overload
*/
* @return a substr trimmed to the result in the output buffer. If the buffer is
* insufficient (when error_on_excess is false), the string pointer of the
* result will be set to null, and the length will report the required buffer size. */
inline substr emit_json(ConstNodeRef const& r, EmitOptions const& opts, substr buf, bool error_on_excess=true)
{
if(!detail::is_set_(r))
return {};
EmitterBuf em(opts, buf);
return em.emit_as(EMIT_JSON, r, error_on_excess);
}
/** (2) like (1), but use default emit options */
inline substr emit_json(ConstNodeRef const& r, substr buf, bool error_on_excess=true)
{
if(!detail::is_set_(r))
return {};
EmitterBuf em(buf);
return em.emit_as(EMIT_JSON, r, error_on_excess);
}
@@ -322,141 +590,225 @@ inline substr emit_json(ConstNodeRef const& r, substr buf, bool error_on_excess=
//-----------------------------------------------------------------------------
/** emit+resize: emit YAML to the given `std::string`/`std::vector`-like
* container, resizing it as needed to fit the emitted YAML. */
/** @defgroup doc_emit_to_container Emit to resizeable container
*
* @{
*/
// emit from tree and node id ---------------------------
/** (1) emit+resize: emit YAML to the given `std::string`/`std::vector`-like
* container, resizing it as needed to fit the emitted YAML. If @p append is
* set to true, the emitted YAML is appended at the end of the container.
*
* @return a substr trimmed to the emitted YAML (excluding the initial contents, when appending) */
template<class CharOwningContainer>
substr emitrs_yaml(Tree const& t, size_t id, CharOwningContainer * cont)
substr emitrs_yaml(Tree const& t, id_type id, EmitOptions const& opts, CharOwningContainer * cont, bool append=false)
{
substr buf = to_substr(*cont);
substr ret = emit_yaml(t, id, buf, /*error_on_excess*/false);
size_t startpos = append ? cont->size() : 0u;
cont->resize(cont->capacity()); // otherwise the first emit would be certain to fail
substr buf = to_substr(*cont).sub(startpos);
substr ret = emit_yaml(t, id, opts, buf, /*error_on_excess*/false);
if(ret.str == nullptr && ret.len > 0)
{
cont->resize(ret.len);
buf = to_substr(*cont);
ret = emit_yaml(t, id, buf, /*error_on_excess*/true);
cont->resize(startpos + ret.len);
buf = to_substr(*cont).sub(startpos);
ret = emit_yaml(t, id, opts, buf, /*error_on_excess*/true);
}
else
{
cont->resize(startpos + ret.len);
}
return ret;
}
/** emit+resize: emit JSON to the given `std::string`/`std::vector`-like
* container, resizing it as needed to fit the emitted JSON. */
/** (2) like (1), but use default emit options */
template<class CharOwningContainer>
substr emitrs_json(Tree const& t, size_t id, CharOwningContainer * cont)
substr emitrs_yaml(Tree const& t, id_type id, CharOwningContainer * cont, bool append=false)
{
substr buf = to_substr(*cont);
substr ret = emit_json(t, id, buf, /*error_on_excess*/false);
return emitrs_yaml(t, id, EmitOptions{}, cont, append);
}
/** (1) emit+resize: emit JSON to the given `std::string`/`std::vector`-like
* container, resizing it as needed to fit the emitted JSON. If @p append is
* set to true, the emitted YAML is appended at the end of the container.
*
* @return a substr trimmed to the emitted JSON (excluding the initial contents, when appending) */
template<class CharOwningContainer>
substr emitrs_json(Tree const& t, id_type id, EmitOptions const& opts, CharOwningContainer * cont, bool append=false)
{
const size_t startpos = append ? cont->size() : 0u;
cont->resize(cont->capacity()); // otherwise the first emit would be certain to fail
substr buf = to_substr(*cont).sub(startpos);
EmitterBuf em(opts, buf);
substr ret = emit_json(t, id, opts, buf, /*error_on_excess*/false);
if(ret.str == nullptr && ret.len > 0)
{
cont->resize(ret.len);
buf = to_substr(*cont);
ret = emit_json(t, id, buf, /*error_on_excess*/true);
cont->resize(startpos + ret.len);
buf = to_substr(*cont).sub(startpos);
ret = emit_json(t, id, opts, buf, /*error_on_excess*/true);
}
else
{
cont->resize(startpos + ret.len);
}
return ret;
}
/** emit+resize: emit YAML to the given `std::string`/`std::vector`-like
* container, resizing it as needed to fit the emitted YAML. */
/** (2) like (1), but use default emit options */
template<class CharOwningContainer>
CharOwningContainer emitrs_yaml(Tree const& t, size_t id)
substr emitrs_json(Tree const& t, id_type id, CharOwningContainer * cont, bool append=false)
{
return emitrs_json(t, id, EmitOptions{}, cont, append);
}
/** (3) emit+resize: YAML to a newly-created `std::string`/`std::vector`-like container. */
template<class CharOwningContainer>
CharOwningContainer emitrs_yaml(Tree const& t, id_type id, EmitOptions const& opts={})
{
CharOwningContainer c;
emitrs_yaml(t, id, &c);
emitrs_yaml(t, id, opts, &c);
return c;
}
/** emit+resize: emit JSON to the given `std::string`/`std::vector`-like
* container, resizing it as needed to fit the emitted JSON. */
/** (3) emit+resize: JSON to a newly-created `std::string`/`std::vector`-like container. */
template<class CharOwningContainer>
CharOwningContainer emitrs_json(Tree const& t, size_t id)
CharOwningContainer emitrs_json(Tree const& t, id_type id, EmitOptions const& opts={})
{
CharOwningContainer c;
emitrs_json(t, id, &c);
emitrs_json(t, id, opts, &c);
return c;
}
/** emit+resize: YAML to the given `std::string`/`std::vector`-like
* container, resizing it as needed to fit the emitted YAML. */
// emit from root -------------------------
/** (1) emit+resize: YAML to the given `std::string`/`std::vector`-like
* container, resizing it as needed to fit the emitted YAML.
* @return a substr trimmed to the new emitted contents. */
template<class CharOwningContainer>
substr emitrs_yaml(Tree const& t, CharOwningContainer * cont)
substr emitrs_yaml(Tree const& t, EmitOptions const& opts, CharOwningContainer * cont, bool append=false)
{
if(t.empty())
return {};
return emitrs_yaml(t, t.root_id(), cont);
return emitrs_yaml(t, t.root_id(), opts, cont, append);
}
/** emit+resize: JSON to the given `std::string`/`std::vector`-like
* container, resizing it as needed to fit the emitted JSON. */
/** (2) like (1), but use default emit options */
template<class CharOwningContainer>
substr emitrs_json(Tree const& t, CharOwningContainer * cont)
substr emitrs_yaml(Tree const& t, CharOwningContainer * cont, bool append=false)
{
if(t.empty())
return {};
return emitrs_json(t, t.root_id(), cont);
return emitrs_yaml(t, t.root_id(), EmitOptions{}, cont, append);
}
/** (1) emit+resize: JSON to the given `std::string`/`std::vector`-like
* container, resizing it as needed to fit the emitted JSON.
* @return a substr trimmed to the new emitted contents. */
template<class CharOwningContainer>
substr emitrs_json(Tree const& t, EmitOptions const& opts, CharOwningContainer * cont, bool append=false)
{
if(t.empty())
return {};
return emitrs_json(t, t.root_id(), opts, cont, append);
}
/** (2) like (1), but use default emit options */
template<class CharOwningContainer>
substr emitrs_json(Tree const& t, CharOwningContainer * cont, bool append=false)
{
if(t.empty())
return {};
return emitrs_json(t, t.root_id(), EmitOptions{}, cont, append);
}
/** emit+resize: YAML to the given `std::string`/`std::vector`-like container,
* resizing it as needed to fit the emitted YAML. */
/** (3) emit+resize: YAML to a newly-created `std::string`/`std::vector`-like container. */
template<class CharOwningContainer>
CharOwningContainer emitrs_yaml(Tree const& t)
CharOwningContainer emitrs_yaml(Tree const& t, EmitOptions const& opts={})
{
CharOwningContainer c;
if(t.empty())
return c;
emitrs_yaml(t, t.root_id(), &c);
emitrs_yaml(t, t.root_id(), opts, &c);
return c;
}
/** emit+resize: JSON to the given `std::string`/`std::vector`-like container,
* resizing it as needed to fit the emitted JSON. */
/** (3) emit+resize: JSON to a newly-created `std::string`/`std::vector`-like container. */
template<class CharOwningContainer>
CharOwningContainer emitrs_json(Tree const& t)
CharOwningContainer emitrs_json(Tree const& t, EmitOptions const& opts={})
{
CharOwningContainer c;
if(t.empty())
return c;
emitrs_json(t, t.root_id(), &c);
emitrs_json(t, t.root_id(), opts, &c);
return c;
}
/** emit+resize: YAML to the given `std::string`/`std::vector`-like container,
* resizing it as needed to fit the emitted YAML. */
// emit from ConstNodeRef ------------------------
/** (1) emit+resize: YAML to the given `std::string`/`std::vector`-like container,
* resizing it as needed to fit the emitted YAML.
* @return a substr trimmed to the new emitted contents */
template<class CharOwningContainer>
substr emitrs_yaml(ConstNodeRef const& n, CharOwningContainer * cont)
substr emitrs_yaml(ConstNodeRef const& n, EmitOptions const& opts, CharOwningContainer * cont, bool append=false)
{
if(!detail::is_set_(n))
return {};
_RYML_CB_CHECK(n.tree()->callbacks(), n.readable());
return emitrs_yaml(*n.tree(), n.id(), cont);
return emitrs_yaml(*n.tree(), n.id(), opts, cont, append);
}
/** emit+resize: JSON to the given `std::string`/`std::vector`-like container,
* resizing it as needed to fit the emitted JSON. */
/** (2) like (1), but use default emit options */
template<class CharOwningContainer>
substr emitrs_json(ConstNodeRef const& n, CharOwningContainer * cont)
substr emitrs_yaml(ConstNodeRef const& n, CharOwningContainer * cont, bool append=false)
{
if(!detail::is_set_(n))
return {};
_RYML_CB_CHECK(n.tree()->callbacks(), n.readable());
return emitrs_json(*n.tree(), n.id(), cont);
return emitrs_yaml(*n.tree(), n.id(), EmitOptions{}, cont, append);
}
/** (1) emit+resize: JSON to the given `std::string`/`std::vector`-like container,
* resizing it as needed to fit the emitted JSON.
* @return a substr trimmed to the new emitted contents */
template<class CharOwningContainer>
substr emitrs_json(ConstNodeRef const& n, EmitOptions const& opts, CharOwningContainer * cont, bool append=false)
{
if(!detail::is_set_(n))
return {};
_RYML_CB_CHECK(n.tree()->callbacks(), n.readable());
return emitrs_json(*n.tree(), n.id(), opts, cont, append);
}
/** (2) like (1), but use default emit options */
template<class CharOwningContainer>
substr emitrs_json(ConstNodeRef const& n, CharOwningContainer * cont, bool append=false)
{
if(!detail::is_set_(n))
return {};
_RYML_CB_CHECK(n.tree()->callbacks(), n.readable());
return emitrs_json(*n.tree(), n.id(), EmitOptions{}, cont, append);
}
/** emit+resize: YAML to the given `std::string`/`std::vector`-like container,
* resizing it as needed to fit the emitted YAML. */
/** (3) emit+resize: YAML to a newly-created `std::string`/`std::vector`-like container. */
template<class CharOwningContainer>
CharOwningContainer emitrs_yaml(ConstNodeRef const& n)
CharOwningContainer emitrs_yaml(ConstNodeRef const& n, EmitOptions const& opts={})
{
if(!detail::is_set_(n))
return {};
_RYML_CB_CHECK(n.tree()->callbacks(), n.readable());
CharOwningContainer c;
emitrs_yaml(*n.tree(), n.id(), &c);
emitrs_yaml(*n.tree(), n.id(), opts, &c);
return c;
}
/** emit+resize: JSON to the given `std::string`/`std::vector`-like container,
* resizing it as needed to fit the emitted JSON. */
/** (3) emit+resize: JSON to a newly-created `std::string`/`std::vector`-like container. */
template<class CharOwningContainer>
CharOwningContainer emitrs_json(ConstNodeRef const& n)
CharOwningContainer emitrs_json(ConstNodeRef const& n, EmitOptions const& opts={})
{
if(!detail::is_set_(n))
return {};
_RYML_CB_CHECK(n.tree()->callbacks(), n.readable());
CharOwningContainer c;
emitrs_json(*n.tree(), n.id(), &c);
emitrs_json(*n.tree(), n.id(), opts, &c);
return c;
}
/** @} */
@@ -464,7 +816,22 @@ CharOwningContainer emitrs_json(ConstNodeRef const& n)
/** @cond dev */
RYML_DEPRECATE_EMIT inline size_t emit(Tree const& t, size_t id, FILE *f)
#define RYML_DEPRECATE_EMIT \
RYML_DEPRECATED("use emit_yaml() instead. " \
"See https://github.com/biojppm/rapidyaml/issues/120")
#define RYML_DEPRECATE_EMITRS \
RYML_DEPRECATED("use emitrs_yaml() instead. " \
"See https://github.com/biojppm/rapidyaml/issues/120")
// workaround for Qt emit which is a macro;
// see https://github.com/biojppm/rapidyaml/issues/120.
// emit is defined in qobjectdefs.h (as an empty define).
#ifdef emit
#define RYML_TMP_EMIT_
#undef emit
#endif
RYML_DEPRECATE_EMIT inline size_t emit(Tree const& t, id_type id, FILE *f)
{
return emit_yaml(t, id, f);
}
@@ -477,7 +844,7 @@ RYML_DEPRECATE_EMIT inline size_t emit(ConstNodeRef const& r, FILE *f=nullptr)
return emit_yaml(r, f);
}
RYML_DEPRECATE_EMIT inline substr emit(Tree const& t, size_t id, substr buf, bool error_on_excess=true)
RYML_DEPRECATE_EMIT inline substr emit(Tree const& t, id_type id, substr buf, bool error_on_excess=true)
{
return emit_yaml(t, id, buf, error_on_excess);
}
@@ -490,13 +857,18 @@ RYML_DEPRECATE_EMIT inline substr emit(ConstNodeRef const& r, substr buf, bool e
return emit_yaml(r, buf, error_on_excess);
}
#ifdef RYML_TMP_EMIT_
#define emit
#undef RYML_TMP_EMIT_
#endif
template<class CharOwningContainer>
RYML_DEPRECATE_EMITRS substr emitrs(Tree const& t, size_t id, CharOwningContainer * cont)
RYML_DEPRECATE_EMITRS substr emitrs(Tree const& t, id_type id, CharOwningContainer * cont)
{
return emitrs_yaml(t, id, cont);
}
template<class CharOwningContainer>
RYML_DEPRECATE_EMITRS CharOwningContainer emitrs(Tree const& t, size_t id)
RYML_DEPRECATE_EMITRS CharOwningContainer emitrs(Tree const& t, id_type id)
{
return emitrs_yaml<CharOwningContainer>(t, id);
}
@@ -520,15 +892,18 @@ RYML_DEPRECATE_EMITRS CharOwningContainer emitrs(ConstNodeRef const& n)
{
return emitrs_yaml<CharOwningContainer>(n);
}
/** @endcond */
} // namespace yml
} // namespace c4
C4_SUPPRESS_WARNING_GCC_CLANG_POP
#undef RYML_DEPRECATE_EMIT
#undef RYML_DEPRECATE_EMITRS
#include "c4/yml/emit.def.hpp"
#include "c4/yml/emit.def.hpp" // NOLINT
#endif /* _C4_YML_EMIT_HPP_ */

View File

@@ -0,0 +1,194 @@
#ifndef _C4_YML_EVENT_HANDLER_STACK_HPP_
#define _C4_YML_EVENT_HANDLER_STACK_HPP_
#ifndef _C4_YML_DETAIL_STACK_HPP_
#include "c4/yml/detail/stack.hpp"
#endif
#ifndef _C4_YML_NODE_TYPE_HPP_
#include "c4/yml/node_type.hpp"
#endif
#ifndef _C4_YML_DETAIL_PARSER_DBG_HPP_
#include "c4/yml/detail/parser_dbg.hpp"
#endif
#ifndef _C4_YML_PARSER_STATE_HPP_
#include "c4/yml/parser_state.hpp"
#endif
#ifdef RYML_DBG
#ifndef _C4_YML_DETAIL_PRINT_HPP_
#include "c4/yml/detail/print.hpp"
#endif
#endif
// NOLINTBEGIN(hicpp-signed-bitwise)
namespace c4 {
namespace yml {
/** @addtogroup doc_event_handlers
* @{ */
namespace detail {
using pfn_relocate_arena = void (*)(void*, csubstr prev_arena, substr next_arena);
} // detail
/** Use this class a base of implementations of event handler to
* simplify the stack logic. */
template<class HandlerImpl, class HandlerState>
struct EventHandlerStack
{
static_assert(std::is_base_of<ParserState, HandlerState>::value,
"ParserState must be a base of HandlerState");
using state = HandlerState;
using pfn_relocate_arena = detail::pfn_relocate_arena;
public:
detail::stack<state> m_stack;
state *C4_RESTRICT m_curr; ///< current stack level: top of the stack. cached here for easier access.
state *C4_RESTRICT m_parent; ///< parent of the current stack level.
pfn_relocate_arena m_relocate_arena; ///< callback when the arena gets relocated
void * m_relocate_arena_data;
protected:
EventHandlerStack() : m_stack(), m_curr(), m_parent(), m_relocate_arena(), m_relocate_arena_data() {}
EventHandlerStack(Callbacks const& cb) : m_stack(cb), m_curr(), m_parent(), m_relocate_arena(), m_relocate_arena_data() {}
protected:
void _stack_start_parse(const char *filename, pfn_relocate_arena relocate_arena, void *relocate_arena_data)
{
_RYML_CB_ASSERT(m_stack.m_callbacks, m_curr != nullptr);
_RYML_CB_ASSERT(m_stack.m_callbacks, relocate_arena != nullptr);
_RYML_CB_ASSERT(m_stack.m_callbacks, relocate_arena_data != nullptr);
m_curr->start_parse(filename, m_curr->node_id);
m_relocate_arena = relocate_arena;
m_relocate_arena_data = relocate_arena_data;
}
void _stack_finish_parse()
{
}
protected:
void _stack_reset_root()
{
m_stack.clear();
m_stack.push({});
m_parent = nullptr;
m_curr = &m_stack.top();
}
void _stack_reset_non_root()
{
m_stack.clear();
m_stack.push({}); // parent
m_stack.push({}); // node
m_parent = &m_stack.top(1);
m_curr = &m_stack.top();
}
void _stack_push()
{
m_stack.push_top();
m_parent = &m_stack.top(1); // don't use m_curr. watch out for relocations inside the prev push
m_curr = &m_stack.top();
m_curr->reset_after_push();
}
void _stack_pop()
{
_RYML_CB_ASSERT(m_stack.m_callbacks, m_parent);
_RYML_CB_ASSERT(m_stack.m_callbacks, m_stack.size() > 1);
m_parent->reset_before_pop(*m_curr);
m_stack.pop();
m_parent = m_stack.size() > 1 ? &m_stack.top(1) : nullptr;
m_curr = &m_stack.top();
#ifdef RYML_DBG
if(m_parent)
_c4dbgpf("popped! top is now node={} (parent={})", m_curr->node_id, m_parent->node_id);
else
_c4dbgpf("popped! top is now node={} @ ROOT", m_curr->node_id);
#endif
}
protected:
// undefined at the end
#define _has_any_(bits) (static_cast<HandlerImpl const* C4_RESTRICT>(this)->template _has_any__<bits>())
bool _stack_should_push_on_begin_doc() const
{
const bool is_root = (m_stack.size() == 1u);
return is_root && (_has_any_(DOC|VAL|MAP|SEQ) || m_curr->has_children);
}
bool _stack_should_pop_on_end_doc() const
{
const bool is_root = (m_stack.size() == 1u);
return !is_root && _has_any_(DOC);
}
protected:
void _stack_relocate_to_new_arena(csubstr prev, substr curr)
{
for(state &st : m_stack)
{
if(st.line_contents.rem.is_sub(prev))
st.line_contents.rem = _stack_relocate_to_new_arena(st.line_contents.rem, prev, curr);
if(st.line_contents.full.is_sub(prev))
st.line_contents.full = _stack_relocate_to_new_arena(st.line_contents.full, prev, curr);
if(st.line_contents.stripped.is_sub(prev))
st.line_contents.stripped = _stack_relocate_to_new_arena(st.line_contents.stripped, prev, curr);
}
_RYML_CB_ASSERT(m_stack.m_callbacks, m_relocate_arena != nullptr);
_RYML_CB_ASSERT(m_stack.m_callbacks, m_relocate_arena_data != nullptr);
m_relocate_arena(m_relocate_arena_data, prev, curr);
}
substr _stack_relocate_to_new_arena(csubstr s, csubstr prev, substr curr)
{
_RYML_CB_ASSERT(m_stack.m_callbacks, prev.is_super(s));
auto pos = s.str - prev.str;
substr out = {curr.str + pos, s.len};
_RYML_CB_ASSERT(m_stack.m_callbacks, curr.is_super(out));
return out;
}
public:
/** Check whether the current parse tokens are trailing on the
* previous doc, and raise an error if they are. This function is
* called by the parse engine (not the event handler) before a doc
* is started. */
void check_trailing_doc_token() const
{
const bool is_root = (m_stack.size() == 1u);
const bool isndoc = (m_curr->flags & NDOC) != 0;
const bool suspicious = _has_any_(MAP|SEQ|VAL);
_c4dbgpf("target={} isroot={} suspicious={} ndoc={}", m_curr->node_id, is_root, suspicious, isndoc);
if((is_root || _has_any_(DOC)) && suspicious && !isndoc)
_RYML_CB_ERR_(m_stack.m_callbacks, "parse error", m_curr->pos);
}
protected:
#undef _has_any_
};
/** @} */
} // namespace yml
} // namespace c4
// NOLINTEND(hicpp-signed-bitwise)
#endif /* _C4_YML_EVENT_HANDLER_STACK_HPP_ */

View File

@@ -0,0 +1,769 @@
#ifndef _C4_YML_EVENT_HANDLER_TREE_HPP_
#define _C4_YML_EVENT_HANDLER_TREE_HPP_
#ifndef _C4_YML_TREE_HPP_
#include "c4/yml/tree.hpp"
#endif
#ifndef _C4_YML_EVENT_HANDLER_STACK_HPP_
#include "c4/yml/event_handler_stack.hpp"
#endif
C4_SUPPRESS_WARNING_MSVC_WITH_PUSH(4702) // unreachable code
// NOLINTBEGIN(hicpp-signed-bitwise)
namespace c4 {
namespace yml {
/** @addtogroup doc_event_handlers
* @{ */
/** The stack state needed specifically by @ref EventHandlerTree */
struct EventHandlerTreeState : public ParserState
{
NodeData *tr_data;
};
/** The event handler to create a ryml @ref Tree. See the
* documentation for @ref doc_event_handlers, which has important
* notes about the event model used by rapidyaml. */
struct EventHandlerTree : public EventHandlerStack<EventHandlerTree, EventHandlerTreeState>
{
/** @name types
* @{ */
using state = EventHandlerTreeState;
/** @} */
public:
/** @cond dev */
Tree *C4_RESTRICT m_tree;
id_type m_id;
size_t m_num_directives;
bool m_yaml_directive;
#ifdef RYML_DBG
#define _enable_(bits) _enable__<bits>(); _c4dbgpf("node[{}]: enable {}", m_curr->node_id, #bits)
#define _disable_(bits) _disable__<bits>(); _c4dbgpf("node[{}]: disable {}", m_curr->node_id, #bits)
#else
#define _enable_(bits) _enable__<bits>()
#define _disable_(bits) _disable__<bits>()
#endif
#define _has_any_(bits) _has_any__<bits>()
/** @endcond */
public:
/** @name construction and resetting
* @{ */
EventHandlerTree() : EventHandlerStack(), m_tree(), m_id(NONE), m_num_directives(), m_yaml_directive() {}
EventHandlerTree(Callbacks const& cb) : EventHandlerStack(cb), m_tree(), m_id(NONE), m_num_directives(), m_yaml_directive() {}
EventHandlerTree(Tree *tree, id_type id) : EventHandlerStack(tree->callbacks()), m_tree(tree), m_id(id), m_num_directives(), m_yaml_directive()
{
reset(tree, id);
}
void reset(Tree *tree, id_type id)
{
if(C4_UNLIKELY(!tree))
_RYML_CB_ERR(m_stack.m_callbacks, "null tree");
if(C4_UNLIKELY(id >= tree->capacity()))
_RYML_CB_ERR(tree->callbacks(), "invalid node");
if(C4_UNLIKELY(!tree->is_root(id)))
if(C4_UNLIKELY(tree->is_map(tree->parent(id))))
if(C4_UNLIKELY(!tree->has_key(id)))
_RYML_CB_ERR(tree->callbacks(), "destination node belongs to a map and has no key");
m_tree = tree;
m_id = id;
if(m_tree->is_root(id))
{
_stack_reset_root();
_reset_parser_state(m_curr, id, m_tree->root_id());
}
else
{
_stack_reset_non_root();
_reset_parser_state(m_parent, id, m_tree->parent(id));
_reset_parser_state(m_curr, id, id);
}
m_num_directives = 0;
m_yaml_directive = false;
}
/** @} */
public:
/** @name parse events
* @{ */
void start_parse(const char* filename, detail::pfn_relocate_arena relocate_arena, void *relocate_arena_data)
{
_RYML_CB_ASSERT(m_stack.m_callbacks, m_tree != nullptr);
this->_stack_start_parse(filename, relocate_arena, relocate_arena_data);
}
void finish_parse()
{
_RYML_CB_ASSERT(m_stack.m_callbacks, m_tree != nullptr);
if(m_num_directives && !m_tree->is_stream(m_tree->root_id()))
_RYML_CB_ERR_(m_stack.m_callbacks, "directives cannot be used without a document", {});
this->_stack_finish_parse();
/* This pointer is temporary. Remember that:
*
* - this handler object may be held by the user
* - it may be used with a temporary tree inside the parse function
* - when the parse function returns the temporary tree, its address
* will change
*
* As a result, the user could try to read the tree from m_tree, and
* end up reading the stale temporary object.
*
* So it is better to clear it here; then the user will get an obvious
* segfault if reading from m_tree. */
m_tree = nullptr;
}
void cancel_parse()
{
m_tree = nullptr;
}
/** @} */
public:
/** @name YAML stream events */
/** @{ */
C4_ALWAYS_INLINE void begin_stream() const noexcept { /*nothing to do*/ }
C4_ALWAYS_INLINE void end_stream() const noexcept { /*nothing to do*/ }
/** @} */
public:
/** @name YAML document events */
/** @{ */
/** implicit doc start (without ---) */
void begin_doc()
{
_c4dbgp("begin_doc");
if(_stack_should_push_on_begin_doc())
{
_c4dbgp("push!");
_set_root_as_stream();
_push();
_enable_(DOC);
}
}
/** implicit doc end (without ...) */
void end_doc()
{
_c4dbgp("end_doc");
if(_stack_should_pop_on_end_doc())
{
_remove_speculative();
_c4dbgp("pop!");
_pop();
}
}
/** explicit doc start, with --- */
void begin_doc_expl()
{
_c4dbgp("begin_doc_expl");
_RYML_CB_ASSERT(m_stack.m_callbacks, m_tree->root_id() == m_curr->node_id);
if(!m_tree->is_stream(m_tree->root_id())) //if(_should_push_on_begin_doc())
{
_c4dbgp("ensure stream");
_set_root_as_stream();
id_type first = m_tree->first_child(m_tree->root_id());
_RYML_CB_ASSERT(m_stack.m_callbacks, m_tree->is_stream(m_tree->root_id()));
_RYML_CB_ASSERT(m_stack.m_callbacks, m_tree->num_children(m_tree->root_id()) == 1u);
if(m_tree->has_children(first) || m_tree->is_val(first))
{
_c4dbgp("push!");
_push();
}
else
{
_c4dbgp("tweak");
_push();
_remove_speculative();
m_curr->node_id = m_tree->last_child(m_tree->root_id());
m_curr->tr_data = m_tree->_p(m_curr->node_id);
}
}
else
{
_c4dbgp("push!");
_push();
}
_enable_(DOC);
}
/** explicit doc end, with ... */
void end_doc_expl()
{
_c4dbgp("end_doc_expl");
_remove_speculative();
if(_stack_should_pop_on_end_doc())
{
_c4dbgp("pop!");
_pop();
}
m_yaml_directive = false;
}
/** @} */
public:
/** @name YAML map events */
/** @{ */
void begin_map_key_flow()
{
_RYML_CB_ERR_(m_stack.m_callbacks, "ryml trees cannot handle containers as keys", m_curr->pos);
}
void begin_map_key_block()
{
_RYML_CB_ERR_(m_stack.m_callbacks, "ryml trees cannot handle containers as keys", m_curr->pos);
}
void begin_map_val_flow()
{
_c4dbgpf("node[{}]: begin_map_val_flow", m_curr->node_id);
_RYML_CB_CHECK(m_stack.m_callbacks, !_has_any_(VAL));
_enable_(MAP|FLOW_SL);
_save_loc();
_push();
}
void begin_map_val_block()
{
_c4dbgpf("node[{}]: begin_map_val_block", m_curr->node_id);
_RYML_CB_CHECK(m_stack.m_callbacks, !_has_any_(VAL));
_enable_(MAP|BLOCK);
_save_loc();
_push();
}
void end_map()
{
_pop();
_c4dbgpf("node[{}]: end_map_val", m_curr->node_id);
}
/** @} */
public:
/** @name YAML seq events */
/** @{ */
void begin_seq_key_flow()
{
_RYML_CB_ERR_(m_stack.m_callbacks, "ryml trees cannot handle containers as keys", m_curr->pos);
}
void begin_seq_key_block()
{
_RYML_CB_ERR_(m_stack.m_callbacks, "ryml trees cannot handle containers as keys", m_curr->pos);
}
void begin_seq_val_flow()
{
_c4dbgpf("node[{}]: begin_seq_val_flow", m_curr->node_id);
_RYML_CB_CHECK(m_stack.m_callbacks, !_has_any_(VAL));
_enable_(SEQ|FLOW_SL);
_save_loc();
_push();
}
void begin_seq_val_block()
{
_c4dbgpf("node[{}]: begin_seq_val_block", m_curr->node_id);
_RYML_CB_CHECK(m_stack.m_callbacks, !_has_any_(VAL));
_enable_(SEQ|BLOCK);
_save_loc();
_push();
}
void end_seq()
{
_pop();
_c4dbgpf("node[{}]: end_seq_val", m_curr->node_id);
}
/** @} */
public:
/** @name YAML structure events */
/** @{ */
void add_sibling()
{
_RYML_CB_ASSERT(m_stack.m_callbacks, m_tree);
_RYML_CB_ASSERT(m_stack.m_callbacks, m_parent);
_RYML_CB_ASSERT(m_stack.m_callbacks, m_tree->has_children(m_parent->node_id));
NodeData const* prev = m_tree->m_buf; // watchout against relocation of the tree nodes
_set_state_(m_curr, m_tree->_append_child__unprotected(m_parent->node_id));
if(prev != m_tree->m_buf)
_refresh_after_relocation();
_c4dbgpf("node[{}]: added sibling={} prev={}", m_parent->node_id, m_curr->node_id, m_tree->prev_sibling(m_curr->node_id));
}
/** set the previous val as the first key of a new map, with flow style.
*
* See the documentation for @ref doc_event_handlers, which has
* important notes about this event.
*/
void actually_val_is_first_key_of_new_map_flow()
{
if(C4_UNLIKELY(m_tree->is_container(m_curr->node_id)))
_RYML_CB_ERR_(m_stack.m_callbacks, "ryml trees cannot handle containers as keys", m_curr->pos);
_RYML_CB_ASSERT(m_stack.m_callbacks, m_parent);
_RYML_CB_ASSERT(m_stack.m_callbacks, m_tree->is_seq(m_parent->node_id));
_RYML_CB_ASSERT(m_stack.m_callbacks, !m_tree->is_container(m_curr->node_id));
_RYML_CB_ASSERT(m_stack.m_callbacks, !m_tree->has_key(m_curr->node_id));
const NodeData tmp = _val2key_(*m_curr->tr_data);
_disable_(_VALMASK|VAL_STYLE);
m_curr->tr_data->m_val = {};
begin_map_val_flow();
m_curr->tr_data->m_type = tmp.m_type;
m_curr->tr_data->m_key = tmp.m_key;
}
/** like its flow counterpart, but this function can only be
* called after the end of a flow-val at root or doc level.
*
* See the documentation for @ref doc_event_handlers, which has
* important notes about this event.
*/
void actually_val_is_first_key_of_new_map_block()
{
_RYML_CB_ERR_(m_stack.m_callbacks, "ryml trees cannot handle containers as keys", m_curr->pos);
}
/** @} */
public:
/** @name YAML scalar events */
/** @{ */
C4_ALWAYS_INLINE void set_key_scalar_plain_empty() noexcept
{
_c4dbgpf("node[{}]: set key scalar plain as empty", m_curr->node_id);
m_curr->tr_data->m_key.scalar = {};
_enable_(KEY|KEY_PLAIN|KEYNIL);
}
C4_ALWAYS_INLINE void set_val_scalar_plain_empty() noexcept
{
_c4dbgpf("node[{}]: set val scalar plain as empty", m_curr->node_id);
m_curr->tr_data->m_val.scalar = {};
_enable_(VAL|VAL_PLAIN|VALNIL);
}
C4_ALWAYS_INLINE void set_key_scalar_plain(csubstr scalar) noexcept
{
_c4dbgpf("node[{}]: set key scalar plain: [{}]~~~{}~~~", m_curr->node_id, scalar.len, scalar);
m_curr->tr_data->m_key.scalar = scalar;
_enable_(KEY|KEY_PLAIN);
}
C4_ALWAYS_INLINE void set_val_scalar_plain(csubstr scalar) noexcept
{
_c4dbgpf("node[{}]: set val scalar plain: [{}]~~~{}~~~", m_curr->node_id, scalar.len, scalar);
m_curr->tr_data->m_val.scalar = scalar;
_enable_(VAL|VAL_PLAIN);
}
C4_ALWAYS_INLINE void set_key_scalar_dquoted(csubstr scalar) noexcept
{
_c4dbgpf("node[{}]: set key scalar dquot: [{}]~~~{}~~~", m_curr->node_id, scalar.len, scalar);
m_curr->tr_data->m_key.scalar = scalar;
_enable_(KEY|KEY_DQUO);
}
C4_ALWAYS_INLINE void set_val_scalar_dquoted(csubstr scalar) noexcept
{
_c4dbgpf("node[{}]: set val scalar dquot: [{}]~~~{}~~~", m_curr->node_id, scalar.len, scalar);
m_curr->tr_data->m_val.scalar = scalar;
_enable_(VAL|VAL_DQUO);
}
C4_ALWAYS_INLINE void set_key_scalar_squoted(csubstr scalar) noexcept
{
_c4dbgpf("node[{}]: set key scalar squot: [{}]~~~{}~~~", m_curr->node_id, scalar.len, scalar);
m_curr->tr_data->m_key.scalar = scalar;
_enable_(KEY|KEY_SQUO);
}
C4_ALWAYS_INLINE void set_val_scalar_squoted(csubstr scalar) noexcept
{
_c4dbgpf("node[{}]: set val scalar squot: [{}]~~~{}~~~", m_curr->node_id, scalar.len, scalar);
m_curr->tr_data->m_val.scalar = scalar;
_enable_(VAL|VAL_SQUO);
}
C4_ALWAYS_INLINE void set_key_scalar_literal(csubstr scalar) noexcept
{
_c4dbgpf("node[{}]: set key scalar literal: [{}]~~~{}~~~", m_curr->node_id, scalar.len, scalar);
m_curr->tr_data->m_key.scalar = scalar;
_enable_(KEY|KEY_LITERAL);
}
C4_ALWAYS_INLINE void set_val_scalar_literal(csubstr scalar) noexcept
{
_c4dbgpf("node[{}]: set val scalar literal: [{}]~~~{}~~~", m_curr->node_id, scalar.len, scalar);
m_curr->tr_data->m_val.scalar = scalar;
_enable_(VAL|VAL_LITERAL);
}
C4_ALWAYS_INLINE void set_key_scalar_folded(csubstr scalar) noexcept
{
_c4dbgpf("node[{}]: set key scalar folded: [{}]~~~{}~~~", m_curr->node_id, scalar.len, scalar);
m_curr->tr_data->m_key.scalar = scalar;
_enable_(KEY|KEY_FOLDED);
}
C4_ALWAYS_INLINE void set_val_scalar_folded(csubstr scalar) noexcept
{
_c4dbgpf("node[{}]: set val scalar folded: [{}]~~~{}~~~", m_curr->node_id, scalar.len, scalar);
m_curr->tr_data->m_val.scalar = scalar;
_enable_(VAL|VAL_FOLDED);
}
C4_ALWAYS_INLINE void mark_key_scalar_unfiltered() noexcept
{
_enable_(KEY_UNFILT);
}
C4_ALWAYS_INLINE void mark_val_scalar_unfiltered() noexcept
{
_enable_(VAL_UNFILT);
}
/** @} */
public:
/** @name YAML anchor/reference events */
/** @{ */
void set_key_anchor(csubstr anchor)
{
_c4dbgpf("node[{}]: set key anchor: [{}]~~~{}~~~", m_curr->node_id, anchor.len, anchor);
_RYML_CB_ASSERT(m_stack.m_callbacks, m_tree);
if(C4_UNLIKELY(_has_any_(KEYREF)))
_RYML_CB_ERR_(m_tree->callbacks(), "key cannot have both anchor and ref", m_curr->pos);
_RYML_CB_ASSERT(m_tree->callbacks(), !anchor.begins_with('&'));
_enable_(KEYANCH);
m_curr->tr_data->m_key.anchor = anchor;
}
void set_val_anchor(csubstr anchor)
{
_c4dbgpf("node[{}]: set val anchor: [{}]~~~{}~~~", m_curr->node_id, anchor.len, anchor);
_RYML_CB_ASSERT(m_stack.m_callbacks, m_tree);
if(C4_UNLIKELY(_has_any_(VALREF)))
_RYML_CB_ERR_(m_tree->callbacks(), "val cannot have both anchor and ref", m_curr->pos);
_RYML_CB_ASSERT(m_tree->callbacks(), !anchor.begins_with('&'));
_enable_(VALANCH);
m_curr->tr_data->m_val.anchor = anchor;
}
void set_key_ref(csubstr ref)
{
_c4dbgpf("node[{}]: set key ref: [{}]~~~{}~~~", m_curr->node_id, ref.len, ref);
_RYML_CB_ASSERT(m_stack.m_callbacks, m_tree);
if(C4_UNLIKELY(_has_any_(KEYANCH)))
_RYML_CB_ERR_(m_tree->callbacks(), "key cannot have both anchor and ref", m_curr->pos);
_RYML_CB_ASSERT(m_tree->callbacks(), ref.begins_with('*'));
_enable_(KEY|KEYREF);
m_curr->tr_data->m_key.anchor = ref.sub(1);
m_curr->tr_data->m_key.scalar = ref;
}
void set_val_ref(csubstr ref)
{
_c4dbgpf("node[{}]: set val ref: [{}]~~~{}~~~", m_curr->node_id, ref.len, ref);
_RYML_CB_ASSERT(m_stack.m_callbacks, m_tree);
if(C4_UNLIKELY(_has_any_(VALANCH)))
_RYML_CB_ERR_(m_tree->callbacks(), "val cannot have both anchor and ref", m_curr->pos);
_RYML_CB_ASSERT(m_tree->callbacks(), ref.begins_with('*'));
_enable_(VAL|VALREF);
m_curr->tr_data->m_val.anchor = ref.sub(1);
m_curr->tr_data->m_val.scalar = ref;
}
/** @} */
public:
/** @name YAML tag events */
/** @{ */
void set_key_tag(csubstr tag) noexcept
{
_c4dbgpf("node[{}]: set key tag: [{}]~~~{}~~~", m_curr->node_id, tag.len, tag);
_enable_(KEYTAG);
m_curr->tr_data->m_key.tag = tag;
}
void set_val_tag(csubstr tag) noexcept
{
_c4dbgpf("node[{}]: set val tag: [{}]~~~{}~~~", m_curr->node_id, tag.len, tag);
_enable_(VALTAG);
m_curr->tr_data->m_val.tag = tag;
}
/** @} */
public:
/** @name YAML directive events */
/** @{ */
C4_NO_INLINE void add_directive(csubstr directive)
{
_c4dbgpf("% directive! {}", directive);
_RYML_CB_ASSERT(m_tree->callbacks(), directive.begins_with('%'));
if(directive.begins_with("%TAG"))
{
if(C4_UNLIKELY(!m_tree->add_tag_directive(directive)))
_RYML_CB_ERR_(m_stack.m_callbacks, "failed to add directive", m_curr->pos);
}
else if(directive.begins_with("%YAML"))
{
_c4dbgpf("%YAML directive! ignoring...: {}", directive);
if(C4_UNLIKELY(m_yaml_directive))
_RYML_CB_ERR_(m_stack.m_callbacks, "multiple yaml directives", m_curr->pos);
m_yaml_directive = true;
}
else
{
_c4dbgpf("unknown directive! ignoring... {}", directive);
}
++m_num_directives;
}
/** @} */
public:
/** @name arena functions */
/** @{ */
substr alloc_arena(size_t len)
{
_RYML_CB_ASSERT(m_stack.m_callbacks, m_tree);
csubstr prev = m_tree->arena();
substr out = m_tree->alloc_arena(len);
substr curr = m_tree->arena();
if(curr.str != prev.str)
_stack_relocate_to_new_arena(prev, curr);
return out;
}
substr alloc_arena(size_t len, substr *relocated)
{
_RYML_CB_ASSERT(m_stack.m_callbacks, m_tree);
csubstr prev = m_tree->arena();
if(!prev.is_super(*relocated))
return alloc_arena(len);
substr out = alloc_arena(len);
substr curr = m_tree->arena();
if(curr.str != prev.str)
*relocated = _stack_relocate_to_new_arena(*relocated, prev, curr);
return out;
}
/** @} */
public:
/** @cond dev */
void _reset_parser_state(state* st, id_type parse_root, id_type node)
{
_RYML_CB_ASSERT(m_stack.m_callbacks, m_tree);
_set_state_(st, node);
const NodeType type = m_tree->type(node);
#ifdef RYML_DBG
char flagbuf[80];
_c4dbgpf("resetting state: initial flags={}", detail::_parser_flags_to_str(flagbuf, st->flags));
#endif
if(type == NOTYPE)
{
_c4dbgpf("node[{}] is notype", node);
if(m_tree->is_root(parse_root))
{
_c4dbgpf("node[{}] is root", node);
st->flags |= RUNK|RTOP;
}
else
{
_c4dbgpf("node[{}] is not root. setting USTY", node);
st->flags |= USTY;
}
}
else if(type.is_map())
{
_c4dbgpf("node[{}] is map", node);
st->flags |= RMAP|USTY;
}
else if(type.is_seq())
{
_c4dbgpf("node[{}] is map", node);
st->flags |= RSEQ|USTY;
}
else if(type.has_key())
{
_c4dbgpf("node[{}] has key. setting USTY", node);
st->flags |= USTY;
}
else
{
_RYML_CB_ERR(m_tree->callbacks(), "cannot append to node");
}
if(type.is_doc())
{
_c4dbgpf("node[{}] is doc", node);
st->flags |= RDOC;
}
#ifdef RYML_DBG
_c4dbgpf("resetting state: final flags={}", detail::_parser_flags_to_str(flagbuf, st->flags));
#endif
}
/** push a new parent, add a child to the new parent, and set the
* child as the current node */
void _push()
{
_stack_push();
NodeData const* prev = m_tree->m_buf; // watch out against relocation of the tree nodes
m_curr->node_id = m_tree->_append_child__unprotected(m_parent->node_id);
m_curr->tr_data = m_tree->_p(m_curr->node_id);
if(prev != m_tree->m_buf)
_refresh_after_relocation();
_c4dbgpf("pushed! level={}. top is now node={} (parent={})", m_curr->level, m_curr->node_id, m_parent ? m_parent->node_id : NONE);
}
/** end the current scope */
void _pop()
{
_remove_speculative_with_parent();
_stack_pop();
}
public:
template<type_bits bits> C4_HOT C4_ALWAYS_INLINE void _enable__() noexcept
{
m_curr->tr_data->m_type.type = static_cast<NodeType_e>(m_curr->tr_data->m_type.type | bits);
}
template<type_bits bits> C4_HOT C4_ALWAYS_INLINE void _disable__() noexcept
{
m_curr->tr_data->m_type.type = static_cast<NodeType_e>(m_curr->tr_data->m_type.type & (~bits));
}
template<type_bits bits> C4_HOT C4_ALWAYS_INLINE bool _has_any__() const noexcept
{
return (m_curr->tr_data->m_type.type & bits) != 0;
}
public:
C4_ALWAYS_INLINE void _set_state_(state *C4_RESTRICT s, id_type id) const noexcept
{
s->node_id = id;
s->tr_data = m_tree->_p(id);
}
void _refresh_after_relocation()
{
_c4dbgp("tree: refreshing stack data after tree data relocation");
for(auto &st : m_stack)
st.tr_data = m_tree->_p(st.node_id);
}
void _set_root_as_stream()
{
_c4dbgp("set root as stream");
_RYML_CB_ASSERT(m_tree->callbacks(), m_tree->root_id() == 0u);
_RYML_CB_ASSERT(m_tree->callbacks(), m_curr->node_id == 0u);
const bool hack = !m_tree->has_children(m_curr->node_id) && !m_tree->is_val(m_curr->node_id);
if(hack)
m_tree->_p(m_tree->root_id())->m_type.add(VAL);
m_tree->set_root_as_stream();
_RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_stream(m_tree->root_id()));
_RYML_CB_ASSERT(m_tree->callbacks(), m_tree->has_children(m_tree->root_id()));
_RYML_CB_ASSERT(m_tree->callbacks(), m_tree->is_doc(m_tree->first_child(m_tree->root_id())));
if(hack)
m_tree->_p(m_tree->first_child(m_tree->root_id()))->m_type.rem(VAL);
_set_state_(m_curr, m_tree->root_id());
}
static NodeData _val2key_(NodeData const& C4_RESTRICT d) noexcept
{
NodeData r = d;
r.m_key = d.m_val;
r.m_val = {};
r.m_type = d.m_type;
static_assert((_VALMASK >> 1u) == _KEYMASK, "required for this function to work");
static_assert((VAL_STYLE >> 1u) == KEY_STYLE, "required for this function to work");
r.m_type.type = ((d.m_type.type & (_VALMASK|VAL_STYLE)) >> 1u);
r.m_type.type = (r.m_type.type & ~(_VALMASK|VAL_STYLE));
r.m_type.type = (r.m_type.type | KEY);
return r;
}
void _remove_speculative()
{
_c4dbgp("remove speculative node");
_RYML_CB_ASSERT(m_stack.m_callbacks, m_tree);
_RYML_CB_ASSERT(m_tree->callbacks(), !m_tree->empty());
const id_type last_added = m_tree->size() - 1;
if(m_tree->has_parent(last_added))
if(m_tree->_p(last_added)->m_type == NOTYPE)
m_tree->remove(last_added);
}
void _remove_speculative_with_parent()
{
_RYML_CB_ASSERT(m_stack.m_callbacks, m_tree);
_RYML_CB_ASSERT(m_tree->callbacks(), !m_tree->empty());
const id_type last_added = m_tree->size() - 1;
_RYML_CB_ASSERT(m_tree->callbacks(), m_tree->has_parent(last_added));
if(m_tree->_p(last_added)->m_type == NOTYPE)
{
_c4dbgpf("remove speculative node with parent. parent={} node={} parent(node)={}", m_parent->node_id, last_added, m_tree->parent(last_added));
m_tree->remove(last_added);
}
}
C4_ALWAYS_INLINE void _save_loc()
{
_RYML_CB_ASSERT(m_stack.m_callbacks, m_tree);
_RYML_CB_ASSERT(m_tree->callbacks(), m_tree->_p(m_curr->node_id)->m_val.scalar.len == 0);
m_tree->_p(m_curr->node_id)->m_val.scalar.str = m_curr->line_contents.rem.str;
}
#undef _enable_
#undef _disable_
#undef _has_any_
/** @endcond */
};
/** @} */
} // namespace yml
} // namespace c4
// NOLINTEND(hicpp-signed-bitwise)
C4_SUPPRESS_WARNING_MSVC_POP
#endif /* _C4_YML_EVENT_HANDLER_TREE_HPP_ */

View File

@@ -0,0 +1,512 @@
#ifndef _C4_YML_FILTER_PROCESSOR_HPP_
#define _C4_YML_FILTER_PROCESSOR_HPP_
#include "c4/yml/common.hpp"
#ifdef RYML_DBG
#include "c4/charconv.hpp"
#include "c4/yml/detail/parser_dbg.hpp"
#endif
namespace c4 {
namespace yml {
/** @defgroup doc_filter_processors Scalar filter processors
*
* These are internal classes used by @ref ParseEngine to parse the
* scalars; normally there is no reason for a user to be manually
* using these classes.
*
* @ingroup doc_parse */
/** @{ */
//-----------------------------------------------------------------------------
/** Filters an input string into a different output string */
struct FilterProcessorSrcDst
{
csubstr src;
substr dst;
size_t rpos; ///< read position
size_t wpos; ///< write position
C4_ALWAYS_INLINE FilterProcessorSrcDst(csubstr src_, substr dst_) noexcept
: src(src_)
, dst(dst_)
, rpos(0)
, wpos(0)
{
RYML_ASSERT(!dst.overlaps(src));
}
C4_ALWAYS_INLINE void setwpos(size_t wpos_) noexcept { wpos = wpos_; }
C4_ALWAYS_INLINE void setpos(size_t rpos_, size_t wpos_) noexcept { rpos = rpos_; wpos = wpos_; }
C4_ALWAYS_INLINE void set_at_end() noexcept { skip(src.len - rpos); }
C4_ALWAYS_INLINE bool has_more_chars() const noexcept { return rpos < src.len; }
C4_ALWAYS_INLINE bool has_more_chars(size_t maxpos) const noexcept { RYML_ASSERT(maxpos <= src.len); return rpos < maxpos; }
C4_ALWAYS_INLINE csubstr rem() const noexcept { return src.sub(rpos); }
C4_ALWAYS_INLINE csubstr sofar() const noexcept { return csubstr(dst.str, wpos <= dst.len ? wpos : dst.len); }
C4_ALWAYS_INLINE FilterResult result() const noexcept
{
FilterResult ret;
ret.str.str = wpos <= dst.len ? dst.str : nullptr;
ret.str.len = wpos;
return ret;
}
C4_ALWAYS_INLINE char curr() const noexcept { RYML_ASSERT(rpos < src.len); return src[rpos]; }
C4_ALWAYS_INLINE char next() const noexcept { return rpos+1 < src.len ? src[rpos+1] : '\0'; }
C4_ALWAYS_INLINE bool skipped_chars() const noexcept { return wpos != rpos; }
C4_ALWAYS_INLINE void skip() noexcept { ++rpos; }
C4_ALWAYS_INLINE void skip(size_t num) noexcept { rpos += num; }
C4_ALWAYS_INLINE void set_at(size_t pos, char c) noexcept // NOLINT(readability-make-member-function-const)
{
RYML_ASSERT(pos < wpos);
dst.str[pos] = c;
}
C4_ALWAYS_INLINE void set(char c) noexcept
{
if(wpos < dst.len)
dst.str[wpos] = c;
++wpos;
}
C4_ALWAYS_INLINE void set(char c, size_t num) noexcept
{
RYML_ASSERT(num > 0);
if(wpos + num <= dst.len)
memset(dst.str + wpos, c, num);
wpos += num;
}
C4_ALWAYS_INLINE void copy() noexcept
{
RYML_ASSERT(rpos < src.len);
if(wpos < dst.len)
dst.str[wpos] = src.str[rpos];
++wpos;
++rpos;
}
C4_ALWAYS_INLINE void copy(size_t num) noexcept
{
RYML_ASSERT(num);
RYML_ASSERT(rpos+num <= src.len);
if(wpos + num <= dst.len)
memcpy(dst.str + wpos, src.str + rpos, num);
wpos += num;
rpos += num;
}
C4_ALWAYS_INLINE void translate_esc(char c) noexcept
{
if(wpos < dst.len)
dst.str[wpos] = c;
++wpos;
rpos += 2;
}
C4_ALWAYS_INLINE void translate_esc_bulk(const char *C4_RESTRICT s, size_t nw, size_t nr) noexcept
{
RYML_ASSERT(nw > 0);
RYML_ASSERT(nr > 0);
RYML_ASSERT(rpos+nr <= src.len);
if(wpos+nw <= dst.len)
memcpy(dst.str + wpos, s, nw);
wpos += nw;
rpos += 1 + nr;
}
C4_ALWAYS_INLINE void translate_esc_extending(const char *C4_RESTRICT s, size_t nw, size_t nr) noexcept
{
translate_esc_bulk(s, nw, nr);
}
};
//-----------------------------------------------------------------------------
// filter in place
// debugging scaffold
/** @cond dev */
#if defined(RYML_DBG) && 0
#define _c4dbgip(...) _c4dbgpf(__VA_ARGS__)
#else
#define _c4dbgip(...)
#endif
/** @endcond */
/** Filters in place. While the result may be larger than the source,
* any extending happens only at the end of the string. Consequently,
* it's impossible for characters to be left unfiltered.
*
* @see FilterProcessorInplaceMidExtending */
struct FilterProcessorInplaceEndExtending
{
substr src; ///< the subject string
size_t wcap; ///< write capacity - the capacity of the subject string's buffer
size_t rpos; ///< read position
size_t wpos; ///< write position
C4_ALWAYS_INLINE FilterProcessorInplaceEndExtending(substr src_, size_t wcap_) noexcept
: src(src_)
, wcap(wcap_)
, rpos(0)
, wpos(0)
{
RYML_ASSERT(wcap >= src.len);
}
C4_ALWAYS_INLINE void setwpos(size_t wpos_) noexcept { wpos = wpos_; }
C4_ALWAYS_INLINE void setpos(size_t rpos_, size_t wpos_) noexcept { rpos = rpos_; wpos = wpos_; }
C4_ALWAYS_INLINE void set_at_end() noexcept { skip(src.len - rpos); }
C4_ALWAYS_INLINE bool has_more_chars() const noexcept { return rpos < src.len; }
C4_ALWAYS_INLINE bool has_more_chars(size_t maxpos) const noexcept { RYML_ASSERT(maxpos <= src.len); return rpos < maxpos; }
C4_ALWAYS_INLINE FilterResult result() const noexcept
{
_c4dbgip("inplace: wpos={} wcap={} small={}", wpos, wcap, wpos > rpos);
FilterResult ret;
ret.str.str = (wpos <= wcap) ? src.str : nullptr;
ret.str.len = wpos;
return ret;
}
C4_ALWAYS_INLINE csubstr sofar() const noexcept { return csubstr(src.str, wpos <= wcap ? wpos : wcap); }
C4_ALWAYS_INLINE csubstr rem() const noexcept { return src.sub(rpos); }
C4_ALWAYS_INLINE char curr() const noexcept { RYML_ASSERT(rpos < src.len); return src[rpos]; }
C4_ALWAYS_INLINE char next() const noexcept { return rpos+1 < src.len ? src[rpos+1] : '\0'; }
C4_ALWAYS_INLINE void skip() noexcept { ++rpos; }
C4_ALWAYS_INLINE void skip(size_t num) noexcept { rpos += num; }
void set_at(size_t pos, char c) noexcept
{
RYML_ASSERT(pos < wpos);
const size_t save = wpos;
wpos = pos;
set(c);
wpos = save;
}
void set(char c) noexcept
{
if(wpos < wcap) // respect write-capacity
src.str[wpos] = c;
++wpos;
}
void set(char c, size_t num) noexcept
{
RYML_ASSERT(num);
if(wpos + num <= wcap) // respect write-capacity
memset(src.str + wpos, c, num);
wpos += num;
}
void copy() noexcept
{
RYML_ASSERT(wpos <= rpos);
RYML_ASSERT(rpos < src.len);
if(wpos < wcap) // respect write-capacity
src.str[wpos] = src.str[rpos];
++rpos;
++wpos;
}
void copy(size_t num) noexcept
{
RYML_ASSERT(num);
RYML_ASSERT(rpos+num <= src.len);
RYML_ASSERT(wpos <= rpos);
if(wpos + num <= wcap) // respect write-capacity
{
if(wpos + num <= rpos) // there is no overlap
memcpy(src.str + wpos, src.str + rpos, num);
else // there is overlap
memmove(src.str + wpos, src.str + rpos, num);
}
rpos += num;
wpos += num;
}
void translate_esc(char c) noexcept
{
RYML_ASSERT(rpos + 2 <= src.len);
RYML_ASSERT(wpos <= rpos);
if(wpos < wcap) // respect write-capacity
src.str[wpos] = c;
rpos += 2; // add 1u to account for the escape character
++wpos;
}
void translate_esc_bulk(const char *C4_RESTRICT s, size_t nw, size_t nr) noexcept
{
RYML_ASSERT(nw > 0);
RYML_ASSERT(nr > 0);
RYML_ASSERT(nw <= nr + 1u);
RYML_ASSERT(rpos+nr <= src.len);
RYML_ASSERT(wpos <= rpos);
const size_t wpos_next = wpos + nw;
const size_t rpos_next = rpos + nr + 1u; // add 1u to account for the escape character
RYML_ASSERT(wpos_next <= rpos_next);
if(wpos_next <= wcap)
memcpy(src.str + wpos, s, nw);
rpos = rpos_next;
wpos = wpos_next;
}
C4_ALWAYS_INLINE void translate_esc_extending(const char *C4_RESTRICT s, size_t nw, size_t nr) noexcept
{
translate_esc_bulk(s, nw, nr);
}
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** Filters in place. The result may be larger than the source, and
* extending may happen anywhere. As a result some characters may be
* left unfiltered when there is no slack in the buffer and the
* write-position would overlap the read-position. Consequently, it's
* possible for characters to be left unfiltered. In YAML, this
* happens only with double-quoted strings, and only with a small
* number of escape sequences such as `\L` which is substituted by three
* bytes. These escape sequences cause a call to translate_esc_extending()
* which is the only entry point to this unfiltered situation.
*
* @see FilterProcessorInplaceMidExtending */
struct FilterProcessorInplaceMidExtending
{
substr src; ///< the subject string
size_t wcap; ///< write capacity - the capacity of the subject string's buffer
size_t rpos; ///< read position
size_t wpos; ///< write position
size_t maxcap; ///< the max capacity needed for filtering the string. This may be larger than the final string size.
bool unfiltered_chars; ///< number of characters that were not added to wpos from lack of capacity
C4_ALWAYS_INLINE FilterProcessorInplaceMidExtending(substr src_, size_t wcap_) noexcept
: src(src_)
, wcap(wcap_)
, rpos(0)
, wpos(0)
, maxcap(src.len)
, unfiltered_chars(false)
{
RYML_ASSERT(wcap >= src.len);
}
C4_ALWAYS_INLINE void setwpos(size_t wpos_) noexcept { wpos = wpos_; }
C4_ALWAYS_INLINE void setpos(size_t rpos_, size_t wpos_) noexcept { rpos = rpos_; wpos = wpos_; }
C4_ALWAYS_INLINE void set_at_end() noexcept { skip(src.len - rpos); }
C4_ALWAYS_INLINE bool has_more_chars() const noexcept { return rpos < src.len; }
C4_ALWAYS_INLINE bool has_more_chars(size_t maxpos) const noexcept { RYML_ASSERT(maxpos <= src.len); return rpos < maxpos; }
C4_ALWAYS_INLINE FilterResultExtending result() const noexcept
{
_c4dbgip("inplace: wpos={} wcap={} unfiltered={} maxcap={}", this->wpos, this->wcap, this->unfiltered_chars, this->maxcap);
FilterResultExtending ret;
ret.str.str = (wpos <= wcap && !unfiltered_chars) ? src.str : nullptr;
ret.str.len = wpos;
ret.reqlen = maxcap;
return ret;
}
C4_ALWAYS_INLINE csubstr sofar() const noexcept { return csubstr(src.str, wpos <= wcap ? wpos : wcap); }
C4_ALWAYS_INLINE csubstr rem() const noexcept { return src.sub(rpos); }
C4_ALWAYS_INLINE char curr() const noexcept { RYML_ASSERT(rpos < src.len); return src[rpos]; }
C4_ALWAYS_INLINE char next() const noexcept { return rpos+1 < src.len ? src[rpos+1] : '\0'; }
C4_ALWAYS_INLINE void skip() noexcept { ++rpos; }
C4_ALWAYS_INLINE void skip(size_t num) noexcept { rpos += num; }
void set_at(size_t pos, char c) noexcept
{
RYML_ASSERT(pos < wpos);
const size_t save = wpos;
wpos = pos;
set(c);
wpos = save;
}
void set(char c) noexcept
{
if(wpos < wcap) // respect write-capacity
{
if((wpos <= rpos) && !unfiltered_chars)
src.str[wpos] = c;
}
else
{
_c4dbgip("inplace: add unwritten {}->{} maxcap={}->{}!", unfiltered_chars, true, maxcap, (wpos+1u > maxcap ? wpos+1u : maxcap));
unfiltered_chars = true;
}
++wpos;
maxcap = wpos > maxcap ? wpos : maxcap;
}
void set(char c, size_t num) noexcept
{
RYML_ASSERT(num);
if(wpos + num <= wcap) // respect write-capacity
{
if((wpos <= rpos) && !unfiltered_chars)
memset(src.str + wpos, c, num);
}
else
{
_c4dbgip("inplace: add unwritten {}->{} maxcap={}->{}!", unfiltered_chars, true, maxcap, (wpos+num > maxcap ? wpos+num : maxcap));
unfiltered_chars = true;
}
wpos += num;
maxcap = wpos > maxcap ? wpos : maxcap;
}
void copy() noexcept
{
RYML_ASSERT(rpos < src.len);
if(wpos < wcap) // respect write-capacity
{
if((wpos < rpos) && !unfiltered_chars) // write only if wpos is behind rpos
src.str[wpos] = src.str[rpos];
}
else
{
_c4dbgip("inplace: add unwritten {}->{} (wpos={}!=rpos={})={} (wpos={}<wcap={}) maxcap={}->{}!", unfiltered_chars, true, wpos, rpos, wpos!=rpos, wpos, wcap, wpos<wcap, maxcap, (wpos+1u > maxcap ? wpos+1u : maxcap));
unfiltered_chars = true;
}
++rpos;
++wpos;
maxcap = wpos > maxcap ? wpos : maxcap;
}
void copy(size_t num) noexcept
{
RYML_ASSERT(num);
RYML_ASSERT(rpos+num <= src.len);
if(wpos + num <= wcap) // respect write-capacity
{
if((wpos < rpos) && !unfiltered_chars) // write only if wpos is behind rpos
{
if(wpos + num <= rpos) // there is no overlap
memcpy(src.str + wpos, src.str + rpos, num);
else // there is overlap
memmove(src.str + wpos, src.str + rpos, num);
}
}
else
{
_c4dbgip("inplace: add unwritten {}->{} (wpos={}!=rpos={})={} (wpos={}<wcap={}) maxcap={}->{}!", unfiltered_chars, true, wpos, rpos, wpos!=rpos, wpos, wcap, wpos<wcap);
unfiltered_chars = true;
}
rpos += num;
wpos += num;
maxcap = wpos > maxcap ? wpos : maxcap;
}
void translate_esc(char c) noexcept
{
RYML_ASSERT(rpos + 2 <= src.len);
if(wpos < wcap) // respect write-capacity
{
if((wpos <= rpos) && !unfiltered_chars)
src.str[wpos] = c;
}
else
{
_c4dbgip("inplace: add unfiltered {}->{} maxcap={}->{}!", unfiltered_chars, true, maxcap, (wpos+1u > maxcap ? wpos+1u : maxcap));
unfiltered_chars = true;
}
rpos += 2;
++wpos;
maxcap = wpos > maxcap ? wpos : maxcap;
}
C4_NO_INLINE void translate_esc_bulk(const char *C4_RESTRICT s, size_t nw, size_t nr) noexcept
{
RYML_ASSERT(nw > 0);
RYML_ASSERT(nr > 0);
RYML_ASSERT(nr+1u >= nw);
const size_t wpos_next = wpos + nw;
const size_t rpos_next = rpos + nr + 1u; // add 1u to account for the escape character
if(wpos_next <= wcap) // respect write-capacity
{
if((wpos <= rpos) && !unfiltered_chars) // write only if wpos is behind rpos
memcpy(src.str + wpos, s, nw);
}
else
{
_c4dbgip("inplace: add unwritten {}->{} (wpos={}!=rpos={})={} (wpos={}<wcap={}) maxcap={}->{}!", unfiltered_chars, true, wpos, rpos, wpos!=rpos, wpos, wcap, wpos<wcap);
unfiltered_chars = true;
}
rpos = rpos_next;
wpos = wpos_next;
maxcap = wpos > maxcap ? wpos : maxcap;
}
C4_NO_INLINE void translate_esc_extending(const char *C4_RESTRICT s, size_t nw, size_t nr) noexcept
{
RYML_ASSERT(nw > 0);
RYML_ASSERT(nr > 0);
RYML_ASSERT(rpos+nr <= src.len);
const size_t wpos_next = wpos + nw;
const size_t rpos_next = rpos + nr + 1u; // add 1u to account for the escape character
if(wpos_next <= rpos_next) // read and write do not overlap. just do a vanilla copy.
{
if((wpos_next <= wcap) && !unfiltered_chars)
memcpy(src.str + wpos, s, nw);
rpos = rpos_next;
wpos = wpos_next;
maxcap = wpos > maxcap ? wpos : maxcap;
}
else // there is overlap. move the (to-be-read) string to the right.
{
const size_t excess = wpos_next - rpos_next;
RYML_ASSERT(wpos_next > rpos_next);
if(src.len + excess <= wcap) // ensure we do not go past the end
{
RYML_ASSERT(rpos+nr+excess <= src.len);
if(wpos_next <= wcap)
{
if(!unfiltered_chars)
{
memmove(src.str + wpos_next, src.str + rpos_next, src.len - rpos_next);
memcpy(src.str + wpos, s, nw);
}
rpos = wpos_next; // wpos, not rpos
}
else
{
rpos = rpos_next;
//const size_t unw = nw > (nr + 1u) ? nw - (nr + 1u) : 0;
_c4dbgip("inplace: add unfiltered {}->{} maxcap={}->{}!", unfiltered_chars, true);
unfiltered_chars = true;
}
wpos = wpos_next;
// extend the string up to capacity
src.len += excess;
maxcap = wpos > maxcap ? wpos : maxcap;
}
else
{
//const size_t unw = nw > (nr + 1u) ? nw - (nr + 1u) : 0;
RYML_ASSERT(rpos_next <= src.len);
const size_t required_size = wpos_next + (src.len - rpos_next);
_c4dbgip("inplace: add unfiltered {}->{} maxcap={}->{}!", unfiltered_chars, true, maxcap, required_size > maxcap ? required_size : maxcap);
RYML_ASSERT(required_size > wcap);
unfiltered_chars = true;
maxcap = required_size > maxcap ? required_size : maxcap;
wpos = wpos_next;
rpos = rpos_next;
}
}
}
};
#undef _c4dbgip
/** @} */
} // namespace yml
} // namespace c4
#endif /* _C4_YML_FILTER_PROCESSOR_HPP_ */

View File

@@ -0,0 +1,24 @@
#ifndef _C4_YML_FWD_HPP_
#define _C4_YML_FWD_HPP_
/** @file fwd.hpp forward declarations */
namespace c4 {
namespace yml {
struct NodeScalar;
struct NodeInit;
struct NodeData;
struct NodeType;
class NodeRef;
class ConstNodeRef;
class Tree;
struct ReferenceResolver;
template<class EventHandler> class ParseEngine;
struct EventHandlerTree;
using Parser = ParseEngine<EventHandlerTree>;
} // namespace c4
} // namespace yml
#endif /* _C4_YML_FWD_HPP_ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,275 @@
#ifndef C4_YML_NODE_TYPE_HPP_
#define C4_YML_NODE_TYPE_HPP_
#ifndef _C4_YML_COMMON_HPP_
#include "c4/yml/common.hpp"
#endif
C4_SUPPRESS_WARNING_MSVC_PUSH
C4_SUPPRESS_WARNING_GCC_CLANG_PUSH
C4_SUPPRESS_WARNING_GCC_CLANG("-Wold-style-cast")
namespace c4 {
namespace yml {
/** @addtogroup doc_node_type
*
* @{
*/
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** the integral type necessary to cover all the bits for NodeType_e */
using type_bits = uint32_t;
/** a bit mask for marking node types and styles */
typedef enum : type_bits {
#define __(v) (type_bits(1) << v) // a convenience define, undefined below // NOLINT
NOTYPE = 0, ///< no node type or style is set
KEY = __(0), ///< is member of a map
VAL = __(1), ///< a scalar: has a scalar (ie string) value, possibly empty. must be a leaf node, and cannot be MAP or SEQ
MAP = __(2), ///< a map: a parent of KEYVAL/KEYSEQ/KEYMAP nodes
SEQ = __(3), ///< a seq: a parent of VAL/SEQ/MAP nodes
DOC = __(4), ///< a document
STREAM = __(5)|SEQ, ///< a stream: a seq of docs
KEYREF = __(6), ///< a *reference: the key references an &anchor
VALREF = __(7), ///< a *reference: the val references an &anchor
KEYANCH = __(8), ///< the key has an &anchor
VALANCH = __(9), ///< the val has an &anchor
KEYTAG = __(10), ///< the key has a tag
VALTAG = __(11), ///< the val has a tag
KEYNIL = __(12), ///< the key is null (eg `{ : b}` results in a null key)
VALNIL = __(13), ///< the val is null (eg `{a : }` results in a null val)
_TYMASK = __(14)-1, ///< all the bits up to here
//
// unfiltered flags:
//
KEY_UNFILT = __(14), ///< the key scalar was left unfiltered; the parser was set not to filter. @see ParserOptions
VAL_UNFILT = __(15), ///< the val scalar was left unfiltered; the parser was set not to filter. @see ParserOptions
//
// style flags:
//
FLOW_SL = __(16), ///< mark container with single-line flow style (seqs as '[val1,val2], maps as '{key: val,key2: val2}')
FLOW_ML = __(17), ///< (NOT IMPLEMENTED, work in progress) mark container with multi-line flow style (seqs as '[\n val1,\n val2\n], maps as '{\n key: val,\n key2: val2\n}')
BLOCK = __(18), ///< mark container with block style (seqs as '- val\n', maps as 'key: val')
KEY_LITERAL = __(19), ///< mark key scalar as multiline, block literal |
VAL_LITERAL = __(20), ///< mark val scalar as multiline, block literal |
KEY_FOLDED = __(21), ///< mark key scalar as multiline, block folded >
VAL_FOLDED = __(22), ///< mark val scalar as multiline, block folded >
KEY_SQUO = __(23), ///< mark key scalar as single quoted '
VAL_SQUO = __(24), ///< mark val scalar as single quoted '
KEY_DQUO = __(25), ///< mark key scalar as double quoted "
VAL_DQUO = __(26), ///< mark val scalar as double quoted "
KEY_PLAIN = __(27), ///< mark key scalar as plain scalar (unquoted, even when multiline)
VAL_PLAIN = __(28), ///< mark val scalar as plain scalar (unquoted, even when multiline)
//
// type combination masks:
//
KEYVAL = KEY|VAL,
KEYSEQ = KEY|SEQ,
KEYMAP = KEY|MAP,
DOCMAP = DOC|MAP,
DOCSEQ = DOC|SEQ,
DOCVAL = DOC|VAL,
//
// style combination masks:
//
SCALAR_LITERAL = KEY_LITERAL|VAL_LITERAL,
SCALAR_FOLDED = KEY_FOLDED|VAL_FOLDED,
SCALAR_SQUO = KEY_SQUO|VAL_SQUO,
SCALAR_DQUO = KEY_DQUO|VAL_DQUO,
SCALAR_PLAIN = KEY_PLAIN|VAL_PLAIN,
KEYQUO = KEY_SQUO|KEY_DQUO|KEY_FOLDED|KEY_LITERAL, ///< key style is one of ', ", > or |
VALQUO = VAL_SQUO|VAL_DQUO|VAL_FOLDED|VAL_LITERAL, ///< val style is one of ', ", > or |
KEY_STYLE = KEY_LITERAL|KEY_FOLDED|KEY_SQUO|KEY_DQUO|KEY_PLAIN, ///< mask of all the scalar styles for key (not container styles!)
VAL_STYLE = VAL_LITERAL|VAL_FOLDED|VAL_SQUO|VAL_DQUO|VAL_PLAIN, ///< mask of all the scalar styles for val (not container styles!)
SCALAR_STYLE = KEY_STYLE|VAL_STYLE,
CONTAINER_STYLE_FLOW = FLOW_SL|FLOW_ML,
CONTAINER_STYLE_BLOCK = BLOCK,
CONTAINER_STYLE = FLOW_SL|FLOW_ML|BLOCK,
STYLE = SCALAR_STYLE | CONTAINER_STYLE,
//
// mixed masks
_KEYMASK = KEY | KEYQUO | KEYANCH | KEYREF | KEYTAG,
_VALMASK = VAL | VALQUO | VALANCH | VALREF | VALTAG,
#undef __
} NodeType_e;
constexpr C4_ALWAYS_INLINE C4_CONST NodeType_e operator| (NodeType_e lhs, NodeType_e rhs) noexcept { return (NodeType_e)(((type_bits)lhs) | ((type_bits)rhs)); }
constexpr C4_ALWAYS_INLINE C4_CONST NodeType_e operator& (NodeType_e lhs, NodeType_e rhs) noexcept { return (NodeType_e)(((type_bits)lhs) & ((type_bits)rhs)); }
constexpr C4_ALWAYS_INLINE C4_CONST NodeType_e operator>> (NodeType_e bits, uint32_t n) noexcept { return (NodeType_e)(((type_bits)bits) >> n); }
constexpr C4_ALWAYS_INLINE C4_CONST NodeType_e operator<< (NodeType_e bits, uint32_t n) noexcept { return (NodeType_e)(((type_bits)bits) << n); }
constexpr C4_ALWAYS_INLINE C4_CONST NodeType_e operator~ (NodeType_e bits) noexcept { return (NodeType_e)(~(type_bits)bits); }
C4_ALWAYS_INLINE NodeType_e& operator&= (NodeType_e &subject, NodeType_e bits) noexcept { subject = (NodeType_e)((type_bits)subject & (type_bits)bits); return subject; }
C4_ALWAYS_INLINE NodeType_e& operator|= (NodeType_e &subject, NodeType_e bits) noexcept { subject = (NodeType_e)((type_bits)subject | (type_bits)bits); return subject; }
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** wraps a NodeType_e element with some syntactic sugar and predicates */
struct RYML_EXPORT NodeType
{
public:
NodeType_e type;
public:
C4_ALWAYS_INLINE NodeType() noexcept : type(NOTYPE) {}
C4_ALWAYS_INLINE NodeType(NodeType_e t) noexcept : type(t) {}
C4_ALWAYS_INLINE NodeType(type_bits t) noexcept : type((NodeType_e)t) {}
C4_ALWAYS_INLINE bool has_any(NodeType_e t) const noexcept { return (type & t) != 0u; }
C4_ALWAYS_INLINE bool has_all(NodeType_e t) const noexcept { return (type & t) == t; }
C4_ALWAYS_INLINE bool has_none(NodeType_e t) const noexcept { return (type & t) == 0; }
C4_ALWAYS_INLINE void set(NodeType_e t) noexcept { type = t; }
C4_ALWAYS_INLINE void add(NodeType_e t) noexcept { type = (type|t); }
C4_ALWAYS_INLINE void rem(NodeType_e t) noexcept { type = (type & ~t); }
C4_ALWAYS_INLINE void addrem(NodeType_e bits_to_add, NodeType_e bits_to_remove) noexcept { type |= bits_to_add; type &= ~bits_to_remove; }
C4_ALWAYS_INLINE void clear() noexcept { type = NOTYPE; }
public:
C4_ALWAYS_INLINE operator NodeType_e & C4_RESTRICT () noexcept { return type; }
C4_ALWAYS_INLINE operator NodeType_e const& C4_RESTRICT () const noexcept { return type; }
public:
/** @name node type queries
* @{ */
/** return a preset string based on the node type */
C4_ALWAYS_INLINE const char *type_str() const noexcept { return type_str(type); }
/** return a preset string based on the node type */
static const char* type_str(NodeType_e t) noexcept;
/** fill a string with the node type flags. If the string is small, returns {null, len} */
C4_ALWAYS_INLINE csubstr type_str(substr buf) const noexcept { return type_str(buf, type); }
/** fill a string with the node type flags. If the string is small, returns {null, len} */
static csubstr type_str(substr buf, NodeType_e t) noexcept;
public:
/** @name node type queries
* @{ */
C4_ALWAYS_INLINE bool is_notype() const noexcept { return type == NOTYPE; }
C4_ALWAYS_INLINE bool is_stream() const noexcept { return ((type & STREAM) == STREAM) != 0; }
C4_ALWAYS_INLINE bool is_doc() const noexcept { return (type & DOC) != 0; }
C4_ALWAYS_INLINE bool is_container() const noexcept { return (type & (MAP|SEQ|STREAM)) != 0; }
C4_ALWAYS_INLINE bool is_map() const noexcept { return (type & MAP) != 0; }
C4_ALWAYS_INLINE bool is_seq() const noexcept { return (type & SEQ) != 0; }
C4_ALWAYS_INLINE bool has_key() const noexcept { return (type & KEY) != 0; }
C4_ALWAYS_INLINE bool has_val() const noexcept { return (type & VAL) != 0; }
C4_ALWAYS_INLINE bool is_val() const noexcept { return (type & KEYVAL) == VAL; }
C4_ALWAYS_INLINE bool is_keyval() const noexcept { return (type & KEYVAL) == KEYVAL; }
C4_ALWAYS_INLINE bool key_is_null() const noexcept { return (type & KEYNIL) != 0; }
C4_ALWAYS_INLINE bool val_is_null() const noexcept { return (type & VALNIL) != 0; }
C4_ALWAYS_INLINE bool has_key_tag() const noexcept { return (type & KEYTAG) != 0; }
C4_ALWAYS_INLINE bool has_val_tag() const noexcept { return (type & VALTAG) != 0; }
C4_ALWAYS_INLINE bool has_key_anchor() const noexcept { return (type & KEYANCH) != 0; }
C4_ALWAYS_INLINE bool has_val_anchor() const noexcept { return (type & VALANCH) != 0; }
C4_ALWAYS_INLINE bool has_anchor() const noexcept { return (type & (KEYANCH|VALANCH)) != 0; }
C4_ALWAYS_INLINE bool is_key_ref() const noexcept { return (type & KEYREF) != 0; }
C4_ALWAYS_INLINE bool is_val_ref() const noexcept { return (type & VALREF) != 0; }
C4_ALWAYS_INLINE bool is_ref() const noexcept { return (type & (KEYREF|VALREF)) != 0; }
C4_ALWAYS_INLINE bool is_key_unfiltered() const noexcept { return (type & (KEY_UNFILT)) != 0; }
C4_ALWAYS_INLINE bool is_val_unfiltered() const noexcept { return (type & (VAL_UNFILT)) != 0; }
RYML_DEPRECATED("use has_key_anchor()") bool is_key_anchor() const noexcept { return has_key_anchor(); }
RYML_DEPRECATED("use has_val_anchor()") bool is_val_anchor() const noexcept { return has_val_anchor(); }
RYML_DEPRECATED("use has_anchor()") bool is_anchor() const noexcept { return has_anchor(); }
RYML_DEPRECATED("use has_anchor() || is_ref()") bool is_anchor_or_ref() const noexcept { return has_anchor() || is_ref(); }
/** @} */
public:
/** @name container+scalar style queries
* @{ */
C4_ALWAYS_INLINE bool is_container_styled() const noexcept { return (type & (CONTAINER_STYLE)) != 0; }
C4_ALWAYS_INLINE bool is_block() const noexcept { return (type & (BLOCK)) != 0; }
C4_ALWAYS_INLINE bool is_flow_sl() const noexcept { return (type & (FLOW_SL)) != 0; }
C4_ALWAYS_INLINE bool is_flow_ml() const noexcept { return (type & (FLOW_ML)) != 0; }
C4_ALWAYS_INLINE bool is_flow() const noexcept { return (type & (FLOW_ML|FLOW_SL)) != 0; }
C4_ALWAYS_INLINE bool is_key_styled() const noexcept { return (type & (KEY_STYLE)) != 0; }
C4_ALWAYS_INLINE bool is_val_styled() const noexcept { return (type & (VAL_STYLE)) != 0; }
C4_ALWAYS_INLINE bool is_key_literal() const noexcept { return (type & (KEY_LITERAL)) != 0; }
C4_ALWAYS_INLINE bool is_val_literal() const noexcept { return (type & (VAL_LITERAL)) != 0; }
C4_ALWAYS_INLINE bool is_key_folded() const noexcept { return (type & (KEY_FOLDED)) != 0; }
C4_ALWAYS_INLINE bool is_val_folded() const noexcept { return (type & (VAL_FOLDED)) != 0; }
C4_ALWAYS_INLINE bool is_key_squo() const noexcept { return (type & (KEY_SQUO)) != 0; }
C4_ALWAYS_INLINE bool is_val_squo() const noexcept { return (type & (VAL_SQUO)) != 0; }
C4_ALWAYS_INLINE bool is_key_dquo() const noexcept { return (type & (KEY_DQUO)) != 0; }
C4_ALWAYS_INLINE bool is_val_dquo() const noexcept { return (type & (VAL_DQUO)) != 0; }
C4_ALWAYS_INLINE bool is_key_plain() const noexcept { return (type & (KEY_PLAIN)) != 0; }
C4_ALWAYS_INLINE bool is_val_plain() const noexcept { return (type & (VAL_PLAIN)) != 0; }
C4_ALWAYS_INLINE bool is_key_quoted() const noexcept { return (type & KEYQUO) != 0; }
C4_ALWAYS_INLINE bool is_val_quoted() const noexcept { return (type & VALQUO) != 0; }
C4_ALWAYS_INLINE bool is_quoted() const noexcept { return (type & (KEYQUO|VALQUO)) != 0; }
C4_ALWAYS_INLINE void set_container_style(NodeType_e style) noexcept { type = ((style & CONTAINER_STYLE) | (type & ~CONTAINER_STYLE)); }
C4_ALWAYS_INLINE void set_key_style(NodeType_e style) noexcept { type = ((style & KEY_STYLE) | (type & ~KEY_STYLE)); }
C4_ALWAYS_INLINE void set_val_style(NodeType_e style) noexcept { type = ((style & VAL_STYLE) | (type & ~VAL_STYLE)); }
/** @} */
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** @name scalar style helpers
* @{ */
/** choose a YAML emitting style based on the scalar's contents */
RYML_EXPORT NodeType_e scalar_style_choose(csubstr scalar) noexcept;
/** choose a json style based on the scalar's contents */
RYML_EXPORT NodeType_e scalar_style_json_choose(csubstr scalar) noexcept;
/** query whether a scalar can be encoded using single quotes.
* It may not be possible, notably when there is leading
* whitespace after a newline. */
RYML_EXPORT bool scalar_style_query_squo(csubstr s) noexcept;
/** query whether a scalar can be encoded using plain style (no
* quotes, not a literal/folded block scalar). */
RYML_EXPORT bool scalar_style_query_plain(csubstr s) noexcept;
/** YAML-sense query of nullity. returns true if the scalar points
* to `nullptr` or is otherwise equal to one of the strings
* `"~"`,`"null"`,`"Null"`,`"NULL"` */
RYML_EXPORT inline C4_NO_INLINE bool scalar_is_null(csubstr s) noexcept
{
return s.str == nullptr ||
s == "~" ||
s == "null" ||
s == "Null" ||
s == "NULL";
}
/** @} */
/** @} */
} // namespace yml
} // namespace c4
C4_SUPPRESS_WARNING_MSVC_POP
C4_SUPPRESS_WARNING_GCC_CLANG_POP
#endif /* C4_YML_NODE_TYPE_HPP_ */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,791 @@
#ifndef _C4_YML_PARSE_ENGINE_HPP_
#define _C4_YML_PARSE_ENGINE_HPP_
#ifndef _C4_YML_DETAIL_PARSER_DBG_HPP_
#include "c4/yml/detail/parser_dbg.hpp"
#endif
#ifndef _C4_YML_PARSER_STATE_HPP_
#include "c4/yml/parser_state.hpp"
#endif
#if defined(_MSC_VER)
# pragma warning(push)
# pragma warning(disable: 4251/*needs to have dll-interface to be used by clients of struct*/)
#endif
// NOLINTBEGIN(hicpp-signed-bitwise)
namespace c4 {
namespace yml {
/** @addtogroup doc_parse
* @{ */
/** @defgroup doc_event_handlers Event Handlers
*
* @brief rapidyaml implements its parsing logic with a two-level
* model, where a @ref ParseEngine object reads through the YAML
* source, and dispatches events to an EventHandler bound to the @ref
* ParseEngine. Because @ref ParseEngine is templated on the event
* handler, the binding uses static polymorphism, without any virtual
* functions. The actual handler object can be changed at run time,
* (but of course needs to be the type of the template parameter).
* This is thus a very efficient architecture, and further enables the
* user to provide his own custom handler if he wishes to bypass the
* rapidyaml @ref Tree.
*
* There are two handlers implemented in this project:
*
* - @ref EventHandlerTree is the handler responsible for creating the
* ryml @ref Tree
*
* - @ref EventHandlerYamlStd is the handler responsible for emitting
* standardized [YAML test suite
* events](https://github.com/yaml/yaml-test-suite), used (only) in
* the CI of this project.
*
*
* ### Event model
*
* The event model used by the parse engine and event handlers follows
* very closely the event model in the [YAML test
* suite](https://github.com/yaml/yaml-test-suite).
*
* Consider for example this YAML,
* ```yaml
* {foo: bar,foo2: bar2}
* ```
* which would produce these events in the test-suite parlance:
* ```
* +STR
* +DOC
* +MAP {}
* =VAL :foo
* =VAL :bar
* =VAL :foo2
* =VAL :bar2
* -MAP
* -DOC
* -STR
* ```
*
* For reference, the @ref ParseEngine object will produce this
* sequence of calls to its bound EventHandler:
* ```cpp
* handler.begin_stream();
* handler.begin_doc();
* handler.begin_map_val_flow();
* handler.set_key_scalar_plain("foo");
* handler.set_val_scalar_plain("bar");
* handler.add_sibling();
* handler.set_key_scalar_plain("foo2");
* handler.set_val_scalar_plain("bar2");
* handler.end_map();
* handler.end_doc();
* handler.end_stream();
* ```
*
* For many other examples of all areas of YAML and how ryml's parse
* model corresponds to the YAML standard model, refer to the [unit
* tests for the parse
* engine](https://github.com/biojppm/rapidyaml/tree/master/test/test_parse_engine.cpp).
*
*
* ### Special events
*
* Most of the parsing events adopted by rapidyaml in its event model
* are fairly obvious, but there are two less-obvious events requiring
* some explanation.
*
* These events exist to make it easier to parse some special YAML
* cases. They are called by the parser when a just-handled
* value/container is actually the first key of a new map:
*
* - `actually_val_is_first_key_of_new_map_flow()` (@ref EventHandlerTree::actually_val_is_first_key_of_new_map_flow() "see implementation in EventHandlerTree" / @ref EventHandlerYamlStd::actually_val_is_first_key_of_new_map_flow() "see implementation in EventHandlerYamlStd")
* - `actually_val_is_first_key_of_new_map_block()` (@ref EventHandlerTree::actually_val_is_first_key_of_new_map_block() "see implementation in EventHandlerTree" / @ref EventHandlerYamlStd::actually_val_is_first_key_of_new_map_block() "see implementation in EventHandlerYamlStd")
*
* For example, consider an implicit map inside a seq: `[a: b, c:
* d]` which is parsed as `[{a: b}, {c: d}]`. The standard event
* sequence for this YAML would be the following:
* ```cpp
* handler.begin_seq_val_flow();
* handler.begin_map_val_flow();
* handler.set_key_scalar_plain("a");
* handler.set_val_scalar_plain("b");
* handler.end_map();
* handler.add_sibling();
* handler.begin_map_val_flow();
* handler.set_key_scalar_plain("c");
* handler.set_val_scalar_plain("d");
* handler.end_map();
* handler.end_seq();
* ```
* The problem with this event sequence is that it forces the
* parser to delay setting the val scalar (in this case "a" and
* "c") until it knows whether the scalar is a key or a val. This
* would require the parser to store the scalar until this
* time. For instance, in the example above, the parser should
* delay setting "a" and "c", because they are in fact keys and
* not vals. Until then, the parser would have to store "a" and
* "c" in its internal state. The downside is that this complexity
* cost would apply even if there is no implicit map -- every val
* in a seq would have to be delayed until one of the
* disambiguating subsequent tokens `,-]:` is found.
* By calling this function, the parser can avoid this complexity,
* by preemptively setting the scalar as a val. Then a call to
* this function will create the map and rearrange the scalar as
* key. Now the cost applies only once: when a seqimap starts. So
* the following (easier and cheaper) event sequence below has the
* same effect as the event sequence above:
* ```cpp
* handler.begin_seq_val_flow();
* handler.set_val_scalar_plain("notmap");
* handler.set_val_scalar_plain("a"); // preemptively set "a" as val!
* handler.actually_as_new_map_key(); // create a map, move the "a" val as the key of the first child of the new map
* handler.set_val_scalar_plain("b"); // now "a" is a key and "b" the val
* handler.end_map();
* handler.set_val_scalar_plain("c"); // "c" also as val!
* handler.actually_as_block_flow(); // likewise
* handler.set_val_scalar_plain("d"); // now "c" is a key and "b" the val
* handler.end_map();
* handler.end_seq();
* ```
* This also applies to container keys (although ryml's tree
* cannot accomodate these): the parser can preemptively set a
* container as a val, and call this event to turn that container
* into a key. For example, consider this yaml:
* ```yaml
* [aa, bb]: [cc, dd]
* # ^ ^ ^
* # | | |
* # (2) (1) (3) <- event sequence
* ```
* The standard event sequence for this YAML would be the
* following:
* ```cpp
* handler.begin_map_val_block(); // (1)
* handler.begin_seq_key_flow(); // (2)
* handler.set_val_scalar_plain("aa");
* handler.add_sibling();
* handler.set_val_scalar_plain("bb");
* handler.end_seq();
* handler.begin_seq_val_flow(); // (3)
* handler.set_val_scalar_plain("cc");
* handler.add_sibling();
* handler.set_val_scalar_plain("dd");
* handler.end_seq();
* handler.end_map();
* ```
* The problem with the sequence above is that, reading from
* left-to-right, the parser can only detect the proper calls at
* (1) and (2) once it reaches (1) in the YAML source. So, the
* parser would have to buffer the entire event sequence starting
* from the beginning until it reaches (1). Using this function,
* the parser can do instead:
* ```cpp
* handler.begin_seq_val_flow(); // (2) -- preemptively as val!
* handler.set_val_scalar_plain("aa");
* handler.add_sibling();
* handler.set_val_scalar_plain("bb");
* handler.end_seq();
* handler.actually_as_new_map_key(); // (1) -- adjust when finding that the prev val was actually a key.
* handler.begin_seq_val_flow(); // (3) -- go on as before
* handler.set_val_scalar_plain("cc");
* handler.add_sibling();
* handler.set_val_scalar_plain("dd");
* handler.end_seq();
* handler.end_map();
* ```
*/
class Tree;
class NodeRef;
class ConstNodeRef;
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** Options to give to the parser to control its behavior. */
struct RYML_EXPORT ParserOptions
{
private:
typedef enum : uint32_t {
SCALAR_FILTERING = (1u << 0u),
LOCATIONS = (1u << 1u),
DEFAULTS = SCALAR_FILTERING,
} Flags_e;
uint32_t flags = DEFAULTS;
public:
ParserOptions() = default;
public:
/** @name source location tracking */
/** @{ */
/** enable/disable source location tracking */
ParserOptions& locations(bool enabled) noexcept
{
if(enabled)
flags |= LOCATIONS;
else
flags &= ~LOCATIONS;
return *this;
}
/** query source location tracking status */
C4_ALWAYS_INLINE bool locations() const noexcept { return (flags & LOCATIONS); }
/** @} */
public:
/** @name scalar filtering status (experimental; disable at your discretion) */
/** @{ */
/** enable/disable scalar filtering while parsing */
ParserOptions& scalar_filtering(bool enabled) noexcept
{
if(enabled)
flags |= SCALAR_FILTERING;
else
flags &= ~SCALAR_FILTERING;
return *this;
}
/** query scalar filtering status */
C4_ALWAYS_INLINE bool scalar_filtering() const noexcept { return (flags & SCALAR_FILTERING); }
/** @} */
};
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
/** This is the main driver of parsing logic: it scans the YAML or
* JSON source for tokens, and emits the appropriate sequence of
* parsing events to its event handler. The parse engine itself has no
* special limitations, and *can* accomodate containers as keys; it is the
* event handler may introduce additional constraints.
*
* There are two implemented handlers (see @ref doc_event_handlers,
* which has important notes about the event model):
*
* - @ref EventHandlerTree is the handler responsible for creating the
* ryml @ref Tree
*
* - @ref EventHandlerYamlStd is the handler responsible for emitting
* standardized [YAML test suite
* events](https://github.com/yaml/yaml-test-suite), used (only) in
* the CI of this project. This is not part of the library and is
* not installed.
*/
template<class EventHandler>
class ParseEngine
{
public:
using handler_type = EventHandler;
public:
/** @name construction and assignment */
/** @{ */
ParseEngine(EventHandler *evt_handler, ParserOptions opts={});
~ParseEngine();
ParseEngine(ParseEngine &&) noexcept;
ParseEngine(ParseEngine const&);
ParseEngine& operator=(ParseEngine &&) noexcept;
ParseEngine& operator=(ParseEngine const&);
/** @} */
public:
/** @name modifiers */
/** @{ */
/** Reserve a certain capacity for the parsing stack.
* This should be larger than the expected depth of the parsed
* YAML tree.
*
* The parsing stack is the only (potential) heap memory used
* directly by the parser.
*
* If the requested capacity is below the default
* stack size of 16, the memory is used directly in the parser
* object; otherwise it will be allocated from the heap.
*
* @note this reserves memory only for the parser itself; all the
* allocations for the parsed tree will go through the tree's
* allocator (when different).
*
* @note for maximum efficiency, the tree and the arena can (and
* should) also be reserved. */
void reserve_stack(id_type capacity)
{
m_evt_handler->m_stack.reserve(capacity);
}
/** Reserve a certain capacity for the array used to track node
* locations in the source buffer. */
void reserve_locations(size_t num_source_lines)
{
_resize_locations(num_source_lines);
}
RYML_DEPRECATED("filter arena no longer needed")
void reserve_filter_arena(size_t) {}
/** @} */
public:
/** @name getters */
/** @{ */
/** Get the options used to build this parser object. */
ParserOptions const& options() const { return m_options; }
/** Get the current callbacks in the parser. */
Callbacks const& callbacks() const { RYML_ASSERT(m_evt_handler); return m_evt_handler->m_stack.m_callbacks; }
/** Get the name of the latest file parsed by this object. */
csubstr filename() const { return m_file; }
/** Get the latest YAML buffer parsed by this object. */
csubstr source() const { return m_buf; }
/** Get the encoding of the latest YAML buffer parsed by this object.
* If no encoding was specified, UTF8 is assumed as per the YAML standard. */
Encoding_e encoding() const { return m_encoding != NOBOM ? m_encoding : UTF8; }
id_type stack_capacity() const { RYML_ASSERT(m_evt_handler); return m_evt_handler->m_stack.capacity(); }
size_t locations_capacity() const { return m_newline_offsets_capacity; }
RYML_DEPRECATED("filter arena no longer needed")
size_t filter_arena_capacity() const { return 0u; }
/** @} */
public:
/** @name parse methods */
/** @{ */
/** parse YAML in place, emitting events to the current handler */
void parse_in_place_ev(csubstr filename, substr src);
/** parse JSON in place, emitting events to the current handler */
void parse_json_in_place_ev(csubstr filename, substr src);
/** @} */
public:
/** @name deprecated parse methods
* @{ */
/** @cond dev */
template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the freestanding function in parse.hpp.") typename std::enable_if<U::is_wtree, void>::type parse_in_place(csubstr filename, substr yaml, Tree *t, size_t node_id);
template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the freestanding function in parse.hpp.") typename std::enable_if<U::is_wtree, void>::type parse_in_place( substr yaml, Tree *t, size_t node_id);
template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the freestanding function in parse.hpp.") typename std::enable_if<U::is_wtree, void>::type parse_in_place(csubstr filename, substr yaml, Tree *t );
template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the freestanding function in parse.hpp.") typename std::enable_if<U::is_wtree, void>::type parse_in_place( substr yaml, Tree *t );
template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the freestanding function in parse.hpp.") typename std::enable_if<U::is_wtree, void>::type parse_in_place(csubstr filename, substr yaml, NodeRef node );
template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the freestanding function in parse.hpp.") typename std::enable_if<U::is_wtree, void>::type parse_in_place( substr yaml, NodeRef node );
template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the freestanding function in parse.hpp.") typename std::enable_if<U::is_wtree, Tree>::type parse_in_place(csubstr filename, substr yaml );
template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the freestanding function in parse.hpp.") typename std::enable_if<U::is_wtree, Tree>::type parse_in_place( substr yaml );
template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the freestanding function in parse.hpp.") typename std::enable_if<U::is_wtree, void>::type parse_in_arena(csubstr filename, csubstr yaml, Tree *t, size_t node_id);
template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the freestanding function in parse.hpp.") typename std::enable_if<U::is_wtree, void>::type parse_in_arena( csubstr yaml, Tree *t, size_t node_id);
template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the freestanding function in parse.hpp.") typename std::enable_if<U::is_wtree, void>::type parse_in_arena(csubstr filename, csubstr yaml, Tree *t );
template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the freestanding function in parse.hpp.") typename std::enable_if<U::is_wtree, void>::type parse_in_arena( csubstr yaml, Tree *t );
template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the freestanding function in parse.hpp.") typename std::enable_if<U::is_wtree, void>::type parse_in_arena(csubstr filename, csubstr yaml, NodeRef node );
template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the freestanding function in parse.hpp.") typename std::enable_if<U::is_wtree, void>::type parse_in_arena( csubstr yaml, NodeRef node );
template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the freestanding function in parse.hpp.") typename std::enable_if<U::is_wtree, Tree>::type parse_in_arena(csubstr filename, csubstr yaml );
template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the freestanding function in parse.hpp.") typename std::enable_if<U::is_wtree, Tree>::type parse_in_arena( csubstr yaml );
template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the freestanding csubstr version in parse.hpp.") typename std::enable_if<U::is_wtree, void>::type parse_in_arena(csubstr filename, substr yaml, Tree *t, size_t node_id);
template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the freestanding csubstr version in parse.hpp.") typename std::enable_if<U::is_wtree, void>::type parse_in_arena( substr yaml, Tree *t, size_t node_id);
template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the freestanding csubstr version in parse.hpp.") typename std::enable_if<U::is_wtree, void>::type parse_in_arena(csubstr filename, substr yaml, Tree *t );
template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the freestanding csubstr version in parse.hpp.") typename std::enable_if<U::is_wtree, void>::type parse_in_arena( substr yaml, Tree *t );
template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the freestanding csubstr version in parse.hpp.") typename std::enable_if<U::is_wtree, void>::type parse_in_arena(csubstr filename, substr yaml, NodeRef node );
template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the freestanding csubstr version in parse.hpp.") typename std::enable_if<U::is_wtree, void>::type parse_in_arena( substr yaml, NodeRef node );
template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the freestanding csubstr version in parse.hpp.") typename std::enable_if<U::is_wtree, Tree>::type parse_in_arena(csubstr filename, substr yaml );
template<class U=EventHandler> RYML_DEPRECATED("removed, deliberately undefined. use the freestanding csubstr version in parse.hpp.") typename std::enable_if<U::is_wtree, Tree>::type parse_in_arena( substr yaml );
/** @endcond */
/** @} */
public:
/** @name locations */
/** @{ */
/** Get the location of a node of the last tree to be parsed by this parser. */
Location location(Tree const& tree, id_type node_id) const;
/** Get the location of a node of the last tree to be parsed by this parser. */
Location location(ConstNodeRef node) const;
/** Get the string starting at a particular location, to the end
* of the parsed source buffer. */
csubstr location_contents(Location const& loc) const;
/** Given a pointer to a buffer position, get the location.
* @param[in] val must be pointing to somewhere in the source
* buffer that was last parsed by this object. */
Location val_location(const char *val) const;
/** @} */
public:
/** @name scalar filtering */
/** @{*/
/** filter a plain scalar */
FilterResult filter_scalar_plain(csubstr scalar, substr dst, size_t indentation);
/** filter a plain scalar in place */
FilterResult filter_scalar_plain_in_place(substr scalar, size_t cap, size_t indentation);
/** filter a single-quoted scalar */
FilterResult filter_scalar_squoted(csubstr scalar, substr dst);
/** filter a single-quoted scalar in place */
FilterResult filter_scalar_squoted_in_place(substr scalar, size_t cap);
/** filter a double-quoted scalar */
FilterResult filter_scalar_dquoted(csubstr scalar, substr dst);
/** filter a double-quoted scalar in place */
FilterResultExtending filter_scalar_dquoted_in_place(substr scalar, size_t cap);
/** filter a block-literal scalar */
FilterResult filter_scalar_block_literal(csubstr scalar, substr dst, size_t indentation, BlockChomp_e chomp);
/** filter a block-literal scalar in place */
FilterResult filter_scalar_block_literal_in_place(substr scalar, size_t cap, size_t indentation, BlockChomp_e chomp);
/** filter a block-folded scalar */
FilterResult filter_scalar_block_folded(csubstr scalar, substr dst, size_t indentation, BlockChomp_e chomp);
/** filter a block-folded scalar in place */
FilterResult filter_scalar_block_folded_in_place(substr scalar, size_t cap, size_t indentation, BlockChomp_e chomp);
/** @} */
private:
struct ScannedScalar
{
substr scalar;
bool needs_filter;
};
struct ScannedBlock
{
substr scalar;
size_t indentation;
BlockChomp_e chomp;
};
bool _is_doc_begin(csubstr s);
bool _is_doc_end(csubstr s);
bool _scan_scalar_plain_blck(ScannedScalar *C4_RESTRICT sc, size_t indentation);
bool _scan_scalar_plain_seq_flow(ScannedScalar *C4_RESTRICT sc);
bool _scan_scalar_plain_seq_blck(ScannedScalar *C4_RESTRICT sc);
bool _scan_scalar_plain_map_flow(ScannedScalar *C4_RESTRICT sc);
bool _scan_scalar_plain_map_blck(ScannedScalar *C4_RESTRICT sc);
bool _scan_scalar_map_json(ScannedScalar *C4_RESTRICT sc);
bool _scan_scalar_seq_json(ScannedScalar *C4_RESTRICT sc);
bool _scan_scalar_plain_unk(ScannedScalar *C4_RESTRICT sc);
bool _is_valid_start_scalar_plain_flow(csubstr s);
ScannedScalar _scan_scalar_squot();
ScannedScalar _scan_scalar_dquot();
void _scan_block(ScannedBlock *C4_RESTRICT sb, size_t indref);
csubstr _scan_anchor();
csubstr _scan_ref_seq();
csubstr _scan_ref_map();
csubstr _scan_tag();
public: // exposed for testing
/** @cond dev */
csubstr _filter_scalar_plain(substr s, size_t indentation);
csubstr _filter_scalar_squot(substr s);
csubstr _filter_scalar_dquot(substr s);
csubstr _filter_scalar_literal(substr s, size_t indentation, BlockChomp_e chomp);
csubstr _filter_scalar_folded(substr s, size_t indentation, BlockChomp_e chomp);
csubstr _maybe_filter_key_scalar_plain(ScannedScalar const& sc, size_t indendation);
csubstr _maybe_filter_val_scalar_plain(ScannedScalar const& sc, size_t indendation);
csubstr _maybe_filter_key_scalar_squot(ScannedScalar const& sc);
csubstr _maybe_filter_val_scalar_squot(ScannedScalar const& sc);
csubstr _maybe_filter_key_scalar_dquot(ScannedScalar const& sc);
csubstr _maybe_filter_val_scalar_dquot(ScannedScalar const& sc);
csubstr _maybe_filter_key_scalar_literal(ScannedBlock const& sb);
csubstr _maybe_filter_val_scalar_literal(ScannedBlock const& sb);
csubstr _maybe_filter_key_scalar_folded(ScannedBlock const& sb);
csubstr _maybe_filter_val_scalar_folded(ScannedBlock const& sb);
/** @endcond */
private:
void _handle_map_block();
void _handle_seq_block();
void _handle_map_flow();
void _handle_seq_flow();
void _handle_seq_imap();
void _handle_map_json();
void _handle_seq_json();
void _handle_unk();
void _handle_unk_json();
void _handle_usty();
void _handle_flow_skip_whitespace();
void _end_map_blck();
void _end_seq_blck();
void _end2_map();
void _end2_seq();
void _begin2_doc();
void _begin2_doc_expl();
void _end2_doc();
void _end2_doc_expl();
void _maybe_begin_doc();
void _maybe_end_doc();
void _start_doc_suddenly();
void _end_doc_suddenly();
void _end_doc_suddenly__pop();
void _end_stream();
void _set_indentation(size_t indentation);
void _save_indentation();
void _handle_indentation_pop_from_block_seq();
void _handle_indentation_pop_from_block_map();
void _handle_indentation_pop(ParserState const* dst);
void _maybe_skip_comment();
void _skip_comment();
void _maybe_skip_whitespace_tokens();
void _maybe_skipchars(char c);
#ifdef RYML_NO_COVERAGE__TO_BE_DELETED
void _maybe_skipchars_up_to(char c, size_t max_to_skip);
#endif
template<size_t N>
void _skipchars(const char (&chars)[N]);
bool _maybe_scan_following_colon() noexcept;
bool _maybe_scan_following_comma() noexcept;
public:
/** @cond dev */
template<class FilterProcessor> auto _filter_plain(FilterProcessor &C4_RESTRICT proc, size_t indentation) -> decltype(proc.result());
template<class FilterProcessor> auto _filter_squoted(FilterProcessor &C4_RESTRICT proc) -> decltype(proc.result());
template<class FilterProcessor> auto _filter_dquoted(FilterProcessor &C4_RESTRICT proc) -> decltype(proc.result());
template<class FilterProcessor> auto _filter_block_literal(FilterProcessor &C4_RESTRICT proc, size_t indentation, BlockChomp_e chomp) -> decltype(proc.result());
template<class FilterProcessor> auto _filter_block_folded(FilterProcessor &C4_RESTRICT proc, size_t indentation, BlockChomp_e chomp) -> decltype(proc.result());
/** @endcond */
public:
/** @cond dev */
template<class FilterProcessor> void _filter_nl_plain(FilterProcessor &C4_RESTRICT proc, size_t indentation);
template<class FilterProcessor> void _filter_nl_squoted(FilterProcessor &C4_RESTRICT proc);
template<class FilterProcessor> void _filter_nl_dquoted(FilterProcessor &C4_RESTRICT proc);
template<class FilterProcessor> bool _filter_ws_handle_to_first_non_space(FilterProcessor &C4_RESTRICT proc);
template<class FilterProcessor> void _filter_ws_copy_trailing(FilterProcessor &C4_RESTRICT proc);
template<class FilterProcessor> void _filter_ws_skip_trailing(FilterProcessor &C4_RESTRICT proc);
template<class FilterProcessor> void _filter_dquoted_backslash(FilterProcessor &C4_RESTRICT proc);
template<class FilterProcessor> void _filter_chomp(FilterProcessor &C4_RESTRICT proc, BlockChomp_e chomp, size_t indentation);
template<class FilterProcessor> size_t _handle_all_whitespace(FilterProcessor &C4_RESTRICT proc, BlockChomp_e chomp);
template<class FilterProcessor> size_t _extend_to_chomp(FilterProcessor &C4_RESTRICT proc, size_t contents_len);
template<class FilterProcessor> void _filter_block_indentation(FilterProcessor &C4_RESTRICT proc, size_t indentation);
template<class FilterProcessor> void _filter_block_folded_newlines(FilterProcessor &C4_RESTRICT proc, size_t indentation, size_t len);
template<class FilterProcessor> size_t _filter_block_folded_newlines_compress(FilterProcessor &C4_RESTRICT proc, size_t num_newl, size_t wpos_at_first_newl);
template<class FilterProcessor> void _filter_block_folded_newlines_leading(FilterProcessor &C4_RESTRICT proc, size_t indentation, size_t len);
template<class FilterProcessor> void _filter_block_folded_indented_block(FilterProcessor &C4_RESTRICT proc, size_t indentation, size_t len, size_t curr_indentation) noexcept;
/** @endcond */
private:
void _line_progressed(size_t ahead);
void _line_ended();
void _line_ended_undo();
bool _finished_file() const;
bool _finished_line() const;
void _scan_line();
substr _peek_next_line(size_t pos=npos) const;
bool _at_line_begin() const
{
return m_evt_handler->m_curr->line_contents.rem.begin() == m_evt_handler->m_curr->line_contents.full.begin();
}
void _relocate_arena(csubstr prev_arena, substr next_arena);
static void _s_relocate_arena(void*, csubstr prev_arena, substr next_arena);
private:
C4_ALWAYS_INLINE bool has_all(ParserFlag_t f) const noexcept { return (m_evt_handler->m_curr->flags & f) == f; }
C4_ALWAYS_INLINE bool has_any(ParserFlag_t f) const noexcept { return (m_evt_handler->m_curr->flags & f) != 0; }
C4_ALWAYS_INLINE bool has_none(ParserFlag_t f) const noexcept { return (m_evt_handler->m_curr->flags & f) == 0; }
static C4_ALWAYS_INLINE bool has_all(ParserFlag_t f, ParserState const* C4_RESTRICT s) noexcept { return (s->flags & f) == f; }
static C4_ALWAYS_INLINE bool has_any(ParserFlag_t f, ParserState const* C4_RESTRICT s) noexcept { return (s->flags & f) != 0; }
static C4_ALWAYS_INLINE bool has_none(ParserFlag_t f, ParserState const* C4_RESTRICT s) noexcept { return (s->flags & f) == 0; }
#ifndef RYML_DBG
C4_ALWAYS_INLINE static void add_flags(ParserFlag_t on, ParserState *C4_RESTRICT s) noexcept { s->flags |= on; }
C4_ALWAYS_INLINE static void addrem_flags(ParserFlag_t on, ParserFlag_t off, ParserState *C4_RESTRICT s) noexcept { s->flags &= ~off; s->flags |= on; }
C4_ALWAYS_INLINE static void rem_flags(ParserFlag_t off, ParserState *C4_RESTRICT s) noexcept { s->flags &= ~off; }
C4_ALWAYS_INLINE void add_flags(ParserFlag_t on) noexcept { m_evt_handler->m_curr->flags |= on; }
C4_ALWAYS_INLINE void addrem_flags(ParserFlag_t on, ParserFlag_t off) noexcept { m_evt_handler->m_curr->flags &= ~off; m_evt_handler->m_curr->flags |= on; }
C4_ALWAYS_INLINE void rem_flags(ParserFlag_t off) noexcept { m_evt_handler->m_curr->flags &= ~off; }
#else
static void add_flags(ParserFlag_t on, ParserState *C4_RESTRICT s);
static void addrem_flags(ParserFlag_t on, ParserFlag_t off, ParserState *C4_RESTRICT s);
static void rem_flags(ParserFlag_t off, ParserState *C4_RESTRICT s);
C4_ALWAYS_INLINE void add_flags(ParserFlag_t on) noexcept { add_flags(on, m_evt_handler->m_curr); }
C4_ALWAYS_INLINE void addrem_flags(ParserFlag_t on, ParserFlag_t off) noexcept { addrem_flags(on, off, m_evt_handler->m_curr); }
C4_ALWAYS_INLINE void rem_flags(ParserFlag_t off) noexcept { rem_flags(off, m_evt_handler->m_curr); }
#endif
private:
void _prepare_locations();
void _resize_locations(size_t sz);
bool _locations_dirty() const;
bool _location_from_cont(Tree const& tree, id_type node, Location *C4_RESTRICT loc) const;
bool _location_from_node(Tree const& tree, id_type node, Location *C4_RESTRICT loc, id_type level) const;
private:
void _reset();
void _free();
void _clr();
#ifdef RYML_DBG
template<class ...Args> void _dbg(csubstr fmt, Args const& C4_RESTRICT ...args) const;
#endif
template<class ...Args> void _err(csubstr fmt, Args const& C4_RESTRICT ...args) const;
template<class ...Args> void _errloc(csubstr fmt, Location const& loc, Args const& C4_RESTRICT ...args) const;
template<class DumpFn> void _fmt_msg(DumpFn &&dumpfn) const;
private:
/** store pending tag or anchor/ref annotations */
struct Annotation
{
struct Entry
{
csubstr str;
size_t indentation;
size_t line;
};
Entry annotations[2];
size_t num_entries;
};
void _handle_colon();
void _add_annotation(Annotation *C4_RESTRICT dst, csubstr str, size_t indentation, size_t line);
void _clear_annotations(Annotation *C4_RESTRICT dst);
bool _has_pending_annotations() const { return m_pending_tags.num_entries || m_pending_anchors.num_entries; }
#ifdef RYML_NO_COVERAGE__TO_BE_DELETED
bool _handle_indentation_from_annotations();
#endif
bool _annotations_require_key_container() const;
void _handle_annotations_before_blck_key_scalar();
void _handle_annotations_before_blck_val_scalar();
void _handle_annotations_before_start_mapblck(size_t current_line);
void _handle_annotations_before_start_mapblck_as_key();
void _handle_annotations_and_indentation_after_start_mapblck(size_t key_indentation, size_t key_line);
size_t _select_indentation_from_annotations(size_t val_indentation, size_t val_line);
void _handle_directive(csubstr rem);
bool _handle_bom();
void _handle_bom(Encoding_e enc);
void _check_tag(csubstr tag);
private:
ParserOptions m_options;
csubstr m_file;
substr m_buf;
public:
/** @cond dev */
EventHandler *C4_RESTRICT m_evt_handler; // NOLINT
/** @endcond */
private:
Annotation m_pending_anchors;
Annotation m_pending_tags;
bool m_was_inside_qmrk;
bool m_doc_empty = true;
size_t m_prev_colon = npos;
Encoding_e m_encoding = UTF8;
private:
size_t *m_newline_offsets;
size_t m_newline_offsets_size;
size_t m_newline_offsets_capacity;
csubstr m_newline_offsets_buf;
};
/** @cond dev */
RYML_EXPORT C4_NO_INLINE size_t _find_last_newline_and_larger_indentation(csubstr s, size_t indentation) noexcept;
/** @endcond */
/** Quickly inspect the source to estimate the number of nodes the
* resulting tree is likely have. If a tree is empty before
* parsing, considerable time will be spent growing it, so calling
* this to reserve the tree size prior to parsing is likely to
* result in a time gain. We encourage using this method before
* parsing, but as always measure its impact in performance to
* obtain a good trade-off.
*
* @note since this method is meant for optimizing performance, it
* is approximate. The result may be actually smaller than the
* resulting number of nodes, notably if the YAML uses implicit
* maps as flow seq members as in `[these: are, individual:
* maps]`. */
RYML_EXPORT id_type estimate_tree_capacity(csubstr src); // NOLINT(readability-redundant-declaration)
/** @} */
} // namespace yml
} // namespace c4
// NOLINTEND(hicpp-signed-bitwise)
#if defined(_MSC_VER)
# pragma warning(pop)
#endif
#endif /* _C4_YML_PARSE_ENGINE_HPP_ */

View File

@@ -0,0 +1,212 @@
#ifndef _C4_YML_PARSER_STATE_HPP_
#define _C4_YML_PARSER_STATE_HPP_
#ifndef _C4_YML_COMMON_HPP_
#include "c4/yml/common.hpp"
#endif
// NOLINTBEGIN(hicpp-signed-bitwise)
namespace c4 {
namespace yml {
/** data type for @ref ParserState_e */
using ParserFlag_t = int;
/** Enumeration of the state flags for the parser */
typedef enum : ParserFlag_t {
RTOP = 0x01 << 0, ///< reading at top level
RUNK = 0x01 << 1, ///< reading unknown state (when starting): must determine whether scalar, map or seq
RMAP = 0x01 << 2, ///< reading a map
RSEQ = 0x01 << 3, ///< reading a seq
FLOW = 0x01 << 4, ///< reading is inside explicit flow chars: [] or {}
BLCK = 0x01 << 5, ///< reading in block mode
QMRK = 0x01 << 6, ///< reading an explicit key (`? key`)
RKEY = 0x01 << 7, ///< reading a scalar as key
RVAL = 0x01 << 9, ///< reading a scalar as val
RKCL = 0x01 << 8, ///< reading the key colon (ie the : after the key in the map)
RNXT = 0x01 << 10, ///< read next val or keyval
SSCL = 0x01 << 11, ///< there's a stored scalar
QSCL = 0x01 << 12, ///< stored scalar was quoted
RSET = 0x01 << 13, ///< the (implicit) map being read is a !!set. @see https://yaml.org/type/set.html
RDOC = 0x01 << 14, ///< reading a document
NDOC = 0x01 << 15, ///< no document mode. a document has ended and another has not started yet.
USTY = 0x01 << 16, ///< reading in unknown style mode - must determine FLOW or BLCK
//! reading an implicit map nested in an explicit seq.
//! eg, {key: [key2: value2, key3: value3]}
//! is parsed as {key: [{key2: value2}, {key3: value3}]}
RSEQIMAP = 0x01 << 17,
} ParserState_e;
#ifdef RYML_DBG
/** @cond dev */
namespace detail {
csubstr _parser_flags_to_str(substr buf, ParserFlag_t flags);
} // namespace
/** @endcond */
#endif
/** Helper to control the line contents while parsing a buffer */
struct LineContents
{
substr rem; ///< the stripped line remainder; initially starts at the first non-space character
size_t indentation; ///< the number of spaces on the beginning of the line
substr full; ///< the full line, including newlines on the right
substr stripped; ///< the stripped line, excluding newlines on the right
LineContents() = default;
void reset_with_next_line(substr buf, size_t offset)
{
RYML_ASSERT(offset <= buf.len);
size_t e = offset;
// get the current line stripped of newline chars
while(e < buf.len && (buf.str[e] != '\n' && buf.str[e] != '\r'))
++e;
RYML_ASSERT(e >= offset);
const substr stripped_ = buf.range(offset, e);
#if defined(__GNUC__) && __GNUC__ == 11
C4_DONT_OPTIMIZE(stripped_);
#endif
// advance pos to include the first line ending
if(e < buf.len && buf.str[e] == '\r')
++e;
if(e < buf.len && buf.str[e] == '\n')
++e;
const substr full_ = buf.range(offset, e);
reset(full_, stripped_);
}
void reset(substr full_, substr stripped_)
{
rem = stripped_;
indentation = stripped_.first_not_of(' '); // find the first column where the character is not a space
full = full_;
stripped = stripped_;
}
C4_ALWAYS_INLINE size_t current_col() const RYML_NOEXCEPT
{
// WARNING: gcc x86 release builds were wrong (eg returning 0
// when the result should be 4 ) when this function was like
// this:
//
//return current_col(rem);
//
// (see below for the full definition of the called overload
// of current_col())
//
// ... so we explicitly inline the code in here:
RYML_ASSERT(rem.str >= full.str);
size_t col = static_cast<size_t>(rem.str - full.str);
return col;
//
// this was happening only on builds specifically with (gcc
// AND x86 AND release); no other builds were having the
// problem: not in debug, not in x64, not in other
// architectures, not in clang, not in visual studio. WTF!?
//
// Enabling debug prints with RYML_DBG made the problem go
// away, so these could not be used to debug the
// problem. Adding prints inside the called current_col() also
// made the problem go away! WTF!???
//
// a prize will be offered to anybody able to explain why this
// was happening.
}
C4_ALWAYS_INLINE size_t current_col(csubstr s) const RYML_NOEXCEPT
{
RYML_ASSERT(s.str >= full.str);
RYML_ASSERT(full.is_super(s));
size_t col = static_cast<size_t>(s.str - full.str);
return col;
}
};
static_assert(std::is_standard_layout<LineContents>::value, "LineContents not standard");
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
struct ParserState
{
LineContents line_contents;
Location pos;
ParserFlag_t flags;
size_t indref; ///< the reference indentation in the current block scope
id_type level;
id_type node_id; ///< don't hold a pointer to the node as it will be relocated during tree resizes
size_t scalar_col; // the column where the scalar (or its quotes) begin
bool more_indented;
bool has_children;
ParserState() = default;
void start_parse(const char *file, id_type node_id_)
{
level = 0;
pos.name = to_csubstr(file);
pos.offset = 0;
pos.line = 1;
pos.col = 1;
node_id = node_id_;
more_indented = false;
scalar_col = 0;
indref = 0;
has_children = false;
}
void reset_after_push()
{
node_id = NONE;
indref = npos;
more_indented = false;
++level;
has_children = false;
}
C4_ALWAYS_INLINE void reset_before_pop(ParserState const& to_pop)
{
pos = to_pop.pos;
line_contents = to_pop.line_contents;
}
public:
C4_ALWAYS_INLINE bool at_line_beginning() const noexcept
{
return line_contents.rem.str == line_contents.full.str;
}
C4_ALWAYS_INLINE bool indentation_eq() const noexcept
{
RYML_ASSERT(indref != npos);
return line_contents.indentation != npos && line_contents.indentation == indref;
}
C4_ALWAYS_INLINE bool indentation_ge() const noexcept
{
RYML_ASSERT(indref != npos);
return line_contents.indentation != npos && line_contents.indentation >= indref;
}
C4_ALWAYS_INLINE bool indentation_gt() const noexcept
{
RYML_ASSERT(indref != npos);
return line_contents.indentation != npos && line_contents.indentation > indref;
}
C4_ALWAYS_INLINE bool indentation_lt() const noexcept
{
RYML_ASSERT(indref != npos);
return line_contents.indentation != npos && line_contents.indentation < indref;
}
};
static_assert(std::is_standard_layout<ParserState>::value, "ParserState not standard");
} // namespace yml
} // namespace c4
// NOLINTEND(hicpp-signed-bitwise)
#endif /* _C4_YML_PARSER_STATE_HPP_ */

View File

@@ -0,0 +1,88 @@
#ifndef _C4_YML_REFERENCE_RESOLVER_HPP_
#define _C4_YML_REFERENCE_RESOLVER_HPP_
#include "c4/yml/tree.hpp"
#include "c4/yml/detail/stack.hpp"
namespace c4 {
namespace yml {
/** @addtogroup doc_ref_utils
* @{
*/
/** Reusable object to resolve references/aliases in a @ref Tree. */
struct RYML_EXPORT ReferenceResolver
{
ReferenceResolver() = default;
/** Resolve references: for each reference, look for a matching
* anchor, and copy its contents to the ref node.
*
* @p tree the subject tree
*
* @p clear_anchors whether to clear existing anchors after
* resolving
*
* This method first does a full traversal of the tree to gather
* all anchors and references in a separate collection, then it
* goes through that collection to locate the names, which it does
* by obeying the YAML standard diktat that "an alias node refers
* to the most recent node in the serialization having the
* specified anchor"
*
* So, depending on the number of anchor/alias nodes, this is a
* potentially expensive operation, with a best-case linear
* complexity (from the initial traversal). This potential cost is
* one of the reasons for requiring an explicit call.
*
* The @ref Tree has an `Tree::resolve()` overload set forwarding
* here. Previously this operation was done there, using a
* discarded object; using this separate class offers opportunity
* for reuse of the object.
*
* @warning resolving references opens an attack vector when the
* data is malicious or severely malformed, as the tree can expand
* exponentially. See for example the [Billion Laughs
* Attack](https://en.wikipedia.org/wiki/Billion_laughs_attack).
*
*/
void resolve(Tree *tree, bool clear_anchors=true);
public:
/** @cond dev */
struct RefData
{
NodeType type;
id_type node;
id_type prev_anchor;
id_type target;
id_type parent_ref;
id_type parent_ref_sibling;
};
void reset_(Tree *t_);
void resolve_();
void gather_anchors_and_refs_();
void gather_anchors_and_refs__(id_type n);
id_type count_anchors_and_refs_(id_type n);
id_type lookup_(RefData const* C4_RESTRICT ra);
Tree *C4_RESTRICT m_tree;
/** We're using this stack purely as an array. */
detail::stack<RefData> m_refs;
/** @endcond */
};
/** @} */
} // namespace ryml
} // namespace c4
#endif // _C4_YML_REFERENCE_RESOLVER_HPP_

View File

@@ -25,16 +25,17 @@ void write(c4::yml::NodeRef *n, std::map<K, V, Less, Alloc> const& m)
}
}
/** read the node members, assigning into the existing map. If a key
* is already present in the map, then its value will be
* move-assigned. */
template<class K, class V, class Less, class Alloc>
bool read(c4::yml::ConstNodeRef const& n, std::map<K, V, Less, Alloc> * m)
{
K k{};
V v{};
for(auto const& C4_RESTRICT ch : n)
{
K k{};
ch >> c4::yml::key(k);
ch >> v;
m->emplace(std::make_pair(std::move(k), std::move(v)));
ch >> (*m)[k];
}
return true;
}

View File

@@ -17,31 +17,37 @@ template<class V, class Alloc>
void write(c4::yml::NodeRef *n, std::vector<V, Alloc> const& vec)
{
*n |= c4::yml::SEQ;
for(auto const& v : vec)
for(V const& v : vec)
n->append_child() << v;
}
/** read the node members, overwriting existing vector entries. */
template<class V, class Alloc>
bool read(c4::yml::ConstNodeRef const& n, std::vector<V, Alloc> *vec)
{
vec->resize(n.num_children());
C4_SUPPRESS_WARNING_GCC_WITH_PUSH("-Wuseless-cast")
vec->resize(static_cast<size_t>(n.num_children()));
C4_SUPPRESS_WARNING_GCC_POP
size_t pos = 0;
for(auto const ch : n)
ch >> (*vec)[pos++];
for(ConstNodeRef const child : n)
child >> (*vec)[pos++];
return true;
}
/** specialization: std::vector<bool> uses std::vector<bool>::reference as
/** read the node members, overwriting existing vector entries.
* specialization: std::vector<bool> uses std::vector<bool>::reference as
* the return value of its operator[]. */
template<class Alloc>
bool read(c4::yml::ConstNodeRef const& n, std::vector<bool, Alloc> *vec)
{
vec->resize(n.num_children());
C4_SUPPRESS_WARNING_GCC_WITH_PUSH("-Wuseless-cast")
vec->resize(static_cast<size_t>(n.num_children()));
C4_SUPPRESS_WARNING_GCC_POP
size_t pos = 0;
bool tmp = false;
for(auto const ch : n)
bool tmp = {};
for(ConstNodeRef const child : n)
{
ch >> tmp;
child >> tmp;
(*vec)[pos++] = tmp;
}
return true;

View File

@@ -0,0 +1,84 @@
#ifndef _C4_YML_TAG_HPP_
#define _C4_YML_TAG_HPP_
#include <c4/yml/common.hpp>
namespace c4 {
namespace yml {
class Tree;
/** @addtogroup doc_tag_utils
*
* @{
*/
#ifndef RYML_MAX_TAG_DIRECTIVES
/** the maximum number of tag directives in a Tree */
#define RYML_MAX_TAG_DIRECTIVES 4
#endif
/** the integral type necessary to cover all the bits marking node tags */
using tag_bits = uint16_t;
/** a bit mask for marking tags for types */
typedef enum : tag_bits {
TAG_NONE = 0,
// container types
TAG_MAP = 1, /**< !!map Unordered set of key: value pairs without duplicates. @see https://yaml.org/type/map.html */
TAG_OMAP = 2, /**< !!omap Ordered sequence of key: value pairs without duplicates. @see https://yaml.org/type/omap.html */
TAG_PAIRS = 3, /**< !!pairs Ordered sequence of key: value pairs allowing duplicates. @see https://yaml.org/type/pairs.html */
TAG_SET = 4, /**< !!set Unordered set of non-equal values. @see https://yaml.org/type/set.html */
TAG_SEQ = 5, /**< !!seq Sequence of arbitrary values. @see https://yaml.org/type/seq.html */
// scalar types
TAG_BINARY = 6, /**< !!binary A sequence of zero or more octets (8 bit values). @see https://yaml.org/type/binary.html */
TAG_BOOL = 7, /**< !!bool Mathematical Booleans. @see https://yaml.org/type/bool.html */
TAG_FLOAT = 8, /**< !!float Floating-point approximation to real numbers. https://yaml.org/type/float.html */
TAG_INT = 9, /**< !!float Mathematical integers. https://yaml.org/type/int.html */
TAG_MERGE = 10, /**< !!merge Specify one or more mapping to be merged with the current one. https://yaml.org/type/merge.html */
TAG_NULL = 11, /**< !!null Devoid of value. https://yaml.org/type/null.html */
TAG_STR = 12, /**< !!str A sequence of zero or more Unicode characters. https://yaml.org/type/str.html */
TAG_TIMESTAMP = 13, /**< !!timestamp A point in time https://yaml.org/type/timestamp.html */
TAG_VALUE = 14, /**< !!value Specify the default value of a mapping https://yaml.org/type/value.html */
TAG_YAML = 15, /**< !!yaml Specify the default value of a mapping https://yaml.org/type/yaml.html */
} YamlTag_e;
RYML_EXPORT YamlTag_e to_tag(csubstr tag);
RYML_EXPORT csubstr from_tag(YamlTag_e tag);
RYML_EXPORT csubstr from_tag_long(YamlTag_e tag);
RYML_EXPORT csubstr normalize_tag(csubstr tag);
RYML_EXPORT csubstr normalize_tag_long(csubstr tag);
RYML_EXPORT csubstr normalize_tag_long(csubstr tag, substr output);
RYML_EXPORT bool is_custom_tag(csubstr tag);
struct RYML_EXPORT TagDirective
{
/** Eg <pre>!e!</pre> in <pre>%TAG !e! tag:example.com,2000:app/</pre> */
csubstr handle;
/** Eg <pre>tag:example.com,2000:app/</pre> in <pre>%TAG !e! tag:example.com,2000:app/</pre> */
csubstr prefix;
/** The next node to which this tag directive applies */
id_type next_node_id;
bool create_from_str(csubstr directive_); ///< leaves next_node_id unfilled
bool create_from_str(csubstr directive_, Tree *tree);
size_t transform(csubstr tag, substr output, Callbacks const& callbacks) const;
};
struct RYML_EXPORT TagDirectiveRange
{
TagDirective const* C4_RESTRICT b;
TagDirective const* C4_RESTRICT e;
C4_ALWAYS_INLINE TagDirective const* begin() const noexcept { return b; }
C4_ALWAYS_INLINE TagDirective const* end() const noexcept { return e; }
};
/** @} */
} // namespace yml
} // namespace c4
#endif /* _C4_YML_TAG_HPP_ */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,25 @@
#ifndef _C4_YML_VERSION_HPP_
#define _C4_YML_VERSION_HPP_
/** @file version.hpp */
#define RYML_VERSION "0.9.0"
#define RYML_VERSION_MAJOR 0
#define RYML_VERSION_MINOR 9
#define RYML_VERSION_PATCH 0
#include <c4/substr.hpp>
#include <c4/yml/export.hpp>
namespace c4 {
namespace yml {
RYML_EXPORT csubstr version();
RYML_EXPORT int version_major();
RYML_EXPORT int version_minor();
RYML_EXPORT int version_patch();
} // namespace yml
} // namespace c4
#endif /* _C4_YML_VERSION_HPP_ */

View File

@@ -23,18 +23,6 @@ namespace yml {
*/
/** Repeat-Character: a character to be written a number of times. */
struct RepC
{
char c;
size_t num_times;
};
inline RepC indent_to(size_t num_levels)
{
return {' ', size_t(2) * num_levels};
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
@@ -46,7 +34,7 @@ struct WriterFile
WriterFile(FILE *f = nullptr) : m_file(f ? f : stdout), m_pos(0) {}
inline substr _get(bool /*error_on_excess*/)
substr _get(bool /*error_on_excess*/) const
{
substr sp;
sp.str = nullptr;
@@ -55,44 +43,33 @@ struct WriterFile
}
template<size_t N>
inline void _do_write(const char (&a)[N])
void _do_write(const char (&a)[N])
{
fwrite(a, sizeof(char), N - 1, m_file);
(void)fwrite(a, sizeof(char), N - 1, m_file);
m_pos += N - 1;
}
inline void _do_write(csubstr sp)
void _do_write(csubstr sp)
{
#if defined(__clang__)
# pragma clang diagnostic push
# pragma GCC diagnostic ignored "-Wsign-conversion"
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wsign-conversion"
#endif
if(sp.empty()) return;
fwrite(sp.str, sizeof(csubstr::char_type), sp.len, m_file);
C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wsign-conversion")
if(sp.empty())
return;
(void)fwrite(sp.str, sizeof(csubstr::char_type), sp.len, m_file);
m_pos += sp.len;
#if defined(__clang__)
# pragma clang diagnostic pop
#elif defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
C4_SUPPRESS_WARNING_GCC_CLANG_POP
}
inline void _do_write(const char c)
void _do_write(const char c)
{
fputc(c, m_file);
(void)fputc(c, m_file);
++m_pos;
}
inline void _do_write(RepC const rc)
void _do_write(const char c, size_t num_times)
{
for(size_t i = 0; i < rc.num_times; ++i)
{
fputc(rc.c, m_file);
}
m_pos += rc.num_times;
for(size_t i = 0; i < num_times; ++i)
(void)fputc(c, m_file);
m_pos += num_times;
}
};
@@ -104,12 +81,12 @@ struct WriterFile
template<class OStream>
struct WriterOStream
{
OStream& m_stream;
OStream* m_stream;
size_t m_pos;
WriterOStream(OStream &s) : m_stream(s), m_pos(0) {}
WriterOStream(OStream &s) : m_stream(&s), m_pos(0) {}
inline substr _get(bool /*error_on_excess*/)
substr _get(bool /*error_on_excess*/) const
{
substr sp;
sp.str = nullptr;
@@ -118,44 +95,33 @@ struct WriterOStream
}
template<size_t N>
inline void _do_write(const char (&a)[N])
void _do_write(const char (&a)[N])
{
m_stream.write(a, N - 1);
m_stream->write(a, N - 1);
m_pos += N - 1;
}
inline void _do_write(csubstr sp)
void _do_write(csubstr sp)
{
#if defined(__clang__)
# pragma clang diagnostic push
# pragma GCC diagnostic ignored "-Wsign-conversion"
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wsign-conversion"
#endif
if(sp.empty()) return;
m_stream.write(sp.str, sp.len);
C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wsign-conversion")
if(sp.empty())
return;
m_stream->write(sp.str, sp.len);
m_pos += sp.len;
#if defined(__clang__)
# pragma clang diagnostic pop
#elif defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
C4_SUPPRESS_WARNING_GCC_CLANG_POP
}
inline void _do_write(const char c)
void _do_write(const char c)
{
m_stream.put(c);
m_stream->put(c);
++m_pos;
}
inline void _do_write(RepC const rc)
void _do_write(const char c, size_t num_times)
{
for(size_t i = 0; i < rc.num_times; ++i)
{
m_stream.put(rc.c);
}
m_pos += rc.num_times;
for(size_t i = 0; i < num_times; ++i)
m_stream->put(c);
m_pos += num_times;
}
};
@@ -171,16 +137,12 @@ struct WriterBuf
WriterBuf(substr sp) : m_buf(sp), m_pos(0) {}
inline substr _get(bool error_on_excess)
substr _get(bool error_on_excess) const
{
if(m_pos <= m_buf.len)
{
return m_buf.first(m_pos);
}
if(error_on_excess)
{
else if(error_on_excess)
c4::yml::error("not enough space in the given buffer");
}
substr sp;
sp.str = nullptr;
sp.len = m_pos;
@@ -188,46 +150,37 @@ struct WriterBuf
}
template<size_t N>
inline void _do_write(const char (&a)[N])
void _do_write(const char (&a)[N])
{
RYML_ASSERT( ! m_buf.overlaps(a));
if(m_pos + N-1 <= m_buf.len)
{
memcpy(&(m_buf[m_pos]), a, N-1);
}
m_pos += N-1;
}
inline void _do_write(csubstr sp)
void _do_write(csubstr sp)
{
if(sp.empty()) return;
if(sp.empty())
return;
RYML_ASSERT( ! sp.overlaps(m_buf));
if(m_pos + sp.len <= m_buf.len)
{
memcpy(&(m_buf[m_pos]), sp.str, sp.len);
}
m_pos += sp.len;
}
inline void _do_write(const char c)
void _do_write(const char c)
{
if(m_pos + 1 <= m_buf.len)
{
m_buf[m_pos] = c;
}
++m_pos;
}
inline void _do_write(RepC const rc)
void _do_write(const char c, size_t num_times)
{
if(m_pos + rc.num_times <= m_buf.len)
{
for(size_t i = 0; i < rc.num_times; ++i)
{
m_buf[m_pos + i] = rc.c;
}
}
m_pos += rc.num_times;
if(m_pos + num_times <= m_buf.len)
for(size_t i = 0; i < num_times; ++i)
m_buf[m_pos + i] = c;
m_pos += num_times;
}
};

View File

@@ -1,10 +1,16 @@
#ifndef _C4_YML_YML_HPP_
#define _C4_YML_YML_HPP_
#include "c4/yml/version.hpp"
#include "c4/yml/tree.hpp"
#include "c4/yml/node.hpp"
#include "c4/yml/emit.hpp"
#include "c4/yml/event_handler_tree.hpp"
#include "c4/yml/parse_engine.hpp"
#include "c4/yml/filter_processor.hpp"
#include "c4/yml/parse.hpp"
#include "c4/yml/preprocess.hpp"
#include "c4/yml/reference_resolver.hpp"
#include "c4/yml/tag.hpp"
#endif // _C4_YML_YML_HPP_

View File

@@ -25,12 +25,24 @@ See also:
</Type>
<Type Name="c4::yml::NodeType">
<DisplayString>{type}</DisplayString>
<DisplayString Condition="((type &amp; c4::yml::KEY ) == c4::yml::KEY) &amp;&amp; ((type &amp; c4::yml::VAL) == c4::yml::VAL)">[KEYVAL]</DisplayString>
<DisplayString Condition="((type &amp; c4::yml::KEY ) == c4::yml::KEY) &amp;&amp; ((type &amp; c4::yml::SEQ) == c4::yml::SEQ)">[KEYSEQ]</DisplayString>
<DisplayString Condition="((type &amp; c4::yml::KEY ) == c4::yml::KEY) &amp;&amp; ((type &amp; c4::yml::MAP) == c4::yml::MAP)">[KEYMAP]</DisplayString>
<DisplayString Condition="((type &amp; c4::yml::DOC ) == c4::yml::DOC) &amp;&amp; ((type &amp; c4::yml::VAL) == c4::yml::VAL)">[DOCVAL]</DisplayString>
<DisplayString Condition="((type &amp; c4::yml::DOC ) == c4::yml::DOC) &amp;&amp; ((type &amp; c4::yml::SEQ) == c4::yml::SEQ)">[DOCSEQ]</DisplayString>
<DisplayString Condition="((type &amp; c4::yml::DOC ) == c4::yml::DOC) &amp;&amp; ((type &amp; c4::yml::MAP) == c4::yml::MAP)">[DOCMAP]</DisplayString>
<DisplayString Condition="(type &amp; c4::yml::VAL ) == c4::yml::VAL" >[VAL]</DisplayString>
<DisplayString Condition="(type &amp; c4::yml::KEY ) == c4::yml::KEY" >[KEY]</DisplayString>
<DisplayString Condition="(type &amp; c4::yml::SEQ ) == c4::yml::SEQ" >[SEQ]</DisplayString>
<DisplayString Condition="(type &amp; c4::yml::MAP ) == c4::yml::MAP" >[MAP]</DisplayString>
<DisplayString Condition="(type &amp; c4::yml::DOC ) == c4::yml::DOC" >[DOC]</DisplayString>
<DisplayString Condition="(type &amp; c4::yml::STREAM) == c4::yml::STREAM">[STREAM]</DisplayString>
<DisplayString Condition="(type &amp; c4::yml::NOTYPE) == c4::yml::NOTYPE">[NOTYPE]</DisplayString>
<Expand>
<Synthetic Name="[enabled bits]">
<Synthetic Name="[type bits]">
<Expand>
<Item Name="[0]" Condition="(type &amp; c4::yml::VAL) != 0">c4::yml::VAL</Item>
<Item Name="[1]" Condition="(type &amp; c4::yml::KEY) != 0">c4::yml::KEY</Item>
<Item Name="[0]" Condition="(type &amp; c4::yml::KEY) != 0">c4::yml::KEY</Item>
<Item Name="[1]" Condition="(type &amp; c4::yml::VAL) != 0">c4::yml::VAL</Item>
<Item Name="[2]" Condition="(type &amp; c4::yml::MAP) != 0">c4::yml::MAP</Item>
<Item Name="[3]" Condition="(type &amp; c4::yml::SEQ) != 0">c4::yml::SEQ</Item>
<Item Name="[4]" Condition="(type &amp; c4::yml::DOC) != 0">c4::yml::DOC</Item>
@@ -41,8 +53,25 @@ See also:
<Item Name="[9]" Condition="(type &amp; c4::yml::VALANCH) != 0">c4::yml::VALANCH</Item>
<Item Name="[10]" Condition="(type &amp; c4::yml::KEYTAG) != 0">c4::yml::KEYTAG</Item>
<Item Name="[11]" Condition="(type &amp; c4::yml::VALTAG) != 0">c4::yml::VALTAG</Item>
<Item Name="[12]" Condition="(type &amp; c4::yml::VALQUO) != 0">c4::yml::VALQUO</Item>
<Item Name="[13]" Condition="(type &amp; c4::yml::KEYQUO) != 0">c4::yml::KEYQUO</Item>
</Expand>
</Synthetic>
<Synthetic Name="[style bits]">
<Expand>
<Item Name="[0]" Condition="(type &amp; c4::yml::_WIP_KEY_UNFILT) != 0">c4::yml::_WIP_KEY_UNFILT</Item>
<Item Name="[1]" Condition="(type &amp; c4::yml::_WIP_VAL_UNFILT) != 0">c4::yml::_WIP_VAL_UNFILT</Item>
<Item Name="[2]" Condition="(type &amp; c4::yml::_WIP_STYLE_FLOW_SL) != 0">c4::yml::_WIP_STYLE_FLOW</Item>
<Item Name="[3]" Condition="(type &amp; c4::yml::_WIP_STYLE_FLOW_ML) != 0">c4::yml::_WIP_STYLE_FLOW</Item>
<Item Name="[4]" Condition="(type &amp; c4::yml::_WIP_STYLE_BLOCK) != 0">c4::yml::_WIP_STYLE_BLOCK</Item>
<Item Name="[5]" Condition="(type &amp; c4::yml::_WIP_KEY_LITERAL) != 0">c4::yml::_WIP_KEY_LITERAL</Item>
<Item Name="[6]" Condition="(type &amp; c4::yml::_WIP_VAL_LITERAL) != 0">c4::yml::_WIP_VAL_LITERAL</Item>
<Item Name="[7]" Condition="(type &amp; c4::yml::_WIP_KEY_FOLDED) != 0">c4::yml::_WIP_KEY_FOLDED</Item>
<Item Name="[8]" Condition="(type &amp; c4::yml::_WIP_VAL_FOLDED) != 0">c4::yml::_WIP_VAL_FOLDED</Item>
<Item Name="[9]" Condition="(type &amp; c4::yml::_WIP_KEY_SQUO) != 0">c4::yml::_WIP_KEY_SQUO</Item>
<Item Name="[10]" Condition="(type &amp; c4::yml::_WIP_VAL_SQUO) != 0">c4::yml::_WIP_VAL_SQUO</Item>
<Item Name="[11]" Condition="(type &amp; c4::yml::_WIP_KEY_DQUO) != 0">c4::yml::_WIP_KEY_DQUO</Item>
<Item Name="[12]" Condition="(type &amp; c4::yml::_WIP_VAL_DQUO) != 0">c4::yml::_WIP_VAL_DQUO</Item>
<Item Name="[13]" Condition="(type &amp; c4::yml::_WIP_KEY_PLAIN) != 0">c4::yml::_WIP_KEY_PLAIN</Item>
<Item Name="[14]" Condition="(type &amp; c4::yml::_WIP_VAL_PLAIN) != 0">c4::yml::_WIP_VAL_PLAIN</Item>
</Expand>
</Synthetic>
</Expand>
@@ -216,4 +245,60 @@ See also:
</Expand>
</Type>
<Type Name="c4::yml::detail::FilterProcessorSrcDst">
<DisplayString>src={src.str,[rpos]} dst={dst.str,[wpos]}</DisplayString>
<Expand>
<Item Name="[src]">src</Item>
<Item Name="[dst]">dst</Item>
<Item Name="[rpos]">rpos</Item>
<Item Name="[wpos]">wpos</Item>
<Synthetic Name="[read]">
<StringView>src.str,[rpos]</StringView>
<Expand>
<ArrayItems>
<Size>rpos</Size>
<ValuePointer>src.str</ValuePointer>
</ArrayItems>
</Expand>
</Synthetic>
</Expand>
</Type>
<Type Name="c4::yml::detail::FilterProcessorInplace">
<DisplayString>src={src.str,[rpos]} dst={src.str,[wpos]}</DisplayString>
<Expand>
<Item Name="[rpos]">rpos</Item>
<Item Name="[wpos]">wpos</Item>
<Item Name="[wcap]">wcap</Item>
<Synthetic Name="[buf]">
<StringView>src.str,[wcap]</StringView>
<Expand>
<ArrayItems>
<Size>wcap</Size>
<ValuePointer>src.str</ValuePointer>
</ArrayItems>
</Expand>
</Synthetic>
<Item Name="[src]">src</Item>
<Synthetic Name="[to be read]">
<StringView>src.str+rpos,[src.len-rpos]</StringView>
<Expand>
<ArrayItems><Size>src.len-rpos</Size><ValuePointer>src.str+rpos</ValuePointer></ArrayItems>
</Expand>
</Synthetic>
<Synthetic Name="[read]">
<StringView>src.str,[rpos]</StringView>
<Expand>
<ArrayItems><Size>rpos</Size><ValuePointer>src.str</ValuePointer></ArrayItems>
</Expand>
</Synthetic>
<Synthetic Name="[written]">
<StringView>src.str,[wpos]</StringView>
<Expand>
<ArrayItems><Size>wpos</Size><ValuePointer>src.str</ValuePointer></ArrayItems>
</Expand>
</Synthetic>
</Expand>
</Type>
</AutoVisualizer>

View File

@@ -95,8 +95,19 @@
<ClInclude Include="include\c4\yml\std\std.hpp" />
<ClInclude Include="include\c4\yml\std\string.hpp" />
<ClInclude Include="include\c4\yml\std\vector.hpp" />
<ClInclude Include="include\c4\yml\tag.hpp" />
<ClInclude Include="include\c4\yml\version.hpp" />
<ClInclude Include="include\c4\yml\tree.hpp" />
<ClInclude Include="include\c4\yml\writer.hpp" />
<ClInclude Include="include\c4\yml\event_handler_stack.hpp" />
<ClInclude Include="include\c4\yml\event_handler_tree.hpp" />
<ClInclude Include="include\c4\yml\filter_processor.hpp" />
<ClInclude Include="include\c4\yml\fwd.hpp" />
<ClInclude Include="include\c4\yml\node_type.hpp" />
<ClInclude Include="include\c4\yml\parse_engine.def.hpp" />
<ClInclude Include="include\c4\yml\parse_engine.hpp" />
<ClInclude Include="include\c4\yml\parser_state.hpp" />
<ClInclude Include="include\c4\yml\reference_resolver.hpp" />
<ClInclude Include="include\c4\yml\yml.hpp" />
<ClInclude Include="include\ryml.hpp" />
<ClInclude Include="include\ryml_std.hpp" />
@@ -117,6 +128,10 @@
<ClCompile Include="src\c4\yml\parse.cpp" />
<ClCompile Include="src\c4\yml\preprocess.cpp" />
<ClCompile Include="src\c4\yml\tree.cpp" />
<ClCompile Include="src\c4\yml\node_type.cpp" />
<ClCompile Include="src\c4\yml\reference_resolver.cpp" />
<ClCompile Include="src\c4\yml\tag.cpp" />
<ClCompile Include="src\c4\yml\version.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">

View File

@@ -6,11 +6,14 @@
# pragma clang diagnostic ignored "-Wold-style-cast"
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wuseless-cast"
# pragma GCC diagnostic ignored "-Wchar-subscripts"
# pragma GCC diagnostic ignored "-Wtype-limits"
# pragma GCC diagnostic ignored "-Wold-style-cast"
#endif
// NOLINTBEGIN(bugprone-signed-char-misuse,cert-str34-c,hicpp-signed-bitwise)
namespace c4 {
namespace detail {
@@ -78,7 +81,7 @@ void base64_test_tables()
for(size_t i = 0; i < C4_COUNTOF(detail::base64_sextet_to_char_); ++i)
{
char s2c = base64_sextet_to_char_[i];
char c2s = base64_char_to_sextet_[(int)s2c];
char c2s = base64_char_to_sextet_[(unsigned)s2c];
C4_CHECK((size_t)c2s == i);
}
for(size_t i = 0; i < C4_COUNTOF(detail::base64_char_to_sextet_); ++i)
@@ -86,7 +89,7 @@ void base64_test_tables()
char c2s = base64_char_to_sextet_[i];
if(c2s == char(-1))
continue;
char s2c = base64_sextet_to_char_[(int)c2s];
char s2c = base64_sextet_to_char_[(unsigned)c2s];
C4_CHECK((size_t)s2c == i);
}
}
@@ -96,7 +99,7 @@ void base64_test_tables()
bool base64_valid(csubstr encoded)
{
if(encoded.len & 3u) // (encoded.len % 4u)
if((encoded.len & size_t(3u)) != size_t(0)) // (encoded.len % 4u)
return false;
for(const char c : encoded)
{
@@ -159,7 +162,7 @@ size_t base64_decode(csubstr encoded, blob data)
#define c4append_(c) { if(wpos < data.len) { data.buf[wpos] = static_cast<c4::byte>(c); } ++wpos; }
#define c4appendval_(c, shift)\
{\
C4_XASSERT(c >= 0);\
C4_XASSERT((c) >= 0);\
C4_XASSERT(size_t(c) < sizeof(detail::base64_char_to_sextet_));\
val |= static_cast<uint32_t>(detail::base64_char_to_sextet_[(c)]) << ((shift) * 6);\
}
@@ -214,6 +217,8 @@ size_t base64_decode(csubstr encoded, blob data)
} // namespace c4
// NOLINTEND(bugprone-signed-char-misuse,cert-str34-c,hicpp-signed-bitwise)
#ifdef __clang__
# pragma clang diagnostic pop
#elif defined(__GNUC__)

View File

@@ -4,9 +4,9 @@
#include <stdio.h>
#include <stdarg.h>
#define C4_LOGF_ERR(...) fprintf(stderr, __VA_ARGS__); fflush(stderr)
#define C4_LOGF_WARN(...) fprintf(stderr, __VA_ARGS__); fflush(stderr)
#define C4_LOGP(msg, ...) printf(msg)
#define C4_LOGF_ERR(...) (void)fprintf(stderr, __VA_ARGS__); (void)fflush(stderr)
#define C4_LOGF_WARN(...) (void)fprintf(stderr, __VA_ARGS__); (void)fflush(stderr)
#define C4_LOGP(msg, ...) (void)printf(msg)
#if defined(C4_XBOX) || (defined(C4_WIN) && defined(C4_MSVC))
# include "c4/windows.hpp"
@@ -41,6 +41,7 @@
# pragma GCC diagnostic ignored "-Wformat-nonliteral"
# pragma GCC diagnostic ignored "-Wold-style-cast"
#endif
// NOLINTBEGIN(*use-anonymous-namespace*,cert-dcl50-cpp)
//-----------------------------------------------------------------------------
@@ -49,6 +50,7 @@ namespace c4 {
static error_flags s_error_flags = ON_ERROR_DEFAULTS;
static error_callback_type s_error_callback = nullptr;
//-----------------------------------------------------------------------------
error_flags get_error_flags()
@@ -70,6 +72,7 @@ void set_error_callback(error_callback_type cb)
s_error_callback = cb;
}
//-----------------------------------------------------------------------------
void handle_error(srcloc where, const char *fmt, ...)
@@ -80,7 +83,7 @@ void handle_error(srcloc where, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
int ilen = vsnprintf(buf, sizeof(buf), fmt, args); // ss.vprintf(fmt, args);
int ilen = vsnprintf(buf, sizeof(buf), fmt, args); // NOLINT(clang-analyzer-valist.Uninitialized)
va_end(args);
msglen = ilen >= 0 && ilen < (int)sizeof(buf) ? static_cast<size_t>(ilen) : sizeof(buf)-1;
}
@@ -102,23 +105,24 @@ void handle_error(srcloc where, const char *fmt, ...)
{
if(s_error_callback)
{
s_error_callback(buf, msglen/*ss.c_strp(), ss.tellp()*/);
s_error_callback(buf, msglen);
}
}
if(s_error_flags & ON_ERROR_THROW)
{
#if defined(C4_EXCEPTIONS_ENABLED) && defined(C4_ERROR_THROWS_EXCEPTION)
throw std::runtime_error(buf);
#endif
}
if(s_error_flags & ON_ERROR_ABORT)
{
abort();
}
if(s_error_flags & ON_ERROR_THROW)
{
#if defined(C4_EXCEPTIONS_ENABLED) && defined(C4_ERROR_THROWS_EXCEPTION)
throw std::runtime_error(buf);
#else
abort();
#endif
}
abort(); // abort anyway, in case nothing was set
C4_UNREACHABLE_AFTER_ERR();
}
//-----------------------------------------------------------------------------
@@ -126,20 +130,23 @@ void handle_error(srcloc where, const char *fmt, ...)
void handle_warning(srcloc where, const char *fmt, ...)
{
va_list args;
char buf[1024]; //sstream<c4::string> ss;
char buf[1024];
va_start(args, fmt);
vsnprintf(buf, sizeof(buf), fmt, args);
int ret = vsnprintf(buf, sizeof(buf), fmt, args); // NOLINT(clang-analyzer-valist.Uninitialized)
if(ret+1 > (int)sizeof(buf))
buf[sizeof(buf) - 1] = '\0'; // truncate
else if(ret < 0)
buf[0] = '\0'; // output/format error
va_end(args);
C4_LOGF_WARN("\n");
#if defined(C4_ERROR_SHOWS_FILELINE) && defined(C4_ERROR_SHOWS_FUNC)
C4_LOGF_WARN("%s:%d: WARNING: %s\n", where.file, where.line, buf/*ss.c_strp()*/);
C4_LOGF_WARN("%s:%d: WARNING: %s\n", where.file, where.line, buf);
C4_LOGF_WARN("%s:%d: WARNING: here: %s\n", where.file, where.line, where.func);
#elif defined(C4_ERROR_SHOWS_FILELINE)
C4_LOGF_WARN("%s:%d: WARNING: %s\n", where.file, where.line, buf/*ss.c_strp()*/);
C4_LOGF_WARN("%s:%d: WARNING: %s\n", where.file, where.line, buf);
#elif ! defined(C4_ERROR_SHOWS_FUNC)
C4_LOGF_WARN("WARNING: %s\n", buf/*ss.c_strp()*/);
C4_LOGF_WARN("WARNING: %s\n", buf);
#endif
//c4::log.flush();
}
//-----------------------------------------------------------------------------
@@ -158,30 +165,21 @@ bool is_debugger_attached()
//! @see http://stackoverflow.com/questions/3596781/how-to-detect-if-the-current-process-is-being-run-by-gdb
//! (this answer: http://stackoverflow.com/a/24969863/3968589 )
char buf[1024] = "";
int status_fd = open("/proc/self/status", O_RDONLY);
int status_fd = open("/proc/self/status", O_RDONLY); // NOLINT
if (status_fd == -1)
return false;
ssize_t num_read = ::read(status_fd, buf, sizeof(buf));
if (num_read > 0)
{
return 0;
}
else
{
ssize_t num_read = ::read(status_fd, buf, sizeof(buf));
if (num_read > 0)
{
static const char TracerPid[] = "TracerPid:";
char *tracer_pid;
if(num_read < 1024)
{
buf[num_read] = 0;
}
tracer_pid = strstr(buf, TracerPid);
if (tracer_pid)
{
first_call_result = !!::atoi(tracer_pid + sizeof(TracerPid) - 1);
}
}
close(status_fd);
static const char TracerPid[] = "TracerPid:";
char *tracer_pid;
if(num_read < 1024)
buf[num_read] = 0;
tracer_pid = strstr(buf, TracerPid);
if(tracer_pid)
first_call_result = !!::atoi(tracer_pid + sizeof(TracerPid) - 1); // NOLINT
}
close(status_fd);
C4_SUPPRESS_WARNING_GCC_POP
}
return first_call_result;
@@ -216,6 +214,7 @@ bool is_debugger_attached()
size = sizeof(info);
junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0);
assert(junk == 0);
(void)junk;
// We're being debugged if the P_TRACED flag is set.
return ((info.kp_proc.p_flag & P_TRACED) != 0);
@@ -226,6 +225,7 @@ bool is_debugger_attached()
} // namespace c4
// NOLINTEND(*use-anonymous-namespace*,cert-dcl50-cpp)
#ifdef __clang__
# pragma clang diagnostic pop

View File

@@ -19,7 +19,7 @@ size_t to_chars(substr buf, fmt::const_raw_wrapper r)
{
void * vptr = buf.str;
size_t space = buf.len;
auto ptr = (decltype(buf.str)) std::align(r.alignment, r.len, vptr, space);
char * ptr = (char*) std::align(r.alignment, r.len, vptr, space);
if(ptr == nullptr)
{
// if it was not possible to align, return a conservative estimate
@@ -42,7 +42,7 @@ bool from_chars(csubstr buf, fmt::raw_wrapper *r)
void * vptr = (void*)buf.str;
C4_SUPPRESS_WARNING_GCC_POP
size_t space = buf.len;
auto ptr = (decltype(buf.str)) std::align(r->alignment, r->len, vptr, space);
char * ptr = (char*) std::align(r->alignment, r->len, vptr, space);
C4_CHECK(ptr != nullptr);
C4_CHECK(ptr >= buf.begin() && ptr <= buf.end());
C4_SUPPRESS_WARNING_GCC_PUSH

View File

@@ -19,7 +19,7 @@ void mem_repeat(void* dest, void const* pattern, size_t pattern_size, size_t num
while(begin + 2*n < end)
{
::memcpy(begin + n, begin, n);
n <<= 1; // double n
n <<= 1u; // double n
}
// copy the missing part
if(begin + n < end)

View File

@@ -7,8 +7,9 @@ C4_SUPPRESS_WARNING_GCC_CLANG_WITH_PUSH("-Wold-style-cast")
size_t decode_code_point(uint8_t *C4_RESTRICT buf, size_t buflen, const uint32_t code)
{
C4_UNUSED(buflen);
C4_ASSERT(buf);
C4_ASSERT(buflen >= 4);
C4_UNUSED(buflen);
if (code <= UINT32_C(0x7f))
{
buf[0] = (uint8_t)code;
@@ -16,23 +17,23 @@ size_t decode_code_point(uint8_t *C4_RESTRICT buf, size_t buflen, const uint32_t
}
else if(code <= UINT32_C(0x7ff))
{
buf[0] = (uint8_t)(UINT32_C(0xc0) | (code >> 6)); /* 110xxxxx */
buf[0] = (uint8_t)(UINT32_C(0xc0) | (code >> 6u)); /* 110xxxxx */
buf[1] = (uint8_t)(UINT32_C(0x80) | (code & UINT32_C(0x3f))); /* 10xxxxxx */
return 2u;
}
else if(code <= UINT32_C(0xffff))
{
buf[0] = (uint8_t)(UINT32_C(0xe0) | ((code >> 12))); /* 1110xxxx */
buf[1] = (uint8_t)(UINT32_C(0x80) | ((code >> 6) & UINT32_C(0x3f))); /* 10xxxxxx */
buf[2] = (uint8_t)(UINT32_C(0x80) | ((code ) & UINT32_C(0x3f))); /* 10xxxxxx */
buf[0] = (uint8_t)(UINT32_C(0xe0) | ((code >> 12u))); /* 1110xxxx */
buf[1] = (uint8_t)(UINT32_C(0x80) | ((code >> 6u) & UINT32_C(0x3f))); /* 10xxxxxx */
buf[2] = (uint8_t)(UINT32_C(0x80) | ((code ) & UINT32_C(0x3f))); /* 10xxxxxx */
return 3u;
}
else if(code <= UINT32_C(0x10ffff))
{
buf[0] = (uint8_t)(UINT32_C(0xf0) | ((code >> 18))); /* 11110xxx */
buf[1] = (uint8_t)(UINT32_C(0x80) | ((code >> 12) & UINT32_C(0x3f))); /* 10xxxxxx */
buf[2] = (uint8_t)(UINT32_C(0x80) | ((code >> 6) & UINT32_C(0x3f))); /* 10xxxxxx */
buf[3] = (uint8_t)(UINT32_C(0x80) | ((code ) & UINT32_C(0x3f))); /* 10xxxxxx */
buf[0] = (uint8_t)(UINT32_C(0xf0) | ((code >> 18u))); /* 11110xxx */
buf[1] = (uint8_t)(UINT32_C(0x80) | ((code >> 12u) & UINT32_C(0x3f))); /* 10xxxxxx */
buf[2] = (uint8_t)(UINT32_C(0x80) | ((code >> 6u) & UINT32_C(0x3f))); /* 10xxxxxx */
buf[3] = (uint8_t)(UINT32_C(0x80) | ((code ) & UINT32_C(0x3f))); /* 10xxxxxx */
return 4u;
}
return 0;
@@ -55,6 +56,59 @@ substr decode_code_point(substr out, csubstr code_point)
return out.first(ret);
}
size_t first_non_bom(csubstr s)
{
#define c4check2_(s, c0, c1) ((s).len >= 2) && (((s).str[0] == (c0)) && ((s).str[1] == (c1)))
#define c4check3_(s, c0, c1, c2) ((s).len >= 3) && (((s).str[0] == (c0)) && ((s).str[1] == (c1)) && ((s).str[2] == (c2)))
#define c4check4_(s, c0, c1, c2, c3) ((s).len >= 4) && (((s).str[0] == (c0)) && ((s).str[1] == (c1)) && ((s).str[2] == (c2)) && ((s).str[3] == (c3)))
// see https://en.wikipedia.org/wiki/Byte_order_mark#Byte-order_marks_by_encoding
if(s.len < 2u)
return false;
else if(c4check3_(s, '\xef', '\xbb', '\xbf')) // UTF-8
return 3u;
else if(c4check4_(s, '\x00', '\x00', '\xfe', '\xff')) // UTF-32BE
return 4u;
else if(c4check4_(s, '\xff', '\xfe', '\x00', '\x00')) // UTF-32LE
return 4u;
else if(c4check2_(s, '\xfe', '\xff')) // UTF-16BE
return 2u;
else if(c4check2_(s, '\xff', '\xfe')) // UTF-16BE
return 2u;
else if(c4check3_(s, '\x2b', '\x2f', '\x76')) // UTF-7
return 3u;
else if(c4check3_(s, '\xf7', '\x64', '\x4c')) // UTF-1
return 3u;
else if(c4check4_(s, '\xdd', '\x73', '\x66', '\x73')) // UTF-EBCDIC
return 4u;
else if(c4check3_(s, '\x0e', '\xfe', '\xff')) // SCSU
return 3u;
else if(c4check3_(s, '\xfb', '\xee', '\x28')) // BOCU-1
return 3u;
else if(c4check4_(s, '\x84', '\x31', '\x95', '\x33')) // GB18030
return 4u;
return 0u;
#undef c4check2_
#undef c4check3_
#undef c4check4_
}
substr get_bom(substr s)
{
return s.first(first_non_bom(s));
}
csubstr get_bom(csubstr s)
{
return s.first(first_non_bom(s));
}
substr skip_bom(substr s)
{
return s.sub(first_non_bom(s));
}
csubstr skip_bom(csubstr s)
{
return s.sub(first_non_bom(s));
}
C4_SUPPRESS_WARNING_GCC_CLANG_POP
} // namespace c4

View File

@@ -28,21 +28,27 @@ void report_error_impl(const char* msg, size_t length, Location loc, FILE *f)
{
if(!loc.name.empty())
{
fwrite(loc.name.str, 1, loc.name.len, f);
fputc(':', f);
// this is more portable than using fprintf("%.*s:") which
// is not available in some embedded platforms
fwrite(loc.name.str, 1, loc.name.len, f); // NOLINT
fputc(':', f); // NOLINT
}
fprintf(f, "%zu:", loc.line);
fprintf(f, "%zu:", loc.line); // NOLINT
if(loc.col)
fprintf(f, "%zu:", loc.col);
fprintf(f, "%zu:", loc.col); // NOLINT
if(loc.offset)
fprintf(f, " (%zuB):", loc.offset);
fprintf(f, " (%zuB):", loc.offset); // NOLINT
fputc(' ', f); // NOLINT
}
fprintf(f, "%.*s\n", (int)length, msg);
fflush(f);
RYML_ASSERT(!csubstr(msg, length).ends_with('\0'));
fwrite(msg, 1, length, f); // NOLINT
fputc('\n', f); // NOLINT
fflush(f); // NOLINT
}
[[noreturn]] void error_impl(const char* msg, size_t length, Location loc, void * /*user_data*/)
{
RYML_ASSERT(!csubstr(msg, length).ends_with('\0'));
report_error_impl(msg, length, loc, nullptr);
#ifdef RYML_DEFAULT_CALLBACK_USES_EXCEPTIONS
throw std::runtime_error(std::string(msg, length));
@@ -70,7 +76,7 @@ void free_impl(void *mem, size_t /*length*/, void * /*user_data*/)
Callbacks::Callbacks()
Callbacks::Callbacks() noexcept
:
m_user_data(nullptr),
#ifndef RYML_NO_DEFAULT_CALLBACKS
@@ -98,9 +104,9 @@ Callbacks::Callbacks(void *user_data, pfn_allocate alloc_, pfn_free free_, pfn_e
m_error(error_)
#endif
{
C4_CHECK(m_allocate);
C4_CHECK(m_free);
C4_CHECK(m_error);
RYML_CHECK(m_allocate);
RYML_CHECK(m_free);
RYML_CHECK(m_error);
}

View File

@@ -0,0 +1,215 @@
#include "c4/yml/node_type.hpp"
namespace c4 {
namespace yml {
const char* NodeType::type_str(NodeType_e ty) noexcept
{
switch(ty & _TYMASK)
{
case KEYVAL:
return "KEYVAL";
case KEY:
return "KEY";
case VAL:
return "VAL";
case MAP:
return "MAP";
case SEQ:
return "SEQ";
case KEYMAP:
return "KEYMAP";
case KEYSEQ:
return "KEYSEQ";
case DOCSEQ:
return "DOCSEQ";
case DOCMAP:
return "DOCMAP";
case DOCVAL:
return "DOCVAL";
case DOC:
return "DOC";
case STREAM:
return "STREAM";
case NOTYPE:
return "NOTYPE";
default:
if((ty & KEYVAL) == KEYVAL)
return "KEYVAL***";
if((ty & KEYMAP) == KEYMAP)
return "KEYMAP***";
if((ty & KEYSEQ) == KEYSEQ)
return "KEYSEQ***";
if((ty & DOCSEQ) == DOCSEQ)
return "DOCSEQ***";
if((ty & DOCMAP) == DOCMAP)
return "DOCMAP***";
if((ty & DOCVAL) == DOCVAL)
return "DOCVAL***";
if(ty & KEY)
return "KEY***";
if(ty & VAL)
return "VAL***";
if(ty & MAP)
return "MAP***";
if(ty & SEQ)
return "SEQ***";
if(ty & DOC)
return "DOC***";
return "(unk)";
}
}
csubstr NodeType::type_str(substr buf, NodeType_e flags) noexcept
{
size_t pos = 0;
bool gotone = false;
#define _prflag(fl, txt) \
do { \
if((flags & (fl)) == (fl)) \
{ \
if(gotone) \
{ \
if(pos + 1 < buf.len) \
buf[pos] = '|'; \
++pos; \
} \
csubstr fltxt = txt; \
if(pos + fltxt.len <= buf.len) \
memcpy(buf.str + pos, fltxt.str, fltxt.len); \
pos += fltxt.len; \
gotone = true; \
flags = (flags & ~(fl)); /*remove the flag*/ \
} \
} while(0)
_prflag(STREAM, "STREAM");
_prflag(DOC, "DOC");
// key properties
_prflag(KEY, "KEY");
_prflag(KEYNIL, "KNIL");
_prflag(KEYTAG, "KTAG");
_prflag(KEYANCH, "KANCH");
_prflag(KEYREF, "KREF");
_prflag(KEY_LITERAL, "KLITERAL");
_prflag(KEY_FOLDED, "KFOLDED");
_prflag(KEY_SQUO, "KSQUO");
_prflag(KEY_DQUO, "KDQUO");
_prflag(KEY_PLAIN, "KPLAIN");
_prflag(KEY_UNFILT, "KUNFILT");
// val properties
_prflag(VAL, "VAL");
_prflag(VALNIL, "VNIL");
_prflag(VALTAG, "VTAG");
_prflag(VALANCH, "VANCH");
_prflag(VALREF, "VREF");
_prflag(VAL_UNFILT, "VUNFILT");
_prflag(VAL_LITERAL, "VLITERAL");
_prflag(VAL_FOLDED, "VFOLDED");
_prflag(VAL_SQUO, "VSQUO");
_prflag(VAL_DQUO, "VDQUO");
_prflag(VAL_PLAIN, "VPLAIN");
_prflag(VAL_UNFILT, "VUNFILT");
// container properties
_prflag(MAP, "MAP");
_prflag(SEQ, "SEQ");
_prflag(FLOW_SL, "FLOWSL");
_prflag(FLOW_ML, "FLOWML");
_prflag(BLOCK, "BLCK");
if(pos == 0)
_prflag(NOTYPE, "NOTYPE");
#undef _prflag
if(pos < buf.len)
{
buf[pos] = '\0';
return buf.first(pos);
}
else
{
csubstr failed;
failed.len = pos + 1;
failed.str = nullptr;
return failed;
}
}
//-----------------------------------------------------------------------------
// see https://www.yaml.info/learn/quote.html#noplain
bool scalar_style_query_squo(csubstr s) noexcept
{
return ! s.first_of_any("\n ", "\n\t");
}
// see https://www.yaml.info/learn/quote.html#noplain
bool scalar_style_query_plain(csubstr s) noexcept
{
if(s.begins_with("-."))
{
if(s == "-.inf" || s == "-.INF")
return true;
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(":#"))
// make this check in the last place, as it has linear
// complexity, while the previous ones are
// constant-time
&& (s.first_of("\n#:[]{},") == npos);
}
NodeType_e scalar_style_choose(csubstr s) noexcept
{
if(s.len)
{
if(s.begins_with_any(" \n\t")
||
s.ends_with_any(" \n\t"))
{
return SCALAR_DQUO;
}
else if( ! scalar_style_query_plain(s))
{
return scalar_style_query_squo(s) ? SCALAR_SQUO : SCALAR_DQUO;
}
// nothing remarkable - use plain
return SCALAR_PLAIN;
}
return s.str ? SCALAR_SQUO : SCALAR_PLAIN;
}
NodeType_e scalar_style_json_choose(csubstr s) noexcept
{
// do not quote special cases
bool plain = (
(s == "true" || s == "false" || s == "null")
||
(
// do not quote numbers
s.is_number()
&&
(
// quote integral numbers if they have a leading 0
// https://github.com/biojppm/rapidyaml/issues/291
(!(s.len > 1 && s.begins_with('0')))
// do not quote reals with leading 0
// https://github.com/biojppm/rapidyaml/issues/313
|| (s.find('.') != csubstr::npos)
)
)
);
return plain ? SCALAR_PLAIN : SCALAR_DQUO;
}
} // namespace yml
} // namespace c4

File diff suppressed because it is too large Load Diff

View File

@@ -21,7 +21,7 @@ C4_ALWAYS_INLINE bool _is_idchar(char c)
|| (c == '_' || c == '-' || c == '~' || c == '$');
}
typedef enum { kReadPending = 0, kKeyPending = 1, kValPending = 2 } _ppstate;
enum _ppstate : int { kReadPending = 0, kKeyPending = 1, kValPending = 2 };
C4_ALWAYS_INLINE _ppstate _next(_ppstate s)
{
int n = (int)s + 1;

View File

@@ -0,0 +1,333 @@
#include "c4/yml/reference_resolver.hpp"
#include "c4/yml/common.hpp"
#include "c4/yml/detail/parser_dbg.hpp"
#ifdef RYML_DBG
#include "c4/yml/detail/print.hpp"
#else
#define _c4dbg_tree(...)
#define _c4dbg_node(...)
#endif
namespace c4 {
namespace yml {
/** @cond dev */
id_type ReferenceResolver::count_anchors_and_refs_(id_type n)
{
id_type c = 0;
c += m_tree->has_key_anchor(n);
c += m_tree->has_val_anchor(n);
c += m_tree->is_key_ref(n);
c += m_tree->is_val_ref(n);
c += m_tree->has_key(n) && m_tree->key(n) == "<<";
for(id_type ch = m_tree->first_child(n); ch != NONE; ch = m_tree->next_sibling(ch))
c += count_anchors_and_refs_(ch);
return c;
}
void ReferenceResolver::gather_anchors_and_refs__(id_type n)
{
// insert key refs BEFORE inserting val refs
if(m_tree->has_key(n))
{
if(m_tree->key(n) == "<<")
{
_c4dbgpf("node[{}]: key is <<", n);
if(m_tree->has_val(n))
{
if(m_tree->is_val_ref(n))
{
_c4dbgpf("node[{}]: instance[{}]: val ref, inheriting! '{}'", n, m_refs.size(), m_tree->val_ref(n));
m_refs.push({VALREF, n, NONE, NONE, NONE, NONE});
//m_refs.push({KEYREF, n, NONE, NONE, NONE, NONE});
}
else
{
_c4dbgpf("node[{}]: not ref!", n);
}
}
else if(m_tree->is_seq(n))
{
// for merging multiple inheritance targets
// <<: [ *CENTER, *BIG ]
_c4dbgpf("node[{}]: is seq!", n);
for(id_type ich = m_tree->first_child(n); ich != NONE; ich = m_tree->next_sibling(ich))
{
_c4dbgpf("node[{}]: instance [{}]: val ref, inheriting multiple: {} '{}'", n, m_refs.size(), ich, m_tree->val_ref(ich));
if(m_tree->is_container(ich))
{
detail::_report_err(m_tree->m_callbacks, "ERROR: node {} child {}: refs for << cannot be containers.'", n, ich);
C4_UNREACHABLE_AFTER_ERR();
}
m_refs.push({VALREF, ich, NONE, NONE, n, m_tree->next_sibling(n)});
}
return; // don't descend into the seq
}
else
{
detail::_report_err(m_tree->m_callbacks, "ERROR: node {}: refs for << must be either val or seq", n);
C4_UNREACHABLE_AFTER_ERR();
}
}
else if(m_tree->is_key_ref(n))
{
_c4dbgpf("node[{}]: instance[{}]: key ref: '{}', key='{}'", n, m_refs.size(), m_tree->key_ref(n), m_tree->has_key(n) ? m_tree->key(n) : csubstr{"-"});
_RYML_CB_ASSERT(m_tree->m_callbacks, m_tree->key(n) != "<<");
_RYML_CB_CHECK(m_tree->m_callbacks, (!m_tree->has_key(n)) || m_tree->key(n).ends_with(m_tree->key_ref(n)));
m_refs.push({KEYREF, n, NONE, NONE, NONE, NONE});
}
}
// val ref
if(m_tree->is_val_ref(n) && (!m_tree->has_key(n) || m_tree->key(n) != "<<"))
{
_c4dbgpf("node[{}]: instance[{}]: val ref: '{}'", n, m_refs.size(), m_tree->val_ref(n));
RYML_CHECK((!m_tree->has_val(n)) || m_tree->val(n).ends_with(m_tree->val_ref(n)));
m_refs.push({VALREF, n, NONE, NONE, NONE, NONE});
}
// anchors
if(m_tree->has_key_anchor(n))
{
_c4dbgpf("node[{}]: instance[{}]: key anchor: '{}'", n, m_refs.size(), m_tree->key_anchor(n));
RYML_CHECK(m_tree->has_key(n));
m_refs.push({KEYANCH, n, NONE, NONE, NONE, NONE});
}
if(m_tree->has_val_anchor(n))
{
_c4dbgpf("node[{}]: instance[{}]: val anchor: '{}'", n, m_refs.size(), m_tree->val_anchor(n));
RYML_CHECK(m_tree->has_val(n) || m_tree->is_container(n));
m_refs.push({VALANCH, n, NONE, NONE, NONE, NONE});
}
// recurse
for(id_type ch = m_tree->first_child(n); ch != NONE; ch = m_tree->next_sibling(ch))
gather_anchors_and_refs__(ch);
}
void ReferenceResolver::gather_anchors_and_refs_()
{
_c4dbgp("gathering anchors and refs...");
// minimize (re-)allocations by counting first
id_type num_anchors_and_refs = count_anchors_and_refs_(m_tree->root_id());
if(!num_anchors_and_refs)
return;
m_refs.reserve(num_anchors_and_refs);
m_refs.clear();
// now descend through the hierarchy
gather_anchors_and_refs__(m_tree->root_id());
_c4dbgpf("found {} anchors/refs", m_refs.size());
// finally connect the reference list
id_type prev_anchor = NONE;
id_type count = 0;
for(auto &rd : m_refs)
{
rd.prev_anchor = prev_anchor;
if(rd.type.has_anchor())
prev_anchor = count;
++count;
}
_c4dbgp("gathering anchors and refs: finished");
}
id_type ReferenceResolver::lookup_(RefData const* C4_RESTRICT ra)
{
#ifdef RYML_DBG
id_type instance = static_cast<id_type>(ra-m_refs.m_stack);
id_type node = ra->node;
#endif
RYML_ASSERT(ra->type.is_key_ref() || ra->type.is_val_ref());
RYML_ASSERT(ra->type.is_key_ref() != ra->type.is_val_ref());
csubstr refname;
_c4dbgpf("instance[{}:node{}]: lookup from node={}...", instance, node, ra->node);
if(ra->type.is_val_ref())
{
refname = m_tree->val_ref(ra->node);
_c4dbgpf("instance[{}:node{}]: valref: '{}'", instance, node, refname);
}
else
{
RYML_ASSERT(ra->type.is_key_ref());
refname = m_tree->key_ref(ra->node);
_c4dbgpf("instance[{}:node{}]: keyref: '{}'", instance, node, refname);
}
while(ra->prev_anchor != NONE)
{
ra = &m_refs[ra->prev_anchor];
_c4dbgpf("instance[{}:node{}]: lookup '{}' at [{}:node{}]: keyref='{}' valref='{}'", instance, node, refname, ra-m_refs.m_stack, ra->node,
(m_tree->has_key_anchor(ra->node) ? m_tree->key_anchor(ra->node) : csubstr("~")),
(m_tree->has_val_anchor(ra->node) ? m_tree->val_anchor(ra->node) : csubstr("~")));
if(m_tree->has_anchor(ra->node, refname))
{
_c4dbgpf("instance[{}:node{}]: got it at [{}:node{}]!", instance, node, ra-m_refs.m_stack, ra->node);
return ra->node;
}
}
detail::_report_err(m_tree->m_callbacks, "ERROR: anchor not found: '{}'", refname);
C4_UNREACHABLE_AFTER_ERR();
}
void ReferenceResolver::reset_(Tree *t_)
{
if(t_->callbacks() != m_refs.m_callbacks)
{
m_refs.m_callbacks = t_->callbacks();
}
m_tree = t_;
m_refs.clear();
}
void ReferenceResolver::resolve_()
{
/* from the specs: "an alias node refers to the most recent
* node in the serialization having the specified anchor". So
* we need to start looking upward from ref nodes.
*
* @see http://yaml.org/spec/1.2/spec.html#id2765878 */
_c4dbgp("matching anchors/refs...");
for(id_type i = 0, e = m_refs.size(); i < e; ++i)
{
RefData &C4_RESTRICT refdata = m_refs.top(i);
if( ! refdata.type.is_ref())
continue;
refdata.target = lookup_(&refdata);
}
_c4dbgp("matching anchors/refs: finished");
// insert the resolved references
_c4dbgp("modifying tree...");
id_type prev_parent_ref = NONE;
id_type prev_parent_ref_after = NONE;
for(id_type i = 0, e = m_refs.size(); i < e; ++i)
{
RefData const& C4_RESTRICT refdata = m_refs[i];
_c4dbgpf("instance[{}:node{}]: {}/{}...", i, refdata.node, i+1, e);
if( ! refdata.type.is_ref())
continue;
_c4dbgpf("instance[{}:node{}]: is reference!", i, refdata.node);
if(refdata.parent_ref != NONE)
{
_c4dbgpf("instance[{}:node{}] has parent: {}", i, refdata.node, refdata.parent_ref);
_RYML_CB_ASSERT(m_tree->m_callbacks, m_tree->is_seq(refdata.parent_ref));
const id_type p = m_tree->parent(refdata.parent_ref);
const id_type after = (prev_parent_ref != refdata.parent_ref) ?
refdata.parent_ref//prev_sibling(rd.parent_ref_sibling)
:
prev_parent_ref_after;
prev_parent_ref = refdata.parent_ref;
prev_parent_ref_after = m_tree->duplicate_children_no_rep(refdata.target, p, after);
m_tree->remove(refdata.node);
}
else
{
_c4dbgpf("instance[{}:node{}] has no parent", i, refdata.node, refdata.parent_ref);
if(m_tree->has_key(refdata.node) && m_tree->key(refdata.node) == "<<")
{
_c4dbgpf("instance[{}:node{}] is inheriting", i, refdata.node);
_RYML_CB_ASSERT(m_tree->m_callbacks, m_tree->is_keyval(refdata.node));
const id_type p = m_tree->parent(refdata.node);
const id_type after = m_tree->prev_sibling(refdata.node);
_c4dbgpf("instance[{}:node{}] p={} after={}", i, refdata.node, p, after);
m_tree->duplicate_children_no_rep(refdata.target, p, after);
m_tree->remove(refdata.node);
}
else if(refdata.type.is_key_ref())
{
_c4dbgpf("instance[{}:node{}] is key ref", i, refdata.node);
_RYML_CB_ASSERT(m_tree->m_callbacks, m_tree->is_key_ref(refdata.node));
_RYML_CB_ASSERT(m_tree->m_callbacks, m_tree->has_key_anchor(refdata.target) || m_tree->has_val_anchor(refdata.target));
if(m_tree->has_val_anchor(refdata.target) && m_tree->val_anchor(refdata.target) == m_tree->key_ref(refdata.node))
{
_c4dbgpf("instance[{}:node{}] target.anchor==val.anchor=={}", i, refdata.node, m_tree->val_anchor(refdata.target));
_RYML_CB_CHECK(m_tree->m_callbacks, !m_tree->is_container(refdata.target));
_RYML_CB_CHECK(m_tree->m_callbacks, m_tree->has_val(refdata.target));
const type_bits existing_style_flags = VAL_STYLE & m_tree->_p(refdata.target)->m_type.type;
static_assert((VAL_STYLE >> 1u) == (KEY_STYLE), "bad flags");
m_tree->_p(refdata.node)->m_key.scalar = m_tree->val(refdata.target);
m_tree->_add_flags(refdata.node, KEY | (existing_style_flags >> 1u));
}
else
{
_c4dbgpf("instance[{}:node{}] don't inherit container flags", i, refdata.node);
_RYML_CB_CHECK(m_tree->m_callbacks, m_tree->key_anchor(refdata.target) == m_tree->key_ref(refdata.node));
m_tree->_p(refdata.node)->m_key.scalar = m_tree->key(refdata.target);
// keys cannot be containers, so don't inherit container flags
const type_bits existing_style_flags = KEY_STYLE & m_tree->_p(refdata.target)->m_type.type;
m_tree->_add_flags(refdata.node, KEY | existing_style_flags);
}
}
else // val ref
{
_c4dbgpf("instance[{}:node{}] is val ref", i, refdata.node);
_RYML_CB_ASSERT(m_tree->m_callbacks, refdata.type.is_val_ref());
if(m_tree->has_key_anchor(refdata.target) && m_tree->key_anchor(refdata.target) == m_tree->val_ref(refdata.node))
{
_c4dbgpf("instance[{}:node{}] target.anchor==key.anchor=={}", i, refdata.node, m_tree->val_anchor(refdata.target));
_RYML_CB_CHECK(m_tree->m_callbacks, !m_tree->is_container(refdata.target));
_RYML_CB_CHECK(m_tree->m_callbacks, m_tree->has_val(refdata.target));
// keys cannot be containers, so don't inherit container flags
const type_bits existing_style_flags = (KEY_STYLE) & m_tree->_p(refdata.target)->m_type.type;
static_assert((KEY_STYLE << 1u) == (VAL_STYLE), "bad flags");
m_tree->_p(refdata.node)->m_val.scalar = m_tree->key(refdata.target);
m_tree->_add_flags(refdata.node, VAL | (existing_style_flags << 1u));
}
else
{
_c4dbgpf("instance[{}:node{}] duplicate contents", i, refdata.node);
m_tree->duplicate_contents(refdata.target, refdata.node);
}
}
}
_c4dbg_tree("after insertion", *m_tree);
}
}
void ReferenceResolver::resolve(Tree *t_, bool clear_anchors)
{
_c4dbgp("resolving references...");
reset_(t_);
_c4dbg_tree("unresolved tree", *m_tree);
gather_anchors_and_refs_();
if(m_refs.empty())
return;
resolve_();
_c4dbg_tree("resolved tree", *m_tree);
// clear anchors and refs
if(clear_anchors)
{
_c4dbgp("clearing anchors/refs");
auto clear_ = [this]{
for(auto const& C4_RESTRICT ar : m_refs)
{
m_tree->rem_anchor_ref(ar.node);
if(ar.parent_ref != NONE)
if(m_tree->type(ar.parent_ref) != NOTYPE)
m_tree->remove(ar.parent_ref);
}
};
clear_();
// some of the elements injected during the resolution may
// have nested anchors; these anchors will have been newly
// injected during the resolution; collect again, and clear
// again, to ensure those are also cleared:
gather_anchors_and_refs_();
clear_();
_c4dbgp("clearing anchors/refs: finished");
}
_c4dbg_tree("final resolved tree", *m_tree);
m_tree = nullptr;
_c4dbgp("resolving references: finished");
}
/** @endcond */
} // namespace ryml
} // namespace c4

323
3rdparty/rapidyaml/src/c4/yml/tag.cpp vendored Normal file
View File

@@ -0,0 +1,323 @@
#include "c4/yml/tag.hpp"
#include "c4/yml/tree.hpp"
#include "c4/yml/detail/parser_dbg.hpp"
namespace c4 {
namespace yml {
bool is_custom_tag(csubstr tag)
{
if((tag.len > 2) && (tag.str[0] == '!'))
{
size_t pos = tag.find('!', 1);
return pos != npos && pos > 1 && tag.str[1] != '<';
}
return false;
}
csubstr normalize_tag(csubstr tag)
{
YamlTag_e t = to_tag(tag);
if(t != TAG_NONE)
return from_tag(t);
if(tag.begins_with("!<"))
tag = tag.sub(1);
if(tag.begins_with("<!"))
return tag;
return tag;
}
csubstr normalize_tag_long(csubstr tag)
{
YamlTag_e t = to_tag(tag);
if(t != TAG_NONE)
return from_tag_long(t);
if(tag.begins_with("!<"))
tag = tag.sub(1);
if(tag.begins_with("<!"))
return tag;
return tag;
}
csubstr normalize_tag_long(csubstr tag, substr output)
{
csubstr result = normalize_tag_long(tag);
if(result.begins_with("!!"))
{
tag = tag.sub(2);
const csubstr pfx = "<tag:yaml.org,2002:";
const size_t len = pfx.len + tag.len + 1;
if(len <= output.len)
{
memcpy(output.str , pfx.str, pfx.len);
memcpy(output.str + pfx.len, tag.str, tag.len);
output[pfx.len + tag.len] = '>';
result = output.first(len);
}
else
{
result.str = nullptr;
result.len = len;
}
}
return result;
}
YamlTag_e to_tag(csubstr tag)
{
if(tag.begins_with("!<"))
tag = tag.sub(1);
if(tag.begins_with("!!"))
tag = tag.sub(2);
else if(tag.begins_with('!'))
return TAG_NONE;
else if(tag.begins_with("tag:yaml.org,2002:"))
{
RYML_ASSERT(csubstr("tag:yaml.org,2002:").len == 18);
tag = tag.sub(18);
}
else if(tag.begins_with("<tag:yaml.org,2002:"))
{
RYML_ASSERT(csubstr("<tag:yaml.org,2002:").len == 19);
tag = tag.sub(19);
if(!tag.len)
return TAG_NONE;
tag = tag.offs(0, 1);
}
if(tag == "map")
return TAG_MAP;
else if(tag == "omap")
return TAG_OMAP;
else if(tag == "pairs")
return TAG_PAIRS;
else if(tag == "set")
return TAG_SET;
else if(tag == "seq")
return TAG_SEQ;
else if(tag == "binary")
return TAG_BINARY;
else if(tag == "bool")
return TAG_BOOL;
else if(tag == "float")
return TAG_FLOAT;
else if(tag == "int")
return TAG_INT;
else if(tag == "merge")
return TAG_MERGE;
else if(tag == "null")
return TAG_NULL;
else if(tag == "str")
return TAG_STR;
else if(tag == "timestamp")
return TAG_TIMESTAMP;
else if(tag == "value")
return TAG_VALUE;
else if(tag == "yaml")
return TAG_YAML;
return TAG_NONE;
}
csubstr from_tag_long(YamlTag_e tag)
{
switch(tag)
{
case TAG_MAP:
return {"<tag:yaml.org,2002:map>"};
case TAG_OMAP:
return {"<tag:yaml.org,2002:omap>"};
case TAG_PAIRS:
return {"<tag:yaml.org,2002:pairs>"};
case TAG_SET:
return {"<tag:yaml.org,2002:set>"};
case TAG_SEQ:
return {"<tag:yaml.org,2002:seq>"};
case TAG_BINARY:
return {"<tag:yaml.org,2002:binary>"};
case TAG_BOOL:
return {"<tag:yaml.org,2002:bool>"};
case TAG_FLOAT:
return {"<tag:yaml.org,2002:float>"};
case TAG_INT:
return {"<tag:yaml.org,2002:int>"};
case TAG_MERGE:
return {"<tag:yaml.org,2002:merge>"};
case TAG_NULL:
return {"<tag:yaml.org,2002:null>"};
case TAG_STR:
return {"<tag:yaml.org,2002:str>"};
case TAG_TIMESTAMP:
return {"<tag:yaml.org,2002:timestamp>"};
case TAG_VALUE:
return {"<tag:yaml.org,2002:value>"};
case TAG_YAML:
return {"<tag:yaml.org,2002:yaml>"};
case TAG_NONE:
default:
return {""};
}
}
csubstr from_tag(YamlTag_e tag)
{
switch(tag)
{
case TAG_MAP:
return {"!!map"};
case TAG_OMAP:
return {"!!omap"};
case TAG_PAIRS:
return {"!!pairs"};
case TAG_SET:
return {"!!set"};
case TAG_SEQ:
return {"!!seq"};
case TAG_BINARY:
return {"!!binary"};
case TAG_BOOL:
return {"!!bool"};
case TAG_FLOAT:
return {"!!float"};
case TAG_INT:
return {"!!int"};
case TAG_MERGE:
return {"!!merge"};
case TAG_NULL:
return {"!!null"};
case TAG_STR:
return {"!!str"};
case TAG_TIMESTAMP:
return {"!!timestamp"};
case TAG_VALUE:
return {"!!value"};
case TAG_YAML:
return {"!!yaml"};
case TAG_NONE:
default:
return {""};
}
}
bool TagDirective::create_from_str(csubstr directive_)
{
csubstr directive = directive_;
directive = directive.sub(4);
if(!directive.begins_with(' '))
return false;
directive = directive.triml(' ');
size_t pos = directive.find(' ');
if(pos == npos)
return false;
handle = directive.first(pos);
directive = directive.sub(handle.len).triml(' ');
pos = directive.find(' ');
if(pos != npos)
directive = directive.first(pos);
prefix = directive;
next_node_id = NONE;
_c4dbgpf("%TAG: handle={} prefix={}", handle, prefix);
return true;
}
bool TagDirective::create_from_str(csubstr directive_, Tree *tree)
{
_RYML_CB_CHECK(tree->callbacks(), directive_.begins_with("%TAG "));
if(!create_from_str(directive_))
{
_RYML_CB_ERR(tree->callbacks(), "invalid tag directive");
}
next_node_id = tree->size();
if(!tree->empty())
{
const id_type prev = tree->size() - 1;
if(tree->is_root(prev) && tree->type(prev) != NOTYPE && !tree->is_stream(prev))
++next_node_id;
}
_c4dbgpf("%TAG: handle={} prefix={} next_node={}", handle, prefix, next_node_id);
return true;
}
size_t TagDirective::transform(csubstr tag, substr output, Callbacks const& callbacks) const
{
_c4dbgpf("%TAG: handle={} prefix={} next_node={}. tag={}", handle, prefix, next_node_id, tag);
_RYML_CB_ASSERT(callbacks, tag.len >= handle.len);
csubstr rest = tag.sub(handle.len);
_c4dbgpf("%TAG: rest={}", rest);
if(rest.begins_with('<'))
{
_c4dbgpf("%TAG: begins with <. rest={}", rest);
if(C4_UNLIKELY(!rest.ends_with('>')))
_RYML_CB_ERR(callbacks, "malformed tag");
rest = rest.offs(1, 1);
if(rest.begins_with(prefix))
{
_c4dbgpf("%TAG: already transformed! actual={}", rest.sub(prefix.len));
return 0; // return 0 to signal that the tag is local and cannot be resolved
}
}
size_t len = 1u + prefix.len + rest.len + 1u;
size_t numpc = rest.count('%');
if(numpc == 0)
{
if(len <= output.len)
{
output.str[0] = '<';
memcpy(1u + output.str, prefix.str, prefix.len);
memcpy(1u + output.str + prefix.len, rest.str, rest.len);
output.str[1u + prefix.len + rest.len] = '>';
}
}
else
{
// need to decode URI % sequences
size_t pos = rest.find('%');
_RYML_CB_ASSERT(callbacks, pos != npos);
do {
size_t next = rest.first_not_of("0123456789abcdefABCDEF", pos+1);
if(next == npos)
next = rest.len;
_RYML_CB_CHECK(callbacks, pos+1 < next);
_RYML_CB_CHECK(callbacks, pos+1 + 2 <= next);
size_t delta = next - (pos+1);
len -= delta;
pos = rest.find('%', pos+1);
} while(pos != npos);
if(len <= output.len)
{
size_t prev = 0, wpos = 0;
auto appendstr = [&](csubstr s) { memcpy(output.str + wpos, s.str, s.len); wpos += s.len; };
auto appendchar = [&](char c) { output.str[wpos++] = c; };
appendchar('<');
appendstr(prefix);
pos = rest.find('%');
_RYML_CB_ASSERT(callbacks, pos != npos);
do {
size_t next = rest.first_not_of("0123456789abcdefABCDEF", pos+1);
if(next == npos)
next = rest.len;
_RYML_CB_CHECK(callbacks, pos+1 < next);
_RYML_CB_CHECK(callbacks, pos+1 + 2 <= next);
uint8_t val;
if(C4_UNLIKELY(!read_hex(rest.range(pos+1, next), &val) || val > 127))
_RYML_CB_ERR(callbacks, "invalid URI character");
appendstr(rest.range(prev, pos));
appendchar(static_cast<char>(val));
prev = next;
pos = rest.find('%', pos+1);
} while(pos != npos);
_RYML_CB_ASSERT(callbacks, pos == npos);
_RYML_CB_ASSERT(callbacks, prev > 0);
_RYML_CB_ASSERT(callbacks, rest.len >= prev);
appendstr(rest.sub(prev));
appendchar('>');
_RYML_CB_ASSERT(callbacks, wpos == len);
}
}
return len;
}
} // namespace yml
} // namespace c4

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,27 @@
#include "c4/yml/version.hpp"
namespace c4 {
namespace yml {
csubstr version()
{
return RYML_VERSION;
}
int version_major()
{
return RYML_VERSION_MAJOR;
}
int version_minor()
{
return RYML_VERSION_MINOR;
}
int version_patch()
{
return RYML_VERSION_PATCH;
}
} // namespace yml
} // namespace c4